Merge pull request #4 from isocolsky/clilib

Client lib
This commit is contained in:
Matias Alejo Garcia 2015-02-13 11:30:51 -03:00
commit 7177baf043
12 changed files with 449 additions and 177 deletions

24
app.js
View File

@ -44,6 +44,8 @@ var router = express.Router();
function returnError(err, res) {
if (err instanceof CopayServer.ClientError) {
console.log('[app.js.47]'); //TODO
var status = (err.code == 'NOTAUTHORIZED') ? 401 : 400;
res.status(status).json({
code: err.code,
@ -78,7 +80,6 @@ function getServerWithAuth(req, res, cb) {
message: req.url + '|' + JSON.stringify(req.body),
signature: credentials.signature,
};
CopayServer.getInstanceWithAuth(auth, function(err, server) {
if (err) return returnError(err, res);
return cb(server);
@ -87,26 +88,27 @@ function getServerWithAuth(req, res, cb) {
router.post('/v1/wallets/', function(req, res) {
var server = CopayServer.getInstance();
server.createWallet(req.body, function(err, wallet) {
if (err) returnError(err, res);
server.createWallet(req.body, function(err, walletId) {
if (err) return returnError(err, res);
res.json(wallet);
res.json({
walletId: walletId,
});
});
});
router.post('/v1/wallets/:id/copayers/', function(req, res) {
req.body.walletId = req.params['id'];
var server = CopayServer.getInstance();
server.joinWallet(req.body, function(err) {
if (err) returnError(err, res);
server.joinWallet(req.body, function(err, result) {
if (err) return returnError(err, res);
res.end();
res.json(result);
});
});
router.get('/v1/wallets/', function(req, res) {
getServerWithAuth(req, res, function(server) {
if (err) return returnError(err, res);
server.getWallet({}, function(err, wallet) {
if (err) returnError(err, res);
res.json(wallet);
@ -117,7 +119,7 @@ router.get('/v1/wallets/', function(req, res) {
router.post('/v1/addresses/', function(req, res) {
getServerWithAuth(req, res, function(server) {
server.createAddress(req.body, function(err, address) {
if (err) returnError(err, res);
if (err) return returnError(err, res);
res.json(address);
});
});
@ -126,7 +128,7 @@ router.post('/v1/addresses/', function(req, res) {
router.get('/v1/addresses/', function(req, res) {
getServerWithAuth(req, res, function(server) {
server.getAddresses({}, function(err, addresses) {
if (err) returnError(err, res);
if (err) return returnError(err, res);
res.json(addresses);
});
});
@ -135,7 +137,7 @@ router.get('/v1/addresses/', function(req, res) {
router.get('/v1/balance/', function(req, res) {
getServerWithAuth(req, res, function(server) {
server.getBalance({}, function(err, balance) {
if (err) returnError(err, res);
if (err) return returnError(err, res);
res.json(balance);
});
});

14
bit-wallet/bit Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env node
var program = require('commander');
var cli = require('../lib/clilib.js');
program
.version('0.0.1')
.command('create <walletName> <m-n> [username]', 'creates a wallet')
.command('join <secret> [username]', 'join a wallet')
.command('status', 'get wallet status')
.parse(process.argv);

32
bit-wallet/bit-create Executable file
View File

@ -0,0 +1,32 @@
#!/usr/bin/env node
var program = require('commander');
var CliLib = require('../lib/clilib.js');
var common = require('./common');
program
.version('0.0.1')
.option('-c,--config [file]', 'Wallet config filename')
.option('-n,--network [networkname]', 'livenet|testnet', String, 'livenet')
.usage('[options] <walletName> <m-n> [copayerName]')
.parse(process.argv);
var args = program.args;
if (!args[0])
program.help();
var walletName = args[0];
var copayerName = args[2] || process.env.USER;
var network = program.network;
var mn = common.parseMN(args[1]);
var cli = new CliLib({
filename: program.config
});
cli.createWallet(walletName, copayerName, mn[0], mn[1], network, function(err, secret) {
common.die(err);
console.log(' * Wallet Created.');
console.log(' - Secret to share:\n\t' + secret);
});

10
bit-wallet/bit-create.js Normal file
View File

@ -0,0 +1,10 @@
#!/usr/bin/env node
var program = require('commander');
var cli = require('../lib/clilib.js');
program
.version('0.0.1')
.parse(process.argv);

27
bit-wallet/bit-join Normal file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env node
var program = require('commander');
var CliLib = require('../lib/clilib.js');
var common = require('./common');
program
.version('0.0.1')
.option('-c,--config [file]', 'Wallet config filename')
.usage('[options] <secret> [copayerName]')
.parse(process.argv);
var args = program.args;
if (!args[0])
program.help();
var secret = args[0];
var copayerName = args[1] || process.env.USER;
var cli = new CliLib({
filename: program.config
});
cli.joinWallet(secret, copayerName, function(err, xx) {
common.die(err);
console.log(' * Wallet Joined.', xx || '');
});

21
bit-wallet/bit-status Normal file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env node
var program = require('commander');
var CliLib = require('../lib/clilib.js');
var common = require('./common');
program
.version('0.0.1')
.option('-c,--config [file]', 'Wallet config filename')
.parse(process.argv);
var args = program.args;
var cli = new CliLib({
filename: program.config
});
cli.status(function(err, xx) {
common.die(err);
console.log(' * Status:', xx);
});

27
bit-wallet/common.js Normal file
View File

@ -0,0 +1,27 @@
var common = function() {};
var die = common.die = function(err) {
if (err) {
console.error(err);
process.exit(1);
}
};
common.parseMN = function(MN) {
if (!MN)
die('No m-n parameter');
var mn = MN.split('-');
var m = parseInt(mn[0]);
var n = parseInt(mn[1]);
if (!m || ! n) {
die('Bad m-n parameter');
}
return [m, n];
};
module.exports = common;

30
cli.js Normal file
View File

@ -0,0 +1,30 @@
var _ = require('lodash');
var async = require('async');
var log = require('npmlog');
var fs = require('fs');
var CliLib = require('./lib/clilib');
try {
fs.unlinkSync('copay.dat');
} catch (e) {}
var cli = new CliLib({
filename: 'copay.dat'
});
cli.createWallet('my wallet', 'me', 1, 1, 'testnet', function(err, secret) {
if (err) {
console.log(err);
process.exit();
}
cli.status(function(err, status) {
if (err) {
console.log(err);
process.exit();
}
console.log(status);
})
});

View File

@ -4,7 +4,6 @@ var _ = require('lodash');
var async = require('async');
var log = require('npmlog');
var request = require('request')
var commander = require('commander')
log.debug = log.verbose;
log.level = 'debug';
var fs = require('fs')
@ -14,132 +13,238 @@ var SignUtils = require('./signutils');
var BASE_URL = 'http://localhost:3001/copay/api/';
var cli = {};
function _getUrl(path) {
return BASE_URL + path;
};
function signRequest(url, args) {
function _parseError(body) {
if (_.isString(body)) {
body = JSON.parse(body);
}
var code = body.code || 'ERROR';
var message = body.error || 'There was an unknown error processing the request';
log.error(code, message);
};
function save(data) {
fs.writeFileSync('./.bit', JSON.stringify(data));
function _signRequest(url, args, privKey) {
var message = url + '|' + JSON.stringify(args);
return SignUtils.sign(message, privKey);
};
function load() {
function _createXPrivKey() {
return new Bitcore.HDPrivateKey().toString();
};
function CliLib(opts) {
if (!opts.filename) {
throw new Error('Please set the config filename');
}
this.filename = opts.filename;
};
CliLib.prototype._save = function(data) {
fs.writeFileSync(this.filename, JSON.stringify(data));
};
CliLib.prototype._load = function() {
try {
return JSON.parse(fs.readFileSync('./.bit'));
return JSON.parse(fs.readFileSync(this.filename));
} catch (ex) {}
};
clilib.createWallet = function(walletName, copayerName, m, n, cb) {
var data = load();
CliLib.prototype._loadAndCheck = function() {
var data = this._load();
if (!data) {
data = {};
data.xPrivKey = new Bitcore.HDPrivateKey().toString();
data.m = m;
log.error('Wallet file not found.');
process.exit(1);
}
if (data.verified == 'corrupt') {
log.error('The wallet is tagged as corrupt. Some of the copayers cannot be verified to have known the wallet secret.');
process.exit(1);
}
if (data.n > 1) {
var pkrComplete = data.publicKeyRing && data.m && data.publicKeyRing.length === data.n;
if (!pkrComplete) {
log.warn('The file ' + this.filename + ' is incomplete. It will allow you to operate with the wallet but it should not be trusted as a backup. Please wait for all copayers to join the wallet and run the tool with -export flag.')
}
}
return data;
};
CliLib.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) {
var self = this;
var data = this._load();
if (data) return cb('File ' + this.filename + ' already contains a wallet');
// Generate wallet key pair to verify copayers
var privKey = new Bitcore.PrivateKey();
var pubKey = privKey.toPublicKey();
data = {
xPrivKey: _createXPrivKey(),
m: m,
n: n,
walletPrivKey: privKey.toString(),
};
var args = {
name: walletName,
m: m,
n: n,
pubKey: pubKey.toString(),
network: network || 'livenet',
};
request({
method: 'post',
url: _getUrl('v1/wallets'),
url: _getUrl('/v1/wallets/'),
body: args,
json: true,
}, function(err, res, body) {
if (err) return cb(err);
var walletId = body;
var secret = walletId + '|' + privKey.toString();
if (res.statusCode != 200) {
_parseError(body);
return cb('Request error');
}
joinWallet(secret, copayerName, function(err) {
var walletId = body.walletId;
var secret = walletId + ':' + privKey.toString();
data.secret = secret;
self._save(data);
self._joinWallet(data, secret, copayerName, function(err) {
if (err) return cb(err);
save(data);
return cb(null, secret);
return cb(null, data.secret);
});
});
};
clilib.joinWallet = function(secret, copayerName, cb) {
var data = load();
if (!data) {
data = {};
data.xPrivKey = new Bitcore.HDPrivateKey().toString();
}
var secretSplit = secret.split('|');
CliLib.prototype._joinWallet = function(data, secret, copayerName, cb) {
var self = this;
var secretSplit = secret.split(':');
var walletId = secretSplit[0];
var privKey = Bitcore.PrivateKey.fromString(secretSplit[1]);
var pubKey = privKey.toPublicKey();
var xPubKey = new Bitcore.HDPublicKey(data.xPrivKey).toString();
var xPubKeySignature = SignUtils.sign(xPubKey, privKey);
var xPubKey = new Bitcore.HDPublicKey(data.xPrivKey);
var xPubKeySignature = SignUtils.sign(xPubKey.toString(), privKey);
var signingPrivKey = (new Bitcore.HDPrivateKey(data.xPrivKey)).derive('m/1/0').privateKey;
var args = {
walletId: walletId,
name: copayerName,
xPubKey: xPubKey,
xPubKey: xPubKey.toString(),
xPubKeySignature: xPubKeySignature,
};
request({
method: 'post',
url: _getUrl('v1/wallets/' + walletId + '/copayers'),
url: _getUrl('/v1/wallets/' + walletId + '/copayers'),
body: args,
json: true,
}, function(err, res, body) {
if (err) return cb(err);
if (res.statusCode != 200) {
_parseError(body);
return cb('Request error');
}
var copayerId = body;
data.copayerId = copayerId;
save(data);
return status(cb);
});
};
var wallet = body.wallet;
data.copayerId = body.copayerId;
data.signingPrivKey = signingPrivKey.toString();
data.m = wallet.m;
data.n = wallet.n;
data.publicKeyRing = wallet.publicKeyRing;
self._save(data);
clilib.status = function(cb) {
request({
method: 'get',
url: _getUrl('v1/dump/'),
}, function(err, res, body) {
if (err) return cb(err);
console.log(body);
return cb();
});
};
clilib.send = function(addressTo, amount, message, cb) {
CliLib.prototype.joinWallet = function(secret, copayerName, cb) {
var self = this;
var data = this._load();
if (data) return cb('File ' + this.filename + ' already contains a wallet');
data = {
xPrivKey: _createXPrivKey(),
};
self._joinWallet(data, secret, copayerName, cb);
};
CliLib.prototype.status = function(cb) {
var self = this;
var data = this._loadAndCheck();
console.log('[clilib.js.180:data:]', data); //TODO
var url = '/v1/wallets/';
var signature = _signRequest(url, {}, data.signingPrivKey);
request({
headers: {
'x-identity': data.copayerId,
'x-signature': signature,
},
method: 'get',
url: _getUrl(url),
json: true,
}, function(err, res, body) {
if (err) return cb(err);
if (res.statusCode != 200) {
_parseError(body);
return cb('Request error');
}
var wallet = body;
if (wallet.n > 0 && wallet.status === 'complete' && !data.verified) {
var pubKey = Bitcore.PrivateKey.fromString(data.walletPrivKey).toPublicKey().toString();
var fake = [];
_.each(wallet.copayers, function(copayer) {
if (!SignUtils.verify(copayer.xPubKey, copayer.xPubKeySignature, pubKey)) {
fake.push(copayer);
}
});
if (fake.length > 0) {
log.error('Some copayers in the wallet could not be verified to have known the wallet secret');
data.verified = 'corrupt';
} else {
data.verified = 'ok';
}
self._save(data);
}
return cb(null, wallet);
});
};
CliLib.prototype.send = function(addressTo, amount, message, cb) {
};
clilib.sign = function(proposalId, cb) {
CliLib.prototype.sign = function(proposalId, cb) {
};
clilib.reject = function(proposalId, cb) {
CliLib.prototype.reject = function(proposalId, cb) {
};
clilib.address = function(cb) {
CliLib.prototype.address = function(cb) {
};
clilib.history = function(limit, cb) {
CliLib.prototype.history = function(limit, cb) {
};
module.exports = clilib;
module.exports = CliLib;

View File

@ -221,11 +221,17 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
wallet.addCopayer(copayer);
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
if (err) return cb(err);
self._notify('NewCopayer', {
walletId: opts.walletId,
copayerId: copayer.id,
copayerName: copayer.name,
});
return cb(null, {
copayerId: copayer.id,
wallet: wallet
});
return cb(err, copayer.id);
});
});
});

View File

@ -5,6 +5,7 @@ var levelup = require('levelup');
var async = require('async');
var $ = require('preconditions').singleton();
var log = require('npmlog');
var util = require('util');
log.debug = log.verbose;
var Wallet = require('./model/wallet');
@ -195,8 +196,8 @@ Storage.prototype.fetchNotifications = function(walletId, opts, cb) {
var txs = [];
opts = opts || {};
opts.limit = _.isNumber(opts.limit) ? parseInt(opts.limit) : -1;
opts.minTs = _.isNumber(opts.minTs) ? zeroPad(opts.minTs,11) : 0;
opts.maxTs = _.isNumber(opts.maxTs) ? zeroPad(opts.maxTs,11) : MAX_TS;
opts.minTs = _.isNumber(opts.minTs) ? zeroPad(opts.minTs, 11) : 0;
opts.maxTs = _.isNumber(opts.maxTs) ? zeroPad(opts.maxTs, 11) : MAX_TS;
var key = KEY.NOTIFICATION(walletId, opts.minTs);
var endkey = KEY.NOTIFICATION(walletId, opts.maxTs);
@ -380,7 +381,11 @@ Storage.prototype._dump = function(cb, fn) {
fn = fn || console.log;
this.db.readStream()
.on('data', fn)
.on('data', function(data) {
fn(util.inspect(data, {
depth: 10
}));
})
.on('end', function() {
if (cb) return cb();
});

View File

@ -57,8 +57,8 @@ helpers.createAndJoinWallet = function(m, n, cb) {
xPubKeySignature: TestData.copayers[i].xPubKeySignature,
};
server.joinWallet(copayerOpts, function(err, copayerId) {
copayerIds.push(copayerId);
server.joinWallet(copayerOpts, function(err, result) {
copayerIds.push(result.copayerId);
return cb(err);
});
}, function(err) {
@ -66,7 +66,7 @@ helpers.createAndJoinWallet = function(m, n, cb) {
helpers.getAuthServer(copayerIds[0], function(s) {
s.getWallet({}, function(err, w) {
cb(s, w, _.take(TestData.copayers, w.n), copayerIds);
cb(s, w);
});
});
});
@ -193,8 +193,8 @@ describe('Copay server', function() {
beforeEach(function() {});
it('should get server instance for existing copayer', function(done) {
helpers.createAndJoinWallet(1, 2, function(s, wallet, copayers, copayerIds) {
var xpriv = copayers[0].xPrivKey;
helpers.createAndJoinWallet(1, 2, function(s, wallet) {
var xpriv = TestData.copayers[0].xPrivKey;
var priv = Bitcore.HDPrivateKey
.fromString(xpriv)
.derive('m/1/0')
@ -205,7 +205,7 @@ describe('Copay server', function() {
var sig = SignUtils.sign(message, priv);
CopayServer.getInstanceWithAuth({
copayerId: copayerIds[0],
copayerId: wallet.copayers[0].id,
message: message,
signature: sig,
}, function(err, server) {
@ -218,22 +218,24 @@ describe('Copay server', function() {
it('should fail when requesting for non-existent copayer', function(done) {
CopayServer.getInstanceWithAuth({
copayerId: 'ads',
message: 'dummy',
signature: 'dummy',
message: TestData.message.text,
signature: TestData.message.signature,
}, function(err, server) {
err.should.contain('Copayer not found');
err.code.should.equal('NOTAUTHORIZED');
err.message.should.contain('Copayer not found');
done();
});
});
it('should fail when message signature cannot be verified', function(done) {
helpers.createAndJoinWallet(1, 2, function(s, wallet, copayers, copayerIds) {
helpers.createAndJoinWallet(1, 2, function(s, wallet) {
CopayServer.getInstanceWithAuth({
copayerId: copayerIds[0],
copayerId: wallet.copayers[0].id,
message: 'dummy',
signature: 'dummy',
}, function(err, server) {
err.should.contain('Invalid signature');
err.code.should.equal('NOTAUTHORIZED');
err.message.should.contain('Invalid signature');
done();
});
});
@ -343,8 +345,9 @@ describe('Copay server', function() {
xPubKey: TestData.copayers[0].xPubKey,
xPubKeySignature: TestData.copayers[0].xPubKeySignature,
};
server.joinWallet(copayerOpts, function(err, copayerId) {
server.joinWallet(copayerOpts, function(err, result) {
should.not.exist(err);
var copayerId = result.copayerId;
helpers.getAuthServer(copayerId, function(server) {
server.getWallet({}, function(err, wallet) {
wallet.id.should.equal(walletId);
@ -365,8 +368,8 @@ describe('Copay server', function() {
xPubKey: TestData.copayers[0].xPubKey,
xPubKeySignature: TestData.copayers[0].xPubKeySignature,
};
server.joinWallet(copayerOpts, function(err, copayerId) {
should.not.exist(copayerId);
server.joinWallet(copayerOpts, function(err, result) {
should.not.exist(result);
err.should.exist;
err.message.should.contain('name');
done();
@ -546,9 +549,9 @@ describe('Copay server', function() {
xPubKey: TestData.copayers[0].xPubKey,
xPubKeySignature: TestData.copayers[0].xPubKeySignature,
};
server.joinWallet(copayerOpts, function(err, copayerId) {
server.joinWallet(copayerOpts, function(err, result) {
should.not.exist(err);
helpers.getAuthServer(copayerId, function(server) {
helpers.getAuthServer(result.copayerId, function(server) {
server.createAddress({}, function(err, address) {
should.not.exist(address);
err.should.exist;
@ -598,12 +601,11 @@ describe('Copay server', function() {
});
describe('#createTx', function() {
var server, wallet, copayerPriv;
var server, wallet;
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 3, function(s, w, c) {
helpers.createAndJoinWallet(2, 3, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
done();
});
@ -613,7 +615,7 @@ describe('Copay server', function() {
it('should create a tx', function(done) {
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
@ -650,10 +652,10 @@ describe('Copay server', function() {
xPubKey: TestData.copayers[0].xPubKey,
xPubKeySignature: TestData.copayers[0].xPubKeySignature,
};
server.joinWallet(copayerOpts, function(err, copayerId) {
server.joinWallet(copayerOpts, function(err, result) {
should.not.exist(err);
helpers.getAuthServer(copayerId, function(server, wallet) {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, copayerPriv[0].privKey);
helpers.getAuthServer(result.copayerId, function(server, wallet) {
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(tx);
err.should.exist;
@ -668,7 +670,7 @@ describe('Copay server', function() {
it('should fail to create tx for address invalid address', function(done) {
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('invalid address', 80, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('invalid address', 80, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(tx);
@ -683,7 +685,7 @@ describe('Copay server', function() {
it('should fail to create tx for address of different network', function(done) {
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('myE38JHdxmQcTJGP1ZiX4BiGhDxMJDvLJD', 80, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('myE38JHdxmQcTJGP1ZiX4BiGhDxMJDvLJD', 80, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(tx);
@ -698,7 +700,7 @@ describe('Copay server', function() {
it('should fail to create tx when insufficient funds', function(done) {
helpers.createUtxos(server, wallet, [100], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 120, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 120, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
err.code.should.equal('INSUFFICIENTFUNDS');
err.message.should.equal('Insufficient funds');
@ -723,11 +725,11 @@ describe('Copay server', function() {
it('should create tx when there is a pending tx and enough UTXOs', function(done) {
helpers.createUtxos(server, wallet, [10.1, 10.2, 10.3], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
var txOpts2 = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, copayerPriv[0].privKey);
var txOpts2 = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, null, TestData.copayers[0].privKey);
server.createTx(txOpts2, function(err, tx) {
should.not.exist(err);
tx.should.exist;
@ -749,11 +751,11 @@ describe('Copay server', function() {
it('should fail to create tx when there is a pending tx and not enough UTXOs', function(done) {
helpers.createUtxos(server, wallet, [10.1, 10.2, 10.3], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 12, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
var txOpts2 = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 24, null, copayerPriv[0].privKey);
var txOpts2 = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 24, null, TestData.copayers[0].privKey);
server.createTx(txOpts2, function(err, tx) {
err.code.should.equal('INSUFFICIENTFUNDS');
err.message.should.equal('Insufficient funds');
@ -783,7 +785,7 @@ describe('Copay server', function() {
should.not.exist(err);
balance.totalAmount.should.equal(helpers.toSatoshi(N * 100));
balance.lockedAmount.should.equal(helpers.toSatoshi(0));
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, null, TestData.copayers[0].privKey);
async.map(_.range(N), function(i, cb) {
server.createTx(txOpts, function(err, tx) {
cb(err, tx);
@ -808,18 +810,16 @@ describe('Copay server', function() {
describe('#rejectTx', function() {
var server, wallet, copayerPriv, txid, copayerIds;
var server, wallet, txid;
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 3, function(s, w, c, ids) {
helpers.createAndJoinWallet(2, 3, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
copayerIds = ids;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, _.range(1, 9), function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
@ -847,8 +847,8 @@ describe('Copay server', function() {
var actors = tx.getActors();
actors.length.should.equal(1);
actors[0].should.equal(copayerIds[0]);
tx.getActionBy(copayerIds[0]).type.should.equal('reject');
actors[0].should.equal(wallet.copayers[0].id);
tx.getActionBy(wallet.copayers[0].id).type.should.equal('reject');
done();
});
@ -858,18 +858,16 @@ describe('Copay server', function() {
});
describe('#signTx', function() {
var server, wallet, copayerPriv, txid, copayerIds;
var server, wallet, txid;
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 3, function(s, w, c, ids) {
helpers.createAndJoinWallet(2, 3, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
copayerIds = ids;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, _.range(1, 9), function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
tx.should.exist;
@ -899,8 +897,8 @@ describe('Copay server', function() {
var actors = tx.getActors();
actors.length.should.equal(1);
actors[0].should.equal(copayerIds[0]);
tx.getActionBy(copayerIds[0]).type.should.equal('accept');
actors[0].should.equal(wallet.copayers[0].id);
tx.getActionBy(wallet.copayers[0].id).type.should.equal('accept');
done();
});
@ -1002,12 +1000,11 @@ describe('Copay server', function() {
describe('#signTx and broadcast', function() {
var server, wallet, copayerPriv, utxos;
var server, wallet, utxos;
beforeEach(function(done) {
helpers.createAndJoinWallet(1, 1, function(s, w, c) {
helpers.createAndJoinWallet(1, 1, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, _.range(1, 9), function(inutxos) {
utxos = inutxos;
@ -1019,7 +1016,7 @@ describe('Copay server', function() {
it('should sign and broadcast a tx', function(done) {
helpers.stubBlockExplorer(server, utxos, '1122334455');
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, txp) {
should.not.exist(err);
txp.should.exist;
@ -1045,7 +1042,7 @@ describe('Copay server', function() {
it('should keep tx as *accepted* if unable to broadcast it', function(done) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, null, TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, txp) {
should.not.exist(err);
txp.should.exist;
@ -1076,12 +1073,11 @@ describe('Copay server', function() {
});
describe('Tx proposal workflow', function() {
var server, wallet, copayerPriv, utxos;
var server, wallet, utxos;
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 3, function(s, w, c) {
helpers.createAndJoinWallet(2, 3, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, _.range(1, 9), function(inutxos) {
utxos = inutxos;
@ -1093,7 +1089,7 @@ describe('Copay server', function() {
it('other copayers should see pending proposal created by one copayer', function(done) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 10, 'some message', TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, txp) {
should.not.exist(err);
should.exist.txp;
@ -1119,21 +1115,20 @@ describe('Copay server', function() {
});
describe('#getTxs', function() {
var server, wallet, copayerPriv, clock;
var server, wallet, clock;
beforeEach(function(done) {
if (server) return done();
this.timeout(5000);
console.log('\tCreating TXS...');
clock = sinon.useFakeTimers();
helpers.createAndJoinWallet(1, 1, function(s, w, c) {
helpers.createAndJoinWallet(1, 1, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, _.range(10), function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.1, null, TestData.copayers[0].privKey);
async.eachSeries(_.range(10), function(i, next) {
clock.tick(10000);
server.createTx(txOpts, function(err, tx) {
@ -1211,19 +1206,18 @@ describe('Copay server', function() {
describe('Notifications', function() {
var server, wallet, copayerPriv;
var server, wallet;
beforeEach(function(done) {
if (server) return done();
console.log('\tCreating TXS...');
helpers.createAndJoinWallet(1, 1, function(s, w, c) {
helpers.createAndJoinWallet(1, 1, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, helpers.toSatoshi(_.range(4)), function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.01, null, copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.01, null, TestData.copayers[0].privKey);
async.eachSeries(_.range(3), function(i, next) {
server.createTx(txOpts, function(err, tx) {
should.not.exist(err);
@ -1253,8 +1247,8 @@ describe('Copay server', function() {
server.getNotifications({
limit: 5,
reverse: true,
maxTs: Date.now()/1000,
minTs: Date.now()/1000-1000,
maxTs: Date.now() / 1000,
minTs: Date.now() / 1000 - 1000,
}, function(err, notifications) {
should.not.exist(err);
var types = _.pluck(notifications, 'type');
@ -1436,16 +1430,15 @@ describe('Copay server', function() {
describe('#removePendingTx', function() {
var server, wallet, copayerPriv, txp;
var server, wallet, txp;
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 3, function(s, w, c) {
helpers.createAndJoinWallet(2, 3, function(s, w) {
server = s;
wallet = w;
copayerPriv = c;
server.createAddress({}, function(err, address) {
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
helpers.stubBlockExplorer(server, utxos);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', copayerPriv[0].privKey);
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey);
server.createTx(txOpts, function(err, tx) {
server.getPendingTxs({}, function(err, txs) {
txp = txs[0];