Browse Source

Merge pull request #6 from matiu/feat/cli-send

WIP: Feat/cli send
activeAddress
Ivan Socolsky 10 years ago
parent
commit
b4f233e601
  1. 34
      app.js
  2. 4
      bit-wallet/bit
  3. 24
      bit-wallet/bit-address
  4. 29
      bit-wallet/bit-addresses
  5. 24
      bit-wallet/bit-balance
  6. 33
      bit-wallet/bit-send
  7. 9
      bit-wallet/bit-status
  8. 166
      lib/clilib.js
  9. 2
      lib/model/wallet.js
  10. 11
      lib/server.js

34
app.js

@ -42,11 +42,11 @@ app.use(bodyParser.json({
var port = process.env.COPAY_PORT || 3001;
var router = express.Router();
function returnError(err, res) {
function returnError(err, res, req) {
if (err instanceof CopayServer.ClientError) {
console.log('[app.js.47]'); //TODO
var status = (err.code == 'NOTAUTHORIZED') ? 401 : 400;
log.error('Err: ' + status + ':' + req.url + ' :' + err.code + ':' + err.message);
res.status(status).json({
code: err.code,
error: err.message,
@ -57,8 +57,12 @@ console.log('[app.js.47]'); //TODO
code = err.code;
message = err.message;
}
var m = message || err.toString();
console.log('[app.js.60]'); //TODO
log.error('Error: ' + req.url + ' :' + code + ':' + m);
res.status(code || 500).json({
error: message || err.toString(),
error: m,
}).end();
}
};
@ -81,7 +85,7 @@ function getServerWithAuth(req, res, cb) {
signature: credentials.signature,
};
CopayServer.getInstanceWithAuth(auth, function(err, server) {
if (err) return returnError(err, res);
if (err) return returnError(err, res, req);
return cb(server);
});
};
@ -89,7 +93,7 @@ function getServerWithAuth(req, res, cb) {
router.post('/v1/wallets/', function(req, res) {
var server = CopayServer.getInstance();
server.createWallet(req.body, function(err, walletId) {
if (err) return returnError(err, res);
if (err) return returnError(err, res, req);
res.json({
walletId: walletId,
@ -101,7 +105,7 @@ 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, result) {
if (err) return returnError(err, res);
if (err) return returnError(err, res, req);
res.json(result);
});
@ -110,16 +114,26 @@ router.post('/v1/wallets/:id/copayers/', function(req, res) {
router.get('/v1/wallets/', function(req, res) {
getServerWithAuth(req, res, function(server) {
server.getWallet({}, function(err, wallet) {
if (err) returnError(err, res);
if (err) returnError(err, res, req);
res.json(wallet);
});
});
});
router.post('/v1/txproposals/', function(req, res) {
getServerWithAuth(req, res, function(server) {
server.createTx(req.body, function(err, txp) {
if (err) return returnError(err, res, req);
res.json(txp);
});
});
});
router.post('/v1/addresses/', function(req, res) {
getServerWithAuth(req, res, function(server) {
server.createAddress(req.body, function(err, address) {
if (err) return returnError(err, res);
if (err) return returnError(err, res, req);
res.json(address);
});
});
@ -128,7 +142,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) return returnError(err, res);
if (err) return returnError(err, res, req);
res.json(addresses);
});
});
@ -137,7 +151,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) return returnError(err, res);
if (err) return returnError(err, res,req);
res.json(balance);
});
});

4
bit-wallet/bit

@ -8,6 +8,10 @@ program
.command('create <walletName> <m-n> [username]', 'creates a wallet')
.command('join <secret> [username]', 'join a wallet')
.command('status', 'get wallet status')
.command('address', 'create a new address from server')
.command('addresses', 'list addresses')
.command('balance', 'wallet balance')
.command('send <address> <amount> <note>', 'send bitcoins')
.parse(process.argv);

24
bit-wallet/bit-address

@ -0,0 +1,24 @@
#!/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('-v,--verbose', 'be verbose')
.parse(process.argv);
var args = program.args;
var cli = new CliLib({
filename: program.config
});
cli.address(function(err, x) {
common.die(err);
console.log('* New Address %s ', x.address);
if (program.verbose)
console.log('* Raw Server Response:\n', x); //TODO
});

29
bit-wallet/bit-addresses

@ -0,0 +1,29 @@
#!/usr/bin/env node
var _ = require('lodash');
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('-v,--verbose', 'be verbose')
.parse(process.argv);
var args = program.args;
var cli = new CliLib({
filename: program.config
});
cli.addresses(function(err, x) {
common.die(err);
console.log('* Addresses:');
_.each(x, function(a){
console.log(' ', a.address);
});
if (program.verbose)
console.log('* Raw Server Response:\n', x); //TODO
});

24
bit-wallet/bit-balance

@ -0,0 +1,24 @@
#!/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('-v,--verbose', 'be verbose')
.parse(process.argv);
var args = program.args;
var cli = new CliLib({
filename: program.config
});
cli.balance(function(err, x) {
common.die(err);
console.log('* Wallet balance', x);
if (program.verbose)
console.log('* Raw Server Response:\n', x); //TODO
});

33
bit-wallet/bit-send

@ -0,0 +1,33 @@
#!/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('-v,--verbose', 'be verbose')
.usage('[options] <address> <amount> <message>')
.parse(process.argv);
var args = program.args;
if (!args[0] || !args[1] || !args[2])
program.help();
var address = args[0];
var amount = args[1];
var message = args[2];
var cli = new CliLib({
filename: program.config
});
cli.send({toAddress: address, amount: amount, message:message}, function(err, x) {
common.die(err);
console.log(' * Tx created: ID %s [%s] RequiredSignatures:',
x.id, x.status, x.requiredSignatures);
if (program.verbose)
console.log('* Raw Server Response:\n', x); //TODO
});

9
bit-wallet/bit-status

@ -7,6 +7,7 @@ var common = require('./common');
program
.version('0.0.1')
.option('-c,--config [file]', 'Wallet config filename')
.option('-v,--verbose', 'be verbose')
.parse(process.argv);
var args = program.args;
@ -14,8 +15,10 @@ var cli = new CliLib({
filename: program.config
});
cli.status(function(err, xx) {
cli.status(function(err, x) {
common.die(err);
console.log(' * Status:', xx);
});
console.log('* Wallet %s [%s]: %d-%d %s ', x.name, x.isTestnet ? 'testnet' : 'livenet', x.m, x.n, x.status);
if (program.verbose)
console.log('* Raw Server Response:\n', x); //TODO
});

166
lib/clilib.js

@ -13,7 +13,11 @@ var SignUtils = require('./signutils');
var BASE_URL = 'http://localhost:3001/copay/api/';
function _createProposalOpts(opts, signingKey) {
var msg = opts.toAddress + '|' + opts.amount + '|' + opts.message;
opts.proposalSignature = SignUtils.sign(msg, signingKey);
return opts;
};
function _getUrl(path) {
return BASE_URL + path;
@ -21,7 +25,13 @@ function _getUrl(path) {
function _parseError(body) {
if (_.isString(body)) {
body = JSON.parse(body);
try {
body = JSON.parse(body);
} catch (e) {
body = {
error: body
};
}
}
var code = body.code || 'ERROR';
var message = body.error || 'There was an unknown error processing the request';
@ -61,6 +71,9 @@ CliLib.prototype._loadAndCheck = function() {
log.error('Wallet file not found.');
process.exit(1);
}
// TODO
delete data['verified'];
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);
@ -185,7 +198,6 @@ 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);
@ -205,12 +217,19 @@ CliLib.prototype.status = function(cb) {
return cb('Request error');
}
var wallet = body;
// TODO
//console.log('[clilib.js.214:wallet:]',wallet); //TODO
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) {
console.log('[clilib.js.224]', copayer.xPubKey, copayer.xPubKeySignature, pubKey); //TODO
if (!SignUtils.verify(copayer.xPubKey, copayer.xPubKeySignature, pubKey)) {
console.log('[clilib.js.227] FAKE'); //TODO
fake.push(copayer);
}
});
@ -227,10 +246,43 @@ CliLib.prototype.status = function(cb) {
});
};
CliLib.prototype.send = function(addressTo, amount, message, cb) {
/**
* send
*
* @param inArgs
* @param inArgs.toAddress
* @param inArgs.amount
* @param inArgs.message
*/
CliLib.prototype.send = function(inArgs, cb) {
var self = this;
var data = this._loadAndCheck();
var args = _createProposalOpts(inArgs, data.signingPrivKey);
var url = '/v1/txproposals/';
var signature = _signRequest(url, args, data.signingPrivKey);
request({
headers: {
'x-identity': data.copayerId,
'x-signature': signature,
},
method: 'post',
url: _getUrl(url),
body: args,
json: true,
}, function(err, res, body) {
if (err) return cb(err);
if (res.statusCode != 200) {
_parseError(body);
return cb('Request error');
}
return cb(null, body);
});
};
// TODO check change address
CliLib.prototype.sign = function(proposalId, cb) {
};
@ -239,12 +291,118 @@ CliLib.prototype.reject = function(proposalId, cb) {
};
// Get addresses
CliLib.prototype.addresses = function(cb) {
var self = this;
var data = this._loadAndCheck();
var url = '/v1/addresses/';
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');
}
return cb(null, body);
});
};
// Creates a new address
// TODO: verify derivation!!
CliLib.prototype.address = function(cb) {
var self = this;
var data = this._loadAndCheck();
var url = '/v1/addresses/';
var signature = _signRequest(url, {}, data.signingPrivKey);
request({
headers: {
'x-identity': data.copayerId,
'x-signature': signature,
},
method: 'post',
url: _getUrl(url),
json: true,
}, function(err, res, body) {
if (err) return cb(err);
if (res.statusCode != 200) {
_parseError(body);
return cb('Request error');
}
return cb(null, body);
});
};
CliLib.prototype.history = function(limit, cb) {
};
CliLib.prototype.balance = function(cb) {
var self = this;
var data = this._loadAndCheck();
var url = '/v1/balance/';
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');
}
return cb(null, body);
});
};
CliLib.prototype.txProposals = function(cb) {
var self = this;
var data = this._loadAndCheck();
var url = '/v1/txproposals/';
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');
}
return cb(null, body);
});
};
module.exports = CliLib;

2
lib/model/wallet.js

@ -28,7 +28,7 @@ function Wallet(opts) {
this.addressIndex = 0;
this.copayers = [];
this.pubKey = opts.pubKey;
this.isTestnet = false;
this.isTestnet = opts.isTestnet;
this.addressManager = new AddressManager();
};

11
lib/server.js

@ -119,12 +119,12 @@ CopayServer.prototype.createWallet = function(opts, cb) {
name: opts.name,
m: opts.m,
n: opts.n,
network: opts.network || 'livenet',
isTestnet: network === 'testnet',
pubKey: pubKey,
});
self.storage.storeWallet(wallet, function(err) {
log.debug('Wallet created', wallet.id);
log.debug('Wallet created', wallet.id, network);
return cb(err, wallet.id);
});
};
@ -332,12 +332,14 @@ CopayServer.prototype._getUtxos = function(cb) {
// Get addresses for this wallet
self.storage.fetchAddresses(self.walletId, function(err, addresses) {
console.log('[server.js.334:addresses:]',addresses); //TODO
if (err) return cb(err);
if (addresses.length == 0) return cb(new ClientError('The wallet has no addresses'));
if (addresses.length == 0)
return cb(null, []);
var addressStrs = _.pluck(addresses, 'address');
var addressToPath = _.indexBy(addresses, 'address'); // TODO : check performance
var networkName = Bitcore.Address(addressStrs[0]).toObject().networkName;
var networkName = Bitcore.Address(addressStrs[0]).toObject().network;
var bc = self._getBlockExplorer('insight', networkName);
bc.getUnspentUtxos(addressStrs, function(err, utxos) {
@ -387,6 +389,7 @@ CopayServer.prototype.getBalance = function(opts, cb) {
var self = this;
self._getUtxos(function(err, utxos) {
console.log('[server.js.390:utxos:]',utxos); //TODO
if (err) return cb(err);
var balance = {};

Loading…
Cancel
Save