commit
1e3366617e
|
@ -15,6 +15,8 @@ program
|
|||
.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('export', 'export wallet critical data')
|
||||
.command('import', 'import wallet critical data')
|
||||
.parse(process.argv);
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var program = require('commander');
|
||||
var qr = require('qr-image');
|
||||
|
||||
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')
|
||||
.option('-q, --qr')
|
||||
.parse(process.argv);
|
||||
|
||||
var args = program.args;
|
||||
var client = utils.getClient(program);
|
||||
|
||||
client.export(function(err, x) {
|
||||
utils.die(err);
|
||||
if (program.qr) {
|
||||
var filename = program.config + '.svg';
|
||||
var qr_svg = qr.image(x, { type: 'svg' });
|
||||
qr_svg.pipe(require('fs').createWriteStream(filename));
|
||||
console.log('Wallet Critical Data: exported to ' + filename);
|
||||
} else {
|
||||
console.log('Wallet Critical Data:\n', x);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env node
|
||||
var program = require('commander');
|
||||
|
||||
var Client = require('../lib/client');
|
||||
var utils = require('./cli-utils');
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
program
|
||||
.version('0.0.1')
|
||||
.option('-c, --config [file]', 'Wallet config filename')
|
||||
.option('-v, --verbose', 'be verbose')
|
||||
.usage('import [options] <file>')
|
||||
.parse(process.argv);
|
||||
|
||||
var args = program.args;
|
||||
|
||||
if (!args[0])
|
||||
program.help();
|
||||
|
||||
var client = utils.getClient(program);
|
||||
var str = fs.readFileSync(args[0]);
|
||||
|
||||
client.import(str, function(err, x) {
|
||||
utils.die(err);
|
||||
console.log('Wallet Imported');
|
||||
});
|
|
@ -33,7 +33,7 @@ Utils.shortID = function(id) {
|
|||
|
||||
Utils.getClient = function(args) {
|
||||
var storage = new Client.FileStorage({
|
||||
filename: args.config
|
||||
filename: args.config || process.env['BIT_FILE'],
|
||||
});
|
||||
return new Client({
|
||||
storage: storage,
|
||||
|
|
|
@ -15,6 +15,8 @@ var ServerCompromisedError = require('./servercompromisederror')
|
|||
|
||||
var BASE_URL = 'http://localhost:3001/copay/api';
|
||||
|
||||
var WALLET_CRITICAL_DATA = ['xPrivKey', 'm', 'publicKeyRing'];
|
||||
|
||||
function _createProposalOpts(opts, signingKey) {
|
||||
var msg = opts.toAddress + '|' + opts.amount + '|' + opts.message;
|
||||
opts.proposalSignature = WalletUtils.signMessage(msg, signingKey);
|
||||
|
@ -104,7 +106,12 @@ API.prototype._loadAndCheck = function(cb) {
|
|||
};
|
||||
|
||||
API.prototype._doRequest = function(method, url, args, data, cb) {
|
||||
var reqSignature = _signRequest(method, url, args, data.signingPrivKey);
|
||||
var reqSignature;
|
||||
data = data || {};
|
||||
|
||||
if (data.signingPrivKey)
|
||||
reqSignature = _signRequest(method, url, args, data.signingPrivKey);
|
||||
|
||||
var absUrl = _getUrl(url);
|
||||
var args = {
|
||||
headers: {
|
||||
|
@ -142,8 +149,6 @@ API.prototype._doGetRequest = function(url, data, cb) {
|
|||
return this._doRequest('get', url, {}, data, cb);
|
||||
};
|
||||
|
||||
|
||||
|
||||
API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) {
|
||||
var self = this;
|
||||
network = network || 'livenet';
|
||||
|
@ -154,63 +159,46 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
|
|||
if (data)
|
||||
return cb('Storage already contains a wallet');
|
||||
|
||||
console.log('[API.js.132]'); //TODO
|
||||
// Generate wallet key pair to verify copayers
|
||||
var privKey = new Bitcore.PrivateKey(null, network);
|
||||
var pubKey = privKey.toPublicKey();
|
||||
|
||||
data = {
|
||||
m: m,
|
||||
n: n,
|
||||
walletPrivKey: privKey.toWIF(),
|
||||
network: network,
|
||||
};
|
||||
|
||||
var walletPrivKey = new Bitcore.PrivateKey();
|
||||
var args = {
|
||||
name: walletName,
|
||||
m: m,
|
||||
n: n,
|
||||
pubKey: pubKey.toString(),
|
||||
pubKey: walletPrivKey.toPublicKey().toString(),
|
||||
network: network,
|
||||
};
|
||||
var url = '/v1/wallets/';
|
||||
|
||||
self._doPostRequest(url, args, data, function(err, body) {
|
||||
self._doPostRequest(url, args, {}, function(err, body) {
|
||||
if (err) return cb(err);
|
||||
|
||||
var walletId = body.walletId;
|
||||
var secret = walletId + ':' + privKey.toString() + ':' + (network == 'testnet' ? 'T' : 'L');
|
||||
var secret = walletId + ':' + walletPrivKey.toWIF() + ':' + (network == 'testnet' ? 'T' : 'L');
|
||||
var ret;
|
||||
|
||||
if (n > 1)
|
||||
ret = data.secret = secret;
|
||||
|
||||
self.storage.save(data, function(err) {
|
||||
if (err) return cb(err);
|
||||
self._joinWallet(data, secret, copayerName, function(err) {
|
||||
return cb(err, ret);
|
||||
});
|
||||
ret = secret;
|
||||
|
||||
self._joinWallet(secret, copayerName, function(err) {
|
||||
return cb(err, ret);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
API.prototype._joinWallet = function(data, secret, copayerName, cb) {
|
||||
API.prototype._joinWallet = function(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 xPrivKey = _createXPrivKey(network);
|
||||
|
||||
var xPubKey = new Bitcore.HDPublicKey(data.xPrivKey);
|
||||
var xPubKey = new Bitcore.HDPublicKey(xPrivKey);
|
||||
var xPubKeySignature = WalletUtils.signMessage(xPubKey.toString(), walletPrivKey);
|
||||
|
||||
var signingPrivKey = (new Bitcore.HDPrivateKey(data.xPrivKey)).derive('m/1/0').privateKey;
|
||||
var signingPrivKey = (new Bitcore.HDPrivateKey(xPrivKey)).derive('m/1/0').privateKey;
|
||||
var args = {
|
||||
walletId: walletId,
|
||||
name: copayerName,
|
||||
|
@ -219,15 +207,20 @@ API.prototype._joinWallet = function(data, secret, copayerName, cb) {
|
|||
};
|
||||
var url = '/v1/wallets/' + walletId + '/copayers';
|
||||
|
||||
this._doPostRequest(url, args, data, function(err, body) {
|
||||
this._doPostRequest(url, args, {}, function(err, body) {
|
||||
var wallet = body.wallet;
|
||||
data.copayerId = body.copayerId;
|
||||
data.walletPrivKey = walletPrivKey.toWIF();
|
||||
data.signingPrivKey = signingPrivKey.toString();
|
||||
data.m = wallet.m;
|
||||
data.n = wallet.n;
|
||||
data.publicKeyRing = wallet.publicKeyRing;
|
||||
data.network = wallet.network,
|
||||
var data = {
|
||||
copayerId: body.copayerId,
|
||||
|
||||
publicKeyRing: wallet.publicKeyRing,
|
||||
network: wallet.network,
|
||||
m: wallet.m,
|
||||
n: wallet.n,
|
||||
|
||||
xPrivKey: xPrivKey,
|
||||
walletPrivKey: walletPrivKey.toWIF(),
|
||||
signingPrivKey: signingPrivKey.toWIF(),
|
||||
};
|
||||
self.storage.save(data, cb);
|
||||
});
|
||||
};
|
||||
|
@ -239,7 +232,7 @@ API.prototype.joinWallet = function(secret, copayerName, cb) {
|
|||
if (data)
|
||||
return cb('Storage already contains a wallet');
|
||||
|
||||
self._joinWallet(data, secret, copayerName, cb);
|
||||
self._joinWallet(secret, copayerName, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -309,66 +302,116 @@ API.prototype.getBalance = function(cb) {
|
|||
});
|
||||
};
|
||||
|
||||
API.prototype.export = function(cb) {
|
||||
var self = this;
|
||||
|
||||
this._loadAndCheck(function(err, data) {
|
||||
if (err) return cb(err);
|
||||
var v = [];
|
||||
|
||||
var myXPubKey = (new Bitcore.HDPublicKey(data.xPrivKey)).toString();
|
||||
|
||||
_.each(WALLET_CRITICAL_DATA, function(k) {
|
||||
var d;
|
||||
if (k === 'publicKeyRing') {
|
||||
d = _.without(data[k], myXPubKey);
|
||||
} else {
|
||||
d = data[k];
|
||||
}
|
||||
v.push(d);
|
||||
});
|
||||
return cb(null, JSON.stringify(v));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
API.prototype.import = function(str, cb) {
|
||||
var self = this;
|
||||
|
||||
this.storage.load(function(err, data) {
|
||||
if (data)
|
||||
return cb('Storage already contains a wallet');
|
||||
|
||||
data = {};
|
||||
|
||||
var inData = JSON.parse(str);
|
||||
var i = 0;
|
||||
|
||||
_.each(WALLET_CRITICAL_DATA, function(k) {
|
||||
data[k] = inData[i++];
|
||||
if (!data[k])
|
||||
return cb('Invalid wallet data');
|
||||
});
|
||||
|
||||
var xPubKey = (new Bitcore.HDPublicKey(data.xPrivKey)).toString();
|
||||
|
||||
data.publicKeyRing.push(xPubKey);
|
||||
data.copayerId = WalletUtils.xpubToCopayerId(xPubKey);
|
||||
data.m = data.publicKeyRing.length;
|
||||
data.signingPrivKey = (new Bitcore.HDPrivateKey(data.xPrivKey)).derive('m/1/0').privateKey.toWIF();
|
||||
data.network = data.xPrivKey.substr(0,4) === 'tprv' ? 'testnet' : 'livenet';
|
||||
self.storage.save(data, cb);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
API.prototype.getTxProposals = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
this._loadAndCheck(
|
||||
function(err, data) {
|
||||
if (err) return cb(err);
|
||||
var url = '/v1/txproposals/';
|
||||
self._doGetRequest(url, data, cb);
|
||||
});
|
||||
this._loadAndCheck(function(err, data) {
|
||||
if (err) return cb(err);
|
||||
var url = '/v1/txproposals/';
|
||||
self._doGetRequest(url, data, cb);
|
||||
});
|
||||
};
|
||||
|
||||
API.prototype.signTxProposal = function(txp, cb) {
|
||||
var self = this;
|
||||
|
||||
this._loadAndCheck(
|
||||
function(err, data) {
|
||||
if (err) return cb(err);
|
||||
this._loadAndCheck(function(err, data) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (!Verifier.checkTxProposal(data, txp)) {
|
||||
return cb(new ServerCompromisedError('Server sent fake transaction proposal'));
|
||||
}
|
||||
|
||||
|
||||
//Derive proper key to sign, for each input
|
||||
var privs = [],
|
||||
derived = {};
|
||||
//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);
|
||||
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]) {
|
||||
derived[i.path] = xpriv.derive(i.path).privateKey;
|
||||
}
|
||||
privs.push(derived[i.path]);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
var url = '/v1/txproposals/' + txp.id + '/signatures/';
|
||||
var args = {
|
||||
signatures: signatures
|
||||
};
|
||||
|
||||
self._doPostRequest(url, args, data, cb);
|
||||
_.each(txp.inputs, function(i) {
|
||||
if (!derived[i.path]) {
|
||||
derived[i.path] = xpriv.derive(i.path).privateKey;
|
||||
}
|
||||
privs.push(derived[i.path]);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
var url = '/v1/txproposals/' + txp.id + '/signatures/';
|
||||
var args = {
|
||||
signatures: signatures
|
||||
};
|
||||
|
||||
self._doPostRequest(url, args, data, cb);
|
||||
});
|
||||
};
|
||||
|
||||
API.prototype.rejectTxProposal = function(txp, reason, cb) {
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
var _ = require('lodash');
|
||||
var HDPath = require('./hdpath');
|
||||
|
||||
function Addressable (opts) {
|
||||
this.receiveAddressIndex = 0;
|
||||
this.changeAddressIndex = 0;
|
||||
this.copayerIndex = ( opts && _.isNumber(opts.copayerIndex)) ? opts.copayerIndex : HDPath.SHARED_INDEX;
|
||||
};
|
||||
|
||||
|
||||
Addressable.prototype.fromObj = function (obj) {
|
||||
this.receiveAddressIndex = obj.receiveAddressIndex;
|
||||
this.changeAddressIndex = obj.changeAddressIndex;
|
||||
this.copayerIndex = obj.copayerIndex;
|
||||
};
|
||||
|
||||
Addressable.prototype.addAddress = function (isChange) {
|
||||
if (isChange) {
|
||||
this.changeAddressIndex++;
|
||||
} else {
|
||||
this.receiveAddressIndex++;
|
||||
}
|
||||
};
|
||||
|
||||
Addressable.prototype.getCurrentAddressPath = function (isChange) {
|
||||
return HDPath.Branch(isChange ? this.changeAddressIndex : this.receiveAddressIndex, isChange, this.copayerIndex);
|
||||
};
|
||||
|
||||
Addressable.prototype.getNewAddressPath = function (isChange) {
|
||||
this.addAddress(isChange);
|
||||
return this.getCurrentAddressPath(isChange);
|
||||
};
|
||||
|
||||
module.exports = Addressable;
|
|
@ -1,5 +1,5 @@
|
|||
var _ = require('lodash');
|
||||
var HDPath = require('./hdpath');
|
||||
var HDPath = require('../hdpath');
|
||||
|
||||
function AddressManager(opts) {
|
||||
this.receiveAddressIndex = 0;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
var $ = require('preconditions').singleton();
|
||||
var _ = require('lodash');
|
||||
var util = require('util');
|
||||
|
||||
|
@ -7,20 +8,22 @@ var Bitcore = require('bitcore');
|
|||
var HDPublicKey = Bitcore.HDPublicKey;
|
||||
var Uuid = require('uuid');
|
||||
var AddressManager = require('./addressmanager');
|
||||
var Utils = require('../walletutils');
|
||||
|
||||
|
||||
var VERSION = '1.0.0';
|
||||
var MESSAGE_SIGNING_PATH = "m/1/0";
|
||||
|
||||
function Copayer(opts) {
|
||||
opts = opts || {};
|
||||
$.checkArgument(opts && opts.xPubKey, 'need to provide an xPubKey');
|
||||
opts.copayerIndex = opts.copayerIndex || 0;
|
||||
|
||||
this.id = Uuid.v4();
|
||||
this.xPubKey = opts.xPubKey;
|
||||
this.id = Utils.xpubToCopayerId(this.xPubKey);
|
||||
|
||||
this.version = VERSION;
|
||||
this.createdOn = Math.floor(Date.now() / 1000);
|
||||
this.name = opts.name;
|
||||
this.xPubKey = opts.xPubKey;
|
||||
this.xPubKeySignature = opts.xPubKeySignature; // So third parties can check independently
|
||||
if (this.xPubKey)
|
||||
this.signingPubKey = this.getSigningPubKey();
|
||||
|
@ -40,14 +43,14 @@ Copayer.prototype.getSigningPubKey = function() {
|
|||
};
|
||||
|
||||
Copayer.fromObj = function(obj) {
|
||||
var x = new Copayer();
|
||||
var x = new Copayer({
|
||||
xPubKey: obj.xPubKey,
|
||||
});
|
||||
|
||||
x.createdOn = obj.createdOn;
|
||||
x.id = obj.id;
|
||||
x.name = obj.name;
|
||||
x.xPubKey = obj.xPubKey;
|
||||
x.xPubKeySignature = obj.xPubKeySignature;
|
||||
x.signingPubKey = obj.signingPubKey;
|
||||
x.addressManager = AddressManager.fromObj(obj.addressManager);
|
||||
|
||||
return x;
|
||||
|
|
|
@ -208,6 +208,7 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
|
|||
if (_.find(wallet.copayers, {
|
||||
xPubKey: opts.xPubKey
|
||||
})) return cb(new ClientError('CINWALLET', 'Copayer already in wallet'));
|
||||
|
||||
if (wallet.copayers.length == wallet.n)
|
||||
return cb(new ClientError('WFULL', 'Wallet full'));
|
||||
|
||||
|
@ -218,18 +219,24 @@ CopayServer.prototype.joinWallet = function(opts, cb) {
|
|||
copayerIndex: wallet.copayers.length,
|
||||
});
|
||||
|
||||
wallet.addCopayer(copayer);
|
||||
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
|
||||
self.storage.fetchCopayerLookup(copayer.id, function(err, res) {
|
||||
if (err) return cb(err);
|
||||
if (res)
|
||||
return cb(new ClientError('CREGISTED', 'Copayer ID already registered on server'));
|
||||
|
||||
self._notify('NewCopayer', {
|
||||
walletId: opts.walletId,
|
||||
copayerId: copayer.id,
|
||||
copayerName: copayer.name,
|
||||
});
|
||||
return cb(null, {
|
||||
copayerId: copayer.id,
|
||||
wallet: wallet
|
||||
wallet.addCopayer(copayer);
|
||||
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
self._notify('NewCopayer', {
|
||||
walletId: opts.walletId,
|
||||
copayerId: copayer.id,
|
||||
copayerName: copayer.name,
|
||||
});
|
||||
return cb(null, {
|
||||
copayerId: copayer.id,
|
||||
wallet: wallet
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,6 +4,7 @@ var Address = Bitcore.Address;
|
|||
var PrivateKey = Bitcore.PrivateKey;
|
||||
var PublicKey = Bitcore.PublicKey;
|
||||
var crypto = Bitcore.crypto;
|
||||
var HDPath = require('./hdpath');
|
||||
|
||||
function WalletUtils() {};
|
||||
|
||||
|
@ -56,4 +57,10 @@ WalletUtils.getProposalHash = function(toAddress, amount, message) {
|
|||
return toAddress + '|' + amount + '|' + (message || '');
|
||||
};
|
||||
|
||||
WalletUtils.xpubToCopayerId = function(xpub) {
|
||||
return (new Bitcore.HDPublicKey(xpub)).derive(HDPath.IdBranch).publicKey.toString();
|
||||
};
|
||||
|
||||
|
||||
|
||||
module.exports = WalletUtils;
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
"npmlog": "^0.1.1",
|
||||
"preconditions": "^1.0.7",
|
||||
"request": "^2.53.0",
|
||||
"qr-image": "*",
|
||||
"uuid": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
'use strict';
|
||||
|
||||
var HDPath = require('../lib/model/hdpath');
|
||||
var HDPath = require('../lib/hdpath');
|
||||
|
||||
describe('HDPath model', function() {
|
||||
it('should have the correct constants', function() {
|
||||
|
|
|
@ -149,6 +149,28 @@ describe('client API ', function() {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#export & #import 2-2 wallet', function() {
|
||||
it('round trip ', function(done) {
|
||||
client.storage.fs.readFile = sinon.stub().yields(null, JSON.stringify(TestData.storage.complete22));
|
||||
client.export(function(err, str) {
|
||||
should.not.exist(err);
|
||||
|
||||
client.storage.fs.readFile = sinon.stub().yields(null);
|
||||
client.import(str, function(err,wallet) {
|
||||
should.not.exist(err);
|
||||
var wallet = JSON.parse(client.storage.fs.writeFile.getCall(0).args[1]);
|
||||
|
||||
TestData.storage.complete22.should.deep.equal(wallet);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
describe('#signTxProposal ', function() {
|
||||
it.skip('should sign tx proposal', function(done) {});
|
||||
|
||||
|
|
|
@ -13,103 +13,116 @@ var storage = {
|
|||
incompleteWallet22: {
|
||||
"m": 2,
|
||||
"n": 2,
|
||||
"walletPrivKey":"L2Fu6TM1AqSNBaQcjgjvYjGf3EzS3MVSTwEeTw3bvy52x7ZkffWj",
|
||||
"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,
|
||||
"publicKeyRing": ["xpub661MyMwAqRbcGzNFbVQLh6CV6ukHuhBn4Bf4CGrQ6pFfNNdJ3pxrEVDtFHGsTzyz6Py23FhP8GWAqew3PsvnstEs2iayH1PK5Mx1bSVSEAG", "xpub661MyMwAqRbcGH2FXudWPDdrRobZ9XWTGaz18AnN1gkG8QW9ZUcn63RcK5qJJ5DXYXeAWBNqprdvvg8VHA5twmBHCUc6gWygXkwmU1Dohwh"],
|
||||
"copayerId": "020b41cfea5fae42050580474a195a8385b093f291af4079759851d8819383a680",
|
||||
"signingPrivKey": "KyhU3befBaePqHuPQNNyY1XFUgnArR3GUKZpZwV5vS7u1pcR3uzB",
|
||||
"network": "livenet"
|
||||
},
|
||||
};
|
||||
|
||||
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',
|
||||
}},
|
||||
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',
|
||||
}},
|
||||
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',
|
||||
}},
|
||||
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',
|
||||
}],
|
||||
}},
|
||||
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',
|
||||
}, ],
|
||||
}},
|
||||
m: 2,
|
||||
n: 2,
|
||||
status: 'complete',
|
||||
publicKeyRing: ['tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E',
|
||||
'tpubD6NzVbkrYhZ4WSuBBLyubi8DHMipbFQcZoLJHjb21gEtznCEJMJhwkvaSshHVLtq8C1uNMKD4GtADVYY6WZt1cyT218JUm3PiNKYVkMATWV'
|
||||
],
|
||||
copayers: [{
|
||||
xPubKey: 'tpubD6NzVbkrYhZ4Y1CGuCZ88eZvhDSTjAqjotZWGXC7e4GEoyXq3SQgZK9iRz4qC2h8MrzqrYBndCMQDiaaLdqpY8ihYmJC9Msvns83jGopb3E',
|
||||
}, ],
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports.serverResponse = serverResponse;
|
||||
|
|
|
@ -39,6 +39,7 @@ helpers.getAuthServer = function(copayerId, cb) {
|
|||
helpers.createAndJoinWallet = function(m, n, cb) {
|
||||
var server = new CopayServer();
|
||||
var copayerIds = [];
|
||||
var offset = helpers.offset || 0;
|
||||
|
||||
var walletOpts = {
|
||||
name: 'a wallet',
|
||||
|
@ -53,11 +54,12 @@ helpers.createAndJoinWallet = function(m, n, cb) {
|
|||
var copayerOpts = {
|
||||
walletId: walletId,
|
||||
name: 'copayer ' + (i + 1),
|
||||
xPubKey: TestData.copayers[i].xPubKey,
|
||||
xPubKeySignature: TestData.copayers[i].xPubKeySignature,
|
||||
xPubKey: TestData.copayers[i + offset].xPubKey,
|
||||
xPubKeySignature: TestData.copayers[i + offset].xPubKeySignature,
|
||||
};
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
copayerIds.push(result.copayerId);
|
||||
return cb(err);
|
||||
});
|
||||
|
@ -198,12 +200,14 @@ describe('Copay server', function() {
|
|||
CopayServer.initialize({
|
||||
storage: storage
|
||||
});
|
||||
helpers.offset = 0;
|
||||
});
|
||||
|
||||
describe('#getInstanceWithAuth', function() {
|
||||
beforeEach(function() {});
|
||||
|
||||
it('should get server instance for existing copayer', function(done) {
|
||||
|
||||
helpers.createAndJoinWallet(1, 2, function(s, wallet) {
|
||||
var xpriv = TestData.copayers[0].xPrivKey;
|
||||
var priv = Bitcore.HDPrivateKey
|
||||
|
@ -544,36 +548,6 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should fail to create address when wallet is not complete', function(done) {
|
||||
var server = new CopayServer();
|
||||
var walletOpts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, walletId) {
|
||||
should.not.exist(err);
|
||||
var copayerOpts = {
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey,
|
||||
xPubKeySignature: TestData.copayers[0].xPubKeySignature,
|
||||
};
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
helpers.getAuthServer(result.copayerId, function(server) {
|
||||
server.createAddress({}, function(err, address) {
|
||||
should.not.exist(address);
|
||||
err.should.exist;
|
||||
err.message.should.contain('not complete');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create many addresses on simultaneous requests', function(done) {
|
||||
var N = 5;
|
||||
async.map(_.range(N), function(i, cb) {
|
||||
|
@ -611,35 +585,31 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#createTx', function() {
|
||||
var server, wallet;
|
||||
beforeEach(function(done) {
|
||||
helpers.createAndJoinWallet(2, 3, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
server.createAddress({}, function(err, address) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a tx', function(done) {
|
||||
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
describe('Wallet not complete tests', function() {
|
||||
it('should fail to create address when wallet is not complete', function(done) {
|
||||
var server = new CopayServer();
|
||||
var walletOpts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, walletId) {
|
||||
should.not.exist(err);
|
||||
var copayerOpts = {
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey,
|
||||
xPubKeySignature: TestData.copayers[0].xPubKeySignature,
|
||||
};
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
tx.should.exist;
|
||||
tx.message.should.equal('some message');
|
||||
tx.isAccepted().should.equal.false;
|
||||
tx.isRejected().should.equal.false;
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
should.not.exist(err);
|
||||
txs.length.should.equal(1);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(300));
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(100));
|
||||
helpers.getAuthServer(result.copayerId, function(server) {
|
||||
server.createAddress({}, function(err, address) {
|
||||
should.not.exist(address);
|
||||
err.should.exist;
|
||||
err.message.should.contain('not complete');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -677,6 +647,45 @@ describe('Copay server', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('#createTx', function() {
|
||||
var server, wallet;
|
||||
beforeEach(function(done) {
|
||||
helpers.createAndJoinWallet(2, 3, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
server.createAddress({}, function(err, address) {
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a tx', function(done) {
|
||||
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
|
||||
helpers.stubBlockExplorer(server, utxos);
|
||||
var txOpts = helpers.createProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 80, 'some message', TestData.copayers[0].privKey);
|
||||
server.createTx(txOpts, function(err, tx) {
|
||||
should.not.exist(err);
|
||||
tx.should.exist;
|
||||
tx.message.should.equal('some message');
|
||||
tx.isAccepted().should.equal.false;
|
||||
tx.isRejected().should.equal.false;
|
||||
server.getPendingTxs({}, function(err, txs) {
|
||||
should.not.exist(err);
|
||||
txs.length.should.equal(1);
|
||||
server.getBalance({}, function(err, balance) {
|
||||
should.not.exist(err);
|
||||
balance.totalAmount.should.equal(helpers.toSatoshi(300));
|
||||
balance.lockedAmount.should.equal(helpers.toSatoshi(100));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should fail to create tx with invalid proposal signature', function(done) {
|
||||
helpers.createUtxos(server, wallet, [100, 200], function(utxos) {
|
||||
|
@ -1637,6 +1646,7 @@ describe('Copay server', function() {
|
|||
var before = _.clone(db);
|
||||
db.length.should.above(1);
|
||||
|
||||
helpers.offset = 1;
|
||||
helpers.createAndJoinWallet(2, 3, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
|
|
Loading…
Reference in New Issue