Various utility functions for buffer operations, ECC library initialization, serialization helpers, and validation functions that support the core Bitcoin operations.
Initialize and manage the elliptic curve cryptography library required for Bitcoin operations.
/**
* Initialize the ECC library with the provided instance
* @param eccLib - ECC library instance or undefined to clear
* @param opts - Extra initialization options
* @throws Error if ECC library verification fails (unless disabled)
*/
function initEccLib(eccLib: TinySecp256k1Interface | undefined, opts?: {
DANGER_DO_NOT_VERIFY_ECCLIB: boolean;
}): void;
/**
* Get the current ECC library instance
* @returns Current ECC library instance
* @throws Error if ECC library is not initialized
*/
function getEccLib(): TinySecp256k1Interface;
interface TinySecp256k1Interface {
/** Check if point is valid x-only point (32 bytes) */
isXOnlyPoint(p: Uint8Array): boolean;
/** Add tweak to x-only point, returns new point and parity */
xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
}
interface XOnlyPointAddTweakResult {
/** Parity of the resulting point (0 or 1) */
parity: 1 | 0;
/** Resulting x-only public key */
xOnlyPubkey: Uint8Array;
}Usage Examples:
import { initEccLib, getEccLib } from 'bitcoinjs-lib';
import * as ecc from 'tiny-secp256k1'; // Example ECC library
// Initialize ECC library (required before using bitcoinjs-lib)
initEccLib(ecc);
// Get initialized library
const eccInstance = getEccLib();
// Use ECC functions
const xOnlyPubkey = new Uint8Array(32); // 32-byte x-only pubkey
const isValid = eccInstance.isXOnlyPoint(xOnlyPubkey);
console.log('Valid x-only point:', isValid);
// Add tweak for Taproot
const tweak = new Uint8Array(32); // 32-byte tweak
const result = eccInstance.xOnlyPointAddTweak(xOnlyPubkey, tweak);
if (result) {
console.log('Tweaked pubkey:', Buffer.from(result.xOnlyPubkey).toString('hex'));
console.log('Parity:', result.parity);
}Helper functions for buffer operations commonly used in Bitcoin protocol.
/**
* Read 64-bit unsigned integer in little-endian format
* @param buffer - Buffer to read from
* @param offset - Offset to start reading
* @returns 64-bit integer value (may lose precision for large values)
*/
function readUInt64LE(buffer: Buffer, offset: number): number;
/**
* Write 64-bit unsigned integer in little-endian format
* @param buffer - Buffer to write to
* @param value - Value to write
* @param offset - Offset to start writing
* @returns New offset after writing
*/
function writeUInt64LE(buffer: Buffer, value: number, offset: number): number;
/**
* Reverse the order of bytes in a buffer
* @param buffer - Buffer to reverse
* @returns New buffer with reversed bytes
*/
function reverseBuffer(buffer: Buffer): Buffer;
/**
* Create a copy of a buffer
* @param buffer - Buffer to clone
* @returns New buffer with same content
*/
function cloneBuffer(buffer: Buffer): Buffer;Usage Examples:
import { readUInt64LE, writeUInt64LE, reverseBuffer, cloneBuffer } from 'bitcoinjs-lib';
// Read/write 64-bit integers
const buffer = Buffer.alloc(8);
const offset = writeUInt64LE(buffer, 1234567890, 0);
console.log('Wrote 8 bytes at offset:', offset);
const value = readUInt64LE(buffer, 0);
console.log('Read value:', value); // 1234567890
// Reverse buffer (common for Bitcoin hashes)
const hash = Buffer.from('abc123def456', 'hex');
const reversed = reverseBuffer(hash);
console.log('Original:', hash.toString('hex'));
console.log('Reversed:', reversed.toString('hex'));
// Clone buffer
const original = Buffer.from('hello', 'utf8');
const copy = cloneBuffer(original);
console.log('Clone equals original:', copy.equals(original));
console.log('Clone is different object:', copy !== original);Helper classes for efficient buffer reading and writing.
/**
* Helper class for serialization of bitcoin data types into a pre-allocated buffer
*/
class BufferWriter {
buffer: Buffer;
offset: number;
/**
* Create BufferWriter with specific capacity
* @param size - Buffer size to allocate
* @returns New BufferWriter instance
*/
static withCapacity(size: number): BufferWriter;
constructor(buffer: Buffer, offset?: number);
writeUInt8(i: number): void;
writeInt32(i: number): void;
writeUInt32(i: number): void;
writeUInt64(i: number): void;
writeVarInt(i: number): void;
writeSlice(slice: Buffer): void;
writeVarSlice(slice: Buffer): void;
writeVector(vector: Buffer[]): void;
/** Get final buffer with written data */
end(): Buffer;
}
/**
* Helper class for reading bitcoin data types from a buffer
*/
class BufferReader {
buffer: Buffer;
offset: number;
constructor(buffer: Buffer, offset?: number);
readUInt8(): number;
readInt32(): number;
readUInt32(): number;
readUInt64(): number;
readVarInt(): number;
readSlice(n: number): Buffer;
readVarSlice(): Buffer;
readVector(): Buffer[];
}Usage Examples:
import { BufferWriter, BufferReader } from 'bitcoinjs-lib';
// Write data to buffer
const writer = BufferWriter.withCapacity(100);
writer.writeUInt32(0x01000000); // Version
writer.writeVarInt(2); // Input count
writer.writeSlice(Buffer.from('abc123def456789012345678901234567890', 'hex')); // Hash
writer.writeUInt32(0); // Index
writer.writeVarSlice(Buffer.from('473044...', 'hex')); // ScriptSig
const serialized = writer.end();
console.log('Serialized data:', serialized.toString('hex'));
// Read data from buffer
const reader = new BufferReader(serialized);
const version = reader.readUInt32();
const inputCount = reader.readVarInt();
const hash = reader.readSlice(32);
const index = reader.readUInt32();
const scriptSig = reader.readVarSlice();
console.log('Version:', version);
console.log('Input count:', inputCount);
console.log('Hash:', hash.toString('hex'));
console.log('Index:', index);
console.log('ScriptSig length:', scriptSig.length);Handle Bitcoin's variable-length integer encoding.
const varuint: {
/** Calculate encoding length for a number */
encodingLength(number: number): number;
/** Encode number to buffer */
encode(number: number, buffer?: Buffer, offset?: number): Buffer;
/** Decode number from buffer */
decode(buffer: Buffer, offset?: number): number;
};Usage Examples:
import { varuint } from 'bitcoinjs-lib';
// Encode various sizes
const small = varuint.encode(42);
const medium = varuint.encode(300);
const large = varuint.encode(70000);
console.log('Small (42):', small.toString('hex')); // '2a'
console.log('Medium (300):', medium.toString('hex')); // 'fd2c01'
console.log('Large (70000):', large.toString('hex')); // 'fd7011'
// Decode
console.log('Decoded small:', varuint.decode(small)); // 42
console.log('Decoded medium:', varuint.decode(medium)); // 300
console.log('Decoded large:', varuint.decode(large)); // 70000
// Calculate encoding length
console.log('Length for 42:', varuint.encodingLength(42)); // 1
console.log('Length for 300:', varuint.encodingLength(300)); // 3
console.log('Length for 70000:', varuint.encodingLength(70000)); // 3Utility functions for validating Bitcoin-specific data types.
/**
* Check if two arrays of buffers are equal
* @param a - First array of buffers
* @param b - Second array of buffers
* @returns True if arrays are equal
*/
function stacksEqual(a: Buffer[], b: Buffer[]): boolean;
/**
* Check if value is a valid elliptic curve point
* @param p - Value to check
* @returns True if valid EC point
*/
function isPoint(p: Buffer | number | undefined | null): boolean;
/**
* Validate satoshi amount
* @param value - Value to validate
* @returns True if valid satoshi amount
*/
function Satoshi(value: number): boolean;
/**
* Check if object is a Tapleaf
* @param o - Object to check
* @returns True if valid Tapleaf
*/
function isTapleaf(o: any): o is Tapleaf;
/**
* Check if object is a Taptree
* @param scriptTree - Object to check
* @returns True if valid Taptree
*/
function isTaptree(scriptTree: any): scriptTree is Taptree;Usage Examples:
import { stacksEqual, isPoint, Satoshi, isTapleaf, isTaptree } from 'bitcoinjs-lib';
// Compare stacks
const stack1 = [Buffer.from('abc', 'hex'), Buffer.from('def', 'hex')];
const stack2 = [Buffer.from('abc', 'hex'), Buffer.from('def', 'hex')];
const stack3 = [Buffer.from('abc', 'hex'), Buffer.from('123', 'hex')];
console.log('Stacks equal:', stacksEqual(stack1, stack2)); // true
console.log('Stacks different:', stacksEqual(stack1, stack3)); // false
// Validate points
const validPubkey = Buffer.from('03a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd', 'hex');
const invalidPoint = Buffer.from('hello', 'utf8');
console.log('Valid pubkey:', isPoint(validPubkey)); // true
console.log('Invalid point:', isPoint(invalidPoint)); // false
console.log('Null point:', isPoint(null)); // false
// Validate satoshi amounts
console.log('Valid amount:', Satoshi(100000)); // true
console.log('Zero amount:', Satoshi(0)); // true
console.log('Negative amount:', Satoshi(-1)); // false
console.log('Too large:', Satoshi(21000000 * 100000000 + 1)); // false
// Validate Taproot structures
const validTapleaf = { output: Buffer.from('abc', 'hex'), version: 0xc0 };
const validTaptree = [validTapleaf, validTapleaf];
console.log('Valid tapleaf:', isTapleaf(validTapleaf)); // true
console.log('Valid taptree:', isTaptree(validTaptree)); // true
console.log('Invalid tapleaf:', isTapleaf({ output: 'not a buffer' })); // falseUtility functions for Merkle tree operations.
/**
* Calculate Merkle root of array of buffers using digest function
* @param values - Array of hash values
* @param digestFn - Hash function to use (typically hash256)
* @returns Merkle root
* @throws TypeError if invalid parameters
*/
function fastMerkleRoot(values: Buffer[], digestFn: (b: Buffer) => Buffer): Buffer;Usage Examples:
import { fastMerkleRoot, crypto } from 'bitcoinjs-lib';
// Calculate Merkle root of transaction hashes
const txHashes = [
Buffer.from('abc123...', 'hex'),
Buffer.from('def456...', 'hex'),
Buffer.from('789012...', 'hex'),
Buffer.from('345678...', 'hex')
];
const merkleRoot = fastMerkleRoot(txHashes, crypto.hash256);
console.log('Merkle root:', merkleRoot.toString('hex'));
// Single item (returns the item itself)
const singleRoot = fastMerkleRoot([txHashes[0]], crypto.hash256);
console.log('Single root equals input:', singleRoot.equals(txHashes[0])); // true
// Empty array handling
try {
fastMerkleRoot([], crypto.hash256);
} catch (error) {
console.log('Empty array error:', error.message);
}Handle Bitcoin script push data operations.
/**
* Calculate encoding length for push data
* @param i - Number to calculate encoding length for
* @returns Encoding length in bytes
*/
function encodingLength(i: number): number;
/**
* Encode push data into buffer
* @param buffer - Buffer to write into
* @param num - Number to encode
* @param offset - Offset to start writing
* @returns Size of encoded data
*/
function encode(buffer: Buffer, num: number, offset: number): number;
/**
* Decode push data from buffer
* @param buffer - Buffer to decode from
* @param offset - Offset to start reading
* @returns Decoded push data info or null if invalid
*/
function decode(buffer: Buffer, offset: number): {
opcode: number;
number: number;
size: number;
} | null;Usage Examples:
import { encodingLength, encode, decode } from 'bitcoinjs-lib/src/push_data';
// Calculate encoding length
console.log('Length for 20:', encodingLength(20)); // 1 (OP_PUSHDATA + length byte)
console.log('Length for 300:', encodingLength(300)); // 3 (OP_PUSHDATA1 + 1 length byte + 2 data bytes)
// Encode push data
const buffer = Buffer.alloc(10);
const size = encode(buffer, 42, 0);
console.log('Encoded 42 in', size, 'bytes');
console.log('Encoded data:', buffer.slice(0, size).toString('hex'));
// Decode push data
const decoded = decode(buffer, 0);
if (decoded) {
console.log('Opcode:', decoded.opcode);
console.log('Number:', decoded.number);
console.log('Size:', decoded.size);
}Combine utilities for complex buffer operations:
import { BufferWriter, BufferReader, reverseBuffer, crypto } from 'bitcoinjs-lib';
function serializeTransaction(version: number, inputs: any[], outputs: any[], locktime: number): Buffer {
const writer = BufferWriter.withCapacity(1000);
// Version
writer.writeUInt32(version);
// Inputs
writer.writeVarInt(inputs.length);
inputs.forEach(input => {
writer.writeSlice(reverseBuffer(Buffer.from(input.hash, 'hex'))); // Reverse for little-endian
writer.writeUInt32(input.index);
writer.writeVarSlice(input.script || Buffer.alloc(0));
writer.writeUInt32(input.sequence || 0xffffffff);
});
// Outputs
writer.writeVarInt(outputs.length);
outputs.forEach(output => {
writer.writeUInt64(output.value);
writer.writeVarSlice(output.script);
});
// Locktime
writer.writeUInt32(locktime);
return writer.end();
}
// Use custom serialization
const txData = {
version: 2,
inputs: [{ hash: 'abc123...', index: 0, script: Buffer.from('47304402...', 'hex') }],
outputs: [{ value: 50000, script: Buffer.from('76a914...88ac', 'hex') }],
locktime: 0
};
const serialized = serializeTransaction(txData.version, txData.inputs, txData.outputs, txData.locktime);
console.log('Serialized tx:', serialized.toString('hex'));Use utilities for performance-critical operations:
import { BufferWriter, varuint } from 'bitcoinjs-lib';
// Pre-allocate buffers for better performance
function efficientSerialization(data: Buffer[]): Buffer {
// Calculate exact size needed
let totalSize = varuint.encodingLength(data.length);
data.forEach(item => {
totalSize += varuint.encodingLength(item.length) + item.length;
});
// Allocate exact size buffer
const writer = BufferWriter.withCapacity(totalSize);
// Write data
writer.writeVarInt(data.length);
data.forEach(item => writer.writeVarSlice(item));
const result = writer.end();
console.log('Allocated:', totalSize, 'Used:', result.length);
return result;
}interface Tapleaf {
output: Buffer;
version?: number;
}
type Taptree = [Taptree | Tapleaf, Taptree | Tapleaf] | Tapleaf;
interface TinySecp256k1Interface {
isXOnlyPoint(p: Uint8Array): boolean;
xOnlyPointAddTweak(p: Uint8Array, tweak: Uint8Array): XOnlyPointAddTweakResult | null;
}
interface XOnlyPointAddTweakResult {
parity: 1 | 0;
xOnlyPubkey: Uint8Array;
}
const TAPLEAF_VERSION_MASK = 254;