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",
|
"lint": "tslint -p tsconfig.json -c tslint.json",
|
||||||
"nobuild:coverage-report": "nyc report --reporter=lcov",
|
"nobuild:coverage-report": "nyc report --reporter=lcov",
|
||||||
"nobuild:coverage-html": "nyc report --reporter=html",
|
"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:integration": "mocha --timeout 50000 test/integration/",
|
||||||
"nobuild:unit": "mocha",
|
"nobuild:unit": "mocha",
|
||||||
"prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore",
|
"prettier": "prettier 'ts_src/**/*.ts' --ignore-path ./.prettierignore",
|
||||||
|
|
|
@ -13,6 +13,20 @@ const transaction_1 = require('./transaction');
|
||||||
const types = require('./types');
|
const types = require('./types');
|
||||||
const typeforce = require('typeforce');
|
const typeforce = require('typeforce');
|
||||||
const SCRIPT_TYPES = classify.types;
|
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) {
|
function txIsString(tx) {
|
||||||
return typeof tx === 'string' || tx instanceof String;
|
return typeof tx === 'string' || tx instanceof String;
|
||||||
}
|
}
|
||||||
|
@ -118,7 +132,103 @@ class TransactionBuilder {
|
||||||
buildIncomplete() {
|
buildIncomplete() {
|
||||||
return this.__build(true);
|
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
|
// TODO: remove keyPair.network matching in 4.0.0
|
||||||
if (keyPair.network && keyPair.network !== this.network)
|
if (keyPair.network && keyPair.network !== this.network)
|
||||||
throw new TypeError('Inconsistent network');
|
throw new TypeError('Inconsistent network');
|
||||||
|
|
|
@ -16,6 +16,21 @@ const typeforce = require('typeforce');
|
||||||
|
|
||||||
const SCRIPT_TYPES = classify.types;
|
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 MaybeBuffer = Buffer | undefined;
|
||||||
type TxbSignatures = Buffer[] | MaybeBuffer[];
|
type TxbSignatures = Buffer[] | MaybeBuffer[];
|
||||||
type TxbPubkeys = MaybeBuffer[];
|
type TxbPubkeys = MaybeBuffer[];
|
||||||
|
@ -50,6 +65,16 @@ interface TxbOutput {
|
||||||
maxSignatures?: number;
|
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 {
|
function txIsString(tx: Buffer | string | Transaction): tx is string {
|
||||||
return typeof tx === 'string' || tx instanceof String;
|
return typeof tx === 'string' || tx instanceof String;
|
||||||
}
|
}
|
||||||
|
@ -197,13 +222,102 @@ export class TransactionBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
sign(
|
sign(
|
||||||
vin: number,
|
signParams: number | TxbSignArg,
|
||||||
keyPair: ECPairInterface,
|
keyPair: ECPairInterface,
|
||||||
redeemScript?: Buffer,
|
redeemScript?: Buffer,
|
||||||
hashType?: number,
|
hashType?: number,
|
||||||
witnessValue?: number,
|
witnessValue?: number,
|
||||||
witnessScript?: Buffer,
|
witnessScript?: Buffer,
|
||||||
): void {
|
): 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
|
// TODO: remove keyPair.network matching in 4.0.0
|
||||||
if (keyPair.network && keyPair.network !== this.network)
|
if (keyPair.network && keyPair.network !== this.network)
|
||||||
throw new TypeError('Inconsistent 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 { ECPairInterface } from './ecpair';
|
||||||
import { Network } from './networks';
|
import { Network } from './networks';
|
||||||
import { Transaction } from './transaction';
|
import { Transaction } from './transaction';
|
||||||
|
interface TxbSignArg {
|
||||||
|
prevOutScriptType: string;
|
||||||
|
vin: number;
|
||||||
|
keyPair: ECPairInterface;
|
||||||
|
redeemScript?: Buffer;
|
||||||
|
hashType?: number;
|
||||||
|
witnessValue?: number;
|
||||||
|
witnessScript?: Buffer;
|
||||||
|
}
|
||||||
export declare class TransactionBuilder {
|
export declare class TransactionBuilder {
|
||||||
network: Network;
|
network: Network;
|
||||||
maximumFeeRate: number;
|
maximumFeeRate: number;
|
||||||
|
@ -18,7 +27,7 @@ export declare class TransactionBuilder {
|
||||||
addOutput(scriptPubKey: string | Buffer, value: number): number;
|
addOutput(scriptPubKey: string | Buffer, value: number): number;
|
||||||
build(): Transaction;
|
build(): Transaction;
|
||||||
buildIncomplete(): 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 __addInputUnsafe;
|
||||||
private __build;
|
private __build;
|
||||||
private __canModifyInputs;
|
private __canModifyInputs;
|
||||||
|
@ -26,3 +35,4 @@ export declare class TransactionBuilder {
|
||||||
private __canModifyOutputs;
|
private __canModifyOutputs;
|
||||||
private __overMaximumFees;
|
private __overMaximumFees;
|
||||||
}
|
}
|
||||||
|
export {};
|
||||||
|
|
Loading…
Add table
Reference in a new issue