From ff0f9f621653a3dfca8d56dbf90d66d6b7e5f253 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 30 Oct 2015 14:04:23 +0100 Subject: [PATCH 1/7] fixed wrong option name in jsdoc --- lib/transaction/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index 03875b7..386d328 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -137,7 +137,7 @@ Transaction.prototype._getHash = function() { * * `disableAll`: disable all checks * * `disableSmallFees`: disable checking for fees that are too small * * `disableLargeFees`: disable checking for fees that are too large - * * `disableNotFullySigned`: disable checking if all inputs are fully signed + * * `disableIsFullySigned`: disable checking if all inputs are fully signed * * `disableDustOutputs`: disable checking if there are no outputs that are dust amounts * * `disableMoreOutputThanInput`: disable checking if the transaction spends more bitcoins than the sum of the input amounts * @return {string} From 4e007e22bea0b3645687318b11c7a6c95ac1b4b7 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 30 Oct 2015 14:04:44 +0100 Subject: [PATCH 2/7] make Sighash accessible --- lib/transaction/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/transaction/index.js b/lib/transaction/index.js index c03b0c8..79c9574 100644 --- a/lib/transaction/index.js +++ b/lib/transaction/index.js @@ -4,3 +4,4 @@ module.exports.Input = require('./input'); module.exports.Output = require('./output'); module.exports.UnspentOutput = require('./unspentoutput'); module.exports.Signature = require('./signature'); +module.exports.Sighash = require('./sighash'); From 4f99bf08823c68cc17d19fb3956682089bbfa03c Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 30 Oct 2015 14:05:41 +0100 Subject: [PATCH 3/7] add bare multisig support --- lib/script/script.js | 27 +++++ lib/transaction/input/index.js | 1 + lib/transaction/input/multisig.js | 165 ++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+) create mode 100644 lib/transaction/input/multisig.js diff --git a/lib/script/script.js b/lib/script/script.js index f6b8edb..176efdb 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -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 * diff --git a/lib/transaction/input/index.js b/lib/transaction/input/index.js index 1744ea7..7005ed0 100644 --- a/lib/transaction/input/index.js +++ b/lib/transaction/input/index.js @@ -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'); diff --git a/lib/transaction/input/multisig.js b/lib/transaction/input/multisig.js new file mode 100644 index 0000000..2511a49 --- /dev/null +++ b/lib/transaction/input/multisig.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; From af70e695885ff60041b241889ce95fb64b359091 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 30 Oct 2015 15:52:58 +0100 Subject: [PATCH 4/7] add test for bare multisig --- lib/transaction/transaction.js | 11 ++- test/transaction/input/multisig.js | 113 +++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 test/transaction/input/multisig.js diff --git a/lib/transaction/transaction.js b/lib/transaction/transaction.js index 386d328..225fb35 100644 --- a/lib/transaction/transaction.js +++ b/lib/transaction/transaction.js @@ -20,6 +20,7 @@ var Input = require('./input'); var PublicKeyHashInput = Input.PublicKeyHash; var PublicKeyInput = Input.PublicKey; var MultiSigScriptHashInput = Input.MultiSigScriptHash; +var MultiSigInput = Input.MultiSig; var Output = require('./output'); var Script = require('../script'); var PrivateKey = require('../privatekey'); @@ -577,8 +578,16 @@ Transaction.prototype._fromNonP2SH = function(utxo) { Transaction.prototype._fromMultisigUtxo = function(utxo, pubkeys, threshold) { $.checkArgument(threshold <= pubkeys.length, 'Number of required signatures must be greater than the number of public keys'); + var clazz; utxo = new UnspentOutput(utxo); - this.addInput(new MultiSigScriptHashInput({ + if (utxo.script.isMultisigOut()) { + clazz = MultiSigInput; + } else if (utxo.script.isScriptHashOut()) { + clazz = MultiSigScriptHashInput; + } else { + throw new Error("@TODO"); + } + this.addInput(new clazz({ output: new Output({ script: utxo.script, satoshis: utxo.satoshis diff --git a/test/transaction/input/multisig.js b/test/transaction/input/multisig.js new file mode 100644 index 0000000..bdc9866 --- /dev/null +++ b/test/transaction/input/multisig.js @@ -0,0 +1,113 @@ +'use strict'; +/* jshint unused: false */ + +var should = require('chai').should(); +var expect = require('chai').expect; +var _ = require('lodash'); + +var bitcore = require('../../..'); +var Transaction = bitcore.Transaction; +var PrivateKey = bitcore.PrivateKey; +var Address = bitcore.Address; +var Script = bitcore.Script; +var Signature = bitcore.crypto.Signature; +var MultiSigInput = bitcore.Transaction.Input.MultiSig; + +describe('MultiSigInput', function() { + + 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 = { + txId: '66e64ef8a3b384164b78453fa8c8194de9a473ba14f89485a0e433699daec140', + outputIndex: 0, + script: new Script("5221025c95ec627038e85b5688a9b3d84d28c5ebe66e8c8d697d498e20fe96e3b1ab1d2102cdddfc974d41a62f1f80081deee70592feb7d6e6cf6739d6592edbe7946720e72103c95924e02c240b5545089c69c6432447412b58be43fd671918bd184a5009834353ae"), + 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); + }); + it('can clear all signatures', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000) + .sign(privateKey1) + .sign(privateKey2); + + var input = transaction.inputs[0]; + input.isFullySigned().should.equal(true); + input.clearSignatures(); + input.isFullySigned().should.equal(false); + }); + it('can estimate how heavy is the output going to be', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000); + var input = transaction.inputs[0]; + input._estimateSize().should.equal(257); + }); + it('uses SIGHASH_ALL by default', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000); + var input = transaction.inputs[0]; + var sigs = input.getSignatures(transaction, privateKey1, 0); + sigs[0].sigtype.should.equal(Signature.SIGHASH_ALL); + }); + it('roundtrips to/from object', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000) + .sign(privateKey1); + var input = transaction.inputs[0]; + var roundtrip = new MultiSigInput(input.toObject()); + roundtrip.toObject().should.deep.equal(input.toObject()); + }); + it('roundtrips to/from object when not signed', function() { + var transaction = new Transaction() + .from(output, [public1, public2, public3], 2) + .to(address, 1000000); + var input = transaction.inputs[0]; + var roundtrip = new MultiSigInput(input.toObject()); + roundtrip.toObject().should.deep.equal(input.toObject()); + }); +}); From 6e1e4e69c49b0ca88a692d44a7ead304334e8ba3 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 30 Oct 2015 16:11:41 +0100 Subject: [PATCH 5/7] add function to parse list of signatures buffers into proper list of TransactionSignature objects --- lib/transaction/input/multisig.js | 48 +++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/lib/transaction/input/multisig.js b/lib/transaction/input/multisig.js index 2511a49..2767a6f 100644 --- a/lib/transaction/input/multisig.js +++ b/lib/transaction/input/multisig.js @@ -2,6 +2,7 @@ var _ = require('lodash'); var inherits = require('inherits'); +var Transaction = require('../transaction'); var Input = require('./input'); var Output = require('../output'); var $ = require('../../util/preconditions'); @@ -152,6 +153,53 @@ MultiSigInput.prototype.isValidSignature = function(transaction, signature) { ); }; +/** + * + * @param {Buffer[]} signatures + * @param {PublicKey[]} publicKeys + * @param {Transaction} transaction + * @param {Integer} inputIndex + * @param {Input} input + * @returns {TransactionSignature[]} + */ +MultiSigInput.normalizeSignatures = function(transaction, input, inputIndex, signatures, publicKeys) { + return publicKeys.map(function (pubKey) { + var signatureMatch = null; + signatures = signatures.filter(function (signatureBuffer) { + if (signatureMatch) { + return true; + } + + var signature = new TransactionSignature({ + signature: Signature.fromTxFormat(signatureBuffer), + publicKey: pubKey, + prevTxId: input.prevTxId, + outputIndex: input.outputIndex, + inputIndex: inputIndex, + sigtype: Signature.SIGHASH_ALL + }); + + signature.signature.nhashtype = signature.sigtype; + var isMatch = Sighash.verify( + transaction, + signature.signature, + signature.publicKey, + signature.inputIndex, + input.output.script + ); + + if (isMatch) { + signatureMatch = signature; + return false; + } + + return true; + }); + + return signatureMatch ? signatureMatch : null; + }); +}; + 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) From f9d9fa0622ecfb7aab2661d0d116fc93fab48198 Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 30 Oct 2015 17:07:04 +0100 Subject: [PATCH 6/7] add test for parsing list of signatures --- test/transaction/input/multisig.js | 68 +++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 2 deletions(-) diff --git a/test/transaction/input/multisig.js b/test/transaction/input/multisig.js index bdc9866..62d79b7 100644 --- a/test/transaction/input/multisig.js +++ b/test/transaction/input/multisig.js @@ -31,8 +31,8 @@ describe('MultiSigInput', function() { }; it('can count missing signatures', function() { var transaction = new Transaction() - .from(output, [public1, public2, public3], 2) - .to(address, 1000000); + .from(output, [public1, public2, public3], 2) + .to(address, 1000000); var input = transaction.inputs[0]; input.countSignatures().should.equal(0); @@ -47,6 +47,24 @@ describe('MultiSigInput', function() { input.countMissingSignatures().should.equal(0); input.isFullySigned().should.equal(true); }); + it('can count missing signatures, signed with key 3 and 1', 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(privateKey3); + input.countSignatures().should.equal(1); + input.countMissingSignatures().should.equal(1); + input.isFullySigned().should.equal(false); + + transaction.sign(privateKey1); + 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) @@ -110,4 +128,50 @@ describe('MultiSigInput', function() { var roundtrip = new MultiSigInput(input.toObject()); roundtrip.toObject().should.deep.equal(input.toObject()); }); + it('can parse list of signature buffers, from TX signed with key 1 and 2', function() { + var transaction = new Transaction("010000000140c1ae9d6933e4a08594f814ba73a4e94d19c8a83f45784b1684b3a3f84ee666000000009200473044022012bd2f15e56ab1b63d5ee23e194ed995ad4b81a21bcb8e0d913e5e791c07f7280220278bdb6b54cdc608193c869affe28dc2f700902218122770faff25c56142102b01483045022100e74e9955e042aca36f4f3ad907a0926c5b85e5d9608b0678a78a9cbc0259c7a2022053ff761e5f9a80558db7023e45c4979ac3c19a423f0184fb0596d3da308cc4b501ffffffff0140420f000000000017a91419438da7d16709643be5abd8df62ca4034a489a78700000000"); + + var inputObj = transaction.inputs[0].toObject(); + inputObj.output = output; + transaction.inputs[0] = new Transaction.Input(inputObj); + + inputObj.signatures = MultiSigInput.normalizeSignatures( + transaction, + transaction.inputs[0], + 0, + transaction.inputs[0].script.chunks.slice(1).map(function(s) { return s.buf; }), + [public1, public2, public3] + ); + + transaction.inputs[0] = new MultiSigInput(inputObj, [public1, public2, public3], 2); + + transaction.inputs[0].signatures[0].publicKey.should.deep.equal(public1); + transaction.inputs[0].signatures[1].publicKey.should.deep.equal(public2); + should.equal(transaction.inputs[0].signatures[2], undefined); + transaction.inputs[0].isValidSignature(transaction, transaction.inputs[0].signatures[0]).should.be.true; + transaction.inputs[0].isValidSignature(transaction, transaction.inputs[0].signatures[1]).should.be.true; + }); + it('can parse list of signature buffers, from TX signed with key 3 and 1', function() { + var transaction = new Transaction("010000000140c1ae9d6933e4a08594f814ba73a4e94d19c8a83f45784b1684b3a3f84ee666000000009300483045022100fc39ce4f51b2766ec8e978296e0594ea4578a3eb2543722fd4053e92bf16e6b1022030f739868397a881b019508b9c725a5c69a3652cb8928027748e93e67dfaef5501483045022100e74e9955e042aca36f4f3ad907a0926c5b85e5d9608b0678a78a9cbc0259c7a2022053ff761e5f9a80558db7023e45c4979ac3c19a423f0184fb0596d3da308cc4b501ffffffff0140420f000000000017a91419438da7d16709643be5abd8df62ca4034a489a78700000000"); + + var inputObj = transaction.inputs[0].toObject(); + inputObj.output = output; + transaction.inputs[0] = new Transaction.Input(inputObj); + + inputObj.signatures = MultiSigInput.normalizeSignatures( + transaction, + transaction.inputs[0], + 0, + transaction.inputs[0].script.chunks.slice(1).map(function(s) { return s.buf; }), + [public1, public2, public3] + ); + + transaction.inputs[0] = new MultiSigInput(inputObj, [public1, public2, public3], 2); + + transaction.inputs[0].signatures[0].publicKey.should.deep.equal(public1); + should.equal(transaction.inputs[0].signatures[1], undefined); + transaction.inputs[0].signatures[2].publicKey.should.deep.equal(public3); + transaction.inputs[0].isValidSignature(transaction, transaction.inputs[0].signatures[0]).should.be.true; + transaction.inputs[0].isValidSignature(transaction, transaction.inputs[0].signatures[2]).should.be.true; + }); }); From 502026086c523f2ce12a7d114e7b05ac14b1d68c Mon Sep 17 00:00:00 2001 From: Ruben de Vries Date: Fri, 6 Nov 2015 14:37:58 +0100 Subject: [PATCH 7/7] updated _estimateSize for bare multisig input --- lib/transaction/input/multisig.js | 8 +++----- test/transaction/input/multisig.js | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/transaction/input/multisig.js b/lib/transaction/input/multisig.js index 2767a6f..03a8413 100644 --- a/lib/transaction/input/multisig.js +++ b/lib/transaction/input/multisig.js @@ -200,14 +200,12 @@ MultiSigInput.normalizeSignatures = function(transaction, input, inputIndex, sig }); }; -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.OPCODES_SIZE = 1; // 0 +MultiSigInput.SIGNATURE_SIZE = 73; // size (1) + DER (<=72) MultiSigInput.prototype._estimateSize = function() { return MultiSigInput.OPCODES_SIZE + - this.threshold * MultiSigInput.SIGNATURE_SIZE + - this.publicKeys.length * MultiSigInput.PUBKEY_SIZE; + this.threshold * MultiSigInput.SIGNATURE_SIZE; }; module.exports = MultiSigInput; diff --git a/test/transaction/input/multisig.js b/test/transaction/input/multisig.js index 62d79b7..dd05034 100644 --- a/test/transaction/input/multisig.js +++ b/test/transaction/input/multisig.js @@ -101,7 +101,7 @@ describe('MultiSigInput', function() { .from(output, [public1, public2, public3], 2) .to(address, 1000000); var input = transaction.inputs[0]; - input._estimateSize().should.equal(257); + input._estimateSize().should.equal(147); }); it('uses SIGHASH_ALL by default', function() { var transaction = new Transaction()