Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion modules/utxo-bin/src/InputParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,12 @@ export class InputParser extends Parser {
...this.parseSignaturesWithSigners(
parsed,
signedBy.flatMap((v, i) => (v ? [i.toString()] : [])),
parsed.signatures.map((k: Buffer | 0) => (k === 0 ? -1 : signedBy.indexOf(k)))
parsed.signatures.map((k: Buffer | 0) => {
if (k === 0) {
return -1;
}
return signedBy.findIndex((kk) => kk && kl.equals(k));
})
)
);
} catch (e) {
Expand Down
23 changes: 17 additions & 6 deletions modules/utxo-bin/src/commands/cmdParseTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ import {
fetchTransactionStatus,
getClient,
} from '../fetch';
import { getParserTxProperties } from '../ParserTx';
import { getParserTxProperties, ParserTx } from '../ParserTx';
import { parseUnknown } from '../parseUnknown';
import { Parser } from '../Parser';

import { formatString } from './formatString';
import { getPrevOutputsFromPrevTxs } from '../prevTx';

export type ArgsParseTransaction = ReadStringOptions & {
network: utxolib.Network;
txid?: string;
prevTx?: string[];
blockHeight?: number;
txIndex?: number;
all: boolean;
Expand Down Expand Up @@ -71,6 +73,7 @@ export const cmdParseTx = {
.options(readStringOptions)
.options(getNetworkOptionsDemand())
.option('txid', { type: 'string' })
.option('prevTx', { type: 'string', description: 'previous transaction hex or base64 string', array: true })
.option('blockHeight', { type: 'number' })
.option('txIndex', { type: 'number' })
.option('fetchAll', { type: 'boolean', default: false })
Expand Down Expand Up @@ -128,11 +131,14 @@ export const cmdParseTx = {
throw new Error(`no txdata`);
}

const bytes = stringToBuffer(string, ['hex', 'base64']);
function decodeBytes(bytes: Buffer): ParserTx {
return utxolib.bitgo.isPsbt(bytes)
? utxolib.bitgo.createPsbtFromBuffer(bytes, argv.network)
: utxolib.bitgo.createTransactionFromBuffer(bytes, argv.network, { amountType: 'bigint' });
}

let tx = utxolib.bitgo.isPsbt(bytes)
? utxolib.bitgo.createPsbtFromBuffer(bytes, argv.network)
: utxolib.bitgo.createTransactionFromBuffer(bytes, argv.network, { amountType: 'bigint' });
const bytes = stringToBuffer(string, ['hex', 'base64']);
let tx = decodeBytes(bytes);

const { id: txid } = getParserTxProperties(tx, undefined);
if (tx instanceof utxolib.bitgo.UtxoTransaction) {
Expand All @@ -144,6 +150,11 @@ export const cmdParseTx = {
tx = tx.extractTransaction();
}

const prevTxs: ParserTx[] = (argv.prevTx ?? []).map((s) => {
const buf = stringToBuffer(s, ['hex', 'base64']);
return decodeBytes(buf);
});

if (argv.parseAsUnknown) {
console.log(formatString(parseUnknown(new Parser(), 'tx', tx), argv));
return;
Expand All @@ -157,7 +168,7 @@ export const cmdParseTx = {

const parsed = getTxParser(argv).parse(tx, {
status: argv.fetchStatus && txid ? await fetchTransactionStatus(httpClient, txid, argv.network) : undefined,
prevOutputs: argv.fetchInputs ? await fetchPrevOutputs(httpClient, tx) : undefined,
prevOutputs: argv.fetchInputs ? await fetchPrevOutputs(httpClient, tx) : getPrevOutputsFromPrevTxs(tx, prevTxs),
prevOutputSpends: argv.fetchSpends ? await fetchPrevOutputSpends(httpClient, tx) : undefined,
outputSpends:
argv.fetchSpends && tx instanceof utxolib.bitgo.UtxoTransaction
Expand Down
47 changes: 47 additions & 0 deletions modules/utxo-bin/src/prevTx.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import * as utxolib from '@bitgo/utxo-lib';
import { ParserTx } from './ParserTx';

function getPrevOutForOutpoint(outpoint: utxolib.bitgo.TxOutPoint, prevTx: ParserTx) {
if (prevTx instanceof utxolib.bitgo.UtxoTransaction) {
const hash = prevTx.getId();
if (hash !== outpoint.txid) {
return undefined;
}
const out = prevTx.outs[outpoint.vout];
if (!out) {
throw new Error(`vout ${outpoint.vout} not found in prevTx ${hash}`);
}
return out;
}

if (prevTx instanceof utxolib.bitgo.UtxoPsbt) {
throw new Error(`not implemented for Psbt yet`);
}

throw new Error(`unknown tx type`);
}

export function getPrevOutputsFromPrevTxs(tx: ParserTx, prevTxs: ParserTx[]): utxolib.TxOutput<bigint>[] | undefined {
if (prevTxs.length === 0) {
return undefined;
}
if (tx instanceof utxolib.bitgo.UtxoTransaction) {
const outpoints = tx.ins.map((i) => utxolib.bitgo.getOutputIdForInput(i));
return outpoints.map((o) => {
const matches = prevTxs.flatMap((t) => getPrevOutForOutpoint(o, t));
if (matches.length === 0) {
throw new Error(`no prevTx found for input ${o.txid}:${o.vout}`);
}
if (matches.length > 1) {
throw new Error(`more than one prevTx found for input ${o.txid}:${o.vout}`);
}
return matches[0] as utxolib.TxOutput<bigint>;
});
}

if (tx instanceof utxolib.bitgo.UtxoPsbt) {
throw new Error(`not implemented for Psbt yet`);
}

throw new Error(`unknown tx type`);
}
Loading