Add sighash checks for signer
This commit is contained in:
parent
fa897cf78e
commit
ccab2652f9
3 changed files with 117 additions and 20 deletions
65
src/psbt.js
65
src/psbt.js
|
@ -273,7 +273,7 @@ class Psbt extends bip174_1.Psbt {
|
|||
}
|
||||
return results.every(res => res === true);
|
||||
}
|
||||
sign(keyPair) {
|
||||
sign(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
throw new Error('Need Signer to sign input');
|
||||
// TODO: Add a pubkey/pubkeyhash cache to each input
|
||||
|
@ -282,7 +282,7 @@ class Psbt extends bip174_1.Psbt {
|
|||
const results = [];
|
||||
for (const i of range(this.inputs.length)) {
|
||||
try {
|
||||
this.signInput(i, keyPair);
|
||||
this.signInput(i, keyPair, sighashTypes);
|
||||
results.push(true);
|
||||
} catch (err) {
|
||||
results.push(false);
|
||||
|
@ -293,7 +293,7 @@ class Psbt extends bip174_1.Psbt {
|
|||
}
|
||||
return this;
|
||||
}
|
||||
signAsync(keyPair) {
|
||||
signAsync(keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
return reject(new Error('Need Signer to sign input'));
|
||||
|
@ -304,7 +304,7 @@ class Psbt extends bip174_1.Psbt {
|
|||
const promises = [];
|
||||
for (const [i] of this.inputs.entries()) {
|
||||
promises.push(
|
||||
this.signInputAsync(i, keyPair).then(
|
||||
this.signInputAsync(i, keyPair, sighashTypes).then(
|
||||
() => {
|
||||
results.push(true);
|
||||
},
|
||||
|
@ -322,7 +322,11 @@ class Psbt extends bip174_1.Psbt {
|
|||
});
|
||||
});
|
||||
}
|
||||
signInput(inputIndex, keyPair) {
|
||||
signInput(
|
||||
inputIndex,
|
||||
keyPair,
|
||||
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
||||
) {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
throw new Error('Need Signer to sign input');
|
||||
const { hash, sighashType } = getHashAndSighashType(
|
||||
|
@ -330,6 +334,7 @@ class Psbt extends bip174_1.Psbt {
|
|||
inputIndex,
|
||||
keyPair.publicKey,
|
||||
this.__CACHE,
|
||||
sighashTypes,
|
||||
);
|
||||
const partialSig = {
|
||||
pubkey: keyPair.publicKey,
|
||||
|
@ -337,7 +342,11 @@ class Psbt extends bip174_1.Psbt {
|
|||
};
|
||||
return this.addPartialSigToInput(inputIndex, partialSig);
|
||||
}
|
||||
signInputAsync(inputIndex, keyPair) {
|
||||
signInputAsync(
|
||||
inputIndex,
|
||||
keyPair,
|
||||
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
|
||||
) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
return reject(new Error('Need Signer to sign input'));
|
||||
|
@ -346,6 +355,7 @@ class Psbt extends bip174_1.Psbt {
|
|||
inputIndex,
|
||||
keyPair.publicKey,
|
||||
this.__CACHE,
|
||||
sighashTypes,
|
||||
);
|
||||
Promise.resolve(keyPair.sign(hash)).then(signature => {
|
||||
const partialSig = {
|
||||
|
@ -548,19 +558,37 @@ function getFinalScripts(
|
|||
finalScriptWitness,
|
||||
};
|
||||
}
|
||||
function getHashAndSighashType(inputs, inputIndex, pubkey, cache) {
|
||||
function getHashAndSighashType(
|
||||
inputs,
|
||||
inputIndex,
|
||||
pubkey,
|
||||
cache,
|
||||
sighashTypes,
|
||||
) {
|
||||
const input = utils_1.checkForInput(inputs, inputIndex);
|
||||
const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
|
||||
const { hash, sighashType, script } = getHashForSig(
|
||||
inputIndex,
|
||||
input,
|
||||
cache,
|
||||
sighashTypes,
|
||||
);
|
||||
checkScriptForPubkey(pubkey, script, 'sign');
|
||||
return {
|
||||
hash,
|
||||
sighashType,
|
||||
};
|
||||
}
|
||||
function getHashForSig(inputIndex, input, cache) {
|
||||
function getHashForSig(inputIndex, input, cache, sighashTypes) {
|
||||
const unsignedTx = cache.__TX;
|
||||
const sighashType =
|
||||
input.sighashType || transaction_1.Transaction.SIGHASH_ALL;
|
||||
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
|
||||
const str = sighashTypeToString(sighashType);
|
||||
throw new Error(
|
||||
`Sighash type is not allowed. Retry the sign method passing the ` +
|
||||
`sighashTypes array of whitelisted types. Sighash type: ${str}`,
|
||||
);
|
||||
}
|
||||
let hash;
|
||||
let script;
|
||||
if (input.nonWitnessUtxo) {
|
||||
|
@ -800,6 +828,25 @@ function scriptWitnessToWitnessStack(buffer) {
|
|||
}
|
||||
return readVector();
|
||||
}
|
||||
function sighashTypeToString(sighashType) {
|
||||
let text =
|
||||
sighashType & transaction_1.Transaction.SIGHASH_ANYONECANPAY
|
||||
? 'SIGHASH_ANYONECANPAY | '
|
||||
: '';
|
||||
const sigMod = sighashType & 0x1f;
|
||||
switch (sigMod) {
|
||||
case transaction_1.Transaction.SIGHASH_ALL:
|
||||
text += 'SIGHASH_ALL';
|
||||
break;
|
||||
case transaction_1.Transaction.SIGHASH_SINGLE:
|
||||
text += 'SIGHASH_SINGLE';
|
||||
break;
|
||||
case transaction_1.Transaction.SIGHASH_NONE:
|
||||
text += 'SIGHASH_NONE';
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
function witnessStackToScriptWitness(witness) {
|
||||
let buffer = Buffer.allocUnsafe(0);
|
||||
function writeSlice(slice) {
|
||||
|
|
|
@ -332,7 +332,10 @@ export class Psbt extends PsbtBase {
|
|||
return results.every(res => res === true);
|
||||
}
|
||||
|
||||
sign(keyPair: Signer): this {
|
||||
sign(
|
||||
keyPair: Signer,
|
||||
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
||||
): this {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
throw new Error('Need Signer to sign input');
|
||||
|
||||
|
@ -342,7 +345,7 @@ export class Psbt extends PsbtBase {
|
|||
const results: boolean[] = [];
|
||||
for (const i of range(this.inputs.length)) {
|
||||
try {
|
||||
this.signInput(i, keyPair);
|
||||
this.signInput(i, keyPair, sighashTypes);
|
||||
results.push(true);
|
||||
} catch (err) {
|
||||
results.push(false);
|
||||
|
@ -354,7 +357,10 @@ export class Psbt extends PsbtBase {
|
|||
return this;
|
||||
}
|
||||
|
||||
signAsync(keyPair: SignerAsync): Promise<void> {
|
||||
signAsync(
|
||||
keyPair: SignerAsync,
|
||||
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
||||
): Promise<void> {
|
||||
return new Promise(
|
||||
(resolve, reject): any => {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
|
@ -367,7 +373,7 @@ export class Psbt extends PsbtBase {
|
|||
const promises: Array<Promise<void>> = [];
|
||||
for (const [i] of this.inputs.entries()) {
|
||||
promises.push(
|
||||
this.signInputAsync(i, keyPair).then(
|
||||
this.signInputAsync(i, keyPair, sighashTypes).then(
|
||||
() => {
|
||||
results.push(true);
|
||||
},
|
||||
|
@ -387,7 +393,11 @@ export class Psbt extends PsbtBase {
|
|||
);
|
||||
}
|
||||
|
||||
signInput(inputIndex: number, keyPair: Signer): this {
|
||||
signInput(
|
||||
inputIndex: number,
|
||||
keyPair: Signer,
|
||||
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
||||
): this {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
throw new Error('Need Signer to sign input');
|
||||
const { hash, sighashType } = getHashAndSighashType(
|
||||
|
@ -395,6 +405,7 @@ export class Psbt extends PsbtBase {
|
|||
inputIndex,
|
||||
keyPair.publicKey,
|
||||
this.__CACHE,
|
||||
sighashTypes,
|
||||
);
|
||||
|
||||
const partialSig = {
|
||||
|
@ -405,7 +416,11 @@ export class Psbt extends PsbtBase {
|
|||
return this.addPartialSigToInput(inputIndex, partialSig);
|
||||
}
|
||||
|
||||
signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void> {
|
||||
signInputAsync(
|
||||
inputIndex: number,
|
||||
keyPair: SignerAsync,
|
||||
sighashTypes: number[] = [Transaction.SIGHASH_ALL],
|
||||
): Promise<void> {
|
||||
return new Promise(
|
||||
(resolve, reject): void => {
|
||||
if (!keyPair || !keyPair.publicKey)
|
||||
|
@ -415,6 +430,7 @@ export class Psbt extends PsbtBase {
|
|||
inputIndex,
|
||||
keyPair.publicKey,
|
||||
this.__CACHE,
|
||||
sighashTypes,
|
||||
);
|
||||
|
||||
Promise.resolve(keyPair.sign(hash)).then(signature => {
|
||||
|
@ -683,12 +699,18 @@ function getHashAndSighashType(
|
|||
inputIndex: number,
|
||||
pubkey: Buffer,
|
||||
cache: PsbtCache,
|
||||
sighashTypes: number[],
|
||||
): {
|
||||
hash: Buffer;
|
||||
sighashType: number;
|
||||
} {
|
||||
const input = checkForInput(inputs, inputIndex);
|
||||
const { hash, sighashType, script } = getHashForSig(inputIndex, input, cache);
|
||||
const { hash, sighashType, script } = getHashForSig(
|
||||
inputIndex,
|
||||
input,
|
||||
cache,
|
||||
sighashTypes,
|
||||
);
|
||||
checkScriptForPubkey(pubkey, script, 'sign');
|
||||
return {
|
||||
hash,
|
||||
|
@ -700,6 +722,7 @@ function getHashForSig(
|
|||
inputIndex: number,
|
||||
input: PsbtInput,
|
||||
cache: PsbtCache,
|
||||
sighashTypes?: number[],
|
||||
): {
|
||||
script: Buffer;
|
||||
hash: Buffer;
|
||||
|
@ -707,6 +730,13 @@ function getHashForSig(
|
|||
} {
|
||||
const unsignedTx = cache.__TX;
|
||||
const sighashType = input.sighashType || Transaction.SIGHASH_ALL;
|
||||
if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
|
||||
const str = sighashTypeToString(sighashType);
|
||||
throw new Error(
|
||||
`Sighash type is not allowed. Retry the sign method passing the ` +
|
||||
`sighashTypes array of whitelisted types. Sighash type: ${str}`,
|
||||
);
|
||||
}
|
||||
let hash: Buffer;
|
||||
let script: Buffer;
|
||||
|
||||
|
@ -981,6 +1011,26 @@ function scriptWitnessToWitnessStack(buffer: Buffer): Buffer[] {
|
|||
return readVector();
|
||||
}
|
||||
|
||||
function sighashTypeToString(sighashType: number): string {
|
||||
let text =
|
||||
sighashType & Transaction.SIGHASH_ANYONECANPAY
|
||||
? 'SIGHASH_ANYONECANPAY | '
|
||||
: '';
|
||||
const sigMod = sighashType & 0x1f;
|
||||
switch (sigMod) {
|
||||
case Transaction.SIGHASH_ALL:
|
||||
text += 'SIGHASH_ALL';
|
||||
break;
|
||||
case Transaction.SIGHASH_SINGLE:
|
||||
text += 'SIGHASH_SINGLE';
|
||||
break;
|
||||
case Transaction.SIGHASH_NONE:
|
||||
text += 'SIGHASH_NONE';
|
||||
break;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
function witnessStackToScriptWitness(witness: Buffer[]): Buffer {
|
||||
let buffer = Buffer.allocUnsafe(0);
|
||||
|
||||
|
|
8
types/psbt.d.ts
vendored
8
types/psbt.d.ts
vendored
|
@ -27,10 +27,10 @@ export declare class Psbt extends PsbtBase {
|
|||
finalizeInput(inputIndex: number): this;
|
||||
validateAllSignatures(): boolean;
|
||||
validateSignatures(inputIndex: number, pubkey?: Buffer): boolean;
|
||||
sign(keyPair: Signer): this;
|
||||
signAsync(keyPair: SignerAsync): Promise<void>;
|
||||
signInput(inputIndex: number, keyPair: Signer): this;
|
||||
signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
|
||||
sign(keyPair: Signer, sighashTypes?: number[]): this;
|
||||
signAsync(keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
|
||||
signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this;
|
||||
signInputAsync(inputIndex: number, keyPair: SignerAsync, sighashTypes?: number[]): Promise<void>;
|
||||
}
|
||||
interface PsbtOptsOptional {
|
||||
network?: Network;
|
||||
|
|
Loading…
Add table
Reference in a new issue