add bare multisig support
This commit is contained in:
parent
4e007e22be
commit
4f99bf0882
|
@ -687,6 +687,33 @@ Script.buildMultisigOut = function(publicKeys, threshold, opts) {
|
|||
return script;
|
||||
};
|
||||
|
||||
/**
|
||||
* A new Multisig input script for the given public keys, requiring m of those public keys to spend
|
||||
*
|
||||
* @param {PublicKey[]} pubkeys list of all public keys controlling the output
|
||||
* @param {number} threshold amount of required signatures to spend the output
|
||||
* @param {Array} signatures and array of signature buffers to append to the script
|
||||
* @param {Object=} opts
|
||||
* @param {boolean=} opts.noSorting don't sort the given public keys before creating the script (false by default)
|
||||
* @param {Script=} opts.cachedMultisig don't recalculate the redeemScript
|
||||
*
|
||||
* @returns {Script}
|
||||
*/
|
||||
Script.buildMultisigIn = function(pubkeys, threshold, signatures, opts) {
|
||||
$.checkArgument(_.isArray(pubkeys));
|
||||
$.checkArgument(_.isNumber(threshold));
|
||||
$.checkArgument(_.isArray(signatures));
|
||||
opts = opts || {};
|
||||
var s = new Script();
|
||||
s.add(Opcode.OP_0);
|
||||
_.each(signatures, function(signature) {
|
||||
$.checkArgument(BufferUtil.isBuffer(signature), 'Signatures must be an array of Buffers');
|
||||
// TODO: allow signatures to be an array of Signature objects
|
||||
s.add(signature);
|
||||
});
|
||||
return s;
|
||||
};
|
||||
|
||||
/**
|
||||
* A new P2SH Multisig input script for the given public keys, requiring m of those public keys to spend
|
||||
*
|
||||
|
|
|
@ -2,4 +2,5 @@ module.exports = require('./input');
|
|||
|
||||
module.exports.PublicKey = require('./publickey');
|
||||
module.exports.PublicKeyHash = require('./publickeyhash');
|
||||
module.exports.MultiSig = require('./multisig.js');
|
||||
module.exports.MultiSigScriptHash = require('./multisigscripthash.js');
|
||||
|
|
|
@ -0,0 +1,165 @@
|
|||
'use strict';
|
||||
|
||||
var _ = require('lodash');
|
||||
var inherits = require('inherits');
|
||||
var Input = require('./input');
|
||||
var Output = require('../output');
|
||||
var $ = require('../../util/preconditions');
|
||||
|
||||
var Script = require('../../script');
|
||||
var Signature = require('../../crypto/signature');
|
||||
var Sighash = require('../sighash');
|
||||
var PublicKey = require('../../publickey');
|
||||
var BufferUtil = require('../../util/buffer');
|
||||
var TransactionSignature = require('../signature');
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function MultiSigInput(input, pubkeys, threshold, signatures) {
|
||||
Input.apply(this, arguments);
|
||||
var self = this;
|
||||
pubkeys = pubkeys || input.publicKeys;
|
||||
threshold = threshold || input.threshold;
|
||||
signatures = signatures || input.signatures;
|
||||
this.publicKeys = _.sortBy(pubkeys, function(publicKey) { return publicKey.toString('hex'); });
|
||||
$.checkState(Script.buildMultisigOut(this.publicKeys, threshold).equals(this.output.script),
|
||||
'Provided public keys don\'t match to the provided output script');
|
||||
this.publicKeyIndex = {};
|
||||
_.each(this.publicKeys, function(publicKey, index) {
|
||||
self.publicKeyIndex[publicKey.toString()] = index;
|
||||
});
|
||||
this.threshold = threshold;
|
||||
// Empty array of signatures
|
||||
this.signatures = signatures ? this._deserializeSignatures(signatures) : new Array(this.publicKeys.length);
|
||||
}
|
||||
inherits(MultiSigInput, Input);
|
||||
|
||||
MultiSigInput.prototype.toObject = function() {
|
||||
var obj = Input.prototype.toObject.apply(this, arguments);
|
||||
obj.threshold = this.threshold;
|
||||
obj.publicKeys = _.map(this.publicKeys, function(publicKey) { return publicKey.toString(); });
|
||||
obj.signatures = this._serializeSignatures();
|
||||
return obj;
|
||||
};
|
||||
|
||||
MultiSigInput.prototype._deserializeSignatures = function(signatures) {
|
||||
return _.map(signatures, function(signature) {
|
||||
if (!signature) {
|
||||
return undefined;
|
||||
}
|
||||
return new TransactionSignature(signature);
|
||||
});
|
||||
};
|
||||
|
||||
MultiSigInput.prototype._serializeSignatures = function() {
|
||||
return _.map(this.signatures, function(signature) {
|
||||
if (!signature) {
|
||||
return undefined;
|
||||
}
|
||||
return signature.toObject();
|
||||
});
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.getSignatures = function(transaction, privateKey, index, sigtype) {
|
||||
$.checkState(this.output instanceof Output);
|
||||
sigtype = sigtype || Signature.SIGHASH_ALL;
|
||||
|
||||
var self = this;
|
||||
var results = [];
|
||||
_.each(this.publicKeys, function(publicKey) {
|
||||
if (publicKey.toString() === privateKey.publicKey.toString()) {
|
||||
results.push(new TransactionSignature({
|
||||
publicKey: privateKey.publicKey,
|
||||
prevTxId: self.prevTxId,
|
||||
outputIndex: self.outputIndex,
|
||||
inputIndex: index,
|
||||
signature: Sighash.sign(transaction, privateKey, sigtype, index, self.output.script),
|
||||
sigtype: sigtype
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.addSignature = function(transaction, signature) {
|
||||
$.checkState(!this.isFullySigned(), 'All needed signatures have already been added');
|
||||
$.checkArgument(!_.isUndefined(this.publicKeyIndex[signature.publicKey.toString()]),
|
||||
'Signature has no matching public key');
|
||||
$.checkState(this.isValidSignature(transaction, signature));
|
||||
this.signatures[this.publicKeyIndex[signature.publicKey.toString()]] = signature;
|
||||
this._updateScript();
|
||||
return this;
|
||||
};
|
||||
|
||||
MultiSigInput.prototype._updateScript = function() {
|
||||
this.setScript(Script.buildMultisigIn(
|
||||
this.publicKeys,
|
||||
this.threshold,
|
||||
this._createSignatures()
|
||||
));
|
||||
return this;
|
||||
};
|
||||
|
||||
MultiSigInput.prototype._createSignatures = function() {
|
||||
return _.map(
|
||||
_.filter(this.signatures, function(signature) { return !_.isUndefined(signature); }),
|
||||
function(signature) {
|
||||
return BufferUtil.concat([
|
||||
signature.signature.toDER(),
|
||||
BufferUtil.integerAsSingleByteBuffer(signature.sigtype)
|
||||
]);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.clearSignatures = function() {
|
||||
this.signatures = new Array(this.publicKeys.length);
|
||||
this._updateScript();
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.isFullySigned = function() {
|
||||
return this.countSignatures() === this.threshold;
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.countMissingSignatures = function() {
|
||||
return this.threshold - this.countSignatures();
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.countSignatures = function() {
|
||||
return _.reduce(this.signatures, function(sum, signature) {
|
||||
return sum + (!!signature);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.publicKeysWithoutSignature = function() {
|
||||
var self = this;
|
||||
return _.filter(this.publicKeys, function(publicKey) {
|
||||
return !(self.signatures[self.publicKeyIndex[publicKey.toString()]]);
|
||||
});
|
||||
};
|
||||
|
||||
MultiSigInput.prototype.isValidSignature = function(transaction, signature) {
|
||||
// FIXME: Refactor signature so this is not necessary
|
||||
signature.signature.nhashtype = signature.sigtype;
|
||||
return Sighash.verify(
|
||||
transaction,
|
||||
signature.signature,
|
||||
signature.publicKey,
|
||||
signature.inputIndex,
|
||||
this.output.script
|
||||
);
|
||||
};
|
||||
|
||||
MultiSigInput.OPCODES_SIZE = 7; // serialized size (<=3) + 0 .. N .. M OP_CHECKMULTISIG
|
||||
MultiSigInput.SIGNATURE_SIZE = 74; // size (1) + DER (<=72) + sighash (1)
|
||||
MultiSigInput.PUBKEY_SIZE = 34; // size (1) + DER (<=33)
|
||||
|
||||
MultiSigInput.prototype._estimateSize = function() {
|
||||
return MultiSigInput.OPCODES_SIZE +
|
||||
this.threshold * MultiSigInput.SIGNATURE_SIZE +
|
||||
this.publicKeys.length * MultiSigInput.PUBKEY_SIZE;
|
||||
};
|
||||
|
||||
module.exports = MultiSigInput;
|
Loading…
Reference in New Issue