Check for signatures, add setSequence
This commit is contained in:
parent
3e7f490093
commit
45bd5b4751
3 changed files with 124 additions and 0 deletions
60
src/psbt.js
60
src/psbt.js
|
@ -80,16 +80,31 @@ class Psbt extends bip174_1.Psbt {
|
||||||
dpew(this, 'opts', false, true);
|
dpew(this, 'opts', false, true);
|
||||||
}
|
}
|
||||||
setVersion(version) {
|
setVersion(version) {
|
||||||
|
check32Bit(version);
|
||||||
|
checkInputsForPartialSig(this.inputs, 'setVersion');
|
||||||
this.__TX.version = version;
|
this.__TX.version = version;
|
||||||
this.__TX_BUF_CACHE = undefined;
|
this.__TX_BUF_CACHE = undefined;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
setLocktime(locktime) {
|
setLocktime(locktime) {
|
||||||
|
check32Bit(locktime);
|
||||||
|
checkInputsForPartialSig(this.inputs, 'setLocktime');
|
||||||
this.__TX.locktime = locktime;
|
this.__TX.locktime = locktime;
|
||||||
this.__TX_BUF_CACHE = undefined;
|
this.__TX_BUF_CACHE = undefined;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
setSequence(inputIndex, sequence) {
|
||||||
|
check32Bit(sequence);
|
||||||
|
checkInputsForPartialSig(this.inputs, 'setSequence');
|
||||||
|
if (this.__TX.ins.length <= inputIndex) {
|
||||||
|
throw new Error('Input index too high');
|
||||||
|
}
|
||||||
|
this.__TX.ins[inputIndex].sequence = sequence;
|
||||||
|
this.__TX_BUF_CACHE = undefined;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
addInput(inputData) {
|
addInput(inputData) {
|
||||||
|
checkInputsForPartialSig(this.inputs, 'addInput');
|
||||||
const self = this;
|
const self = this;
|
||||||
const inputAdder = (_inputData, txBuf) => {
|
const inputAdder = (_inputData, txBuf) => {
|
||||||
if (
|
if (
|
||||||
|
@ -119,6 +134,7 @@ class Psbt extends bip174_1.Psbt {
|
||||||
return super.addInput(inputData, inputAdder);
|
return super.addInput(inputData, inputAdder);
|
||||||
}
|
}
|
||||||
addOutput(outputData) {
|
addOutput(outputData) {
|
||||||
|
checkInputsForPartialSig(this.inputs, 'addOutput');
|
||||||
const { address } = outputData;
|
const { address } = outputData;
|
||||||
if (typeof address === 'string') {
|
if (typeof address === 'string') {
|
||||||
const { network } = this.opts;
|
const { network } = this.opts;
|
||||||
|
@ -578,3 +594,47 @@ function checkTxEmpty(tx) {
|
||||||
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function checkInputsForPartialSig(inputs, action) {
|
||||||
|
inputs.forEach(input => {
|
||||||
|
let throws = false;
|
||||||
|
if ((input.partialSig || []).length > 0) {
|
||||||
|
if (input.sighashType !== undefined) {
|
||||||
|
const whitelist = [];
|
||||||
|
const isAnyoneCanPay =
|
||||||
|
input.sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY;
|
||||||
|
if (isAnyoneCanPay) whitelist.push('addInput');
|
||||||
|
if (!isAnyoneCanPay && action === 'addInput') {
|
||||||
|
throws = true;
|
||||||
|
}
|
||||||
|
const hashType = input.sighashType & 0x1f;
|
||||||
|
switch (hashType) {
|
||||||
|
case transaction_1.Transaction.SIGHASH_ALL:
|
||||||
|
break;
|
||||||
|
case transaction_1.Transaction.SIGHASH_SINGLE:
|
||||||
|
case transaction_1.Transaction.SIGHASH_NONE:
|
||||||
|
whitelist.push('addOutput');
|
||||||
|
whitelist.push('setSequence');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (whitelist.indexOf(action) === -1) {
|
||||||
|
throws = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throws = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (throws) {
|
||||||
|
throw new Error('Can not modify transaction, signatures exist.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function check32Bit(num) {
|
||||||
|
if (
|
||||||
|
typeof num !== 'number' ||
|
||||||
|
num !== Math.floor(num) ||
|
||||||
|
num > 0xffffffff ||
|
||||||
|
num < 0
|
||||||
|
) {
|
||||||
|
throw new Error('Invalid 32 bit integer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -108,18 +108,34 @@ export class Psbt extends PsbtBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
setVersion(version: number): this {
|
setVersion(version: number): this {
|
||||||
|
check32Bit(version);
|
||||||
|
checkInputsForPartialSig(this.inputs, 'setVersion');
|
||||||
this.__TX.version = version;
|
this.__TX.version = version;
|
||||||
this.__TX_BUF_CACHE = undefined;
|
this.__TX_BUF_CACHE = undefined;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocktime(locktime: number): this {
|
setLocktime(locktime: number): this {
|
||||||
|
check32Bit(locktime);
|
||||||
|
checkInputsForPartialSig(this.inputs, 'setLocktime');
|
||||||
this.__TX.locktime = locktime;
|
this.__TX.locktime = locktime;
|
||||||
this.__TX_BUF_CACHE = undefined;
|
this.__TX_BUF_CACHE = undefined;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setSequence(inputIndex: number, sequence: number): this {
|
||||||
|
check32Bit(sequence);
|
||||||
|
checkInputsForPartialSig(this.inputs, 'setSequence');
|
||||||
|
if (this.__TX.ins.length <= inputIndex) {
|
||||||
|
throw new Error('Input index too high');
|
||||||
|
}
|
||||||
|
this.__TX.ins[inputIndex].sequence = sequence;
|
||||||
|
this.__TX_BUF_CACHE = undefined;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
addInput(inputData: TransactionInput): this {
|
addInput(inputData: TransactionInput): this {
|
||||||
|
checkInputsForPartialSig(this.inputs, 'addInput');
|
||||||
const self = this;
|
const self = this;
|
||||||
const inputAdder = (
|
const inputAdder = (
|
||||||
_inputData: TransactionInput,
|
_inputData: TransactionInput,
|
||||||
|
@ -152,6 +168,7 @@ export class Psbt extends PsbtBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
addOutput(outputData: TransactionOutput): this {
|
addOutput(outputData: TransactionOutput): this {
|
||||||
|
checkInputsForPartialSig(this.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;
|
||||||
|
@ -734,3 +751,49 @@ function checkTxEmpty(tx: Transaction): void {
|
||||||
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
throw new Error('Format Error: Transaction ScriptSigs are not empty');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkInputsForPartialSig(inputs: PsbtInput[], action: string): void {
|
||||||
|
inputs.forEach(input => {
|
||||||
|
let throws = false;
|
||||||
|
if ((input.partialSig || []).length > 0) {
|
||||||
|
if (input.sighashType !== undefined) {
|
||||||
|
const whitelist: string[] = [];
|
||||||
|
const isAnyoneCanPay =
|
||||||
|
input.sighashType & Transaction.SIGHASH_ANYONECANPAY;
|
||||||
|
if (isAnyoneCanPay) whitelist.push('addInput');
|
||||||
|
if (!isAnyoneCanPay && action === 'addInput') {
|
||||||
|
throws = true;
|
||||||
|
}
|
||||||
|
const hashType = input.sighashType & 0x1f;
|
||||||
|
switch (hashType) {
|
||||||
|
case Transaction.SIGHASH_ALL:
|
||||||
|
break;
|
||||||
|
case Transaction.SIGHASH_SINGLE:
|
||||||
|
case Transaction.SIGHASH_NONE:
|
||||||
|
whitelist.push('addOutput');
|
||||||
|
whitelist.push('setSequence');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (whitelist.indexOf(action) === -1) {
|
||||||
|
throws = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throws = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (throws) {
|
||||||
|
throw new Error('Can not modify transaction, signatures exist.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function check32Bit(num: number): void {
|
||||||
|
if (
|
||||||
|
typeof num !== 'number' ||
|
||||||
|
num !== Math.floor(num) ||
|
||||||
|
num > 0xffffffff ||
|
||||||
|
num < 0
|
||||||
|
) {
|
||||||
|
throw new Error('Invalid 32 bit integer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
types/psbt.d.ts
vendored
1
types/psbt.d.ts
vendored
|
@ -13,6 +13,7 @@ export declare class Psbt extends PsbtBase {
|
||||||
constructor(opts?: PsbtOptsOptional);
|
constructor(opts?: PsbtOptsOptional);
|
||||||
setVersion(version: number): this;
|
setVersion(version: number): this;
|
||||||
setLocktime(locktime: number): this;
|
setLocktime(locktime: number): this;
|
||||||
|
setSequence(inputIndex: number, sequence: number): this;
|
||||||
addInput(inputData: TransactionInput): this;
|
addInput(inputData: TransactionInput): this;
|
||||||
addOutput(outputData: TransactionOutput): this;
|
addOutput(outputData: TransactionOutput): this;
|
||||||
extractTransaction(): Transaction;
|
extractTransaction(): Transaction;
|
||||||
|
|
Loading…
Reference in a new issue