Browse Source

Refactor sign for clarity

psbt-tx-getters
junderw 6 years ago
parent
commit
ee3150d7c7
No known key found for this signature in database GPG Key ID: B256185D3A971908
  1. 223
      src/transaction_builder.js
  2. 264
      ts_src/transaction_builder.ts

223
src/transaction_builder.js

@ -153,98 +153,25 @@ class TransactionBuilder {
witnessValue, witnessValue,
witnessScript, witnessScript,
) { ) {
let vin; const data = getSigningData(
if (typeof signParams === 'number') { this,
console.warn( signParams,
'DEPRECATED: TransactionBuilder sign method arguments ' + keyPair,
'will change in v6, please use the TxbSignArg interface', redeemScript,
); hashType,
vin = signParams; witnessValue,
} else if (typeof signParams === 'object') { witnessScript,
checkSignArgs(this, signParams); );
({ const { input, ourPubKey, signatureHash } = data;
vin, ({ keyPair, hashType } = data);
keyPair, trySign(
redeemScript, input,
hashType, ourPubKey,
witnessValue, keyPair,
witnessScript, signatureHash,
} = signParams); hashType,
} else { this.__USE_LOW_R,
throw new TypeError( );
'TransactionBuilder sign first arg must be TxbSignArg or number',
);
}
if (keyPair === undefined) {
throw new Error('sign requires keypair');
}
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== this.network)
throw new TypeError('Inconsistent network');
if (!this.__INPUTS[vin]) throw new Error('No input at index: ' + vin);
hashType = hashType || transaction_1.Transaction.SIGHASH_ALL;
if (this.__needsOutputs(hashType))
throw new Error('Transaction needs outputs');
const input = this.__INPUTS[vin];
// if redeemScript was previously provided, enforce consistency
if (
input.redeemScript !== undefined &&
redeemScript &&
!input.redeemScript.equals(redeemScript)
) {
throw new Error('Inconsistent redeemScript');
}
const ourPubKey = keyPair.publicKey || keyPair.getPublicKey();
if (!canSign(input)) {
if (witnessValue !== undefined) {
if (input.value !== undefined && input.value !== witnessValue)
throw new Error('Input did not match witnessValue');
typeforce(types.Satoshi, witnessValue);
input.value = witnessValue;
}
if (!canSign(input)) {
const prepared = prepareInput(
input,
ourPubKey,
redeemScript,
witnessScript,
);
// updates inline
Object.assign(input, prepared);
}
if (!canSign(input)) throw Error(input.prevOutType + ' not supported');
}
// ready to sign
let signatureHash;
if (input.hasWitness) {
signatureHash = this.__TX.hashForWitnessV0(
vin,
input.signScript,
input.value,
hashType,
);
} else {
signatureHash = this.__TX.hashForSignature(
vin,
input.signScript,
hashType,
);
}
// enforce in order signing of public keys
const signed = input.pubkeys.some((pubKey, i) => {
if (!ourPubKey.equals(pubKey)) return false;
if (input.signatures[i]) throw new Error('Signature already exists');
// TODO: add tests
if (ourPubKey.length !== 33 && input.hasWitness) {
throw new Error(
'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH',
);
}
const signature = keyPair.sign(signatureHash, this.__USE_LOW_R);
input.signatures[i] = bscript.signature.encode(signature, hashType);
return true;
});
if (!signed) throw new Error('Key pair cannot sign for this input');
} }
__addInputUnsafe(txHash, vout, options) { __addInputUnsafe(txHash, vout, options) {
if (transaction_1.Transaction.isCoinbaseHash(txHash)) { if (transaction_1.Transaction.isCoinbaseHash(txHash)) {
@ -1007,3 +934,115 @@ function checkSignArgs(txb, signParams) {
break; break;
} }
} }
function trySign(input, ourPubKey, keyPair, signatureHash, hashType, useLowR) {
// enforce in order signing of public keys
const signed = input.pubkeys.some((pubKey, i) => {
if (!ourPubKey.equals(pubKey)) return false;
if (input.signatures[i]) throw new Error('Signature already exists');
// TODO: add tests
if (ourPubKey.length !== 33 && input.hasWitness) {
throw new Error(
'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH',
);
}
const signature = keyPair.sign(signatureHash, useLowR);
input.signatures[i] = bscript.signature.encode(signature, hashType);
return true;
});
if (!signed) throw new Error('Key pair cannot sign for this input');
}
function getSigningData(
txb,
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') {
checkSignArgs(txb, signParams);
({
vin,
keyPair,
redeemScript,
hashType,
witnessValue,
witnessScript,
} = signParams);
} else {
throw new TypeError(
'TransactionBuilder sign first arg must be TxbSignArg or number',
);
}
if (keyPair === undefined) {
throw new Error('sign requires keypair');
}
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== txb.network)
throw new TypeError('Inconsistent network');
// @ts-ignore
if (!txb.__INPUTS[vin]) throw new Error('No input at index: ' + vin);
hashType = hashType || transaction_1.Transaction.SIGHASH_ALL;
// @ts-ignore
if (txb.__needsOutputs(hashType))
throw new Error('Transaction needs outputs');
// @ts-ignore
const input = txb.__INPUTS[vin];
// if redeemScript was previously provided, enforce consistency
if (
input.redeemScript !== undefined &&
redeemScript &&
!input.redeemScript.equals(redeemScript)
) {
throw new Error('Inconsistent redeemScript');
}
const ourPubKey = keyPair.publicKey || keyPair.getPublicKey();
if (!canSign(input)) {
if (witnessValue !== undefined) {
if (input.value !== undefined && input.value !== witnessValue)
throw new Error('Input did not match witnessValue');
typeforce(types.Satoshi, witnessValue);
input.value = witnessValue;
}
if (!canSign(input)) {
const prepared = prepareInput(
input,
ourPubKey,
redeemScript,
witnessScript,
);
// updates inline
Object.assign(input, prepared);
}
if (!canSign(input)) throw Error(input.prevOutType + ' not supported');
}
// ready to sign
let signatureHash;
if (input.hasWitness) {
// @ts-ignore
signatureHash = txb.__TX.hashForWitnessV0(
vin,
input.signScript,
input.value,
hashType,
);
} else {
// @ts-ignore
signatureHash = txb.__TX.hashForSignature(vin, input.signScript, hashType);
}
return {
input,
ourPubKey,
keyPair,
signatureHash,
hashType,
};
}

264
ts_src/transaction_builder.ts

@ -243,110 +243,27 @@ export class TransactionBuilder {
witnessValue?: number, witnessValue?: number,
witnessScript?: Buffer, witnessScript?: Buffer,
): void { ): void {
let vin: number; const data = getSigningData(
if (typeof signParams === 'number') { this,
console.warn( signParams,
'DEPRECATED: TransactionBuilder sign method arguments ' + keyPair,
'will change in v6, please use the TxbSignArg interface', redeemScript,
); hashType,
vin = signParams; witnessValue,
} else if (typeof signParams === 'object') { witnessScript,
checkSignArgs(this, signParams); );
({
vin,
keyPair,
redeemScript,
hashType,
witnessValue,
witnessScript,
} = signParams);
} else {
throw new TypeError(
'TransactionBuilder sign first arg must be TxbSignArg or number',
);
}
if (keyPair === undefined) {
throw new Error('sign requires keypair');
}
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== this.network)
throw new TypeError('Inconsistent network');
if (!this.__INPUTS[vin]) throw new Error('No input at index: ' + vin);
hashType = hashType || Transaction.SIGHASH_ALL;
if (this.__needsOutputs(hashType))
throw new Error('Transaction needs outputs');
const input = this.__INPUTS[vin];
// if redeemScript was previously provided, enforce consistency
if (
input.redeemScript !== undefined &&
redeemScript &&
!input.redeemScript.equals(redeemScript)
) {
throw new Error('Inconsistent redeemScript');
}
const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!();
if (!canSign(input)) {
if (witnessValue !== undefined) {
if (input.value !== undefined && input.value !== witnessValue)
throw new Error('Input did not match witnessValue');
typeforce(types.Satoshi, witnessValue);
input.value = witnessValue;
}
if (!canSign(input)) {
const prepared = prepareInput(
input,
ourPubKey,
redeemScript,
witnessScript,
);
// updates inline
Object.assign(input, prepared);
}
if (!canSign(input)) throw Error(input.prevOutType + ' not supported');
}
// ready to sign
let signatureHash: Buffer;
if (input.hasWitness) {
signatureHash = this.__TX.hashForWitnessV0(
vin,
input.signScript as Buffer,
input.value as number,
hashType,
);
} else {
signatureHash = this.__TX.hashForSignature(
vin,
input.signScript as Buffer,
hashType,
);
}
// enforce in order signing of public keys
const signed = input.pubkeys!.some((pubKey, i) => {
if (!ourPubKey.equals(pubKey!)) return false;
if (input.signatures![i]) throw new Error('Signature already exists');
// TODO: add tests
if (ourPubKey.length !== 33 && input.hasWitness) {
throw new Error(
'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH',
);
}
const signature = keyPair!.sign(signatureHash, this.__USE_LOW_R); const { input, ourPubKey, signatureHash } = data;
input.signatures![i] = bscript.signature.encode(signature, hashType!); ({ keyPair, hashType } = data);
return true;
});
if (!signed) throw new Error('Key pair cannot sign for this input'); trySign(
input,
ourPubKey,
keyPair,
signatureHash,
hashType,
this.__USE_LOW_R,
);
} }
private __addInputUnsafe( private __addInputUnsafe(
@ -1243,3 +1160,146 @@ function checkSignArgs(txb: TransactionBuilder, signParams: TxbSignArg): void {
break; break;
} }
} }
function trySign(
input: TxbInput,
ourPubKey: Buffer,
keyPair: ECPairInterface,
signatureHash: Buffer,
hashType: number,
useLowR: boolean,
): void {
// enforce in order signing of public keys
const signed = input.pubkeys!.some((pubKey, i) => {
if (!ourPubKey.equals(pubKey!)) return false;
if (input.signatures![i]) throw new Error('Signature already exists');
// TODO: add tests
if (ourPubKey.length !== 33 && input.hasWitness) {
throw new Error(
'BIP143 rejects uncompressed public keys in P2WPKH or P2WSH',
);
}
const signature = keyPair.sign(signatureHash, useLowR);
input.signatures![i] = bscript.signature.encode(signature, hashType);
return true;
});
if (!signed) throw new Error('Key pair cannot sign for this input');
}
function getSigningData(
txb: TransactionBuilder,
signParams: number | TxbSignArg,
keyPair?: ECPairInterface,
redeemScript?: Buffer,
hashType?: number,
witnessValue?: number,
witnessScript?: Buffer,
): {
input: TxbInput;
ourPubKey: Buffer;
keyPair: ECPairInterface;
signatureHash: Buffer;
hashType: number;
} {
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') {
checkSignArgs(txb, signParams);
({
vin,
keyPair,
redeemScript,
hashType,
witnessValue,
witnessScript,
} = signParams);
} else {
throw new TypeError(
'TransactionBuilder sign first arg must be TxbSignArg or number',
);
}
if (keyPair === undefined) {
throw new Error('sign requires keypair');
}
// TODO: remove keyPair.network matching in 4.0.0
if (keyPair.network && keyPair.network !== txb.network)
throw new TypeError('Inconsistent network');
// @ts-ignore
if (!txb.__INPUTS[vin]) throw new Error('No input at index: ' + vin);
hashType = hashType || Transaction.SIGHASH_ALL;
// @ts-ignore
if (txb.__needsOutputs(hashType))
throw new Error('Transaction needs outputs');
// @ts-ignore
const input = txb.__INPUTS[vin];
// if redeemScript was previously provided, enforce consistency
if (
input.redeemScript !== undefined &&
redeemScript &&
!input.redeemScript.equals(redeemScript)
) {
throw new Error('Inconsistent redeemScript');
}
const ourPubKey = keyPair.publicKey || keyPair.getPublicKey!();
if (!canSign(input)) {
if (witnessValue !== undefined) {
if (input.value !== undefined && input.value !== witnessValue)
throw new Error('Input did not match witnessValue');
typeforce(types.Satoshi, witnessValue);
input.value = witnessValue;
}
if (!canSign(input)) {
const prepared = prepareInput(
input,
ourPubKey,
redeemScript,
witnessScript,
);
// updates inline
Object.assign(input, prepared);
}
if (!canSign(input)) throw Error(input.prevOutType + ' not supported');
}
// ready to sign
let signatureHash: Buffer;
if (input.hasWitness) {
// @ts-ignore
signatureHash = txb.__TX.hashForWitnessV0(
vin,
input.signScript as Buffer,
input.value as number,
hashType,
);
} else {
// @ts-ignore
signatureHash = txb.__TX.hashForSignature(
vin,
input.signScript as Buffer,
hashType,
);
}
return {
input,
ourPubKey,
keyPair,
signatureHash,
hashType,
};
}

Loading…
Cancel
Save