mirror of https://github.com/BTCPrivate/copay.git
Merge pull request #20 from matiu/feature/PublicKeyRing-fix
Feature/public key ring fix
This commit is contained in:
commit
493150e802
|
@ -17,10 +17,10 @@ var log = imports.log || console.log;
|
||||||
var storage = Storage.default();
|
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
|
* 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/
|
* http://sourceforge.net/p/bitcoin/mailman/message/32148600/
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -76,14 +76,16 @@ PublicKeyRing.read = function (id, passphrase) {
|
||||||
if (data.id !== id)
|
if (data.id !== id)
|
||||||
throw new Error('Wrong id in data');
|
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
|
bitcore.networks.livenet : bitcore.networks.testnet
|
||||||
};
|
};
|
||||||
|
|
||||||
var w = new PublicKeyRing(config);
|
var w = new PublicKeyRing(config);
|
||||||
|
|
||||||
w.requiredCopayers = data.neededCopayers;
|
w.requiredCopayers = data.requiredCopayers;
|
||||||
w.totalCopayers = data.totalCopayers;
|
w.totalCopayers = data.totalCopayers;
|
||||||
|
w.addressIndex = data.addressIndex;
|
||||||
|
w.changeAddressIndex = data.changeAddressIndex;
|
||||||
|
|
||||||
// this.bip32 = ;
|
// this.bip32 = ;
|
||||||
w.copayersBIP32 = data.copayersExtPubKeys.map( function (pk) {
|
w.copayersBIP32 = data.copayersExtPubKeys.map( function (pk) {
|
||||||
|
@ -95,16 +97,24 @@ PublicKeyRing.read = function (id, passphrase) {
|
||||||
return w;
|
return w;
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.serialize = function () {
|
PublicKeyRing.prototype.toObj = function() {
|
||||||
return JSON.stringify({
|
return {
|
||||||
id: this.id,
|
id: this.id,
|
||||||
network: this.network.name,
|
networkName: this.network.name,
|
||||||
requiredCopayers: this.neededCopayers,
|
requiredCopayers: this.requiredCopayers,
|
||||||
totalCopayers: this.totalCopayers,
|
totalCopayers: this.totalCopayers,
|
||||||
|
|
||||||
|
changeAddressIndex: this.changeAddressIndex,
|
||||||
|
addressIndex: this.addressIndex,
|
||||||
copayersExtPubKeys: this.copayersBIP32.map( function (b) {
|
copayersExtPubKeys: this.copayersBIP32.map( function (b) {
|
||||||
return b.extendedPublicKeyString();
|
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) {
|
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||||
this._checkIndexRange(index, isChange);
|
this._checkIndexRange(index, isChange);
|
||||||
|
|
||||||
var pubKeys = this.getCopayersPubKeys();
|
var pubKeys = this.getCopayersPubKeys(index, isChange);
|
||||||
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
||||||
return script;
|
return script;
|
||||||
};
|
};
|
||||||
|
@ -207,9 +217,9 @@ PublicKeyRing.prototype.generateAddress = function(isChange) {
|
||||||
var ret =
|
var ret =
|
||||||
this.getAddress(isChange ? this.changeAddressIndex : this.addressIndex, isChange);
|
this.getAddress(isChange ? this.changeAddressIndex : this.addressIndex, isChange);
|
||||||
if (isChange)
|
if (isChange)
|
||||||
this.addressIndex++;
|
|
||||||
else
|
|
||||||
this.changeAddressIndex++;
|
this.changeAddressIndex++;
|
||||||
|
else
|
||||||
|
this.addressIndex++;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -228,4 +238,78 @@ PublicKeyRing.prototype.getAddresses = function() {
|
||||||
return ret;
|
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; j<l; j++) {
|
||||||
|
if (self.copayersBIP32[j].extendedPublicKeyString() === epk) {
|
||||||
|
haveIt=true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!haveIt) {
|
||||||
|
self.copayersBIP32.push(new BIP32(epk));
|
||||||
|
hasChanged=true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return hasChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
PublicKeyRing.prototype.merge = function(inPKR) {
|
||||||
|
var hasChanged = false;
|
||||||
|
|
||||||
|
this._checkInPRK(inPKR);
|
||||||
|
|
||||||
|
if (this._mergeIndexes(inPKR))
|
||||||
|
hasChanged = true;
|
||||||
|
|
||||||
|
if (this._mergePubkeys(inPKR))
|
||||||
|
hasChanged = true;
|
||||||
|
|
||||||
|
return hasChanged;
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = require('soop')(PublicKeyRing);
|
module.exports = require('soop')(PublicKeyRing);
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var should = chai.should();
|
|
||||||
describe('Network service', function() {
|
|
||||||
|
|
||||||
var network;
|
|
||||||
beforeEach(angular.mock.module('cosign'));
|
|
||||||
|
|
||||||
it('should exist', function() {
|
|
||||||
inject(function($injector) {
|
|
||||||
network = $injector.get('network');
|
|
||||||
should.exist(network);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('should return 2', function() {
|
|
||||||
network.f().should.equal(2);
|
|
||||||
});
|
|
||||||
it('can start a tx signing request', function() {});
|
|
||||||
it('should', function() {});
|
|
||||||
|
|
||||||
});
|
|
|
@ -75,6 +75,10 @@ describe('PublicKeyRing model', function() {
|
||||||
var k = createW();
|
var k = createW();
|
||||||
var w = k.w;
|
var w = k.w;
|
||||||
var copayers = k.copayers;
|
var copayers = k.copayers;
|
||||||
|
for(var i=0; i<3; i++)
|
||||||
|
w.generateAddress(true);
|
||||||
|
for(var i=0; i<5; i++)
|
||||||
|
w.generateAddress(false);
|
||||||
|
|
||||||
w.store().should.equal(true);
|
w.store().should.equal(true);
|
||||||
var ID = w.id;
|
var ID = w.id;
|
||||||
|
@ -87,6 +91,8 @@ describe('PublicKeyRing model', function() {
|
||||||
for(var i =0; i<5; i++)
|
for(var i =0; i<5; i++)
|
||||||
w2.addCopayer.bind(copayers[i]).should.throw();
|
w2.addCopayer.bind(copayers[i]).should.throw();
|
||||||
|
|
||||||
|
w2.changeAddressIndex.should.equal(3);
|
||||||
|
w2.addressIndex.should.equal(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,6 +107,10 @@ describe('PublicKeyRing model', function() {
|
||||||
a.isValid().should.equal(true);
|
a.isValid().should.equal(true);
|
||||||
a.isScript().should.equal(true);
|
a.isScript().should.equal(true);
|
||||||
a.network().name.should.equal('livenet');
|
a.network().name.should.equal('livenet');
|
||||||
|
if (i>1) {
|
||||||
|
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();
|
var as = w.getAddresses();
|
||||||
as.length.should.equal(12);
|
as.length.should.equal(12);
|
||||||
for(var i in as) {
|
for(var j in as) {
|
||||||
var a = new Address(as[i]);
|
var a = new Address(as[j]);
|
||||||
a.isValid().should.equal(true);
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue