PHP WebShell
Текущая директория: /opt/BitGoJS/modules/unspents/test/signedTx
Просмотр файла: txGen.ts
/* eslint-disable @typescript-eslint/ban-ts-comment */
import * as utxolib from '@bitgo/utxo-lib';
import { bip32, BIP32Interface } from '@bitgo/utxo-lib';
import _ from 'lodash';
import 'lodash.combinations';
import { Dimensions } from '../../src';
import {
InputScriptType,
TestUnspentType,
UnspentTypeOpReturn,
UnspentTypeP2shP2pk,
UnspentTypePubKeyHash,
UnspentTypeScript2of3,
} from '../testutils';
interface IUnspent {
scriptPubKey: Buffer;
redeemScript?: Buffer;
witnessScript?: Buffer;
value: number;
inputType: utxolib.bitgo.outputScripts.ScriptType;
}
function createUnspent(pubkeys: Buffer[], inputType: string, value: number): IUnspent {
let spendableScript;
const scriptType = inputType === 'taprootKeyPathSpend' ? 'p2trMusig2' : inputType;
if (scriptType === UnspentTypeP2shP2pk) {
spendableScript = utxolib.bitgo.outputScripts.createOutputScriptP2shP2pk(pubkeys[0]);
} else if (utxolib.bitgo.outputScripts.isScriptType2Of3(scriptType)) {
spendableScript = utxolib.bitgo.outputScripts.createOutputScript2of3(pubkeys, scriptType);
} else {
throw new Error(`unexpected inputType ${scriptType}`);
}
return {
...spendableScript,
value,
inputType: scriptType,
};
}
/**
*
* @param keys - Pubkeys to use for generating the address.
* If unspentType is one of UnspentTypePubKeyHash is used, the first key will be used.
* @param unspentType {String} - one of UnspentTypeScript2of3 or UnspentTypePubKeyHash
* @return {String} address
*/
export const createScriptPubKey = (keys: BIP32Interface[], unspentType: TestUnspentType): Buffer => {
const pubkeys = keys.map((key) => key.publicKey);
if (typeof unspentType === 'string' && unspentType in UnspentTypeScript2of3) {
return createUnspent(pubkeys, unspentType, 0).scriptPubKey;
}
const pkHash = utxolib.crypto.hash160(pubkeys[0]);
switch (unspentType) {
case UnspentTypePubKeyHash.p2pkh:
return utxolib.payments.p2pkh({ hash: pkHash }).output!;
case UnspentTypePubKeyHash.p2wpkh:
return utxolib.payments.p2wpkh({ hash: pkHash }).output!;
}
if (unspentType instanceof UnspentTypeOpReturn) {
const payload = Buffer.alloc(unspentType.size).fill(pubkeys[0]);
return utxolib.script.compile([0x6a, payload]);
}
throw new Error(`unsupported output type ${unspentType}`);
};
const createInputTx = (unspents: any[], inputValue: number) => {
const txInputBuilder = new utxolib.bitgo.UtxoTransactionBuilder(utxolib.networks.bitcoin);
txInputBuilder.addInput(Array(32).fill('01').join(''), 0);
unspents.forEach(({ scriptPubKey }) => txInputBuilder.addOutput(scriptPubKey, inputValue));
return txInputBuilder.buildIncomplete();
};
function signInput(
txBuilder: utxolib.bitgo.UtxoTransactionBuilder,
index: number,
walletKeys: BIP32Interface[],
unspent: IUnspent,
signKeys: BIP32Interface[] = unspent.inputType === 'p2shP2pk' ? [walletKeys[0]] : [walletKeys[0], walletKeys[2]]
) {
signKeys.forEach((keyPair) => {
if (unspent.inputType === 'p2shP2pk') {
utxolib.bitgo.signInputP2shP2pk(txBuilder, index, keyPair);
} else {
if (signKeys.length !== 2) {
throw new Error(`invalid signKeys length`);
}
const cosigner = keyPair === signKeys[0] ? signKeys[1] : signKeys[0];
utxolib.bitgo.signInput2Of3(
txBuilder,
index,
unspent.inputType,
walletKeys.map((k) => k.publicKey) as utxolib.bitgo.Triple<Buffer>,
keyPair,
cosigner.publicKey,
unspent.value
);
}
});
}
class TxCombo {
public unspents: IUnspent[];
public inputTx: any;
constructor(
public walletKeys: BIP32Interface[],
public inputTypes: string[],
public outputTypes: TestUnspentType[],
public expectedDims: Readonly<Dimensions> = Dimensions.ZERO,
public signKeys?: BIP32Interface[],
public inputValue: number = 10
) {
this.unspents = inputTypes.map((inputType) =>
createUnspent(
walletKeys.map((key) => key.publicKey),
inputType,
this.inputValue
)
);
this.inputTx = createInputTx(this.unspents, inputValue);
}
public getBuilderWithUnsignedTx(): utxolib.bitgo.UtxoTransactionBuilder {
const txBuilder = utxolib.bitgo.createTransactionBuilderForNetwork(utxolib.networks.bitcoin);
this.inputTx.outs.forEach(({}, i: number) => txBuilder.addInput(this.inputTx, i));
this.outputTypes.forEach((unspentType) =>
txBuilder.addOutput(createScriptPubKey(this.walletKeys, unspentType), this.inputValue)
);
return txBuilder;
}
public getUnsignedTx(): utxolib.bitgo.UtxoTransaction {
return this.getBuilderWithUnsignedTx().buildIncomplete();
}
public getSignedTx(): utxolib.Transaction {
const txBuilder = this.getBuilderWithUnsignedTx();
this.unspents.forEach((unspent, i) => {
signInput(txBuilder, i, this.walletKeys, unspent, this.signKeys);
});
return txBuilder.build();
}
}
const runCombinations = (
{
inputTypes,
maxNInputs,
outputTypes,
maxNOutputs,
}: {
inputTypes: InputScriptType[];
maxNInputs: number;
outputTypes: TestUnspentType[];
maxNOutputs: number;
},
callback: (inputCombo: InputScriptType[], outputCombo: TestUnspentType[]) => void
): void => {
// Create combinations of different input and output types. Length between 1 and 3.
const inputCombinations = _.flatten(
// @ts-ignore
[...Array(maxNInputs)].map((__, i) => _.combinations(inputTypes, i + 1))
);
const outputCombinations = _.flatten(
// @ts-ignore
[...Array(maxNOutputs)].map((__, i) => _.combinations(outputTypes, i + 1))
);
inputCombinations.forEach((inputTypeCombo) =>
outputCombinations.forEach((outputTypeCombo) => {
callback(inputTypeCombo, outputTypeCombo);
})
);
};
class Histogram {
public total = 0;
constructor(public map: Map<number, number> = new Map()) {}
public add(size: number): void {
this.map.set(size, (this.map.get(size) || 0) + 1);
this.total++;
}
public asSortedArray(): number[][] {
return [...this.map.entries()].sort(([a], [b]) => a - b);
}
public asFullSortedArray(): number[][] {
return _.range(this.getPercentile(0), this.getPercentile(1)).map((v) => [v, this.map.get(v) || 0]);
}
public getPercentile(p: number): number {
if (0 > p || p > 1) {
throw new Error(`p must be between 0 and 1`);
}
let sum = 0;
for (const [k, v] of this.asSortedArray()) {
sum += v;
if (sum / this.total >= p) {
return k;
}
}
throw new Error('could not find percentile');
}
public toString(): string {
const keys = [...this.map.keys()].sort((a, b) => a - b);
return `[${keys.map((k) => `[${k}, ${this.map.get(k)}]`).join(' ')}]`;
}
}
const getKeyTriplets = (prefix: string, count: number) =>
[...Array(count)].map((v, i) =>
[1, 2, 3].map((j) => bip32.fromSeed(Buffer.alloc(16, `${prefix}/${i}/${j}`), utxolib.networks.bitcoin))
);
/**
*
* Calls `callback` with a variety of signed txs, based on input parameters
* Callback arguments are
* inputType, inputCount, outputType, txs
* where `txs` implements `forEach()`
*
* @param inputTypes - input types to test
* @param nInputKeyTriplets - number of different input key triples to cycle through
* @param outputTypes - output types to test
* @param nOutputKeyTriplets - number of different output key triplets to cycle through
* @param callback
*/
const runSignedTransactions = (
{
inputTypes,
nInputKeyTriplets,
outputTypes,
nOutputKeyTriplets,
}: {
inputTypes: Array<{ inputType: string; count: number }>;
nInputKeyTriplets: number;
outputTypes: TestUnspentType[];
nOutputKeyTriplets: number;
},
callback: (inputType: string, inputCount: number, outputType: TestUnspentType, txs: any) => void
): void => {
const inputKeyTriplets = getKeyTriplets('test/input/', nInputKeyTriplets);
const outputKeyTriplets = getKeyTriplets('test/output/', nOutputKeyTriplets);
const outputValue = 1e8;
inputTypes.forEach(({ inputType, count: inputCount }) => {
const inputTxs = inputKeyTriplets.map((inputKeys) => {
const unspents = [...Array(inputCount)].map(() =>
createUnspent(
inputKeys.map((key) => key.publicKey),
inputType,
outputValue
)
);
const inputTx = createInputTx(unspents, outputValue);
return { inputKeys, unspents, inputTx };
});
outputTypes.forEach((outputType) => {
const outputs = outputKeyTriplets.map((outputKeys) => createScriptPubKey(outputKeys, outputType));
const txs = {
forEach(cb: (tx: utxolib.Transaction) => void) {
inputTxs.forEach(({ inputKeys, unspents, inputTx }) => {
outputs.forEach((scriptPubKey) => {
const txBuilder = utxolib.bitgo.createTransactionBuilderForNetwork(utxolib.networks.bitcoin);
inputTx.outs.forEach((v: any, i: number) => txBuilder.addInput(inputTx, i));
txBuilder.addOutput(scriptPubKey, outputValue);
unspents.forEach((unspent, i) => {
signInput(txBuilder, i, inputKeys, unspent);
});
cb(txBuilder.build());
});
});
},
};
callback(inputType, inputCount, outputType, txs);
});
});
};
export { TxCombo, Histogram, runCombinations, runSignedTransactions };
Выполнить команду
Для локальной разработки. Не используйте в интернете!