Merge pull request #1582 from bitcoinjs/psbt-signInputAsync-fix

Fix signInputAsync when SignerAsync rejects
This commit is contained in:
Jonathan Underwood 2020-06-01 15:55:14 +09:00 committed by GitHub
commit 0a80584881
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 91 additions and 40 deletions

View file

@ -488,9 +488,9 @@ class Psbt {
keyPair, keyPair,
sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], sighashTypes = [transaction_1.Transaction.SIGHASH_ALL],
) { ) {
return new Promise((resolve, reject) => { return Promise.resolve().then(() => {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input')); throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.data.inputs, this.data.inputs,
inputIndex, inputIndex,
@ -498,7 +498,7 @@ class Psbt {
this.__CACHE, this.__CACHE,
sighashTypes, sighashTypes,
); );
Promise.resolve(keyPair.sign(hash)).then(signature => { return Promise.resolve(keyPair.sign(hash)).then(signature => {
const partialSig = [ const partialSig = [
{ {
pubkey: keyPair.publicKey, pubkey: keyPair.publicKey,
@ -506,7 +506,6 @@ class Psbt {
}, },
]; ];
this.data.updateInput(inputIndex, { partialSig }); this.data.updateInput(inputIndex, { partialSig });
resolve();
}); });
}); });
} }

View file

@ -1,7 +1,14 @@
import * as assert from 'assert'; import * as assert from 'assert';
import { describe, it } from 'mocha'; import { describe, it } from 'mocha';
import { bip32, ECPair, networks as NETWORKS, Psbt } from '..'; import {
bip32,
ECPair,
networks as NETWORKS,
Psbt,
Signer,
SignerAsync,
} from '..';
import * as preFixtures from './fixtures/psbt.json'; import * as preFixtures from './fixtures/psbt.json';
@ -22,6 +29,40 @@ const fixtures = initBuffers(preFixtures);
const upperCaseFirstLetter = (str: string): string => const upperCaseFirstLetter = (str: string): string =>
str.replace(/^./, s => s.toUpperCase()); str.replace(/^./, s => s.toUpperCase());
const toAsyncSigner = (signer: Signer): SignerAsync => {
const ret: SignerAsync = {
publicKey: signer.publicKey,
sign: (hash: Buffer, lowerR: boolean | undefined): Promise<Buffer> => {
return new Promise(
(resolve, rejects): void => {
setTimeout(() => {
try {
const r = signer.sign(hash, lowerR);
resolve(r);
} catch (e) {
rejects(e);
}
}, 10);
},
);
},
};
return ret;
};
const failedAsyncSigner = (publicKey: Buffer): SignerAsync => {
return {
publicKey,
sign: (__: Buffer): Promise<Buffer> => {
return new Promise(
(_, reject): void => {
setTimeout(() => {
reject(new Error('sign failed'));
}, 10);
},
);
},
};
};
// const b = (hex: string) => Buffer.from(hex, 'hex'); // const b = (hex: string) => Buffer.from(hex, 'hex');
describe(`Psbt`, () => { describe(`Psbt`, () => {
@ -164,25 +205,39 @@ describe(`Psbt`, () => {
it(f.description, async () => { it(f.description, async () => {
if (f.shouldSign) { if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
assert.doesNotReject(async () => { await assert.doesNotReject(async () => {
await psbtThatShouldsign.signInputAsync( await psbtThatShouldsign.signInputAsync(
f.shouldSign.inputToCheck, f.shouldSign.inputToCheck,
ECPair.fromWIF(f.shouldSign.WIF), ECPair.fromWIF(f.shouldSign.WIF),
f.shouldSign.sighashTypes || undefined, f.shouldSign.sighashTypes || undefined,
); );
}); });
await assert.rejects(async () => {
await psbtThatShouldsign.signInputAsync(
f.shouldSign.inputToCheck,
failedAsyncSigner(ECPair.fromWIF(f.shouldSign.WIF).publicKey),
f.shouldSign.sighashTypes || undefined,
);
}, /sign failed/);
} }
if (f.shouldThrow) { if (f.shouldThrow) {
const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt);
assert.rejects(async () => { await assert.rejects(async () => {
await psbtThatShouldThrow.signInputAsync( await psbtThatShouldThrow.signInputAsync(
f.shouldThrow.inputToCheck, f.shouldThrow.inputToCheck,
ECPair.fromWIF(f.shouldThrow.WIF), ECPair.fromWIF(f.shouldThrow.WIF),
(f.shouldThrow as any).sighashTypes || undefined, (f.shouldThrow as any).sighashTypes || undefined,
); );
}, new RegExp(f.shouldThrow.errorMessage)); }, new RegExp(f.shouldThrow.errorMessage));
assert.rejects(async () => { await assert.rejects(async () => {
await psbtThatShouldThrow.signInputAsync(
f.shouldThrow.inputToCheck,
toAsyncSigner(ECPair.fromWIF(f.shouldThrow.WIF)),
(f.shouldThrow as any).sighashTypes || undefined,
);
}, new RegExp(f.shouldThrow.errorMessage));
await assert.rejects(async () => {
await (psbtThatShouldThrow.signInputAsync as any)( await (psbtThatShouldThrow.signInputAsync as any)(
f.shouldThrow.inputToCheck, f.shouldThrow.inputToCheck,
); );
@ -229,7 +284,7 @@ describe(`Psbt`, () => {
it(f.description, async () => { it(f.description, async () => {
if (f.shouldSign) { if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
assert.doesNotReject(async () => { await assert.doesNotReject(async () => {
await psbtThatShouldsign.signAllInputsAsync( await psbtThatShouldsign.signAllInputsAsync(
ECPair.fromWIF(f.shouldSign.WIF), ECPair.fromWIF(f.shouldSign.WIF),
f.shouldSign.sighashTypes || undefined, f.shouldSign.sighashTypes || undefined,
@ -239,13 +294,13 @@ describe(`Psbt`, () => {
if (f.shouldThrow) { if (f.shouldThrow) {
const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt);
assert.rejects(async () => { await assert.rejects(async () => {
await psbtThatShouldThrow.signAllInputsAsync( await psbtThatShouldThrow.signAllInputsAsync(
ECPair.fromWIF(f.shouldThrow.WIF), ECPair.fromWIF(f.shouldThrow.WIF),
(f.shouldThrow as any).sighashTypes || undefined, (f.shouldThrow as any).sighashTypes || undefined,
); );
}, new RegExp('No inputs were signed')); }, new RegExp('No inputs were signed'));
assert.rejects(async () => { await assert.rejects(async () => {
await (psbtThatShouldThrow.signAllInputsAsync as any)(); await (psbtThatShouldThrow.signAllInputsAsync as any)();
}, new RegExp('Need Signer to sign input')); }, new RegExp('Need Signer to sign input'));
} }
@ -288,7 +343,7 @@ describe(`Psbt`, () => {
it(f.description, async () => { it(f.description, async () => {
if (f.shouldSign) { if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
assert.doesNotReject(async () => { await assert.doesNotReject(async () => {
await psbtThatShouldsign.signInputHDAsync( await psbtThatShouldsign.signInputHDAsync(
f.shouldSign.inputToCheck, f.shouldSign.inputToCheck,
bip32.fromBase58(f.shouldSign.xprv), bip32.fromBase58(f.shouldSign.xprv),
@ -299,14 +354,14 @@ describe(`Psbt`, () => {
if (f.shouldThrow) { if (f.shouldThrow) {
const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt);
assert.rejects(async () => { await assert.rejects(async () => {
await psbtThatShouldThrow.signInputHDAsync( await psbtThatShouldThrow.signInputHDAsync(
f.shouldThrow.inputToCheck, f.shouldThrow.inputToCheck,
bip32.fromBase58(f.shouldThrow.xprv), bip32.fromBase58(f.shouldThrow.xprv),
(f.shouldThrow as any).sighashTypes || undefined, (f.shouldThrow as any).sighashTypes || undefined,
); );
}, new RegExp(f.shouldThrow.errorMessage)); }, new RegExp(f.shouldThrow.errorMessage));
assert.rejects(async () => { await assert.rejects(async () => {
await (psbtThatShouldThrow.signInputHDAsync as any)( await (psbtThatShouldThrow.signInputHDAsync as any)(
f.shouldThrow.inputToCheck, f.shouldThrow.inputToCheck,
); );
@ -354,7 +409,7 @@ describe(`Psbt`, () => {
it(f.description, async () => { it(f.description, async () => {
if (f.shouldSign) { if (f.shouldSign) {
const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt);
assert.doesNotReject(async () => { await assert.doesNotReject(async () => {
await psbtThatShouldsign.signAllInputsHDAsync( await psbtThatShouldsign.signAllInputsHDAsync(
bip32.fromBase58(f.shouldSign.xprv), bip32.fromBase58(f.shouldSign.xprv),
(f.shouldSign as any).sighashTypes || undefined, (f.shouldSign as any).sighashTypes || undefined,
@ -364,13 +419,13 @@ describe(`Psbt`, () => {
if (f.shouldThrow) { if (f.shouldThrow) {
const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt);
assert.rejects(async () => { await assert.rejects(async () => {
await psbtThatShouldThrow.signAllInputsHDAsync( await psbtThatShouldThrow.signAllInputsHDAsync(
bip32.fromBase58(f.shouldThrow.xprv), bip32.fromBase58(f.shouldThrow.xprv),
(f.shouldThrow as any).sighashTypes || undefined, (f.shouldThrow as any).sighashTypes || undefined,
); );
}, new RegExp('No inputs were signed')); }, new RegExp('No inputs were signed'));
assert.rejects(async () => { await assert.rejects(async () => {
await (psbtThatShouldThrow.signAllInputsHDAsync as any)(); await (psbtThatShouldThrow.signAllInputsHDAsync as any)();
}, new RegExp('Need HDSigner to sign input')); }, new RegExp('Need HDSigner to sign input'));
} }

View file

@ -580,10 +580,9 @@ export class Psbt {
keyPair: Signer | SignerAsync, keyPair: Signer | SignerAsync,
sighashTypes: number[] = [Transaction.SIGHASH_ALL], sighashTypes: number[] = [Transaction.SIGHASH_ALL],
): Promise<void> { ): Promise<void> {
return new Promise( return Promise.resolve().then(() => {
(resolve, reject): void => {
if (!keyPair || !keyPair.publicKey) if (!keyPair || !keyPair.publicKey)
return reject(new Error('Need Signer to sign input')); throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType( const { hash, sighashType } = getHashAndSighashType(
this.data.inputs, this.data.inputs,
inputIndex, inputIndex,
@ -592,7 +591,7 @@ export class Psbt {
sighashTypes, sighashTypes,
); );
Promise.resolve(keyPair.sign(hash)).then(signature => { return Promise.resolve(keyPair.sign(hash)).then(signature => {
const partialSig = [ const partialSig = [
{ {
pubkey: keyPair.publicKey, pubkey: keyPair.publicKey,
@ -601,10 +600,8 @@ export class Psbt {
]; ];
this.data.updateInput(inputIndex, { partialSig }); this.data.updateInput(inputIndex, { partialSig });
resolve();
}); });
}, });
);
} }
toBuffer(): Buffer { toBuffer(): Buffer {