PHP WebShell
Текущая директория: /opt/BitGoJS/modules/utxo-bin/src
Просмотр файла: InputParser.ts
import * as utxolib from '@bitgo/utxo-lib';
import { script, ScriptSignature } from 'bitcoinjs-lib';
import { Parser, ParserNode } from './Parser';
import { getParserTxInputProperties, getPrevOut, ParserTx, ParserTxInput } from './ParserTx';
import { getHollowSpendMessage, HollowSegwitSpend, parseHollowSegwitSpend } from './hollowSegwitSpend';
import { isHighS } from './ecdsa';
import { ChainInfo } from './TxParser';
import { OutputParser } from './OutputParser';
import { parseUnknown } from './parseUnknown';
import { ScriptParser } from './ScriptParser';
type ParsedSignatureScript =
| utxolib.bitgo.ParsedSignatureScriptP2ms
| utxolib.bitgo.ParsedSignatureScriptP2shP2pk
| utxolib.bitgo.ParsedSignatureScriptTaproot
| utxolib.bitgo.ParsedPsbtP2ms
| utxolib.bitgo.ParsedPsbtP2shP2pk
| utxolib.bitgo.ParsedPsbtTaproot
| HollowSegwitSpend;
function getOutputId(v: { hash: Buffer; index: number }): string {
return utxolib.bitgo.formatOutputId(utxolib.bitgo.getOutputIdForInput(v));
}
function parseSignatureScript(tx: ParserTx, inputIndex: number, network: utxolib.Network): ParsedSignatureScript {
if (tx instanceof utxolib.bitgo.UtxoTransaction) {
return (
parseHollowSegwitSpend(tx.ins[inputIndex], network) || utxolib.bitgo.parseSignatureScript(tx.ins[inputIndex])
);
}
return utxolib.bitgo.parsePsbtInput(tx.data.inputs[inputIndex]);
}
function toBufferUInt32BE(n: number): Buffer {
const buf = Buffer.alloc(4);
buf.writeUInt32LE(n, 0);
return buf;
}
export class InputParser extends Parser {
private input: ParserTxInput;
constructor(
private txid: string,
private tx: ParserTx,
private inputIndex: number,
private chainInfo: ChainInfo,
private params: {
parseOutputScript: boolean;
parseScriptAsm: boolean;
parseScriptData: boolean;
parseSignatureData: {
script: boolean;
ecdsa: boolean;
schnorr: boolean;
};
parseError?: 'throw' | 'continue';
}
) {
super({ parseError: params.parseError });
if (tx instanceof utxolib.bitgo.UtxoTransaction) {
this.input = tx.ins[inputIndex];
} else if (tx instanceof utxolib.bitgo.UtxoPsbt) {
this.input = tx.txInputs[inputIndex];
} else {
throw new Error('unknown transaction type');
}
}
parseScriptAsm(type: string, part: Buffer, i: number, n: number): string | undefined {
if (
(type === 'taproot' && i === n - 2) ||
((type === 'scripthash' || type === 'witnessscripthash') && i === n - 1)
) {
return ScriptParser.toASM(part);
}
}
parseScriptParts(type: string, parts: Array<Buffer | number>): ParserNode[] {
return parts.map((v, i) => {
let asmNode: ParserNode | undefined;
if (this.params.parseScriptAsm && Buffer.isBuffer(v)) {
const parsed = this.parseScriptAsm(type, v, i, parts.length);
asmNode = parsed ? this.node('asm', parsed) : undefined;
}
return this.node(i, this.params.parseScriptData ? v : undefined, asmNode ? [asmNode] : undefined);
});
}
parseInputScript(buffer: Buffer | undefined): ParserNode {
let value;
let nodes;
if (buffer && buffer.length && this.params.parseSignatureData.script) {
const type = utxolib.classify.input(buffer, true) ?? 'unknown';
const decompiled = utxolib.script.decompile(buffer);
if (decompiled) {
nodes = this.parseScriptParts(type, decompiled);
}
value = type;
} else {
value = buffer;
}
return this.node('scriptSig', value, nodes);
}
parseWitness(script: Buffer[] | undefined): ParserNode {
if (!script || script.length === 0) {
return this.node('witness', '[]');
}
const type = utxolib.classify.witness(script, true) ?? 'unknown';
return this.node('witness', type, this.params.parseScriptData ? this.parseScriptParts(type, script) : undefined);
}
parsePubkeys(parsed: ParsedSignatureScript): ParserNode {
if ('publicKeys' in parsed) {
return this.node(
'pubkeys',
parsed.publicKeys.length,
parsed.publicKeys.map((k, i) => this.node(i, k))
);
} else {
return this.node('pubkeys', '[]');
}
}
parseSignatureBuffer(
type:
| utxolib.bitgo.outputScripts.ScriptType2Of3
| utxolib.bitgo.ParsedScriptType2Of3
| utxolib.bitgo.outputScripts.ScriptTypeP2shP2pk,
buf: Buffer | 0,
signerIndex: number | undefined
): ParserNode[] {
if (buf === 0) {
return [this.node('type', 'placeholder (0)')];
}
if (buf.length === 0) {
return [this.node('type', 'placeholder (empty Buffer)')];
}
const nodes = [this.node('bytes', buf)];
if (signerIndex !== undefined) {
nodes.push(this.node('valid', 0 <= signerIndex));
if (0 <= signerIndex) {
nodes.push(this.node('signedBy', ['user', 'backup', 'bitgo'][signerIndex]));
}
}
if (type === 'taprootScriptPathSpend' || type === 'taprootKeyPathSpend') {
// TODO
} else {
const { signature, hashType } = ScriptSignature.decode(buf);
const r = signature.subarray(0, 32);
const s = signature.subarray(32);
if (r.length !== 32 || s.length !== 32) {
throw new Error(`invalid scalar length`);
}
nodes.push(
this.node('isCanonical', script.isCanonicalScriptSignature(buf)),
this.node('hashType', hashType),
this.node('r', r),
this.node('s', s),
this.node('highS', isHighS(s))
);
}
return nodes;
}
parseSignaturesWithSigners(
parsed: {
scriptType:
| utxolib.bitgo.outputScripts.ScriptType2Of3
| utxolib.bitgo.outputScripts.ScriptTypeP2shP2pk
| utxolib.bitgo.ParsedScriptType2Of3;
signatures: (Buffer | 0)[];
},
signedByLabels: string[] | undefined,
signerIndex: number[] | undefined
): ParserNode[] {
const nodes = signedByLabels ? [this.node('signed by', `[${signedByLabels.join(', ')}]`)] : [];
if (this.params.parseSignatureData.ecdsa || this.params.parseSignatureData.schnorr) {
nodes.push(
...parsed.signatures.map((s: Buffer | 0, i: number) => {
try {
return this.node(
i,
undefined,
this.parseSignatureBuffer(parsed.scriptType, s, signerIndex ? signerIndex[i] : undefined)
);
} catch (e) {
return this.node(i, undefined, [this.node('buf', s), this.handleParseError(e)]);
}
})
);
}
return nodes;
}
parseSignatures(parsed: ParsedSignatureScript): ParserNode {
const nodes: ParserNode[] = [];
if (
'signatures' in parsed &&
'publicKeys' in parsed &&
'scriptType' in parsed &&
parsed.signatures !== undefined &&
parsed.scriptType !== undefined
) {
if (this.tx instanceof utxolib.bitgo.UtxoTransaction && this.chainInfo.prevOutputs) {
const signedBy = utxolib.bitgo.getSignaturesWithPublicKeys(
this.tx,
this.inputIndex,
this.chainInfo.prevOutputs,
parsed.publicKeys
);
try {
nodes.push(
...this.parseSignaturesWithSigners(
parsed,
signedBy.flatMap((v, i) => (v ? [i.toString()] : [])),
parsed.signatures.map((k: Buffer | 0) => (k === 0 ? -1 : signedBy.indexOf(k)))
)
);
} catch (e) {
nodes.push(this.node('parseSignaturesWithSigners', undefined, [this.handleParseError(e)]));
}
}
if (this.tx instanceof utxolib.bitgo.UtxoPsbt) {
let signedByLabels: string[] | undefined;
if (parsed.publicKeys && Array.isArray(parsed.publicKeys)) {
const psbt = this.tx;
signedByLabels = parsed.publicKeys.flatMap((k: Buffer, i) =>
Buffer.isBuffer(k) && psbt.validateSignaturesOfInputCommon(this.inputIndex, k) ? [i.toString()] : []
);
}
// TODO: the current UtxoPsbt API does not allow us to determine which signer created which signature
const signerIndex = undefined;
nodes.push(...this.parseSignaturesWithSigners(parsed, signedByLabels, signerIndex));
}
return this.node(
'signatures',
parsed.signatures
.map((s: Buffer | 0) =>
utxolib.bitgo.isPlaceholderSignature(s) ? '[]' : Buffer.isBuffer(s) ? `[${s.length}byte]` : `[${s}]`
)
.join(' '),
nodes
);
} else if ('signatures' in parsed) {
return this.node(
'signatures',
undefined,
(parsed.signatures ?? []).map((s: Buffer | 0, i: number) => this.node(i, s))
);
} else {
return this.node('signatures', undefined);
}
}
parseSigScriptWithType(parsed: ParsedSignatureScript): ParserNode {
if (
parsed.scriptType &&
(utxolib.bitgo.outputScripts.isScriptType2Of3(parsed.scriptType) ||
parsed.scriptType === 'taprootScriptPathSpend' ||
parsed.scriptType === 'taprootKeyPathSpend')
) {
return this.node('sigScript', parsed.scriptType, [this.parsePubkeys(parsed), this.parseSignatures(parsed)]);
}
if (parsed.scriptType === 'p2shP2wshHollow' || parsed.scriptType === 'p2shP2wpkhHollow') {
return this.node('sigScript', parsed.scriptType, [this.node('info', getHollowSpendMessage())]);
}
return this.node('sigScript', parsed.scriptType ?? 'unknown');
}
parseSigScript(): ParserNode {
try {
return this.parseSigScriptWithType(parseSignatureScript(this.tx, this.inputIndex, this.tx.network));
} catch (e) {
return this.node('sigScript', undefined, [this.handleParseError(e)]);
}
}
parsePrevOut(prevOutput: utxolib.TxOutput<bigint> | utxolib.bitgo.PsbtInput): ParserNode[] {
let script: Buffer;
let value: bigint;
if ('script' in prevOutput && prevOutput.script) {
({ script, value } = prevOutput);
} else if ('witnessUtxo' in prevOutput || 'nonWitnessUtxo' in prevOutput) {
if (!(this.tx instanceof utxolib.bitgo.UtxoPsbt)) {
throw new Error('invalid state');
}
const result = getPrevOut(prevOutput, this.tx.txInputs[this.inputIndex], this.tx.network);
if (!result) {
return [this.node('script', 'unknown'), this.node('value', 'unknown')];
}
({ script, value } = result);
} else {
throw new Error('invalid prevOutput');
}
let address;
try {
address = utxolib.address.fromOutputScript(script, this.tx.network);
} catch (e) {
address = '(error)';
}
return [
this.node('value', Number(value) / 1e8),
this.node('pubScript', script, address ? [this.node('address', address)] : undefined),
];
}
parsePsbtInput(input: utxolib.bitgo.PsbtInput): ParserNode[] {
return Object.entries({
musig2Participants: utxolib.bitgo.musig2.parsePsbtMusig2Participants(input),
musig2Nonces: utxolib.bitgo.musig2.parsePsbtMusig2Nonces(input),
musig2PartialSignatures: utxolib.bitgo.musig2.parsePsbtMusig2PartialSigs(input),
}).flatMap(([key, value]) => (value ? [parseUnknown(this, key, value)] : []));
}
parseInput(): ParserNode {
const psbtInput = this.tx instanceof utxolib.bitgo.UtxoPsbt ? this.tx.data.inputs[this.inputIndex] : undefined;
const { txid: prevTxid, vout, sequence, script, witness } = getParserTxInputProperties(this.input, psbtInput);
const prevOutput = this.chainInfo.prevOutputs?.[this.inputIndex];
return this.node(this.inputIndex, getOutputId(this.input), [
this.node('sequence', sequence === undefined ? undefined : toBufferUInt32BE(sequence)),
this.parseInputScript(script),
this.parseWitness(witness),
this.parseSigScript(),
...(psbtInput ? this.parsePrevOut(psbtInput) : []),
...(prevOutput
? new OutputParser(
this.tx.network,
prevTxid,
vout,
prevOutput,
this.chainInfo,
this.params
).parsePrevOutputSpend({
conflict: true,
})
: []),
...(psbtInput ? this.parsePsbtInput(psbtInput) : []),
]);
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!