Browse Source

Composition over inheritance

psbt
junderw 6 years ago
parent
commit
1feef9569c
No known key found for this signature in database GPG Key ID: B256185D3A971908
  1. 6
      package-lock.json
  2. 2
      package.json
  3. 199
      src/psbt.js
  4. 12
      test/psbt.js
  5. 277
      ts_src/psbt.ts
  6. 36
      types/psbt.d.ts

6
package-lock.json

@ -200,9 +200,9 @@
} }
}, },
"bip174": { "bip174": {
"version": "0.0.14", "version": "0.0.15",
"resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.14.tgz", "resolved": "https://registry.npmjs.org/bip174/-/bip174-0.0.15.tgz",
"integrity": "sha512-v9cre0W4ZpAJS1v18WUJLE9yKdSZyenGpZBg7CXiZ5n35JPXganH92d4Yk8WXpRfbFZ4SMXTqKLEgpLPX1TmcA==" "integrity": "sha512-mK/s9p7i+PG7W2s2cAedNVk1NDZQn9wAoq1DlsS2+1zz5TXR3TRTzqRqm9BQtOXwbkxkhfLwlmsOjuiRdAYkdg=="
}, },
"bip32": { "bip32": {
"version": "2.0.3", "version": "2.0.3",

2
package.json

@ -47,7 +47,7 @@
"dependencies": { "dependencies": {
"@types/node": "10.12.18", "@types/node": "10.12.18",
"bech32": "^1.1.2", "bech32": "^1.1.2",
"bip174": "0.0.14", "bip174": "0.0.15",
"bip32": "^2.0.3", "bip32": "^2.0.3",
"bip66": "^1.1.0", "bip66": "^1.1.0",
"bitcoin-ops": "^1.4.0", "bitcoin-ops": "^1.4.0",

199
src/psbt.js

@ -15,9 +15,9 @@ const DEFAULT_OPTS = {
network: networks_1.bitcoin, network: networks_1.bitcoin,
maximumFeeRate: 5000, maximumFeeRate: 5000,
}; };
class Psbt extends bip174_1.Psbt { class Psbt {
constructor(opts = {}) { constructor(opts = {}, data = new bip174_1.Psbt()) {
super(); this.data = data;
this.__CACHE = { this.__CACHE = {
__NON_WITNESS_UTXO_TX_CACHE: [], __NON_WITNESS_UTXO_TX_CACHE: [],
__NON_WITNESS_UTXO_BUF_CACHE: [], __NON_WITNESS_UTXO_BUF_CACHE: [],
@ -27,11 +27,11 @@ class Psbt extends bip174_1.Psbt {
// set defaults // set defaults
this.opts = Object.assign({}, DEFAULT_OPTS, opts); this.opts = Object.assign({}, DEFAULT_OPTS, opts);
const c = this.__CACHE; const c = this.__CACHE;
c.__TX = transaction_1.Transaction.fromBuffer(this.globalMap.unsignedTx); c.__TX = transaction_1.Transaction.fromBuffer(data.globalMap.unsignedTx);
this.setVersion(2); if (this.data.inputs.length === 0) this.setVersion(2);
// set cache // set cache
delete this.globalMap.unsignedTx; delete data.globalMap.unsignedTx;
Object.defineProperty(this.globalMap, 'unsignedTx', { Object.defineProperty(data.globalMap, 'unsignedTx', {
enumerable: true, enumerable: true,
get() { get() {
const buf = c.__TX_BUF_CACHE; const buf = c.__TX_BUF_CACHE;
@ -42,8 +42,8 @@ class Psbt extends bip174_1.Psbt {
return c.__TX_BUF_CACHE; return c.__TX_BUF_CACHE;
} }
}, },
set(data) { set(_data) {
c.__TX_BUF_CACHE = data; c.__TX_BUF_CACHE = _data;
}, },
}); });
// Make data hidden when enumerating // Make data hidden when enumerating
@ -55,29 +55,38 @@ class Psbt extends bip174_1.Psbt {
dpew(this, '__CACHE', false, true); dpew(this, '__CACHE', false, true);
dpew(this, 'opts', false, true); dpew(this, 'opts', false, true);
} }
static fromTransaction(txBuf) { static fromTransaction(txBuf, opts = {}) {
const tx = transaction_1.Transaction.fromBuffer(txBuf); const tx = transaction_1.Transaction.fromBuffer(txBuf);
checkTxEmpty(tx); checkTxEmpty(tx);
const psbt = new this(); const psbtBase = new bip174_1.Psbt();
const psbt = new Psbt(opts, psbtBase);
psbt.__CACHE.__TX = tx; psbt.__CACHE.__TX = tx;
checkTxForDupeIns(tx, psbt.__CACHE); checkTxForDupeIns(tx, psbt.__CACHE);
let inputCount = tx.ins.length; let inputCount = tx.ins.length;
let outputCount = tx.outs.length; let outputCount = tx.outs.length;
while (inputCount > 0) { while (inputCount > 0) {
psbt.inputs.push({ psbtBase.inputs.push({
keyVals: [], unknownKeyVals: [],
}); });
inputCount--; inputCount--;
} }
while (outputCount > 0) { while (outputCount > 0) {
psbt.outputs.push({ psbtBase.outputs.push({
keyVals: [], unknownKeyVals: [],
}); });
outputCount--; outputCount--;
} }
return psbt; return psbt;
} }
static fromBuffer(buffer) { static fromBase64(data, opts = {}) {
const buffer = Buffer.from(data, 'base64');
return this.fromBuffer(buffer, opts);
}
static fromHex(data, opts = {}) {
const buffer = Buffer.from(data, 'hex');
return this.fromBuffer(buffer, opts);
}
static fromBuffer(buffer, opts = {}) {
let tx; let tx;
const txCountGetter = txBuf => { const txCountGetter = txBuf => {
tx = transaction_1.Transaction.fromBuffer(txBuf); tx = transaction_1.Transaction.fromBuffer(txBuf);
@ -87,17 +96,22 @@ class Psbt extends bip174_1.Psbt {
outputCount: tx.outs.length, outputCount: tx.outs.length,
}; };
}; };
const psbt = super.fromBuffer(buffer, txCountGetter); const psbtBase = bip174_1.Psbt.fromBuffer(buffer, txCountGetter);
const psbt = new Psbt(opts, psbtBase);
psbt.__CACHE.__TX = tx; psbt.__CACHE.__TX = tx;
checkTxForDupeIns(tx, psbt.__CACHE); checkTxForDupeIns(tx, psbt.__CACHE);
return psbt; return psbt;
} }
get inputCount() { get inputCount() {
return this.inputs.length; return this.data.inputs.length;
}
combine(...those) {
this.data.combine(...those.map(o => o.data));
return this;
} }
clone() { clone() {
// TODO: more efficient cloning // TODO: more efficient cloning
const res = Psbt.fromBuffer(this.toBuffer()); const res = Psbt.fromBuffer(this.data.toBuffer());
res.opts = JSON.parse(JSON.stringify(this.opts)); res.opts = JSON.parse(JSON.stringify(this.opts));
return res; return res;
} }
@ -107,7 +121,7 @@ class Psbt extends bip174_1.Psbt {
} }
setVersion(version) { setVersion(version) {
check32Bit(version); check32Bit(version);
checkInputsForPartialSig(this.inputs, 'setVersion'); checkInputsForPartialSig(this.data.inputs, 'setVersion');
const c = this.__CACHE; const c = this.__CACHE;
c.__TX.version = version; c.__TX.version = version;
c.__TX_BUF_CACHE = undefined; c.__TX_BUF_CACHE = undefined;
@ -116,7 +130,7 @@ class Psbt extends bip174_1.Psbt {
} }
setLocktime(locktime) { setLocktime(locktime) {
check32Bit(locktime); check32Bit(locktime);
checkInputsForPartialSig(this.inputs, 'setLocktime'); checkInputsForPartialSig(this.data.inputs, 'setLocktime');
const c = this.__CACHE; const c = this.__CACHE;
c.__TX.locktime = locktime; c.__TX.locktime = locktime;
c.__TX_BUF_CACHE = undefined; c.__TX_BUF_CACHE = undefined;
@ -125,7 +139,7 @@ class Psbt extends bip174_1.Psbt {
} }
setSequence(inputIndex, sequence) { setSequence(inputIndex, sequence) {
check32Bit(sequence); check32Bit(sequence);
checkInputsForPartialSig(this.inputs, 'setSequence'); checkInputsForPartialSig(this.data.inputs, 'setSequence');
const c = this.__CACHE; const c = this.__CACHE;
if (c.__TX.ins.length <= inputIndex) { if (c.__TX.ins.length <= inputIndex) {
throw new Error('Input index too high'); throw new Error('Input index too high');
@ -140,10 +154,15 @@ class Psbt extends bip174_1.Psbt {
return this; return this;
} }
addInput(inputData) { addInput(inputData) {
checkInputsForPartialSig(this.inputs, 'addInput'); checkInputsForPartialSig(this.data.inputs, 'addInput');
const c = this.__CACHE; const c = this.__CACHE;
const inputAdder = getInputAdder(c); const inputAdder = getInputAdder(c);
super.addInput(inputData, inputAdder); this.data.addInput(inputData, inputAdder);
const inputIndex = this.data.inputs.length - 1;
const input = this.data.inputs[inputIndex];
if (input.nonWitnessUtxo) {
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
}
c.__FEE_RATE = undefined; c.__FEE_RATE = undefined;
c.__EXTRACTED_TX = undefined; c.__EXTRACTED_TX = undefined;
return this; return this;
@ -153,7 +172,7 @@ class Psbt extends bip174_1.Psbt {
return this; return this;
} }
addOutput(outputData) { addOutput(outputData) {
checkInputsForPartialSig(this.inputs, 'addOutput'); checkInputsForPartialSig(this.data.inputs, 'addOutput');
const { address } = outputData; const { address } = outputData;
if (typeof address === 'string') { if (typeof address === 'string') {
const { network } = this.opts; const { network } = this.opts;
@ -162,30 +181,24 @@ class Psbt extends bip174_1.Psbt {
} }
const c = this.__CACHE; const c = this.__CACHE;
const outputAdder = getOutputAdder(c); const outputAdder = getOutputAdder(c);
super.addOutput(outputData, true, outputAdder); this.data.addOutput(outputData, outputAdder, true);
c.__FEE_RATE = undefined; c.__FEE_RATE = undefined;
c.__EXTRACTED_TX = undefined; c.__EXTRACTED_TX = undefined;
return this; return this;
} }
addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
const input = this.inputs[inputIndex];
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
return this;
}
extractTransaction(disableFeeCheck) { extractTransaction(disableFeeCheck) {
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
const c = this.__CACHE; const c = this.__CACHE;
if (!disableFeeCheck) { if (!disableFeeCheck) {
checkFees(this, c, this.opts); checkFees(this, c, this.opts);
} }
if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
const tx = c.__TX.clone(); const tx = c.__TX.clone();
inputFinalizeGetAmts(this.inputs, tx, c, true); inputFinalizeGetAmts(this.data.inputs, tx, c, true);
return tx; return tx;
} }
getFeeRate() { getFeeRate() {
if (!this.inputs.every(isFinalized)) if (!this.data.inputs.every(isFinalized))
throw new Error('PSBT must be finalized to calculate fee rate'); throw new Error('PSBT must be finalized to calculate fee rate');
const c = this.__CACHE; const c = this.__CACHE;
if (c.__FEE_RATE) return c.__FEE_RATE; if (c.__FEE_RATE) return c.__FEE_RATE;
@ -197,16 +210,16 @@ class Psbt extends bip174_1.Psbt {
} else { } else {
tx = c.__TX.clone(); tx = c.__TX.clone();
} }
inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize);
return c.__FEE_RATE; return c.__FEE_RATE;
} }
finalizeAllInputs() { finalizeAllInputs() {
utils_1.checkForInput(this.inputs, 0); // making sure we have at least one utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
range(this.inputs.length).forEach(idx => this.finalizeInput(idx)); range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
return this; return this;
} }
finalizeInput(inputIndex) { finalizeInput(inputIndex) {
const input = utils_1.checkForInput(this.inputs, inputIndex); const input = utils_1.checkForInput(this.data.inputs, inputIndex);
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex, inputIndex,
input, input,
@ -226,23 +239,23 @@ class Psbt extends bip174_1.Psbt {
isP2WSH, isP2WSH,
); );
if (finalScriptSig) if (finalScriptSig)
this.addFinalScriptSigToInput(inputIndex, finalScriptSig); this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
if (finalScriptWitness) if (finalScriptWitness)
this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
if (!finalScriptSig && !finalScriptWitness) if (!finalScriptSig && !finalScriptWitness)
throw new Error(`Unknown error finalizing input #${inputIndex}`); throw new Error(`Unknown error finalizing input #${inputIndex}`);
this.clearFinalizedInput(inputIndex); this.data.clearFinalizedInput(inputIndex);
return this; return this;
} }
validateAllSignatures() { validateAllSignatures() {
utils_1.checkForInput(this.inputs, 0); // making sure we have at least one utils_1.checkForInput(this.data.inputs, 0); // making sure we have at least one
const results = range(this.inputs.length).map(idx => const results = range(this.data.inputs.length).map(idx =>
this.validateSignatures(idx), this.validateSignatures(idx),
); );
return results.reduce((final, res) => res === true && final, true); return results.reduce((final, res) => res === true && final, true);
} }
validateSignatures(inputIndex, pubkey) { validateSignatures(inputIndex, pubkey) {
const input = this.inputs[inputIndex]; const input = this.data.inputs[inputIndex];
const partialSig = (input || {}).partialSig; const partialSig = (input || {}).partialSig;
if (!input || !partialSig || partialSig.length < 1) if (!input || !partialSig || partialSig.length < 1)
throw new Error('No signatures to validate'); throw new Error('No signatures to validate');
@ -280,7 +293,7 @@ class Psbt extends bip174_1.Psbt {
// as input information is added, then eventually // as input information is added, then eventually
// optimize this method. // optimize this method.
const results = []; const results = [];
for (const i of range(this.inputs.length)) { for (const i of range(this.data.inputs.length)) {
try { try {
this.signInput(i, keyPair, sighashTypes); this.signInput(i, keyPair, sighashTypes);
results.push(true); results.push(true);
@ -302,7 +315,7 @@ class Psbt extends bip174_1.Psbt {
// optimize this method. // optimize this method.
const results = []; const results = [];
const promises = []; const promises = [];
for (const [i] of this.inputs.entries()) { for (const [i] of this.data.inputs.entries()) {
promises.push( promises.push(
this.signInputAsync(i, keyPair, sighashTypes).then( this.signInputAsync(i, keyPair, sighashTypes).then(
() => { () => {
@ -330,7 +343,7 @@ class Psbt extends bip174_1.Psbt {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input'); throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.inputs, this.data.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.__CACHE, this.__CACHE,
@ -340,7 +353,8 @@ class Psbt extends bip174_1.Psbt {
pubkey: keyPair.publicKey, pubkey: keyPair.publicKey,
signature: bscript.signature.encode(keyPair.sign(hash), sighashType), signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
}; };
return this.addPartialSigToInput(inputIndex, partialSig); this.data.addPartialSigToInput(inputIndex, partialSig);
return this;
} }
signInputAsync( signInputAsync(
inputIndex, inputIndex,
@ -351,7 +365,7 @@ class Psbt extends bip174_1.Psbt {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input')); return reject(new Error('Need Signer to sign input'));
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.inputs, this.data.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.__CACHE, this.__CACHE,
@ -362,11 +376,94 @@ class Psbt extends bip174_1.Psbt {
pubkey: keyPair.publicKey, pubkey: keyPair.publicKey,
signature: bscript.signature.encode(signature, sighashType), signature: bscript.signature.encode(signature, sighashType),
}; };
this.addPartialSigToInput(inputIndex, partialSig); this.data.addPartialSigToInput(inputIndex, partialSig);
resolve(); resolve();
}); });
}); });
} }
toBuffer() {
return this.data.toBuffer();
}
toHex() {
return this.data.toHex();
}
toBase64() {
return this.data.toBase64();
}
addGlobalXpubToGlobal(globalXpub) {
this.data.addGlobalXpubToGlobal(globalXpub);
return this;
}
addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo) {
this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
const input = this.data.inputs[inputIndex];
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
return this;
}
addWitnessUtxoToInput(inputIndex, witnessUtxo) {
this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo);
return this;
}
addPartialSigToInput(inputIndex, partialSig) {
this.data.addPartialSigToInput(inputIndex, partialSig);
return this;
}
addSighashTypeToInput(inputIndex, sighashType) {
this.data.addSighashTypeToInput(inputIndex, sighashType);
return this;
}
addRedeemScriptToInput(inputIndex, redeemScript) {
this.data.addRedeemScriptToInput(inputIndex, redeemScript);
return this;
}
addWitnessScriptToInput(inputIndex, witnessScript) {
this.data.addWitnessScriptToInput(inputIndex, witnessScript);
return this;
}
addBip32DerivationToInput(inputIndex, bip32Derivation) {
this.data.addBip32DerivationToInput(inputIndex, bip32Derivation);
return this;
}
addFinalScriptSigToInput(inputIndex, finalScriptSig) {
this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
return this;
}
addFinalScriptWitnessToInput(inputIndex, finalScriptWitness) {
this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
return this;
}
addPorCommitmentToInput(inputIndex, porCommitment) {
this.data.addPorCommitmentToInput(inputIndex, porCommitment);
return this;
}
addRedeemScriptToOutput(outputIndex, redeemScript) {
this.data.addRedeemScriptToOutput(outputIndex, redeemScript);
return this;
}
addWitnessScriptToOutput(outputIndex, witnessScript) {
this.data.addWitnessScriptToOutput(outputIndex, witnessScript);
return this;
}
addBip32DerivationToOutput(outputIndex, bip32Derivation) {
this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation);
return this;
}
addUnknownKeyValToGlobal(keyVal) {
this.data.addUnknownKeyValToGlobal(keyVal);
return this;
}
addUnknownKeyValToInput(inputIndex, keyVal) {
this.data.addUnknownKeyValToInput(inputIndex, keyVal);
return this;
}
addUnknownKeyValToOutput(outputIndex, keyVal) {
this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
return this;
}
clearFinalizedInput(inputIndex) {
this.data.clearFinalizedInput(inputIndex);
return this;
}
} }
exports.Psbt = Psbt; exports.Psbt = Psbt;
function canFinalize(input, script, scriptType) { function canFinalize(input, script, scriptType) {

12
test/psbt.js

@ -143,8 +143,8 @@ describe(`Psbt`, () => {
assert.strictEqual(transaction1, f.transaction) assert.strictEqual(transaction1, f.transaction)
const psbt3 = Psbt.fromBase64(f.psbt) const psbt3 = Psbt.fromBase64(f.psbt)
delete psbt3.inputs[0].finalScriptSig delete psbt3.data.inputs[0].finalScriptSig
delete psbt3.inputs[0].finalScriptWitness delete psbt3.data.inputs[0].finalScriptWitness
assert.throws(() => { assert.throws(() => {
psbt3.extractTransaction() psbt3.extractTransaction()
}, new RegExp('Not finalized')) }, new RegExp('Not finalized'))
@ -414,7 +414,7 @@ describe(`Psbt`, () => {
assert.strictEqual(clone.toBase64(), psbt.toBase64()) assert.strictEqual(clone.toBase64(), psbt.toBase64())
assert.strictEqual(clone.toBase64(), notAClone.toBase64()) assert.strictEqual(clone.toBase64(), notAClone.toBase64())
assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
psbt.globalMap.unsignedTx[3] = 0xff psbt.data.globalMap.unsignedTx[3] = 0xff
assert.notStrictEqual(clone.toBase64(), psbt.toBase64()) assert.notStrictEqual(clone.toBase64(), psbt.toBase64())
assert.notStrictEqual(clone.toBase64(), notAClone.toBase64()) assert.notStrictEqual(clone.toBase64(), notAClone.toBase64())
assert.strictEqual(psbt.toBase64(), notAClone.toBase64()) assert.strictEqual(psbt.toBase64(), notAClone.toBase64())
@ -542,14 +542,14 @@ describe(`Psbt`, () => {
// Cache is populated // Cache is populated
psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo) psbt.addNonWitnessUtxoToInput(index, f.nonWitnessUtxo)
const value = psbt.inputs[index].nonWitnessUtxo const value = psbt.data.inputs[index].nonWitnessUtxo
assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value)) assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(value))
assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo)) assert.ok(psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index].equals(f.nonWitnessUtxo))
// Cache is rebuilt from internal transaction object when cleared // Cache is rebuilt from internal transaction object when cleared
psbt.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3]) psbt.data.inputs[index].nonWitnessUtxo = Buffer.from([1,2,3])
psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined psbt.__CACHE.__NON_WITNESS_UTXO_BUF_CACHE[index] = undefined
assert.ok(psbt.inputs[index].nonWitnessUtxo.equals(value)) assert.ok(psbt.data.inputs[index].nonWitnessUtxo.equals(value))
}) })
}) })
}) })

277
ts_src/psbt.ts

@ -1,11 +1,21 @@
import { Psbt as PsbtBase } from 'bip174'; import { Psbt as PsbtBase } from 'bip174';
import * as varuint from 'bip174/src/lib/converter/varint'; import * as varuint from 'bip174/src/lib/converter/varint';
import { import {
Bip32Derivation,
FinalScriptSig,
FinalScriptWitness,
GlobalXpub,
KeyValue,
NonWitnessUtxo, NonWitnessUtxo,
PartialSig, PartialSig,
PorCommitment,
PsbtInput, PsbtInput,
RedeemScript,
SighashType,
TransactionInput, TransactionInput,
TransactionOutput, TransactionOutput,
WitnessScript,
WitnessUtxo,
} from 'bip174/src/lib/interfaces'; } from 'bip174/src/lib/interfaces';
import { checkForInput } from 'bip174/src/lib/utils'; import { checkForInput } from 'bip174/src/lib/utils';
import { toOutputScript } from './address'; import { toOutputScript } from './address';
@ -26,37 +36,42 @@ const DEFAULT_OPTS: PsbtOpts = {
maximumFeeRate: 5000, // satoshi per byte maximumFeeRate: 5000, // satoshi per byte
}; };
export class Psbt extends PsbtBase { export class Psbt {
static fromTransaction<T extends typeof PsbtBase>( static fromTransaction(txBuf: Buffer, opts: PsbtOptsOptional = {}): Psbt {
this: T,
txBuf: Buffer,
): InstanceType<T> {
const tx = Transaction.fromBuffer(txBuf); const tx = Transaction.fromBuffer(txBuf);
checkTxEmpty(tx); checkTxEmpty(tx);
const psbt = new this() as Psbt; const psbtBase = new PsbtBase();
const psbt = new Psbt(opts, psbtBase);
psbt.__CACHE.__TX = tx; psbt.__CACHE.__TX = tx;
checkTxForDupeIns(tx, psbt.__CACHE); checkTxForDupeIns(tx, psbt.__CACHE);
let inputCount = tx.ins.length; let inputCount = tx.ins.length;
let outputCount = tx.outs.length; let outputCount = tx.outs.length;
while (inputCount > 0) { while (inputCount > 0) {
psbt.inputs.push({ psbtBase.inputs.push({
keyVals: [], unknownKeyVals: [],
}); });
inputCount--; inputCount--;
} }
while (outputCount > 0) { while (outputCount > 0) {
psbt.outputs.push({ psbtBase.outputs.push({
keyVals: [], unknownKeyVals: [],
}); });
outputCount--; outputCount--;
} }
return psbt as InstanceType<T>; return psbt;
}
static fromBase64(data: string, opts: PsbtOptsOptional = {}): Psbt {
const buffer = Buffer.from(data, 'base64');
return this.fromBuffer(buffer, opts);
} }
static fromBuffer<T extends typeof PsbtBase>( static fromHex(data: string, opts: PsbtOptsOptional = {}): Psbt {
this: T, const buffer = Buffer.from(data, 'hex');
buffer: Buffer, return this.fromBuffer(buffer, opts);
): InstanceType<T> { }
static fromBuffer(buffer: Buffer, opts: PsbtOptsOptional = {}): Psbt {
let tx: Transaction | undefined; let tx: Transaction | undefined;
const txCountGetter = ( const txCountGetter = (
txBuf: Buffer, txBuf: Buffer,
@ -71,10 +86,11 @@ export class Psbt extends PsbtBase {
outputCount: tx.outs.length, outputCount: tx.outs.length,
}; };
}; };
const psbt = super.fromBuffer(buffer, txCountGetter) as Psbt; const psbtBase = PsbtBase.fromBuffer(buffer, txCountGetter);
const psbt = new Psbt(opts, psbtBase);
psbt.__CACHE.__TX = tx!; psbt.__CACHE.__TX = tx!;
checkTxForDupeIns(tx!, psbt.__CACHE); checkTxForDupeIns(tx!, psbt.__CACHE);
return psbt as InstanceType<T>; return psbt;
} }
private __CACHE: PsbtCache = { private __CACHE: PsbtCache = {
@ -85,17 +101,19 @@ export class Psbt extends PsbtBase {
}; };
private opts: PsbtOpts; private opts: PsbtOpts;
constructor(opts: PsbtOptsOptional = {}) { constructor(
super(); opts: PsbtOptsOptional = {},
readonly data: PsbtBase = new PsbtBase(),
) {
// set defaults // set defaults
this.opts = Object.assign({}, DEFAULT_OPTS, opts); this.opts = Object.assign({}, DEFAULT_OPTS, opts);
const c = this.__CACHE; const c = this.__CACHE;
c.__TX = Transaction.fromBuffer(this.globalMap.unsignedTx!); c.__TX = Transaction.fromBuffer(data.globalMap.unsignedTx!);
this.setVersion(2); if (this.data.inputs.length === 0) this.setVersion(2);
// set cache // set cache
delete this.globalMap.unsignedTx; delete data.globalMap.unsignedTx;
Object.defineProperty(this.globalMap, 'unsignedTx', { Object.defineProperty(data.globalMap, 'unsignedTx', {
enumerable: true, enumerable: true,
get(): Buffer { get(): Buffer {
const buf = c.__TX_BUF_CACHE; const buf = c.__TX_BUF_CACHE;
@ -106,8 +124,8 @@ export class Psbt extends PsbtBase {
return c.__TX_BUF_CACHE; return c.__TX_BUF_CACHE;
} }
}, },
set(data: Buffer): void { set(_data: Buffer): void {
c.__TX_BUF_CACHE = data; c.__TX_BUF_CACHE = _data;
}, },
}); });
@ -127,12 +145,17 @@ export class Psbt extends PsbtBase {
} }
get inputCount(): number { get inputCount(): number {
return this.inputs.length; return this.data.inputs.length;
}
combine(...those: Psbt[]): this {
this.data.combine(...those.map(o => o.data));
return this;
} }
clone(): Psbt { clone(): Psbt {
// TODO: more efficient cloning // TODO: more efficient cloning
const res = Psbt.fromBuffer(this.toBuffer()); const res = Psbt.fromBuffer(this.data.toBuffer());
res.opts = JSON.parse(JSON.stringify(this.opts)); res.opts = JSON.parse(JSON.stringify(this.opts));
return res; return res;
} }
@ -144,7 +167,7 @@ export class Psbt extends PsbtBase {
setVersion(version: number): this { setVersion(version: number): this {
check32Bit(version); check32Bit(version);
checkInputsForPartialSig(this.inputs, 'setVersion'); checkInputsForPartialSig(this.data.inputs, 'setVersion');
const c = this.__CACHE; const c = this.__CACHE;
c.__TX.version = version; c.__TX.version = version;
c.__TX_BUF_CACHE = undefined; c.__TX_BUF_CACHE = undefined;
@ -154,7 +177,7 @@ export class Psbt extends PsbtBase {
setLocktime(locktime: number): this { setLocktime(locktime: number): this {
check32Bit(locktime); check32Bit(locktime);
checkInputsForPartialSig(this.inputs, 'setLocktime'); checkInputsForPartialSig(this.data.inputs, 'setLocktime');
const c = this.__CACHE; const c = this.__CACHE;
c.__TX.locktime = locktime; c.__TX.locktime = locktime;
c.__TX_BUF_CACHE = undefined; c.__TX_BUF_CACHE = undefined;
@ -164,7 +187,7 @@ export class Psbt extends PsbtBase {
setSequence(inputIndex: number, sequence: number): this { setSequence(inputIndex: number, sequence: number): this {
check32Bit(sequence); check32Bit(sequence);
checkInputsForPartialSig(this.inputs, 'setSequence'); checkInputsForPartialSig(this.data.inputs, 'setSequence');
const c = this.__CACHE; const c = this.__CACHE;
if (c.__TX.ins.length <= inputIndex) { if (c.__TX.ins.length <= inputIndex) {
throw new Error('Input index too high'); throw new Error('Input index too high');
@ -181,10 +204,16 @@ export class Psbt extends PsbtBase {
} }
addInput(inputData: TransactionInput): this { addInput(inputData: TransactionInput): this {
checkInputsForPartialSig(this.inputs, 'addInput'); checkInputsForPartialSig(this.data.inputs, 'addInput');
const c = this.__CACHE; const c = this.__CACHE;
const inputAdder = getInputAdder(c); const inputAdder = getInputAdder(c);
super.addInput(inputData, inputAdder); this.data.addInput(inputData, inputAdder);
const inputIndex = this.data.inputs.length - 1;
const input = this.data.inputs[inputIndex];
if (input.nonWitnessUtxo) {
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
}
c.__FEE_RATE = undefined; c.__FEE_RATE = undefined;
c.__EXTRACTED_TX = undefined; c.__EXTRACTED_TX = undefined;
return this; return this;
@ -196,7 +225,7 @@ export class Psbt extends PsbtBase {
} }
addOutput(outputData: TransactionOutput): this { addOutput(outputData: TransactionOutput): this {
checkInputsForPartialSig(this.inputs, 'addOutput'); checkInputsForPartialSig(this.data.inputs, 'addOutput');
const { address } = outputData as any; const { address } = outputData as any;
if (typeof address === 'string') { if (typeof address === 'string') {
const { network } = this.opts; const { network } = this.opts;
@ -205,36 +234,26 @@ export class Psbt extends PsbtBase {
} }
const c = this.__CACHE; const c = this.__CACHE;
const outputAdder = getOutputAdder(c); const outputAdder = getOutputAdder(c);
super.addOutput(outputData, true, outputAdder); this.data.addOutput(outputData, outputAdder, true);
c.__FEE_RATE = undefined; c.__FEE_RATE = undefined;
c.__EXTRACTED_TX = undefined; c.__EXTRACTED_TX = undefined;
return this; return this;
} }
addNonWitnessUtxoToInput(
inputIndex: number,
nonWitnessUtxo: NonWitnessUtxo,
): this {
super.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
const input = this.inputs[inputIndex];
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
return this;
}
extractTransaction(disableFeeCheck?: boolean): Transaction { extractTransaction(disableFeeCheck?: boolean): Transaction {
if (!this.inputs.every(isFinalized)) throw new Error('Not finalized'); if (!this.data.inputs.every(isFinalized)) throw new Error('Not finalized');
const c = this.__CACHE; const c = this.__CACHE;
if (!disableFeeCheck) { if (!disableFeeCheck) {
checkFees(this, c, this.opts); checkFees(this, c, this.opts);
} }
if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX; if (c.__EXTRACTED_TX) return c.__EXTRACTED_TX;
const tx = c.__TX.clone(); const tx = c.__TX.clone();
inputFinalizeGetAmts(this.inputs, tx, c, true); inputFinalizeGetAmts(this.data.inputs, tx, c, true);
return tx; return tx;
} }
getFeeRate(): number { getFeeRate(): number {
if (!this.inputs.every(isFinalized)) if (!this.data.inputs.every(isFinalized))
throw new Error('PSBT must be finalized to calculate fee rate'); throw new Error('PSBT must be finalized to calculate fee rate');
const c = this.__CACHE; const c = this.__CACHE;
if (c.__FEE_RATE) return c.__FEE_RATE; if (c.__FEE_RATE) return c.__FEE_RATE;
@ -246,18 +265,18 @@ export class Psbt extends PsbtBase {
} else { } else {
tx = c.__TX.clone(); tx = c.__TX.clone();
} }
inputFinalizeGetAmts(this.inputs, tx, c, mustFinalize); inputFinalizeGetAmts(this.data.inputs, tx, c, mustFinalize);
return c.__FEE_RATE!; return c.__FEE_RATE!;
} }
finalizeAllInputs(): this { finalizeAllInputs(): this {
checkForInput(this.inputs, 0); // making sure we have at least one checkForInput(this.data.inputs, 0); // making sure we have at least one
range(this.inputs.length).forEach(idx => this.finalizeInput(idx)); range(this.data.inputs.length).forEach(idx => this.finalizeInput(idx));
return this; return this;
} }
finalizeInput(inputIndex: number): this { finalizeInput(inputIndex: number): this {
const input = checkForInput(this.inputs, inputIndex); const input = checkForInput(this.data.inputs, inputIndex);
const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput( const { script, isP2SH, isP2WSH, isSegwit } = getScriptFromInput(
inputIndex, inputIndex,
input, input,
@ -281,26 +300,26 @@ export class Psbt extends PsbtBase {
); );
if (finalScriptSig) if (finalScriptSig)
this.addFinalScriptSigToInput(inputIndex, finalScriptSig); this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
if (finalScriptWitness) if (finalScriptWitness)
this.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness); this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
if (!finalScriptSig && !finalScriptWitness) if (!finalScriptSig && !finalScriptWitness)
throw new Error(`Unknown error finalizing input #${inputIndex}`); throw new Error(`Unknown error finalizing input #${inputIndex}`);
this.clearFinalizedInput(inputIndex); this.data.clearFinalizedInput(inputIndex);
return this; return this;
} }
validateAllSignatures(): boolean { validateAllSignatures(): boolean {
checkForInput(this.inputs, 0); // making sure we have at least one checkForInput(this.data.inputs, 0); // making sure we have at least one
const results = range(this.inputs.length).map(idx => const results = range(this.data.inputs.length).map(idx =>
this.validateSignatures(idx), this.validateSignatures(idx),
); );
return results.reduce((final, res) => res === true && final, true); return results.reduce((final, res) => res === true && final, true);
} }
validateSignatures(inputIndex: number, pubkey?: Buffer): boolean { validateSignatures(inputIndex: number, pubkey?: Buffer): boolean {
const input = this.inputs[inputIndex]; const input = this.data.inputs[inputIndex];
const partialSig = (input || {}).partialSig; const partialSig = (input || {}).partialSig;
if (!input || !partialSig || partialSig.length < 1) if (!input || !partialSig || partialSig.length < 1)
throw new Error('No signatures to validate'); throw new Error('No signatures to validate');
@ -343,7 +362,7 @@ export class Psbt extends PsbtBase {
// as input information is added, then eventually // as input information is added, then eventually
// optimize this method. // optimize this method.
const results: boolean[] = []; const results: boolean[] = [];
for (const i of range(this.inputs.length)) { for (const i of range(this.data.inputs.length)) {
try { try {
this.signInput(i, keyPair, sighashTypes); this.signInput(i, keyPair, sighashTypes);
results.push(true); results.push(true);
@ -371,7 +390,7 @@ export class Psbt extends PsbtBase {
// optimize this method. // optimize this method.
const results: boolean[] = []; const results: boolean[] = [];
const promises: Array<Promise<void>> = []; const promises: Array<Promise<void>> = [];
for (const [i] of this.inputs.entries()) { for (const [i] of this.data.inputs.entries()) {
promises.push( promises.push(
this.signInputAsync(i, keyPair, sighashTypes).then( this.signInputAsync(i, keyPair, sighashTypes).then(
() => { () => {
@ -401,7 +420,7 @@ export class Psbt extends PsbtBase {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input'); throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.inputs, this.data.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.__CACHE, this.__CACHE,
@ -413,7 +432,8 @@ export class Psbt extends PsbtBase {
signature: bscript.signature.encode(keyPair.sign(hash), sighashType), signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
}; };
return this.addPartialSigToInput(inputIndex, partialSig); this.data.addPartialSigToInput(inputIndex, partialSig);
return this;
} }
signInputAsync( signInputAsync(
@ -426,7 +446,7 @@ export class Psbt extends PsbtBase {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input')); return reject(new Error('Need Signer to sign input'));
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.inputs, this.data.inputs,
inputIndex, inputIndex,
keyPair.publicKey, keyPair.publicKey,
this.__CACHE, this.__CACHE,
@ -439,12 +459,143 @@ export class Psbt extends PsbtBase {
signature: bscript.signature.encode(signature, sighashType), signature: bscript.signature.encode(signature, sighashType),
}; };
this.addPartialSigToInput(inputIndex, partialSig); this.data.addPartialSigToInput(inputIndex, partialSig);
resolve(); resolve();
}); });
}, },
); );
} }
toBuffer(): Buffer {
return this.data.toBuffer();
}
toHex(): string {
return this.data.toHex();
}
toBase64(): string {
return this.data.toBase64();
}
addGlobalXpubToGlobal(globalXpub: GlobalXpub): this {
this.data.addGlobalXpubToGlobal(globalXpub);
return this;
}
addNonWitnessUtxoToInput(
inputIndex: number,
nonWitnessUtxo: NonWitnessUtxo,
): this {
this.data.addNonWitnessUtxoToInput(inputIndex, nonWitnessUtxo);
const input = this.data.inputs[inputIndex];
addNonWitnessTxCache(this.__CACHE, input, inputIndex);
return this;
}
addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this {
this.data.addWitnessUtxoToInput(inputIndex, witnessUtxo);
return this;
}
addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this {
this.data.addPartialSigToInput(inputIndex, partialSig);
return this;
}
addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this {
this.data.addSighashTypeToInput(inputIndex, sighashType);
return this;
}
addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this {
this.data.addRedeemScriptToInput(inputIndex, redeemScript);
return this;
}
addWitnessScriptToInput(
inputIndex: number,
witnessScript: WitnessScript,
): this {
this.data.addWitnessScriptToInput(inputIndex, witnessScript);
return this;
}
addBip32DerivationToInput(
inputIndex: number,
bip32Derivation: Bip32Derivation,
): this {
this.data.addBip32DerivationToInput(inputIndex, bip32Derivation);
return this;
}
addFinalScriptSigToInput(
inputIndex: number,
finalScriptSig: FinalScriptSig,
): this {
this.data.addFinalScriptSigToInput(inputIndex, finalScriptSig);
return this;
}
addFinalScriptWitnessToInput(
inputIndex: number,
finalScriptWitness: FinalScriptWitness,
): this {
this.data.addFinalScriptWitnessToInput(inputIndex, finalScriptWitness);
return this;
}
addPorCommitmentToInput(
inputIndex: number,
porCommitment: PorCommitment,
): this {
this.data.addPorCommitmentToInput(inputIndex, porCommitment);
return this;
}
addRedeemScriptToOutput(
outputIndex: number,
redeemScript: RedeemScript,
): this {
this.data.addRedeemScriptToOutput(outputIndex, redeemScript);
return this;
}
addWitnessScriptToOutput(
outputIndex: number,
witnessScript: WitnessScript,
): this {
this.data.addWitnessScriptToOutput(outputIndex, witnessScript);
return this;
}
addBip32DerivationToOutput(
outputIndex: number,
bip32Derivation: Bip32Derivation,
): this {
this.data.addBip32DerivationToOutput(outputIndex, bip32Derivation);
return this;
}
addUnknownKeyValToGlobal(keyVal: KeyValue): this {
this.data.addUnknownKeyValToGlobal(keyVal);
return this;
}
addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this {
this.data.addUnknownKeyValToInput(inputIndex, keyVal);
return this;
}
addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this {
this.data.addUnknownKeyValToOutput(outputIndex, keyVal);
return this;
}
clearFinalizedInput(inputIndex: number): this {
this.data.clearFinalizedInput(inputIndex);
return this;
}
} }
interface PsbtCache { interface PsbtCache {

36
types/psbt.d.ts

@ -1,16 +1,20 @@
/// <reference types="node" /> /// <reference types="node" />
import { Psbt as PsbtBase } from 'bip174'; import { Psbt as PsbtBase } from 'bip174';
import { NonWitnessUtxo, TransactionInput, TransactionOutput } from 'bip174/src/lib/interfaces'; import { Bip32Derivation, FinalScriptSig, FinalScriptWitness, GlobalXpub, KeyValue, NonWitnessUtxo, PartialSig, PorCommitment, RedeemScript, SighashType, TransactionInput, TransactionOutput, WitnessScript, WitnessUtxo } from 'bip174/src/lib/interfaces';
import { Signer, SignerAsync } from './ecpair'; import { Signer, SignerAsync } from './ecpair';
import { Network } from './networks'; import { Network } from './networks';
import { Transaction } from './transaction'; import { Transaction } from './transaction';
export declare class Psbt extends PsbtBase { export declare class Psbt {
static fromTransaction<T extends typeof PsbtBase>(this: T, txBuf: Buffer): InstanceType<T>; readonly data: PsbtBase;
static fromBuffer<T extends typeof PsbtBase>(this: T, buffer: Buffer): InstanceType<T>; static fromTransaction(txBuf: Buffer, opts?: PsbtOptsOptional): Psbt;
static fromBase64(data: string, opts?: PsbtOptsOptional): Psbt;
static fromHex(data: string, opts?: PsbtOptsOptional): Psbt;
static fromBuffer(buffer: Buffer, opts?: PsbtOptsOptional): Psbt;
private __CACHE; private __CACHE;
private opts; private opts;
constructor(opts?: PsbtOptsOptional); constructor(opts?: PsbtOptsOptional, data?: PsbtBase);
readonly inputCount: number; readonly inputCount: number;
combine(...those: Psbt[]): this;
clone(): Psbt; clone(): Psbt;
setMaximumFeeRate(satoshiPerByte: number): void; setMaximumFeeRate(satoshiPerByte: number): void;
setVersion(version: number): this; setVersion(version: number): this;
@ -20,7 +24,6 @@ export declare class Psbt extends PsbtBase {
addInput(inputData: TransactionInput): this; addInput(inputData: TransactionInput): this;
addOutputs(outputDatas: TransactionOutput[]): this; addOutputs(outputDatas: TransactionOutput[]): this;
addOutput(outputData: TransactionOutput): this; addOutput(outputData: TransactionOutput): this;
addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
extractTransaction(disableFeeCheck?: boolean): Transaction; extractTransaction(disableFeeCheck?: boolean): Transaction;
getFeeRate(): number; getFeeRate(): number;
finalizeAllInputs(): this; finalizeAllInputs(): this;
@ -31,6 +34,27 @@ export declare class Psbt extends PsbtBase {
signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this; signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>; signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
toBuffer(): Buffer;
toHex(): string;
toBase64(): string;
addGlobalXpubToGlobal(globalXpub: GlobalXpub): this;
addNonWitnessUtxoToInput(inputIndex: number, nonWitnessUtxo: NonWitnessUtxo): this;
addWitnessUtxoToInput(inputIndex: number, witnessUtxo: WitnessUtxo): this;
addPartialSigToInput(inputIndex: number, partialSig: PartialSig): this;
addSighashTypeToInput(inputIndex: number, sighashType: SighashType): this;
addRedeemScriptToInput(inputIndex: number, redeemScript: RedeemScript): this;
addWitnessScriptToInput(inputIndex: number, witnessScript: WitnessScript): this;
addBip32DerivationToInput(inputIndex: number, bip32Derivation: Bip32Derivation): this;
addFinalScriptSigToInput(inputIndex: number, finalScriptSig: FinalScriptSig): this;
addFinalScriptWitnessToInput(inputIndex: number, finalScriptWitness: FinalScriptWitness): this;
addPorCommitmentToInput(inputIndex: number, porCommitment: PorCommitment): this;
addRedeemScriptToOutput(outputIndex: number, redeemScript: RedeemScript): this;
addWitnessScriptToOutput(outputIndex: number, witnessScript: WitnessScript): this;
addBip32DerivationToOutput(outputIndex: number, bip32Derivation: Bip32Derivation): this;
addUnknownKeyValToGlobal(keyVal: KeyValue): this;
addUnknownKeyValToInput(inputIndex: number, keyVal: KeyValue): this;
addUnknownKeyValToOutput(outputIndex: number, keyVal: KeyValue): this;
clearFinalizedInput(inputIndex: number): this;
} }
interface PsbtOptsOptional { interface PsbtOptsOptional {
network?: Network; network?: Network;

Loading…
Cancel
Save