Add some tests and an input duplicate checker
This commit is contained in:
parent
93e1661c6c
commit
8d52ce1668
5 changed files with 228 additions and 36 deletions
ts_src
|
@ -26,6 +26,7 @@ export class Psbt extends PsbtBase {
|
|||
checkTxEmpty(tx);
|
||||
const psbt = new this() as Psbt;
|
||||
psbt.__TX = tx;
|
||||
checkTxForDupeIns(tx, psbt.__CACHE);
|
||||
let inputCount = tx.ins.length;
|
||||
let outputCount = tx.outs.length;
|
||||
while (inputCount > 0) {
|
||||
|
@ -62,11 +63,13 @@ export class Psbt extends PsbtBase {
|
|||
};
|
||||
const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt;
|
||||
psbt.__TX = tx!;
|
||||
checkTxForDupeIns(tx!, psbt.__CACHE);
|
||||
return psbt as InstanceType<T>;
|
||||
}
|
||||
private __NON_WITNESS_UTXO_CACHE = {
|
||||
private __CACHE = {
|
||||
__NON_WITNESS_UTXO_TX_CACHE: [] as Transaction[],
|
||||
__NON_WITNESS_UTXO_BUF_CACHE: [] as Buffer[],
|
||||
__TX_IN_CACHE: {} as { [index: string]: number },
|
||||
};
|
||||
private __TX: Transaction;
|
||||
private __TX_BUF_CACHE?: Buffer;
|
||||
|
@ -113,10 +116,14 @@ export class Psbt extends PsbtBase {
|
|||
dpew(this, '__EXTRACTED_TX', false, true);
|
||||
dpew(this, '__FEE_RATE', false, true);
|
||||
dpew(this, '__TX_BUF_CACHE', false, true);
|
||||
dpew(this, '__NON_WITNESS_UTXO_CACHE', false, true);
|
||||
dpew(this, '__CACHE', false, true);
|
||||
dpew(this, 'opts', false, true);
|
||||
}
|
||||
|
||||
get inputCount(): number {
|
||||
return this.inputs.length;
|
||||
}
|
||||
|
||||
setMaximumFeeRate(satoshiPerByte: number): void {
|
||||
check32Bit(satoshiPerByte); // 42.9 BTC per byte IS excessive... so throw
|
||||
this.opts.maximumFeeRate = satoshiPerByte;
|
||||
|
@ -172,9 +179,13 @@ export class Psbt extends PsbtBase {
|
|||
const prevHash = Buffer.isBuffer(_inputData.hash)
|
||||
? _inputData.hash
|
||||
: reverseBuffer(Buffer.from(_inputData.hash, 'hex'));
|
||||
|
||||
// Check if input already exists in cache.
|
||||
const input = { hash: prevHash, index: _inputData.index };
|
||||
checkTxInputCache(self.__CACHE, input);
|
||||
|
||||
self.__TX.ins.push({
|
||||
hash: prevHash,
|
||||
index: _inputData.index,
|
||||
...input,
|
||||
script: Buffer.alloc(0),
|
||||
sequence: _inputData.sequence || Transaction.DEFAULT_SEQUENCE,
|
||||
witness: [],
|
||||
|
@ -227,7 +238,7 @@ export class Psbt extends PsbtBase {
|
|||
): this {
|
||||
super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
|
||||
const input = this.inputs[inputIndex];
|
||||
addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, inputIndex);
|
||||
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -285,9 +296,9 @@ export class Psbt extends PsbtBase {
|
|||
if (input.witnessUtxo) {
|
||||
inputAmount += input.witnessUtxo.value;
|
||||
} else if (input.nonWitnessUtxo) {
|
||||
const cache = this.__NON_WITNESS_UTXO_CACHE;
|
||||
const cache = this.__CACHE;
|
||||
if (!cache.__NON_WITNESS_UTXO_TX_CACHE[idx]) {
|
||||
addNonWitnessTxCache(this.__NON_WITNESS_UTXO_CACHE, input, idx);
|
||||
addNonWitnessTxCache(this.__CACHE, input, idx);
|
||||
}
|
||||
const vout = this.__TX.ins[idx].index;
|
||||
const out = cache.__NON_WITNESS_UTXO_TX_CACHE[idx].outs[vout] as Output;
|
||||
|
@ -327,7 +338,7 @@ export class Psbt extends PsbtBase {
|
|||
inputIndex,
|
||||
input,
|
||||
this.__TX,
|
||||
this.__NON_WITNESS_UTXO_CACHE,
|
||||
this.__CACHE,
|
||||
);
|
||||
if (!script) return false;
|
||||
|
||||
|
@ -361,7 +372,7 @@ export class Psbt extends PsbtBase {
|
|||
inputIndex,
|
||||
keyPair.publicKey,
|
||||
this.__TX,
|
||||
this.__NON_WITNESS_UTXO_CACHE,
|
||||
this.__CACHE,
|
||||
);
|
||||
|
||||
const partialSig = {
|
||||
|
@ -382,7 +393,7 @@ export class Psbt extends PsbtBase {
|
|||
inputIndex,
|
||||
keyPair.publicKey,
|
||||
this.__TX,
|
||||
this.__NON_WITNESS_UTXO_CACHE,
|
||||
this.__CACHE,
|
||||
);
|
||||
|
||||
Promise.resolve(keyPair.sign(hash)).then(signature => {
|
||||
|
@ -409,9 +420,10 @@ export class Psbt extends PsbtBase {
|
|||
//
|
||||
//
|
||||
|
||||
interface NonWitnessUtxoCache {
|
||||
interface PsbtCache {
|
||||
__NON_WITNESS_UTXO_TX_CACHE: Transaction[];
|
||||
__NON_WITNESS_UTXO_BUF_CACHE: Buffer[];
|
||||
__TX_IN_CACHE: { [index: string]: number };
|
||||
}
|
||||
|
||||
interface PsbtOptsOptional {
|
||||
|
@ -430,7 +442,7 @@ const DEFAULT_OPTS = {
|
|||
};
|
||||
|
||||
function addNonWitnessTxCache(
|
||||
cache: NonWitnessUtxoCache,
|
||||
cache: PsbtCache,
|
||||
input: PsbtInput,
|
||||
inputIndex: number,
|
||||
): void {
|
||||
|
@ -460,6 +472,22 @@ function addNonWitnessTxCache(
|
|||
});
|
||||
}
|
||||
|
||||
function checkTxForDupeIns(tx: Transaction, cache: PsbtCache): void {
|
||||
tx.ins.forEach(input => {
|
||||
checkTxInputCache(cache, input);
|
||||
});
|
||||
}
|
||||
|
||||
function checkTxInputCache(
|
||||
cache: PsbtCache,
|
||||
input: { hash: Buffer; index: number },
|
||||
): void {
|
||||
const key =
|
||||
reverseBuffer(Buffer.from(input.hash)).toString('hex') + ':' + input.index;
|
||||
if (cache.__TX_IN_CACHE[key]) throw new Error('Duplicate input detected.');
|
||||
cache.__TX_IN_CACHE[key] = 1;
|
||||
}
|
||||
|
||||
function isFinalized(input: PsbtInput): boolean {
|
||||
return !!input.finalScriptSig || !!input.finalScriptWitness;
|
||||
}
|
||||
|
@ -469,7 +497,7 @@ function getHashAndSighashType(
|
|||
inputIndex: number,
|
||||
pubkey: Buffer,
|
||||
unsignedTx: Transaction,
|
||||
cache: NonWitnessUtxoCache,
|
||||
cache: PsbtCache,
|
||||
): {
|
||||
hash: Buffer;
|
||||
sighashType: number;
|
||||
|
@ -630,7 +658,7 @@ const getHashForSig = (
|
|||
inputIndex: number,
|
||||
input: PsbtInput,
|
||||
unsignedTx: Transaction,
|
||||
cache: NonWitnessUtxoCache,
|
||||
cache: PsbtCache,
|
||||
): HashForSigData => {
|
||||
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
||||
let hash: Buffer;
|
||||
|
@ -780,7 +808,7 @@ function getScriptFromInput(
|
|||
inputIndex: number,
|
||||
input: PsbtInput,
|
||||
unsignedTx: Transaction,
|
||||
cache: NonWitnessUtxoCache,
|
||||
cache: PsbtCache,
|
||||
): GetScriptReturn {
|
||||
const res: GetScriptReturn = {
|
||||
script: null,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue