This commit is contained in:
Matias Alejo Garcia 2015-03-06 12:00:10 -03:00
commit 320de62f13
348 changed files with 7745 additions and 30874 deletions

View file

@ -1,58 +0,0 @@
var compat = require('../js/models/Compatibility');
describe('Compatibility', function() {
describe('#import', function() {
it('should not be able to decrypt with wrong password', function() {
var wo = compat.importLegacy(encryptedLegacy1, 'badpassword');
should.not.exist(wo);
});
it('should generate passphrases acording to old algorithm', function() {
var passphrase = compat.kdf(legacyPassword1);
passphrase.should.equal(legacyPassphrase1);
});
it('should be able to decrypt an old backup', function() {
var str = compat.importLegacy(encryptedLegacy1, legacyPassword1);
should.exist(str);
var wo = JSON.parse(str);
wo.opts.id.should.equal('48ba2f1ffdfe9708');
wo.opts.spendUnconfirmed.should.equal(true);
wo.opts.requiredCopayers.should.equal(1);
wo.opts.totalCopayers.should.equal(1);
wo.opts.name.should.equal('pepe wallet');
wo.opts.version.should.equal('0.4.7');
wo.publicKeyRing.walletId.should.equal('48ba2f1ffdfe9708');
wo.publicKeyRing.networkName.should.equal('testnet');
wo.publicKeyRing.requiredCopayers.should.equal(1);
wo.publicKeyRing.totalCopayers.should.equal(1);
wo.publicKeyRing.indexes.length.should.equal(2);
JSON.stringify(wo.publicKeyRing.indexes[0]).should.equal('{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1}');
JSON.stringify(wo.publicKeyRing.indexes[1]).should.equal('{"copayerIndex":0,"changeIndex":0,"receiveIndex":1}');
wo.publicKeyRing.copayersBackup.length.should.equal(1);
wo.publicKeyRing.copayersBackup[0].should.equal('0298f65b2694c55f9048bc05f10368242727c7f9d2065cbd788c3ecde1ec57f33f');
wo.publicKeyRing.copayersExtPubKeys.length.should.equal(1);
wo.publicKeyRing.copayersExtPubKeys[0].should.equal('tpubD9SGoP7CXsqSKTiQxCZSCpicDcophqnE4yuqjfw5M9tAR3fSjT9GDGwPEUFCN7SSmRKGDLZgKQePYFaLWyK32akeSan45TNTd8sgef9Ymh6');
wo.privateKey.extendedPrivateKeyString.should.equal('tprv8ZgxMBicQKsPfQCscb7CtJKzixxcVSyrCVcfr3WCFbtT8kYTzNubhjQ5R7AuYJgPCcSH4R8T34YVxeohKGhAB9wbB4eFBbQFjUpjGCqptHm');
wo.privateKey.networkName.should.equal('testnet');
});
it('should be able import an old backup', function(done) {
var iden = sinon.stub();
iden.importWalletFromObj = sinon.stub().yields(null);
compat.importEncryptedWallet(iden, encryptedLegacy1, legacyPassword1, {}, function(err) {
var s = iden.importWalletFromObj;
s.getCall(0).args[0].opts.id.should.equal('48ba2f1ffdfe9708');
done();
});
})
});
});
var legacyPassword1 = '1';
var legacyPassphrase1 = '1DUpLRbuVpgLkcEY8gY8iod/SmA7+OheGZJ9PtvmTlvNE0FkEWpCKW9STdzXYJqbn0wiAapE4ojHNYj2hjYYAQ==';
var encryptedLegacy1 = 'U2FsdGVkX19yGM1uBAIzQa8Po/dvUicmxt1YyRk/S97PcZ6I6rHMp9dMagIrehg4Qd6JHn/ustmFHS7vmBYj0EBpf6rdXiQezaWnVAJS9/xYjAO36EFUbl+NmUanuwujAxgYdSP/sNssRLeInvExmZYW993EEclxkwL6YUyX66kKsxGQo2oWng0NreBJNhFmrbOEWeFje2PiWP57oUjKsurFzwpluAAarUTYSLud+nXeabC7opzOP5yqniWBMJz0Ou8gpNCWCMhG/P9F9ccVPY7juyd0Hf41FVse8nd2++axKB57+paozLdO+HRfV6zkMqC3h8gWY7LkS75j3bvqcTw9LhXmzE0Sz21n9yDnRpA4chiAvtwQvvBGgj1pFMKhNQU6Obac9ZwKYzUTgdDn3Uzg1UlDzgyOh9S89rbRTV84WB+hXwhuVluWzbNNYV3vXe5PFrocVktIrtS3xQh+k/7my4A6/gRRrzNYpKrUASJqDS/9u9WBkG35xD63J/qXjtG2M0YPwbI57BK1IK4K510b8V72lz5U2XQrIC4ldBwni1rpSavwCJV9xF6hUdOmNV8fZsVHP0NeN1PYlLkSb2QgfuoWnkcsJerwuFR7GZC/i6efrswtpO0wMEQr/J0CLbeXlHAru6xxjCBhWoJvZpMGw72zgnDLoyMNsEVglNhx/VlV9ZMYkkdaEYAxPOEIyZdQ5MS+2jEAlXf818n/xzJSVrniCn9be8EPePvkw35pivprvy09vbW4cKsWBKvgIyoT6A3OhUOCCS8E9cg0WAjjav2EymrbKmGWRHaiD+EoJqaDg6s20zhHn1YEa/YwvGGSB5+Hg8baLHD8ZASvxz4cFFAAVZrBUedRFgHzqwaMUlFXLgueivWUj7RXlIw6GuNhLoo1QkhZMacf23hrFxxQYvGBRw1hekBuDmcsGWljA28udBxBd5f9i+3gErttMLJ6IPaud590uvrxRIclu0Sz9R2EQX64YJxqDtLpMY0PjddSMu8vaDRpK9/ZSrnz/xrXsyabaafz4rE/ItFXjwFUFkvtmuauHTz6nmuKjVfxvNLNAiKb/gI7vQyUhnTbKIApe7XyJsjedNDtZqsPoJRIzdDmrZYxGStbAZ7HThqFJlSJ9NPNhH+E2jm3TwL5mwt0fFZ5h+p497lHMtIcKffESo7KNa2juSVNMDREk0NcyxGXGiVB2FWl4sLdvyhcsVq0I7tmW6OGZKRf8W49GCJXq6Ie69DJ9LB1DO67NV1jsYbsLx9uhE2yEmpWZ3jkoCV/Eas4grxt0CGN6EavzQ==';

View file

@ -1,143 +0,0 @@
'use strict';
var Address = bitcore.Address;
var PublicKeyRing = copay.PublicKeyRing;
var HDParams = copay.HDParams;
var HDPath = copay.HDPath;
describe('HDParams model', function() {
it('should create an instance (livenet)', function() {
var i = new HDParams();
should.exist(i);
});
it('should init indexes', function() {
var is = HDParams.init(2);
should.exist(is);
is.length.should.equal(3);
var cosigners = is.map(function(i) {
return i.copayerIndex;
});
cosigners.indexOf(HDPath.SHARED_INDEX).should.not.equal(-1);
cosigners.indexOf(0).should.not.equal(-1);
cosigners.indexOf(1).should.not.equal(-1);
cosigners.indexOf(2).should.equal(-1);
});
it('should serialize to object list and back', function() {
var is = HDParams.init(3);
should.exist(is);
is.length.should.equal(4);
var list = HDParams.serialize(is);
list.length.should.equal(4);
var is2 = HDParams.fromList(list);
is2.length.should.equal(4);
});
it('should be able to store and read', function() {
var i = new HDParams();
i.copayerIndex = 1;
var changeN = 2;
var addressN = 2;
for (var j = 0; j < changeN; j++) {
i.increment(true);
}
for (var j = 0; j < addressN; j++) {
i.increment(false);
}
var data = i.toObj();
should.exist(data);
var i2 = HDParams.fromObj(data);
i2.copayerIndex.should.equal(i.copayerIndex);
i2.getChangeIndex().should.equal(changeN);
i2.getReceiveIndex().should.equal(addressN);
});
it('should throw an error', function() {
var data = new HDParams();
(function() {
HDParams.fromObj(data);
}).should.throw('BADDATA');
});
it('should count generation indexes', function() {
var j = new HDParams();
j.copayerIndex = 1;
for (var i = 0; i < 3; i++)
j.increment(true);
for (var i = 0; i < 2; i++)
j.increment(false);
j.changeIndex.should.equal(3);
j.receiveIndex.should.equal(2);
});
it('#merge tests', function() {
var j = new HDParams();
j.copayerIndex = 1;
for (var i = 0; i < 15; i++)
j.increment(true);
for (var i = 0; i < 7; i++)
j.increment(false);
var j2 = new HDParams({
copayerIndex: j.copayerIndex,
});
j2.merge(j).should.equal(true);
j2.changeIndex.should.equal(15);
j2.receiveIndex.should.equal(7);
j2.merge(j).should.equal(false);
});
it('#merge should fail with different copayerIndex index', function() {
var j1 = new HDParams({
walletId: '1234',
copayerIndex: 2
});
var j2 = new HDParams({
walletId: '1234',
copayerIndex: 3
});
var merge = function() {
j2.merge(j1);
};
merge.should.throw(Error);
})
});
describe('#checkRange', function() {
it('should throw an error', function() {
var hd = new HDParams();
(function() {
hd.checkRange(60, true);
}).should.throw('Out of bounds');
});
it('should throw an error', function() {
var hd = new HDParams();
(function() {
hd.checkRange(60, false);
}).should.throw('Out of bounds');
});
it('should not throw an error', function() {
var hd = new HDParams();
hd.checkRange(0, false);
hd.checkRange(0, true);
});
});

View file

@ -1,71 +0,0 @@
'use strict';
var HDPath = copay.HDPath;
describe('HDPath model', function() {
it('should have the correct constants', function() {
HDPath.MAX_NON_HARDENED.should.equal(Math.pow(2, 31) - 1);
HDPath.SHARED_INDEX.should.equal(HDPath.MAX_NON_HARDENED);
HDPath.ID_INDEX.should.equal(HDPath.SHARED_INDEX - 1);
HDPath.IdFullBranch.should.equal('m/45\'/2147483646/0/0');
});
it('should get the correct branches', function() {
// shared branch (no cosigner index specified)
HDPath.FullBranch(0, false).should.equal('m/45\'/2147483647/0/0');
// copayer 0, address 0, external address (receiving)
HDPath.FullBranch(0, false, 0).should.equal('m/45\'/0/0/0');
// copayer 0, address 10, external address (receiving)
HDPath.FullBranch(0, false, 10).should.equal('m/45\'/10/0/0');
// copayer 0, address 0, internal address (change)
HDPath.FullBranch(0, true, 0).should.equal('m/45\'/0/1/0');
// copayer 0, address 10, internal address (change)
HDPath.FullBranch(10, true, 0).should.equal('m/45\'/0/1/10');
// copayer 7, address 10, internal address (change)
HDPath.FullBranch(10, true, 7).should.equal('m/45\'/7/1/10');
});
[
['m/45\'/0/0/0', {
index: 0,
isChange: false
}],
['m/45\'/0/0/1', {
index: 1,
isChange: false
}],
['m/45\'/0/0/2', {
index: 2,
isChange: false
}],
['m/45\'/0/1/0', {
index: 0,
isChange: true
}],
['m/45\'/0/1/1', {
index: 1,
isChange: true
}],
['m/45\'/0/1/2', {
index: 2,
isChange: true
}],
['m/45\'/0/0/900', {
index: 900,
isChange: false
}],
].forEach(function(datum) {
var path = datum[0];
var result = datum[1];
it('should get the correct indexes for path ' + path, function() {
var i = HDPath.indexesForPath(path);
i.addressIndex.should.equal(result.index);
i.isChange.should.equal(result.isChange);
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -1,141 +0,0 @@
'use strict';
var Transaction = bitcore.Transaction;
var buffertools = bitcore.buffertools;
var WalletKey = bitcore.WalletKey;
var Key = bitcore.Key;
var bignum = bitcore.Bignum;
var networks = bitcore.networks;
var Address = bitcore.Address;
var BitcorePrivateKey = bitcore.PrivateKey;
var PrivateKey = copay.PrivateKey;
var pkConfig = {
networkName: 'livenet',
};
describe('PrivateKey model', function() {
it('should create an instance', function() {
var w = new PrivateKey(pkConfig);
should.exist(w);
should.exist(w.bip);
should.exist(w.bip.derive);
});
it('should return the extended public key', function() {
var w = new PrivateKey(pkConfig);
var pubk1 = w.getExtendedPublicKeyString();
should.exist(pubk1);
});
it('should return the private key', function() {
var w = new PrivateKey(pkConfig);
var pk1 = w.getIdKey();
should.exist(pk1);
var pk2 = w.getIdKey();
should.exist(pk2);
pk1.should.be.equal(pk2);
});
it('should return the Hierarchical key', function() {
var w = new PrivateKey(pkConfig);
var hk = w._getHK();
should.exist(hk);
});
it('should derive priv keys', function() {
var pk = new PrivateKey(pkConfig);
for (var j = false; !j; j = true) {
for (var i = 0; i < 3; i++) {
var wk = pk.get(i, j);
should.exist(wk);
var o = wk.storeObj();
should.exist(o);
should.exist(o.priv);
should.exist(o.pub);
should.exist(o.addr);
var a = new Address(o.addr);
a.isValid().should.equal(true);
(function() {
var p = new PrivateKey(o.priv)
}).should.not.throw();
}
}
});
it('should derive priv keys array', function() {
var w = new PrivateKey(pkConfig);
var wks = w.getAll(2, 3);
wks.length.should.equal(5);
for (var j = 0; j < wks.length; j++) {
var wk = wks[j];
should.exist(wk);
var o = wk.storeObj();
should.exist(o);
should.exist(o.priv);
should.exist(o.pub);
should.exist(o.addr);
var a = new Address(o.addr);
a.isValid().should.equal(true);
(function() {
var p = new PrivateKey(o.priv)
}).should.not.throw();
}
});
it('fromObj toObj roundtrip', function() {
var w1 = new PrivateKey(pkConfig);
var o = JSON.parse(JSON.stringify(w1.toObj()))
var w2 = PrivateKey.fromObj(o);
w2.toObj().extendedPrivateKeyString.should.equal(w1.toObj().extendedPrivateKeyString);
w2.getId().should.equal(w1.getId());
JSON.stringify(w2.get(1, true).storeObj()).should
.equal(JSON.stringify(w1.get(1, true).storeObj()));
JSON.stringify(w2.get(1, false).storeObj()).should
.equal(JSON.stringify(w1.get(1, false).storeObj()));
});
describe('#getId', function() {
it('should calculate the copayerId', function() {
var w1 = new PrivateKey(pkConfig);
should.exist(w1.getId());
w1.getId().length.should.equal(33 * 2);
});
});
describe('#getIdPriv', function() {
it('should calculate .id', function() {
var w1 = new PrivateKey(pkConfig);
should.exist(w1.getIdPriv());
w1.getIdPriv().length.should.equal(32 * 2);
});
});
describe('#cacheId', function() {
it('should set .id and .idpriv', function() {
var w1 = new PrivateKey(pkConfig);
w1.cacheId();
var pub = w1.id;
var priv = w1.idpriv;
pub.length.should.equal(33 * 2);
priv.length.should.equal(32 * 2);
});
it('should set the id equal to the public key of the idpriv private key', function() {
var w1 = new PrivateKey(pkConfig);
w1.cacheId();
var pub = w1.id;
var priv = w1.idpriv;
pub.length.should.equal(33 * 2);
priv.length.should.equal(32 * 2);
var key1 = new bitcore.Key();
key1.private = new bitcore.Buffer(priv, 'hex');
key1.regenerateSync();
key1.public.toString('hex').should.equal(pub);
});
});
});

View file

@ -1,607 +0,0 @@
'use strict';
var Address = bitcore.Address;
var HDPath = copay.HDPath;
var PrivateKey = copay.PrivateKey;
var PublicKeyRing = copay.PublicKeyRing;
var aMasterPubKey = 'tprv8ZgxMBicQKsPdSVTiWXEqCCzqRaRr9EAQdn5UVMpT9UHX67Dh1FmzEMbavPumpAicsUm2XvC6NTdcWB89yN5DUWx5HQ7z3KByUg7Ht74VRZ';
var getNewEpk = function() {
return new PrivateKey({
networkName: 'livenet',
})
.deriveBIP45Branch()
.extendedPublicKeyString();
}
var createW = function(networkName) {
var config = {
networkName: networkName || 'livenet',
};
var w = new PublicKeyRing(config);
should.exist(w);
var copayers = [];
for (var i = 0; i < 5; i++) {
w.isComplete().should.equal(false);
w.remainingCopayers().should.equal(5 - i);
var newEpk = w.addCopayer(getNewEpk());
copayers.push(newEpk);
}
w.isComplete().should.equal(true);
w.walletId = '1234567';
return {
w: w,
copayers: copayers,
pub: w.copayersHK[0].eckey.public.toString('hex')
};
};
var cachedW;
var getCachedW = function() {
if (!cachedW) {
cachedW = createW();
}
return cachedW;
};
describe('PublicKeyRing model', function() {
it('should create an instance (livenet)', function() {
var config = {
networkName: 'livenet',
};
var w = new PublicKeyRing({
networkName: config.networkName
});
should.exist(w);
w.network.name.should.equal('livenet');
});
it('should create an instance (testnet)', function() {
var w2 = new PublicKeyRing();
should.exist(w2);
w2.network.name.should.equal('testnet');
});
it('should fail to generate shared pub keys wo extended key', function() {
var config = {
networkName: 'livenet',
};
var w2 = new PublicKeyRing(config);
should.exist(w2);
w2.registeredCopayers().should.equal(0);
w2.isComplete().should.equal(false);
(function() {
w2.getAddress(0, false);
}).should.throw();
});
it('should add and check when adding shared pub keys', function() {
var k = getCachedW();
var w = k.w;
var copayers = k.copayers;
w.isComplete().should.equal(true);
w.addCopayer.should.throw();
for (var i = 0; i < 5; i++) {
(function() {
w.addCopayer(copayers[i])
}).should.throw();
}
});
it('should be able to to store and read', function() {
var k = getCachedW();
var w = k.w;
var copayers = k.copayers;
var changeN = 2;
var addressN = 2;
var start = new Date().getTime();
for (var i = 0; i < changeN; i++) {
w.generateAddress(true, k.pub);
}
for (var i = 0; i < addressN; i++) {
w.generateAddress(false, k.pub);
}
var data = w.toObj();
should.exist(data);
var w2 = PublicKeyRing.fromObj(data);
w2.walletId.should.equal(w.walletId);
w2.isComplete().should.equal(true);
w2.addCopayer.should.throw();
for (var i = 0; i < 5; i++) {
(function() {
w.addCopayer(copayers[i])
}).should.throw();
}
w2.getHDParams(k.pub).getChangeIndex().should.equal(changeN);
w2.getHDParams(k.pub).getReceiveIndex().should.equal(addressN);
});
it('should generate some p2sh addresses', function() {
var k = getCachedW();
var w = k.w;
[true, false].forEach(function(isChange) {
for (var i = 0; i < 2; i++) {
var aStr = w.generateAddress(isChange, k.pub);
var a = new bitcore.Address(aStr);
a.isValid().should.equal(true);
a.isScript().should.equal(true);
a.network().name.should.equal('livenet');
if (i > 1) {
w.getAddress(i - 1, isChange).toString().should
.not.equal(w.getAddress(i - 2, isChange).toString());
}
}
});
});
it('caches calls to getAddress', function() {
var setup = getCachedW();
var pubkeyring = setup.w;
var address = pubkeyring._getAddress(3, false, 4);
pubkeyring.cache.addressToPath[address].should.equal("m/45'/4/0/3");
});
it('cach4es calls to getAddress (2)', function() {
var setup = getCachedW();
var pubkeyring = setup.w;
var address = pubkeyring.generateAddress(false, setup.pub);
_.indexOf(pubkeyring.getReceiveAddresses(), address).should.be.above(-1);
_.indexOf(pubkeyring.getAddresses(), address).should.be.above(-1);
});
it('cach4es calls to getAddress (3)', function() {
var setup = getCachedW();
var pubkeyring = setup.w;
var address = pubkeyring.generateAddress(true, setup.pub);
_.indexOf(pubkeyring.getReceiveAddresses(), address).should.be.equal(-1);
_.indexOf(pubkeyring.getAddresses(), address).should.be.above(-1);
});
it('should generate one address by default', function() {
var k = createW();
var w = k.w;
var a = w.getAddresses();
a.length.should.equal(1);
var b = w.getAddressesOrdered();
b.length.should.equal(1);
});
it('should generate one address by default', function() {
var k = createW();
var w = k.w;
var a = w.getAddresses();
a.length.should.equal(1);
a = w.getAddresses();
a.length.should.equal(1);
});
it('should generate 4+1 addresses', function() {
var k = createW();
var w = k.w;
var a = w.getAddresses();
a.length.should.equal(1);
[true, false].forEach(function(isChange) {
for (var i = 0; i < 2; i++) {
w.generateAddress(isChange, k.pub);
}
});
});
it('should check isChange 4+1 addresses', function() {
var k = createW();
var w = k.w;
var a = w.getAddresses();
_.each(a, function(a, j) {
var addr = new bitcore.Address(a);
w.addressIsChange(a).should.equal([false, true, true, false, false][j]);
});
});
it('should start with one shared address', function() {
var k = createW();
var a = k.w.getAddresses();
a.length.should.equal(1);
});
it('should count generation indexes', function() {
var k = createW();
var w = k.w;
for (var i = 0; i < 3; i++)
w.generateAddress(true, k.pub);
for (var i = 0; i < 2; i++)
w.generateAddress(false, k.pub);
w.getHDParams(k.pub).getChangeIndex().should.equal(3);
w.getHDParams(k.pub).getReceiveIndex().should.equal(2);
});
it('#merge index tests', function() {
var k = createW();
var w = k.w;
for (var i = 0; i < 2; i++)
w.generateAddress(true, k.pub);
for (var i = 0; i < 3; i++)
w.generateAddress(false, k.pub);
var w2 = new PublicKeyRing({
networkName: 'livenet',
walletId: w.walletId,
});
w2.merge(w).should.equal(true);
w2.requiredCopayers.should.equal(3);
w2.totalCopayers.should.equal(5);
w2.getHDParams(k.pub).getChangeIndex().should.equal(2);
w2.getHDParams(k.pub).getReceiveIndex().should.equal(3);
w2.merge(w).should.equal(false);
});
it('#merge check tests', function() {
var config = {
networkName: 'livenet',
};
var w = new PublicKeyRing(config);
w.walletId = 'lwjd5qra8257b9';
var w2 = new PublicKeyRing({
networkName: 'testnet', //wrong
walletId: w.walletId,
});
(function() {
w2.merge(w);
}).should.throw();
var w3 = new PublicKeyRing({
networkName: 'livenet',
walletId: w.walletId,
requiredCopayers: 2, // wrong
});
(function() {
w3.merge(w);
}).should.throw();
var w4 = new PublicKeyRing({
networkName: 'livenet',
walletId: w.walletId,
totalCopayers: 3, // wrong
});
(function() {
w4.merge(w);
}).should.throw();
var w6 = new PublicKeyRing({
networkName: 'livenet',
});
(function() {
w6.merge(w);
}).should.throw();
w.networkName = 'livenet';
(function() {
w6.merge(w);
}).should.throw();
var w0 = new PublicKeyRing({
networkName: 'livenet',
});
for (var i = 0; i < 5; i++)
w0.addCopayer(getNewEpk());
(function() {
w0.merge(w);
}).should.throw();
w.merge(w0, true).should.equal(true);
w.isComplete().should.equal(true);
var wx = new PublicKeyRing({
networkName: 'livenet',
});
wx.addCopayer(getNewEpk());
(function() {
w.merge(wx);
}).should.throw();
});
it('#merge pubkey tests', function() {
var config = {
networkName: 'livenet',
};
var copayers;
var w = new PublicKeyRing(config);
should.exist(w);
copayers = [];
for (var i = 0; i < 2; i++) {
w.isComplete().should.equal(false);
w.addCopayer(getNewEpk());
}
var w2 = new PublicKeyRing({
networkName: 'livenet',
id: w.id,
});
should.exist(w);
copayers = [];
for (var i = 0; i < 3; i++) {
w2.isComplete().should.equal(false);
w2.addCopayer(getNewEpk());
}
w2.merge(w).should.equal(true);
w2.isComplete().should.equal(true);
w2.merge(w).should.equal(false);
w.isComplete().should.equal(false);
w.merge(w2).should.equal(true);
w.isComplete().should.equal(true);
w.merge(w2).should.equal(false);
});
it('#merge pubkey tests (case 2)', function() {
var config = {
networkName: 'livenet',
};
var w = new PublicKeyRing(config);
should.exist(w);
for (var i = 0; i < 5; i++) {
w.isComplete().should.equal(false);
var w2 = new PublicKeyRing({
networkName: 'livenet',
id: w.id,
});
w2.addCopayer(getNewEpk());
w.merge(w2).should.equal(true);
}
w.isComplete().should.equal(true);
});
it('#merge with nickname', function() {
var config = {
networkName: 'livenet',
};
var w = new PublicKeyRing(config);
should.exist(w);
for (var i = 0; i < 3; i++) {
w.addCopayer(getNewEpk());
};
w._setNicknameForIndex(0, 'pepe0');
w._setNicknameForIndex(1, 'pepe1');
w.nicknameForIndex(0).should.equal('pepe0');
w.nicknameForIndex(1).should.equal('pepe1');
should.not.exist(w.nicknameForIndex(2));
for (var i = 0; i < 2; i++) {
w.isComplete().should.equal(false);
var w2 = new PublicKeyRing({
networkName: 'livenet',
id: w.id,
});
w2.addCopayer(getNewEpk());
w2._setNicknameForIndex(0, 'juan' + i);
w.merge(w2).should.equal(true);
}
w.isComplete().should.equal(true);
w.nicknameForIndex(0).should.equal('pepe0');
w.nicknameForIndex(1).should.equal('pepe1');
should.not.exist(w.nicknameForIndex(2));
w.nicknameForIndex(3).should.equal('juan0');
w.nicknameForIndex(4).should.equal('juan1');
});
it('#fromObj with error', function() {
var config = {
networkName: 'livenet',
};
var pkr = new PublicKeyRing(config);
(function() {
PublicKeyRing.fromObj(pkr);
}).should.throw('format');
});
it('#toObj #fromObj with nickname', function() {
var config = {
networkName: 'livenet',
};
var w = new PublicKeyRing(config);
should.exist(w);
for (var i = 0; i < 3; i++) {
w.addCopayer(getNewEpk(), 'tito' + i);
};
w.nicknameForIndex(0).should.equal('tito0');
w.nicknameForIndex(1).should.equal('tito1');
w.nicknameForIndex(2).should.equal('tito2');
should.not.exist(w.nicknameForIndex(3));
var o = JSON.parse(JSON.stringify(w.toObj()));
var w2 = PublicKeyRing.fromObj(o);
w2.nicknameForIndex(0).should.equal('tito0');
w2.nicknameForIndex(1).should.equal('tito1');
w2.nicknameForIndex(2).should.equal('tito2');
should.not.exist(w2.nicknameForIndex(3));
});
it('#fromObj old backup ', function() {
var pkr = PublicKeyRing.fromObj(JSON.parse(obj));
should.exist(pkr);
pkr.isComplete().should.equal(true);
pkr.requiredCopayers.should.equal(2);
pkr.totalCopayers.should.equal(2);
});
it('#fromObj #toObj rountrip', function() {
var obj2 = PublicKeyRing.fromObj(JSON.parse(obj)).toObj();
var pkr = PublicKeyRing.fromObj(obj2);
pkr.isComplete().should.equal(true);
pkr.requiredCopayers.should.equal(2);
pkr.totalCopayers.should.equal(2);
});
it('#fromUntrustedObj #toObj rountrip', function() {
var obj2 = PublicKeyRing.fromUntrustedObj(JSON.parse(obj)).toObj();
var pkr = PublicKeyRing.fromUntrustedObj(obj2);
pkr.isComplete().should.equal(true);
pkr.requiredCopayers.should.equal(2);
pkr.totalCopayers.should.equal(2);
});
it('#getHDParams should return the right one', function() {
var config = {
networkName: 'livenet',
};
var p = new PublicKeyRing(config);
var i = p.getHDParams(HDPath.SHARED_INDEX);
should.exist(i);
i.copayerIndex.should.equal(HDPath.SHARED_INDEX);
});
it('#getHDParams should throw error', function() {
var config = {
networkName: 'livenet',
};
var p = new PublicKeyRing(config);
(function badCosigner() {
return p.getHDParams(54);
}).should.throw();
});
it('#getRedeemScriptMap check tests', function() {
var k = getCachedW();
var w = k.w;
var amount = 2;
for (var i = 0; i < amount; i++)
w.generateAddress(true, k.pub);
for (var i = 0; i < amount; i++)
w.generateAddress(false, k.pub);
var m = w.getRedeemScriptMap([
'm/45\'/2147483647/1/0',
'm/45\'/2147483647/1/1',
'm/45\'/2147483647/0/0',
'm/45\'/2147483647/0/1'
]);
Object.keys(m).length.should.equal(4);
Object.keys(m).forEach(function(k) {
should.exist(m[k]);
});
});
it('#_getForPath should return 5 pubkeys', function() {
var w = getCachedW().w;
var pubkeys = w._getForPath('m/45\'/2147483647/1/0');
pubkeys.length.should.equal(5);
});
it('#_getForPaths should return 2 arrays of 5 pubkey ', function() {
var w = getCachedW().w;
var pubkeys = w._getForPaths(['m/45\'/2147483647/1/0', 'm/45\'/2147483647/1/1']);
pubkeys.length.should.equal(2);
pubkeys[0].length.should.equal(5);
pubkeys[1].length.should.equal(5);
});
it('#forPaths should return copayers and pubkeys ', function() {
var w = getCachedW().w;
var ret = w.forPaths(['m/45\'/2147483647/1/0', 'm/45\'/2147483647/1/1']);
ret.copayerIds.length.should.equal(5);
ret.pubKeys.length.should.equal(2);
ret.pubKeys[0].length.should.equal(5);
ret.pubKeys[1].length.should.equal(5);
});
it('#myCopayerId should return first copayerId ', function() {
var w = getCachedW().w;
w.myCopayerId().should.be.equal(w.getCopayerId(0));
});
it('#_checkKeys should throw error is not complete', function() {
var config = {
networkName: 'livenet',
};
var w2 = new PublicKeyRing(config);
(function() {
return w2._checkKeys();
}).should.throw('dont have required keys');
});
it.skip('#pathForAddress', function() {
var k = getCachedW();
var w = k.w;
var addr = w.generateAddress(true, k.pub);
var path = w.pathForAddress(addr);
path.should.not.be.undefined;
(function() {
return w.pathForAddress('abcd');
}).should.throw('find path for address');
});
it('#copayersForPubkeys', function() {
var k = getCachedW();
var w = k.w;
var addr = w.generateAddress(true, k.pub);
var path = w.pathForAddress(addr);
var paths = [];
paths.push(path);
(function() {
return w.copayersForPubkeys(k.pub, paths);
}).should.throw('Pubkeys not identified');
});
});
var obj = '{"walletId":"0a903a2eb33793d1","networkName":"testnet","requiredCopayers":2,"totalCopayers":2,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":1},{"copayerIndex":0,"changeIndex":39,"receiveIndex":0},{"copayerIndex":1,"changeIndex":102,"receiveIndex":39}],"copayersExtPubKeys":["tpubD9peJo88ArhgmJNqRkQmhHt4zAGTYVowsHrDj385xyXyMy4RhWZpV5Qx2mMDUVzpbAD5V9jci5D7cZaHhjLYP8gEkngmTKtSF4Y7V3qkAsy","tpubD8udwzKWwNUgoE2WG7LYsXKf5m1eRtJ1Etp43vnoxViFmrmZ1ND2CkdqGyQtuidcN1CiqdBUvbKegbdsMQaj5VLY2hbA4LEnLDrqkgSzikz"],"nicknameFor":{"03338b105850c7126f1f5b0439b357765b17ead8eed15bcdfdbd28d0e3915b696f":"5@queparece","0286b376d65cc4af0de5932fb8299cbef2ca9ed37ec9fdb0edfd4e9cb74eac45da":"4@queparece"}}';

View file

@ -1,437 +0,0 @@
'use strict';
var moment = moment || require('moment');
var RateService = copay.RateService;
describe('RateService model', function() {
it('should create an instance', function() {
var rs = new RateService();
should.exist(rs);
});
describe('Fetching currencies', function() {
var clock;
before(function() {
clock = sinon.useFakeTimers();
});
after(function() {
clock.restore();
});
it('should retry fetching currencies on error', function() {
var request = sinon.stub();
request.get = sinon.stub().yields('dummy error');
var rs = new RateService({
request: request
});
should.exist(rs);
request.get.calledOnce.should.be.true;
clock.tick(1000);
request.get.calledTwice.should.be.false;
clock.tick(4000);
request.get.calledTwice.should.be.true;
request.get = sinon.stub().yields(null, null, [{
code: 'USD',
name: 'United States Dollar',
rate: 2
}]);
clock.tick(7500);
request.get.calledOnce.should.be.true;
clock.tick(15000);
request.get.callCount.should.equal(1);
});
it('should refresh exchange rates after 1 hour', function() {
var request = sinon.stub();
request.get = sinon.stub().yields(null, null, [{
code: 'USD',
name: 'United States Dollar',
rate: 2
}]);
var rs = new RateService({
request: request
});
should.exist(rs);
request.get.calledOnce.should.be.true;
rs.toFiat(1e8, 'USD').should.equal(2);
request.get = sinon.stub().yields(null, null, [{
code: 'USD',
name: 'United States Dollar',
rate: 3
}]);
clock.tick(3600 * 1000);
request.get.calledOnce.should.be.true;
rs.toFiat(1e8, 'USD').should.equal(3);
});
});
describe('Conversion methods', function() {
before(function() {
sinon.stub(RateService.prototype, '_fetchCurrencies').returns();
});
after(function() {
RateService.prototype._fetchCurrencies.restore();
});
describe('#toFiat', function() {
it('should throw error when unavailable', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(false);
(function() {
rs.toFiat(10000, 'USD');
}).should.throw('not available');
});
it('should return current valuation', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(true);
var getRateStub = sinon.stub(rs, 'getRate')
getRateStub.withArgs('USD').returns(300.00);
getRateStub.withArgs('EUR').returns(250.00);
var params = [{
satoshis: 0,
code: 'USD',
expected: '0.00'
}, {
satoshis: 1e8,
code: 'USD',
expected: '300.00'
}, {
satoshis: 10000,
code: 'USD',
expected: '0.03'
}, {
satoshis: 20000,
code: 'EUR',
expected: '0.05'
}, ];
_.each(params, function(p) {
rs.toFiat(p.satoshis, p.code).toFixed(2).should.equal(p.expected);
});
});
});
describe('#toFiatHistoric', function() {
it('should return historic valuation', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(true);
var today = Date.now();
var yesterday = today - 24 * 3600;
var getHistoricalRateStub = sinon.stub(rs, 'getHistoricRate');
getHistoricalRateStub.withArgs('USD', today).yields(null, 300.00);
getHistoricalRateStub.withArgs('USD', yesterday).yields(null, 250.00);
getHistoricalRateStub.withArgs('EUR', today).yields(null, 250.00);
getHistoricalRateStub.withArgs('EUR', yesterday).yields(null, 200.00);
var params = [{
satoshis: 0,
code: 'USD',
date: today,
expected: '0.00'
}, {
satoshis: 1e8,
code: 'USD',
date: today,
expected: '300.00'
}, {
satoshis: 10000,
code: 'USD',
date: today,
expected: '0.03'
}, {
satoshis: 0,
code: 'USD',
date: today,
expected: '0.00'
}, {
satoshis: 1e8,
code: 'USD',
date: today,
expected: '300.00'
}, {
satoshis: 10000,
code: 'USD',
date: today,
expected: '0.03'
}, {
satoshis: 20000,
code: 'EUR',
date: today,
expected: '0.05'
}, {
satoshis: 20000,
code: 'EUR',
date: yesterday,
expected: '0.04'
}, ];
_.each(params, function(p) {
rs.toFiatHistoric(p.satoshis, p.code, p.date, function(err, rate) {
rate.toFixed(2).should.equal(p.expected);
});
});
});
it('should return an error', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(true);
var today = Date.now();
var yesterday = today - 24 * 3600;
var getHistoricalRateStub = sinon.stub(rs, 'getHistoricRate');
getHistoricalRateStub.withArgs('XXX', today).yields('Not found', null);
var params = [{
satoshis: 0,
code: 'XXX',
date: today,
expected: '0.00'
}];
_.each(params, function(p) {
rs.toFiatHistoric(p.satoshis, p.code, p.date, function(err, rate) {
err.should.equal('Not found');
});
});
});
});
describe('#getHistoricRate', function() {
it('should return historic rate', function() {
var yesterday = moment().subtract(1, 'day');
var reqStub = sinon.stub();
reqStub.get = sinon.stub().yields(null, {
statusCode: 200
}, {
ts: yesterday,
rate: 100
});
var rs = new RateService({
request: reqStub
});
rs.isAvailable = sinon.stub().returns(true);
var params = [{
code: 'USD',
date: yesterday,
expected: '100.00'
}];
_.each(params, function(p) {
rs.getHistoricRate('USD', yesterday, function(err, rate) {
rate.toFixed(2).should.equal(p.expected);
});
});
});
it('should return error', function() {
var yesterday = moment().subtract(1, 'day');
var reqStub = sinon.stub();
reqStub.get = sinon.stub().yields(null, {
statusCode: 500
});
var rs = new RateService({
request: reqStub
});
rs.isAvailable = sinon.stub().returns(true);
rs.getHistoricRate('USD', yesterday, function(err, rate) {
err.statusCode.should.equal(500);
});
});
});
describe('#getHistoricRates', function() {
it('should return historic rates for many dates', function() {
var yesterday = moment().subtract(1, 'day');
var lastWeekday = moment().subtract(7, 'day');
var reqStub = sinon.stub();
reqStub.get = sinon.stub().yields(null, {
statusCode: 200
}, [{
ts: lastWeekday,
rate: 90,
}, {
ts: yesterday,
rate: 100,
}, ]);
var rs = new RateService({
request: reqStub
});
rs.isAvailable = sinon.stub().returns(true);
var params = [{
code: 'USD',
date: lastWeekday,
expected: '90.00'
}, {
code: 'USD',
date: yesterday,
expected: '100.00'
}];
var dates = [lastWeekday, yesterday];
rs.getHistoricRates('USD', dates, function(err, r) {
r.length.should.equal(2);
});
//same input dates should return only two values
dates.push(lastWeekday);
dates.push(yesterday);
rs.getHistoricRates('USD', dates, function(err, r) {
r.length.should.equal(2);
});
});
it('should return a value', function() {
var yesterday = moment().subtract(1, 'day');
var reqStub = sinon.stub();
var statusIn = {
statusCode: 200
};
var rateIn = {
rate: 50
};
reqStub.get = sinon.stub().yields(null, statusIn, rateIn);
var rs = new RateService({
request: reqStub
});
rs.isAvailable = sinon.stub().returns(true);
var dates = [yesterday, yesterday];
rs.getHistoricRates('USD', dates, function(err, status, rate) {
status[0].rate.should.equal(50);
});
});
it('should return error', function() {
var yesterday = moment().subtract(1, 'day');
var reqStub = sinon.stub();
reqStub.get = sinon.stub().yields(null, {
statusCode: 500
});
var rs = new RateService({
request: reqStub
});
rs.isAvailable = sinon.stub().returns(true);
var dates = [yesterday, yesterday];
rs.getHistoricRates('USD', dates, function(err, rate) {
err.statusCode.should.equal(500);
});
});
});
describe('#fromFiat', function() {
it('should throw error when unavailable', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(false);
(function() {
rs.fromFiat(300, 'USD');
}).should.throw('not available');
});
it('should return current valuation', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(true);
var getRateStub = sinon.stub(rs, 'getRate')
getRateStub.withArgs('USD').returns(300.00);
getRateStub.withArgs('EUR').returns(250.00);
var params = [{
amount: 0,
code: 'USD',
expected: 0
}, {
amount: 300.00,
code: 'USD',
expected: 1e8
}, {
amount: 600.00,
code: 'USD',
expected: 2e8
}, {
amount: 250.00,
code: 'EUR',
expected: 1e8
}, ];
_.each(params, function(p) {
rs.fromFiat(p.amount, p.code).should.equal(p.expected);
});
});
});
describe('#listAlternatives', function() {
it('should throw error when unavailable', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(false);
(function() {
rs.listAlternatives();
}).should.throw('not available');
});
it('should return list of available currencies', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(true);
sinon.stub(rs, 'getAlternatives').returns([{
name: 'United States Dollar',
isoCode: 'USD',
rate: 300.00,
}, {
name: 'European Union Euro',
isoCode: 'EUR',
rate: 250.00,
}, ])
var list = rs.listAlternatives();
list.should.exist;
list.length.should.equal(2);
});
});
describe('#getAlternatives', function() {
it('should return nothing when trying to get the list of available currencies', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(false);
rs.whenAvailable(function() {});
rs.getAlternatives().length.should.equal(0);
});
});
describe('#whenAvailable is available', function() {
it('should return callback ', function() {
var rs = new RateService();
rs.isAvailable = sinon.stub().returns(true);
rs.whenAvailable(function() {});
});
});
describe('#whenAvailable is not available', function() {
it('should queue the callback ', function() {
var rs = new RateService();
var count = rs._queued.length;
rs.isAvailable = sinon.stub().returns(false);
rs.whenAvailable(function() {});
rs._queued.length.should.be.equal(count + 1);
});
});
describe('#singleton', function() {
it('should create only one instance', function() {
var rs = RateService.singleton();
rs.should.be.not.null;
var rs2 = RateService.singleton();
rs2.should.be.not.null;
rs.should.deep.equal(rs2);
});
});
});
});

View file

@ -1,817 +0,0 @@
'use strict';
var Transaction = bitcore.Transaction;
var WalletKey = bitcore.WalletKey;
var Key = bitcore.Key;
var bignum = bitcore.Bignum;
var Script = bitcore.Script;
var TransactionBuilder = bitcore.TransactionBuilder;
var util = bitcore.util;
var networks = bitcore.networks;
var Buffer = bitcore.Buffer;
var TxProposal = copay.TxProposal;
describe('TxProposal', function() {
// These 2 signed the scripts below
var PUBKEYS = ['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3'];
// Signatures of the scripts below
var SIG0 = '304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101';
var SIG1 = '3044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae01';
/* decoded redeemscript
*
"asm" : "3 03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d 0380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127 0392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed03 03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3 03e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e4 5 OP_CHECKMULTISIG",
*/
// 1,2 signatures 3-5!
var SCRIPTSIG = _.map([
'0048304502207d8e832bd576c93300e53ab6cbd68641961bec60690c358fd42d8e42b7d7d687022100a1daa89923efdb4c9b615d065058d9e1644f67000694a7d0806759afa7bef19b014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae',
'0048304502200708a381dde585ef7fdfaeaeb5da9b451d3e22b01eac8a5e3d03b959e24a7478022100c90e76e423523a54a9e9c43858337ebcef1a539a7fc685c2698dd8648fcf1b9101473044022030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'
], function(hex) {
return new Buffer(hex, 'hex');
});
var someKeys = ["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5", "022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"];
// 3-5
function dummyBuilder(opts) {
opts = opts || {};
var index = opts.nsig ? opts.nsig - 1 : 1;
var script = SCRIPTSIG[index];
var aIn = {
s: script
};
var tx = {};
tx.ins = opts.noins ? [] : [opts.nosigs ? {} : aIn];
tx.serialize = sinon.stub().returns(new Buffer('1234', 'hex'));
tx.getSize = sinon.stub().returns(1);
tx.getHashType = sinon.stub().returns(opts.hashtype || 1);
tx.getNormalizedHash = sinon.stub().returns('123456');
tx.hashForSignature = sinon.stub().returns(
new Buffer('31103626e162f1cbfab6b95b08c9f6e78aae128523261cb37f8dfd4783cb09a7', 'hex'));
var builder = {};
builder.opts = opts.opts || {};
builder.build = sinon.stub().returns(tx)
builder.toObj = sinon.stub().returns({
iAmBuilderObj: true,
version: 1,
opts: builder.opts,
});
builder.isFullySigned = sinon.stub().returns(false);
builder.vanilla = {
scriptSig: [SCRIPTSIG[1]],
outs: JSON.stringify([{
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
amountSatStr: '123',
}]),
};
builder.inputsSigned = 0;
return builder;
};
function dummyProposal(opts) {
opts = opts || {};
return new TxProposal({
creator: 'creator',
createdTs: 1,
builder: dummyBuilder(opts),
inputChainPaths: ['m/1'],
})
};
describe('new', function() {
it('should fail to create an instance with wrong arguments', function() {
(function() {
var txp = new TxProposal();
}).should.throw('Illegal Argument');
(function() {
var txp = new TxProposal({
creator: 1
});
}).should.throw('no inputChainPaths');
});
it('should create an instance', function() {
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: dummyBuilder(),
inputChainPaths: 'm/1',
});
should.exist(txp);
txp.creator.should.equal(1);
should.exist(txp.builder);
txp.inputChainPaths.should.equal('m/1');
});
});
describe('#getId', function() {
it('should return id', function() {
var b = new dummyBuilder();
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: b,
inputChainPaths: 'm/1',
});
txp.getId().should.equal('123456');;
sinon.assert.callCount(b.build().getNormalizedHash, 1);
});
});
describe('#toObj', function() {
it('should return an object and remove builder', function() {
var b = new dummyBuilder();
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: b,
inputChainPaths: 'm/1',
});
var o = txp.toObj();
should.exist(o);
o.creator.should.equal(1);
should.not.exist(o.builder);
should.exist(o.builderObj);
});
it('toObjTrim', function() {
var b = new dummyBuilder();
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: b,
inputChainPaths: 'm/1',
comment: 'hola',
});
var o = txp.toObjTrim();
should.exist(o);
should.not.exist(o.creator);
should.not.exist(o.builder);
should.exist(o.comment);
should.exist(o.builderObj);
});
});
describe('#fromUntrustedObj', function() {
it('should fail to create from wrong object', function() {
var b = new dummyBuilder();
(function() {
var txp = TxProposal.fromUntrustedObj({
creator: 1,
createdTs: 1,
builderObj: b.toObj(),
inputChainPaths: ['m/1'],
});
}).should.throw('tx is not defined');
});
it('sets force opts', function() {
// Create an incomming TX proposal, with certain options...
var b = new dummyBuilder({
opts: {
juan: 1,
pepe: 1,
fee: 1000
}
});
var o = {
creator: 1,
createdTs: 1,
builderObj: b.toObj(),
inputChainPaths: ['m/1'],
};
sinon.stub(TxProposal.prototype, '_check').returns(true);
//Force other options
var txp = TxProposal.fromUntrustedObj(o, {
pepe: 100
});
o.builderObj.opts.should.deep.equal({
juan: 1,
pepe: 100,
feeSat: undefined,
fee: undefined
});
TxProposal.prototype._check.restore();
});
});
describe('#fromObj', function() {
});
describe('#getSeen', function() {
it('should set txid and timestamp', function() {
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: new dummyBuilder(),
inputChainPaths: ['m/1'],
});
var ts = txp.getSeen('pepe');
expect(ts).to.be.undefined;
txp.setSeen('pepe');
ts = txp.getSeen('pepe');
expect(ts).to.be.not.undefined;
txp.setSeen('pepe');
var ts2 = txp.getSeen('pepe');
ts.should.be.equal(ts2);
});
});
describe('#setSent', function() {
it('should set txid and timestamp', function() {
var now = Date.now();
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: new dummyBuilder(),
inputChainPaths: ['m/1'],
});
txp.setSent('3a42');
txp.sentTs.should.gte(now);
txp.sentTxid.should.equal('3a42');
});
});
describe('#getSent', function() {
it('should get sent timestamp', function() {
var now = Date.now();
var txp = new TxProposal({
creator: 1,
createdTs: 1,
builder: new dummyBuilder(),
inputChainPaths: ['m/1'],
});
var sentTs = txp.getSent();
should.not.exist(sentTs);
txp.setSent('3a42');
sentTs = txp.getSent();
sentTs.should.gte(now);
});
});
describe('Signature verification', function() {
var validScriptSig1Sig = new bitcore.Script(SCRIPTSIG[0]);
var validScriptSig = new bitcore.Script(SCRIPTSIG[1]);
var pubkeys = [
'03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d',
'0380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127',
'0392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed03',
'03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3',
'03e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e4'
].map(function(hex) {
return new Buffer(hex, 'hex');
});
var keyBuf = someKeys.map(function(hex) {
return new Buffer(hex, 'hex');
});
it('#formatKeys', function() {
(function() {
TxProposal.formatKeys(someKeys);
}).should.throw('buffers');
var res = TxProposal.formatKeys(keyBuf);
});
it('#_verifyScriptSig arg checks', function() {
var txp = dummyProposal();
(function() {
txp.verifySignatures(
keyBuf,
new bitcore.Script(new Buffer('112233', 'hex')),
new Buffer('1a', 'hex'));
}).should.throw('script');
});
it('#_verifyScriptSig, no signatures', function() {
var txp = dummyProposal();
(function() {
txp.verifySignatures(keyBuf, validScriptSig, new Buffer(32));
}).should.throw('invalid');
});
it('#_verifyScriptSig, one signature', function() {
// Data taken from bitcore's TransactionBuilder test
var txp = dummyProposal();
var tx = dummyProposal().builder.build();
var ret = txp.verifySignatures(pubkeys, validScriptSig1Sig, tx.hashForSignature());
ret.should.deep.equal(['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d']);
});
it('#_verifyScriptSig, two signatures', function() {
// Data taken from bitcore's TransactionBuilder test
var txp = dummyProposal();
var tx = dummyProposal().builder.build();
var ret = txp.verifySignatures(pubkeys, validScriptSig, tx.hashForSignature());
ret.should.deep.equal(['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']);
});
it('#infoFromRedeemScript', function() {
var info = TxProposal.infoFromRedeemScript(validScriptSig);
var keys = info.keys;
keys.length.should.equal(5);
for (var i in keys) {
keys[i].toString('hex').should.equal(pubkeys[i].toString('hex'));
}
Buffer.isBuffer(info.script.getBuffer()).should.equal(true);
});
it('#getSignersPubKeys', function() {
var txp = dummyProposal();
var pubkeys = txp.getSignersPubKeys();
pubkeys.should.deep.equal([PUBKEYS]);
});
describe('#getSignatures', function() {
it('should get signatures', function() {
var txp = dummyProposal();
var sigs = txp.getSignatures();
sigs.length.should.equal(1);
sigs[0].length.should.equal(2);
sigs[0][0].should.equal(SIG0);
sigs[0][1].should.equal(SIG1);
});
});
describe('#addSignature', function() {
it('should add signatures maintaing pubkeys order', function() {
var txp = dummyProposal({
nsig: 1
});
txp.getSignersPubKeys()[0].length.should.equal(1);
txp.addSignature('pepe', [SIG1]);
txp.getSignersPubKeys(true)[0].length.should.equal(2);
var keys = txp.getSignersPubKeys()[0];
var keysSorted = _.clone(keys).sort();
keysSorted.should.deep.equal(keys);
});
it('should add signatures to incomplete txs ', function() {
var txp = dummyProposal({
nsig: 1
});
txp.addSignature('pepe', [SIG1]);
txp.builder.inputsSigned.should.be.equal(0);
});
it('should not add signatures to complete txs ', function() {
var txp = dummyProposal({
nsig: 1
});
txp.builder.isFullySigned = sinon.stub.returns(true);
var r = txp.addSignature('pepe', [SIG1]);
r.should.be.false;
});
it('should fail with invalid signatures', function() {
var txp = dummyProposal({
nsig: 1
});
txp.getSignersPubKeys()[0].length.should.equal(1);
(function() {
txp.addSignature('pepe', ['002030a77c9613d6ee010717c1abc494668d877e3fa0ae4c520f65cc3b308754c98c02205219d387bcb291bd44805b9468439e4168b02a6a180cdbcc24d84d71d696c1ae01']);
}).should.throw('BADSIG');
});
it('should fail adding the same signature twice', function() {
var txp = dummyProposal({
nsig: 1
});
txp.getSignersPubKeys()[0].length.should.equal(1);
txp.addSignature('pepe', [SIG1]);
(function() {
txp.addSignature('pepe', [SIG1]);
}).should.throw('BADSIG');
});
});
describe('#_check', function() {
it('OK', function() {
dummyProposal({})._check();
});
it('FAIL Invalid tx proposal', function() {
var txp = dummyProposal();
var old = txp.builder.signhash;
txp.builder.signhash = 'ppp';
(function() {
txp._check();
}).should.throw('Invalid tx proposal');
txp.builder.signhash = old;
});
it('FAIL ins', function() {
(function() {
dummyProposal({
noins: true,
})._check();
}).should.throw('no ins');
});
it('FAIL txp too big ', function() {
var txp = dummyProposal();
var old_builder = txp.builder.build;
var tx = {
getSize: function() {
return 90000;
}
};
txp.builder.build = sinon.stub().returns(tx);;
(function() {
txp._check();
}).should.throw('BIG: Invalid TX proposal. Too big');
txp.builder.build = old_builder;
});
it('FAIL signhash SINGLE', function() {
var txp = dummyProposal({
hashtype: Transaction.SIGHASH_SINGLE
});
(function() {
txp._check();
}).should.throw('signatures');
});
it('FAIL signhash NONE', function() {
var txp = dummyProposal({
hashtype: Transaction.SIGHASH_NONE,
});
(function() {
txp._check();
}).should.throw('signatures');
});
it('FAIL signhash ANYONECANPAY', function() {
var txp = dummyProposal({
hashtype: Transaction.SIGHASH_ANYONECANPAY,
});
(function() {
txp._check();
}).should.throw('signatures');
});
it('FAIL no signatures', function() {
var txp = dummyProposal({
nosigs: true,
});
(function() {
txp._check();
}).should.throw('no signatures');
});
});
describe('#_checkPayPro', function() {
var txp, md;
beforeEach(function() {
txp = dummyProposal();
txp.paymentProtocolURL = '123';
md = {
request_url: '123',
pr: {
pd: {
expires: 123,
memo: 'memo',
},
},
total: '1230',
outs: [{
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
amountSatStr: "123"
}],
expires: 92345678900,
};
});
it('OK no merchant data', function() {
txp._checkPayPro();
});
it('OK merchant data', function() {
txp.addMerchantData(md);
});
it('NOK URL', function() {
txp.paymentProtocolURL = '1234';
(function() {
txp.addMerchantData(md);
}).should.throw('Mismatch');
});
it('NOK OUTS', function() {
md.outs = [];
(function() {
txp.addMerchantData(md);
}).should.throw('outputs');
});
it('NOK OUTS (case 2)', function() {
md.outs = [{}, {}];
(function() {
txp.addMerchantData(md);
}).should.throw('outputs');
});
it('NOK OUTS (case 3)', function() {
md.outs = [{}, {}];
(function() {
txp.addMerchantData(md);
}).should.throw('outputs');
});
it('NOK OUTS (case 4)', function() {
txp.builder.vanilla.outs = '[1,2]';
txp.merchant = {};
txp.paymentProtocolURL = txp.merchant.request_url;
txp.merchant.total = 1;
txp.merchant.outs = [{
amountSatStr: '1',
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6'
}];
(function() {
txp._checkPayPro();
}).should.throw('Wrong outs in Tx');
});
it('NOK Amount', function() {
md.total = undefined;
(function() {
txp.addMerchantData(md);
}).should.throw('amount');
});
it('NOK Outs case 4', function() {
md.outs[0].address = 'aaa';
(function() {
txp.addMerchantData(md);
}).should.throw('address');
});
it('NOK Outs case 5', function() {
md.outs[0].amountSatStr = '432';
(function() {
txp.addMerchantData(md);
}).should.throw('amount');
});
it('NOK Expired', function() {
md.expires = 1;
(function() {
txp.addMerchantData(md);
}).should.throw('expired');
});
it('OK Expired but sent', function() {
md.expires = 2;
txp.sentTs = 1;
txp.addMerchantData(md);
});
});
describe.skip('#merge', function() {
it('with self', function() {
var txp = dummyProposal();
var hasChanged = txp.merge(txp);
hasChanged.should.equal(false);
});
it('with less signatures', function() {
var txp = dummyProposal();
var txp1Sig = dummyProposal({
nsig: 1
});
var backup = txp.builder.vanilla.scriptSig[0];
var hasChanged = txp.merge(txp);
hasChanged.should.equal(true);
txp.builder.vanilla.scriptSig = [backup];
txp.builder.tx.ins[0].s = new Buffer(backup, 'hex');
});
it('with more signatures', function() {
txp.builder.merge = function() {
// 3 signatures.
this.vanilla.scriptSig = ['00483045022100f75bd3eb92d8c9be9a94d848bbd1985fc0eaf4c47fb470a0b222881802a1f03802204eb239ae3082779b1ec4f2e69baa0362494071e707e1696c14ad23c8f2e184e20148304502201981482db0f369ce943293b6fec06a0347918663c766a79d4cbd0457801768d1022100aedf8d7c51d55a9ddbdcc0067ed6b648b77ce9660447bbcf4e2c209698efa0a30148304502203f0ddad47757f8705cb40e7c706590d2e2028a7027ffdb26dd208fd6155e0d28022100ccd206f9b969ab7f88ee4c5c6cee48c800a62dda024c5a8de7eb8612b833a0c0014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
this.tx.ins[0].s = new Buffer(this.vanilla.scriptSig[0], 'hex');
};
var hasChanged = txp.merge(txp);
hasChanged.should.equal(true);
});
});
describe('#setCopayers', function() {
it("should fails if Tx has no creator", function() {
var txp = dummyProposal();
txp.signedBy = {
'hugo': 1
};
delete txp['creator'];
(function() {
txp.setCopayers({
pk1: 'pepe'
})
}).should.throw('no creator');
});
it("should fails if Tx is not signed by creator", function() {
var txp = dummyProposal();
txp.creator = 'creator';
txp.signedBy = {
'hugo': 1
};
txp._inputSigners = [
['pkX']
];
(function() {
txp.setCopayers({
pk1: 'pepe'
})
}).should.throw('creator');
});
it("should fails if Tx has unmapped signatures", function() {
var txp = dummyProposal();
txp.creator = 'creator';
txp.signedBy = {
creator: 1
};
txp._inputSigners = [
['pk0', 'pkX']
];
(function() {
txp.setCopayers({
pk1: 'pepe'
})
}).should.throw('unknown sig');
});
// This was disabled. Unnecessary to check this.
it.skip("should be signed by sender", function() {
var txp = dummyProposal();
var ts = Date.now();
txp._inputSigners = [
['pk1', 'pk0']
];
txp.signedBy = {
'creator': Date.now()
};
(function() {
txp.setCopayers({
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
}).should.throw('senders sig');
});
it("should set signedBy (trivial case)", function() {
var txp = dummyProposal();
var ts = Date.now();
sinon.stub(txp, 'getSignersPubKeys').returns(['pk1', 'pk0']);
txp.signedBy = {
'creator': Date.now()
};
txp.setCopayers({
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
Object.keys(txp.signedBy).length.should.equal(2);
txp.signedBy['pepe'].should.gte(ts);
txp.signedBy['creator'].should.gte(ts);
});
it("should assign creator", function() {
var txp = dummyProposal();
var ts = Date.now();
sinon.stub(txp, 'getSignersPubKeys').returns(['pk0']);
txp.signedBy = {};
delete txp['creator'];
delete txp['creatorTs'];
txp.setCopayers({
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
Object.keys(txp.signedBy).length.should.equal(1);
txp.creator.should.equal('creator');
txp.createdTs.should.gte(ts);
txp.seenBy['creator'].should.equal(txp.createdTs);
});
it("New tx should have only 1 signature", function() {
var txp = dummyProposal();
var ts = Date.now();
txp.signedBy = {};
delete txp['creator'];
delete txp['creatorTs'];
sinon.stub(txp, 'getSignersPubKeys').returns(['pk0', 'pk1']);
(function() {
txp.setCopayers({
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
}, {
'creator2': 1
});
}).should.throw('only 1');
});
it("if signed, should not change ts", function() {
var txp = dummyProposal();
var ts = Date.now();
sinon.stub(txp, 'getSignersPubKeys').returns(['pk0', 'pk1']);
txp.creator = 'creator';
txp.signedBy = {
'creator': 1
};
txp.setCopayers({
pk0: 'creator',
pk1: 'pepe',
pk2: 'john'
})
Object.keys(txp.signedBy).length.should.equal(2);
txp.creator.should.equal('creator');
txp.signedBy['creator'].should.equal(1);
txp.signedBy['pepe'].should.gte(ts);
});
});
});
describe('micelaneous functions', function() {
it('should sign previously reject tx', function() {
var txp = dummyProposal();
txp.rejectCount().should.equal(0);
txp.setRejected(['juan'])
txp.rejectCount().should.equal(1);
txp._setSigned('juan');
txp.rejectCount().should.equal(0);
});
it('should report rejectCount', function() {
var txp = dummyProposal();
txp.rejectCount().should.equal(0);
txp.setRejected(['juan'])
txp.rejectCount().should.equal(1);
});
it('should report isPending 1', function() {
var txp = dummyProposal();
txp.rejectedBy = [];
txp.sentTxid = 1;
txp.isPending(3).should.equal(false);
});
it('should report isPending 2', function() {
var txp = dummyProposal();
txp.rejectedBy = [];
txp.sentTxid = null;
txp.isPending(3).should.equal(true);
});
it('should report isPending 3', function() {
var txp = dummyProposal();
txp.rejectedBy = [1, 2, 3, 4];
txp.sentTxid = null;
txp.isPending(3).should.equal(false);
});
});
});

View file

@ -1,298 +0,0 @@
'use strict';
var Transaction = bitcore.Transaction;
var WalletKey = bitcore.WalletKey;
var Key = bitcore.Key;
var bignum = bitcore.Bignum;
var Script = bitcore.Script;
var TransactionBuilder = bitcore.TransactionBuilder;
var util = bitcore.util;
var networks = bitcore.networks;
var TxProposal = copay.TxProposal;
var TxProposals = copay.TxProposals;
var moment = moment || require('moment');
var dummyProposal = new TxProposal({
creator: 1,
createdTs: 1,
builder: {
toObj: sinon.stub().returns({
getId: sinon.stub().returns('1234')
}),
},
inputChainPaths: ['m/1'],
});
var someKeys = ["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5", "022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"];
describe('TxProposals', function() {
describe('constructor', function() {
it('should create an instance', function() {
var txps = new TxProposals();
should.exist(txps);
txps.network.name.should.equal('testnet');
});
});
describe('#fromObj', function() {
it('should create an instance from an Object', function() {
var txps = TxProposals.fromObj({
networkName: 'livenet',
walletId: '123a12',
txps: [],
});
should.exist(txps);
txps.network.name.should.equal('livenet');
});
it('should create an instance from an Object using builder', function() {
function dummyBuilder(opts) {
opts = opts || {};
var index = opts.nsig ? opts.nsig - 1 : 1;
var script = SCRIPTSIG[index];
var aIn = {
s: script
};
var tx = {};
tx.ins = opts.noins ? [] : [opts.nosigs ? {} : aIn];
tx.serialize = sinon.stub().returns(new Buffer('1234', 'hex'));
tx.getSize = sinon.stub().returns(1);
tx.getHashType = sinon.stub().returns(opts.hashtype || 1);
tx.getNormalizedHash = sinon.stub().returns('123456');
tx.hashForSignature = sinon.stub().returns(
new Buffer('31103626e162f1cbfab6b95b08c9f6e78aae128523261cb37f8dfd4783cb09a7', 'hex'));
tx.getId = sinon.returns(tx.getNormalizedHash().toString('hex'));
var builder = {};
builder.opts = opts.opts || {};
builder.build = sinon.stub().returns(tx)
builder.toObj = sinon.stub().returns({
iAmBuilderObj: true,
version: 1,
opts: builder.opts,
});
builder.isFullySigned = sinon.stub().returns(false);
builder.vanilla = {
scriptSig: [SCRIPTSIG[1]],
outs: JSON.stringify([{
address: '2NDJbzwzsmRgD2o5HHXPhuq5g6tkKTjYkd6',
amountSatStr: '123',
}]),
};
builder.inputsSigned = 0;
return builder;
};
var txps1 = [];
txps1.push(dummyProposal);
var txps = TxProposals.fromObj({
networkName: 'testnet',
walletId: '123a12',
txps: txps1,
builder: dummyBuilder
});
should.exist(txps);
});
it('should skip Objects with errors', function() {
var txps = TxProposals.fromObj({
networkName: 'livenet',
walletId: '123a12',
txps: [{
a: 1
}],
});
should.exist(txps);
Object.keys(txps.txps).length.should.equal(0);
});
});
describe('#length', function() {
it('should return length', function() {
var txps = new TxProposals();
txps.txps = {
a: 1,
b: 2
};
txps.length().should.equal(2);
});
});
describe('#getNtxidsSince', function() {
it('should throw illegal argument', function() {
var txps = new TxProposals();
txps.txps = {
a: 1,
b: 2
};
(function() {
txps.getNtxidsSince()
}).should.throw('Illegal Argument');
});
it('should return keys since a date', function() {
var today = moment().toDate();
var today_plus_1 = moment().add(1, 'day').toDate();
var today_plus_2 = moment().add(2, 'day').toDate();
var today_plus_3 = moment().add(3, 'day').toDate();
var txps = new TxProposals();
txps.txps = [{
id: 1,
createdTs: today
}, {
id: 2,
createdTs: today_plus_1
}, {
id: 3,
createdTs: today_plus_2
}];
txps.getNtxidsSince(today).length.should.be.equal(3);
txps.getNtxidsSince(today_plus_1).length.should.be.equal(2);
txps.getNtxidsSince(today_plus_2).length.should.be.equal(1);
txps.getNtxidsSince(today_plus_3).length.should.be.equal(0);
});
});
describe('#getNtxids', function() {
it('should return keys', function() {
var txps = new TxProposals();
txps.txps = {
a: 1,
b: 2
};
txps.getNtxids().should.deep.equal(['a', 'b']);
});
});
describe('#deleteOne', function() {
it('should delete specified ntxid', function() {
var txps = new TxProposals();
txps.txps = {
a: 1,
b: 2
};
txps.deleteOne('a');
txps.getNtxids().should.deep.equal(['b']);
});
it('should fail on non-existent ntxid', function() {
var txps = new TxProposals();
txps.txps = {
a: 1,
b: 2
};
(function() {
txps.deleteOne('c');
}).should.throw('Unknown TXP: c');
});
});
describe('#deletePending', function() {
it('should delete pending proposals', function() {
var txps = new TxProposals();
txps.txps = {
a: {
isPending: sinon.stub().returns(true)
},
b: {
isPending: sinon.stub().returns(false)
},
};
txps.deletePending(2);
txps.getNtxids().should.deep.equal(['b']);
});
});
describe('#getUsedUnspent', function() {
it('should return an empty object', function() {
var txps = new TxProposals();
txps.txps = {
a: {
isPending: sinon.stub().returns(false)
},
b: {
isPending: sinon.stub().returns(false)
},
};
var r = txps.getUsedUnspent(2);
Object.keys(r).length.should.equal(0);
});
it('should return an non empty object', function() {
var txps = new TxProposals();
txps.txps = {
a: {
isPending: sinon.stub().returns(true),
builder: {
getSelectedUnspent: sinon.stub().returns([{
txid: 'a1',
vout: '00'
}])
}
},
b: {
isPending: sinon.stub().returns(false)
},
};
var r = txps.getUsedUnspent(2);
Object.keys(r).length.should.equal(1);
});
});
describe('#toObj', function() {
it('should an object', function() {
var txps = TxProposals.fromObj({
networkName: 'livenet',
walletId: '123a12',
txps: [],
});
var o = txps.toObj();
o.walletId.should.equal('123a12');
o.networkName.should.equal('livenet');
});
it('should export txps', function() {
var txps = TxProposals.fromObj({
networkName: 'livenet',
walletId: '123a12',
txps: [],
});
txps.txps = {
'hola': dummyProposal,
'chau': dummyProposal,
};
var o = txps.toObj();
o.txps.length.should.equal(2);
});
it('should filter sent txp', function() {
var txps = TxProposals.fromObj({
networkName: 'livenet',
walletId: '123a12',
txps: [],
});
var d = JSON.parse(JSON.stringify(dummyProposal));
d.sent = 1;
txps.txps = {
'hola': dummyProposal,
'chau': d,
};
var o = txps.toObj();
o.txps.length.should.equal(1);
});
});
describe.skip('#merge', function() {
it('should merge', function() {
var txps = new TxProposals();
var d = dummyProposal;
txps.merge(d.toObj(), {});
});
});
});

File diff suppressed because one or more lines are too long

View file

@ -1,518 +0,0 @@
'use strict';
var FakeSocket = requireMock('FakeBlockchainSocket');
var Buffer = bitcore.Buffer;
var Insight = copay.Insight;
var ADDRESSES = [
'2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM',
'2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb', // 41btc
'2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x', // 50btc
'2NBEAi14f3xhwmGs9omEgKUwsW84BkzLp7S',
'2N3RhiBW4ssXJnEbPjBCYThJHhEHQWAapf6',
'2Mvn2Duvw8cdHs5AB8ZLXfoef1a71UrDr4W',
'2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY',
'2N9EdxU3co5XKTyj3yhFBeU3qw3EM1rrgzE'
];
var UNSPENT = [{
address: "2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb",
txid: "d5597c6cf7f72507af63a4d5a2f9f84edb45fb42452cc8c514435b7a93158915",
vout: 0,
ts: 1397050347,
scriptPubKey: "a914e54f125244a0bf91f9c5d861dc28343ccf19883d87",
amount: 41,
confirmations: 7007
}, {
address: "2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x",
txid: "90d0e1f993fc41596e7b0a7a3be8ef65d606164e13ce538bd3f48136b60eff5a",
vout: 0,
ts: 1397070106,
scriptPubKey: "a914af1a2d1a9c0fa172ed70bc1c50ea6b66994e9abf87",
amount: 50,
confirmations: 6728
}];
var FAKE_OPTS = {
url: 'http://something.com:123',
}
describe('Insight model', function() {
before(function() {
sinon.stub(Insight.prototype, "_getSocketIO", function() {
return new FakeSocket();
});
});
after(function() {
Insight.prototype._getSocketIO.restore();
});
it('should create an instance', function() {
var blockchain = new Insight(FAKE_OPTS);
should.exist(blockchain);
blockchain.url.should.be.equal('http://something.com:123');
});
it('should subscribe to inventory', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
var emitSpy = sinon.spy(socket, 'emit');
blockchain.on('connect', function() {
emitSpy.calledWith('subscribe', 'inv');
done();
});
});
it('should be able to destroy the instance', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.status.should.be.equal('disconnected');
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
blockchain.destroy();
Object.keys(blockchain.getSubscriptions()).length.should.equal(0);
blockchain.status.should.be.equal('destroyed');
done();
});
});
it('should subscribe to an address', function() {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
var emitSpy = sinon.spy(socket, 'emit');
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
});
it('should subscribe to an address once', function() {
var blockchain = new Insight(FAKE_OPTS);
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
Object.keys(blockchain.getSubscriptions()).length.should.equal(1);
});
it('should subscribe to a list of addresses', function() {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
var emitSpy = sinon.spy(socket, 'emit');
blockchain.subscribe([
'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM',
'2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8'
]);
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
emitSpy.calledWith('subscribe', 'mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
emitSpy.calledWith('subscribe', '2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
});
it('should resubscribe to all addresses', function() {
var blockchain = new Insight(FAKE_OPTS);
blockchain.subscribe('mg7UbtKgMvWAixTNMbC8soyUnwFk1qxEuM');
blockchain.subscribe('2NBBHBjB5sd7HFqKtout1L7d6dPhwJgP2j8');
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
blockchain.reSubscribe();
Object.keys(blockchain.getSubscriptions()).length.should.equal(2);
});
it('should broadcast a raw transaction', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var rawtx = '01000000010c2a03ed71ee18148e8c99c5ff66d5ffb75e5def46cdea2acc6f30103f33bfb5010000006a47304402207f960aeefdfad270dd77d1acca7af17d3a2e47e2059034ff5d6305cf63635e1d02202f061ee196cc4459cdecae6559beac696a9ecde9a17520849f319fa2a627e64f012103870465f9b4efb90b5d186a7a5eacd7081e601020dacd68d942e5918a56ed0bfcffffffff02a086010000000000ad532102a9495c64323cd8c3354dbf0b3400d830ee680da493acbccc3c2c356d1b20fabf21028233cf8bc6112ae2c36468bd447732c5586b52e1ba3284a2319cadfac6367f99210279fd856e5ed13ab6807e85ed7c0cd6f80613be042240fd731c43f5aba3dcae9821021380858a67a4f99eda52ce2d72c300911f9d3eb9d7a45102a2133f14f7b2dc14210215739b613ce42106a11ce433342c13c610bf68a1bc934f607ad7aeb4178e04cf55ae2044d200000000001976a9146917322f0010aaf7ec136a34b476dfc5eb7a331288ac00000000';
sinon.stub(blockchain, "requestPost", function(url, data, cb) {
url.should.be.equal('/api/tx/send');
var res = {
statusCode: 200
};
var body = {
txid: 1234
};
setTimeout(function() {
cb(null, res, body);
}, 0);
});
blockchain.broadcast(rawtx, function(err, id) {
id.should.be.equal(1234);
done();
});
});
describe('getTransaction', function() {
it('should get a transaction by id', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var txid = '123321';
var tx = {
txid: txid,
more: 'something'
};
sinon.stub(blockchain, "request", function(url, cb) {
url.should.be.equal('/api/tx/' + txid);
var res = {
statusCode: 200
};
var body = JSON.stringify(tx);
setTimeout(function() {
cb(null, res, body);
}, 0);
});
blockchain.getTransaction(txid, function(err, t) {
chai.expect(err).to.be.null;
t.should.be.an('object');
t.txid.should.be.equal(tx.txid);
t.more.should.be.equal(tx.more);
done();
});
});
it('should handle a 404 error code', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var txid = '123321';
sinon.stub(blockchain, "request", function(url, cb) {
url.should.be.equal('/api/tx/' + txid);
var res = {
statusCode: 404
};
var body = '';
setTimeout(function() {
cb(null, res, body);
}, 0);
});
blockchain.getTransaction(txid, function(err, t) {
chai.expect(t).to.be.undefined;
chai.expect(err).not.be.null;
done();
});
});
it('should handle a null response', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var txid = '123321';
sinon.stub(blockchain, "request", function(url, cb) {
url.should.be.equal('/api/tx/' + txid);
var res = {
statusCode: 200
};
var body = null;
setTimeout(function() {
cb(null, res, body);
}, 0);
});
blockchain.getTransaction(txid, function(err, t) {
chai.expect(t).to.be.undefined;
chai.expect(err).not.be.null;
done();
});
});
it('should handle an empty response', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var txid = '123321';
sinon.stub(blockchain, "request", function(url, cb) {
url.should.be.equal('/api/tx/' + txid);
var res = {
statusCode: 200
};
var body = null;
setTimeout(function() {
cb(null, res, body);
}, 0);
});
blockchain.getTransaction(txid, function(err, t) {
chai.expect(t).to.be.undefined;
chai.expect(err).not.be.null;
done();
});
});
});
it('should get a set of transaction by addresses', function(done) {
var blockchain = new Insight(FAKE_OPTS);
sinon.stub(blockchain, "requestPost", function(url, data, cb) {
url.should.be.equal('/api/addrs/txs?from=0');
data.addrs.should.be.equal('2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM,2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb');
setTimeout(function() {
var res = {
statusCode: 200
};
var body = {
totalItems: 3,
items: [1, 2, 3]
};
cb(null, res, body);
}, 0);
});
var addresses = ['2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM', '2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb'];
blockchain.getTransactions(addresses, 0, null, function(err, res) {
chai.expect(err).to.be.null;
res.items.length.should.be.equal(3);
done();
});
});
it('should get a list of unspent output', function(done) {
var blockchain = new Insight(FAKE_OPTS);
sinon.stub(blockchain, "requestPost", function(url, data, cb) {
url.should.be.equal('/api/addrs/utxo');
data.addrs.should.be.equal('2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM,2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb,2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x');
setTimeout(function() {
var res = {
statusCode: 200
};
var body = UNSPENT;
cb(null, res, body);
}, 0);
});
blockchain.getUnspent(ADDRESSES.slice(0, 3), function(err, unspent) {
chai.expect(err).to.be.null;
unspent.length.should.be.equal(2);
unspent[0].address.should.be.equal('2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb');
unspent[1].address.should.be.equal('2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x');
done();
});
});
describe('getActivity', function() {
it('should get activity for an innactive address', function(done) {
var blockchain = new Insight(FAKE_OPTS);
sinon.stub(blockchain, "getTransactions", function(addresses, from, to, cb) {
cb(null, []);
});
blockchain.getActivity(ADDRESSES, function(err, actives) {
chai.expect(err).to.be.null;
actives.length.should.equal(ADDRESSES.length);
actives.filter(function(i) {
return i
}).length.should.equal(0);
done();
});
});
it('should not get activity because of error', function(done) {
var blockchain = new Insight(FAKE_OPTS);
sinon.stub(blockchain, "getTransactions", function(addresses, from, to, cb) {
cb('error', []);
});
blockchain.getActivity(ADDRESSES, function(err, actives) {
chai.expect(err).to.be.not.null;
done();
});
});
it('should get activity for active addresses', function(done) {
var blockchain = new Insight(FAKE_OPTS);
sinon.stub(blockchain, "getTransactions", function(addresses, from, to, cb) {
cb(null, [{
vin: [{
addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'
}],
vout: []
}, {
vin: [{
addr: '2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM'
}],
vout: []
}, {
vin: [{
addr: '2N9D5bcCQ2bPWUDByQ6Qb5bMgMtgsk1rw3x'
}],
vout: []
}, {
vin: [],
vout: [{
scriptPubKey: {
addresses: ['2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY']
}
}]
}]);
});
blockchain.getActivity(ADDRESSES, function(err, actives) {
chai.expect(err).to.be.null;
actives.length.should.equal(ADDRESSES.length);
actives.filter(function(i) {
return i
}).length.should.equal(3);
done();
});
});
});
describe('Events', function() {
it('should emit event on a new block', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
var socket = blockchain.getSocket();
socket.emit('block', '12312312');
});
blockchain.on('block', function(blockid) {
blockid.should.be.equal('12312312');
done();
});
});
it('should emit event on a transaction for subscribed addresses', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.subscribe('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
blockchain.on('connect', function() {
var socket = blockchain.getSocket();
socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
});
blockchain.on('tx', function(ev) {
ev.address.should.be.equal('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY');
ev.txid.should.be.equal('1123');
done();
});
});
it('should\'t emit event on a transaction for non subscribed addresses', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
var socket = blockchain.getSocket();
socket.emit('2NFjCBFZSsxiwWAD7CKQ3hzWFtf9DcqTucY', '1123');
setTimeout(function() {
done();
}, 20);
});
blockchain.on('tx', function(ev) {
throw Error('should not call this event!');
});
});
it('should emit event on connection', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
done();
});
});
it('should emit event on disconnection', function(done) {
var blockchain = new Insight(FAKE_OPTS);
var socket = blockchain.getSocket();
blockchain.on('connect', function() {
var socket = blockchain.getSocket();
socket.emit('connect_error');
});
blockchain.on('disconnect', function() {
done();
});
});
});
describe('setCompleteUrl', function() {
it('should check setCompleteUrl', function() {
var url;
expect(Insight.setCompleteUrl(url)).to.be.undefined;
url = '';
Insight.setCompleteUrl(url).should.be.equal(url);
url = 'http://mydomain.com';
Insight.setCompleteUrl(url).should.be.equal('http://mydomain.com:80');
url = 'https://mydomain.com';
Insight.setCompleteUrl(url).should.be.equal('https://mydomain.com:443');
});
});
describe.skip('#request', function() {
it('should check request', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.request('hola', function() {
done();
});
});
});
describe.skip('#requestPost', function() {
it('should check requestPost', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.requestPost('hola', 'myData', function() {
done();
});
});
});
describe('#broadcast', function() {
it('should check broadcast', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.requestPost = sinon.stub().yields({
error: 'error',
status: 500
});
blockchain.broadcast('myTX', function(err) {
err.should.not.be.undefined;
done();
});
});
});
describe('#getTransactions', function() {
it('should check getTransactions', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.requestPost = sinon.stub().yields({
error: 'error',
status: 500
});
blockchain.getTransactions(['addr1'], 'to', 'from', function(err) {
err.should.not.be.undefined;
done();
});
});
it('should check getTransactions using to and from as numbers', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.requestPost = sinon.stub().yields({
error: 'error',
status: 500
});
blockchain.getTransactions(['addr1'], 1, 10, function(err) {
err.should.not.be.undefined;
done();
});
});
});
describe('#getUnspent', function() {
it('should check getUnspent with error response', function(done) {
var blockchain = new Insight(FAKE_OPTS);
blockchain.requestPost = sinon.stub().yields({
error: 'error',
status: 500
});
blockchain.getUnspent(['addr1'], function(err) {
err.should.not.be.undefined;
done();
});
});
});
});

View file

@ -0,0 +1,15 @@
'use strict';
describe('menuController', function(){
var scope, controller;
beforeEach(angular.mock.module('copayApp.controllers'));
beforeEach(angular.mock.inject(function($rootScope, $controller){
scope = $rootScope.$new();
controller = $controller('menuController', {$scope: scope});
}));
it('should have a menu variable', function(){
expect(controller.menu).not.toBeUndefined();
});
});

98
test/karma.conf.js Normal file
View file

@ -0,0 +1,98 @@
// Karma configuration
// Generated on Thu Mar 12 2015 18:13:33 GMT-0300 (ART)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '..',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['jasmine'],
// list of files / patterns to load in the browser
files: [
'bower_components/moment/moment.js',
'bower_components/angular/angular.js',
'bower_components/angular-ui-router/release/angular-ui-router.js',
'bower_components/angular-local-storage/dist/angular-local-storage.js',
'bower_components/angular-foundation/mm-foundation.js',
'bower_components/angular-foundation/mm-foundation-tpls.js',
'bower_components/angular-animate/angular-animate.js',
'bower_components/angular-moment/angular-moment.js',
'bower_components/ng-lodash/build/ng-lodash.js',
'bower_components/angular-qrcode/qrcode.js',
'bower_components/angular-gettext/dist/angular-gettext.js',
'bower_components/angular-touch/angular-touch.js',
'bower_components/buffer/buffer.js',
'node_modules/angular-mocks/angular-mocks.js',
'src/js/**/*.js',
'test/**/*.js'
],
// list of files to exclude
exclude: [
'src/js/translations.js',
'src/js/version.js',
'test/karma.conf.js',
'test/old/*'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'src/js/**/*.js': ['coverage']
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', , 'coverage'],
// optionally, configure the reporter
coverageReporter: {
dir: 'coverage/',
reporters: [{
type: 'html',
subdir: 'report-html'
}, {
type: 'lcov',
subdir: 'report-lcov'
}, {
type: 'text-summary'
}]
},
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};

View file

@ -1 +0,0 @@
var expect = chai.expect;

View file

@ -1 +0,0 @@
var should = chai.should();

View file

@ -1,5 +0,0 @@
if (typeof window != 'undefined') {
window.mocha.setup({
timeout: 8000
});
}

View file

@ -1 +0,0 @@
--require setup/node.js --reporter spec

View file

@ -1,69 +0,0 @@
'use strict';
var bitcore = require('bitcore');
function FakeBlockchain(opts) {
opts = opts || {};
}
FakeBlockchain.prototype.getTransaction = function(txid, cb) {
return cb(null, {txid: txid});
};
FakeBlockchain.prototype.getTransactions = function(addresses, from, to, cb) {
cb(null, { totalItems: 0, items: [] });
};
FakeBlockchain.prototype.subscribe = function() {
};
FakeBlockchain.prototype.on = function() {
};
FakeBlockchain.prototype.fixUnspent = function(u) {
this.u = u;
};
FakeBlockchain.prototype.destroy = function() {
};
FakeBlockchain.prototype.getUnspent = function(addresses, cb) {
return cb(null, this.u || [{
'address': 'mji7zocy8QzYywQakwWf99w9bCT6orY1C1',
'txid': '0be0fb4579911be829e3077202e1ab47fcc12cf3ab8f8487ccceae768e1f95fa',
'vout': 0,
'ts': 1402323949,
'scriptPubKey': '21032ca453c1d9a93b7de8cf3d44d7bb8d52a45dbdf8fff63f69de4e51b740bb1da3ac',
'amount': 25.0001,
'confirmations': 0,
'confirmationsFromCache': false
}]);
};
FakeBlockchain.prototype.getUnspent2 = function(addresses, cb) {
return cb(null, this.u || [{
'address': 'mji7zocy8QzYywQakwWf99w9bCT6orY1C1',
'txid': '0be0fb4579911be829e3077202e1ab47fcc12cf3ab8f8487ccceae768e1f95fa',
'vout': 0,
'ts': 1402323949,
'scriptPubKey': '21032ca453c1d9a93b7de8cf3d44d7bb8d52a45dbdf8fff63f69de4e51b740bb1da3ac',
'amount': 25.0001,
'confirmations': 1,
'confirmationsFromCache': false
}]);
};
FakeBlockchain.prototype.broadcast = function(rawtx, cb) {
var txid = '0be0fb4579911be829e3077202e1ab47fcc12cf3ab8f8487ccceae768e1f95fa';
return cb(null, txid);
};
FakeBlockchain.prototype.checkSentTx = function (tx, cb) {
var txid = '0be0fb4579911be829e3077202e1ab47fcc12cf3ab8f8487ccceae768e1f95fa';
return cb(null, txid);
};
FakeBlockchain.prototype.removeAllListeners = function() {
};
module.exports = FakeBlockchain;

View file

@ -1,45 +0,0 @@
'use strict';
var EventEmitter = require('events').EventEmitter;
var FakeSocket = function (url, opts) {
var self = this;
self.connected = false;
setTimeout(function() {
self.connected = true;
self.emit('connect');
}, 0);
}
var inherits = function(ctor, superCtor) {
ctor.super_ = superCtor;
ctor.prototype = Object.create(superCtor.prototype, {
constructor: {
value: ctor,
enumerable: false,
writable: true,
configurable: true
}
});
};
inherits(FakeSocket, EventEmitter);
FakeSocket.prototype.removeListener = function() {
return;
}
FakeSocket.prototype.destroy = function() {
this.connected = false;
this.removeAllListeners();
};
FakeSocket.prototype.disconnect = function() {
this.destroy();
};
module.exports = FakeSocket;

View file

@ -1,93 +0,0 @@
var EventEmitter = require('events').EventEmitter;
var util = require('util');
function Network(opts) {}
util.inherits(Network, EventEmitter);
Network.prototype.start = function(opts, cb) {
this.peer = {
options: {
token: "asd"
}
};
if (cb) cb();
};
Network.prototype.send = function(peerIds, data, cb) {};
Network.prototype.connectTo = function(peerId) {};
Network.prototype.disconnect = function(cb) {};
Network.prototype.lockIncommingConnections = function() {
};
Network.prototype.getPeer = function() {};
Network.prototype.connectToCopayers = function(cps) {};
Network.prototype.isOnline = function() {
return true;
};
Network.prototype.peerFromCopayer = function(copayerId) {
return copayerId;
};
//hex version of one's own nonce
Network.prototype.setHexNonce = function(networkNonce) {
if (networkNonce && networkNonce.length === 16)
this.networkNonce = new Buffer(networkNonce, 'hex');
else
this.iterateNonce();
};
//hex version of copayers' nonces
Network.prototype.setHexNonces = function(networkNonces) {
for (var i in networkNonces) {
if (!this.networkNonces)
this.networkNonces = {};
if (networkNonces[i].length === 16)
this.networkNonces[i] = new Buffer(networkNonces[i], 'hex');
}
};
//for oneself
Network.prototype.getHexNonce = function() {
return this.networkNonce.toString('hex');
};
//for copayers
Network.prototype.getHexNonces = function() {
var networkNoncesHex = [];
for (var i in this.networkNonces) {
networkNoncesHex = this.networkNonces[i].toString('hex');
}
return networkNoncesHex;
};
Network.prototype.iterateNonce = function() {
if (!this.networkNonce || this.networkNonce.length !== 8) {
this.networkNonce = new Buffer(8);
this.networkNonce.fill(0);
this.networkNonce[7] = 1;
return this.networkNonce;
}
//the first 4 bytes of a nonce is a unix timestamp in seconds
//the second 4 bytes is just an iterated "sub" nonce
//the whole thing is interpreted as one big endian number
var noncep1 = this.networkNonce.slice(0, 4);
noncep1.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
var noncep2uint = this.networkNonce.slice(4, 8).readUInt32BE(0);
var noncep2 = this.networkNonce.slice(4, 8);
noncep2.writeUInt32BE(noncep2uint + 1, 0);
return this.networkNonce;
};
Network.prototype.cleanUp = function() {
return;
};
module.exports = Network;

View file

@ -1,549 +0,0 @@
'use strict';
var Async = copay.Async;
var EventEmitter = require('events').EventEmitter;
describe('Network / Async', function() {
var createN = function(pk) {
var n = new Async({
url: 'http://insight.example.com:1234'
});
var fakeSocket = {};
fakeSocket.emit = function() {};
fakeSocket.on = function() {};
fakeSocket.disconnect = function() {};
fakeSocket.removeAllListeners = function() {};
n.createSocket = function() {
return fakeSocket;
};
var opts = {
copayerId: '03b51d01d798522cf61211b4dfcdd6db219ee33cf166e1cb7f43d836ab00ccddee',
privkey: pk || '31701118abde096d166607115ed00ce74a2231f68f43144406c863f5ebf06c32',
lastTimestamp: 1,
};
n.secretNumber = 'mySecret';
n.start(opts);
return n;
};
it('should create an instance', function() {
var n = createN();
should.exist(n);
});
describe('#cleanUp', function() {
it('should not set netKey', function() {
var n = createN();
(n.netKey === undefined).should.equal(true);
});
it('should set privkey to null', function() {
var n = createN();
n.cleanUp();
expect(n.privkey).to.equal(null);
});
it('should remove handlers', function() {
var n = createN();
var save = Async.prototype.removeAllListeners;
var spy = Async.prototype.removeAllListeners = sinon.spy();
n.cleanUp();
spy.calledOnce.should.equal(true);
Async.prototype.removeAllListeners = save;
});
});
describe('#send', function() {
it('should be able to broadcast', function() {
var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
var spy = sinon.spy();
var dummy = {
dummy: true
};
n.send(null, dummy, spy);
spy.calledOnce.should.be.true;
});
it('should call _sendToOne for a copayer', function(done) {
var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
var data = 'my data to send';
var copayerId = '03b51d01d798522cf61211b4dfcdd6d01020304cf166e1cb7f43d836abc5c18b23';
n._sendToOne = function(a, b, cb) {
cb();
};
var opts = {};
n.send(copayerId, data, done);
});
it('should call _sendToOne with encrypted data for a copayer', function(done) {
var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
var data = new bitcore.Buffer('my data to send');
var copayerId = '03b51d01d798522cf61001b4dfcdd6db219ee33cf166e1cb7f43d836abc5c18b23';
n._sendToOne = function(a1, enc, cb) {
var encPayload = JSON.parse(enc.toString());
encPayload.sig.length.should.be.greaterThan(0);
cb();
};
var opts = {};
n.send(copayerId, data, function() {
done();
});
});
it('should call _sendToOne for a list of copayers', function(done) {
var n = createN('9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08');
var data = new bitcore.Buffer('my data to send');
var copayerIds = ['03b51d01d798522cf61211b4dfcdd6db219ee33cf166e1cb7f43d836abc5c18b23'];
n._sendToOne = function(a1, a2, cb) {
cb();
};
var opts = {};
n.send(copayerIds, data, function() {
done();
});
});
});
describe('#_onMessage', function() {
var pk1 = 'fb23b9074ca5e7163719b86b41c7ce8348cf3d2839bb5f6125ef6efd5d40d7d3';
var cid1 = '0311a10109320efb3646c832d3e140c6d9c4f69b16e73fc3f0c23b3d014ec77828';
var pk2 = '89073fe4d3fdef2c5f2909bcda92e4470633f08640d1a62acc464327d611577e';
var cid2 = '03ceefb9dbcf7410411e5c1268d9d8e850ffd3a55da764a8377f3212571a52c01b';
var pk3 = 'a2ae2c7029c6a4136d7fe60c4d078a2e9d5af8a246bf2d5fee3410e273a5d430';
var cid3 = '034d3dd2054234737c1cff9d973c9c7e0fb5902c8e56c9d57a699b7842cedfe984';
it('should not reject data sent from a peer with hijacked pubkey', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret'
};
var enc = n1.encode(cid2, message);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(false);
});
it('should ignore messages', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret',
ts: 1
};
var enc = n1.encode(cid2, message);
enc.ts = 1;
n2.ignoreMessageFromTs = 1;
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(false);
});
it('should delete peer if copayer does not belong', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2.lockIncommingConnections([cid3]);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret',
};
var enc = n1.encode(cid2, message);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(true);
});
it('should emit data', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2.emit = sinon.spy();
var message = {
type: 'helloxxx',
copayerId: cid1,
secretNumber: 'mySecret',
};
var enc = n1.encode(cid2, message);
n2._onMessage(enc);
n2.emit.calledOnce.should.equal(true);
});
it('should reject data sent from a peer with hijacked pubkey', function() {
var n = createN(pk2);
var message = {
type: 'hello',
copayerId: cid3, // MITM
secretNumber: 'mySecret'
};
var enc = n.encode(cid2, message);
n._deletePeer = sinon.spy();
n._onMessage(enc);
n._deletePeer.calledOnce.should.equal(true);
n._deletePeer.getCall(0).args[1].should.equal('incorrect pubkey for peerId');
});
it('should not reject data sent from a peer with no previously set nonce but who is setting one now', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret'
};
var nonce = new Buffer('0000000000000001', 'hex');
var enc = n1.encode(cid2, message, nonce);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(false);
n2.getHexNonces()[cid1].toString('hex').should.equal('0000000000000001');
});
it('should not reject data sent from a peer with a really big new nonce', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret'
};
n2.networkNonces = {};
n2.networkNonces[cid1] = new Buffer('5000000000000001', 'hex');
var nonce = new Buffer('5000000000000002', 'hex')
var enc = n1.encode(cid2, message, nonce);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(false);
n2.getHexNonces()[cid1].toString('hex').should.equal('5000000000000002');
n2._deletePeer.calledOnce.should.equal(false);
});
it('should reject data sent from a peer with an outdated nonce', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret'
};
n2.networkNonces = {};
n2.networkNonces[cid1] = new Buffer('0000000000000002', 'hex');
var nonce = new Buffer('0000000000000001', 'hex');
var enc = n1.encode(cid2, message, nonce);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(true);
});
it('should accept join with a correct secret number', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'mySecret'
};
var enc = n1.encode(cid2, message);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(false);
});
it('should reject join with a incorrect secret number', function() {
var n1 = createN(pk1);
var n2 = createN(pk2);
n2._deletePeer = sinon.spy();
var message = {
type: 'hello',
copayerId: cid1,
secretNumber: 'otherSecret'
};
var enc = n1.encode(cid2, message);
n2._onMessage(enc);
n2._deletePeer.calledOnce.should.equal(true);
});
});
describe('#setHexNonce', function() {
it('should set a nonce from a hex value', function() {
var hex = '0000000000000000';
var n = createN();
n.setHexNonce(hex);
n.getHexNonce().should.equal(hex);
n.networkNonce.toString('hex').should.equal(hex);
});
it('should return an error', function() {
var hex = '0000';
var n = createN();
(function() {
n.setHexNonce(hex);
}).should.throw('incorrect length');
});
it('should iterateNonce', function() {
var n = createN();
n.iterateNonce = sinon.spy();
n.setHexNonce();
n.iterateNonce.callCount.should.be.equal(1);
n.setHexNonce(null);
n.iterateNonce.callCount.should.be.equal(2);
n.setHexNonce(undefined);
n.iterateNonce.callCount.should.be.equal(3);
});
});
describe('#setHexNonces', function() {
it('should set a nonce from a hex value', function() {
var hex = '0000000000000000';
var n = createN();
n.setHexNonces({
fakeid: hex
});
n.getHexNonces().fakeid.should.equal(hex);
});
});
describe('#getHexNonce', function() {
it('should get a nonce hex value', function() {
var hex = '0000000000000000';
var n = createN();
n.setHexNonce(hex);
n.getHexNonce().should.equal(hex);
});
});
describe('#getHexNonces', function() {
it('should get a nonce from a hex value', function() {
var hex = '0000000000000000';
var n = createN();
n.setHexNonces({
fakeid: hex
});
n.getHexNonces().fakeid.should.equal(hex);
});
});
describe('#iterateNonce', function() {
it('should set a nonce not already set', function() {
var n = createN();
n.iterateNonce();
n.networkNonce.slice(4, 8).toString('hex').should.equal('00000001');
n.networkNonce.slice(0, 4).toString('hex').should.not.equal('00000000');
});
it('called twice should increment', function() {
var n = createN();
n.iterateNonce();
n.networkNonce.slice(4, 8).toString('hex').should.equal('00000001');
n.iterateNonce();
n.networkNonce.slice(4, 8).toString('hex').should.equal('00000002');
});
it('should set the first byte to the most significant "now" digit', function() {
var n = createN();
n.iterateNonce();
var buf = new Buffer(4);
buf.writeUInt32BE(Math.floor(Date.now() / 1000), 0);
n.networkNonce[0].should.equal(buf[0]);
});
});
describe('#_arrayRemove', function() {
it('should remove an element from an array', function() {
var array = ['1', '2', '3', '4'];
array = Async._arrayRemove('2', array);
array.length.should.be.equal(3);
array.indexOf('2').should.be.equal(-1);
array = Async._arrayRemove('5', array);
array.length.should.be.equal(3);
});
});
describe('#getOnlinePeerIDs', function() {
it('should get peer ids that are online', function() {
var n = createN();
n.getOnlinePeerIDs().length.should.be.equal(0);
n._addCopayer('ab0001');
n.getOnlinePeerIDs().length.should.be.equal(1);
n._addCopayer('ab0001');
n.getOnlinePeerIDs().length.should.be.equal(1);
n._addCopayer('ab0002');
n.getOnlinePeerIDs().length.should.be.equal(2);
});
});
describe('#connectedCopayers', function() {
it('should get peer ids that are online', function() {
var n = createN();
n.connectedCopayers().length.should.be.equal(0);
n._addCopayer('ab0001');
n.connectedCopayers().length.should.be.equal(1);
n._addCopayer('ab0001');
n.connectedCopayers().length.should.be.equal(1);
n._addCopayer('ab0002');
n.connectedCopayers().length.should.be.equal(2);
});
});
describe('#_deletePeer', function() {
it('should delete a Peer', function() {
var n = createN();
n._addCopayer('ab0001');
n.connectedPeers.length.should.be.equal(1);
var peerId = n.connectedPeers[0];
n._deletePeer(peerId);
n.connectedPeers.length.should.be.equal(0);
});
});
describe('#getCopayerIds', function() {
it('should return the copayer ids', function() {
var n = createN();
n.getCopayerIds().length.should.be.equal(1);
});
it('should return the allowed copayer ids', function() {
var n = createN();
var lockIds = ['abc001', 'abc002'];
n.lockIncommingConnections(lockIds);
n.getCopayerIds().length.should.be.equal(2);
});
});
describe('#isOnline', function() {
it('should return if is online', function() {
var n = createN();
n.isOnline().should.be.true;
n.cleanUp();
n.isOnline().should.be.false;
});
});
describe('#greet', function() {
it('should greet ', function() {
var n = createN();
n.greet('03b51d01d798522cf61211b4dfcdd6db219ee33cf166e1cb7f43d836ab00ccddee', 'mySecret');
});
});
describe('#setCopayers', function() {
it('should setCopayers ', function() {
var n = createN();
n.connectedPeers.length.should.be.equal(0);
var cids = ['abc001', 'abc002'];
n.setCopayers(cids);
n.connectedPeers.length.should.be.equal(2);
});
});
describe('#lockIncommingConnections', function() {
it('should lock Incomming Connections ', function() {
var n = createN();
var cids = ['abc001', 'abc002', 'abc003'];
n.setCopayers(cids);
var lockIds = ['abc001', 'abc002'];
n.lockIncommingConnections(lockIds);
Object.keys(n.allowedCopayerIds).length.should.be.equal(2);
});
});
describe('#getKey', function() {
it('should return the key or generate a new one ', function() {
var n = createN();
n.key = null;
var k1 = n.getKey();
k1.should.not.be.undefined;
var k2 = n.getKey();
k2.should.not.be.undefined;
k1.should.be.equal(k2);
});
});
describe('#_onError', function() {
it('should set criticalError ', function() {
var n = createN();
expect(n.criticalError).to.be.undefined;
var myError = {
message: 'Some error'
};
n._onError(myError);
n.criticalError.should.be.equal('Some error');
});
});
describe('#_setInboundPeerAuth', function() {
it('should set isInboundPeerAuth ', function() {
var n = createN();
expect(n.isInboundPeerAuth['1']).to.be.undefined;
n._setInboundPeerAuth('1');
n.isInboundPeerAuth['1'].should.be.true;
});
});
describe('#start', function() {
it('should not start ', function(done) {
var n = createN();
var opts = {
privkey: 'fb23b9074ca5e7163719b86b41c7ce8348cf3d2839bb5f6125ef6efd5d40d7d3',
copayerId: '0311a10109320efb3646c832d3e140c6d9c4f69b16e73fc3f0c23b3d014ec77828'
};
n.started = 1;
n.start(opts, function() {
done();
});
});
});
});

View file

@ -1,243 +0,0 @@
var InsightStorage = require('../js/plugins/InsightStorage');
var assert = require('assert');
var querystring = require('querystring');
describe('insight storage plugin', function() {
var requestMock = sinon.stub();
var storage = new InsightStorage({
request: requestMock
});
var email = 'john@doe.com';
var password = '1234';
var data = '{"random": true}';
var headers = 'X-test: 12\r\nX-testb: 32';
var namespace = 'profile::0000000000000000000000000000000000000000';
var oldSecret = 'rFA+F/N+ZvKXp717zBdfCKYQ5v9Fjry0W6tautj5etIH' + 'KLQliZBEYXA7AXjTJ9K3DglzGWJKost3QJUCMbhM/A=='
var newSecret = '96KnVsaQFv8vsbxAFeYyGM4nO/8B6YaVNKz9IxDmwzk=';
var setupStorageCredentials = function() {
storage.setCredentials(email, password);
};
beforeEach(function() {
requestMock.reset();
requestMock.get = sinon.stub();
requestMock.post = sinon.stub();
setupStorageCredentials();
});
var setupForCreation = function() {
requestMock.get.onFirstCall().callsArgWith(1, 'Not found');
requestMock.post.onFirstCall().callsArgWith(1, null, {
statusCode: 200
});
};
it('should be able to create a namespace for storage', function(done) {
setupForCreation();
storage.createItem(namespace, data, function(err) {
assert(!err);
return done();
});
});
var setupForRetrieval = function(code) {
requestMock.get.onFirstCall().callsArgWith(1, null, {
statusCode: code || 200,
getAllResponseHeaders: sinon.stub().returns(headers),
}, data);
};
describe('#getItem', function() {
it('should be able to retrieve data in a namespace', function(done) {
setupForRetrieval();
storage.getItem(namespace, function(err, retrieved) {
assert(!err);
assert(retrieved === data);
var url = requestMock.get.getCall(0).args[0].url;
url.should.contain('?key=' + querystring.encode(namespace));
return done();
});
});
it('should be able to retrieve headers', function(done) {
setupForRetrieval();
storage.getItem(namespace, function(err, retrieved, headers) {
assert(!err);
headers['X-test'].should.equal('12');
headers['X-testb'].should.equal('32');
return done();
});
});
it('should be able handle 403', function(done) {
setupForRetrieval(403);
// old profile query
requestMock.get.onSecondCall().callsArgWith(1, null, {
statusCode: 403,
getAllResponseHeaders: sinon.stub().returns(headers),
}, data);
storage.getItem(namespace, function(err) {
err.should.contain('PNOTFOUND');
return done();
});
});
it('should be able handle other error', function(done) {
setupForRetrieval(510);
storage.getItem(namespace, function(err) {
err.should.contain('Unable');
return done();
});
});
});
describe('#removeItem', function() {
it('should be able to delete Items', function(done) {
setupForRetrieval();
storage.removeItem(namespace, function(err) {
should.not.exist(err);
var url = requestMock.get.getCall(0).args[0].url;
url.should.contain('?key=' + querystring.encode(namespace));
return done();
});
});
it('should be able handle 406', function(done) {
setupForRetrieval(409);
storage.removeItem(namespace, function(err) {
err.should.contain('BADCREDENTIALS');
return done();
});
});
it('should be able handle other error', function(done) {
setupForRetrieval(510);
storage.removeItem(namespace, function(err) {
err.should.contain('Unable');
return done();
});
});
});
var setupForSave = function(code) {
requestMock.post.onFirstCall().callsArgWith(1, null, {
statusCode: code || 200
});
};
it('should be able to overwrite data when using same password', function(done) {
setupForSave();
storage.setItem(namespace, data, function(err) {
assert(!err);
assert(requestMock.post.firstCall.args[0].url.indexOf('save') !== -1);
return done();
});
});
it('should handle 406 (quota)', function(done) {
setupForSave(406);
storage.setItem(namespace, data, function(err) {
err.should.contain('OVERQUOTA');
return done();
});
});
it('should handle other error ', function(done) {
setupForSave(505);
storage.setItem(namespace, data, function(err) {
err.should.contain('Unable');
return done();
});
});
it('should handle 409 (unauthorized)', function(done) {
setupForSave(409);
storage.setItem(namespace, data, function(err) {
err.should.contain('BADCREDENTIALS');
return done();
});
});
it('won\'t make an unnecessary request if old password can\'t work', function(done) {
storage.setCredentials(email, '!');
setupForRetrieval();
storage.getItem(namespace, function(err, retrieved) {
assert(requestMock.get.firstCall);
assert(!requestMock.get.secondCall);
return done();
});
});
it('shouldn\'t be able to create a namespace twice', function(done) {
setupForRetrieval();
storage.createItem(namespace, data, function(err) {
assert(err);
assert(requestMock.get.firstCall.args[0].url.indexOf('retrieve') !== -1);
assert(!requestMock.post.firstCall);
return done();
});
});
var setupForOldData = function() {
requestMock.get = sinon.stub();
requestMock.get.onFirstCall().callsArgWith(1, null, {
statusCode: 403
});
requestMock.get.onSecondCall().callsArgWith(1, null, {
statusCode: 200,
getAllResponseHeaders: sinon.stub(),
}, data);
requestMock.post = sinon.stub();
requestMock.post.onFirstCall().callsArgWith(1, null, {
statusCode: 200,
getAllResponseHeaders: sinon.stub(),
});
}
it('should be able to restore 0.7.2 data', function(done) {
setupForOldData();
storage.getItem(namespace, function(error, dataReturned) {
assert(!error);
done();
});
});
it('should change the remote passphrase if retrieved with 0.7.2 passphrase',
function(done) {
setupForOldData();
storage.getItem(namespace, function(error, dataReturned) {
var receivedArgs = requestMock.post.firstCall.args[0].body;
var url = requestMock.post.firstCall.args[0].url;
var args = querystring.decode(receivedArgs);
url.indexOf('change_passphrase').should.not.be.equal(-1);
requestMock.post.firstCall.args[0].headers.Authorization.should.be.equal(new Buffer(email + ':' + oldSecret).toString('base64'));
args.newPassphrase.should.be.equal(newSecret);
done();
});
}
);
});

View file

@ -1,73 +0,0 @@
var LocalStorage = require('../js/plugins/LocalStorage');
var assert = require('assert');
describe('local storage plugin', function() {
var storage, storageMock, VALUE=123;
beforeEach(function() {
storageMock = {};
storageMock.getItem = sinon.stub().returns(VALUE);
storageMock.createItem = sinon.stub().returns();
storageMock.setItem = sinon.stub().returns();
storageMock.removeItem = sinon.stub().returns();
storageMock.clear = sinon.stub().returns();
storage = new LocalStorage({
ls: storageMock
});
});
it('#getItem', function(done) {
storage.getItem('hola', function(err, value) {
assert(!err);
storageMock.getItem.getCall(0).args[0].should.equal('hola');
value.should.equal(VALUE);
return done();
});
});
it('#createItem', function(done) {
storageMock.getItem = sinon.stub().returns(null);
storage.createItem('hola', 'value', function(err) {
assert(!err);
return done();
});
});
it('#createItem (Exists)', function(done) {
storage.createItem('hola', 'value', function(err) {
err.should.contain('EEXISTS');
return done();
});
});
it('#removeItem', function(done) {
storage.removeItem('pepe', function(err) {
assert(!err);
storageMock.removeItem.getCall(0).args[0].should.equal('pepe');
return done();
});
});
it('#setItem', function(done) {
storage.setItem('hola', 'chau', function(err) {
assert(!err);
storageMock.setItem.getCall(0).args[0].should.equal('hola');
storageMock.setItem.getCall(0).args[1].should.equal('chau');
return done();
});
});
it('#clear', function(done) {
storage.clear(function(err) {
assert(!err);
storageMock.clear.calledOnce.should.be.false;
return done();
});
});
});

View file

@ -1,5 +0,0 @@
#! /bin/bash
node_modules/.bin/istanbul cover grunt -- mochaTest && \
cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && \
./node_modules/karma/bin/karma start --browsers Firefox --single-run

View file

@ -1,89 +0,0 @@
'use strict';
var cryptoUtils = copay.crypto;
var assert = require('assert');
describe('crypto utils', function() {
it('should decrypt what it encrypts', function() {
var key = 'My secret key';
var message = 'My secret message';
var encrypted = cryptoUtils.encrypt(key, message);
var decrypted = cryptoUtils.decrypt(key, encrypted);
decrypted.should.equal(message);
});
it('should decrypt what it encrypts (JSON)', function() {
var key = 'My secret key';
var message = {'hola': 'picho'};
var encrypted = cryptoUtils.encrypt(key, message);
var decrypted = cryptoUtils.decrypt(key, encrypted);
JSON.parse(decrypted).should.deep.equal(message);
});
it('should return null if the provided key cant decrypt', function() {
var key = 'My secret key';
var message = 'My secret message';
var encrypted = cryptoUtils.encrypt(key, message);
var decrypted = cryptoUtils.decrypt('Invalid key', encrypted);
assert(decrypted === null);
});
it('should sign a message', function() {
var key = 'My secret key';
var message = 'My secret message';
var signature = cryptoUtils.hmac(key, message);
signature.should.be.equal('6tpegxYl/Eig9k1Lla8b8G8OcdtOxyNbDsdyic1Yzh4=');
});
var tests = [
{
salt: 'mjuBtGybi/4=',
iterations: 10,
word: '123456',
phrase: 'UUNLzkU5b2aT2/bIoyYwL3teyiFuRYEJtGCGQ0y0aEDciEtNCX0Wb73j4gmoCWl++epj6StBQg4SorTROZ2wFA==',
},{
salt: 'mjuBtGybi/4=',
iterations: 5,
word: '123456',
phrase: '+3uClcHrIU52WGHPHBwbIDFirhbiIORYTDPs9xFLiXAkR2dEVN9gNoGtqhBPdi9U47tPkPoRqZtqXDaeetXflQ==',
},{
salt: 'asklhehuhug24',
iterations: 5,
word: '123456',
phrase: 'lI82NmwibnUCHSQVQunv3aL0XCimZyFj/TZlHNIXV5Rzbf6TEj5L/335N/t7k2zUVub6XmMaWvufqmvSqA4znA==',
}
];
var test=0;
_.each(tests,function(t){
it('should generate a passphrase. Test case:' + test++,function(){
var phrase = cryptoUtils.kdf(t.word, t.salt, t.iterations);
phrase.should.equal(t.phrase);
});
it('should generate a passphrase from weird chars', function() {
var phrase = cryptoUtils.kdf('Pwd123!@#$%^&*(){}[]\|/?.>,<=+-_`~åéþ䲤þçæ¶', tests[0].salt, 100);
var expected = 'CZwb5KdikvZHVsEoZUdJckAy+yyzGnd++XhyqxJXbc30'
+ 'pEoO+WqHgqBbdf0gn2wiyWZv3zymB+7L75Xnz3uSlg==';
phrase.should.equal(expected);
});
});
it('should generate a passphrase using default salt/iter', function() {
var phrase = cryptoUtils.kdf('Pwd123!@#$%^&*(){}[]\|/?.>,<=+-_`~åéþ䲤þçæ¶');
var expected = 'n9QYDBJvRCHfpAfp8X/Z1XDA00CnZtnehLKOVrtNYTLt9H+hlcyaZgbAGGgJ/dVRCsVtIBzYwaACNPckknMiCg==';
phrase.should.equal(expected);
});
});

View file

@ -1,165 +0,0 @@
'use strict';
var _ = require('lodash');
var chai = chai || require('chai');
var sinon = sinon || require('sinon');
var should = chai.should();
var csv = require('../js/util/csv')
var moment = moment || require('moment');
describe('csv utils', function() {
it('should convert simple json', function(done) {
var data = [
{ name: 'Lennon John', age: 40 },
{ name: 'Cobain, Kurt', age: 27 },
];
var descriptor = {
columns: [
{ label: 'Name', property: 'name', type: 'string' },
{ label: 'Age', property: 'age', type: 'number' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('Name,Age\r\nLennon John,40\r\n"Cobain, Kurt",27\r\n');
done();
});
});
it('should handle empty data', function(done) {
var data = [];
var descriptor = {
columns: [
{ label: 'Name', property: 'name', type: 'string' },
{ label: 'Age', property: 'age', type: 'number' },
{ property: 'lastLogin', type: 'date' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('Name,Age,lastLogin\r\n');
done();
});
});
it('should handle null row in data', function(done) {
var data = [
{ name: 'John', age: 40 },
null,
{ name: 'Kurt', age: 27 },
];
var descriptor = {
columns: [
{ label: 'Name', property: 'name', type: 'string' },
{ label: 'Age', property: 'age', type: 'number' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('Name,Age\r\nJohn,40\r\n,\r\nKurt,27\r\n');
done();
});
});
it('should format dates', function(done) {
var data = [
{ name: 'John', age: 40, lastLogin: moment(1417608870000), },
{ name: 'Kurt', age: 27, lastLogin: moment('2014-11-01'), },
];
var descriptor = {
columns: [
{ property: 'name', type: 'string' },
{ property: 'age', type: 'number' },
{ property: 'lastLogin', type: 'date', format: 'YYYY MM DD' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('name,age,lastLogin\r\nJohn,40,2014 12 03\r\nKurt,27,2014 11 01\r\n');
done();
});
});
it('should compute values from function properties', function(done) {
var data = [
{ name: 'John', payments: 400, withdrawals: 300, },
{ name: 'Kurt', payments: 270.5, withdrawals: 200, },
];
var descriptor = {
columns: [
{ property: 'name', type: 'string' },
{ label: 'Balance', property: function (obj) { return obj.payments - obj.withdrawals; }, type: 'number' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('name,Balance\r\nJohn,100\r\nKurt,70.5\r\n');
done();
});
});
it('should not fail on error from calculated values', function(done) {
var data = [
{ name: 'John', error: 0 },
{ name: 'Kurt', error: 1 },
];
var descriptor = {
columns: [{
property: 'name',
type: 'string'
}, {
label: 'Error',
property: function(obj) {
if (obj.error) {
throw 'dummy error';
} else {
return 'ok';
}
}
}, ],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('name,Error\r\nJohn,ok\r\nKurt,dummy error\r\n');
done();
});
});
it('should use blank label if label not specified for computed property', function(done) {
var data = [
{ name: 'John', payments: 400, withdrawals: 300, },
{ name: 'Kurt', payments: 270.5, withdrawals: 200, },
];
var descriptor = {
columns: [
{ property: 'name', type: 'string' },
{ property: function (obj) { return obj.payments - obj.withdrawals; }, type: 'number' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('name,\r\nJohn,100\r\nKurt,70.5\r\n');
done();
});
});
it('should handle non existent properties', function(done) {
var data = [
{ name: 'John', age: 40 },
{ name: 'Kurt', age: 27 },
];
var descriptor = {
columns: [
{ property: 'name', type: 'string' },
{ property: 'age', type: 'number' },
{ property: 'lastLogin', type: 'date', format: 'YYYY MM DD' },
],
};
csv.toCsv(data, descriptor, function (err, res) {
res.should.equal('name,age,lastLogin\r\nJohn,40,\r\nKurt,27,\r\n');
done();
});
});
});

View file

@ -1,89 +0,0 @@
'use strict';
var _ = require('lodash');
var chai = chai || require('chai');
var sinon = sinon || require('sinon');
var should = chai.should();
var httpUtils = require('../js/util/HTTP');
describe('http utils', function() {
var xhr;
beforeEach(function() {
xhr = {
open: sinon.stub(),
getAllResponseHeaders: sinon.stub().returns('Content-type: xx'),
setRequestHeader: sinon.stub().returns(),
send: function() {
var self = this;
setTimeout(function() {
self.response = [1,2,3,4];
self.error ? self.onerror() : self.onload();
}, 10);
},
};
});
it('should get success', function(done) {
xhr.error = 0;
httpUtils.request({
xhr: xhr,
method: 'GET',
url: 'http://test'
}).success(function(data, status) {
done();
}).error(function(data, status) {
throw new Error();
});
});
it('should get error', function(done) {
xhr.error = 1;
httpUtils.request({
xhr: xhr,
method: 'GET',
url: 'http://test',
}).success(function(data, status) {
throw new Error();
}).error(function(data, status) {
done();
});
});
it('should get with headers', function(done) {
xhr.error = 0;
httpUtils.request({
xhr: xhr,
method: 'GET',
url: 'http://test',
headers: {
'Content-Transfer-Encoding': 'a',
'Content-Length': 1,
'X-test': 2,
}
}).success(function(data, status) {
done();
});
});
it('should get with body', function(done) {
xhr.error = 0;
httpUtils.request({
xhr: xhr,
method: 'GET',
url: 'http://test',
body: 'hola',
responseType: 'type',
}).success(function(data, status) {
done();
});
});
it('should get with default error', function(done) {
xhr.error = 1;
var ret = httpUtils.request({
xhr: xhr,
method: 'GET',
url: 'http://test',
}).error(function(){
done();
})
});
});

View file

@ -1,60 +0,0 @@
'use strict';
var _ = require('lodash');
var chai = chai || require('chai');
var sinon = sinon || require('sinon');
var should = chai.should();
var log = require('../js/util/log');
describe('log utils', function() {
afterEach(function() {
log.setLevel('info');
});
it('should log .warn', function() {
if (console.warn.restore)
console.warn.restore();
sinon.stub(console,'warn');
log.setLevel('debug');
log.warn('hola');
var arg = console.warn.getCall(0).args[0];
//arg.should.contain('util.log.js'); /* Firefox does not include the stack track */
arg.should.contain('hola');
console.warn.restore();
});
it('should log .fatal', function() {
if (console.log.restore)
console.log.restore();
sinon.stub(console,'log');
log.setLevel('debug');
log.fatal('hola',"que",'tal');
var arg = console.log.getCall(0).args[0];
//arg.should.contain('util.log.js'); /* Firefox does not include the stack track */
arg.should.contain('que');
console.log.restore();
});
it('should not log debug', function() {
sinon.stub(console,'log');
log.setLevel('info');
log.debug('hola');
console.log.called.should.equal(false);
console.log.restore();
});
it('should log debug', function() {
log.getLevels().debug.should.equal(0);
log.getLevels().fatal.should.equal(5);
});
});