diff --git a/lib/transaction/input/multisigscripthash.js b/lib/transaction/input/multisigscripthash.js index ac26b1a45..6640d368a 100644 --- a/lib/transaction/input/multisigscripthash.js +++ b/lib/transaction/input/multisigscripthash.js @@ -92,8 +92,24 @@ MultiSigScriptHashInput.prototype.clearSignatures = function() { }; MultiSigScriptHashInput.prototype.isFullySigned = function() { - var count = _.reduce(this.signatures, function(sum, signature) { return sum + (!!signature); }, 0); - return count === this.threshold; + return this.countSignatures() === this.threshold; +}; + +MultiSigScriptHashInput.prototype.countMissingSignatures = function() { + return this.threshold - this.countSignatures(); +}; + +MultiSigScriptHashInput.prototype.countSignatures = function() { + return _.reduce(this.signatures, function(sum, signature) { + return sum + (!!signature); + }, 0); +}; + +MultiSigScriptHashInput.prototype.publicKeysWithoutSignature = function() { + var self = this; + return _.filter(this.publicKeys, function(publicKey) { + return !(self.signatures[self.publicKeyIndex[publicKey.toString()]]); + }); }; MultiSigScriptHashInput.prototype.isValidSignature = function(transaction, signature) { diff --git a/test/transaction/transaction.js b/test/transaction/transaction.js index 1d534fa9e..22223c031 100644 --- a/test/transaction/transaction.js +++ b/test/transaction/transaction.js @@ -61,6 +61,64 @@ describe('Transaction', function() { }); }); }); + + // TODO: Migrate this into a test for inputs + describe('MultiSigScriptHashInput', function() { + var MultiSigScriptHashInput = Transaction.Input.MultiSigScriptHash; + + var privateKey1 = new PrivateKey('KwF9LjRraetZuEjR8VqEq539z137LW5anYDUnVK11vM3mNMHTWb4'); + var privateKey2 = new PrivateKey('L4PqnaPTCkYhAqH3YQmefjxQP6zRcF4EJbdGqR8v6adtG9XSsadY'); + var privateKey3 = new PrivateKey('L4CTX79zFeksZTyyoFuPQAySfmP7fL3R41gWKTuepuN7hxuNuJwV'); + var public1 = privateKey1.publicKey; + var public2 = privateKey2.publicKey; + var public3 = privateKey3.publicKey; + var address = new Address('33zbk2aSZYdNbRsMPPt6jgy6Kq1kQreqeb'); + + var output = { + address: '33zbk2aSZYdNbRsMPPt6jgy6Kq1kQreqeb', + txId: '66e64ef8a3b384164b78453fa8c8194de9a473ba14f89485a0e433699daec140', + outputIndex: 0, + script: new Script(address), + satoshis: 1000000 + }; + it('can count missing signatures', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000); + var input = transaction.inputs[0]; + + input.countSignatures().should.equal(0); + + transaction.sign(privateKey1); + input.countSignatures().should.equal(1); + input.countMissingSignatures().should.equal(1); + input.isFullySigned().should.equal(false); + + transaction.sign(privateKey2); + input.countSignatures().should.equal(2); + input.countMissingSignatures().should.equal(0); + input.isFullySigned().should.equal(true); + }); + it('returns a list of public keys with missing signatures', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000); + var input = transaction.inputs[0]; + + _.all(input.publicKeysWithoutSignature(), function(publicKeyMissing) { + var serialized = publicKeyMissing.toString(); + return serialized === public1.toString() || + serialized === public2.toString() || + serialized === public3.toString(); + }).should.equal(true); + transaction.sign(privateKey1); + _.all(input.publicKeysWithoutSignature(), function(publicKeyMissing) { + var serialized = publicKeyMissing.toString(); + return serialized === public2.toString() || + serialized === public3.toString(); + }).should.equal(true); + }); + }); }); var tx_empty_hex = '01000000000000000000';