commit
b63b27280e
12
app.js
12
app.js
|
@ -207,7 +207,17 @@ router.post('/v1/txproposals/:id/signatures/', function(req, res) {
|
||||||
router.post('/v1/txproposals/:id/rejections', function(req, res) {
|
router.post('/v1/txproposals/:id/rejections', function(req, res) {
|
||||||
getServerWithAuth(req, res, function(server) {
|
getServerWithAuth(req, res, function(server) {
|
||||||
req.body.txProposalId = req.params['id'];
|
req.body.txProposalId = req.params['id'];
|
||||||
server.signTx(req.body, function(err, txp) {
|
server.rejectTx(req.body, function(err, txp) {
|
||||||
|
if (err) return returnError(err, res, req);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/v1/txproposals/:id/', function(req, res) {
|
||||||
|
getServerWithAuth(req, res, function(server) {
|
||||||
|
req.body.txProposalId = req.params['id'];
|
||||||
|
server.removePendingTx(req.body, function(err) {
|
||||||
if (err) return returnError(err, res, req);
|
if (err) return returnError(err, res, req);
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,8 +11,9 @@ program
|
||||||
.command('addresses', 'list addresses')
|
.command('addresses', 'list addresses')
|
||||||
.command('balance', 'wallet balance')
|
.command('balance', 'wallet balance')
|
||||||
.command('send <address> <amount> <note>', 'send bitcoins')
|
.command('send <address> <amount> <note>', 'send bitcoins')
|
||||||
.command('sign <txpId>', 'sign a Transaction Proposal')
|
.command('sign <txpId>', 'sign a transaction proposal')
|
||||||
.command('reject <txpId>', 'reject a Transaction Proposal')
|
.command('reject <txpId> [reason]', 'reject a transaction proposal')
|
||||||
|
.command('rm <txpId>', 'remove a transaction proposal')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,43 +9,32 @@ program
|
||||||
.version('0.0.1')
|
.version('0.0.1')
|
||||||
.option('-c,--config [file]', 'Wallet config filename')
|
.option('-c,--config [file]', 'Wallet config filename')
|
||||||
.option('-v,--verbose', 'be verbose')
|
.option('-v,--verbose', 'be verbose')
|
||||||
.usage('[options] <txpid>')
|
.usage('[options] <txpid> [reason]')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
var args = program.args;
|
var args = program.args;
|
||||||
if (!args[0])
|
if (!args[0])
|
||||||
program.help();
|
program.help();
|
||||||
|
|
||||||
var txpid = args[0];
|
var txpid = args[0];
|
||||||
|
var reason = args[1] || '';
|
||||||
|
|
||||||
var cli = new Client({
|
var cli = new Client({
|
||||||
filename: program.config
|
filename: program.config
|
||||||
});
|
});
|
||||||
|
|
||||||
cli.getTxProposals({}, function(err, x) {
|
cli.getTxProposals({}, function(err, txps) {
|
||||||
common.die(err);
|
common.die(err);
|
||||||
|
|
||||||
if (program.verbose)
|
if (program.verbose)
|
||||||
console.log('* Raw Server Response:\n', x); //TODO
|
console.log('* Raw Server Response:\n', txps); //TODO
|
||||||
|
|
||||||
var txps = _.filter(x, function(x) {
|
var txp = common.findOneTxProposal(txps, txpid);
|
||||||
return _.endsWith(common.shortID(x.id), txpid);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!txps.length)
|
cli.rejectTxProposal(txp, reason, function(err, tx) {
|
||||||
common.die('Could not find TX Proposal:' + txpid);
|
|
||||||
|
|
||||||
if (txps.length > 1)
|
|
||||||
common.die('More than one TX Proposals match:' + txpid + ' : ' + _.map(txps, function(x) {
|
|
||||||
return x.id;
|
|
||||||
}).join(' '));;
|
|
||||||
|
|
||||||
var txp = txps[0];
|
|
||||||
cli.rejectTxProposal(txp, function(err, x) {
|
|
||||||
common.die(err);
|
common.die(err);
|
||||||
|
|
||||||
if (program.verbose)
|
if (program.verbose)
|
||||||
console.log('* Raw Server Response:\n', x); //TODO
|
console.log('* Raw Server Response:\n', tx); //TODO
|
||||||
|
|
||||||
console.log('Transaction rejected.');
|
console.log('Transaction rejected.');
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
var program = require('commander');
|
||||||
|
var Client = require('../lib/client');
|
||||||
|
var common = require('./common');
|
||||||
|
|
||||||
|
program
|
||||||
|
.version('0.0.1')
|
||||||
|
.option('-c,--config [file]', 'Wallet config filename')
|
||||||
|
.option('-v,--verbose', 'be verbose')
|
||||||
|
.usage('[options] <txpid>')
|
||||||
|
.parse(process.argv);
|
||||||
|
|
||||||
|
var args = program.args;
|
||||||
|
if (!args[0])
|
||||||
|
program.help();
|
||||||
|
|
||||||
|
var txpid = args[0];
|
||||||
|
|
||||||
|
var cli = new Client({
|
||||||
|
filename: program.config
|
||||||
|
});
|
||||||
|
|
||||||
|
cli.getTxProposals({}, function(err, txps) {
|
||||||
|
common.die(err);
|
||||||
|
|
||||||
|
if (program.verbose)
|
||||||
|
console.log('* Raw Server Response:\n', txps); //TODO
|
||||||
|
|
||||||
|
var txp = common.findOneTxProposal(txps, txpid);
|
||||||
|
|
||||||
|
cli.removeTxProposal(txp, function(err) {
|
||||||
|
common.die(err);
|
||||||
|
|
||||||
|
console.log('Transaction removed.');
|
||||||
|
});
|
||||||
|
});
|
|
@ -22,30 +22,19 @@ var cli = new ClientLib({
|
||||||
filename: program.config
|
filename: program.config
|
||||||
});
|
});
|
||||||
|
|
||||||
cli.getTxProposals({}, function(err, x) {
|
cli.getTxProposals({}, function(err, txps) {
|
||||||
common.die(err);
|
common.die(err);
|
||||||
|
|
||||||
if (program.verbose)
|
if (program.verbose)
|
||||||
console.log('* Raw Server Response:\n', x); //TODO
|
console.log('* Raw Server Response:\n', txps); //TODO
|
||||||
|
|
||||||
var txps = _.filter(x, function(x) {
|
var txp = common.findOneTxProposal(txps, txpid);
|
||||||
return _.endsWith(common.shortID(x.id), txpid);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!txps.length)
|
cli.signTxProposal(txp, function(err, tx) {
|
||||||
common.die('Could not find TX Proposal:' + txpid);
|
|
||||||
|
|
||||||
if (txps.length > 1)
|
|
||||||
common.die('More than one TX Proposals match:' + txpid + ' : ' + _.map(txps, function(x) {
|
|
||||||
return x.id;
|
|
||||||
}).join(' '));;
|
|
||||||
|
|
||||||
var txp = txps[0];
|
|
||||||
cli.signTxProposal(txp, function(err, x) {
|
|
||||||
common.die(err);
|
common.die(err);
|
||||||
|
|
||||||
if (program.verbose)
|
if (program.verbose)
|
||||||
console.log('* Raw Server Response:\n', x); //TODO
|
console.log('* Raw Server Response:\n', tx); //TODO
|
||||||
|
|
||||||
console.log('Transaction signed.');
|
console.log('Transaction signed.');
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
|
||||||
var common = function() {};
|
var common = function() {};
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +20,7 @@ common.parseMN = function(MN) {
|
||||||
var m = parseInt(mn[0]);
|
var m = parseInt(mn[0]);
|
||||||
var n = parseInt(mn[1]);
|
var n = parseInt(mn[1]);
|
||||||
|
|
||||||
if (!m || ! n) {
|
if (!m || !n) {
|
||||||
die('Bad m-n parameter');
|
die('Bad m-n parameter');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,4 +32,20 @@ common.shortID = function(id) {
|
||||||
return id.substr(id.length - 4);
|
return id.substr(id.length - 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
common.findOneTxProposal = function(txps, id) {
|
||||||
|
var matches = _.filter(txps, function(tx) {
|
||||||
|
return _.endsWith(common.shortID(tx.id), id);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!matches.length)
|
||||||
|
common.die('Could not find TX Proposal:' + id);
|
||||||
|
|
||||||
|
if (matches.length > 1)
|
||||||
|
common.die('More than one TX Proposals match:' + id + ' : ' + _.map(matches, function(tx) {
|
||||||
|
return tx.id;
|
||||||
|
}).join(' '));;
|
||||||
|
|
||||||
|
return matches[0];
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = common;
|
module.exports = common;
|
||||||
|
|
30
cli.js
30
cli.js
|
@ -1,30 +0,0 @@
|
||||||
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);
|
|
||||||
})
|
|
||||||
});
|
|
|
@ -84,7 +84,7 @@ API.prototype._loadAndCheck = function() {
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientLib.prototype._doRequest = function(method, url, args, data, cb) {
|
API.prototype._doRequest = function(method, url, args, data, cb) {
|
||||||
var reqSignature = _signRequest(method, url, args, data.signingPrivKey);
|
var reqSignature = _signRequest(method, url, args, data.signingPrivKey);
|
||||||
var absUrl = _getUrl(url);
|
var absUrl = _getUrl(url);
|
||||||
request({
|
request({
|
||||||
|
@ -359,4 +359,13 @@ API.prototype.rejectTxProposal = function(txp, reason, cb) {
|
||||||
this._doPostRequest(url, args, data, cb);
|
this._doPostRequest(url, args, data, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
API.prototype.removeTxProposal = function(txp, cb) {
|
||||||
|
var self = this;
|
||||||
|
var data = this._loadAndCheck();
|
||||||
|
|
||||||
|
var url = '/v1/txproposals/' + txp.id;
|
||||||
|
|
||||||
|
this._doRequest('delete', url, {}, data, cb);
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = API;
|
module.exports = API;
|
||||||
|
|
|
@ -547,19 +547,19 @@ CopayServer.prototype.removeWallet = function(opts, cb) {
|
||||||
* removePendingTx
|
* removePendingTx
|
||||||
*
|
*
|
||||||
* @param opts
|
* @param opts
|
||||||
* @param {string} opts.id - The tx id.
|
* @param {string} opts.txProposalId - The tx id.
|
||||||
* @return {undefined}
|
* @return {undefined}
|
||||||
*/
|
*/
|
||||||
CopayServer.prototype.removePendingTx = function(opts, cb) {
|
CopayServer.prototype.removePendingTx = function(opts, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (!Utils.checkRequired(opts, ['id']))
|
if (!Utils.checkRequired(opts, ['txProposalId']))
|
||||||
return cb(new ClientError('Required argument missing'));
|
return cb(new ClientError('Required argument missing'));
|
||||||
|
|
||||||
Utils.runLocked(self.walletId, cb, function(cb) {
|
Utils.runLocked(self.walletId, cb, function(cb) {
|
||||||
|
|
||||||
self.getTx({
|
self.getTx({
|
||||||
id: opts.id
|
id: opts.txProposalId,
|
||||||
}, function(err, txp) {
|
}, function(err, txp) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
@ -576,7 +576,7 @@ CopayServer.prototype.removePendingTx = function(opts, cb) {
|
||||||
return cb(new ClientError('Cannot remove a proposal signed/rejected by other copayers'));
|
return cb(new ClientError('Cannot remove a proposal signed/rejected by other copayers'));
|
||||||
|
|
||||||
self._notify('transactionProposalRemoved');
|
self._notify('transactionProposalRemoved');
|
||||||
self.storage.removeTx(self.walletId, opts.id, cb);
|
self.storage.removeTx(self.walletId, txp.id, cb);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1499,7 +1499,7 @@ describe('Copay server', function() {
|
||||||
|
|
||||||
it('should allow creator to remove an unsigned TX', function(done) {
|
it('should allow creator to remove an unsigned TX', function(done) {
|
||||||
server.removePendingTx({
|
server.removePendingTx({
|
||||||
id: txp.id
|
txProposalId: txp.id
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
@ -1516,7 +1516,7 @@ describe('Copay server', function() {
|
||||||
signatures: signatures,
|
signatures: signatures,
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
server.removePendingTx({
|
server.removePendingTx({
|
||||||
id: txp.id
|
txProposalId: txp.id
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
server.getPendingTxs({}, function(err, txs) {
|
server.getPendingTxs({}, function(err, txs) {
|
||||||
|
@ -1530,7 +1530,7 @@ describe('Copay server', function() {
|
||||||
it('should not allow non-creator copayer to remove an unsigned TX ', function(done) {
|
it('should not allow non-creator copayer to remove an unsigned TX ', function(done) {
|
||||||
helpers.getAuthServer(wallet.copayers[1].id, function(server2) {
|
helpers.getAuthServer(wallet.copayers[1].id, function(server2) {
|
||||||
server2.removePendingTx({
|
server2.removePendingTx({
|
||||||
id: txp.id
|
txProposalId: txp.id
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
err.message.should.contain('creators');
|
err.message.should.contain('creators');
|
||||||
server2.getPendingTxs({}, function(err, txs) {
|
server2.getPendingTxs({}, function(err, txs) {
|
||||||
|
@ -1550,7 +1550,7 @@ describe('Copay server', function() {
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
server.removePendingTx({
|
server.removePendingTx({
|
||||||
id: txp.id
|
txProposalId: txp.id
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
err.message.should.contain('other copayers');
|
err.message.should.contain('other copayers');
|
||||||
done();
|
done();
|
||||||
|
|
Loading…
Reference in New Issue