UNPKG

@vbyte/btc-dev

Version:

Batteries-included toolset for plebian bitcoin development

98 lines (97 loc) 3.02 kB
import { Stream } from '@vbyte/buff'; import { Assert } from '@vbyte/micro-lib/assert'; import { parse_error } from '@vbyte/micro-lib/util'; import { COINBASE } from '../../const.js'; export function decode_tx(txdata, use_segwit = true) { Assert.is_bytes(txdata, 'txdata must be hex or bytes'); const stream = new Stream(txdata); const version = read_version(stream); const has_witness = (use_segwit) ? check_witness_flag(stream) : false; const vin = read_inputs(stream); const vout = read_outputs(stream); if (has_witness) { for (const txin of vin) { txin.witness = read_witness(stream); } } const locktime = read_locktime(stream); return { version, vin, vout, locktime }; } function read_version(stream) { return stream.read(4).reverse().to_num(); } function check_witness_flag(stream) { const [marker, flag] = [...stream.peek(2)]; if (marker === 0) { stream.read(2); if (flag === 1) { return true; } else { throw new Error(`Invalid witness flag: ${flag}`); } } return false; } function read_inputs(stream) { const inputs = []; const vinCount = stream.varint(); for (let i = 0; i < vinCount; i++) { const txinput = read_vin(stream); inputs.push(txinput); } return inputs; } function read_vin(stream) { const txid = stream.read(32).reverse().hex; const vout = stream.read(4).reverse().num; const script_sig = read_payload(stream); const sequence = stream.read(4).reverse().num; const witness = []; if (txid === COINBASE.TXID && vout === COINBASE.VOUT) { return { coinbase: script_sig, prevout: null, script_sig: null, sequence, txid, vout, witness }; } else { return { coinbase: null, prevout: null, script_sig, sequence, txid, vout, witness }; } } function read_outputs(stream) { const outputs = []; const vcount = stream.varint(); for (let i = 0; i < vcount; i++) { try { outputs.push(read_vout(stream)); } catch (error) { throw new Error(`failed to decode output: ${i}: ${parse_error(error)}`); } } return outputs; } function read_vout(stream) { const value = stream.read(8).reverse().big; const script_pk = read_payload(stream); Assert.exists(script_pk, 'failed to decode script_pk'); return { value, script_pk }; } function read_witness(stream) { const stack = []; const count = stream.varint(); for (let i = 0; i < count; i++) { const element = read_payload(stream); if (element === null) { throw new Error('failed to decode witness element: ' + i); } stack.push(element); } return stack; } export function read_payload(stream) { const size = stream.varint('le'); return (size > 0) ? stream.read(size).hex : null; } function read_locktime(stream) { return stream.read(4).reverse().to_num(); }