Base classes, encoding utilities, workers, and supporting infrastructure for the BSV library.
// ES6 imports
import {
Struct, Bn, Br, Bw, Base58, Base58Check,
VarInt, OpCode, Constants, Random, Workers
} from 'bsv'
// CommonJS imports
const {
Struct, Bn, Br, Bw, Base58, Base58Check,
VarInt, OpCode, Constants, Random, Workers
} = require('bsv')Base class providing common serialization methods for all BSV objects.
class Struct {
constructor(obj?: object) // Initialize from object
// Object initialization
fromObject(obj: object): this // Set properties from object
// Binary serialization
fromBuffer(buf: Buffer): this // Parse from binary buffer
toBuffer(): Buffer // Serialize to binary buffer
fromFastBuffer(buf: Buffer): this // Fast binary parsing
toFastBuffer(): Buffer // Fast binary serialization
// Hexadecimal serialization
fromHex(hex: string): this // Parse from hex string
toHex(): string // Serialize to hex string
// String serialization (custom format)
fromString(str: string): this // Parse from string
toString(): string // Serialize to string
// JSON serialization
fromJSON(json: object): this // Parse from JSON object
toJSON(): object // Serialize to JSON object
// Binary reader/writer serialization
fromBr(br: Br): this // Parse using buffer reader
toBw(bw?: Bw): Bw // Serialize using buffer writer
// Utility methods
clone(): this // Deep clone object
// Generator methods for streaming
genFromBuffers(): Generator // Generator for streaming deserialization
expect(len: number, startbuf?: Buffer): Buffer // Helper for generators
}// Most BSV classes extend Struct and inherit these methods
const privKey = PrivKey.fromRandom()
// Hex serialization
const hex = privKey.toHex()
const privKey2 = PrivKey.fromHex(hex)
// JSON serialization
const json = privKey.toJSON()
const privKey3 = PrivKey.fromJSON(json)
// Buffer serialization
const buffer = privKey.toBuffer()
const privKey4 = PrivKey.fromBuffer(buffer)
// Cloning
const clone = privKey.clone()
// Object initialization
const privKey5 = new PrivKey().fromObject({
bn: Bn.fromNumber(12345),
compressed: true
})Arbitrary precision arithmetic extending the bn.js library for Bitcoin operations.
class Bn extends BN {
constructor(
n?: number | string | Buffer, // Number, string, or buffer
base?: number, // Number base (2, 8, 10, 16)
endian?: string // Byte order ('big', 'little')
)
// Buffer conversion
static fromBuffer(
buf: Buffer,
opts?: {
endian?: string, // 'big' or 'little' endian
size?: number // Expected size
}
): Bn
toBuffer(opts?: {
endian?: string, // 'big' or 'little' endian
size?: number // Fixed size (zero-padded)
}): Buffer
// Signed magnitude format (Bitcoin-specific)
static fromSm(
buf: Buffer,
opts?: { endian?: string }
): Bn
toSm(opts?: { endian?: string }): Buffer
// String conversion
static fromString(str: string, base?: number): Bn
toString(base?: number): string
// Number conversion
static fromNumber(n: number): Bn
toNumber(): number
// Hex convenience methods
static fromHex(hex: string): Bn
toHex(): string
// Random number generation
static fromRandom(): Bn
}// Create big numbers from various sources
const bn1 = Bn.fromNumber(12345)
const bn2 = Bn.fromString('123456789012345678901234567890')
const bn3 = Bn.fromHex('deadbeef')
const bn4 = Bn.fromBuffer(Buffer.from([0x01, 0x02, 0x03, 0x04]))
// Arithmetic operations (inherited from bn.js)
const sum = bn1.add(bn2)
const diff = bn2.sub(bn1)
const product = bn1.mul(bn2)
const quotient = bn2.div(bn1)
const remainder = bn2.mod(bn1)
// Bitcoin-specific signed magnitude format
const signed = Bn.fromNumber(-12345)
const smBuffer = signed.toSm()
const restored = Bn.fromSm(smBuffer)
// Buffer operations with endianness
const buffer = bn3.toBuffer({ endian: 'little', size: 8 })
const restored2 = Bn.fromBuffer(buffer, { endian: 'little' })
// Comparisons
const isEqual = bn1.eq(bn2)
const isGreater = bn1.gt(bn2)
const isLess = bn1.lt(bn2)
// Generate random big number
const randomBn = Bn.fromRandom()
// Fixed-size serialization (for keys, hashes, etc.)
const bn256bit = Bn.fromNumber(12345)
const key = bn256bit.toBuffer({ size: 32 }) // 32-byte buffer
console.log('Key length:', key.length) // 32Sequential buffer reading with built-in Bitcoin data type support.
class Br {
constructor(buf?: Buffer)
// Core properties
buf: Buffer // Source buffer
pos: number // Current position
// Basic reading
read(len: number): Buffer // Read bytes
readReverse(len: number): Buffer // Read bytes in reverse order
// Integer reading (little endian)
readUInt8(): number // Read 1-byte unsigned integer
readUInt16LE(): number // Read 2-byte unsigned integer
readUInt32LE(): number // Read 4-byte unsigned integer
readUInt64LEBn(): Bn // Read 8-byte unsigned integer as Bn
// Variable-length integers (Bitcoin VarInt format)
readVarIntNum(): number // Read VarInt as number
readVarIntBuf(): Buffer // Read VarInt as buffer
readVarIntBn(): Bn // Read VarInt as big number
// Utility
eof(): boolean // Check if at end of buffer
}// Create buffer with mixed data
const bw = new Bw()
bw.writeUInt8(42)
bw.writeUInt32LE(123456)
bw.writeVarIntNum(999999)
bw.write(Buffer.from('hello', 'utf8'))
const buffer = bw.toBuffer()
// Read data sequentially
const br = new Br(buffer)
const byte = br.readUInt8() // 42
const int32 = br.readUInt32LE() // 123456
const varInt = br.readVarIntNum() // 999999
const remaining = br.read(5) // Buffer('hello')
console.log('Read:', { byte, int32, varInt, remaining: remaining.toString() })
console.log('At EOF:', br.eof())
// Read Bitcoin transaction format
const txBuffer = tx.toBuffer()
const br2 = new Br(txBuffer)
const version = br2.readUInt32LE()
const inputCount = br2.readVarIntNum()
// ... continue reading transaction structureSequential buffer writing with built-in Bitcoin data type support.
class Bw {
constructor()
// Core properties
bufs: Buffer[] // Array of written buffers
// Basic writing
write(buf: Buffer): Bw // Write buffer
writeReverse(buf: Buffer): Bw // Write buffer in reverse order
// Integer writing (little endian)
writeUInt8(n: number): Bw // Write 1-byte unsigned integer
writeUInt16LE(n: number): Bw // Write 2-byte unsigned integer
writeUInt32LE(n: number): Bw // Write 4-byte unsigned integer
writeUInt64LEBn(bn: Bn): Bw // Write 8-byte unsigned integer from Bn
// Variable-length integers (Bitcoin VarInt format)
writeVarIntNum(n: number): Bw // Write number as VarInt
writeVarIntBuf(buf: Buffer): Bw // Write buffer length + buffer
writeVarIntBn(bn: Bn): Bw // Write big number as VarInt
// Result
toBuffer(): Buffer // Get final concatenated buffer
}// Build complex binary structure
const bw = new Bw()
// Write transaction version
bw.writeUInt32LE(1)
// Write input count
bw.writeVarIntNum(2)
// Write transaction inputs
for (const txIn of txIns) {
bw.write(txIn.txHashBuf) // Previous tx hash (32 bytes)
bw.writeUInt32LE(txIn.txOutNum) // Output index (4 bytes)
bw.writeVarIntBuf(txIn.script.toBuffer()) // Script with length prefix
bw.writeUInt32LE(txIn.nSequence) // Sequence (4 bytes)
}
// Write output count and outputs
bw.writeVarIntNum(txOuts.length)
for (const txOut of txOuts) {
bw.writeUInt64LEBn(txOut.valueBn) // Value as 8-byte integer
bw.writeVarIntBuf(txOut.script.toBuffer()) // Script with length prefix
}
// Write lock time
bw.writeUInt32LE(0)
// Get final transaction buffer
const txBuffer = bw.toBuffer()Bitcoin's Base58 encoding for human-readable data representation.
class Base58 {
// Core encoding/decoding
static encode(buf: Buffer): string // Encode buffer to Base58 string
static decode(str: string): Buffer // Decode Base58 string to buffer
}// Encode binary data to Base58
const data = Buffer.from('Hello, Base58!', 'utf8')
const encoded = Base58.encode(data)
console.log('Base58:', encoded) // "2NEpo7TZRhna7vSvL"
// Decode Base58 to binary
const decoded = Base58.decode(encoded)
console.log('Decoded:', decoded.toString('utf8')) // "Hello, Base58!"
// Base58 alphabet excludes confusing characters: 0, O, I, l
const alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'Base58 encoding with checksum for error detection (used in Bitcoin addresses and keys).
class Base58Check {
// Encoding/decoding with checksum
static encode(buf: Buffer): string // Encode with 4-byte checksum
static decode(str: string): Buffer // Decode and verify checksum
}// Create Bitcoin address manually
const version = Buffer.from([0x00]) // Mainnet P2PKH version
const hash160 = Hash.sha256Ripemd160(pubKey.toBuffer())
const payload = Buffer.concat([version, hash160])
const address = Base58Check.encode(payload)
// Decode and verify Bitcoin address
try {
const decoded = Base58Check.decode(address)
const version = decoded[0]
const hash = decoded.slice(1)
console.log('Valid address, version:', version, 'hash:', hash.toString('hex'))
} catch (error) {
console.error('Invalid address checksum')
}
// Private key WIF encoding
const privKeyBytes = privKey.bn.toBuffer({ size: 32 })
const wifVersion = Buffer.from([0x80]) // Mainnet private key version
const compressed = Buffer.from([0x01]) // Compression flag
const wifPayload = Buffer.concat([wifVersion, privKeyBytes, compressed])
const wif = Base58Check.encode(wifPayload)Bitcoin's variable-length integer encoding for space-efficient integer storage.
class VarInt {
constructor(buf?: Buffer)
// Core properties
buf: Buffer // VarInt buffer representation
// Creation methods
static fromNumber(num: number): VarInt // Create from number
static fromBuffer(buf: Buffer): VarInt // Parse from buffer
// Conversion methods
toNumber(): number // Convert to number
toBuffer(): Buffer // Get buffer representation
}// VarInt encoding examples
const small = VarInt.fromNumber(252) // 1 byte: 0xfc
const medium = VarInt.fromNumber(253) // 3 bytes: 0xfd 0xfd 0x00
const large = VarInt.fromNumber(65536) // 3 bytes: 0xfd 0x00 0x01
const huge = VarInt.fromNumber(16777216) // 5 bytes: 0xfe 0x00 0x00 0x00 0x01
console.log('Small VarInt:', small.toBuffer().toString('hex'))
console.log('Medium VarInt:', medium.toBuffer().toString('hex'))
// Parse VarInt from buffer
const buffer = Buffer.from([0xfd, 0x34, 0x12]) // 0x1234 = 4660
const varInt = VarInt.fromBuffer(buffer)
console.log('Parsed value:', varInt.toNumber()) // 4660
// VarInt size calculation
function getVarIntSize(num) {
if (num < 0xfd) return 1 // 1 byte
if (num <= 0xffff) return 3 // 3 bytes (0xfd + 2 bytes)
if (num <= 0xffffffff) return 5 // 5 bytes (0xfe + 4 bytes)
return 9 // 9 bytes (0xff + 8 bytes)
}Bitcoin Script operation codes with complete opcode definitions.
class OpCode {
constructor(num?: number)
// Core properties
num: number // Opcode number
// Creation methods
static fromNumber(num: number): OpCode // Create from number
static fromString(str: string): OpCode // Create from string name
// Conversion methods
toNumber(): number // Get opcode number
toString(): string // Get opcode name
// All Bitcoin opcodes as static constants
static OP_0: number // 0x00 - Push empty array
static OP_1: number // 0x51 - Push number 1
static OP_2: number // 0x52 - Push number 2
// ... continuing through OP_16
// Data push opcodes
static OP_PUSHDATA1: number // 0x4c - Push next byte as data length
static OP_PUSHDATA2: number // 0x4d - Push next 2 bytes as data length
static OP_PUSHDATA4: number // 0x4e - Push next 4 bytes as data length
// Flow control
static OP_IF: number // 0x63 - Execute if top of stack is true
static OP_ELSE: number // 0x67 - Execute if OP_IF was false
static OP_ENDIF: number // 0x68 - End if/else block
static OP_RETURN: number // 0x6a - Mark output as unspendable
// Stack operations
static OP_DUP: number // 0x76 - Duplicate top stack item
static OP_DROP: number // 0x75 - Remove top stack item
static OP_SWAP: number // 0x7c - Swap top two stack items
// Cryptographic operations
static OP_HASH160: number // 0xa9 - SHA256 + RIPEMD160
static OP_CHECKSIG: number // 0xac - Check signature
static OP_CHECKMULTISIG: number // 0xae - Check multisig
// Comparison operations
static OP_EQUAL: number // 0x87 - Return 1 if equal
static OP_EQUALVERIFY: number // 0x88 - Equal + verify (fail if false)
// ... many more opcodes defined
}// Create opcodes from numbers and strings
const opDup = OpCode.fromNumber(OpCode.OP_DUP)
const opHash160 = OpCode.fromString('OP_HASH160')
// Build P2PKH script using opcodes
const script = new Script()
script.writeOpCode(OpCode.OP_DUP)
script.writeOpCode(OpCode.OP_HASH160)
script.writeBuffer(pubKeyHashBuf) // 20 bytes
script.writeOpCode(OpCode.OP_EQUALVERIFY)
script.writeOpCode(OpCode.OP_CHECKSIG)
// Opcode information
console.log('OP_DUP number:', OpCode.OP_DUP) // 118
console.log('OP_DUP string:', opDup.toString()) // "OP_DUP"
console.log('OP_RETURN number:', OpCode.OP_RETURN) // 106
// Common opcode patterns
const opcodes = {
// Standard P2PKH
p2pkh: [OpCode.OP_DUP, OpCode.OP_HASH160, OpCode.OP_EQUALVERIFY, OpCode.OP_CHECKSIG],
// OP_RETURN data
opReturn: [OpCode.OP_RETURN],
// Safe OP_RETURN (OP_FALSE OP_RETURN)
safeOpReturn: [OpCode.OP_0, OpCode.OP_RETURN],
// 2-of-3 multisig
multisig2of3: [OpCode.OP_2, OpCode.OP_3, OpCode.OP_CHECKMULTISIG]
}Network-specific configuration constants for mainnet, testnet, and other networks.
class Constants {
// Network configurations
static Mainnet: {
Address: {
pubKeyHash: number, // P2PKH version byte (0x00)
scriptHash: number // P2SH version byte (0x05)
},
Bip32: {
pubKey: number, // xpub version (0x0488b21e)
privKey: number // xprv version (0x0488ade4)
},
PrivKey: {
version: number // Private key WIF version (0x80)
},
TxBuilder: {
dust: number, // Dust threshold (546 satoshis)
feePerKbNum: number // Default fee rate (400 sat/KB)
},
Block: {
maxNBits: number, // Maximum difficulty target
magicNum: number // Network magic number
}
}
static Testnet: {
// Similar structure with testnet values
}
static Regtest: {
// Similar structure with regtest values
}
static STN: {
// Similar structure with STN values
}
static Default: object // Currently selected network (usually Mainnet)
// Utility method
static getConstants(magicNum: number): object // Get constants by magic number
}// Access mainnet constants
const mainnet = Constants.Mainnet
console.log('Mainnet P2PKH version:', mainnet.Address.pubKeyHash) // 0
console.log('Mainnet xprv version:', mainnet.Bip32.privKey.toString(16)) // 488ade4
// Access testnet constants
const testnet = Constants.Testnet
console.log('Testnet P2PKH version:', testnet.Address.pubKeyHash) // 111
// Use with classes
const mainnetAddress = new Address(
Constants.Mainnet.Address.pubKeyHash,
hashBuf,
Constants.Mainnet
)
const testnetPrivKey = new PrivKey(
bn,
true,
Constants.Testnet
)
// Network-specific classes automatically use appropriate constants
const mainnetAddr = Address.Mainnet.fromRandom() // Uses mainnet constants
const testnetAddr = Address.Testnet.fromRandom() // Uses testnet constants
// Get constants by magic number
const constants = Constants.getConstants(0xe3e1f3e8) // Mainnet magicCryptographically secure random number generation.
class Random {
// Generate cryptographically secure random bytes
static getRandomBuffer(size: number): Buffer
}// Generate random data for cryptographic use
const randomKey = Random.getRandomBuffer(32) // 256-bit key
const randomIv = Random.getRandomBuffer(16) // 128-bit IV
const randomNonce = Random.getRandomBuffer(32) // 256-bit nonce
const randomSalt = Random.getRandomBuffer(16) // 128-bit salt
// Use for key generation
const privKey = PrivKey.fromBn(Bn.fromBuffer(randomKey))
const keyPair = KeyPair.fromPrivKey(privKey)
// Use for encryption
const message = Buffer.from('Secret message', 'utf8')
const encrypted = Aescbc.encrypt(message, randomKey, randomIv)
// Generate random transaction nonce
const txNonce = Random.getRandomBuffer(4).readUInt32LE(0)Background computation management for non-blocking cryptographic operations.
class Workers {
// Instance methods
asyncObjectMethod(
obj: object, // Object instance
methodName: string, // Method name to call
args: any[], // Method arguments
id?: string // Operation ID
): Promise<any>
asyncClassMethod(
classObj: Function, // Class constructor
methodName: string, // Static method name
args: any[], // Method arguments
id?: string // Operation ID
): Promise<any>
// Static methods
static asyncObjectMethod(...args): Promise<any>
static asyncClassMethod(...args): Promise<any>
static endGlobalWorkers(): void // Cleanup global workers
}// Async object method execution
const privKey = PrivKey.fromRandom()
const workers = new Workers()
// Execute private key methods in background
const address = await workers.asyncObjectMethod(privKey, 'toAddress', [])
const wif = await workers.asyncObjectMethod(privKey, 'toWif', [])
// Async class method execution (static methods)
const hashBuf = await workers.asyncClassMethod(Hash, 'sha256', [dataBuf])
const sig = await workers.asyncClassMethod(Ecdsa, 'sign', [hashBuf, keyPair])
// Global workers (shared across application)
const globalHashResult = await Workers.asyncClassMethod(Hash, 'sha256Sha256', [largeBuf])
const globalSigResult = await Workers.asyncObjectMethod(tx, 'sign', [keyPair, nHashType, nIn, subScript, valueBn])
// Cleanup when application ends
Workers.endGlobalWorkers()
// Batch operations
const operations = [
Workers.asyncClassMethod(Hash, 'sha256', [buf1]),
Workers.asyncClassMethod(Hash, 'sha256', [buf2]),
Workers.asyncClassMethod(Hash, 'sha256', [buf3])
]
const results = await Promise.all(operations)
console.log('Hashed', results.length, 'buffers in parallel')// Parse large transactions using generators
function* parseTransactionStream(buffer) {
const br = new Br(buffer)
// Parse version
const version = br.readUInt32LE()
yield { type: 'version', value: version }
// Parse inputs
const inputCount = br.readVarIntNum()
yield { type: 'inputCount', value: inputCount }
for (let i = 0; i < inputCount; i++) {
const txHash = br.read(32)
const outputIndex = br.readUInt32LE()
const scriptLen = br.readVarIntNum()
const script = br.read(scriptLen)
const sequence = br.readUInt32LE()
yield {
type: 'input',
index: i,
txHash: txHash.toString('hex'),
outputIndex,
script: script.toString('hex'),
sequence
}
}
// Parse outputs
const outputCount = br.readVarIntNum()
yield { type: 'outputCount', value: outputCount }
for (let i = 0; i < outputCount; i++) {
const value = br.readUInt64LEBn()
const scriptLen = br.readVarIntNum()
const script = br.read(scriptLen)
yield {
type: 'output',
index: i,
value: value.toString(),
script: script.toString('hex')
}
}
// Parse lock time
const lockTime = br.readUInt32LE()
yield { type: 'lockTime', value: lockTime }
}
// Usage
const txBuffer = tx.toBuffer()
for (const part of parseTransactionStream(txBuffer)) {
console.log(part.type, part)
}// Create custom serializable class extending Struct
class CustomData extends Struct {
constructor(name, value, timestamp) {
super({ name, value, timestamp })
}
fromBr(br) {
const nameLen = br.readVarIntNum()
this.name = br.read(nameLen).toString('utf8')
this.value = br.readUInt64LEBn()
this.timestamp = br.readUInt32LE()
return this
}
toBw(bw) {
bw = bw || new Bw()
const nameBuf = Buffer.from(this.name, 'utf8')
bw.writeVarIntNum(nameBuf.length)
bw.write(nameBuf)
bw.writeUInt64LEBn(this.value)
bw.writeUInt32LE(this.timestamp)
return bw
}
}
// Usage
const data = new CustomData('test', Bn.fromNumber(12345), Date.now())
const buffer = data.toBuffer()
const restored = CustomData.fromBuffer(buffer)// Process large datasets with workers
async function batchHashFiles(files) {
const workers = new Workers()
// Process files in batches to avoid memory issues
const batchSize = 10
const results = []
for (let i = 0; i < files.length; i += batchSize) {
const batch = files.slice(i, i + batchSize)
const batchPromises = batch.map(async (file) => {
const data = await readFile(file)
return workers.asyncClassMethod(Hash, 'sha256', [data], file)
})
const batchResults = await Promise.all(batchPromises)
results.push(...batchResults)
console.log(`Processed batch ${Math.floor(i/batchSize) + 1}`)
}
workers.endGlobalWorkers()
return results
}Utility classes include comprehensive validation:
// Buffer bounds checking
try {
const br = new Br(Buffer.alloc(10))
br.read(20) // Trying to read more than available
} catch (error) {
console.error('Buffer underflow:', error.message)
}
// VarInt validation
try {
const invalidVarInt = Buffer.from([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
VarInt.fromBuffer(invalidVarInt) // May be too large
} catch (error) {
console.error('Invalid VarInt:', error.message)
}
// Base58Check validation
try {
const corruptedAddress = '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3' // Wrong checksum
Base58Check.decode(corruptedAddress)
} catch (error) {
console.error('Checksum validation failed:', error.message)
}
// OpCode validation
try {
const invalidOpCode = OpCode.fromNumber(300) // Non-existent opcode
} catch (error) {
console.error('Invalid opcode:', error.message)
}