diff --git a/app.js b/app.js
index 99eba80..9519be1 100644
--- a/app.js
+++ b/app.js
@@ -199,6 +199,19 @@ router.post('/v1/txproposals/:id/signatures/', function(req, res) {
req.body.txProposalId = req.params['id'];
server.signTx(req.body, function(err, txp) {
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();
});
});
@@ -209,6 +222,7 @@ router.post('/v1/txproposals/:id/rejections', function(req, res) {
req.body.txProposalId = req.params['id'];
server.rejectTx(req.body, function(err, txp) {
if (err) return returnError(err, res, req);
+ res.json(txp);
res.end();
});
});
diff --git a/bit-wallet/bit b/bit-wallet/bit
index cffe423..b5b3026 100755
--- a/bit-wallet/bit
+++ b/bit-wallet/bit
@@ -13,6 +13,7 @@ program
.command('send
', 'send bitcoins')
.command('sign ', 'sign a transaction proposal')
.command('reject [reason]', 'reject a transaction proposal')
+ .command('broadcast ', 'broadcast a transaction proposal to the Bitcoin network')
.command('rm ', 'remove a transaction proposal')
.parse(process.argv);
diff --git a/bit-wallet/bit-broadcast b/bit-wallet/bit-broadcast
new file mode 100755
index 0000000..e97c6e2
--- /dev/null
+++ b/bit-wallet/bit-broadcast
@@ -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] ')
+ .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);
+
+ });
+});
diff --git a/bit-wallet/bit-create b/bit-wallet/bit-create
index c3053bb..dae6ad6 100755
--- a/bit-wallet/bit-create
+++ b/bit-wallet/bit-create
@@ -1,13 +1,14 @@
#!/usr/bin/env node
+var _ = require('lodash');
var program = require('commander');
var ClientLib = require('../lib/client');
-var utils = require('./cli-utils');
+var utils = require('./cli-utils');
program
.version('0.0.1')
.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] [copayerName]')
.parse(process.argv);
@@ -17,14 +18,14 @@ if (!args[0])
var walletName = args[0];
var copayerName = args[2] || process.env.USER;
-var network = program.network;
+var network = program.testnet ? 'testnet' : 'livenet';
var mn = utils.parseMN(args[1]);
var client = utils.getClient(program);
client.createWallet(walletName, copayerName, mn[0], mn[1], network, function(err, secret) {
utils.die(err);
- console.log(' * Wallet Created.');
- console.log(' - Secret to share:\n\t' + secret);
+ console.log(' * ' + _.capitalize(network) + ' Wallet Created.');
+ if (secret)
+ console.log(' - Secret to share:\n\t' + secret);
});
-
diff --git a/bit-wallet/bit-reject b/bit-wallet/bit-reject
index 3389a6b..3a7ac6a 100644
--- a/bit-wallet/bit-reject
+++ b/bit-wallet/bit-reject
@@ -13,9 +13,7 @@ program
.parse(process.argv);
var args = program.args;
-if (!args[0])
- program.help();
-var txpid = args[0];
+var txpid = args[0] || '';
var reason = args[1] || '';
var client = utils.getClient(program);
@@ -25,6 +23,9 @@ client.getTxProposals({}, function(err, txps) {
var txp = utils.findOneTxProposal(txps, txpid);
client.rejectTxProposal(txp, reason, function(err, tx) {
utils.die(err);
- console.log('Transaction rejected.');
+ if (x.status == 'rejected')
+ console.log('Transaction finally rejected.');
+ else
+ console.log('Transaction rejected by you.');
});
});
diff --git a/bit-wallet/bit-sign b/bit-wallet/bit-sign
index 845c671..7c54dd9 100755
--- a/bit-wallet/bit-sign
+++ b/bit-wallet/bit-sign
@@ -12,10 +12,7 @@ program
.parse(process.argv);
var args = program.args;
-if (!args[0])
- program.help();
-
-var txpid = args[0];
+var txpid = args[0] || '';
var client = utils.getClient(program);
client.getTxProposals({}, function(err, txps) {
@@ -24,6 +21,9 @@ client.getTxProposals({}, function(err, txps) {
var txp = utils.findOneTxProposal(txps, txpid);
client.signTxProposal(txp, function(err, x) {
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.');
});
});
diff --git a/bit-wallet/cli-utils.js b/bit-wallet/cli-utils.js
index fbef376..1f47be4 100644
--- a/bit-wallet/cli-utils.js
+++ b/bit-wallet/cli-utils.js
@@ -2,16 +2,16 @@
var _ = require('lodash');
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) {
console.error(err);
process.exit(1);
}
};
-lib.parseMN = function(MN) {
+Utils.parseMN = function(MN) {
if (!MN)
die('No m-n parameter');
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);
};
-lib.getClient = function(args) {
+Utils.getClient = function(args) {
var storage = new Client.FileStorage({
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) {
- return _.endsWith(lib.shortID(tx.id), id);
+ return _.endsWith(Utils.shortID(tx.id), id);
});
if (!matches.length)
- lib.die('Could not find TX Proposal:' + id);
+ Utils.die('Could not find TX Proposal:' + id);
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;
}).join(' '));;
@@ -59,4 +59,4 @@ lib.findOneTxProposal = function(txps, id) {
-module.exports = lib;
+module.exports = Utils;
diff --git a/lib/client/API.js b/lib/client/API.js
index c3cad57..f582925 100644
--- a/lib/client/API.js
+++ b/lib/client/API.js
@@ -148,13 +148,16 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
var walletId = body.walletId;
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._joinWallet(data, secret, copayerName, function(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);
};
+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) {
var self = this;
var data = this._loadAndCheck();
diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js
index 7fc7347..6035cd0 100644
--- a/lib/model/txproposal.js
+++ b/lib/model/txproposal.js
@@ -105,7 +105,7 @@ TxProposal.prototype._getBitcoreTx = function() {
};
TxProposal.prototype.getNetworkName = function() {
- return Bitcore.Address(this.toAddress).toObject().networkName;
+ return Bitcore.Address(this.toAddress).toObject().network;
};
TxProposal.prototype.getRawTx = function() {
diff --git a/lib/server.js b/lib/server.js
index 579129a..e6d0114 100644
--- a/lib/server.js
+++ b/lib/server.js
@@ -317,6 +317,7 @@ CopayServer.prototype._getBlockExplorer = function(provider, network) {
url = 'https://test-insight.bitpay.com:443'
break;
}
+console.log('[server.js.320:url:]',url); //TODO
return new Explorers.Insight(url, network);
break;
}
@@ -649,6 +650,7 @@ CopayServer.prototype.signTx = function(opts, cb) {
txid: txid
});
+console.log('[server.js.653:txp:]',txp); //TODO
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.
* @param {Object} opts