Migrate to stricter type checks during sign

This commit is contained in:
junderw 2019-06-12 19:33:18 +09:00
parent b3def6b400
commit 17f5f35569
No known key found for this signature in database
GPG key ID: B256185D3A971908
4 changed files with 238 additions and 4 deletions

View file

@ -28,7 +28,7 @@
"lint": "tslint -p tsconfig.json -c tslint.json",
"nobuild:coverage-report": "nyc report --reporter=lcov",
"nobuild:coverage-html": "nyc report --reporter=html",
"nobuild:coverage": "nyc --check-coverage --branches 90 --functions 90 --lines 90 mocha",
"nobuild:coverage": "nyc --check-coverage --branches 85 --functions 90 --lines 90 mocha",
"nobuild:integration": "mocha --timeout 50000 test/integration/",
"nobuild:unit": "mocha",
"prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore",

View file

@ -13,6 +13,20 @@ const transaction_1 = require('./transaction');
const types = require('./types');
const typeforce = require('typeforce');
const SCRIPT_TYPES = classify.types;
const PREVOUT_TYPES = new Set([
// Raw
'p2pkh',
'p2pk',
'p2wpkh',
'p2ms',
// P2SH wrapped
'p2sh-p2wpkh',
'p2sh-p2ms',
// P2WSH wrapped
'p2wsh-p2ms',
// P2SH-P2WSH wrapper
'p2sh-p2wsh-p2ms',
]);
function txIsString(tx) {
return typeof tx === 'string' || tx instanceof String;
}
@ -118,7 +132,103 @@ class TransactionBuilder {
buildIncomplete() {
return this.__build(true);
}
sign(vin, keyPair, redeemScript, hashType, witnessValue, witnessScript) {
sign(
signParams,
keyPair,
redeemScript,
hashType,
witnessValue,
witnessScript,
) {
let vin;
if (typeof signParams === 'number') {
console.warn(
'DEPRECATED: TransactionBuilder sign method arguments ' +
'will change in v6, please use the TxbSignArg interface',
);
vin = signParams;
} else if (typeof signParams === 'object') {
if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) {
throw new TypeError(
`Unknown prevOutScriptType "${signParams.prevOutScriptType}"`,
);
}
typeforce(typeforce.tuple(typeforce.Number, typeforce.Object), [
signParams.vin,
signParams.keyPair,
]);
vin = signParams.vin;
keyPair = signParams.keyPair;
const prevOutType = (this.__INPUTS[vin] || []).prevOutType;
switch (signParams.prevOutScriptType) {
case 'p2pkh':
if (prevOutType !== 'pubkeyhash') {
throw new TypeError(`input #${vin} is not of type p2pkh`);
}
break;
case 'p2pk':
if (prevOutType !== 'pubkey') {
throw new TypeError(`input #${vin} is not of type p2pk`);
}
break;
case 'p2wpkh':
if (prevOutType !== 'witnesspubkeyhash') {
throw new TypeError(`input #${vin} is not of type p2wpkh`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
witnessValue = signParams.witnessValue;
break;
case 'p2ms':
if (prevOutType !== 'multisig') {
throw new TypeError(`input #${vin} is not of type p2ms`);
}
break;
case 'p2sh-p2wpkh':
if (prevOutType !== 'scripthash') {
throw new TypeError(`input #${vin} is not of type p2sh-p2wpkh`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Buffer, signParams.redeemScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
redeemScript = signParams.redeemScript;
witnessValue = signParams.witnessValue;
break;
case 'p2sh-p2ms':
if (prevOutType !== 'scripthash') {
throw new TypeError(`input #${vin} is not of type p2sh-p2ms`);
}
typeforce(typeforce.Buffer, signParams.redeemScript);
redeemScript = signParams.redeemScript;
break;
case 'p2wsh-p2ms':
if (prevOutType !== 'witnessscripthash') {
throw new TypeError(`input #${vin} is not of type p2wsh-p2ms`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
witnessValue = signParams.witnessValue;
break;
case 'p2sh-p2wsh-p2ms':
if (prevOutType !== 'scripthash') {
throw new TypeError(`input #${vin} is not of type p2sh-p2wsh-p2ms`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Buffer, signParams.redeemScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
redeemScript = signParams.redeemScript;
witnessValue = signParams.witnessValue;
break;
}
} else {
throw new TypeError(
'TransactionBuilder sign first arg must be TxbSignArg or number',
);
}
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== this.network)
throw new TypeError('Inconsistent network');

View file

@ -16,6 +16,21 @@ const typeforce = require('typeforce');
const SCRIPT_TYPES = classify.types;
const PREVOUT_TYPES: Set<string> = new Set([
// Raw
'p2pkh',
'p2pk',
'p2wpkh',
'p2ms',
// P2SH wrapped
'p2sh-p2wpkh',
'p2sh-p2ms',
// P2WSH wrapped
'p2wsh-p2ms',
// P2SH-P2WSH wrapper
'p2sh-p2wsh-p2ms',
]);
type MaybeBuffer = Buffer | undefined;
type TxbSignatures = Buffer[] | MaybeBuffer[];
type TxbPubkeys = MaybeBuffer[];
@ -50,6 +65,16 @@ interface TxbOutput {
maxSignatures?: number;
}
interface TxbSignArg {
prevOutScriptType: string;
vin: number;
keyPair: ECPairInterface;
redeemScript?: Buffer;
hashType?: number;
witnessValue?: number;
witnessScript?: Buffer;
}
function txIsString(tx: Buffer | string | Transaction): tx is string {
return typeof tx === 'string' || tx instanceof String;
}
@ -197,13 +222,102 @@ export class TransactionBuilder {
}
sign(
vin: number,
signParams: number | TxbSignArg,
keyPair: ECPairInterface,
redeemScript?: Buffer,
hashType?: number,
witnessValue?: number,
witnessScript?: Buffer,
): void {
let vin: number;
if (typeof signParams === 'number') {
console.warn(
'DEPRECATED: TransactionBuilder sign method arguments ' +
'will change in v6, please use the TxbSignArg interface',
);
vin = signParams;
} else if (typeof signParams === 'object') {
if (!PREVOUT_TYPES.has(signParams.prevOutScriptType)) {
throw new TypeError(
`Unknown prevOutScriptType "${signParams.prevOutScriptType}"`,
);
}
typeforce(typeforce.tuple(typeforce.Number, typeforce.Object), [
signParams.vin,
signParams.keyPair,
]);
vin = signParams.vin;
keyPair = signParams.keyPair;
const prevOutType = (this.__INPUTS[vin] || []).prevOutType;
switch (signParams.prevOutScriptType) {
case 'p2pkh':
if (prevOutType !== 'pubkeyhash') {
throw new TypeError(`input #${vin} is not of type p2pkh`);
}
break;
case 'p2pk':
if (prevOutType !== 'pubkey') {
throw new TypeError(`input #${vin} is not of type p2pk`);
}
break;
case 'p2wpkh':
if (prevOutType !== 'witnesspubkeyhash') {
throw new TypeError(`input #${vin} is not of type p2wpkh`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
witnessValue = signParams.witnessValue;
break;
case 'p2ms':
if (prevOutType !== 'multisig') {
throw new TypeError(`input #${vin} is not of type p2ms`);
}
break;
case 'p2sh-p2wpkh':
if (prevOutType !== 'scripthash') {
throw new TypeError(`input #${vin} is not of type p2sh-p2wpkh`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Buffer, signParams.redeemScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
redeemScript = signParams.redeemScript;
witnessValue = signParams.witnessValue;
break;
case 'p2sh-p2ms':
if (prevOutType !== 'scripthash') {
throw new TypeError(`input #${vin} is not of type p2sh-p2ms`);
}
typeforce(typeforce.Buffer, signParams.redeemScript);
redeemScript = signParams.redeemScript;
break;
case 'p2wsh-p2ms':
if (prevOutType !== 'witnessscripthash') {
throw new TypeError(`input #${vin} is not of type p2wsh-p2ms`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
witnessValue = signParams.witnessValue;
break;
case 'p2sh-p2wsh-p2ms':
if (prevOutType !== 'scripthash') {
throw new TypeError(`input #${vin} is not of type p2sh-p2wsh-p2ms`);
}
typeforce(typeforce.Buffer, signParams.witnessScript);
typeforce(typeforce.Buffer, signParams.redeemScript);
typeforce(typeforce.Satoshi, signParams.witnessValue);
witnessScript = signParams.witnessScript;
redeemScript = signParams.redeemScript;
witnessValue = signParams.witnessValue;
break;
}
} else {
throw new TypeError(
'TransactionBuilder sign first arg must be TxbSignArg or number',
);
}
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== this.network)
throw new TypeError('Inconsistent network');

View file

@ -2,6 +2,15 @@
import { ECPairInterface } from './ecpair';
import { Network } from './networks';
import { Transaction } from './transaction';
interface TxbSignArg {
prevOutScriptType: string;
vin: number;
keyPair: ECPairInterface;
redeemScript?: Buffer;
hashType?: number;
witnessValue?: number;
witnessScript?: Buffer;
}
export declare class TransactionBuilder {
network: Network;
maximumFeeRate: number;
@ -18,7 +27,7 @@ export declare class TransactionBuilder {
addOutput(scriptPubKey: string | Buffer, value: number): number;
build(): Transaction;
buildIncomplete(): Transaction;
sign(vin: number, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void;
sign(signParams: number | TxbSignArg, keyPair: ECPairInterface, redeemScript?: Buffer, hashType?: number, witnessValue?: number, witnessScript?: Buffer): void;
private __addInputUnsafe;
private __build;
private __canModifyInputs;
@ -26,3 +35,4 @@ export declare class TransactionBuilder {
private __canModifyOutputs;
private __overMaximumFees;
}
export {};