diff --git a/src/psbt.js b/src/psbt.js index bb41899..13bbef1 100644 --- a/src/psbt.js +++ b/src/psbt.js @@ -488,9 +488,9 @@ class Psbt { keyPair, sighashTypes = [transaction_1.Transaction.SIGHASH_ALL], ) { - return new Promise((resolve, reject) => { + return Promise.resolve().then(() => { 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( this.data.inputs, inputIndex, @@ -498,7 +498,7 @@ class Psbt { this.__CACHE, sighashTypes, ); - Promise.resolve(keyPair.sign(hash)).then(signature => { + return Promise.resolve(keyPair.sign(hash)).then(signature => { const partialSig = [ { pubkey: keyPair.publicKey, @@ -506,7 +506,6 @@ class Psbt { }, ]; this.data.updateInput(inputIndex, { partialSig }); - resolve(); }); }); } diff --git a/test/psbt.spec.ts b/test/psbt.spec.ts index e0eba81..da35dbf 100644 --- a/test/psbt.spec.ts +++ b/test/psbt.spec.ts @@ -1,7 +1,14 @@ import * as assert from 'assert'; 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'; @@ -22,6 +29,40 @@ const fixtures = initBuffers(preFixtures); const upperCaseFirstLetter = (str: string): string => str.replace(/^./, s => s.toUpperCase()); +const toAsyncSigner = (signer: Signer): SignerAsync => { + const ret: SignerAsync = { + publicKey: signer.publicKey, + sign: (hash: Buffer, lowerR: boolean | undefined): Promise => { + 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 => { + return new Promise( + (_, reject): void => { + setTimeout(() => { + reject(new Error('sign failed')); + }, 10); + }, + ); + }, + }; +}; // const b = (hex: string) => Buffer.from(hex, 'hex'); describe(`Psbt`, () => { @@ -164,25 +205,39 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signInputAsync( f.shouldSign.inputToCheck, ECPair.fromWIF(f.shouldSign.WIF), 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) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signInputAsync( f.shouldThrow.inputToCheck, ECPair.fromWIF(f.shouldThrow.WIF), (f.shouldThrow as any).sighashTypes || undefined, ); }, 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)( f.shouldThrow.inputToCheck, ); @@ -229,7 +284,7 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsAsync( ECPair.fromWIF(f.shouldSign.WIF), f.shouldSign.sighashTypes || undefined, @@ -239,13 +294,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsAsync( ECPair.fromWIF(f.shouldThrow.WIF), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp('No inputs were signed')); - assert.rejects(async () => { + await assert.rejects(async () => { await (psbtThatShouldThrow.signAllInputsAsync as any)(); }, new RegExp('Need Signer to sign input')); } @@ -288,7 +343,7 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signInputHDAsync( f.shouldSign.inputToCheck, bip32.fromBase58(f.shouldSign.xprv), @@ -299,14 +354,14 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signInputHDAsync( f.shouldThrow.inputToCheck, bip32.fromBase58(f.shouldThrow.xprv), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp(f.shouldThrow.errorMessage)); - assert.rejects(async () => { + await assert.rejects(async () => { await (psbtThatShouldThrow.signInputHDAsync as any)( f.shouldThrow.inputToCheck, ); @@ -354,7 +409,7 @@ describe(`Psbt`, () => { it(f.description, async () => { if (f.shouldSign) { const psbtThatShouldsign = Psbt.fromBase64(f.shouldSign.psbt); - assert.doesNotReject(async () => { + await assert.doesNotReject(async () => { await psbtThatShouldsign.signAllInputsHDAsync( bip32.fromBase58(f.shouldSign.xprv), (f.shouldSign as any).sighashTypes || undefined, @@ -364,13 +419,13 @@ describe(`Psbt`, () => { if (f.shouldThrow) { const psbtThatShouldThrow = Psbt.fromBase64(f.shouldThrow.psbt); - assert.rejects(async () => { + await assert.rejects(async () => { await psbtThatShouldThrow.signAllInputsHDAsync( bip32.fromBase58(f.shouldThrow.xprv), (f.shouldThrow as any).sighashTypes || undefined, ); }, new RegExp('No inputs were signed')); - assert.rejects(async () => { + await assert.rejects(async () => { await (psbtThatShouldThrow.signAllInputsHDAsync as any)(); }, new RegExp('Need HDSigner to sign input')); } diff --git a/ts_src/psbt.ts b/ts_src/psbt.ts index a09ee0c..b1f83cd 100644 --- a/ts_src/psbt.ts +++ b/ts_src/psbt.ts @@ -580,31 +580,28 @@ export class Psbt { keyPair: Signer | SignerAsync, sighashTypes: number[] = [Transaction.SIGHASH_ALL], ): Promise { - return new Promise( - (resolve, reject): void => { - if (!keyPair || !keyPair.publicKey) - return reject(new Error('Need Signer to sign input')); - const { hash, sighashType } = getHashAndSighashType( - this.data.inputs, - inputIndex, - keyPair.publicKey, - this.__CACHE, - sighashTypes, - ); + return Promise.resolve().then(() => { + if (!keyPair || !keyPair.publicKey) + throw new Error('Need Signer to sign input'); + const { hash, sighashType } = getHashAndSighashType( + this.data.inputs, + inputIndex, + keyPair.publicKey, + this.__CACHE, + sighashTypes, + ); - Promise.resolve(keyPair.sign(hash)).then(signature => { - const partialSig = [ - { - pubkey: keyPair.publicKey, - signature: bscript.signature.encode(signature, sighashType), - }, - ]; + return Promise.resolve(keyPair.sign(hash)).then(signature => { + const partialSig = [ + { + pubkey: keyPair.publicKey, + signature: bscript.signature.encode(signature, sighashType), + }, + ]; - this.data.updateInput(inputIndex, { partialSig }); - resolve(); - }); - }, - ); + this.data.updateInput(inputIndex, { partialSig }); + }); + }); } toBuffer(): Buffer {