commit
ec491430c9
14
app.js
14
app.js
|
@ -199,6 +199,19 @@ router.post('/v1/txproposals/:id/signatures/', function(req, res) {
|
||||||
req.body.txProposalId = req.params['id'];
|
req.body.txProposalId = req.params['id'];
|
||||||
server.signTx(req.body, function(err, txp) {
|
server.signTx(req.body, function(err, txp) {
|
||||||
if (err) return returnError(err, res, req);
|
if (err) return returnError(err, res, req);
|
||||||
|
res.json(txp);
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO Check HTTP verb and URL name
|
||||||
|
router.post('/v1/txproposals/:id/broadcast/', function(req, res) {
|
||||||
|
getServerWithAuth(req, res, function(server) {
|
||||||
|
req.body.txProposalId = req.params['id'];
|
||||||
|
server.broadcastTx(req.body, function(err, txp) {
|
||||||
|
if (err) return returnError(err, res, req);
|
||||||
|
res.json(txp);
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -209,6 +222,7 @@ router.post('/v1/txproposals/:id/rejections', function(req, res) {
|
||||||
req.body.txProposalId = req.params['id'];
|
req.body.txProposalId = req.params['id'];
|
||||||
server.rejectTx(req.body, function(err, txp) {
|
server.rejectTx(req.body, function(err, txp) {
|
||||||
if (err) return returnError(err, res, req);
|
if (err) return returnError(err, res, req);
|
||||||
|
res.json(txp);
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,6 +13,7 @@ program
|
||||||
.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> [reason]', 'reject a transaction proposal')
|
.command('reject <txpId> [reason]', 'reject a transaction proposal')
|
||||||
|
.command('broadcast <txpId>', 'broadcast a transaction proposal to the Bitcoin network')
|
||||||
.command('rm <txpId>', 'remove a transaction proposal')
|
.command('rm <txpId>', 'remove a transaction proposal')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
|
var program = require('commander');
|
||||||
|
var Client = require('../lib/client');
|
||||||
|
var utils = require('./cli-utils');
|
||||||
|
|
||||||
|
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 client = utils.getClient(program);
|
||||||
|
|
||||||
|
client.getTxProposals({}, function(err, txps) {
|
||||||
|
utils.die(err);
|
||||||
|
|
||||||
|
var txp = utils.findOneTxProposal(txps, txpid);
|
||||||
|
client.broadcastTxProposal(txp, function(err, txid) {
|
||||||
|
utils.die(err);
|
||||||
|
console.log('Transaction Broadcasted: TXID: ' + x.txid);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,13 +1,14 @@
|
||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var _ = require('lodash');
|
||||||
var program = require('commander');
|
var program = require('commander');
|
||||||
var ClientLib = require('../lib/client');
|
var ClientLib = require('../lib/client');
|
||||||
var utils = require('./cli-utils');
|
var utils = require('./cli-utils');
|
||||||
|
|
||||||
program
|
program
|
||||||
.version('0.0.1')
|
.version('0.0.1')
|
||||||
.option('-c, --config [file]', 'Wallet config filename')
|
.option('-c, --config [file]', 'Wallet config filename')
|
||||||
.option('-n, --network [networkname]', 'livenet|testnet', String, 'livenet')
|
.option('-t, --testnet', 'Create a Testnet Wallet', String)
|
||||||
.usage('[options] <walletName> <m-n> [copayerName]')
|
.usage('[options] <walletName> <m-n> [copayerName]')
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
|
@ -17,14 +18,14 @@ if (!args[0])
|
||||||
|
|
||||||
var walletName = args[0];
|
var walletName = args[0];
|
||||||
var copayerName = args[2] || process.env.USER;
|
var copayerName = args[2] || process.env.USER;
|
||||||
var network = program.network;
|
var network = program.testnet ? 'testnet' : 'livenet';
|
||||||
|
|
||||||
var mn = utils.parseMN(args[1]);
|
var mn = utils.parseMN(args[1]);
|
||||||
|
|
||||||
var client = utils.getClient(program);
|
var client = utils.getClient(program);
|
||||||
client.createWallet(walletName, copayerName, mn[0], mn[1], network, function(err, secret) {
|
client.createWallet(walletName, copayerName, mn[0], mn[1], network, function(err, secret) {
|
||||||
utils.die(err);
|
utils.die(err);
|
||||||
console.log(' * Wallet Created.');
|
console.log(' * ' + _.capitalize(network) + ' Wallet Created.');
|
||||||
console.log(' - Secret to share:\n\t' + secret);
|
if (secret)
|
||||||
|
console.log(' - Secret to share:\n\t' + secret);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,7 @@ program
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
var args = program.args;
|
var args = program.args;
|
||||||
if (!args[0])
|
var txpid = args[0] || '';
|
||||||
program.help();
|
|
||||||
var txpid = args[0];
|
|
||||||
var reason = args[1] || '';
|
var reason = args[1] || '';
|
||||||
var client = utils.getClient(program);
|
var client = utils.getClient(program);
|
||||||
|
|
||||||
|
@ -25,6 +23,9 @@ client.getTxProposals({}, function(err, txps) {
|
||||||
var txp = utils.findOneTxProposal(txps, txpid);
|
var txp = utils.findOneTxProposal(txps, txpid);
|
||||||
client.rejectTxProposal(txp, reason, function(err, tx) {
|
client.rejectTxProposal(txp, reason, function(err, tx) {
|
||||||
utils.die(err);
|
utils.die(err);
|
||||||
console.log('Transaction rejected.');
|
if (x.status == 'rejected')
|
||||||
|
console.log('Transaction finally rejected.');
|
||||||
|
else
|
||||||
|
console.log('Transaction rejected by you.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,10 +12,7 @@ program
|
||||||
.parse(process.argv);
|
.parse(process.argv);
|
||||||
|
|
||||||
var args = program.args;
|
var args = program.args;
|
||||||
if (!args[0])
|
var txpid = args[0] || '';
|
||||||
program.help();
|
|
||||||
|
|
||||||
var txpid = args[0];
|
|
||||||
|
|
||||||
var client = utils.getClient(program);
|
var client = utils.getClient(program);
|
||||||
client.getTxProposals({}, function(err, txps) {
|
client.getTxProposals({}, function(err, txps) {
|
||||||
|
@ -24,6 +21,9 @@ client.getTxProposals({}, function(err, txps) {
|
||||||
var txp = utils.findOneTxProposal(txps, txpid);
|
var txp = utils.findOneTxProposal(txps, txpid);
|
||||||
client.signTxProposal(txp, function(err, x) {
|
client.signTxProposal(txp, function(err, x) {
|
||||||
utils.die(err);
|
utils.die(err);
|
||||||
console.log('Transaction signed.');
|
if (x.status == 'broadcasted')
|
||||||
|
console.log('Transaction Broadcasted: TXID: ' + x.txid);
|
||||||
|
else
|
||||||
|
console.log('Transaction signed by you.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,16 +2,16 @@
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var Client = require('../lib/client');
|
var Client = require('../lib/client');
|
||||||
|
|
||||||
var lib = function() {};
|
var Utils = function() {};
|
||||||
|
|
||||||
var die = lib.die = function(err) {
|
var die = Utils.die = function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
lib.parseMN = function(MN) {
|
Utils.parseMN = function(MN) {
|
||||||
if (!MN)
|
if (!MN)
|
||||||
die('No m-n parameter');
|
die('No m-n parameter');
|
||||||
var mn = MN.split('-');
|
var mn = MN.split('-');
|
||||||
|
@ -27,11 +27,11 @@ lib.parseMN = function(MN) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
lib.shortID = function(id) {
|
Utils.shortID = function(id) {
|
||||||
return id.substr(id.length - 4);
|
return id.substr(id.length - 4);
|
||||||
};
|
};
|
||||||
|
|
||||||
lib.getClient = function(args) {
|
Utils.getClient = function(args) {
|
||||||
var storage = new Client.FileStorage({
|
var storage = new Client.FileStorage({
|
||||||
filename: args.config
|
filename: args.config
|
||||||
});
|
});
|
||||||
|
@ -41,16 +41,16 @@ lib.getClient = function(args) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
lib.findOneTxProposal = function(txps, id) {
|
Utils.findOneTxProposal = function(txps, id) {
|
||||||
var matches = _.filter(txps, function(tx) {
|
var matches = _.filter(txps, function(tx) {
|
||||||
return _.endsWith(lib.shortID(tx.id), id);
|
return _.endsWith(Utils.shortID(tx.id), id);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!matches.length)
|
if (!matches.length)
|
||||||
lib.die('Could not find TX Proposal:' + id);
|
Utils.die('Could not find TX Proposal:' + id);
|
||||||
|
|
||||||
if (matches.length > 1)
|
if (matches.length > 1)
|
||||||
lib.die('More than one TX Proposals match:' + id + ' : ' + _.map(matches, function(tx) {
|
Utils.die('More than one TX Proposals match:' + id + ' : ' + _.map(matches, function(tx) {
|
||||||
return tx.id;
|
return tx.id;
|
||||||
}).join(' '));;
|
}).join(' '));;
|
||||||
|
|
||||||
|
@ -59,4 +59,4 @@ lib.findOneTxProposal = function(txps, id) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = lib;
|
module.exports = Utils;
|
||||||
|
|
|
@ -148,13 +148,16 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
|
||||||
|
|
||||||
var walletId = body.walletId;
|
var walletId = body.walletId;
|
||||||
var secret = walletId + ':' + privKey.toString() + ':' + (network == 'testnet' ? 'T' : 'L');
|
var secret = walletId + ':' + privKey.toString() + ':' + (network == 'testnet' ? 'T' : 'L');
|
||||||
data.secret = secret;
|
var ret;
|
||||||
|
|
||||||
|
if (n > 1)
|
||||||
|
ret = data.secret = secret;
|
||||||
|
|
||||||
self.storage.save(data);
|
self.storage.save(data);
|
||||||
self._joinWallet(data, secret, copayerName, function(err) {
|
self._joinWallet(data, secret, copayerName, function(err) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
|
|
||||||
return cb(null, data.secret);
|
return cb(null, ret);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -357,6 +360,16 @@ API.prototype.rejectTxProposal = function(txp, reason, cb) {
|
||||||
this._doPostRequest(url, args, data, cb);
|
this._doPostRequest(url, args, data, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
API.prototype.broadcastTxProposal = function(txp, cb) {
|
||||||
|
var self = this;
|
||||||
|
var data = this._loadAndCheck();
|
||||||
|
|
||||||
|
var url = '/v1/txproposals/' + txp.id + '/broadcast/';
|
||||||
|
this._doPostRequest(url, {}, data, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
API.prototype.removeTxProposal = function(txp, cb) {
|
API.prototype.removeTxProposal = function(txp, cb) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var data = this._loadAndCheck();
|
var data = this._loadAndCheck();
|
||||||
|
|
|
@ -105,7 +105,7 @@ TxProposal.prototype._getBitcoreTx = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposal.prototype.getNetworkName = function() {
|
TxProposal.prototype.getNetworkName = function() {
|
||||||
return Bitcore.Address(this.toAddress).toObject().networkName;
|
return Bitcore.Address(this.toAddress).toObject().network;
|
||||||
};
|
};
|
||||||
|
|
||||||
TxProposal.prototype.getRawTx = function() {
|
TxProposal.prototype.getRawTx = function() {
|
||||||
|
|
|
@ -317,6 +317,7 @@ CopayServer.prototype._getBlockExplorer = function(provider, network) {
|
||||||
url = 'https://test-insight.bitpay.com:443'
|
url = 'https://test-insight.bitpay.com:443'
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
console.log('[server.js.320:url:]',url); //TODO
|
||||||
return new Explorers.Insight(url, network);
|
return new Explorers.Insight(url, network);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -649,6 +650,7 @@ CopayServer.prototype.signTx = function(opts, cb) {
|
||||||
txid: txid
|
txid: txid
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('[server.js.653:txp:]',txp); //TODO
|
||||||
return cb(null, txp);
|
return cb(null, txp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -660,6 +662,51 @@ CopayServer.prototype.signTx = function(opts, cb) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast a transaction proposal.
|
||||||
|
* @param {Object} opts
|
||||||
|
* @param {string} opts.txProposalId - The identifier of the transaction.
|
||||||
|
*/
|
||||||
|
CopayServer.prototype.broadcastTx = function(opts, cb) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
if (!Utils.checkRequired(opts, ['txProposalId']))
|
||||||
|
return cb(new ClientError('Required argument missing'));
|
||||||
|
|
||||||
|
self.getWallet({}, function(err, wallet) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self.getTx({
|
||||||
|
id: opts.txProposalId
|
||||||
|
}, function(err, txp) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
if (txp.status == 'broadcasted')
|
||||||
|
return cb(new ClientError('TXALREADYBROADCASTED', 'The transaction proposal is already broadcasted'));
|
||||||
|
|
||||||
|
if (txp.status != 'accepted')
|
||||||
|
return cb(new ClientError('TXNOTACCEPTED', 'The transaction proposal is not accepted'));
|
||||||
|
|
||||||
|
self._broadcastTx(txp, function(err, txid) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
txp.setBroadcasted(txid);
|
||||||
|
self.storage.storeTx(self.walletId, txp, function(err) {
|
||||||
|
if (err) return cb(err);
|
||||||
|
|
||||||
|
self._notify('NewOutgoingTx', {
|
||||||
|
txProposalId: opts.txProposalId,
|
||||||
|
txid: txid
|
||||||
|
});
|
||||||
|
|
||||||
|
return cb(null, txid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reject a transaction proposal.
|
* Reject a transaction proposal.
|
||||||
* @param {Object} opts
|
* @param {Object} opts
|
||||||
|
|
Loading…
Reference in New Issue