bwc
This commit is contained in:
parent
04fb7ba032
commit
320de62f13
348 changed files with 7745 additions and 30874 deletions
|
|
@ -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==';
|
||||
143
test/HDParams.js
143
test/HDParams.js
|
|
@ -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);
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
1617
test/Identity.js
1617
test/Identity.js
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -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"}}';
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -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(), {});
|
||||
});
|
||||
});
|
||||
});
|
||||
3242
test/Wallet.js
3242
test/Wallet.js
File diff suppressed because one or more lines are too long
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
15
test/controllers/menu.test.js
Normal file
15
test/controllers/menu.test.js
Normal 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
98
test/karma.conf.js
Normal 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
|
||||
});
|
||||
};
|
||||
|
|
@ -1 +0,0 @@
|
|||
var expect = chai.expect;
|
||||
|
|
@ -1 +0,0 @@
|
|||
var should = chai.should();
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
if (typeof window != 'undefined') {
|
||||
window.mocha.setup({
|
||||
timeout: 8000
|
||||
});
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
--require setup/node.js --reporter spec
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
});
|
||||
|
|
@ -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
|
||||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
165
test/util.csv.js
165
test/util.csv.js
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -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();
|
||||
})
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue