diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index 292f7c6..fbb41f3 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -69,6 +69,15 @@ TxProposal.prototype._updateStatus = function() { }; +TxProposal.prototype._getCurrentSignatures = function() { + return _.map(_.some(this.actions, 'xpub'), function(x) { + return { + signatures: x.signatures, + xpub: xpub, + }; + }); +}; + TxProposal.prototype._getBitcoreTx = function() { var self = this; @@ -81,6 +90,13 @@ TxProposal.prototype._getBitcoreTx = function() { .change(this.changeAddress); t._updateChangeOutput(); + + + var sigs = this._getCurrentSignatures(); + _.each(sigs, function(x) { + this._addSignaturesToBitcoreTx(t, x.signatures, x.xpub); + }); + return t; }; @@ -121,22 +137,20 @@ TxProposal.prototype.getActionBy = function(copayerId) { }; }; -TxProposal.prototype.addAction = function(copayerId, type, signatures) { +TxProposal.prototype.addAction = function(copayerId, type, signatures, xpub) { var action = new TxProposalAction({ copayerId: copayerId, type: type, signatures: signatures, + xpub: xpub, }); this.actions[copayerId] = action; this._updateStatus(); }; -// TODO: no sure we should receive xpub or a list of pubkeys (pre derived) -TxProposal.prototype.checkSignatures = function(signatures, xpub) { +TxProposal.prototype._addSignaturesToBitcoreTx = function(t, signatures, xpub) { var self = this; - var t = this._getBitcoreTx(); - if (signatures.length != this.inputs.length) return false; @@ -159,17 +173,25 @@ TxProposal.prototype.checkSignatures = function(signatures, xpub) { t.applySignature(s); oks++; - } catch (e) { - // TODO only for debug now - console.log('DEBUG ONLY:', e.message); //TODO - }; + } catch (e) {}; }); - return oks === t.inputs.length; + + if (oks != t.inputs.length) + throw new Error('wrong signatures'); }; -TxProposal.prototype.sign = function(copayerId, signatures) { - this.addAction(copayerId, 'accept', signatures); +TxProposal.prototype.sign = function(copayerId, signatures, xpub) { + + // Tests signatures are OK + var t = this._getBitcoreTx(); + try { + this._addSignaturesToBitcoreTx(t, signatures, xpub); + this.addAction(copayerId, 'accept', signatures, xpub); + return true; + } catch (e) { + return false; + } }; TxProposal.prototype.reject = function(copayerId) { diff --git a/lib/model/txproposalaction.js b/lib/model/txproposalaction.js index 8e0f3af..13fa98b 100644 --- a/lib/model/txproposalaction.js +++ b/lib/model/txproposalaction.js @@ -7,6 +7,7 @@ function TxProposalAction(opts) { this.copayerId = opts.copayerId; this.type = opts.type || (opts.signatures ? 'accept' : 'reject'); this.signatures = opts.signatures; + this.xpub = opts.xpub; }; TxProposalAction.fromObj = function (obj) { @@ -16,6 +17,7 @@ TxProposalAction.fromObj = function (obj) { x.copayerId = obj.copayerId; x.type = obj.type; x.signatures = obj.signatures; + x.xpub = obj.xpub; return x; }; diff --git a/lib/server.js b/lib/server.js index 7aff2fc..d186758 100644 --- a/lib/server.js +++ b/lib/server.js @@ -585,9 +585,7 @@ CopayServer.prototype.removePendingTx = function(opts, cb) { CopayServer.prototype._broadcastTx = function(txp, cb) { var raw = txp.getRawTx(); var bc = this._getBlockExplorer('insight', txp.getNetworkName()); -console.log('[server.js.588:raw:]',raw); //TODO bc.broadcast(raw, function(err, txid) { -console.log('[server.js.589:err:]',err, txid); //TODO return cb(err, txid); }) }; @@ -622,11 +620,9 @@ CopayServer.prototype.signTx = function(opts, cb) { var copayer = wallet.getCopayer(self.copayerId); - if (!txp.checkSignatures(opts.signatures, copayer.xPubKey)) + if (!txp.sign(self.copayerId, opts.signatures, copayer.xPubKey)) return cb(new ClientError('BADSIGNATURES', 'Bad signatures')); - txp.sign(self.copayerId, opts.signatures); - self.storage.storeTx(self.walletId, txp, function(err) { if (err) return cb(err); diff --git a/test/integration.js b/test/integration.js index b4685f4..e80218c 100644 --- a/test/integration.js +++ b/test/integration.js @@ -114,7 +114,14 @@ helpers.createUtxos = function(server, wallet, amounts, cb) { }; -helpers.stubBlockExplorer = function(server, utxos, txid) { +helpers.stubBlockExplorer = function(server, inUtxos, txid) { + + var utxos = _.map(inUtxos, function(x) { + x.toObject = function() { + return this; + }; + return x; + }); var bc = sinon.stub(); bc.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos); diff --git a/test/txproposal.js b/test/txproposal.js index a3709bf..131862e 100644 --- a/test/txproposal.js +++ b/test/txproposal.js @@ -28,10 +28,10 @@ describe('TXProposal', function() { describe('#sign', function() { it('should sign 2-2', function() { var txp = TXP.fromObj(aTXP()); - txp.sign('1', theSignatures); + txp.sign('1', theSignatures, theXPub); txp.isAccepted().should.equal(false); txp.isRejected().should.equal(false); - txp.sign('2', theSignatures); + txp.sign('2', theSignatures, theXPub); txp.isAccepted().should.equal(true); txp.isRejected().should.equal(false); }); @@ -59,26 +59,10 @@ describe('TXProposal', function() { }); }); - - describe('#checkSignatures', function() { - it('should check signatures', function() { - var txp = TXP.fromObj(aTXP()); - var xpriv = new Bitcore.HDPrivateKey(theXPriv); - var priv = xpriv.derive(txp.inputPaths[0]).privateKey; - - var t = txp._getBitcoreTx(); - t.sign(priv); - - var s = t.getSignatures(priv)[0].signature.toDER().toString('hex'); - var xpub = new Bitcore.HDPublicKey(xpriv); - - var res = txp.checkSignatures([s], xpub); - res.should.equal(true); - }); - }); }); var theXPriv = 'xprv9s21ZrQH143K2rMHbXTJmWTuFx6ssqn1vyRoZqPkCXYchBSkp5ey8kMJe84sxfXq5uChWH4gk94rWbXZt2opN9kg4ufKGvUM7HQSLjnoh7e'; +var theXPub = 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2Q9npnVmdTAthYGc3N3uxm5sEdnTpSqBc4YYTAhNnoSxCm9'; var theSignatures = ['3045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9']; var aTXP = function() {