@vbyte/btc-dev
Version:
Batteries-included toolset for plebian bitcoin development
90 lines (89 loc) • 3.36 kB
JavaScript
import { Buff } from '@vbyte/buff';
import { hash160, hash256 } from '@vbyte/micro-lib/hash';
import { Assert } from '@vbyte/micro-lib';
import { parse_txinput } from './util.js';
import { prefix_script_size, decode_script } from '../../lib/script/index.js';
import { encode_txin_vout, encode_tx_locktime, encode_txin_sequence, encode_txin_txid, encode_vout_value, encode_tx_version, parse_tx } from '../../lib/tx/index.js';
import * as CONST from '../../const.js';
export function hash_segwit_tx(txdata, options = {}) {
const { sigflag = 0x01, txindex } = options;
const tx = parse_tx(txdata);
const is_anypay = (sigflag & 0x80) === 0x80;
const flag = sigflag % 0x80;
if (!CONST.SIGHASH_SEGWIT.includes(flag)) {
throw new Error('Invalid hash type: ' + String(sigflag));
}
const { version, vin, vout, locktime } = tx;
const txinput = parse_txinput(tx, options);
const { txid, vout: prevIdx, prevout, sequence } = txinput;
const { value } = prevout ?? {};
if (value === undefined) {
throw new Error('Prevout value is empty!');
}
let { pubkey, script } = options;
if (script === undefined && pubkey !== undefined) {
const pkhash = hash160(pubkey).hex;
script = `76a914${String(pkhash)}88ac`;
}
if (script === undefined) {
throw new Error('No pubkey / script has been set!');
}
if (decode_script(script).includes('OP_CODESEPARATOR')) {
throw new Error('This library does not currently support the use of OP_CODESEPARATOR in segwit scripts.');
}
const sighash = [
encode_tx_version(version),
bip143_hash_prevouts(vin, is_anypay),
bip143_hash_sequence(vin, flag, is_anypay),
encode_txin_txid(txid),
encode_txin_vout(prevIdx),
prefix_script_size(script),
encode_vout_value(value),
encode_txin_sequence(sequence),
bip143_hash_outputs(vout, flag, txindex),
encode_tx_locktime(locktime),
Buff.num(sigflag, 4).reverse()
];
return hash256(Buff.join(sighash));
}
export function bip143_hash_prevouts(vin, isAnypay) {
if (isAnypay === true) {
return Buff.num(0, 32);
}
const stack = [];
for (const { txid, vout } of vin) {
stack.push(encode_txin_txid(txid));
stack.push(encode_txin_vout(vout));
}
return hash256(Buff.join(stack));
}
export function bip143_hash_sequence(vin, sigflag, isAnyPay) {
if (isAnyPay || sigflag !== 0x01) {
return Buff.num(0, 32);
}
const stack = [];
for (const { sequence } of vin) {
stack.push(encode_txin_sequence(sequence));
}
return hash256(Buff.join(stack));
}
export function bip143_hash_outputs(vout, sigflag, idx) {
const stack = [];
if (sigflag === 0x01) {
for (const { value, script_pk } of vout) {
stack.push(encode_vout_value(value));
stack.push(prefix_script_size(script_pk));
}
return hash256(Buff.join(stack));
}
if (sigflag === 0x03) {
Assert.ok(idx !== undefined);
if (idx < vout.length) {
const { value, script_pk } = vout[idx];
stack.push(encode_vout_value(value));
stack.push(prefix_script_size(script_pk));
return hash256(Buff.join(stack));
}
}
return Buff.num(0, 32);
}