diff --git a/js/models/PublicKeyRing.js b/js/models/PublicKeyRing.js index a078c51f3..e047ecb9e 100644 --- a/js/models/PublicKeyRing.js +++ b/js/models/PublicKeyRing.js @@ -17,10 +17,10 @@ var log = imports.log || console.log; var storage = Storage.default(); /* - * This follow Electrum convetion, as described on + * This follow Electrum convetion, as described in * https://bitcointalk.org/index.php?topic=274182.0 * - * We should probably adapt the next standard once its ready as discussed at: + * We should probably adopt the next standard once it's ready, as discussed in: * http://sourceforge.net/p/bitcoin/mailman/message/32148600/ * */ @@ -76,14 +76,16 @@ PublicKeyRing.read = function (id, passphrase) { if (data.id !== id) throw new Error('Wrong id in data'); - var config = { network: data.network === 'livenet' ? + var config = { network: data.networkName === 'livenet' ? bitcore.networks.livenet : bitcore.networks.testnet }; var w = new PublicKeyRing(config); - w.requiredCopayers = data.neededCopayers; + w.requiredCopayers = data.requiredCopayers; w.totalCopayers = data.totalCopayers; + w.addressIndex = data.addressIndex; + w.changeAddressIndex = data.changeAddressIndex; // this.bip32 = ; w.copayersBIP32 = data.copayersExtPubKeys.map( function (pk) { @@ -95,16 +97,24 @@ PublicKeyRing.read = function (id, passphrase) { return w; }; -PublicKeyRing.prototype.serialize = function () { - return JSON.stringify({ +PublicKeyRing.prototype.toObj = function() { + return { id: this.id, - network: this.network.name, - requiredCopayers: this.neededCopayers, + networkName: this.network.name, + requiredCopayers: this.requiredCopayers, totalCopayers: this.totalCopayers, + + changeAddressIndex: this.changeAddressIndex, + addressIndex: this.addressIndex, copayersExtPubKeys: this.copayersBIP32.map( function (b) { return b.extendedPublicKeyString(); }), - }); + ts: parseInt(Date.now() / 1000), + }; +}; + +PublicKeyRing.prototype.serialize = function () { + return JSON.stringify(this.toObj()); }; @@ -186,7 +196,7 @@ PublicKeyRing.prototype._checkIndexRange = function (index, isChange) { PublicKeyRing.prototype.getRedeemScript = function (index, isChange) { this._checkIndexRange(index, isChange); - var pubKeys = this.getCopayersPubKeys(); + var pubKeys = this.getCopayersPubKeys(index, isChange); var script = Script.createMultisig(this.requiredCopayers, pubKeys); return script; }; @@ -207,9 +217,9 @@ PublicKeyRing.prototype.generateAddress = function(isChange) { var ret = this.getAddress(isChange ? this.changeAddressIndex : this.addressIndex, isChange); if (isChange) - this.addressIndex++; - else this.changeAddressIndex++; + else + this.addressIndex++; return ret; @@ -228,4 +238,78 @@ PublicKeyRing.prototype.getAddresses = function() { return ret; }; +PublicKeyRing.prototype._checkInPRK = function(inPKR) { + if (this.id !== inPKR.id) + throw new Error('inPRK id mismatch'); + + if (this.network.name !== inPKR.networkName) + throw new Error('inPRK network mismatch'); + + if ( + this.requiredCopayers && inPKR.requiredCopayers && + (this.requiredCopayers !== inPKR.requiredCopayers)) + throw new Error('inPRK requiredCopayers mismatch'); + + if ( + this.totalCopayers && inPKR.totalCopayers && + (this.totalCopayers !== inPKR.totalCopayers)) + throw new Error('inPRK requiredCopayers mismatch'); + + if (! inPKR.ts) + throw new Error('no ts at inPRK'); +}; + + +PublicKeyRing.prototype._mergeIndexes = function(inPKR) { + var hasChanged = false; + + // Indexes + if (inPKR.changeAddressIndex > this.changeAddressIndex) { + this.changeAddressIndex = inPKR.changeAddressIndex; + hasChanged = true; + } + + if (inPKR.addressIndex > this.addressIndex) { + this.addressIndex = inPKR.addressIndex; + hasChanged = true; + } + return hasChanged; +}; + +PublicKeyRing.prototype._mergePubkeys = function(inPKR) { + var hasChanged = false; + var l= this.copayersBIP32.length; + + var self = this; + + inPKR.copayersExtPubKeys.forEach( function(epk) { + var haveIt = false; + for(var j=0; j1) { + w.getAddress(i-1,isChange).should + .not.equal(w.getAddress(i-2,isChange)); + } } } }); @@ -119,12 +129,136 @@ describe('PublicKeyRing model', function() { var as = w.getAddresses(); as.length.should.equal(12); - for(var i in as) { - var a = new Address(as[i]); + for(var j in as) { + var a = new Address(as[j]); a.isValid().should.equal(true); } }); + it('should count generation indexes', function () { + var k = createW(); + var w = k.w; + + for(var i=0; i<3; i++) + w.generateAddress(true); + for(var i=0; i<5; i++) + w.generateAddress(false); + + w.changeAddressIndex.should.equal(3); + w.addressIndex.should.equal(5); + }); + + it('#merge index tests', function () { + var k = createW(); + var w = k.w; + + for(var i=0; i<2; i++) + w.generateAddress(true); + for(var i=0; i<3; i++) + w.generateAddress(false); + + var w2 = new PublicKeyRing({ + network: 'livenet', + id: w.id, + }); + w2.merge(w.toObj()).should.equal(true); + w2.requiredCopayers.should.equal(3); + w2.totalCopayers.should.equal(5); + w2.changeAddressIndex.should.equal(2); + w2.addressIndex.should.equal(3); + + // + w2.merge(w.toObj()).should.equal(false); + }); + + + it('#merge check tests', function () { + var k = createW(); + var w = k.w; + + for(var i=0; i<2; i++) + w.generateAddress(true); + for(var i=0; i<3; i++) + w.generateAddress(false); + + + var w3 = new PublicKeyRing({ + network: 'livenet', + id: w.id, + requiredCopayers: 2, + }); + (function() { w3.merge(w.toObj());}).should.throw(); + + var w4 = new PublicKeyRing({ + network: 'testnet', + id: w.id, + }); + (function() { w4.merge(w.toObj());}).should.throw(); + + var w5 = new PublicKeyRing({ + network: 'livenet', + id: w.id, + totalCopayers: 4, + }); + (function() { w5.merge(w.toObj());}).should.throw(); + + var w6 = new PublicKeyRing({ + network: 'livenet', + id: w.id, + }); + (function() { w6.merge(w);}).should.throw(); + w.networkName= 'livenet'; + (function() { w6.merge(w);}).should.throw(); + }); + + + it('#merge pubkey tests', function () { + var w = new PublicKeyRing(config); + should.exist(w); + var copayers = []; + for(var i=0; i<2; i++) { + w.haveAllRequiredPubKeys().should.equal(false); + w.addCopayer(); + } + + var w2 = new PublicKeyRing({ + network: 'livenet', + id: w.id, + }); + should.exist(w); + var copayers = []; + for(var i=0; i<3; i++) { + w2.haveAllRequiredPubKeys().should.equal(false); + w2.addCopayer(); + } + w2.merge(w.toObj()).should.equal(true); + w2.haveAllRequiredPubKeys().should.equal(true); + w2.merge(w.toObj()).should.equal(false); + + w.haveAllRequiredPubKeys().should.equal(false); + w.merge(w2.toObj()).should.equal(true); + w.haveAllRequiredPubKeys().should.equal(true); + w.merge(w2.toObj()).should.equal(false); + }); + + it('#merge pubkey tests (case 2)', function () { + var w = new PublicKeyRing(config); + should.exist(w); + + for(var i=0; i<5; i++) { + w.haveAllRequiredPubKeys().should.equal(false); + var w2 = new PublicKeyRing({ + network: 'livenet', + id: w.id, + }); + w2.addCopayer(); + w.merge(w2.toObj()); + } + w.haveAllRequiredPubKeys().should.equal(true); + }); + + + });