Merge pull request #1172 from matiu/big/inputSig3

Big/input sig3
This commit is contained in:
Gustavo Maximiliano Cortez 2014-08-22 17:03:43 -03:00
commit 20b49e8823
5 changed files with 186 additions and 101 deletions

View File

@ -328,6 +328,8 @@ PublicKeyRing.prototype.copayersForPubkeys = function(pubkeys, paths) {
} }
} }
} }
for(var i in inKeyMap) for(var i in inKeyMap)
throw new Error('Pubkey not identified') throw new Error('Pubkey not identified')

View File

@ -25,7 +25,7 @@ function TxProposal(opts) {
this.builder = opts.builder; this.builder = opts.builder;
this.createdTs = opts.createdTs; this.createdTs = opts.createdTs;
this.createdTs = opts.createdTs; this.createdTs = opts.createdTs;
this._inputSignatures = []; this._inputSigners = [];
// CopayerIds // CopayerIds
this.creator = opts.creator; this.creator = opts.creator;
@ -77,24 +77,23 @@ TxProposal.prototype.isPending = function(maxRejectCount) {
return true; return true;
}; };
TxProposal.prototype._updateSignedBy = function() { TxProposal.prototype._updateSignedBy = function() {
this._inputSignatures = []; this._inputSigners = [];
var tx = this.builder.build(); var tx = this.builder.build();
for (var i in tx.ins) { for (var i in tx.ins) {
var scriptSig = new Script(tx.ins[i].s); var scriptSig = new Script(tx.ins[i].s);
var signatureCount = scriptSig.countSignatures(); var signatureCount = scriptSig.countSignatures();
var info = TxProposal._infoFromRedeemScript(scriptSig); var info = TxProposal._infoFromRedeemScript(scriptSig);
var txSigHash = tx.hashForSignature(info.script, parseInt(i), Transaction.SIGHASH_ALL); var txSigHash = tx.hashForSignature(info.script, parseInt(i), Transaction.SIGHASH_ALL);
var signatureIndexes = TxProposal._verifySignatures(info.keys, scriptSig, txSigHash); var signersPubKey = TxProposal._verifySignatures(info.keys, scriptSig, txSigHash);
if (signatureIndexes.length !== signatureCount) if (signersPubKey.length !== signatureCount)
throw new Error('Invalid signature'); throw new Error('Invalid signature');
this._inputSignatures[i] = signatureIndexes.map(function(i) {
var r = info.keys[i].toString('hex'); this._inputSigners[i] = signersPubKey;
return r;
});
}; };
}; };
@ -163,8 +162,11 @@ TxProposal._formatKeys = function(keys) {
var k = new Key(); var k = new Key();
k.public = keys[i]; k.public = keys[i];
ret.push(k); ret.push({
}; keyObj: k,
keyHex: keys[i].toString('hex'),
});
}
return ret; return ret;
}; };
@ -183,8 +185,8 @@ TxProposal._verifySignatures = function(inKeys, scriptSig, txSigHash) {
var sigRaw = new Buffer(chunk.slice(0, chunk.length - 1)); var sigRaw = new Buffer(chunk.slice(0, chunk.length - 1));
for (var j in keys) { for (var j in keys) {
var k = keys[j]; var k = keys[j];
if (k.verifySignatureSync(txSigHash, sigRaw)) { if (k.keyObj.verifySignatureSync(txSigHash, sigRaw)) {
ret.push(parseInt(j)); ret.push(k.keyHex);
break; break;
} }
} }
@ -241,9 +243,9 @@ TxProposal.prototype.setSent = function(sentTxid) {
TxProposal.prototype._allSignatures = function() { TxProposal.prototype._allSignatures = function() {
var ret = {}; var ret = {};
for (var i in this._inputSignatures) for (var i in this._inputSigners)
for (var j in this._inputSignatures[i]) for (var j in this._inputSigners[i])
ret[this._inputSignatures[i][j]] = true; ret[this._inputSigners[i][j]] = true;
return ret; return ret;
}; };
@ -251,10 +253,10 @@ TxProposal.prototype._allSignatures = function() {
TxProposal.prototype.setCopayers = function(senderId, keyMap, readOnlyPeers) { TxProposal.prototype.setCopayers = function(senderId, keyMap, readOnlyPeers) {
var newCopayer = {}, var newCopayer = {},
oldCopayers = {}, oldCopayers = {},
newSignedBy = {}, newSignedBy = {},
readOnlyPeers = {}, readOnlyPeers = {},
isNew = 1; isNew = 1;
for (var k in this.signedBy) { for (var k in this.signedBy) {
oldCopayers[k] = 1; oldCopayers[k] = 1;
@ -274,9 +276,10 @@ TxProposal.prototype.setCopayers = function(senderId, keyMap, readOnlyPeers) {
} }
var iSig = this._inputSignatures[0]; var iSig = this._inputSigners[0];
for (var i in iSig) { for (var i in iSig) {
var copayerId = keyMap[iSig[i]]; var copayerId = keyMap[iSig[i]];
if (!copayerId) if (!copayerId)
throw new Error('Found unknown signature') throw new Error('Found unknown signature')

View File

@ -173,24 +173,32 @@ txId: ntxid
*/ */
Wallet.prototype._getKeyMap = function(txp) { Wallet.prototype._getKeyMap = function(txp) {
preconditions.checkArgument(txp); preconditions.checkArgument(txp);
var inSig0, keyMapAll = {};
var keyMap = this.publicKeyRing.copayersForPubkeys(txp._inputSignatures[0], txp.inputChainPaths); for (var i in txp._inputSigners) {
var keyMap = this.publicKeyRing.copayersForPubkeys(txp._inputSigners[i], txp.inputChainPaths);
var inSig = JSON.stringify(txp._inputSignatures[0].sort()); if (Object.keys(keyMap).length !== txp._inputSigners[i].length)
throw new Error('Signature does not match known copayers');
if (JSON.stringify(Object.keys(keyMap).sort()) !== inSig) { for (var j in keyMap) {
throw new Error('inputSignatures dont match know copayers pubkeys'); keyMapAll[j] = keyMap[j];
}
// From here -> only to check that all inputs have the same sigs
var inSigArr = [];
Object.keys(keyMap).forEach(function(k) {
inSigArr.push(keyMap[k]);
});
var inSig = JSON.stringify(inSigArr.sort());
if (i === '0') {
inSig0 = inSig;
continue;
}
if (inSig !== inSig0)
throw new Error('found inputs with different signatures');
} }
return keyMapAll;
var keyMapStr = JSON.stringify(keyMap);
// All inputs must be signed with the same copayers
for (var i in txp._inputSignatures) {
if (!i) continue;
var inSigX = JSON.stringify(txp._inputSignatures[i].sort());
if (inSigX !== inSig)
throw new Error('found inputs with different signatures:');
}
return keyMap;
}; };
@ -540,8 +548,8 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
opts.privateKey = PrivateKey.fromObj(o.privateKey); opts.privateKey = PrivateKey.fromObj(o.privateKey);
else else
opts.privateKey = new PrivateKey({ opts.privateKey = new PrivateKey({
networkName: opts.networkName networkName: opts.networkName
}); });
if (o.publicKeyRing) if (o.publicKeyRing)
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing); opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
@ -717,7 +725,7 @@ Wallet.prototype.purgeTxProposals = function(deleteAll) {
this.store(); this.store();
var n = this.txProposals.length(); var n = this.txProposals.length();
return m-n; return m - n;
}; };
Wallet.prototype.reject = function(ntxid) { Wallet.prototype.reject = function(ntxid) {

View File

@ -189,7 +189,7 @@ describe('TxProposal', function() {
var txp = dummyProposal; var txp = dummyProposal;
var tx = dummyProposal.builder.build(); var tx = dummyProposal.builder.build();
var ret = TxProposal._verifySignatures(pubkeys, validScriptSig, tx.hashForSignature()); var ret = TxProposal._verifySignatures(pubkeys, validScriptSig, tx.hashForSignature());
ret.should.deep.equal([0, 3]); ret.should.deep.equal(['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']);
}); });
it('#_infoFromRedeemScript', function() { it('#_infoFromRedeemScript', function() {
var info = TxProposal._infoFromRedeemScript(validScriptSig); var info = TxProposal._infoFromRedeemScript(validScriptSig);
@ -202,7 +202,7 @@ describe('TxProposal', function() {
}); });
it('#_updateSignedBy', function() { it('#_updateSignedBy', function() {
var txp = dummyProposal; var txp = dummyProposal;
txp._inputSignatures.should.deep.equal([ txp._inputSigners.should.deep.equal([
['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3'] ['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']
]); ]);
}); });
@ -302,7 +302,7 @@ describe('TxProposal', function() {
txp.signedBy = { txp.signedBy = {
'hugo': 1 'hugo': 1
}; };
txp._inputSignatures = [ txp._inputSigners = [
['pkX'] ['pkX']
]; ];
(function() { (function() {
@ -319,7 +319,7 @@ describe('TxProposal', function() {
txp.signedBy = { txp.signedBy = {
creator: 1 creator: 1
}; };
txp._inputSignatures = [ txp._inputSigners = [
['pk0', 'pkX'] ['pk0', 'pkX']
]; ];
(function() { (function() {
@ -333,7 +333,7 @@ describe('TxProposal', function() {
it.skip("should be signed by sender", function() { it.skip("should be signed by sender", function() {
var txp = dummyProposal; var txp = dummyProposal;
var ts = Date.now(); var ts = Date.now();
txp._inputSignatures = [ txp._inputSigners = [
['pk1', 'pk0'] ['pk1', 'pk0']
]; ];
txp.signedBy = { txp.signedBy = {
@ -352,7 +352,7 @@ describe('TxProposal', function() {
it("should set signedBy (trivial case)", function() { it("should set signedBy (trivial case)", function() {
var txp = dummyProposal; var txp = dummyProposal;
var ts = Date.now(); var ts = Date.now();
txp._inputSignatures = [ txp._inputSigners = [
['pk1', 'pk0'] ['pk1', 'pk0']
]; ];
txp.signedBy = { txp.signedBy = {
@ -370,7 +370,7 @@ describe('TxProposal', function() {
it("should assign creator", function() { it("should assign creator", function() {
var txp = dummyProposal; var txp = dummyProposal;
var ts = Date.now(); var ts = Date.now();
txp._inputSignatures = [ txp._inputSigners = [
['pk0'] ['pk0']
]; ];
txp.signedBy = {}; txp.signedBy = {};
@ -392,7 +392,7 @@ describe('TxProposal', function() {
txp.signedBy = {}; txp.signedBy = {};
delete txp['creator']; delete txp['creator'];
delete txp['creatorTs']; delete txp['creatorTs'];
txp._inputSignatures = [ txp._inputSigners = [
['pk0', 'pk1'] ['pk0', 'pk1']
]; ];
(function() { (function() {
@ -411,7 +411,7 @@ describe('TxProposal', function() {
it("if signed, should not change ts", function() { it("if signed, should not change ts", function() {
var txp = dummyProposal; var txp = dummyProposal;
var ts = Date.now(); var ts = Date.now();
txp._inputSignatures = [ txp._inputSigners = [
['pk0', 'pk1'] ['pk0', 'pk1']
]; ];
txp.creator = 'creator'; txp.creator = 'creator';

View File

@ -30,10 +30,10 @@ var config = {
var getNewEpk = function() { var getNewEpk = function() {
return new PrivateKey({ return new PrivateKey({
networkName: config.networkName, networkName: config.networkName,
}) })
.deriveBIP45Branch() .deriveBIP45Branch()
.extendedPublicKeyString(); .extendedPublicKeyString();
} }
var addCopayers = function(w) { var addCopayers = function(w) {
@ -48,7 +48,7 @@ describe('Wallet model', function() {
(function() { (function() {
new Wallet(config) new Wallet(config)
}).should. }).should.
throw(); throw();
}); });
it('should getNetworkName', function() { it('should getNetworkName', function() {
var w = cachedCreateW(); var w = cachedCreateW();
@ -219,7 +219,7 @@ describe('Wallet model', function() {
var t = w.txProposals; var t = w.txProposals;
var txp = t.txps[ntxid]; var txp = t.txps[ntxid];
Object.keys(txp._inputSignatures).length.should.equal(1); Object.keys(txp._inputSigners).length.should.equal(1);
var tx = txp.builder.build(); var tx = txp.builder.build();
should.exist(tx); should.exist(tx);
chai.expect(txp.comment).to.be.null; chai.expect(txp.comment).to.be.null;
@ -273,7 +273,7 @@ describe('Wallet model', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
var l = w.getAddressesStr(); var l = w.getAddressesStr();
for (var i = 0; i < l.length; i++) for (var i = 0; i < l.length; i++)
w.addressIsOwn(l[i]).should.equal(true); w.addressIsOwn(l[i]).should.equal(true);
w.addressIsOwn(l[0], { w.addressIsOwn(l[0], {
excludeMain: true excludeMain: true
@ -322,14 +322,14 @@ describe('Wallet model', function() {
o.opts.reconnectDelay = 100; o.opts.reconnectDelay = 100;
var w2 = Wallet.fromObj(o, var w2 = Wallet.fromObj(o,
new Storage(config.storage), new Storage(config.storage),
new Network(config.network), new Network(config.network),
new Blockchain(config.blockchain)); new Blockchain(config.blockchain));
should.exist(w2); should.exist(w2);
w2.publicKeyRing.requiredCopayers.should.equal(w.publicKeyRing.requiredCopayers); w2.publicKeyRing.requiredCopayers.should.equal(w.publicKeyRing.requiredCopayers);
should.exist(w2.publicKeyRing.getCopayerId); should.exist(w2.publicKeyRing.getCopayerId);
should.exist(w2.txProposals.toObj); should.exist(w2.txProposals.toObj);
should.exist(w2.privateKey.toObj); should.exist(w2.privateKey.toObj);
}); });
it('#getSecret decodeSecret', function() { it('#getSecret decodeSecret', function() {
@ -346,15 +346,15 @@ describe('Wallet model', function() {
(function() { (function() {
Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM'); Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoKM');
}).should.not. }).should.not.
throw(); throw();
(function() { (function() {
Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK'); Wallet.decodeSecret('4fp61K187CsYmjoRQC5iAdC5eGmbCRsAAXfwEwetSQgHvZs27eWKaLaNHRoK');
}).should. }).should.
throw(); throw();
(function() { (function() {
Wallet.decodeSecret('12345'); Wallet.decodeSecret('12345');
}).should. }).should.
throw(); throw();
}); });
@ -509,9 +509,9 @@ describe('Wallet model', function() {
} }
}; };
var stub = sinon.stub(w.publicKeyRing,'copayersForPubkeys').returns( var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys').returns({
{'027445ab3a935dce7aee1dadb0d103ed6147a0f83deb80474a04538b2c5bc4d509':'pepe'} '027445ab3a935dce7aee1dadb0d103ed6147a0f83deb80474a04538b2c5bc4d509': 'pepe'
); });
w._handleTxProposal('senderID', txp, true); w._handleTxProposal('senderID', txp, true);
Object.keys(w.txProposals.txps).length.should.equal(1); Object.keys(w.txProposals.txps).length.should.equal(1);
w.getTxProposals().length.should.equal(1); w.getTxProposals().length.should.equal(1);
@ -605,21 +605,21 @@ describe('Wallet model', function() {
}); });
var roundErrorChecks = [{ var roundErrorChecks = [{
unspent: [1.0001], unspent: [1.0001],
balance: 100010000 balance: 100010000
}, { }, {
unspent: [1.0002, 1.0003, 1.0004], unspent: [1.0002, 1.0003, 1.0004],
balance: 300090000 balance: 300090000
}, { }, {
unspent: [0.000002, 1.000003, 2.000004], unspent: [0.000002, 1.000003, 2.000004],
balance: 300000900 balance: 300000900
}, { }, {
unspent: [0.0001, 0.0003], unspent: [0.0001, 0.0003],
balance: 40000 balance: 40000
}, { }, {
unspent: [0.0001, 0.0003, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002], unspent: [0.0001, 0.0003, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0002],
balance: 110000 balance: 110000
}, },
]; ];
var roundWallet = cachedCreateW2(); var roundWallet = cachedCreateW2();
@ -765,7 +765,7 @@ describe('Wallet model', function() {
}); });
}); });
describe('#createTxSync', function () { describe('#createTxSync', function() {
it('should fail if amount below min value', function() { it('should fail if amount below min value', function() {
var w = cachedCreateW2(); var w = cachedCreateW2();
var utxo = createUTXO(w); var utxo = createUTXO(w);
@ -872,9 +872,9 @@ describe('Wallet model', function() {
}); });
sinon.assert.callCount(updateIndex, 4); sinon.assert.callCount(updateIndex, 4);
sinon.assert.calledWith(updateIndex, w.publicKeyRing.indexes[0] ); sinon.assert.calledWith(updateIndex, w.publicKeyRing.indexes[0]);
sinon.assert.calledWith(updateIndex, w.publicKeyRing.indexes[1] ); sinon.assert.calledWith(updateIndex, w.publicKeyRing.indexes[1]);
sinon.assert.calledWith(updateIndex, w.publicKeyRing.indexes[2] ); sinon.assert.calledWith(updateIndex, w.publicKeyRing.indexes[2]);
w.updateIndex.restore(); w.updateIndex.restore();
done(); done();
}); });
@ -898,8 +898,8 @@ describe('Wallet model', function() {
index.receiveIndex.should.equal(9); index.receiveIndex.should.equal(9);
index.changeIndex.should.equal(9); index.changeIndex.should.equal(9);
indexDiscovery.callCount.should.equal(2); indexDiscovery.callCount.should.equal(2);
sinon.assert.calledWith(indexDiscovery, 1, true, 2, 20 ); sinon.assert.calledWith(indexDiscovery, 1, true, 2, 20);
sinon.assert.calledWith(indexDiscovery, 2, false, 2, 20 ); sinon.assert.calledWith(indexDiscovery, 2, false, 2, 20);
w.indexDiscovery.restore(); w.indexDiscovery.restore();
done(); done();
}); });
@ -957,7 +957,7 @@ describe('Wallet model', function() {
(function() { (function() {
w.setAddressBook(contacts[0].address, contacts[0].label); w.setAddressBook(contacts[0].address, contacts[0].label);
}).should. }).should.
throw(); throw();
}); });
it('should show/hide everywhere', function() { it('should show/hide everywhere', function() {
@ -1108,7 +1108,7 @@ describe('Wallet model', function() {
}; };
}); });
var txp = { var txp = {
_inputSignatures: [ _inputSigners: [
['123'] ['123']
], ],
inputChainPaths: ['/m/1'], inputChainPaths: ['/m/1'],
@ -1121,19 +1121,34 @@ describe('Wallet model', function() {
it('should throw if unmatched sigs', function() { it('should throw if unmatched sigs', function() {
var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() { var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() {
return { return {};
'123': 'juan'
};
}); });
var txp = { var txp = {
_inputSignatures: [ _inputSigners: [
['234'] ['234']
], ],
inputChainPaths: ['/m/1'], inputChainPaths: ['/m/1'],
}; };
(function() { (function() {
w._getKeyMap(txp); w._getKeyMap(txp);
}).should.throw('dont match know copayers'); }).should.throw('does not match known copayers');
stub.restore();
});
it('should throw if unmatched sigs (case 2)', function() {
var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() {
return {};
});
var txp = {
_inputSigners: [
['234', '321'],
['234', '322']
],
inputChainPaths: ['/m/1'],
};
(function() {
w._getKeyMap(txp);
}).should.throw('does not match known copayers');
stub.restore(); stub.restore();
}); });
@ -1145,7 +1160,7 @@ describe('Wallet model', function() {
}; };
}); });
var txp = { var txp = {
_inputSignatures: [ _inputSigners: [
['234', '123'] ['234', '123']
], ],
inputChainPaths: ['/m/1'], inputChainPaths: ['/m/1'],
@ -1157,17 +1172,20 @@ describe('Wallet model', function() {
stub.restore(); stub.restore();
}); });
it('should throw is one inputs has missing sigs', function() { it('should throw if one inputs has missing sigs', function() {
var call = 0;
var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() { var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() {
return { return call++ ? {
'555': 'pepe',
} : {
'123': 'juan', '123': 'juan',
'234': 'pepe', '234': 'pepe',
}; };
}); });
var txp = { var txp = {
_inputSignatures: [ _inputSigners: [
['234', '123'], ['234', '123'],
['234'] ['555']
], ],
inputChainPaths: ['/m/1'], inputChainPaths: ['/m/1'],
}; };
@ -1176,6 +1194,60 @@ describe('Wallet model', function() {
}).should.throw('different sig'); }).should.throw('different sig');
stub.restore(); stub.restore();
}); });
it('should throw if one inputs has different sigs', function() {
var call = 0;
var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() {
return call++ ? {
'555': 'pepe',
'666': 'pedro',
} : {
'123': 'juan',
'234': 'pepe',
};
});
var txp = {
_inputSigners: [
['234', '123'],
['555', '666']
],
inputChainPaths: ['/m/1'],
};
(function() {
w._getKeyMap(txp);
}).should.throw('different sig');
stub.restore();
});
it('should not throw if 2 inputs has different pubs, same copayers', function() {
var call = 0;
var stub = sinon.stub(w.publicKeyRing, 'copayersForPubkeys', function() {
return call++ ? {
'555': 'pepe',
'666': 'pedro',
} : {
'123': 'pedro',
'234': 'pepe',
};
});
var txp = {
_inputSigners: [
['234', '123'],
['555', '666']
],
inputChainPaths: ['/m/1'],
};
var gk = w._getKeyMap(txp);
gk.should.deep.equal({
'123': 'pedro',
'234': 'pepe',
'555': 'pepe',
'666': 'pedro'
});
stub.restore();
});
}); });