PHP WebShell
Текущая директория: /opt/BitGoJS/modules/utxo-lib/test
Просмотр файла: transaction_util.ts
import { BIP32Interface } from 'bip32';
import * as assert from 'assert';
import { TxOutput } from 'bitcoinjs-lib';
import { networks, Network } from '../src';
import {
createOutputScript2of3,
createOutputScriptP2shP2pk,
isScriptType2Of3,
ScriptType2Of3,
} from '../src/bitgo/outputScripts';
import {
isTriple,
createPsbtFromBuffer,
createPsbtFromTransaction,
createTransactionBuilderForNetwork,
createTransactionBuilderFromTransaction,
createTransactionFromBuffer,
signInput2Of3,
signInputP2shP2pk,
TxOutPoint,
UtxoTransaction,
UtxoTransactionBuilder,
PrevOutput,
toTNumber,
UtxoPsbt,
} from '../src/bitgo';
import { KeyTriple } from '../src/testutil';
import { createScriptPubKey } from './integration_local_rpc/generate/outputScripts.util';
import { fixtureKeys } from './integration_local_rpc/generate/fixtures';
export function getSignKeyCombinations(length: number): BIP32Interface[][] {
if (length === 0) {
return [];
}
if (length === 1) {
return fixtureKeys.map((k) => [k]);
}
return getSignKeyCombinations(length - 1)
.map((head) => fixtureKeys.filter((k) => !head.includes(k)).map((k) => [...head, k]))
.reduce((all, keys) => [...all, ...keys]);
}
export function parseTransactionRoundTrip<TNumber extends number | bigint, T extends UtxoTransaction<TNumber>>(
buf: Buffer,
network: Network,
{
inputs,
amountType = 'number',
version,
roundTripPsbt = true,
}: {
inputs?: (TxOutPoint & TxOutput<TNumber>)[];
amountType?: 'number' | 'bigint';
version?: number;
roundTripPsbt?: boolean;
} = {}
): T {
const tx = createTransactionFromBuffer<TNumber>(buf, network, { version, amountType });
assert.strictEqual(tx.byteLength(), buf.length);
assert.strictEqual(tx.toBuffer().toString('hex'), buf.toString('hex'));
// Test `Transaction.clone()` implementation
assert.strictEqual(tx.clone().toBuffer().toString('hex'), buf.toString('hex'));
if (inputs) {
const bigintTx = tx.clone<bigint>('bigint');
const bigintInputs = inputs.map((input) => ({ ...input, value: BigInt(input.value) }));
if (roundTripPsbt) {
// Test UtxoPsbt.fromTransaction() implementation
assert.strictEqual(
UtxoPsbt.fromTransaction(bigintTx, bigintInputs)
.finalizeAllInputs()
.extractTransaction()
.toBuffer()
.toString('hex'),
buf.toString('hex')
);
// Test UtxoPsbt.toBuffer() and UtxoPsbt.fromBuffer() implementation
assert.strictEqual(
createPsbtFromBuffer(createPsbtFromTransaction(bigintTx, bigintInputs).toBuffer(), network)
.finalizeAllInputs()
.extractTransaction()
.toBuffer()
.toString('hex'),
buf.toString('hex')
);
}
// Test `TransactionBuilder.fromTransaction()` implementation
assert.strictEqual(
createTransactionBuilderFromTransaction<TNumber>(tx, inputs).build().toBuffer().toString('hex'),
buf.toString('hex')
);
}
return tx as T;
}
export const defaultTestOutputAmount = 1e8;
export function mockTransactionId(v = 0xff): string {
return Buffer.alloc(32).fill(v).toString('hex');
}
export function getPrevOutput<TNumber extends number | bigint = number>(
scriptType: ScriptType2Of3 | 'p2shP2pk',
value: TNumber,
network: Network,
vout = 0,
{
keys = fixtureKeys,
prevTx,
}: {
keys?: KeyTriple;
prevTx?: UtxoTransaction<TNumber> | boolean;
} = {}
): PrevOutput<TNumber> {
const script = isScriptType2Of3(scriptType)
? createOutputScript2of3(
keys.map((k) => k.publicKey),
scriptType
).scriptPubKey
: createOutputScriptP2shP2pk(keys[0].publicKey).scriptPubKey;
if (prevTx === true) {
const txb = createTransactionBuilderForNetwork<TNumber>(network);
txb.addInput(Buffer.alloc(32).fill(1), 0);
txb.addOutput(script, value);
prevTx = txb.buildIncomplete();
}
return {
txid: prevTx ? prevTx.getId() : mockTransactionId(),
vout,
script,
value,
prevTx: prevTx ? prevTx.toBuffer() : undefined,
};
}
export function getPrevOutputs<TNumber extends number | bigint = number>(
scriptType: ScriptType2Of3 | 'p2shP2pk',
value: TNumber,
network: Network,
{ keys = fixtureKeys, prevTx }: { keys?: KeyTriple; prevTx?: boolean } = {}
): PrevOutput<TNumber>[] {
return [getPrevOutput<TNumber>(scriptType, value, network, 0, { keys, prevTx })];
}
export type HalfSigner = {
signer: BIP32Interface;
cosigner?: BIP32Interface;
};
type TransactionUtilBuildOptions<TNumber extends number | bigint> = {
amountType?: 'number' | 'bigint';
outputAmount?: number | bigint | string;
prevOutputs?: PrevOutput<TNumber>[];
};
export function getTransactionBuilder<TNumber extends number | bigint = number>(
keys: KeyTriple,
halfSigners: HalfSigner[],
scriptType: ScriptType2Of3 | 'p2shP2pk',
network: Network,
{
amountType = 'number',
outputAmount = defaultTestOutputAmount,
prevOutputs = getPrevOutputs<TNumber>(scriptType, toTNumber<TNumber>(outputAmount, amountType), network),
}: TransactionUtilBuildOptions<TNumber> = {}
): UtxoTransactionBuilder<TNumber> {
const txBuilder = createTransactionBuilderForNetwork<TNumber>(network);
prevOutputs.forEach(({ txid, vout }) => {
txBuilder.addInput(txid, vout);
});
const recipientScript = createScriptPubKey(fixtureKeys, 'p2pkh', networks.bitcoin);
txBuilder.addOutput(recipientScript, toTNumber<TNumber>(BigInt(outputAmount) - BigInt(1000), amountType));
const pubkeys = keys.map((k) => k.publicKey);
assert(isTriple(pubkeys));
prevOutputs.forEach(({ value }, vin) => {
halfSigners.forEach(({ signer, cosigner }) => {
if (scriptType === 'p2shP2pk') {
signInputP2shP2pk(txBuilder, vin, signer);
} else {
if (!cosigner) {
throw new Error(`must set cosigner`);
}
signInput2Of3(txBuilder, vin, scriptType as ScriptType2Of3, pubkeys, signer, cosigner.publicKey, value);
}
});
});
return txBuilder;
}
export function getUnsignedTransaction2Of3<TNumber extends number | bigint = number>(
keys: KeyTriple,
scriptType: ScriptType2Of3 | 'p2shP2pk',
network: Network,
params: TransactionUtilBuildOptions<TNumber> = {}
): UtxoTransaction<TNumber> {
return getTransactionBuilder<TNumber>(keys, [], scriptType, network, params).buildIncomplete();
}
export function getHalfSignedTransaction2Of3<TNumber extends number | bigint = number>(
keys: KeyTriple,
signer1: BIP32Interface,
signer2: BIP32Interface,
scriptType: ScriptType2Of3 | 'p2shP2pk',
network: Network,
opts: TransactionUtilBuildOptions<TNumber> = {}
): UtxoTransaction<TNumber> {
return getTransactionBuilder<TNumber>(
keys,
[{ signer: signer1, cosigner: signer2 }],
scriptType,
network,
opts
).buildIncomplete();
}
export function getFullSignedTransactionP2shP2pk<TNumber extends number | bigint = number>(
keys: KeyTriple,
signer1: BIP32Interface,
network: Network,
opts: TransactionUtilBuildOptions<TNumber> = {}
): UtxoTransaction<TNumber> {
return getTransactionBuilder<TNumber>(keys, [{ signer: signer1 }], 'p2shP2pk', network, opts).build();
}
export function getFullSignedTransaction2Of3<TNumber extends number | bigint = number>(
keys: KeyTriple,
signer1: BIP32Interface,
signer2: BIP32Interface,
scriptType: ScriptType2Of3 | 'p2shP2pk',
network: Network,
opts: TransactionUtilBuildOptions<TNumber> = {}
): UtxoTransaction<TNumber> {
return getTransactionBuilder<TNumber>(
keys,
[
{ signer: signer1, cosigner: signer2 },
{ signer: signer2, cosigner: signer1 },
],
scriptType,
network,
opts
).build();
}
export function getTransactionStages<TNumber extends number | bigint>(
keys: KeyTriple,
signer1: BIP32Interface,
signer2: BIP32Interface,
scriptType: ScriptType2Of3 | 'p2shP2pk',
network: Network,
opts: TransactionUtilBuildOptions<TNumber>
): {
unsigned: UtxoTransaction<TNumber>;
halfSigned: UtxoTransaction<TNumber>;
fullSigned: UtxoTransaction<TNumber>;
} {
const halfSigned = getHalfSignedTransaction2Of3(keys, signer1, signer2, scriptType, network, opts);
const fullSigned =
scriptType === 'p2shP2pk'
? halfSigned
: getFullSignedTransaction2Of3(keys, signer1, signer2, scriptType, network, opts);
return {
unsigned: getUnsignedTransaction2Of3(keys, scriptType, network, opts),
halfSigned,
fullSigned,
};
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!