@vbyte/btc-dev
Version:
Batteries-included toolset for plebian bitcoin development
97 lines (96 loc) • 3.04 kB
JavaScript
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);
let has_witness = check_witness_flag(stream);
has_witness = (use_segwit) ? has_witness : 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();
}