bitcore-lib
Version:
A pure and powerful JavaScript Bitcoin library.
204 lines (183 loc) • 6.99 kB
JavaScript
'use strict';
var inherits = require('inherits');
var $ = require('../../util/preconditions');
var BufferUtil = require('../../util/buffer');
var Address = require('../../address');
var Hash = require('../../crypto/hash');
var Input = require('./input');
var Output = require('../output');
var Sighash = require('../sighash');
var SighashWitness = require('../sighashwitness');
var BufferWriter = require('../../encoding/bufferwriter');
var BufferUtil = require('../../util/buffer');
var Script = require('../../script');
var Signature = require('../../crypto/signature');
var TransactionSignature = require('../signature');
/**
* Represents a special kind of input of PayToPublicKeyHash kind.
* @constructor
*/
function PublicKeyHashInput() {
Input.apply(this, arguments);
}
inherits(PublicKeyHashInput, Input);
PublicKeyHashInput.prototype.getRedeemScript = function(publicKey) {
if (!this.redeemScript) {
var redeemScript = Script.buildWitnessV0Out(publicKey);
if (Script.buildScriptHashOut(redeemScript).equals(this.output.script)) {
var scriptSig = new Script();
scriptSig.add(redeemScript.toBuffer());
this.setScript(scriptSig);
this.redeemScript = redeemScript;
}
}
return this.redeemScript;
};
PublicKeyHashInput.prototype.getScriptCode = function(publicKey) {
var writer = new BufferWriter();
var script;
if (this.output.script.isScriptHashOut()) {
script = this.getRedeemScript(publicKey);
} else {
script = this.output.script;
}
var scriptBuffer = Script.buildPublicKeyHashOut(script.toAddress()).toBuffer();
writer.writeVarintNum(scriptBuffer.length);
writer.write(scriptBuffer);
return writer.toBuffer();
};
PublicKeyHashInput.prototype.getSighash = function(transaction, privateKey, index, sigtype) {
var scriptCode = this.getScriptCode(privateKey);
var satoshisBuffer = this.getSatoshisBuffer();
return SighashWitness.sighash(transaction, sigtype, index, scriptCode, satoshisBuffer);
};
/**
* @param {Transaction} transaction - the transaction to be signed
* @param {PrivateKey} privateKey - the private key with which to sign the transaction
* @param {number} index - the index of the input in the transaction input vector
* @param {number} sigtype - the type of signature, defaults to Signature.SIGHASH_ALL
* @param {Buffer} hashData - the precalculated hash of the public key associated with the privateKey provided
* @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr'
* @param {Buffer} merkleRoot - unused for this input type
* @return {Array<TransactionSignature>}
*/
PublicKeyHashInput.prototype.getSignatures = function(transaction, privateKey, index, sigtype, hashData, signingMethod, merkleRoot) {
$.checkState(this.output instanceof Output);
hashData = hashData || Hash.sha256ripemd160(privateKey.publicKey.toBuffer());
sigtype = sigtype || Signature.SIGHASH_ALL;
signingMethod = signingMethod || 'ecdsa'; // unused. Keeping for consistency with other libs
var script;
if (this.output.script.isScriptHashOut()) {
script = this.getRedeemScript(privateKey.publicKey);
} else {
script = this.output.script;
}
if (script && BufferUtil.equals(hashData, script.getPublicKeyHash())) {
var signature;
if (script.isWitnessPublicKeyHashOut()) {
var satoshisBuffer = this.getSatoshisBuffer();
var scriptCode = this.getScriptCode(privateKey.publicKey);
signature = SighashWitness.sign(transaction, privateKey, sigtype, index, scriptCode, satoshisBuffer);
} else {
signature = Sighash.sign(transaction, privateKey, sigtype, index, this.output.script);
}
return [new TransactionSignature({
publicKey: privateKey.publicKey,
prevTxId: this.prevTxId,
outputIndex: this.outputIndex,
inputIndex: index,
signature: signature,
sigtype: sigtype
})];
}
return [];
};
/* jshint maxparams: 3 */
/**
* Add the provided signature
*
* @param {Transaction} transaction
* @param {Object} signature
* @param {PublicKey} signature.publicKey
* @param {Signature} signature.signature
* @param {number=} signature.sigtype
* @param {String} signingMethod - method used to sign - 'ecdsa' or 'schnorr' (future signing method)
* @return {PublicKeyHashInput} this, for chaining
*/
PublicKeyHashInput.prototype.addSignature = function(transaction, signature, signingMethod) {
$.checkState(this.isValidSignature(transaction, signature, signingMethod), 'Signature is invalid');
if (this.output.script.isWitnessPublicKeyHashOut() || this.output.script.isScriptHashOut()) {
this.setWitnesses([
BufferUtil.concat([
signature.signature.toDER(),
BufferUtil.integerAsSingleByteBuffer(signature.sigtype)
]),
signature.publicKey.toBuffer()
]);
} else {
this.setScript(Script.buildPublicKeyHashIn(
signature.publicKey,
signature.signature.toDER(),
signature.sigtype
));
}
return this;
};
/**
* Clear the input's signature
* @return {PublicKeyHashInput} this, for chaining
*/
PublicKeyHashInput.prototype.clearSignatures = function() {
this.setScript(Script.empty());
this.setWitnesses([]);
return this;
};
/**
* Query whether the input is signed
* @return {boolean}
*/
PublicKeyHashInput.prototype.isFullySigned = function() {
return this.script.isPublicKeyHashIn() || this.hasWitnesses();
};
PublicKeyHashInput.prototype.isValidSignature = function(transaction, signature, signingMethod) {
signingMethod = signingMethod || 'ecdsa'; // unused. Keeping for consistency with other libs
// FIXME: Refactor signature so this is not necessary
signature.signature.nhashtype = signature.sigtype;
if (this.output.script.isWitnessPublicKeyHashOut() || this.output.script.isScriptHashOut()) {
var scriptCode = this.getScriptCode();
var satoshisBuffer = this.getSatoshisBuffer();
return SighashWitness.verify(
transaction,
signature.signature,
signature.publicKey,
signature.inputIndex,
scriptCode,
satoshisBuffer
);
} else {
return Sighash.verify(
transaction,
signature.signature,
signature.publicKey,
signature.inputIndex,
this.output.script
);
}
};
PublicKeyHashInput.SCRIPT_MAX_SIZE = 73 + 34; // sigsize (1 + 72) + pubkey (1 + 33)
PublicKeyHashInput.REDEEM_SCRIPT_SIZE = 1 + 22; // len (1) OP_0 (1) pubkeyhash (1 + 20)
PublicKeyHashInput.prototype._estimateSize = function() {
let result = this._getBaseSize();
result += 1; // script size
const WITNESS_DISCOUNT = 4;
const witnessSize = PublicKeyHashInput.SCRIPT_MAX_SIZE / WITNESS_DISCOUNT;
if (this.output.script.isWitnessPublicKeyHashOut()) {
result += witnessSize;
} else if (this.output.script.isScriptHashOut()) {
result += witnessSize + PublicKeyHashInput.REDEEM_SCRIPT_SIZE;
} else {
result += PublicKeyHashInput.SCRIPT_MAX_SIZE;
}
return result;
};
module.exports = PublicKeyHashInput;