From a3abc3b8ff348fbe5aa602ecf1adffcd73dfe792 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 15:49:47 -0300 Subject: [PATCH 1/8] rn clientLib --- bit-wallet/bit | 1 - bit-wallet/bit-address | 4 ++-- bit-wallet/bit-addresses | 4 ++-- bit-wallet/bit-balance | 4 ++-- bit-wallet/bit-create | 4 ++-- bit-wallet/bit-join | 4 ++-- bit-wallet/bit-send | 4 ++-- bit-wallet/bit-status | 4 ++-- lib/{clilib.js => clientlib.js} | 34 ++++++++++++++++----------------- 9 files changed, 31 insertions(+), 32 deletions(-) rename lib/{clilib.js => clientlib.js} (90%) diff --git a/bit-wallet/bit b/bit-wallet/bit index 297adf8..a71e838 100755 --- a/bit-wallet/bit +++ b/bit-wallet/bit @@ -1,7 +1,6 @@ #!/usr/bin/env node var program = require('commander'); -var cli = require('../lib/clilib.js'); program .version('0.0.1') diff --git a/bit-wallet/bit-address b/bit-wallet/bit-address index c1f6ddc..217b78e 100644 --- a/bit-wallet/bit-address +++ b/bit-wallet/bit-address @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var CliLib = require('../lib/clilib.js'); +var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program @@ -11,7 +11,7 @@ program .parse(process.argv); var args = program.args; -var cli = new CliLib({ +var cli = new ClientLib({ filename: program.config }); diff --git a/bit-wallet/bit-addresses b/bit-wallet/bit-addresses index 31f2a93..19614b1 100644 --- a/bit-wallet/bit-addresses +++ b/bit-wallet/bit-addresses @@ -2,8 +2,8 @@ var _ = require('lodash'); var program = require('commander'); -var CliLib = require('../lib/clilib.js'); var common = require('./common'); +var ClientLib = require('../lib/clientlib.js'); program .version('0.0.1') @@ -12,7 +12,7 @@ program .parse(process.argv); var args = program.args; -var cli = new CliLib({ +var cli = new ClientLib({ filename: program.config }); diff --git a/bit-wallet/bit-balance b/bit-wallet/bit-balance index 7c95f3e..579bc07 100644 --- a/bit-wallet/bit-balance +++ b/bit-wallet/bit-balance @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var CliLib = require('../lib/clilib.js'); +var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program @@ -11,7 +11,7 @@ program .parse(process.argv); var args = program.args; -var cli = new CliLib({ +var cli = new ClientLib({ filename: program.config }); diff --git a/bit-wallet/bit-create b/bit-wallet/bit-create index 01137a6..710838e 100755 --- a/bit-wallet/bit-create +++ b/bit-wallet/bit-create @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var CliLib = require('../lib/clilib.js'); +var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program @@ -21,7 +21,7 @@ var network = program.network; var mn = common.parseMN(args[1]); -var cli = new CliLib({ +var cli = new ClientLib({ filename: program.config }); cli.createWallet(walletName, copayerName, mn[0], mn[1], network, function(err, secret) { diff --git a/bit-wallet/bit-join b/bit-wallet/bit-join index 4a14659..f2a3424 100644 --- a/bit-wallet/bit-join +++ b/bit-wallet/bit-join @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var CliLib = require('../lib/clilib.js'); +var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program @@ -17,7 +17,7 @@ if (!args[0]) var secret = args[0]; var copayerName = args[1] || process.env.USER; -var cli = new CliLib({ +var cli = new ClientLib({ filename: program.config }); diff --git a/bit-wallet/bit-send b/bit-wallet/bit-send index 0fc35d3..2bf5665 100644 --- a/bit-wallet/bit-send +++ b/bit-wallet/bit-send @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var CliLib = require('../lib/clilib.js'); +var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program @@ -19,7 +19,7 @@ if (!args[0] || !args[1] || !args[2]) var amount = args[1]; var message = args[2]; - var cli = new CliLib({ + var cli = new ClientLib({ filename: program.config }); diff --git a/bit-wallet/bit-status b/bit-wallet/bit-status index 6d98416..8e2b163 100644 --- a/bit-wallet/bit-status +++ b/bit-wallet/bit-status @@ -1,7 +1,7 @@ #!/usr/bin/env node var program = require('commander'); -var CliLib = require('../lib/clilib.js'); +var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program @@ -11,7 +11,7 @@ program .parse(process.argv); var args = program.args; -var cli = new CliLib({ +var cli = new ClientLib({ filename: program.config }); diff --git a/lib/clilib.js b/lib/clientlib.js similarity index 90% rename from lib/clilib.js rename to lib/clientlib.js index 9ac09e1..94885f7 100644 --- a/lib/clilib.js +++ b/lib/clientlib.js @@ -47,7 +47,7 @@ function _createXPrivKey() { return new Bitcore.HDPrivateKey().toString(); }; -function CliLib(opts) { +function ClientLib(opts) { if (!opts.filename) { throw new Error('Please set the config filename'); } @@ -55,17 +55,17 @@ function CliLib(opts) { }; -CliLib.prototype._save = function(data) { +ClientLib.prototype._save = function(data) { fs.writeFileSync(this.filename, JSON.stringify(data)); }; -CliLib.prototype._load = function() { +ClientLib.prototype._load = function() { try { return JSON.parse(fs.readFileSync(this.filename)); } catch (ex) {} }; -CliLib.prototype._loadAndCheck = function() { +ClientLib.prototype._loadAndCheck = function() { var data = this._load(); if (!data) { log.error('Wallet file not found.'); @@ -85,7 +85,7 @@ CliLib.prototype._loadAndCheck = function() { return data; }; -CliLib.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) { +ClientLib.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) { var self = this; var data = this._load(); @@ -136,7 +136,7 @@ CliLib.prototype.createWallet = function(walletName, copayerName, m, n, network, }); }; -CliLib.prototype._joinWallet = function(data, secret, copayerName, cb) { +ClientLib.prototype._joinWallet = function(data, secret, copayerName, cb) { var self = this; var secretSplit = secret.split(':'); @@ -180,7 +180,7 @@ CliLib.prototype._joinWallet = function(data, secret, copayerName, cb) { }); }; -CliLib.prototype.joinWallet = function(secret, copayerName, cb) { +ClientLib.prototype.joinWallet = function(secret, copayerName, cb) { var self = this; var data = this._load(); @@ -193,7 +193,7 @@ CliLib.prototype.joinWallet = function(secret, copayerName, cb) { self._joinWallet(data, secret, copayerName, cb); }; -CliLib.prototype.status = function(cb) { +ClientLib.prototype.status = function(cb) { var self = this; var data = this._loadAndCheck(); @@ -253,7 +253,7 @@ CliLib.prototype.status = function(cb) { * @param inArgs.amount * @param inArgs.message */ -CliLib.prototype.send = function(inArgs, cb) { +ClientLib.prototype.send = function(inArgs, cb) { var self = this; var data = this._loadAndCheck(); @@ -282,16 +282,16 @@ CliLib.prototype.send = function(inArgs, cb) { }; // TODO check change address -CliLib.prototype.sign = function(proposalId, cb) { +ClientLib.prototype.sign = function(proposalId, cb) { }; -CliLib.prototype.reject = function(proposalId, cb) { +ClientLib.prototype.reject = function(proposalId, cb) { }; // Get addresses -CliLib.prototype.addresses = function(cb) { +ClientLib.prototype.addresses = function(cb) { var self = this; var data = this._loadAndCheck(); @@ -320,7 +320,7 @@ CliLib.prototype.addresses = function(cb) { // Creates a new address // TODO: verify derivation!! -CliLib.prototype.address = function(cb) { +ClientLib.prototype.address = function(cb) { var self = this; var data = this._loadAndCheck(); @@ -346,11 +346,11 @@ CliLib.prototype.address = function(cb) { }); }; -CliLib.prototype.history = function(limit, cb) { +ClientLib.prototype.history = function(limit, cb) { }; -CliLib.prototype.balance = function(cb) { +ClientLib.prototype.balance = function(cb) { var self = this; var data = this._loadAndCheck(); @@ -377,7 +377,7 @@ CliLib.prototype.balance = function(cb) { }; -CliLib.prototype.txProposals = function(cb) { +ClientLib.prototype.txProposals = function(cb) { var self = this; var data = this._loadAndCheck(); @@ -404,4 +404,4 @@ CliLib.prototype.txProposals = function(cb) { }; -module.exports = CliLib; +module.exports = ClientLib; From 7de9a95dd0a054684c965e1958db39a9437b3541 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 16:07:47 -0300 Subject: [PATCH 2/8] rename clientlib --- app.js | 4 +++- bit-wallet/bit-send | 10 +++++----- bit-wallet/bit-sign | 33 +++++++++++++++++++++++++++++++++ bit-wallet/bit-status | 8 ++++++++ lib/clientlib.js | 2 +- package.json | 1 + 6 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 bit-wallet/bit-sign diff --git a/app.js b/app.js index 2ece691..d623be6 100644 --- a/app.js +++ b/app.js @@ -39,6 +39,9 @@ app.use(bodyParser.json({ limit: POST_LIMIT })); +app.use(require('morgan')('dev')); + + var port = process.env.COPAY_PORT || 3001; var router = express.Router(); @@ -59,7 +62,6 @@ function returnError(err, res, req) { } var m = message || err.toString(); - console.log('[app.js.60]'); //TODO log.error('Error: ' + req.url + ' :' + code + ':' + m); res.status(code || 500).json({ error: m, diff --git a/bit-wallet/bit-send b/bit-wallet/bit-send index 2bf5665..7603bc5 100644 --- a/bit-wallet/bit-send +++ b/bit-wallet/bit-send @@ -19,15 +19,15 @@ if (!args[0] || !args[1] || !args[2]) var amount = args[1]; var message = args[2]; - var cli = new ClientLib({ -filename: program.config +var cli = new ClientLib({ + 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); + x.id, x.status, x.requiredSignatures); if (program.verbose) - console.log('* Raw Server Response:\n', x); //TODO -}); + console.log('* Raw Server Response:\n', x); //TODO + }); diff --git a/bit-wallet/bit-sign b/bit-wallet/bit-sign new file mode 100644 index 0000000..679ea34 --- /dev/null +++ b/bit-wallet/bit-sign @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +var program = require('commander'); +var ClientLib = require('../lib/clientlib.js'); +var common = require('./common'); + +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] || !args[1] || !args[2]) + program.help(); + + var address = args[0]; + var amount = args[1]; + var message = args[2]; + + var cli = new ClientLib({ +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 +}); diff --git a/bit-wallet/bit-status b/bit-wallet/bit-status index 8e2b163..ecad373 100644 --- a/bit-wallet/bit-status +++ b/bit-wallet/bit-status @@ -21,4 +21,12 @@ cli.status(function(err, x) { if (program.verbose) console.log('* Raw Server Response:\n', x); //TODO + + + cli.txProposals(function(err, x) { + console.log('[bit-status.27]', x); //TODO + + if (program.verbose) + console.log('* Raw Server Response:\n', x); //TODO + }); }); diff --git a/lib/clientlib.js b/lib/clientlib.js index 94885f7..cedc345 100644 --- a/lib/clientlib.js +++ b/lib/clientlib.js @@ -11,7 +11,7 @@ var fs = require('fs') var Bitcore = require('bitcore') var SignUtils = require('./signutils'); -var BASE_URL = 'http://localhost:3001/copay/api/'; +var BASE_URL = 'http://localhost:3001/copay/api'; function _createProposalOpts(opts, signingKey) { var msg = opts.toAddress + '|' + opts.amount + '|' + opts.message; diff --git a/package.json b/package.json index 3adba26..dd23bd5 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "npmlog": "^0.1.1", "preconditions": "^1.0.7", "request": "^2.53.0", + "morgan": "*", "uuid": "*" }, "devDependencies": { From f9caf4fc87e783b39ab682e5ea1831c2378a0aca Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 16:09:52 -0300 Subject: [PATCH 3/8] x files --- bit-wallet/bit-address | 0 bit-wallet/bit-addresses | 0 bit-wallet/bit-balance | 0 bit-wallet/bit-join | 0 bit-wallet/bit-send | 0 bit-wallet/bit-sign | 0 bit-wallet/bit-status | 0 7 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 bit-wallet/bit-address mode change 100644 => 100755 bit-wallet/bit-addresses mode change 100644 => 100755 bit-wallet/bit-balance mode change 100644 => 100755 bit-wallet/bit-join mode change 100644 => 100755 bit-wallet/bit-send mode change 100644 => 100755 bit-wallet/bit-sign mode change 100644 => 100755 bit-wallet/bit-status diff --git a/bit-wallet/bit-address b/bit-wallet/bit-address old mode 100644 new mode 100755 diff --git a/bit-wallet/bit-addresses b/bit-wallet/bit-addresses old mode 100644 new mode 100755 diff --git a/bit-wallet/bit-balance b/bit-wallet/bit-balance old mode 100644 new mode 100755 diff --git a/bit-wallet/bit-join b/bit-wallet/bit-join old mode 100644 new mode 100755 diff --git a/bit-wallet/bit-send b/bit-wallet/bit-send old mode 100644 new mode 100755 diff --git a/bit-wallet/bit-sign b/bit-wallet/bit-sign old mode 100644 new mode 100755 diff --git a/bit-wallet/bit-status b/bit-wallet/bit-status old mode 100644 new mode 100755 From 802746889c59904476277d7d371f01ea162a7f29 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 17:02:56 -0300 Subject: [PATCH 4/8] sign WIP --- app.js | 11 ++++++++++ bit-wallet/bit | 1 + bit-wallet/bit-sign | 50 ++++++++++++++++++++++++++++--------------- bit-wallet/bit-status | 26 +++++++++++++--------- bit-wallet/common.js | 4 ++++ lib/clientlib.js | 41 +++++++++++++++++++++++++++++++++-- package.json | 4 ++-- 7 files changed, 106 insertions(+), 31 deletions(-) diff --git a/app.js b/app.js index d623be6..e6d1abd 100644 --- a/app.js +++ b/app.js @@ -146,6 +146,17 @@ router.get('/v1/wallets/', function(req, res) { }); }); + +router.get('/v1/txproposals/', function(req, res) { + getServerWithAuth(req, res, function(server) { + server.getPendingTxs({}, function(err, pendings) { + if (err) return returnError(err, res, req); + res.json(pendings); + }); + }); +}); + + router.post('/v1/txproposals/', function(req, res) { getServerWithAuth(req, res, function(server) { server.createTx(req.body, function(err, txp) { diff --git a/bit-wallet/bit b/bit-wallet/bit index a71e838..1beaac6 100755 --- a/bit-wallet/bit +++ b/bit-wallet/bit @@ -11,6 +11,7 @@ program .command('addresses', 'list addresses') .command('balance', 'wallet balance') .command('send
', 'send bitcoins') + .command('sign ', 'sign a Transaction Proposal') .parse(process.argv); diff --git a/bit-wallet/bit-sign b/bit-wallet/bit-sign index 679ea34..cb97fcb 100755 --- a/bit-wallet/bit-sign +++ b/bit-wallet/bit-sign @@ -1,33 +1,49 @@ #!/usr/bin/env node +var _ = require('lodash'); var program = require('commander'); var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); program -.version('0.0.1') -.option('-c,--config [file]', 'Wallet config filename') -.option('-v,--verbose', 'be verbose') -.usage('[options]
') -.parse(process.argv); + .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] || !args[1] || !args[2]) +if (!args[0]) program.help(); - var address = args[0]; - var amount = args[1]; - var message = args[2]; +var txpid = args[0]; - var cli = new ClientLib({ -filename: program.config +var cli = new ClientLib({ + 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); +cli.txProposals({}, function(err, x) { + common.die(err); - if (program.verbose) - console.log('* Raw Server Response:\n', x); //TODO + if (program.verbose) + console.log('* Raw Server Response:\n', x); //TODO + + var txps = _.filter(x, function(x) { + + var sid = common.shortID(x.id); + + return _.endsWith(sid, txpid); + }); + + if (!txps.length) + common.die('Could not find TX Proposal:' + txpid); + + if (txps.length > 1) + common.die('More that one TX Proposals match:' + txpid + ' : ' + _.map(txps, function(x) { + return x.id; + }).join(' '));; + + var txp = txps[0]; + + cli.sign(txp); }); diff --git a/bit-wallet/bit-status b/bit-wallet/bit-status index ecad373..040ae99 100755 --- a/bit-wallet/bit-status +++ b/bit-wallet/bit-status @@ -1,6 +1,8 @@ #!/usr/bin/env node +var _ = require('lodash'); var program = require('commander'); + var ClientLib = require('../lib/clientlib.js'); var common = require('./common'); @@ -15,18 +17,22 @@ var cli = new ClientLib({ filename: program.config }); -cli.status(function(err, x) { +cli.status(function(err, res) { common.die(err); + + var x = res.wallet; console.log('* Wallet %s [%s]: %d-%d %s ', x.name, x.isTestnet ? 'testnet' : 'livenet', x.m, x.n, x.status); + var x = res.balance; + console.log('* Balance %d (Locked: %d)', x.totalAmount, x.lockedAmount); + + if (! _.isEmpty(res.pendingTxps)) { + console.log("* TX Proposals:") + _.each(res.pendingTxps, function(x){ + console.log("\t[%s] %d => %s", common.shortID(x.id),x.amount, x.toAddress); + }); + } + if (program.verbose) - console.log('* Raw Server Response:\n', x); //TODO - - - cli.txProposals(function(err, x) { - console.log('[bit-status.27]', x); //TODO - - if (program.verbose) - console.log('* Raw Server Response:\n', x); //TODO - }); + console.log('* Raw Server Response:\n', res); //TODO }); diff --git a/bit-wallet/common.js b/bit-wallet/common.js index 7a6c256..5fe7337 100644 --- a/bit-wallet/common.js +++ b/bit-wallet/common.js @@ -24,4 +24,8 @@ common.parseMN = function(MN) { }; +common.shortID = function(id) { + return id.substr(id.length - 4); +}; + module.exports = common; diff --git a/lib/clientlib.js b/lib/clientlib.js index cedc345..e42bccd 100644 --- a/lib/clientlib.js +++ b/lib/clientlib.js @@ -54,7 +54,6 @@ function ClientLib(opts) { this.filename = opts.filename; }; - ClientLib.prototype._save = function(data) { fs.writeFileSync(this.filename, JSON.stringify(data)); }; @@ -377,7 +376,7 @@ ClientLib.prototype.balance = function(cb) { }; -ClientLib.prototype.txProposals = function(cb) { +ClientLib.prototype.txProposals = function(opts, cb) { var self = this; var data = this._loadAndCheck(); @@ -403,5 +402,43 @@ ClientLib.prototype.txProposals = function(cb) { }); }; +ClientLib.prototype.sign = function(txp, cb) { + var self = this; + var data = this._loadAndCheck(); + + + //Derive proper key to sign, for each input + var privs = [], + derived = {}; + var xpriv = new Bitcore.HDPrivateKey(data.xprivKey); + + _.each(txp.inputs, function(i) { + if (!derived[i.path]) { + derived[i.path] = xpriv.derive(i.path).privateKey; + } + privs.push(derived[i.path]); + }); + +console.log('[clientlib.js.419:privs:]',privs); //TODO + + var t = new Bitcore.Transaction(); + _.each(txp.inputs, function(i) { + t.from(i, i.publicKeys, txp.requiredSignatures); + }); + + t.to(txp.toAddress, txp.amount) + .change(txp.changeAddress) + .sign(privs); + + var signatures = []; + _.each(privs, function(p) { + var s = t.getSignatures(p)[0].signature.toDER().toString('hex'); + signatures.push(s); + }); +console.log('[clientlib.js.436:signatures:]',signatures); //TODO + // + return signatures; +}; + module.exports = ClientLib; diff --git a/package.json b/package.json index dd23bd5..c0a8679 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,11 @@ "inherits": "^2.0.1", "leveldown": "^0.10.0", "levelup": "^0.19.0", - "lodash": "^2.4.1", + "lodash": "^3.2.0", + "morgan": "*", "npmlog": "^0.1.1", "preconditions": "^1.0.7", "request": "^2.53.0", - "morgan": "*", "uuid": "*" }, "devDependencies": { From edce55b6ccbc190506c6a7e02c2e40c4c381f11c Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 18:24:35 -0300 Subject: [PATCH 5/8] sign2 --- bit-wallet/bit-sign | 6 +---- lib/clientlib.js | 54 ++++++++++++++++++++++++++++++--------------- lib/server.js | 6 ++++- package.json | 2 +- 4 files changed, 43 insertions(+), 25 deletions(-) diff --git a/bit-wallet/bit-sign b/bit-wallet/bit-sign index cb97fcb..0361b12 100755 --- a/bit-wallet/bit-sign +++ b/bit-wallet/bit-sign @@ -29,10 +29,7 @@ cli.txProposals({}, function(err, x) { console.log('* Raw Server Response:\n', x); //TODO var txps = _.filter(x, function(x) { - - var sid = common.shortID(x.id); - - return _.endsWith(sid, txpid); + return _.endsWith(common.shortID(x.id), txpid); }); if (!txps.length) @@ -44,6 +41,5 @@ cli.txProposals({}, function(err, x) { }).join(' '));; var txp = txps[0]; - cli.sign(txp); }); diff --git a/lib/clientlib.js b/lib/clientlib.js index e42bccd..4db393a 100644 --- a/lib/clientlib.js +++ b/lib/clientlib.js @@ -43,8 +43,8 @@ function _signRequest(url, args, privKey) { return SignUtils.sign(message, privKey); }; -function _createXPrivKey() { - return new Bitcore.HDPrivateKey().toString(); +function _createXPrivKey(network) { + return new Bitcore.HDPrivateKey(network).toString(); }; function ClientLib(opts) { @@ -86,16 +86,16 @@ ClientLib.prototype._loadAndCheck = function() { ClientLib.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) { var self = this; + network = network || 'livenet'; 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 privKey = new Bitcore.PrivateKey(null, network); var pubKey = privKey.toPublicKey(); data = { - xPrivKey: _createXPrivKey(), m: m, n: n, walletPrivKey: privKey.toString(), @@ -106,7 +106,7 @@ ClientLib.prototype.createWallet = function(walletName, copayerName, m, n, netwo m: m, n: n, pubKey: pubKey.toString(), - network: network || 'livenet', + network: network, }; request({ @@ -122,11 +122,10 @@ ClientLib.prototype.createWallet = function(walletName, copayerName, m, n, netwo } var walletId = body.walletId; - var secret = walletId + ':' + privKey.toString(); + var secret = walletId + ':' + privKey.toString() + ':' + (network ? 'T' : 'L'); data.secret = secret; self._save(data); - self._joinWallet(data, secret, copayerName, function(err) { if (err) return cb(err); @@ -135,18 +134,21 @@ ClientLib.prototype.createWallet = function(walletName, copayerName, m, n, netwo }); }; -ClientLib.prototype._joinWallet = function(data, secret, copayerName, cb) { +ClientLib.prototype._joinWallet = function(data, secret, copayerName, cb) { var self = this; + data = data || {}; var secretSplit = secret.split(':'); var walletId = secretSplit[0]; + var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]); + var network = secretSplit[2] == 'T' ? 'testnet' : 'livenet'; + data.xPrivKey = _createXPrivKey(network); var xPubKey = new Bitcore.HDPublicKey(data.xPrivKey); var xPubKeySignature = SignUtils.sign(xPubKey.toString(), walletPrivKey); var signingPrivKey = (new Bitcore.HDPrivateKey(data.xPrivKey)).derive('m/1/0').privateKey; - var args = { walletId: walletId, name: copayerName, @@ -185,10 +187,6 @@ ClientLib.prototype.joinWallet = function(secret, copayerName, cb) { var data = this._load(); if (data) return cb('File ' + this.filename + ' already contains a wallet'); - data = { - xPrivKey: _createXPrivKey(), - }; - self._joinWallet(data, secret, copayerName, cb); }; @@ -410,7 +408,9 @@ ClientLib.prototype.sign = function(txp, cb) { //Derive proper key to sign, for each input var privs = [], derived = {}; - var xpriv = new Bitcore.HDPrivateKey(data.xprivKey); + + var network = new Bitcore.Address(txp.toAddress).network.name; + var xpriv = new Bitcore.HDPrivateKey(data.xPrivKey, network); _.each(txp.inputs, function(i) { if (!derived[i.path]) { @@ -419,8 +419,6 @@ ClientLib.prototype.sign = function(txp, cb) { privs.push(derived[i.path]); }); -console.log('[clientlib.js.419:privs:]',privs); //TODO - var t = new Bitcore.Transaction(); _.each(txp.inputs, function(i) { t.from(i, i.publicKeys, txp.requiredSignatures); @@ -435,8 +433,28 @@ console.log('[clientlib.js.419:privs:]',privs); //TODO var s = t.getSignatures(p)[0].signature.toDER().toString('hex'); signatures.push(s); }); -console.log('[clientlib.js.436:signatures:]',signatures); //TODO - // + + var url = '/v1//'; + 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); + }); + + return signatures; }; diff --git a/lib/server.js b/lib/server.js index 4b58469..71cd3ca 100644 --- a/lib/server.js +++ b/lib/server.js @@ -341,8 +341,11 @@ CopayServer.prototype._getUtxos = function(cb) { var networkName = Bitcore.Address(addressStrs[0]).toObject().network; var bc = self._getBlockExplorer('insight', networkName); - bc.getUnspentUtxos(addressStrs, function(err, utxos) { + bc.getUnspentUtxos(addressStrs, function(err, inutxos) { if (err) return cb(err); + var utxos = _.map(inutxos, function(i) { + return i.toObject(); + }); self.getPendingTxs({}, function(err, txps) { if (err) return cb(err); @@ -371,6 +374,7 @@ CopayServer.prototype._getUtxos = function(cb) { utxo.path = addressToPath[utxo.address].path; utxo.publicKeys = addressToPath[utxo.address].publicKeys; }); + console.log('[server.js.375:utxos:]', utxos); //TODO return cb(null, utxos); }); diff --git a/package.json b/package.json index c0a8679..33c4327 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ }, "dependencies": { "async": "^0.9.0", - "bitcore": "0.10.0", + "bitcore": "^0.10.3", "bitcore-explorers": "^0.9.1", "body-parser": "^1.11.0", "commander": "^2.6.0", From 0af48ff27b78ffd03af198e1f5245ae5b3f538ae Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 18:53:49 -0300 Subject: [PATCH 6/8] sign 3 --- app.js | 6 +++--- bit-wallet/bit-sign | 13 ++++++++++--- bit-wallet/bit-status | 19 +++++++++++++------ lib/clientlib.js | 20 +++++++++++--------- lib/server.js | 3 ++- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/app.js b/app.js index e6d1abd..7eb3f00 100644 --- a/app.js +++ b/app.js @@ -194,9 +194,9 @@ router.get('/v1/balance/', function(req, res) { }); }); -router.post('/v1/txproposals/:id/signatures', function(req, res) { - req.body.txProposalId = req.params['id']; +router.post('/v1/txproposals/:id/signatures/', function(req, res) { getServerWithAuth(req, res, function(server) { + req.body.txProposalId = req.params['id']; server.signTx(req.body, function(err, txp) { if (err) return returnError(err, res, req); res.end(); @@ -205,8 +205,8 @@ router.post('/v1/txproposals/:id/signatures', function(req, res) { }); router.post('/v1/txproposals/:id/rejections', function(req, res) { - req.body.txProposalId = req.params['id']; getServerWithAuth(req, res, function(server) { + req.body.txProposalId = req.params['id']; server.signTx(req.body, function(err, txp) { if (err) return returnError(err, res, req); res.end(); diff --git a/bit-wallet/bit-sign b/bit-wallet/bit-sign index 0361b12..bad5658 100755 --- a/bit-wallet/bit-sign +++ b/bit-wallet/bit-sign @@ -33,13 +33,20 @@ cli.txProposals({}, function(err, x) { }); if (!txps.length) - common.die('Could not find TX Proposal:' + txpid); + common.die('Could not find TX Proposal:' + txpid); if (txps.length > 1) - common.die('More that one TX Proposals match:' + txpid + ' : ' + _.map(txps, function(x) { + common.die('More that one TX Proposals match:' + txpid + ' : ' + _.map(txps, function(x) { return x.id; }).join(' '));; var txp = txps[0]; - cli.sign(txp); + cli.sign(txp, function(err, x) { + common.die(err); + + if (program.verbose) + console.log('* Raw Server Response:\n', x); //TODO + + console.log('Transaction signed.'); + }); }); diff --git a/bit-wallet/bit-status b/bit-wallet/bit-status index 040ae99..8fe542d 100755 --- a/bit-wallet/bit-status +++ b/bit-wallet/bit-status @@ -26,13 +26,20 @@ cli.status(function(err, res) { var x = res.balance; console.log('* Balance %d (Locked: %d)', x.totalAmount, x.lockedAmount); - if (! _.isEmpty(res.pendingTxps)) { + if (!_.isEmpty(res.pendingTxps)) { console.log("* TX Proposals:") - _.each(res.pendingTxps, function(x){ - console.log("\t[%s] %d => %s", common.shortID(x.id),x.amount, x.toAddress); + _.each(res.pendingTxps, function(x) { + console.log("\t%s [%s by %s] %dSAT => %s", common.shortID(x.id), x.message, x.creatorName, x.amount, x.toAddress); + + if (!_.isEmpty(x.actions)) { + console.log('\t\t * Actions'); + console.log('\t\t', _.map(x.actions, function(a) { + return a.copayerName + ': ' + a.type + '' + }).join('. ')); + } + + if (program.verbose) + console.log('* Raw Server Response:\n', res); //TODO }); } - - if (program.verbose) - console.log('* Raw Server Response:\n', res); //TODO }); diff --git a/lib/clientlib.js b/lib/clientlib.js index 4db393a..e5ba809 100644 --- a/lib/clientlib.js +++ b/lib/clientlib.js @@ -134,7 +134,7 @@ ClientLib.prototype.createWallet = function(walletName, copayerName, m, n, netwo }); }; -ClientLib.prototype._joinWallet = function(data, secret, copayerName, cb) { +ClientLib.prototype._joinWallet = function(data, secret, copayerName, cb) { var self = this; data = data || {}; @@ -143,7 +143,7 @@ ClientLib.prototype._joinWallet = function(data, secret, copayerName, cb) { var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]); var network = secretSplit[2] == 'T' ? 'testnet' : 'livenet'; - data.xPrivKey = _createXPrivKey(network); + data.xPrivKey = _createXPrivKey(network); var xPubKey = new Bitcore.HDPublicKey(data.xPrivKey); var xPubKeySignature = SignUtils.sign(xPubKey.toString(), walletPrivKey); @@ -408,7 +408,7 @@ ClientLib.prototype.sign = function(txp, cb) { //Derive proper key to sign, for each input var privs = [], derived = {}; - + var network = new Bitcore.Address(txp.toAddress).network.name; var xpriv = new Bitcore.HDPrivateKey(data.xPrivKey, network); @@ -434,12 +434,17 @@ ClientLib.prototype.sign = function(txp, cb) { signatures.push(s); }); - var url = '/v1//'; - var signature = _signRequest(url, args, data.signingPrivKey); + var url = '/v1/txproposals/' + txp.id + '/signatures/'; + var args = { + signatures: signatures + }; + var reqSignature = _signRequest(url, args, data.signingPrivKey); +console.log('[clientlib.js.441:reqSignature:]',url, args, reqSignature); //TODO + request({ headers: { 'x-identity': data.copayerId, - 'x-signature': signature, + 'x-signature': reqSignature, }, method: 'post', url: _getUrl(url), @@ -453,9 +458,6 @@ ClientLib.prototype.sign = function(txp, cb) { } return cb(null, body); }); - - - return signatures; }; diff --git a/lib/server.js b/lib/server.js index 71cd3ca..7aff2fc 100644 --- a/lib/server.js +++ b/lib/server.js @@ -374,7 +374,6 @@ CopayServer.prototype._getUtxos = function(cb) { utxo.path = addressToPath[utxo.address].path; utxo.publicKeys = addressToPath[utxo.address].publicKeys; }); - console.log('[server.js.375:utxos:]', utxos); //TODO return cb(null, utxos); }); @@ -586,7 +585,9 @@ CopayServer.prototype.removePendingTx = function(opts, cb) { CopayServer.prototype._broadcastTx = function(txp, cb) { var raw = txp.getRawTx(); var bc = this._getBlockExplorer('insight', txp.getNetworkName()); +console.log('[server.js.588:raw:]',raw); //TODO bc.broadcast(raw, function(err, txid) { +console.log('[server.js.589:err:]',err, txid); //TODO return cb(err, txid); }) }; From a04cded8b9f31014e692237f73b766736f3e03cc Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 21:00:12 -0300 Subject: [PATCH 7/8] refactor _checkSignatures --- lib/model/txproposal.js | 46 ++++++++++++++++++++++++++--------- lib/model/txproposalaction.js | 2 ++ lib/server.js | 6 +---- test/integration.js | 9 ++++++- test/txproposal.js | 22 +++-------------- 5 files changed, 48 insertions(+), 37 deletions(-) diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index 292f7c6..fbb41f3 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -69,6 +69,15 @@ TxProposal.prototype._updateStatus = function() { }; +TxProposal.prototype._getCurrentSignatures = function() { + return _.map(_.some(this.actions, 'xpub'), function(x) { + return { + signatures: x.signatures, + xpub: xpub, + }; + }); +}; + TxProposal.prototype._getBitcoreTx = function() { var self = this; @@ -81,6 +90,13 @@ TxProposal.prototype._getBitcoreTx = function() { .change(this.changeAddress); t._updateChangeOutput(); + + + var sigs = this._getCurrentSignatures(); + _.each(sigs, function(x) { + this._addSignaturesToBitcoreTx(t, x.signatures, x.xpub); + }); + return t; }; @@ -121,22 +137,20 @@ TxProposal.prototype.getActionBy = function(copayerId) { }; }; -TxProposal.prototype.addAction = function(copayerId, type, signatures) { +TxProposal.prototype.addAction = function(copayerId, type, signatures, xpub) { var action = new TxProposalAction({ copayerId: copayerId, type: type, signatures: signatures, + xpub: xpub, }); this.actions[copayerId] = action; this._updateStatus(); }; -// TODO: no sure we should receive xpub or a list of pubkeys (pre derived) -TxProposal.prototype.checkSignatures = function(signatures, xpub) { +TxProposal.prototype._addSignaturesToBitcoreTx = function(t, signatures, xpub) { var self = this; - var t = this._getBitcoreTx(); - if (signatures.length != this.inputs.length) return false; @@ -159,17 +173,25 @@ TxProposal.prototype.checkSignatures = function(signatures, xpub) { t.applySignature(s); oks++; - } catch (e) { - // TODO only for debug now - console.log('DEBUG ONLY:', e.message); //TODO - }; + } catch (e) {}; }); - return oks === t.inputs.length; + + if (oks != t.inputs.length) + throw new Error('wrong signatures'); }; -TxProposal.prototype.sign = function(copayerId, signatures) { - this.addAction(copayerId, 'accept', signatures); +TxProposal.prototype.sign = function(copayerId, signatures, xpub) { + + // Tests signatures are OK + var t = this._getBitcoreTx(); + try { + this._addSignaturesToBitcoreTx(t, signatures, xpub); + this.addAction(copayerId, 'accept', signatures, xpub); + return true; + } catch (e) { + return false; + } }; TxProposal.prototype.reject = function(copayerId) { diff --git a/lib/model/txproposalaction.js b/lib/model/txproposalaction.js index 8e0f3af..13fa98b 100644 --- a/lib/model/txproposalaction.js +++ b/lib/model/txproposalaction.js @@ -7,6 +7,7 @@ function TxProposalAction(opts) { this.copayerId = opts.copayerId; this.type = opts.type || (opts.signatures ? 'accept' : 'reject'); this.signatures = opts.signatures; + this.xpub = opts.xpub; }; TxProposalAction.fromObj = function (obj) { @@ -16,6 +17,7 @@ TxProposalAction.fromObj = function (obj) { x.copayerId = obj.copayerId; x.type = obj.type; x.signatures = obj.signatures; + x.xpub = obj.xpub; return x; }; diff --git a/lib/server.js b/lib/server.js index 7aff2fc..d186758 100644 --- a/lib/server.js +++ b/lib/server.js @@ -585,9 +585,7 @@ CopayServer.prototype.removePendingTx = function(opts, cb) { CopayServer.prototype._broadcastTx = function(txp, cb) { var raw = txp.getRawTx(); var bc = this._getBlockExplorer('insight', txp.getNetworkName()); -console.log('[server.js.588:raw:]',raw); //TODO bc.broadcast(raw, function(err, txid) { -console.log('[server.js.589:err:]',err, txid); //TODO return cb(err, txid); }) }; @@ -622,11 +620,9 @@ CopayServer.prototype.signTx = function(opts, cb) { var copayer = wallet.getCopayer(self.copayerId); - if (!txp.checkSignatures(opts.signatures, copayer.xPubKey)) + if (!txp.sign(self.copayerId, opts.signatures, copayer.xPubKey)) return cb(new ClientError('BADSIGNATURES', 'Bad signatures')); - txp.sign(self.copayerId, opts.signatures); - self.storage.storeTx(self.walletId, txp, function(err) { if (err) return cb(err); diff --git a/test/integration.js b/test/integration.js index b4685f4..e80218c 100644 --- a/test/integration.js +++ b/test/integration.js @@ -114,7 +114,14 @@ helpers.createUtxos = function(server, wallet, amounts, cb) { }; -helpers.stubBlockExplorer = function(server, utxos, txid) { +helpers.stubBlockExplorer = function(server, inUtxos, txid) { + + var utxos = _.map(inUtxos, function(x) { + x.toObject = function() { + return this; + }; + return x; + }); var bc = sinon.stub(); bc.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos); diff --git a/test/txproposal.js b/test/txproposal.js index a3709bf..131862e 100644 --- a/test/txproposal.js +++ b/test/txproposal.js @@ -28,10 +28,10 @@ describe('TXProposal', function() { describe('#sign', function() { it('should sign 2-2', function() { var txp = TXP.fromObj(aTXP()); - txp.sign('1', theSignatures); + txp.sign('1', theSignatures, theXPub); txp.isAccepted().should.equal(false); txp.isRejected().should.equal(false); - txp.sign('2', theSignatures); + txp.sign('2', theSignatures, theXPub); txp.isAccepted().should.equal(true); txp.isRejected().should.equal(false); }); @@ -59,26 +59,10 @@ describe('TXProposal', function() { }); }); - - describe('#checkSignatures', function() { - it('should check signatures', function() { - var txp = TXP.fromObj(aTXP()); - var xpriv = new Bitcore.HDPrivateKey(theXPriv); - var priv = xpriv.derive(txp.inputPaths[0]).privateKey; - - var t = txp._getBitcoreTx(); - t.sign(priv); - - var s = t.getSignatures(priv)[0].signature.toDER().toString('hex'); - var xpub = new Bitcore.HDPublicKey(xpriv); - - var res = txp.checkSignatures([s], xpub); - res.should.equal(true); - }); - }); }); var theXPriv = 'xprv9s21ZrQH143K2rMHbXTJmWTuFx6ssqn1vyRoZqPkCXYchBSkp5ey8kMJe84sxfXq5uChWH4gk94rWbXZt2opN9kg4ufKGvUM7HQSLjnoh7e'; +var theXPub = 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2Q9npnVmdTAthYGc3N3uxm5sEdnTpSqBc4YYTAhNnoSxCm9'; var theSignatures = ['3045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9']; var aTXP = function() { From 5953dc8f1d7734d59ff7b49e491520e9d5f05d3e Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 13 Feb 2015 21:16:18 -0300 Subject: [PATCH 8/8] fix getRawTx --- lib/model/txproposal.js | 10 +++++++--- lib/server.js | 1 + test/txproposal.js | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index fbb41f3..11546fd 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -70,10 +70,14 @@ TxProposal.prototype._updateStatus = function() { TxProposal.prototype._getCurrentSignatures = function() { - return _.map(_.some(this.actions, 'xpub'), function(x) { + var acceptedActions = _.filter(this.actions, function(x) { + return x && x.type == 'accept'; + }); + + return _.map(acceptedActions, function(x) { return { signatures: x.signatures, - xpub: xpub, + xpub: x.xpub, }; }); }; @@ -94,7 +98,7 @@ TxProposal.prototype._getBitcoreTx = function() { var sigs = this._getCurrentSignatures(); _.each(sigs, function(x) { - this._addSignaturesToBitcoreTx(t, x.signatures, x.xpub); + self._addSignaturesToBitcoreTx(t, x.signatures, x.xpub); }); return t; diff --git a/lib/server.js b/lib/server.js index d186758..02558b2 100644 --- a/lib/server.js +++ b/lib/server.js @@ -584,6 +584,7 @@ CopayServer.prototype.removePendingTx = function(opts, cb) { CopayServer.prototype._broadcastTx = function(txp, cb) { var raw = txp.getRawTx(); +console.log('[server.js.586:raw:]',raw); //TODO var bc = this._getBlockExplorer('insight', txp.getNetworkName()); bc.broadcast(raw, function(err, txid) { return cb(err, txid); diff --git a/test/txproposal.js b/test/txproposal.js index 131862e..c463a8b 100644 --- a/test/txproposal.js +++ b/test/txproposal.js @@ -37,6 +37,16 @@ describe('TXProposal', function() { }); }); + describe('#getRawTx', function() { + it('should generate correct raw transaction for signed 2-2', function() { + var txp = TXP.fromObj(aTXP()); + txp.sign('1', theSignatures, theXPub); + txp.getRawTx().should.equal('0100000001ab069f7073be9b491bb1ad4233a45d2e383082ccc7206df905662d6d8499e66e080000009200483045022100896aeb8db75fec22fddb5facf791927a996eb3aee23ee6deaa15471ea46047de02204c0c33f42a9d3ff93d62738712a8c8a5ecd21b45393fdd144e7b01b5a186f1f9014752210319008ffe1b3e208f5ebed8f46495c056763f87b07930a7027a92ee477fb0cb0f2103b5f035af8be40d0db5abb306b7754949ab39032cf99ad177691753b37d10130152aeffffffff0280f0fa02000000001976a91451224bca38efcaa31d5340917c3f3f713b8b20e488ac70c9fa020000000017a914778192003f0e9e1d865c082179cc3dae5464b03d8700000000'); + }); + }); + + + describe('#reject', function() { it('should reject 2-2', function() { var txp = TXP.fromObj(aTXP());