diff --git a/bit-wallet/bit-txproposals b/bit-wallet/bit-txproposals index 89c2607..bb64bc4 100755 --- a/bit-wallet/bit-txproposals +++ b/bit-wallet/bit-txproposals @@ -7,8 +7,8 @@ var utils = require('./cli-utils'); program = utils.configureCommander(program); program - .option('-i, --input [filename]', 'input file') - .option('-o, --output [filename]', 'output file') + .option('-i, --input [filename]', 'use input file instead of server\'s') + .option('-o, --output [filename]', 'write tx to output file') .parse(process.argv); var args = program.args; diff --git a/lib/client/api.js b/lib/client/api.js index 9aef259..7b73580 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -178,7 +178,7 @@ API.prototype._doRequest = function(method, url, args, data, cb) { return cb(_parseError(body)); } - return cb(null, body); + return cb(null, body, res.header); }); }; @@ -490,8 +490,15 @@ API.prototype.import = function(str, cb) { API.prototype.parseTxProposals = function(txps, cb) { var self = this; - this._loadAndCheck(function(err, data) { + this._load(function(err, data) { if (err) return cb(err); + if (data.n > 1) { + var pkrComplete = data.publicKeyRing && data.m && data.publicKeyRing.length === data.n; + if (!pkrComplete) { + return cb('Wallet Incomplete'); + } + } + _processTxps(txps, data.sharedEncryptingKey); diff --git a/lib/expressapp.js b/lib/expressapp.js index 64e2d42..8f8c641 100644 --- a/lib/expressapp.js +++ b/lib/expressapp.js @@ -14,6 +14,13 @@ log.level = 'debug'; var ExpressApp = function() {}; +/** + * start + * + * @param opts.WalletService options for WalletService class + * @param opts.basePath + * @param opts.disableLogs + */ ExpressApp.start = function(opts) { opts = opts || {}; @@ -28,7 +35,7 @@ ExpressApp.start = function(opts) { }); var allowCORS = function(req, res, next) { if ('OPTIONS' == req.method) { - res.send(200); + res.sendStatus(200); res.end(); return; } @@ -42,7 +49,12 @@ ExpressApp.start = function(opts) { limit: POST_LIMIT })); - app.use(require('morgan')('dev')); + if (!opts.disableLogs) { + // TODO access.log + //var accessLogStream = fs.createWriteStream(__dirname + '/access.log', {flags: 'a'}) + //app.use(morgan('combined', {stream: accessLogStream})) + app.use(require('morgan')('dev')); + } var router = express.Router(); @@ -51,7 +63,9 @@ ExpressApp.start = function(opts) { if (err instanceof WalletService.ClientError) { var status = (err.code == 'NOTAUTHORIZED') ? 401 : 400; - log.error('Err: ' + status + ':' + req.url + ' :' + err.code + ':' + err.message); + if (!opts.disableLogs) + log.error('Err: ' + status + ':' + req.url + ' :' + err.code + ':' + err.message); + res.status(status).json({ code: err.code, error: err.message, @@ -64,7 +78,9 @@ ExpressApp.start = function(opts) { } var m = message || err.toString(); - log.error('Error: ' + req.url + ' :' + code + ':' + m); + if (!opts.disableLogs) + log.error('Err: ' + req.url + ' :' + code + ':' + m); + res.status(code || 500).json({ error: m, }).end(); @@ -106,7 +122,6 @@ ExpressApp.start = function(opts) { var server = WalletService.getInstance(); server.createWallet(req.body, function(err, walletId) { if (err) return returnError(err, res, req); - res.json({ walletId: walletId, }); @@ -255,7 +270,7 @@ ExpressApp.start = function(opts) { res.end(); }); }); - app.use(opts.base_path || '/copay/api', router); + app.use(opts.basePath || '/copay/api', router); return app; }; diff --git a/lib/server.js b/lib/server.js index 336af2d..de67f34 100644 --- a/lib/server.js +++ b/lib/server.js @@ -432,28 +432,22 @@ WalletService.prototype.getBalance = function(opts, cb) { WalletService.prototype._selectUtxos = function(txp, utxos) { -console.log('[server.js.434:utxos:]',utxos); //TODO var i = 0; var total = 0; var selected = []; var inputs = _.sortBy(utxos, 'amount'); -console.log('[server.js.438:inputs:]',inputs); //TODO while (i < inputs.length) { selected.push(inputs[i]); total += inputs[i].satoshis; -console.log('[server.js.443]'); //TODO if (total >= txp.amount + Bitcore.Transaction.FEE_PER_KB) { try { // Check if there are enough fees txp.inputs = selected; - -console.log('[server.js.449]'); //TODO var raw = txp.getRawTx(); return; } catch (ex) { -console.log('[server.js.453:ex:]',ex); //TODO if (ex.name != 'bitcore.ErrorTransactionFeeError') { throw ex.message; } @@ -504,7 +498,6 @@ WalletService.prototype.createTx = function(opts, cb) { return cb(new ClientError('DUSTAMOUNT', 'Amount below dust threshold')); self._getUtxos(function(err, utxos) { -console.log('[server.js.506:utxos:]',utxos); //TODO if (err) return cb(err); var changeAddress = wallet.createAddress(true); @@ -536,7 +529,6 @@ console.log('[server.js.523:ex:]',ex); //TODO txp.inputPaths = _.pluck(txp.inputs, 'path'); -console.log('[server.js.530]'); //TODO self.storage.storeAddressAndWallet(wallet, changeAddress, function(err) { if (err) return cb(err); diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 8d8c486..3a05a33 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -11,7 +11,6 @@ var request = require('supertest'); var Client = require('../../lib/client'); var API = Client.API; var Bitcore = require('bitcore'); -var TestData = require('./clienttestdata'); var WalletUtils = require('../../lib/walletutils'); var ExpressApp = require('../../lib/expressapp'); var Storage = require('../../lib/storage'); @@ -138,7 +137,8 @@ describe('client API ', function() { WalletService: { storage: storage, blockExplorer: blockExplorerMock, - } + }, + disableLogs: true, }); // Generates 5 clients _.each(_.range(5), function(i) { @@ -157,6 +157,70 @@ describe('client API ', function() { blockExplorerMock.reset(); }); + describe('Server internals', function() { + it('should allow cors', function(done) { + clients[0]._doRequest('options', '/', null, {}, function(err, x, headers) { + headers['access-control-allow-origin'].should.equal('*'); + should.exist(headers['access-control-allow-methods']); + should.exist(headers['access-control-allow-headers']); + done(); + }); + }); + + it('should handle critical errors', function(done) { + var s = sinon.stub(); + s.storeWallet = sinon.stub().yields('bigerror'); + s.fetchWallet = sinon.stub().yields(null); + app = ExpressApp.start({ + WalletService: { + storage: s, + blockExplorer: blockExplorerMock, + }, + disableLogs: true, + }); + var s2 = sinon.stub(); + s2.load = sinon.stub().yields(null); + var client = new Client({ + storage: s2, + }); + client.request = helpers.getRequest(app); + client.createWallet('1', '2', 1, 1, 'testnet', + function(err) { + err.code.should.equal('ERROR'); + done(); + }); + }); + + it('should handle critical errors (Case2)', function(done) { + var s = sinon.stub(); + s.storeWallet = sinon.stub().yields({ + code: 501, + message: 'wow' + }); + s.fetchWallet = sinon.stub().yields(null); + app = ExpressApp.start({ + WalletService: { + storage: s, + blockExplorer: blockExplorerMock, + }, + disableLogs: true, + }); + var s2 = sinon.stub(); + s2.load = sinon.stub().yields(null); + var client = new Client({ + storage: s2, + }); + client.request = helpers.getRequest(app); + client.createWallet('1', '2', 1, 1, 'testnet', + function(err) { + err.code.should.equal('ERROR'); + done(); + }); + }); + + }); + + describe('Wallet Creation', function() { it('should check balance in a 1-1 ', function(done) { helpers.createAndJoinWallet(clients, 1, 1, function(err) { @@ -362,6 +426,70 @@ describe('client API ', function() { }); }); }); + + it('should be able get Tx proposals from a file', function(done) { + helpers.createAndJoinWallet(clients, 1, 2, function(err, w) { + should.not.exist(err); + clients[0].createAddress(function(err, x0) { + should.not.exist(err); + blockExplorerMock.setUtxo(x0, 1, 1); + var opts = { + amount: 10000000, + toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', + message: 'hello 1-1', + }; + clients[1].sendTxProposal(opts, function(err, x) { + should.not.exist(err); + clients[1].getTxProposals({ + getRawTxps: true + }, function(err, txs, rawTxps) { + should.not.exist(err); + + clients[0].parseTxProposals(rawTxps, function(err, txs2) { + should.not.exist(err); + txs[0].should.deep.equal(txs2[0]); + done(); + }); + + }); + }); + }); + }); + }); + it('should detect fakes from Tx proposals file', function(done) { + helpers.createAndJoinWallet(clients, 1, 2, function(err, w) { + should.not.exist(err); + clients[0].createAddress(function(err, x0) { + should.not.exist(err); + blockExplorerMock.setUtxo(x0, 1, 1); + var opts = { + amount: 10000000, + toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', + message: 'hello 1-1', + }; + clients[1].sendTxProposal(opts, function(err, x) { + should.not.exist(err); + clients[1].getTxProposals({ + getRawTxps: true + }, function(err, txs, rawTxps) { + should.not.exist(err); + + //Tamper + rawTxps[0].amount++; + + clients[0].parseTxProposals(rawTxps, function(err, txs2) { + err.code.should.equal('SERVERCOMPROMISED'); + done(); + }); + + }); + }); + }); + }); + }); + + + }); describe('Address Creation', function() { diff --git a/test/integration/clienttestdata.js b/test/integration/clienttestdata.js deleted file mode 100644 index 643371e..0000000 --- a/test/integration/clienttestdata.js +++ /dev/null @@ -1,168 +0,0 @@ -var storage = { - wallet11: { - "m": 1, - "n": 1, - "walletPrivKey": "{\"bn\":\"6b862ffbfc90a37a2fedbbcfea91c6a4e49f49b6aaa322b6e16c46bfdbe71a38\",\"compressed\":true,\"network\":\"livenet\"}", - "network": "testnet", - "xPrivKey": "tprv8ZgxMBicQKsPeisyNJteQXZnb7CnhYc4TVAyxxicXuxMjK1rmaqVq1xnXtbSTPxUKKL9h5xJhUvw1AKfDD3i98A82eJWSYRWYjmPksewFKR", - "copayerId": "a84daa08-17b5-45ad-84cd-e275f3b07123", - "signingPrivKey": "42798f82c4ed9ace4d66335165071edf180e70bc0fc08dacb3e35185a2141d5b", - "publicKeyRing": ["tpubD6NzVbkrYhZ4YBumFxZEowDuA8iirsny2nmmFUkuxBkkZoGdPyf61Waei3tDYvVa1yqW82Xhmmd6oiibeDyM1MS3zTiky7Yg75UEV9oQhFJ"] - }, - - incompleteWallet22: { - "m": 2, - "n": 2, - "walletPrivKey": "L2Fu6TM1AqSNBaQcjgjvYjGf3EzS3MVSTwEeTw3bvy52x7ZkffWj", - "network": "testnet", - "secret": "b6f57154-0df8-4845-a61d-47ecd648c2d4:eab5a55d9214845ee8d13ea1033e42ec8d7f780ae6e521d830252a80433e91a5:T", - "xPrivKey": "tprv8ZgxMBicQKsPfFVXegcKyJjy2Y5DSrHNrtGBHG1f9pPX75QQdHwHGjWUtR7cCUXV7QcCCDon4cieHWTYscy8M7oXwF3qd3ssfBiV9M68bPB", - "copayerId": "3fc03e7a-6ebc-409b-a4b7-45b14d5a8199", - "signingPrivKey": "0d3c796fb12e387c4b5a5c566312b2b22fa0553ca041d859e3f0987215ca3a4f", - "publicKeyRing": [] - }, - complete22: { - "xPrivKey": "xprv9s21ZrQH143K3nwnRt6W25h7smm4k4nbuN4QKnNkTMDHFcB11wJXYF78TpwQ3xKjik9M66nRd9WUiHB5C8XgoWSbpMRMc2AxpcUNUsi4thi", - "m": 2, - "n": 2, - "publicKeyRing": ["xpub661MyMwAqRbcGzNFbVQLh6CV6ukHuhBn4Bf4CGrQ6pFfNNdJ3pxrEVDtFHGsTzyz6Py23FhP8GWAqew3PsvnstEs2iayH1PK5Mx1bSVSEAG", "xpub661MyMwAqRbcGH2FXudWPDdrRobZ9XWTGaz18AnN1gkG8QW9ZUcn63RcK5qJJ5DXYXeAWBNqprdvvg8VHA5twmBHCUc6gWygXkwmU1Dohwh"], - "copayerId": "c6ef9ad6de90b16174a0c0bdc430238ef6c04cb12e3bafa7c46e5acfb2b9d0b9", - "signingPrivKey": "KyhU3befBaePqHuPQNNyY1XFUgnArR3GUKZpZwV5vS7u1pcR3uzB", - "sharedEncryptingKey": "ezDRS2NRchMJLf1IWtjL5A==", - "network": "livenet" - }, - complete11: { - "copayerId": "56cb00afd85f4f37fa900ac4e367676f2eb6189a773633eb9f119eb21a22ba44", - "xPrivKey": "tprv8ZgxMBicQKsPdjYWSKKh8SuMZAQ6K3J6v5H3A8ZVyyvXk4h1xft3qeRTmCZbxQB77n3ndfF6G4AevqgpiAVuCmZqYURH3wzSQviTvP1nkYN", - "publicKeyRing": ["tpubD6NzVbkrYhZ4XCaJKxzHXrZU8Bv2UNV1VNspSeboQFivaYwnb4he293KwLPxnNNSBEj3RAE5EEaHqPWatzexGd613hGMLLQz5BEgjtpgWnZ"], - "network": "testnet", - "m": 1, - "n": 1, - "signingPrivKey": "KxyNf4A1Td61GMrnC6LNQYsfE9zKvgsFmmgkPYrPoJHva4j9YSL1", - "walletPrivKey": "L4JHs2ZggZkEuRLffH2WVe337nwZWMxytxViLLGyAfGREk6bUCXo", - "sharedEncryptingKey": "ezDRS2NRchMJLf1IWtjL5A==" - }, -}; - -var serverResponse = { - completeWallet: { - wallet: { - m: 2, - n: 2, - status: 'complete', - publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' - ], - addressIndex: 0, - copayers: [{ - xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', - }, { - xPubKey: 'tpubD6NzVbkrYhZ4YiXKYLGvNiQ5bZb9cBUHSBrxZn3xa6BuwZfBFgksTE8M4ZFBLWVJ4PLnAJs2JKhkpJVqsrJEAkGpb62rx62Bk4o4N5Lz8dQ', - xPubKeySignature: '3045022100e03b069db333428153c306c9bf66ebc7f25e7d7f3d087e1ca7234fbbb1a47efa02207421fb375d0dd7a7f2116301f2cdf1bce88554a6c88a82d4ec9fb37fb6680ae8', - }], - pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', - network: 'testnet', - } - }, - - missingMyPubKey: { - wallet: { - m: 2, - n: 2, - status: 'complete', - publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' - ], - addressIndex: 0, - copayers: [{ - xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', - }, { - xPubKey: 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV', - xPubKeySignature: '3044022025c93b418ebdbb66a0f2b21af709420e8ae769bf054f29aaa252cb5417c46a2302205e0c8b931324736b7eea4971a48039614e19abe26e13ab0ef1547aef92b55aab', - }], - pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', - network: 'testnet', - } - }, - - - incompleteWallet: { - wallet: { - m: 2, - n: 2, - status: 'pending', - publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' - ], - addressIndex: 0, - copayers: [{ - xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', - }], - pubKey: ' { "x": "b2903ab878ed1316f82b859e9807e23bab3d579175563e1068d2ed9c9e37873c", "y": "5f30165915557394223a58329c1527dfa0f34f483d8aed02e0638f9124dbddef", "compressed": true }', - network: 'testnet', - } - }, - - corruptWallet22: { - wallet: { - m: 2, - n: 2, - status: 'complete', - publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' - ], - copayers: [{ - xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - xPubKeySignature: '3045022100ef86122060bbb7681db05486f8b1ee1579c5800e8da78182a87384f05542a4cc0220215ce7ef8c484b64178779414efdf2b7033d25ed752eebf4eb3241f9fa8e6b67', - }, { - xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - xPubKeySignature: 'bababa', - }], - } - }, - corruptWallet222: { - wallet: { - m: 2, - n: 2, - status: 'complete', - publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - 'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV' - ], - copayers: [{ - xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E', - }, ], - } - }, - pendingTxs: [{ - version: '1.0.0', - createdOn: 1424287779, - id: '01424287779165d80a7123-f5c4-4144-ac1b-663220c01c55', - creatorId: '02da4d1bd797b41a5565fe54f22583051bd3c29dbbc86eedadb6af6e9200af1048', - toAddress: 'n2TBMPzPECGUfcT2EByiTJ12TPZkhN2mN5', - amount: 10000, - message: '{"iv":"vDH4J15lBiokSP+iUS4ofA==","v":1,"iter":1,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","ct":"X/vPh+vh/Qv8IRSx"}', - changeAddress: '2Mu4nmHhBWFk766M4yXJi2oyi59HZLKHWqn', - inputs: [{ - address: '2MvBhgsQX6EusXvNGdcjM5dschDkaos1uuk', - txid: '006a2915d8f1811620f8b83357869a94c53714f90ce4be4a5ca0d5305a21400d', - vout: 0, - scriptPubKey: 'a914203dd60a7659995a6e3bba7caac3c2d684c836ce87', - amount: 0.1, - path: 'm/2147483647/0/0', - publicKeys: ['0371629fe3547002723ef57ad4dae0d97653cf89b0a570637cffca2f23982f0f92'] - }], - requiredSignatures: 1, - requiredRejections: 1, - status: 'pending', - inputPaths: ['m/2147483647/0/0'], - actions: {}, - creatorName: 'ematiu' - }], - -}; - -module.exports.serverResponse = serverResponse; -module.exports.storage = storage; diff --git a/test/integration/server.js b/test/integration/server.js index 939aff3..c334a2e 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -100,13 +100,15 @@ helpers.stubUtxos = function(server, wallet, amounts, cb) { var utxos = _.map(amounts, function(amount, i) { var address = addresses[i % addresses.length]; - return { + var obj = { txid: helpers.randomTXID(), vout: Math.floor((Math.random() * 10) + 1), satoshis: helpers.toSatoshi(amount).toString(), scriptPubKey: address.getScriptPubKey(wallet.m).toBuffer().toString('hex'), address: address.address, }; + obj.toObject = function() {return obj;}; + return obj; }); blockExplorer.getUnspentUtxos = sinon.stub().callsArgWith(1, null, utxos); @@ -191,6 +193,7 @@ describe('Copay server', function() { helpers.offset = 0; }); + describe('#getInstanceWithAuth', function() { beforeEach(function() {});