Migrate to stricter type checks during sign
This commit is contained in:
parent
b3def6b400
commit
17f5f35569
4 changed files with 238 additions and 4 deletions
|
@ -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",
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
|
12
types/transaction_builder.d.ts
vendored
12
types/transaction_builder.d.ts
vendored
|
@ -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 {};
|
||||
|
|
Loading…
Reference in a new issue