Browse Source

Merge pull request #68 from matiu/feat/airgapped01

Feat/airgapped01
activeAddress
Matias Alejo Garcia 10 years ago
parent
commit
851f4bd544
  1. 6
      README.md
  2. 2
      bit-wallet/bit-balance
  3. 19
      bit-wallet/bit-status
  4. 38
      bit-wallet/bit-txproposals
  5. 35
      bit-wallet/cli-utils.js
  6. 41
      lib/client/api.js
  7. 27
      lib/expressapp.js
  8. 3
      lib/server.js
  9. 132
      test/integration/clientApi.js
  10. 168
      test/integration/clienttestdata.js
  11. 5
      test/integration/server.js

6
README.md

@ -35,7 +35,9 @@ A Multisig HD Wallet Service, with minimun server trust.
# Spend coins. Amount can be specified in btc, bit or sat (default)
./bit send 1xxxxx 100bit "100 bits to mother"
# You can use 100bit or 0.00001btc or 10000sat. (Set up BIT_UNIT to btc/sat/bit so select output unit).
# List pending TX Proposals
./bit status
@ -45,7 +47,7 @@ A Multisig HD Wallet Service, with minimun server trust.
# List all commands:
./bit --help
```

2
bit-wallet/bit-balance

@ -13,5 +13,5 @@ var client = utils.getClient(program);
client.getBalance(function(err, x) {
utils.die(err);
console.log('* Wallet balance', x);
console.log('* Wallet balance %s (Locked %s)', utils.renderAmount(x.totalAmount), utils.renderAmount(x.lockedAmount) );
});

19
bit-wallet/bit-status

@ -22,20 +22,7 @@ client.getStatus(function(err, res) {
console.log('* Copayers:', _.pluck(x.copayers,'name').join(', '));
var x = res.balance;
console.log('* Balance %dSAT (Locked: %dSAT)', x.totalAmount, x.lockedAmount);
if (!_.isEmpty(res.pendingTxps)) {
console.log("* TX Proposals:")
_.each(res.pendingTxps, function(x) {
missingSignatures = x.requiredSignatures - _.filter(_.values(x.actions), function (a) { return a.type == 'accept'; }).length;
console.log("\t%s [\"%s\" by %s] %dSAT => %s", utils.shortID(x.id), x.message, x.creatorName, x.amount, x.toAddress);
if (!_.isEmpty(x.actions)) {
console.log('\t\tActions: ', _.map(x.actions, function(a) {
return a.copayerName + ' ' + (a.type == 'accept' ? '✓' : '✗') + (a.comment ? ' (' + a.comment + ')' : '');
}).join('. '));
}
console.log('\t\tMissing signatures: ' + missingSignatures);
});
}
console.log('* Balance %s (Locked: %s)', utils.renderAmount(x.totalAmount), utils.renderAmount(x.lockedAmount));
utils.renderTxProposals(res.pendingTxps);
});

38
bit-wallet/bit-txproposals

@ -0,0 +1,38 @@
#!/usr/bin/env node
var _ = require('lodash');
var fs = require('fs');
var program = require('commander');
var utils = require('./cli-utils');
program = utils.configureCommander(program);
program
.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;
var client = utils.getClient(program);
var txData;
function end(err, txps, rawtxps) {
utils.die(err);
if (program.input) {
console.log('\n* From File : %s\n', program.input);
}
utils.renderTxProposals(txps);
if (program.output) {
fs.writeFileSync(program.output, JSON.stringify(rawtxps));
console.log(' * Proposals Saved to: %s\n', program.output);
}
};
if (program.input) {
var txData = fs.readFileSync(program.input);
txData = JSON.parse(txData);
client.parseTxProposals(txData, end);
} else {
client.getTxProposals({getRawTxps: !!program.output}, end);
}

35
bit-wallet/cli-utils.js

@ -99,4 +99,39 @@ Utils.configureCommander = function(program) {
return program;
};
Utils.renderAmount = function(amount) {
var unit = process.env.BIT_UNIT || 'bit';
if (unit === 'SAT') {
// Do nothing
} else if (process.env.BIT_UNIT === 'btc') {
amount = amount / 1e8;
} else {
amount = amount / 100;
}
amount = (parseFloat(amount.toPrecision(12)));
return amount.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' ' + unit;
};
Utils.renderTxProposals = function(txps) {
if (_.isEmpty(txps))
return;
console.log("* TX Proposals:")
_.each(txps, function(x) {
var missingSignatures = x.requiredSignatures - _.filter(_.values(x.actions), function(a) {
return a.type == 'accept';
}).length;
console.log("\t%s [\"%s\" by %s] %s => %s", Utils.shortID(x.id), x.message, x.creatorName, Utils.renderAmount(x.amount), x.toAddress);
if (!_.isEmpty(x.actions)) {
console.log('\t\tActions: ', _.map(x.actions, function(a) {
return a.copayerName + ' ' + (a.type == 'accept' ? '✓' : '✗') + (a.comment ? ' (' + a.comment + ')' : '');
}).join('. '));
}
console.log('\t\tMissing signatures: ' + missingSignatures);
});
};
module.exports = Utils;

41
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);
});
};
@ -487,9 +487,42 @@ API.prototype.import = function(str, cb) {
});
};
/**
*
*/
API.prototype.parseTxProposals = function(txps, cb) {
var self = this;
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);
var fake = _.any(txps, function(txp) {
return (!Verifier.checkTxProposal(data, txp));
});
if (fake)
return cb(new ServerCompromisedError('Server sent fake transaction proposal'));
return cb(null, txps);
});
};
/**
*
* opts.doNotVerify
* opts.getRawTxps
* @return {undefined}
*/
@ -502,6 +535,10 @@ API.prototype.getTxProposals = function(opts, cb) {
self._doGetRequest(url, data, function(err, txps) {
if (err) return cb(err);
var rawTxps;
if (opts.getRawTxps)
rawTxps = JSON.parse(JSON.stringify(txps));
_processTxps(txps, data.sharedEncryptingKey);
var fake = _.any(txps, function(txp) {
@ -511,7 +548,7 @@ API.prototype.getTxProposals = function(opts, cb) {
if (fake)
return cb(new ServerCompromisedError('Server sent fake transaction proposal'));
return cb(null, txps);
return cb(null, txps, rawTxps);
});
});
};

27
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;
};

3
lib/server.js

@ -363,7 +363,7 @@ WalletService.prototype._getUtxos = function(cb) {
bc.getUnspentUtxos(addressStrs, function(err, inutxos) {
if (err) return cb(err);
var utxos = _.map(inutxos, function(i) {
return _.pick(i, ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis']);
return _.pick(i.toObject(), ['txid', 'vout', 'address', 'scriptPubKey', 'amount', 'satoshis']);
});
self.getPendingTxs({}, function(err, txps) {
if (err) return cb(err);
@ -520,6 +520,7 @@ WalletService.prototype.createTx = function(opts, cb) {
try {
self._selectUtxos(txp, utxos);
} catch (ex) {
console.log('[server.js.523:ex:]',ex); //TODO
return cb(new ClientError(ex));
}

132
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) {
@ -374,6 +438,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() {

168
test/integration/clienttestdata.js

@ -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;

5
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() {});

Loading…
Cancel
Save