Add async signing method

This commit is contained in:
junderw 2019-07-03 15:34:18 +09:00
parent 813b84f91f
commit 77dde89acc
No known key found for this signature in database
GPG key ID: B256185D3A971908
4 changed files with 161 additions and 55 deletions

View file

@ -56,24 +56,14 @@ class Psbt extends bip174_1.Psbt {
if (!script) return false;
const scriptType = classifyScript(script);
if (!canFinalize(input, script, scriptType)) return false;
let finalScriptSig;
let finalScriptWitness;
// Wow, the payments API is very handy
const payment = getPayment(script, scriptType, input.partialSig);
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
if (isSegwit) {
if (p2wsh) {
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
} else {
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
}
if (p2sh) {
finalScriptSig = bscript.compile([p2sh.redeem.output]);
}
} else {
finalScriptSig = payment.input;
}
const { finalScriptSig, finalScriptWitness } = getFinalScripts(
script,
scriptType,
input.partialSig,
isSegwit,
isP2SH,
isP2WSH,
);
if (finalScriptSig)
this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
if (finalScriptWitness)
@ -83,22 +73,38 @@ class Psbt extends bip174_1.Psbt {
return true;
}
signInput(inputIndex, keyPair) {
const input = utils_1.checkForInput(this.inputs, inputIndex);
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
const { hash, sighashType, script } = getHashForSig(
const { hash, sighashType } = getHashAndSighashType(
this.inputs,
inputIndex,
input,
keyPair.publicKey,
this.globalMap.unsignedTx,
);
const pubkey = keyPair.publicKey;
checkScriptForPubkey(pubkey, script);
const partialSig = {
pubkey,
pubkey: keyPair.publicKey,
signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
};
return this.addPartialSigToInput(inputIndex, partialSig);
}
async signInputAsync(inputIndex, keyPair) {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType(
this.inputs,
inputIndex,
keyPair.publicKey,
this.globalMap.unsignedTx,
);
const partialSig = {
pubkey: keyPair.publicKey,
signature: bscript.signature.encode(
await keyPair.sign(hash),
sighashType,
),
};
this.addPartialSigToInput(inputIndex, partialSig);
}
}
exports.Psbt = Psbt;
//
@ -113,6 +119,46 @@ exports.Psbt = Psbt;
function isFinalized(input) {
return !!input.finalScriptSig || !!input.finalScriptWitness;
}
function getHashAndSighashType(inputs, inputIndex, pubkey, txBuf) {
const input = utils_1.checkForInput(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf);
checkScriptForPubkey(pubkey, script);
return {
hash,
sighashType,
};
}
function getFinalScripts(
script,
scriptType,
partialSig,
isSegwit,
isP2SH,
isP2WSH,
) {
let finalScriptSig;
let finalScriptWitness;
// Wow, the payments API is very handy
const payment = getPayment(script, scriptType, partialSig);
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
if (isSegwit) {
if (p2wsh) {
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness);
} else {
finalScriptWitness = witnessStackToScriptWitness(payment.witness);
}
if (p2sh) {
finalScriptSig = bscript.compile([p2sh.redeem.output]);
}
} else {
finalScriptSig = payment.input;
}
return {
finalScriptSig,
finalScriptWitness,
};
}
function getPayment(script, scriptType, partialSig) {
let payment;
switch (scriptType) {

View file

@ -2,7 +2,7 @@ import { Psbt as PsbtBase } from 'bip174';
import { PartialSig, PsbtInput } from 'bip174/src/lib/interfaces';
import { checkForInput } from 'bip174/src/lib/utils';
import { hash160 } from './crypto';
import { Signer } from './ecpair';
import { Signer, SignerAsync } from './ecpair';
import { Network } from './networks';
import * as payments from './payments';
import * as bscript from './script';
@ -65,30 +65,14 @@ export class Psbt extends PsbtBase {
const scriptType = classifyScript(script);
if (!canFinalize(input, script, scriptType)) return false;
let finalScriptSig: Buffer | undefined;
let finalScriptWitness: Buffer | undefined;
// Wow, the payments API is very handy
const payment: payments.Payment = getPayment(
const { finalScriptSig, finalScriptWitness } = getFinalScripts(
script,
scriptType,
input.partialSig!,
isSegwit,
isP2SH,
isP2WSH,
);
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
if (isSegwit) {
if (p2wsh) {
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!);
} else {
finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
}
if (p2sh) {
finalScriptSig = bscript.compile([p2sh.redeem!.output!]);
}
} else {
finalScriptSig = payment.input;
}
if (finalScriptSig)
this.addFinalScriptSigToInput(inputIndex, finalScriptSig);
@ -101,26 +85,46 @@ export class Psbt extends PsbtBase {
}
signInput(inputIndex: number, keyPair: Signer): Psbt {
const input = checkForInput(this.inputs, inputIndex);
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
const { hash, sighashType, script } = getHashForSig(
const { hash, sighashType } = getHashAndSighashType(
this.inputs,
inputIndex,
input,
keyPair.publicKey,
this.globalMap.unsignedTx!,
);
const pubkey = keyPair.publicKey;
checkScriptForPubkey(pubkey, script);
const partialSig = {
pubkey,
pubkey: keyPair.publicKey,
signature: bscript.signature.encode(keyPair.sign(hash), sighashType),
};
return this.addPartialSigToInput(inputIndex, partialSig);
}
async signInputAsync(
inputIndex: number,
keyPair: SignerAsync,
): Promise<void> {
if (!keyPair || !keyPair.publicKey)
throw new Error('Need Signer to sign input');
const { hash, sighashType } = getHashAndSighashType(
this.inputs,
inputIndex,
keyPair.publicKey,
this.globalMap.unsignedTx!,
);
const partialSig = {
pubkey: keyPair.publicKey,
signature: bscript.signature.encode(
await keyPair.sign(hash),
sighashType,
),
};
this.addPartialSigToInput(inputIndex, partialSig);
}
}
//
@ -137,6 +141,61 @@ function isFinalized(input: PsbtInput): boolean {
return !!input.finalScriptSig || !!input.finalScriptWitness;
}
function getHashAndSighashType(
inputs: PsbtInput[],
inputIndex: number,
pubkey: Buffer,
txBuf: Buffer,
): {
hash: Buffer;
sighashType: number;
} {
const input = checkForInput(inputs, inputIndex);
const { hash, sighashType, script } = getHashForSig(inputIndex, input, txBuf);
checkScriptForPubkey(pubkey, script);
return {
hash,
sighashType,
};
}
function getFinalScripts(
script: Buffer,
scriptType: string,
partialSig: PartialSig[],
isSegwit: boolean,
isP2SH: boolean,
isP2WSH: boolean,
): {
finalScriptSig: Buffer | undefined;
finalScriptWitness: Buffer | undefined;
} {
let finalScriptSig: Buffer | undefined;
let finalScriptWitness: Buffer | undefined;
// Wow, the payments API is very handy
const payment: payments.Payment = getPayment(script, scriptType, partialSig);
const p2wsh = !isP2WSH ? null : payments.p2wsh({ redeem: payment });
const p2sh = !isP2SH ? null : payments.p2sh({ redeem: p2wsh || payment });
if (isSegwit) {
if (p2wsh) {
finalScriptWitness = witnessStackToScriptWitness(p2wsh.witness!);
} else {
finalScriptWitness = witnessStackToScriptWitness(payment.witness!);
}
if (p2sh) {
finalScriptSig = bscript.compile([p2sh.redeem!.output!]);
}
} else {
finalScriptSig = payment.input;
}
return {
finalScriptSig,
finalScriptWitness,
};
}
function getPayment(
script: Buffer,
scriptType: string,

View file

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "ES2015",
"target": "ES2017",
"module": "commonjs",
"outDir": "./src",
"declaration": true,

3
types/psbt.d.ts vendored
View file

@ -1,5 +1,5 @@
import { Psbt as PsbtBase } from 'bip174';
import { Signer } from './ecpair';
import { Signer, SignerAsync } from './ecpair';
import { Network } from './networks';
import { Transaction } from './transaction';
export declare class Psbt extends PsbtBase {
@ -12,4 +12,5 @@ export declare class Psbt extends PsbtBase {
};
finalizeInput(inputIndex: number): boolean;
signInput(inputIndex: number, keyPair: Signer): Psbt;
signInputAsync(inputIndex: number, keyPair: SignerAsync): Promise<void>;
}