Fix tests: Allow single files to be run
This commit is contained in:
parent
a3377a5579
commit
41198ff031
21 changed files with 21 additions and 32 deletions
|
|
@ -1,99 +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('show 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 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);
|
||||
})
|
||||
|
||||
});
|
||||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Passphrase = copay.Passphrase;
|
||||
|
||||
describe('Passphrase model', function() {
|
||||
|
||||
it('should create an instance', function() {
|
||||
var p = new Passphrase();
|
||||
should.exist(p);
|
||||
});
|
||||
|
||||
it('should generate key from password', function (done) {
|
||||
var p = new Passphrase({
|
||||
salt: 'mjuBtGybi/4=',
|
||||
iterations: 10,
|
||||
});
|
||||
var pass = '123456';
|
||||
var k = p.get(pass);
|
||||
var k64 = p.getBase64(pass);
|
||||
|
||||
// Note: hashes were generated using CryptoJS
|
||||
k.toString().should.equal('2283fe11b9a189b82f1c09200806920cbdd8ef752f53dea910f90ab526f441acdbd5128555647a7e390a1a9fea042226963ccd0f7851030b3d6e282ccebaa17e');
|
||||
k64.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
||||
|
||||
p.getBase64Async(pass, function (ret) {
|
||||
ret.toString().should.equal('IoP+EbmhibgvHAkgCAaSDL3Y73UvU96pEPkKtSb0Qazb1RKFVWR6fjkKGp/qBCImljzND3hRAws9bigszrqhfg==');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,896 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var Wallet = copay.Wallet;
|
||||
var PrivateKey = copay.PrivateKey;
|
||||
var Network = requireMock('FakeNetwork');
|
||||
var Blockchain = requireMock('FakeBlockchain');
|
||||
var TransactionBuilder = bitcore.TransactionBuilder;
|
||||
var Transaction = bitcore.Transaction;
|
||||
var Address = bitcore.Address;
|
||||
var PayPro = bitcore.PayPro;
|
||||
var bignum = bitcore.Bignum;
|
||||
var startServer = copay.FakePayProServer; // TODO should be require('./mocks/FakePayProServer');
|
||||
var localMock = requireMock('FakeLocalStorage');
|
||||
var sessionMock = requireMock('FakeLocalStorage');
|
||||
var Storage = copay.Storage;
|
||||
|
||||
var server;
|
||||
|
||||
var walletConfig = {
|
||||
requiredCopayers: 1,
|
||||
totalCopayers: 1,
|
||||
spendUnconfirmed: true,
|
||||
reconnectDelay: 100,
|
||||
networkName: 'testnet',
|
||||
storage: requireMock('FakeLocalStorage').storageParams,
|
||||
};
|
||||
|
||||
var getNewEpk = function() {
|
||||
return new PrivateKey({
|
||||
networkName: walletConfig.networkName,
|
||||
})
|
||||
.deriveBIP45Branch()
|
||||
.extendedPublicKeyString();
|
||||
};
|
||||
|
||||
describe('PayPro (in Wallet) model', function() {
|
||||
|
||||
if (!is_browser) {
|
||||
var createW = function(N, conf) {
|
||||
var c = JSON.parse(JSON.stringify(conf || walletConfig));
|
||||
if (!N) N = c.totalCopayers;
|
||||
|
||||
var mainPrivateKey = new copay.PrivateKey({
|
||||
networkName: walletConfig.networkName
|
||||
});
|
||||
var mainCopayerEPK = mainPrivateKey.deriveBIP45Branch().extendedPublicKeyString();
|
||||
c.privateKey = mainPrivateKey;
|
||||
|
||||
c.publicKeyRing = new copay.PublicKeyRing({
|
||||
networkName: c.networkName,
|
||||
requiredCopayers: Math.min(N, c.requiredCopayers),
|
||||
totalCopayers: N,
|
||||
});
|
||||
c.publicKeyRing.addCopayer(mainCopayerEPK);
|
||||
|
||||
c.txProposals = new copay.TxProposals({
|
||||
networkName: c.networkName,
|
||||
});
|
||||
|
||||
var storage = new Storage(walletConfig.storage);
|
||||
storage.setPassphrase('xxx');
|
||||
var network = new Network(walletConfig.network);
|
||||
var blockchain = new Blockchain(walletConfig.blockchain);
|
||||
c.storage = storage;
|
||||
c.network = network;
|
||||
c.blockchain = blockchain;
|
||||
|
||||
c.addressBook = {
|
||||
'2NFR2kzH9NUdp8vsXTB4wWQtTtzhpKxsyoJ': {
|
||||
label: 'John',
|
||||
copayerId: '026a55261b7c898fff760ebe14fd22a71892295f3b49e0ca66727bc0a0d7f94d03',
|
||||
createdTs: 1403102115,
|
||||
hidden: false
|
||||
},
|
||||
'2MtP8WyiwG7ZdVWM96CVsk2M1N8zyfiVQsY': {
|
||||
label: 'Jennifer',
|
||||
copayerId: '032991f836543a492bd6d0bb112552bfc7c5f3b7d5388fcbcbf2fbb893b44770d7',
|
||||
createdTs: 1403103115,
|
||||
hidden: false
|
||||
}
|
||||
};
|
||||
|
||||
c.networkName = walletConfig.networkName;
|
||||
c.version = '0.0.1';
|
||||
|
||||
return new Wallet(c);
|
||||
}
|
||||
|
||||
var unspentTest = [{
|
||||
"address": "dummy",
|
||||
"scriptPubKey": "dummy",
|
||||
"txid": "2ac165fa7a3a2b535d106a0041c7568d03b531e58aeccdd3199d7289ab12cfc1",
|
||||
"vout": 1,
|
||||
"amount": 10,
|
||||
"confirmations": 7
|
||||
}];
|
||||
|
||||
var createW2 = function(privateKeys, N, conf) {
|
||||
if (!N) N = 3;
|
||||
var w = createW(N, conf);
|
||||
should.exist(w);
|
||||
|
||||
var pkr = w.publicKeyRing;
|
||||
|
||||
for (var i = 0; i < N - 1; i++) {
|
||||
if (privateKeys) {
|
||||
var k = privateKeys[i];
|
||||
pkr.addCopayer(k ? k.deriveBIP45Branch().extendedPublicKeyString() : getNewEpk());
|
||||
} else {
|
||||
pkr.addCopayer(getNewEpk());
|
||||
}
|
||||
}
|
||||
|
||||
return w;
|
||||
};
|
||||
|
||||
var cachedW2 = null;
|
||||
var cachedW2obj = null;
|
||||
var cachedCreateW2 = function() {
|
||||
if (!cachedW2) {
|
||||
cachedW2 = createW2();
|
||||
cachedW2obj = cachedW2.toObj();
|
||||
cachedW2obj.opts.reconnectDelay = 100;
|
||||
}
|
||||
var w = Wallet.fromObj(cachedW2obj, cachedW2.storage, cachedW2.network, cachedW2.blockchain);
|
||||
return w;
|
||||
};
|
||||
|
||||
var createWallet = function() {
|
||||
var w = cachedCreateW2();
|
||||
unspentTest[0].address = w.publicKeyRing.getAddress(1, true, w.publicKey).toString();
|
||||
unspentTest[0].scriptPubKey = w.publicKeyRing.getScriptPubKeyHex(1, true, w.publicKey);
|
||||
w.getUnspent = function(cb) {
|
||||
return setTimeout(function() {
|
||||
return cb(null, unspentTest, unspentTest);
|
||||
}, 1);
|
||||
};
|
||||
return w;
|
||||
};
|
||||
|
||||
it('#start the example server', function(done) {
|
||||
startServer(function(err, s) {
|
||||
if (err) return done(err);
|
||||
server = s;
|
||||
server.uri = 'https://localhost:8080/-';
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
var pr;
|
||||
var ppw;
|
||||
|
||||
ppw = createWallet();
|
||||
|
||||
it('#retrieve a payment request message via http', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
|
||||
var req = {
|
||||
headers: {
|
||||
'Host': 'localhost:8080',
|
||||
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE + ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': '0'
|
||||
},
|
||||
socket: {
|
||||
remoteAddress: 'localhost',
|
||||
remotePort: 8080
|
||||
},
|
||||
body: {}
|
||||
};
|
||||
|
||||
Object.keys(req.headers).forEach(function(key) {
|
||||
req.headers[key.toLowerCase()] = req.headers[key];
|
||||
});
|
||||
|
||||
server.GET['/-/request'](req, function(err, res, body) {
|
||||
var data = PayPro.PaymentRequest.decode(body);
|
||||
pr = new PayPro();
|
||||
pr = pr.makePaymentRequest(data);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#send a payment message via http', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
|
||||
var ver = pr.get('payment_details_version');
|
||||
var pki_type = pr.get('pki_type');
|
||||
var pki_data = pr.get('pki_data');
|
||||
var details = pr.get('serialized_payment_details');
|
||||
var sig = pr.get('signature');
|
||||
|
||||
var certs = PayPro.X509Certificates.decode(pki_data);
|
||||
certs = certs.certificate;
|
||||
|
||||
var verified = pr.verify();
|
||||
|
||||
if (!verified) {
|
||||
return done(new Error('Server sent a bad signature.'));
|
||||
}
|
||||
|
||||
details = PayPro.PaymentDetails.decode(details);
|
||||
var pd = new PayPro();
|
||||
pd = pd.makePaymentDetails(details);
|
||||
|
||||
var network = pd.get('network');
|
||||
var outputs = pd.get('outputs');
|
||||
var time = pd.get('time');
|
||||
var expires = pd.get('expires');
|
||||
var memo = pd.get('memo');
|
||||
var payment_url = pd.get('payment_url');
|
||||
var merchant_data = pd.get('merchant_data');
|
||||
|
||||
var priv = w.privateKey;
|
||||
var pkr = w.publicKeyRing;
|
||||
|
||||
var opts = {
|
||||
remainderOut: {
|
||||
address: w._doGenerateAddress(true).toString()
|
||||
}
|
||||
};
|
||||
|
||||
var outs = [];
|
||||
outputs.forEach(function(output) {
|
||||
var amount = output.get('amount');
|
||||
var script = {
|
||||
offset: output.get('script').offset,
|
||||
limit: output.get('script').limit,
|
||||
buffer: new Buffer(new Uint8Array(
|
||||
output.get('script').buffer))
|
||||
};
|
||||
|
||||
// big endian
|
||||
var v = new Buffer(8);
|
||||
v[0] = (amount.high >> 24) & 0xff;
|
||||
v[1] = (amount.high >> 16) & 0xff;
|
||||
v[2] = (amount.high >> 8) & 0xff;
|
||||
v[3] = (amount.high >> 0) & 0xff;
|
||||
v[4] = (amount.low >> 24) & 0xff;
|
||||
v[5] = (amount.low >> 16) & 0xff;
|
||||
v[6] = (amount.low >> 8) & 0xff;
|
||||
v[7] = (amount.low >> 0) & 0xff;
|
||||
|
||||
var s = script.buffer.slice(script.offset, script.limit);
|
||||
var net = network === 'main' ? 'livenet' : 'testnet';
|
||||
var addr = bitcore.Address.fromScriptPubKey(new bitcore.Script(s), net);
|
||||
|
||||
outs.push({
|
||||
address: addr[0].toString(),
|
||||
amountSatStr: bitcore.Bignum.fromBuffer(v, {
|
||||
// XXX for some reason, endian is ALWAYS 'big'
|
||||
// in node (in the browser it behaves correctly)
|
||||
endian: 'big',
|
||||
size: 1
|
||||
}).toString(10)
|
||||
});
|
||||
});
|
||||
|
||||
var b = new bitcore.TransactionBuilder(opts)
|
||||
.setUnspent(unspentTest)
|
||||
.setOutputs(outs);
|
||||
|
||||
outputs.forEach(function(output, i) {
|
||||
var script = {
|
||||
offset: output.get('script').offset,
|
||||
limit: output.get('script').limit,
|
||||
buffer: new Buffer(new Uint8Array(
|
||||
output.get('script').buffer))
|
||||
};
|
||||
var s = script.buffer.slice(script.offset, script.limit);
|
||||
b.tx.outs[i].s = s;
|
||||
});
|
||||
|
||||
var selectedUtxos = b.getSelectedUnspent();
|
||||
var inputChainPaths = selectedUtxos.map(function(utxo) {
|
||||
return pkr.pathForAddress(utxo.address);
|
||||
});
|
||||
|
||||
b = b.setHashToScriptMap(pkr.getRedeemScriptMap(inputChainPaths));
|
||||
|
||||
if (priv) {
|
||||
var keys = priv.getForPaths(inputChainPaths);
|
||||
var signed = b.sign(keys);
|
||||
}
|
||||
|
||||
var tx = b.build();
|
||||
|
||||
var refund_outputs = [];
|
||||
|
||||
var refund_to = w.publicKeyRing.getPubKeys(0, false, w.getMyCopayerId())[0];
|
||||
|
||||
var total = outputs.reduce(function(total, _, i) {
|
||||
// XXX reverse endianness to work around bignum bug:
|
||||
var txv = tx.outs[i].v;
|
||||
var v = new Buffer(8);
|
||||
for (var j = 0; j < 8; j++) v[j] = txv[7 - j];
|
||||
return total.add(bignum.fromBuffer(v, {
|
||||
endian: 'big',
|
||||
size: 1
|
||||
}));
|
||||
}, bitcore.Bignum('0', 10));
|
||||
|
||||
var rpo = new PayPro();
|
||||
rpo = rpo.makeOutput();
|
||||
|
||||
rpo.set('amount', +total.toString(10));
|
||||
|
||||
rpo.set('script',
|
||||
Buffer.concat([
|
||||
new Buffer([
|
||||
118, // OP_DUP
|
||||
169, // OP_HASH160
|
||||
76, // OP_PUSHDATA1
|
||||
20, // number of bytes
|
||||
]),
|
||||
// needs to be ripesha'd
|
||||
bitcore.util.sha256ripe160(refund_to),
|
||||
new Buffer([
|
||||
136, // OP_EQUALVERIFY
|
||||
172 // OP_CHECKSIG
|
||||
])
|
||||
])
|
||||
);
|
||||
|
||||
refund_outputs.push(rpo.message);
|
||||
|
||||
var pay = new PayPro();
|
||||
pay = pay.makePayment();
|
||||
pay.set('merchant_data', new Buffer([0, 1]));
|
||||
pay.set('transactions', [tx.serialize()]);
|
||||
pay.set('refund_to', refund_outputs);
|
||||
pay.set('memo', 'Hi server, I would like to give you some money.');
|
||||
|
||||
pay = pay.serialize();
|
||||
|
||||
var req = {
|
||||
headers: {
|
||||
'Host': 'localhost:8080',
|
||||
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE + ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
||||
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE,
|
||||
'Content-Length': pay.length + ''
|
||||
},
|
||||
socket: {
|
||||
remoteAddress: 'localhost',
|
||||
remotePort: 8080
|
||||
},
|
||||
body: pay,
|
||||
data: pay
|
||||
};
|
||||
|
||||
Object.keys(req.headers).forEach(function(key) {
|
||||
req.headers[key.toLowerCase()] = req.headers[key];
|
||||
});
|
||||
|
||||
server.POST['/-/pay'](req, function(err, res, body) {
|
||||
if (err) return done(err);
|
||||
|
||||
var data = PayPro.PaymentACK.decode(body);
|
||||
var ack = new PayPro();
|
||||
ack = ack.makePaymentACK(data);
|
||||
|
||||
var payment = ack.get('payment');
|
||||
var memo = ack.get('memo');
|
||||
|
||||
payment = PayPro.Payment.decode(payment);
|
||||
var pay = new PayPro();
|
||||
payment = pay.makePayment(payment);
|
||||
|
||||
var tx = payment.message.transactions[0];
|
||||
|
||||
if (!tx) {
|
||||
return done(new Error('No tx in payment ACK.'));
|
||||
}
|
||||
|
||||
if (tx.buffer) {
|
||||
tx.buffer = new Buffer(new Uint8Array(tx.buffer));
|
||||
tx.buffer = tx.buffer.slice(tx.offset, tx.limit);
|
||||
var ptx = new bitcore.Transaction();
|
||||
ptx.parse(tx.buffer);
|
||||
tx = ptx;
|
||||
}
|
||||
|
||||
var ackTotal = outputs.reduce(function(total, _, i) {
|
||||
// XXX reverse endianness to work around bignum bug:
|
||||
var txv = tx.outs[i].v;
|
||||
var v = new Buffer(8);
|
||||
for (var j = 0; j < 8; j++) v[j] = txv[7 - j];
|
||||
return total.add(bignum.fromBuffer(v, {
|
||||
endian: 'big',
|
||||
size: 1
|
||||
}));
|
||||
}, bitcore.Bignum('0', 10));
|
||||
|
||||
ackTotal.toString(10).should.equal(total.toString(10));
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#retrieve a payment request message via http', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
|
||||
var req = {
|
||||
headers: {
|
||||
'Host': 'localhost:8080',
|
||||
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE + ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
||||
'Content-Type': 'application/octet-stream',
|
||||
'Content-Length': '0'
|
||||
},
|
||||
socket: {
|
||||
remoteAddress: 'localhost',
|
||||
remotePort: 8080
|
||||
},
|
||||
body: {}
|
||||
};
|
||||
|
||||
Object.keys(req.headers).forEach(function(key) {
|
||||
req.headers[key.toLowerCase()] = req.headers[key];
|
||||
});
|
||||
|
||||
server.GET['/-/request'](req, function(err, res, body) {
|
||||
var data = PayPro.PaymentRequest.decode(body);
|
||||
pr = new PayPro();
|
||||
pr = pr.makePaymentRequest(data);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#send a payment message via http', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
|
||||
var ver = pr.get('payment_details_version');
|
||||
var pki_type = pr.get('pki_type');
|
||||
var pki_data = pr.get('pki_data');
|
||||
var details = pr.get('serialized_payment_details');
|
||||
var sig = pr.get('signature');
|
||||
|
||||
var certs = PayPro.X509Certificates.decode(pki_data);
|
||||
certs = certs.certificate;
|
||||
|
||||
var verified = pr.verify();
|
||||
|
||||
if (!verified) {
|
||||
return done(new Error('Server sent a bad signature.'));
|
||||
}
|
||||
|
||||
details = PayPro.PaymentDetails.decode(details);
|
||||
var pd = new PayPro();
|
||||
pd = pd.makePaymentDetails(details);
|
||||
|
||||
var network = pd.get('network');
|
||||
var outputs = pd.get('outputs');
|
||||
var time = pd.get('time');
|
||||
var expires = pd.get('expires');
|
||||
var memo = pd.get('memo');
|
||||
var payment_url = pd.get('payment_url');
|
||||
var merchant_data = pd.get('merchant_data');
|
||||
|
||||
var priv = w.privateKey;
|
||||
var pkr = w.publicKeyRing;
|
||||
|
||||
var opts = {
|
||||
remainderOut: {
|
||||
address: w._doGenerateAddress(true).toString()
|
||||
}
|
||||
};
|
||||
|
||||
var outs = [];
|
||||
outputs.forEach(function(output) {
|
||||
var amount = output.get('amount');
|
||||
var script = {
|
||||
offset: output.get('script').offset,
|
||||
limit: output.get('script').limit,
|
||||
buffer: new Buffer(new Uint8Array(
|
||||
output.get('script').buffer))
|
||||
};
|
||||
|
||||
// big endian
|
||||
var v = new Buffer(8);
|
||||
v[0] = (amount.high >> 24) & 0xff;
|
||||
v[1] = (amount.high >> 16) & 0xff;
|
||||
v[2] = (amount.high >> 8) & 0xff;
|
||||
v[3] = (amount.high >> 0) & 0xff;
|
||||
v[4] = (amount.low >> 24) & 0xff;
|
||||
v[5] = (amount.low >> 16) & 0xff;
|
||||
v[6] = (amount.low >> 8) & 0xff;
|
||||
v[7] = (amount.low >> 0) & 0xff;
|
||||
|
||||
var s = script.buffer.slice(script.offset, script.limit);
|
||||
var net = network === 'main' ? 'livenet' : 'testnet';
|
||||
var addr = bitcore.Address.fromScriptPubKey(new bitcore.Script(s), net);
|
||||
|
||||
outs.push({
|
||||
address: addr[0].toString(),
|
||||
amountSatStr: bitcore.Bignum.fromBuffer(v, {
|
||||
// XXX for some reason, endian is ALWAYS 'big'
|
||||
// in node (in the browser it behaves correctly)
|
||||
endian: 'big',
|
||||
size: 1
|
||||
}).toString(10)
|
||||
});
|
||||
});
|
||||
|
||||
var b = new bitcore.TransactionBuilder(opts)
|
||||
.setUnspent(unspentTest)
|
||||
.setOutputs(outs);
|
||||
|
||||
outputs.forEach(function(output, i) {
|
||||
var script = {
|
||||
offset: output.get('script').offset,
|
||||
limit: output.get('script').limit,
|
||||
buffer: new Buffer(new Uint8Array(
|
||||
output.get('script').buffer))
|
||||
};
|
||||
var s = script.buffer.slice(script.offset, script.limit);
|
||||
b.tx.outs[i].s = s;
|
||||
});
|
||||
|
||||
var selectedUtxos = b.getSelectedUnspent();
|
||||
var inputChainPaths = selectedUtxos.map(function(utxo) {
|
||||
return pkr.pathForAddress(utxo.address);
|
||||
});
|
||||
|
||||
b = b.setHashToScriptMap(pkr.getRedeemScriptMap(inputChainPaths));
|
||||
|
||||
if (priv) {
|
||||
var keys = priv.getForPaths(inputChainPaths);
|
||||
var signed = b.sign(keys);
|
||||
}
|
||||
|
||||
var tx = b.build();
|
||||
|
||||
var refund_outputs = [];
|
||||
|
||||
var refund_to = w.publicKeyRing.getPubKeys(0, false, w.getMyCopayerId())[0];
|
||||
|
||||
var total = outputs.reduce(function(total, _, i) {
|
||||
// XXX reverse endianness to work around bignum bug:
|
||||
var txv = tx.outs[i].v;
|
||||
var v = new Buffer(8);
|
||||
for (var j = 0; j < 8; j++) v[j] = txv[7 - j];
|
||||
return total.add(bignum.fromBuffer(v, {
|
||||
endian: 'big',
|
||||
size: 1
|
||||
}));
|
||||
}, bitcore.Bignum('0', 10));
|
||||
|
||||
var rpo = new PayPro();
|
||||
rpo = rpo.makeOutput();
|
||||
|
||||
rpo.set('amount', +total.toString(10));
|
||||
|
||||
rpo.set('script',
|
||||
Buffer.concat([
|
||||
new Buffer([
|
||||
118, // OP_DUP
|
||||
169, // OP_HASH160
|
||||
76, // OP_PUSHDATA1
|
||||
20, // number of bytes
|
||||
]),
|
||||
// needs to be ripesha'd
|
||||
bitcore.util.sha256ripe160(refund_to),
|
||||
new Buffer([
|
||||
136, // OP_EQUALVERIFY
|
||||
172 // OP_CHECKSIG
|
||||
])
|
||||
])
|
||||
);
|
||||
|
||||
refund_outputs.push(rpo.message);
|
||||
|
||||
var pay = new PayPro();
|
||||
pay = pay.makePayment();
|
||||
pay.set('merchant_data', new Buffer([0, 1]));
|
||||
pay.set('transactions', [tx.serialize()]);
|
||||
pay.set('refund_to', refund_outputs);
|
||||
pay.set('memo', 'Hi server, I would like to give you some money.');
|
||||
|
||||
pay = pay.serialize();
|
||||
|
||||
var req = {
|
||||
headers: {
|
||||
'Host': 'localhost:8080',
|
||||
'Accept': PayPro.PAYMENT_REQUEST_CONTENT_TYPE + ', ' + PayPro.PAYMENT_ACK_CONTENT_TYPE,
|
||||
'Content-Type': PayPro.PAYMENT_CONTENT_TYPE,
|
||||
'Content-Length': pay.length + ''
|
||||
},
|
||||
socket: {
|
||||
remoteAddress: 'localhost',
|
||||
remotePort: 8080
|
||||
},
|
||||
body: pay,
|
||||
data: pay
|
||||
};
|
||||
|
||||
Object.keys(req.headers).forEach(function(key) {
|
||||
req.headers[key.toLowerCase()] = req.headers[key];
|
||||
});
|
||||
|
||||
server.POST['/-/pay'](req, function(err, res, body) {
|
||||
if (err) return done(err);
|
||||
|
||||
var data = PayPro.PaymentACK.decode(body);
|
||||
var ack = new PayPro();
|
||||
ack = ack.makePaymentACK(data);
|
||||
|
||||
var payment = ack.get('payment');
|
||||
var memo = ack.get('memo');
|
||||
|
||||
payment = PayPro.Payment.decode(payment);
|
||||
var pay = new PayPro();
|
||||
payment = pay.makePayment(payment);
|
||||
|
||||
var tx = payment.message.transactions[0];
|
||||
|
||||
if (!tx) {
|
||||
return done(new Error('No tx in payment ACK.'));
|
||||
}
|
||||
|
||||
if (tx.buffer) {
|
||||
tx.buffer = new Buffer(new Uint8Array(tx.buffer));
|
||||
tx.buffer = tx.buffer.slice(tx.offset, tx.limit);
|
||||
var ptx = new bitcore.Transaction();
|
||||
ptx.parse(tx.buffer);
|
||||
tx = ptx;
|
||||
}
|
||||
|
||||
var ackTotal = outputs.reduce(function(total, _, i) {
|
||||
// XXX reverse endianness to work around bignum bug:
|
||||
var txv = tx.outs[i].v;
|
||||
var v = new Buffer(8);
|
||||
for (var j = 0; j < 8; j++) v[j] = txv[7 - j];
|
||||
return total.add(bignum.fromBuffer(v, {
|
||||
endian: 'big',
|
||||
size: 1
|
||||
}));
|
||||
}, bitcore.Bignum('0', 10));
|
||||
|
||||
ackTotal.toString(10).should.equal(total.toString(10));
|
||||
|
||||
should.exist(ack);
|
||||
memo.should.equal('Thank you for your payment!');
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
ppw = createWallet();
|
||||
|
||||
it('#retrieve a payment request message via model', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
// Caches Payment Request but does not add TX proposal
|
||||
w.fetchPaymentTx({
|
||||
uri: 'https://localhost:8080/-/request'
|
||||
}, function(err, merchantData) {
|
||||
if (err) return done(err);
|
||||
merchantData.pr.pd.payment_url.should.equal('https://localhost:8080/-/pay');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#add tx proposal based on payment message via model', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
var options = {
|
||||
uri: 'https://localhost:8080/-/request'
|
||||
};
|
||||
var req = w.paymentRequests[options.uri];
|
||||
should.exist(req);
|
||||
delete w.paymentRequests[options.uri];
|
||||
w.receivePaymentRequest(options, req.pr, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
w._ntxid = ntxid;
|
||||
merchantData.pr.pd.payment_url.should.equal('https://localhost:8080/-/pay');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#add tx proposal based on payment message via model', function(done) {
|
||||
var w = ppw;
|
||||
should.exist(w);
|
||||
w.sendPaymentTx(w._ntxid, function(txid, merchantData) {
|
||||
should.exist(txid);
|
||||
should.exist(merchantData);
|
||||
should.exist(merchantData.ack);
|
||||
merchantData.ack.memo.should.equal('Thank you for your payment!');
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#send a payment request using payment api', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var uri = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
|
||||
var memo = 'Hello, server. I\'d like to make a payment.';
|
||||
w.createPaymentTx({
|
||||
uri: uri,
|
||||
memo: memo
|
||||
}, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
if (w.isShared()) {
|
||||
return done();
|
||||
} else {
|
||||
w.sendPaymentTx(ntxid, {
|
||||
memo: memo
|
||||
}, function(txid, merchantData) {
|
||||
should.exist(txid);
|
||||
should.exist(merchantData);
|
||||
return done();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('#send a payment request with merchant prefix', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var address = 'Merchant: ' + server.uri + '/request\nMemo: foo';
|
||||
var commentText = 'Hello, server. I\'d like to make a payment.';
|
||||
var uri;
|
||||
|
||||
// Replicates code in controllers/send.js:
|
||||
if (address.indexOf('bitcoin:') === 0) {
|
||||
uri = new bitcore.BIP21(address).data;
|
||||
} else if (address.indexOf('Merchant: ') === 0) {
|
||||
uri = address.split(/\s+/)[1];
|
||||
}
|
||||
|
||||
w.createPaymentTx({ uri: uri, memo: commentText }, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
if (w.isShared()) {
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
return done();
|
||||
} else {
|
||||
should.exist(merchantData);
|
||||
w.sendTx(ntxid, function(txid, merchantData) {
|
||||
should.exist(txid);
|
||||
should.exist(merchantData);
|
||||
return done();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('#send a payment request with bitcoin uri', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
|
||||
var commentText = 'Hello, server. I\'d like to make a payment.';
|
||||
w.createPaymentTx({ uri: address, memo: commentText }, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
if (w.isShared()) {
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
return done();
|
||||
} else {
|
||||
w.sendTx(ntxid, function(txid, merchantData) {
|
||||
should.exist(txid);
|
||||
should.exist(merchantData);
|
||||
return done();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('#try to sign a tampered payment request (raw)', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
|
||||
var commentText = 'Hello, server. I\'d like to make a payment.';
|
||||
w.createPaymentTx({ uri: address, memo: commentText }, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
|
||||
// Tamper with payment request in its raw form:
|
||||
var data = new Buffer(merchantData.raw, 'hex');
|
||||
data = PayPro.PaymentRequest.decode(data);
|
||||
var pr = new PayPro();
|
||||
pr = pr.makePaymentRequest(data);
|
||||
var details = pr.get('serialized_payment_details');
|
||||
details = PayPro.PaymentDetails.decode(details);
|
||||
var pd = new PayPro();
|
||||
pd = pd.makePaymentDetails(details);
|
||||
var outputs = pd.get('outputs');
|
||||
outputs[outputs.length - 1].set('amount', 1000000000);
|
||||
pd.set('outputs', outputs);
|
||||
pr.set('serialized_payment_details', pd.serialize());
|
||||
merchantData.raw = pr.serialize().toString('hex');
|
||||
|
||||
var myId = w.getMyCopayerId();
|
||||
var txp = w.txProposals.get(ntxid);
|
||||
should.exist(txp);
|
||||
should.exist(txp.signedBy[myId]);
|
||||
should.not.exist(txp.rejectedBy[myId]);
|
||||
|
||||
w.verifyPaymentRequest(ntxid).should.equal(false);
|
||||
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#try to sign a tampered payment request (abstract)', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
|
||||
var commentText = 'Hello, server. I\'d like to make a payment.';
|
||||
w.createPaymentTx({ uri: address, memo: commentText }, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
|
||||
// Tamper with payment request in its abstract form:
|
||||
var outputs = merchantData.pr.pd.outputs;
|
||||
var output = outputs[outputs.length - 1];
|
||||
var amount = output.amount;
|
||||
amount.low = 2;
|
||||
|
||||
var myId = w.getMyCopayerId();
|
||||
var txp = w.txProposals.get(ntxid);
|
||||
should.exist(txp);
|
||||
should.exist(txp.signedBy[myId]);
|
||||
should.not.exist(txp.rejectedBy[myId]);
|
||||
|
||||
w.verifyPaymentRequest(ntxid).should.equal(false);
|
||||
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#try to sign a tampered txp tx (abstract)', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
|
||||
var commentText = 'Hello, server. I\'d like to make a payment.';
|
||||
w.createPaymentTx({ uri: address, memo: commentText }, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
|
||||
// Tamper with payment request in its abstract form:
|
||||
var txp = w.txProposals.get(ntxid);
|
||||
var tx = txp.builder.tx || txp.builder.build();
|
||||
tx.outs[0].v = new Buffer([2, 0, 0, 0, 0, 0, 0, 0]);
|
||||
|
||||
var myId = w.getMyCopayerId();
|
||||
var txp = w.txProposals.get(ntxid);
|
||||
should.exist(txp);
|
||||
should.exist(txp.signedBy[myId]);
|
||||
should.not.exist(txp.rejectedBy[myId]);
|
||||
|
||||
w.verifyPaymentRequest(ntxid).should.equal(false);
|
||||
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#sign an untampered payment request', function(done) {
|
||||
var w = createWallet();
|
||||
should.exist(w);
|
||||
var address = 'bitcoin:2NBzZdFBoQymDgfzH2Pmnthser1E71MmU47?amount=0.00003&r=' + server.uri + '/request';
|
||||
var commentText = 'Hello, server. I\'d like to make a payment.';
|
||||
w.createPaymentTx({ uri: address, memo: commentText }, function(err, ntxid, merchantData) {
|
||||
should.equal(err, null);
|
||||
should.exist(ntxid);
|
||||
should.exist(merchantData);
|
||||
|
||||
var myId = w.getMyCopayerId();
|
||||
var txp = w.txProposals.get(ntxid);
|
||||
should.exist(txp);
|
||||
should.exist(txp.signedBy[myId]);
|
||||
should.not.exist(txp.rejectedBy[myId]);
|
||||
|
||||
w.verifyPaymentRequest(ntxid).should.equal(true);
|
||||
|
||||
return done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#close payment server', function(done) {
|
||||
server.close(function() {
|
||||
return done();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1,120 +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 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,529 +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 a = w.generateAddress(isChange, k.pub);
|
||||
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('should return PublicKeyRing 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);
|
||||
}
|
||||
});
|
||||
|
||||
var as = w.getAddressesInfo();
|
||||
as.length.should.equal(5); // include pre-generated shared one
|
||||
for (var j in as) {
|
||||
var a = as[j];
|
||||
a.address.isValid().should.equal(true);
|
||||
a.addressStr.should.equal(a.address.toString());
|
||||
a.isChange.should.equal([false, false, false, true, true][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('should set backup ready', function() {
|
||||
var w = getCachedW().w;
|
||||
w.isBackupReady().should.equal(false);
|
||||
w.setBackupReady();
|
||||
w.isBackupReady().should.equal(true);
|
||||
});
|
||||
|
||||
|
||||
it('should check for other backups', function() {
|
||||
var w = createW().w;
|
||||
w.remainingBackups().should.equal(5);
|
||||
w.isFullyBackup().should.equal(false);
|
||||
|
||||
w.setBackupReady();
|
||||
w.remainingBackups().should.equal(4);
|
||||
w.isFullyBackup().should.equal(false);
|
||||
|
||||
w.copayersBackup = ["a", "b", "c", "d", "e"];
|
||||
w.remainingBackups().should.equal(0);
|
||||
w.isFullyBackup().should.equal(true);
|
||||
});
|
||||
|
||||
it('should merge backup', function() {
|
||||
var w = getCachedW().w;
|
||||
var hasChanged;
|
||||
|
||||
w.copayersBackup = ["a", "b"];
|
||||
hasChanged = w._mergeBackups(["b", "c"]);
|
||||
w.copayersBackup.length.should.equal(3);
|
||||
hasChanged.should.equal(true);
|
||||
|
||||
w.copayersBackup = ["a", "b", "c"];
|
||||
hasChanged = w._mergeBackups(["b", "c"]);
|
||||
w.copayersBackup.length.should.equal(3);
|
||||
hasChanged.should.equal(false);
|
||||
});
|
||||
|
||||
it('should merge backup tests', function() {
|
||||
var w = createW().w;
|
||||
|
||||
var w2 = new PublicKeyRing({
|
||||
networkName: 'livenet',
|
||||
walletId: w.walletId,
|
||||
});
|
||||
w.merge(w2).should.equal(false);
|
||||
w.remainingBackups().should.equal(5);
|
||||
|
||||
w2.setBackupReady();
|
||||
w.merge(w2).should.equal(true);
|
||||
w.remainingBackups().should.equal(4);
|
||||
});
|
||||
|
||||
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('bad data format: Did you use .toObj()?');
|
||||
});
|
||||
|
||||
|
||||
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('#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);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,460 +0,0 @@
|
|||
'use strict';
|
||||
var Storage = copay.Storage;
|
||||
|
||||
var fakeWallet = 'fake-wallet-id';
|
||||
var timeStamp = Date.now();
|
||||
|
||||
describe('Storage model', function() {
|
||||
|
||||
var s;
|
||||
beforeEach(function() {
|
||||
s = new Storage(requireMock('FakeLocalStorage').storageParams);
|
||||
s.setPassphrase('mysupercoolpassword');
|
||||
s.storage.clear();
|
||||
s.sessionStorage.clear();
|
||||
});
|
||||
|
||||
|
||||
it('should create an instance', function() {
|
||||
var s2 = new Storage(requireMock('FakeLocalStorage').storageParams);
|
||||
should.exist(s2);
|
||||
});
|
||||
it('should fail when encrypting without a password', function() {
|
||||
var s2 = new Storage(requireMock('FakeLocalStorage').storageParams);
|
||||
(function() {
|
||||
s2._write(fakeWallet + timeStamp, 1, function() {});
|
||||
}).should.throw('NOPASSPHRASE');
|
||||
});
|
||||
it('should be able to encrypt and decrypt', function(done) {
|
||||
s._write(fakeWallet + timeStamp, 'value', function() {
|
||||
s._read(fakeWallet + timeStamp, function(v) {
|
||||
v.should.equal('value');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should be able to set a value', function(done) {
|
||||
s._write(fakeWallet + timeStamp, 1, function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
var getSetData = [
|
||||
1, 1000, -15, -1000,
|
||||
0.1, -0.5, -0.5e-10, Math.PI,
|
||||
'hi', 'auydoaiusyodaisudyoa', '0b5b8556a0c2ce828c9ccfa58b3dd0a1ae879b9b',
|
||||
'1CjPR7Z5ZSyWk6WtXvSFgkptmpoi4UM9BC', 'OP_DUP OP_HASH160 80ad90d4035', [1, 2, 3, 4, 5, 6], {
|
||||
x: 1,
|
||||
y: 2
|
||||
}, {
|
||||
x: 'hi',
|
||||
y: null
|
||||
}, {
|
||||
a: {},
|
||||
b: [],
|
||||
c: [1, 2, 'hi']
|
||||
},
|
||||
null
|
||||
];
|
||||
getSetData.forEach(function(obj) {
|
||||
it('should be able to set a value and get it for ' + JSON.stringify(obj), function(done) {
|
||||
s._write(fakeWallet + timeStamp, obj, function() {
|
||||
s._read(fakeWallet + timeStamp, function(obj2) {
|
||||
JSON.stringify(obj2).should.equal(JSON.stringify(obj));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#export', function() {
|
||||
it('should export the encrypted wallet', function(done) {
|
||||
s._write(fakeWallet + timeStamp, 'testval', function() {
|
||||
var obj = {
|
||||
test: 'testval'
|
||||
};
|
||||
var encrypted = s.export(obj);
|
||||
encrypted.length.should.be.greaterThan(10);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#_getWalletIds', function() {
|
||||
it('should get wallet ids', function(done) {
|
||||
s._write('1::hola', 'juan', function() {
|
||||
s._write('2::hola', 'juan', function() {
|
||||
s._getWalletIds(function(v) {
|
||||
v.should.deep.equal(['1', '2']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getLastOpened #setLastOpened', function() {
|
||||
it('should get/set last opened', function() {
|
||||
s.setLastOpened('hey', function() {
|
||||
s.getLastOpened(function(v) {
|
||||
v.should.equal('hey');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (is_browser) {
|
||||
describe('#getSessionId', function() {
|
||||
it('should get SessionId', function(done) {
|
||||
s.getSessionId(function(sid) {
|
||||
should.exist(sid);
|
||||
s.getSessionId(function(sid2) {
|
||||
sid2.should.equal(sid);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('#getWallets_Old', function() {
|
||||
it('should retrieve wallets from storage', function(done) {
|
||||
s._write('1::hola', 'juan', function() {
|
||||
s._write('2::hola', 'juan', function() {
|
||||
s.setGlobal('nameFor::1', 'hola', function() {
|
||||
|
||||
s.getWallets_Old(function(ws) {
|
||||
ws[0].should.deep.equal({
|
||||
id: '1',
|
||||
name: 'hola',
|
||||
});
|
||||
ws[1].should.deep.equal({
|
||||
id: '2',
|
||||
name: undefined
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should retrieve wallets from storage (with delay)', function(done) {
|
||||
s._write('1::hola', 'juan', function() {
|
||||
s._write('2::hola', 'juan', function() {
|
||||
s.setGlobal('nameFor::1', 'hola', function() {
|
||||
|
||||
var orig = s.getGlobal.bind(s);
|
||||
s.getGlobal = function(k, cb) {
|
||||
setTimeout(function() {
|
||||
orig(k, cb);
|
||||
}, 1);
|
||||
};
|
||||
|
||||
s.getWallets_Old(function(ws) {
|
||||
ws[0].should.deep.equal({
|
||||
id: '1',
|
||||
name: 'hola',
|
||||
});
|
||||
ws[1].should.deep.equal({
|
||||
id: '2',
|
||||
name: undefined
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getWallets2', function() {
|
||||
it('should retrieve wallets from storage', function(done) {
|
||||
var w1 = {
|
||||
name: 'juan',
|
||||
opts: {
|
||||
name: 'wallet1'
|
||||
}
|
||||
};
|
||||
var w2 = {
|
||||
name: 'pepe'
|
||||
};
|
||||
s.setFromObj('1', w1, function() {
|
||||
s.setFromObj('2', w2, function() {
|
||||
s.getWallets2(function(ws) {
|
||||
ws[0].should.deep.equal({
|
||||
id: '1',
|
||||
name: 'wallet1',
|
||||
});
|
||||
ws[1].should.deep.equal({
|
||||
id: '2',
|
||||
name: undefined
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#getWallets', function() {
|
||||
it('should retrieve wallets from storage both new and old format', function(done) {
|
||||
var w1 = {
|
||||
name: 'juan',
|
||||
opts: {
|
||||
name: 'wallet1'
|
||||
}
|
||||
};
|
||||
var w2 = {
|
||||
name: 'pepe'
|
||||
};
|
||||
|
||||
s.setFromObj('1', w1, function() {
|
||||
s.setFromObj('2', w2, function() {
|
||||
s._write('3::name', 'matias', function() {
|
||||
s._write('1::name', 'juan', function() {
|
||||
s.setGlobal('nameFor::3', 'wallet3', function() {
|
||||
s.getWallets(function(ws) {
|
||||
ws.length.should.equal(3);
|
||||
ws[0].should.deep.equal({
|
||||
id: '1',
|
||||
name: 'wallet1',
|
||||
});
|
||||
ws[1].should.deep.equal({
|
||||
id: '2',
|
||||
name: undefined
|
||||
});
|
||||
ws[2].should.deep.equal({
|
||||
id: '3',
|
||||
name: 'wallet3',
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#deleteWallet_Old', function() {
|
||||
it('should fail to delete a unexisting wallet', function(done) {
|
||||
s._write('1::hola', 'juan', function() {
|
||||
s._write('2::hola', 'juan', function() {
|
||||
s.deleteWallet_Old('3', function(err) {
|
||||
err.toString().should.include('WNOTFOUND');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a wallet', function(done) {
|
||||
s._write('1::hola', 'juan', function() {
|
||||
s._write('2::hola', 'juan', function() {
|
||||
s.deleteWallet_Old('1', function(err) {
|
||||
should.not.exist(err);
|
||||
s.getWallets_Old(function(ws) {
|
||||
ws.length.should.equal(1);
|
||||
ws[0].should.deep.equal({
|
||||
id: '2',
|
||||
name: undefined
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#deleteWallet', function() {
|
||||
it('should fail to delete a unexisting wallet', function(done) {
|
||||
var w1 = {
|
||||
name: 'juan',
|
||||
opts: {
|
||||
name: 'wallet1'
|
||||
}
|
||||
};
|
||||
var w2 = {
|
||||
name: 'pepe'
|
||||
};
|
||||
|
||||
s.setFromObj('1', w1, function() {
|
||||
s.setFromObj('2', w2, function() {
|
||||
s.deleteWallet('3', function(err) {
|
||||
err.toString().should.include('WNOTFOUND');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should delete a wallet', function(done) {
|
||||
var w1 = {
|
||||
name: 'juan',
|
||||
opts: {
|
||||
name: 'wallet1'
|
||||
}
|
||||
};
|
||||
var w2 = {
|
||||
name: 'pepe'
|
||||
};
|
||||
|
||||
s.setFromObj('1', w1, function() {
|
||||
s.setFromObj('2', w2, function() {
|
||||
s.deleteWallet('1', function(err) {
|
||||
should.not.exist(err);
|
||||
s.getWallets2(function(ws) {
|
||||
ws.length.should.equal(1);
|
||||
ws[0].id.should.equal('2');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#readWallet_Old', function() {
|
||||
it('should read wallet', function(done) {
|
||||
var data = {
|
||||
'id1::a': 'x',
|
||||
'id1::b': 'y',
|
||||
'id2::c': 'z',
|
||||
};
|
||||
s.storage.allKeys = sinon.stub().yields(_.keys(data));
|
||||
sinon.stub(s, '_read', function(k, cb) {
|
||||
return cb(data[k]);
|
||||
});
|
||||
s.readWallet_Old('id1', function(err, w) {
|
||||
should.not.exist(err);
|
||||
w.should.exist;
|
||||
w.hasOwnProperty('a').should.be.true;
|
||||
w.hasOwnProperty('b').should.be.true;
|
||||
w.hasOwnProperty('c').should.be.false;
|
||||
w.a.should.equal('x');
|
||||
w.b.should.equal('y');
|
||||
s._read.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#readWallet', function() {
|
||||
it('should read wallet', function(done) {
|
||||
var data = {
|
||||
'wallet::id1_wallet1': {
|
||||
a: 'x',
|
||||
b: 'y'
|
||||
},
|
||||
'wallet::id2': {
|
||||
c: 'z'
|
||||
},
|
||||
};
|
||||
s.storage.allKeys = sinon.stub().yields(_.keys(data));
|
||||
sinon.stub(s, '_read', function(k, cb) {
|
||||
return cb(data[k]);
|
||||
});
|
||||
s.readWallet('id1', function(err, w) {
|
||||
should.not.exist(err);
|
||||
w.should.exist;
|
||||
w.hasOwnProperty('a').should.be.true;
|
||||
w.hasOwnProperty('b').should.be.true;
|
||||
w.hasOwnProperty('c').should.be.false;
|
||||
w.a.should.equal('x');
|
||||
w.b.should.equal('y');
|
||||
s._read.restore();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#setFromObj', function() {
|
||||
it('should store from an object as single key', function(done) {
|
||||
s.setFromObj('id1', {
|
||||
'key': 'val',
|
||||
'opts': {
|
||||
'name': 'nameid1'
|
||||
},
|
||||
}, function() {
|
||||
s._read('wallet::id1_nameid1', function(r) {
|
||||
r.should.exist;
|
||||
r.key.should.exist;
|
||||
r.key.should.equal('val');
|
||||
r.opts.name.should.equal('nameid1');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#globals', function() {
|
||||
it('should set, get and remove keys', function(done) {
|
||||
s.setGlobal('a', {
|
||||
b: 1
|
||||
}, function() {
|
||||
s.getGlobal('a', function(v) {
|
||||
|
||||
JSON.parse(v).should.deep.equal({
|
||||
b: 1
|
||||
});
|
||||
s.removeGlobal('a', function() {
|
||||
s.getGlobal('a', function(v) {
|
||||
should.not.exist(v);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('session storage', function() {
|
||||
it('should get a session ID', function(done) {
|
||||
s.getSessionId(function(s) {
|
||||
should.exist(s);
|
||||
s.length.should.equal(16);
|
||||
(new Buffer(s, 'hex')).length.should.equal(8);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#import', function() {
|
||||
it('should not be able to decrypt with wrong password', function() {
|
||||
s.setPassphrase('xxx');
|
||||
var wo = s.import(encryptedLegacy1);
|
||||
should.not.exist(wo);
|
||||
});
|
||||
|
||||
it('should be able to decrypt an old backup', function() {
|
||||
s.setPassphrase(legacyPassword1);
|
||||
var wo = s.import(encryptedLegacy1);
|
||||
should.exist(wo);
|
||||
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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
var legacyPassword1 = '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==';
|
||||
|
|
@ -1,451 +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 FakeBuilder = requireMock('FakeBuilder');
|
||||
var TxProposal = copay.TxProposal;
|
||||
|
||||
var dummyProposal = new TxProposal({
|
||||
creator: 1,
|
||||
createdTs: 1,
|
||||
builder: new FakeBuilder(),
|
||||
inputChainPaths: ['m/1'],
|
||||
});
|
||||
|
||||
var someKeys = ["03b39d61dc9a504b13ae480049c140dcffa23a6cc9c09d12d6d1f332fee5e18ca5", "022929f515c5cf967474322468c3bd945bb6f281225b2c884b465680ef3052c07e"];
|
||||
|
||||
describe('TxProposal', function() {
|
||||
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: new FakeBuilder(),
|
||||
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 FakeBuilder();
|
||||
var spy = sinon.spy(b.tx, 'getNormalizedHash');
|
||||
var txp = new TxProposal({
|
||||
creator: 1,
|
||||
createdTs: 1,
|
||||
builder: b,
|
||||
inputChainPaths: 'm/1',
|
||||
});
|
||||
txp.getId().should.equal('123456');;
|
||||
sinon.assert.callCount(spy, 1);
|
||||
});
|
||||
});
|
||||
describe('#toObj', function() {
|
||||
it('should return an object and remove builder', function() {
|
||||
var b = new FakeBuilder();
|
||||
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 FakeBuilder();
|
||||
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('#fromObj', function() {
|
||||
it('should fail to create from wrong object', function() {
|
||||
var b = new FakeBuilder();
|
||||
(function() {
|
||||
var txp = TxProposal.fromObj({
|
||||
creator: 1,
|
||||
createdTs: 1,
|
||||
builderObj: b.toObj(),
|
||||
inputChainPaths: ['m/1'],
|
||||
});
|
||||
}).should.throw('Invalid');
|
||||
});
|
||||
it('sets force opts', function() {
|
||||
var b = new FakeBuilder();
|
||||
b.opts={juan:1, pepe:1, fee:1000};
|
||||
var txp;
|
||||
var o = {
|
||||
creator: 1,
|
||||
createdTs: 1,
|
||||
builderObj: b.toObj(),
|
||||
inputChainPaths: ['m/1'],
|
||||
};
|
||||
(function() {
|
||||
txp = TxProposal.fromObj(o,{pepe:100});
|
||||
}).should.throw('Invalid tx proposal: no ins');
|
||||
o.builderObj.opts.should.deep.equal({juan:1, pepe:100, feeSat:undefined, fee:undefined});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#setSent', function() {
|
||||
it('should set txid and timestamp', function() {
|
||||
var now = Date.now();
|
||||
var txp = dummyProposal;
|
||||
txp.setSent('3a42');
|
||||
txp.sentTs.should.gte(now);
|
||||
txp.sentTxid.should.equal('3a42');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('Signature verification', function() {
|
||||
var validScriptSig = new bitcore.Script(FakeBuilder.VALID_SCRIPTSIG_BUF);
|
||||
|
||||
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() {
|
||||
(function() {
|
||||
TxProposal._verifySignatures(
|
||||
keyBuf,
|
||||
new bitcore.Script(new Buffer('112233', 'hex')),
|
||||
new Buffer('1a', 'hex'));
|
||||
}).should.throw('script');
|
||||
});
|
||||
it('#_verifyScriptSig, no signatures', function() {
|
||||
var ret = TxProposal._verifySignatures(keyBuf, validScriptSig, new Buffer(32));
|
||||
ret.length.should.equal(0);
|
||||
});
|
||||
it('#_verifyScriptSig, two signatures', function() {
|
||||
// Data taken from bitcore's TransactionBuilder test
|
||||
var txp = dummyProposal;
|
||||
var tx = dummyProposal.builder.build();
|
||||
var ret = TxProposal._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('#_updateSignedBy', function() {
|
||||
var txp = dummyProposal;
|
||||
txp._inputSigners.should.deep.equal([
|
||||
['03197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d', '03a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e3']
|
||||
]);
|
||||
});
|
||||
describe('#_check', function() {
|
||||
var txp = dummyProposal;
|
||||
var backup = txp.builder.tx.ins;
|
||||
|
||||
it('OK', function() {
|
||||
txp._check();
|
||||
});
|
||||
it('FAIL ins', function() {
|
||||
txp.builder.tx.ins = [];
|
||||
(function() {
|
||||
txp._check();
|
||||
}).should.throw('no ins');
|
||||
txp.builder.tx.ins = backup;
|
||||
});
|
||||
it('FAIL signhash SINGLE', function() {
|
||||
sinon.stub(txp.builder.tx, 'getHashType').returns(Transaction.SIGHASH_SINGLE);
|
||||
(function() {
|
||||
txp._check();
|
||||
}).should.throw('signatures');
|
||||
txp.builder.tx.getHashType.restore();
|
||||
});
|
||||
it('FAIL signhash NONE', function() {
|
||||
sinon.stub(txp.builder.tx, 'getHashType').returns(Transaction.SIGHASH_NONE);
|
||||
(function() {
|
||||
txp._check();
|
||||
}).should.throw('signatures');
|
||||
txp.builder.tx.getHashType.restore();
|
||||
});
|
||||
it('FAIL signhash ANYONECANPAY', function() {
|
||||
sinon.stub(txp.builder.tx, 'getHashType').returns(Transaction.SIGHASH_ANYONECANPAY);
|
||||
(function() {
|
||||
txp._check();
|
||||
}).should.throw('signatures');
|
||||
txp.builder.tx.getHashType.restore();
|
||||
});
|
||||
it('FAIL no signatures', function() {
|
||||
var backup = txp.builder.tx.ins[0].s;
|
||||
txp.builder.tx.ins[0].s = undefined;
|
||||
(function() {
|
||||
txp._check();
|
||||
}).should.throw('no signatures');
|
||||
txp.builder.tx.ins[0].s = backup;
|
||||
});
|
||||
});
|
||||
describe('#merge', function() {
|
||||
var txp = dummyProposal;
|
||||
var backup = txp.builder.tx.ins;
|
||||
it('with self', function() {
|
||||
var hasChanged = txp.merge(txp);
|
||||
hasChanged.should.equal(false);
|
||||
});
|
||||
|
||||
it('with less signatures', function() {
|
||||
var backup = txp.builder.vanilla.scriptSig[0];
|
||||
txp.builder.merge = function() {
|
||||
// 2 signatures.
|
||||
this.vanilla.scriptSig = ['0048304502207d8e832bd576c93300e53ab6cbd68641961bec60690c358fd42d8e42b7d7d687022100a1daa89923efdb4c9b615d065058d9e1644f67000694a7d0806759afa7bef19b014cad532103197599f6e209cefef07da2fddc6fe47715a70162c531ffff8e611cef23dfb70d210380a29968851f93af55e581c43d9ef9294577a439a3ca9fc2bc47d1ca2b3e9127210392dccb2ed470a45984811d6402fdca613c175f8f3e4eb8e2306e8ccd7d0aed032103a94351fecc4328bb683bf93a1aa67378374904eac5980c7966723a51897c56e32103e085eb6fa1f20b2722c16161144314070a2c316a9cae2489fd52ce5f63fff6e455ae'];
|
||||
this.tx.ins[0].s = new Buffer(this.vanilla.scriptSig[0], 'hex');
|
||||
};
|
||||
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('juan', {
|
||||
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('juan', {
|
||||
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('juan', {
|
||||
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('juan', {
|
||||
pk0: 'creator',
|
||||
pk1: 'pepe',
|
||||
pk2: 'john'
|
||||
})
|
||||
}).should.throw('senders sig');
|
||||
});
|
||||
|
||||
|
||||
it("should set signedBy (trivial case)", function() {
|
||||
var txp = dummyProposal;
|
||||
var ts = Date.now();
|
||||
txp._inputSigners = [
|
||||
['pk1', 'pk0']
|
||||
];
|
||||
txp.signedBy = {
|
||||
'creator': Date.now()
|
||||
};
|
||||
txp.setCopayers('pepe', {
|
||||
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();
|
||||
txp._inputSigners = [
|
||||
['pk0']
|
||||
];
|
||||
txp.signedBy = {};
|
||||
delete txp['creator'];
|
||||
delete txp['creatorTs'];
|
||||
txp.setCopayers('creator', {
|
||||
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'];
|
||||
txp._inputSigners = [
|
||||
['pk0', 'pk1']
|
||||
];
|
||||
(function() {
|
||||
txp.setCopayers(
|
||||
'creator', {
|
||||
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();
|
||||
txp._inputSigners = [
|
||||
['pk0', 'pk1']
|
||||
];
|
||||
txp.creator = 'creator';
|
||||
txp.signedBy = {
|
||||
'creator': 1
|
||||
};
|
||||
txp.setCopayers('pepe', {
|
||||
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 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,123 +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 FakeBuilder = requireMock('FakeBuilder');
|
||||
var TxProposal = copay.TxProposal;
|
||||
var TxProposals = copay.TxProposals;
|
||||
|
||||
var dummyProposal = new TxProposal({
|
||||
creator: 1,
|
||||
createdTs: 1,
|
||||
builder: new FakeBuilder(),
|
||||
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 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('#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('#toObj', function() {
|
||||
it('should an object', function() {
|
||||
var txps = TxProposals.fromObj({
|
||||
networkName:'livenet',
|
||||
walletId: '123a12',
|
||||
txps: [],
|
||||
});
|
||||
var o = txps.toObj();
|
||||
o.walletId.should.equal('123a12');
|
||||
o.networkName.should.equal('livenet');
|
||||
});
|
||||
it('should export txps', function() {
|
||||
var txps = TxProposals.fromObj({
|
||||
networkName:'livenet',
|
||||
walletId: '123a12',
|
||||
txps: [],
|
||||
});
|
||||
txps.txps = {
|
||||
'hola' : dummyProposal,
|
||||
'chau' : dummyProposal,
|
||||
};
|
||||
var o = txps.toObj();
|
||||
o.txps.length.should.equal(2);
|
||||
});
|
||||
it('should filter sent txp', function() {
|
||||
var txps = TxProposals.fromObj({
|
||||
networkName:'livenet',
|
||||
walletId: '123a12',
|
||||
txps: [],
|
||||
});
|
||||
var d = JSON.parse(JSON.stringify(dummyProposal));
|
||||
d.sent=1;
|
||||
txps.txps = {
|
||||
'hola' : dummyProposal,
|
||||
'chau' : d,
|
||||
};
|
||||
var o = txps.toObj();
|
||||
o.txps.length.should.equal(1);
|
||||
});
|
||||
});
|
||||
describe.skip('#merge', function() {
|
||||
it('should merge', function() {
|
||||
var txps = new TxProposals();
|
||||
var d = dummyProposal;
|
||||
txps.merge(d.toObj(),{});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var WalletLock = copay.WalletLock;
|
||||
var PrivateKey = copay.PrivateKey;
|
||||
var Storage = copay.Storage;
|
||||
|
||||
|
||||
var storage;
|
||||
describe('WalletLock model', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
storage = new Storage(requireMock('FakeLocalStorage').storageParams);
|
||||
storage.setPassphrase('mysupercoolpassword');
|
||||
storage.storage.clear();
|
||||
storage.sessionStorage.clear();
|
||||
});
|
||||
|
||||
it('should fail with missing args', function() {
|
||||
(function() {
|
||||
new WalletLock()
|
||||
}).should.throw('Argument');
|
||||
});
|
||||
|
||||
|
||||
it('should fail with missing args (case 2)', function() {
|
||||
(function() {
|
||||
new WalletLock(storage)
|
||||
}).should.throw('Argument');
|
||||
});
|
||||
|
||||
it('should create an instance', function() {
|
||||
var w = new WalletLock(storage, 'id');
|
||||
should.exist(w);
|
||||
});
|
||||
|
||||
|
||||
it('should generate a sessionId with init', function(done) {
|
||||
var w = new WalletLock(storage, 'id');
|
||||
var spy = sinon.spy(storage, 'getSessionId');
|
||||
w.init(function() {
|
||||
spy.calledOnce.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('#keepAlive should call getsessionId if not called before', function(done) {
|
||||
var w = new WalletLock(storage, 'id');
|
||||
var spy = sinon.spy(storage, 'getSessionId');
|
||||
w.keepAlive(function() {
|
||||
spy.calledOnce.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT fail if locked already by me', function(done) {
|
||||
var w = new WalletLock(storage, 'walletId2');
|
||||
w.keepAlive(function() {
|
||||
var w2 = new WalletLock(storage, 'walletId2');
|
||||
w2.init(function() {
|
||||
w2.keepAlive(function() {
|
||||
w.sessionId.should.equal(w2.sessionId);
|
||||
should.exist(w2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('should FAIL if locked by someone else', function(done) {
|
||||
var w = new WalletLock(storage, 'walletId');
|
||||
w.keepAlive(function() {
|
||||
storage.setSessionId('session2', function() {
|
||||
var w2 = new WalletLock(storage, 'walletId');
|
||||
w2.keepAlive(function(locked) {
|
||||
should.exist(locked);
|
||||
locked.message.should.contain('LOCKED');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
|
||||
it('should FAIL if locked by someone else but expired', function(done) {
|
||||
var w = new WalletLock(storage, 'walletId');
|
||||
w.keepAlive(function() {
|
||||
storage.setSessionId('session2', function() {
|
||||
|
||||
var json = JSON.parse(storage.storage.ls['lock::walletId']);
|
||||
json.expireTs -= 3600 * 1000;
|
||||
storage.storage.ls['lock::walletId'] = JSON.stringify(json);
|
||||
var w2 = new WalletLock(storage, 'walletId');
|
||||
w2.keepAlive(function(locked) {
|
||||
w2.sessionId.should.equal('session2');
|
||||
should.not.exist(locked);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
@ -1,409 +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 = {status: 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, "request", function(url, cb) {
|
||||
var res = {statusCode: 200};
|
||||
|
||||
if (url == '/api/addr/2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM') {
|
||||
return setTimeout(function() {
|
||||
var body = JSON.stringify({transactions: [1, 2]});
|
||||
cb(null, res, body);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
if (url == '/api/addr/2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb') {
|
||||
return setTimeout(function() {
|
||||
var body = JSON.stringify({transactions: [3]});
|
||||
cb(null, res, body);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
var body = JSON.stringify({txid: '123123'});
|
||||
cb(null, res, body);
|
||||
}, 0);
|
||||
});
|
||||
|
||||
var addresses = ['2NATQJnaQe2CUKLyhL1zdNkttJM1dUH9HaM', '2NE9hTCffeugo5gQtfB4owq98gyTeWC56yb'];
|
||||
blockchain.getTransactions(addresses, function(err, txs) {
|
||||
chai.expect(err).to.be.null;
|
||||
txs.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, 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 get activity for active addresses', function(done) {
|
||||
var blockchain = new Insight(FAKE_OPTS);
|
||||
|
||||
sinon.stub(blockchain, "getTransactions", function(addresses, 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,323 +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();
|
||||
n.send(null, 'hello', 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 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);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
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]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
var PrivateKey = copay.PrivateKey;
|
||||
var PublicKeyRing = copay.PublicKeyRing;
|
||||
|
||||
var getNewEpk = function() {
|
||||
return new PrivateKey({
|
||||
networkName: 'livenet',
|
||||
})
|
||||
.deriveBIP45Branch()
|
||||
.extendedPublicKeyString();
|
||||
}
|
||||
|
||||
|
||||
describe('Performance tests', function() {
|
||||
describe('PrivateKey', function() {
|
||||
it('should optimize BIP32 private key gen time with cache', function() {
|
||||
var k1 = new PrivateKey();
|
||||
var generateN = 25;
|
||||
var generated = [];
|
||||
var start1 = new Date().getTime();
|
||||
for (var i = 0; i < generateN; i++) {
|
||||
var k = JSON.stringify(k1.get(i, false).storeObj());
|
||||
generated.push(k);
|
||||
}
|
||||
var delta1 = new Date().getTime() - start1;
|
||||
var start2 = new Date().getTime();
|
||||
for (var i = 0; i < generateN; i++) {
|
||||
var k = JSON.stringify(k1.get(i, false).storeObj());
|
||||
generated[i].should.equal(k);
|
||||
}
|
||||
var delta2 = new Date().getTime() - start2;
|
||||
delta2.should.be.below(delta1);
|
||||
});
|
||||
});
|
||||
describe('PublicKeyRing', function() {
|
||||
var maxN = 7;
|
||||
for (var n = 1; n < maxN; n++) {
|
||||
for (var m = 1; m <= n; m++) {
|
||||
if ((m === 3 && n === 5) ||
|
||||
(m === 2 && n === 3)) {
|
||||
var M = m;
|
||||
var N = n;
|
||||
(function(M, N) {
|
||||
it('should optimize BIP32 publickey gen time with cache for ' + M + '-of-' + N, function() {
|
||||
var pkr1 = new PublicKeyRing({
|
||||
totalCopayers: N,
|
||||
requiredCopayers: M
|
||||
});
|
||||
for (var i = 0; i < N; i++) {
|
||||
pkr1.addCopayer(getNewEpk()); // add new random ext public key
|
||||
}
|
||||
var generateN = 5;
|
||||
var generated = [];
|
||||
var start1 = new Date().getTime();
|
||||
for (var i = 0; i < generateN; i++) {
|
||||
var pubKeys = JSON.stringify(pkr1.getPubKeys(i, false));
|
||||
generated.push(pubKeys);
|
||||
}
|
||||
var delta1 = new Date().getTime() - start1;
|
||||
var start2 = new Date().getTime();
|
||||
for (var i = 0; i < generateN; i++) {
|
||||
var pubKeys = JSON.stringify(pkr1.getPubKeys(i, false));
|
||||
generated[i].should.equal(pubKeys);
|
||||
}
|
||||
var delta2 = new Date().getTime() - start2;
|
||||
delta2.should.be.below(delta1);
|
||||
});
|
||||
})(M, N);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue