id param to wallet create
This commit is contained in:
parent
d7964a8239
commit
039396dfaa
|
@ -18,8 +18,8 @@ function Copayer() {
|
|||
|
||||
Copayer.create = function(opts) {
|
||||
opts = opts || {};
|
||||
$.checkArgument(opts.xPubKey, 'Missing extended public key');
|
||||
$.checkArgument(opts.requestPubKey, 'Missing request public key');
|
||||
$.checkArgument(opts.xPubKey, 'Missing copayer extended public key');
|
||||
$.checkArgument(opts.requestPubKey, 'Missing copayer request public key');
|
||||
|
||||
opts.copayerIndex = opts.copayerIndex || 0;
|
||||
|
||||
|
@ -35,6 +35,7 @@ Copayer.create = function(opts) {
|
|||
x.addressManager = AddressManager.create({
|
||||
copayerIndex: opts.copayerIndex
|
||||
});
|
||||
x.isTemporaryRequestKey = opts.isTemporaryRequestKey || false;
|
||||
|
||||
return x;
|
||||
};
|
||||
|
@ -48,6 +49,8 @@ Copayer.fromObj = function(obj) {
|
|||
x.xPubKey = obj.xPubKey;
|
||||
x.requestPubKey = obj.requestPubKey;
|
||||
x.signature = obj.signature;
|
||||
x.isTemporaryRequestKey = obj.isTemporaryRequestKey || false;
|
||||
|
||||
x.addressManager = AddressManager.fromObj(obj.addressManager);
|
||||
|
||||
return x;
|
||||
|
|
|
@ -20,7 +20,7 @@ Wallet.create = function(opts) {
|
|||
var x = new Wallet();
|
||||
|
||||
x.createdOn = Math.floor(Date.now() / 1000);
|
||||
x.id = Uuid.v4();
|
||||
x.id = opts.id || Uuid.v4();
|
||||
x.name = opts.name;
|
||||
x.m = opts.m;
|
||||
x.n = opts.n;
|
||||
|
@ -100,6 +100,23 @@ Wallet.prototype.addCopayer = function(copayer) {
|
|||
});
|
||||
};
|
||||
|
||||
Wallet.prototype.updateCopayerRequestKey = function(copayerId, requestPubKey) {
|
||||
$.checkState(this.copayers.length == this.n);
|
||||
|
||||
var c = _.find(this.copayers, {
|
||||
id: copayerId,
|
||||
});
|
||||
|
||||
$.checkState(c)
|
||||
.checkState(c.isTemporaryRequestKey);
|
||||
|
||||
c.requestPubKey = requestPubKey;
|
||||
c.isTemporaryRequestKey = false;
|
||||
this.publicKeyRing = _.map(this.copayers, function(copayer) {
|
||||
return _.pick(copayer, ['xPubKey', 'requestPubKey']);
|
||||
});
|
||||
};
|
||||
|
||||
Wallet.prototype.getCopayer = function(copayerId) {
|
||||
return _.find(this.copayers, {
|
||||
id: copayerId
|
||||
|
|
|
@ -121,17 +121,35 @@ WalletService.prototype.createWallet = function(opts, cb) {
|
|||
return cb(new ClientError('Invalid public key'));
|
||||
};
|
||||
|
||||
var wallet = Wallet.create({
|
||||
name: opts.name,
|
||||
m: opts.m,
|
||||
n: opts.n,
|
||||
network: network,
|
||||
pubKey: pubKey.toString(),
|
||||
});
|
||||
var newWallet;
|
||||
async.series([
|
||||
|
||||
self.storage.storeWallet(wallet, function(err) {
|
||||
log.debug('Wallet created', wallet.id, network);
|
||||
return cb(err, wallet.id);
|
||||
function(acb) {
|
||||
if (!opts.id)
|
||||
return acb();
|
||||
|
||||
self.storage.fetchWallet(opts.id, function(err, wallet) {
|
||||
if (wallet) return acb(new ClientError('Wallet already exists'));
|
||||
return acb(err);
|
||||
});
|
||||
},
|
||||
function(acb) {
|
||||
var wallet = Wallet.create({
|
||||
name: opts.name,
|
||||
m: opts.m,
|
||||
n: opts.n,
|
||||
network: network,
|
||||
pubKey: pubKey.toString(),
|
||||
id: opts.id,
|
||||
});
|
||||
self.storage.storeWallet(wallet, function(err) {
|
||||
log.debug('Wallet created', wallet.id, network);
|
||||
newWallet = wallet;
|
||||
return acb(err);
|
||||
});
|
||||
}
|
||||
], function(err) {
|
||||
return cb(err, newWallet ? newWallet.id : null);
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -151,6 +169,63 @@ WalletService.prototype.getWallet = function(opts, cb) {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Replace temporary request key
|
||||
* @param {Object} opts
|
||||
* @param {string} opts.name - The copayer name.
|
||||
* @param {string} opts.xPubKey - Extended Public Key for this copayer.
|
||||
* @param {string} opts.requestPubKey - Public Key used to check requests from this copayer.
|
||||
* @param {string} opts.copayerSignature - S(name|xPubKey|requestPubKey). Used by other copayers to verify the that the copayer joining knows the wallet secret.
|
||||
*/
|
||||
WalletService.prototype.replaceTemporaryRequestKey = function(opts, cb) {
|
||||
var self = this;
|
||||
|
||||
if (!Utils.checkRequired(opts, ['name', 'xPubKey', 'requestPubKey', 'copayerSignature']))
|
||||
return cb(new ClientError('Required argument missing'));
|
||||
|
||||
|
||||
if (_.isEmpty(opts.name))
|
||||
return cb(new ClientError('Invalid copayer name'));
|
||||
|
||||
|
||||
if (opts.isTemporaryRequestKey)
|
||||
return cb(new ClientError('Bad arguments'));
|
||||
|
||||
Utils.runLocked(self.walletId, cb, function(cb) {
|
||||
self.storage.fetchWallet(self.walletId, function(err, wallet) {
|
||||
if (err) return cb(err);
|
||||
|
||||
if (!wallet) return cb(new ClientError('Wallet not found'));
|
||||
var hash = WalletUtils.getCopayerHash(opts.name, opts.xPubKey, opts.requestPubKey);
|
||||
if (!self._verifySignature(hash, opts.copayerSignature, wallet.pubKey)) {
|
||||
return cb(new ClientError());
|
||||
}
|
||||
|
||||
var oldCopayerData = _.find(wallet.copayers, {
|
||||
id: self.copayerId
|
||||
});
|
||||
$.checkState(oldCopayerData);
|
||||
|
||||
if (oldCopayerData.xPubKey !== opts.xPubKey || oldCopayerData.name !== opts.name || !oldCopayerData.isTemporaryRequestKey)
|
||||
return cb(new ClientError('CDATAMISMATCH', 'Copayer data mismatch'));
|
||||
|
||||
if (wallet.copayers.length != wallet.n)
|
||||
return cb(new ClientError('WNOTFULL', 'Replace only works on full wallets'));
|
||||
|
||||
wallet.updateCopayerRequestKey(self.copayerId, opts.requestPubKey);
|
||||
|
||||
self.storage.storeWalletAndUpdateCopayersLookup(wallet, function(err) {
|
||||
if (err) return cb(err);
|
||||
|
||||
return cb(null, {
|
||||
copayerId: self.copayerId,
|
||||
wallet: wallet
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Verifies a signature
|
||||
* @param text
|
||||
|
@ -206,6 +281,7 @@ WalletService.prototype._notify = function(type, data) {
|
|||
* @param {string} opts.xPubKey - Extended Public Key for this copayer.
|
||||
* @param {string} opts.requestPubKey - Public Key used to check requests from this copayer.
|
||||
* @param {string} opts.copayerSignature - S(name|xPubKey|requestPubKey). Used by other copayers to verify the that the copayer joining knows the wallet secret.
|
||||
* @param {string} opts.isTemporaryRequestKey - requestPubKey will be marked as 'temporary' (only used for Copay migration)
|
||||
*/
|
||||
WalletService.prototype.joinWallet = function(opts, cb) {
|
||||
var self = this;
|
||||
|
@ -218,6 +294,7 @@ WalletService.prototype.joinWallet = function(opts, cb) {
|
|||
|
||||
Utils.runLocked(opts.walletId, cb, function(cb) {
|
||||
self.storage.fetchWallet(opts.walletId, function(err, wallet) {
|
||||
|
||||
if (err) return cb(err);
|
||||
if (!wallet) return cb(new ClientError('Wallet not found'));
|
||||
|
||||
|
@ -239,6 +316,7 @@ WalletService.prototype.joinWallet = function(opts, cb) {
|
|||
xPubKey: opts.xPubKey,
|
||||
requestPubKey: opts.requestPubKey,
|
||||
signature: opts.copayerSignature,
|
||||
isTemporaryRequestKey: !!opts.isTemporaryRequestKey,
|
||||
});
|
||||
|
||||
self.storage.fetchCopayerLookup(copayer.id, function(err, res) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "bitcore-wallet-service",
|
||||
"description": "A service for Mutisig HD Bitcoin Wallets",
|
||||
"author": "BitPay Inc",
|
||||
"version": "0.0.17",
|
||||
"version": "0.0.18",
|
||||
"keywords": [
|
||||
"bitcoin",
|
||||
"copay",
|
||||
|
|
|
@ -299,6 +299,43 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
|
||||
|
||||
it('should create wallet with given id', function(done) {
|
||||
var opts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
id: '1234',
|
||||
};
|
||||
server.createWallet(opts, function(err, walletId) {
|
||||
should.not.exist(err);
|
||||
server.storage.fetchWallet('1234', function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
wallet.id.should.equal(walletId);
|
||||
wallet.name.should.equal('my wallet');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to create wallets with same id', function(done) {
|
||||
var opts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
id: '1234',
|
||||
};
|
||||
server.createWallet(opts, function(err, walletId) {
|
||||
server.createWallet(opts, function(err, walletId) {
|
||||
err.message.should.contain('Wallet already exists');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should fail to create wallet with no name', function(done) {
|
||||
var opts = {
|
||||
name: '',
|
||||
|
@ -2595,6 +2632,207 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
it.skip('should abort scan if there is an error checking address activity', function(done) {});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('#replaceTemporaryRequestKey', function() {
|
||||
var server, walletId;
|
||||
beforeEach(function(done) {
|
||||
server = new WalletService();
|
||||
var walletOpts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 2,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, wId) {
|
||||
should.not.exist(err);
|
||||
should.exist.walletId;
|
||||
walletId = wId;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should join existing wallet with temporaryRequestKey', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = true;
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
var copayerId = result.copayerId;
|
||||
helpers.getAuthServer(copayerId, function(server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
wallet.id.should.equal(walletId);
|
||||
var copayer = wallet.copayers[0];
|
||||
copayer.isTemporaryRequestKey.should.equal(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail to replace a temporaryRequestKey on a not-complete wallet', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = true;
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
var copayerId = result.copayerId;
|
||||
helpers.getAuthServer(copayerId, function(server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = false;
|
||||
server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
|
||||
err.code.should.equal('WNOTFULL');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should fail to replace a temporaryRequestKey is Copayer is not in wallet', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = true;
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
var copayerId = result.copayerId;
|
||||
helpers.getAuthServer(copayerId, function(server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[1].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[1].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = false;
|
||||
server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
|
||||
err.code.should.equal('CDATAMISMATCH');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail replace a temporaryRequestKey with invalid copayer', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = true;
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
|
||||
var copayerOpts2 = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[1].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[1].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts2.isTemporaryRequestKey = false;
|
||||
|
||||
server.joinWallet(copayerOpts2, function(err, result) {
|
||||
should.not.exist(err);
|
||||
|
||||
var copayerId = result.copayerId;
|
||||
helpers.getAuthServer(copayerId, function(server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[1].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[1].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = false;
|
||||
server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
|
||||
err.code.should.equal('CDATAMISMATCH');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should replace a temporaryRequestKey', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = true;
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.not.exist(err);
|
||||
var copayerId = result.copayerId;
|
||||
|
||||
var copayerOpts2 = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[1].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[1].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts2.isTemporaryRequestKey = false;
|
||||
|
||||
server.joinWallet(copayerOpts2, function(err, result) {
|
||||
should.not.exist(err);
|
||||
var copayerId2 = result.copayerId;
|
||||
|
||||
|
||||
helpers.getAuthServer(copayerId, function(server) {
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1H_0,
|
||||
});
|
||||
copayerOpts.isTemporaryRequestKey = false;
|
||||
server.replaceTemporaryRequestKey(copayerOpts, function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
server.getWallet({}, function(err, wallet) {
|
||||
wallet.copayers[0].isTemporaryRequestKey.should.equal(false);
|
||||
wallet.copayers[1].isTemporaryRequestKey.should.equal(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -17,7 +17,9 @@ var copayers = [{
|
|||
xPrivKey_1H: 'xprv9uZiJ2ZYFNQ1WeKrKfTxrbUnXfNnCtpbdHVuUMSVGB57nhbmjqWbJBQbgGNa2jSbrgQX9NFHXtAoQ3y1hsEgwjKHxUU52ZYTC6m5kjXoNpJ',
|
||||
xPubKey_1H: 'xpub68Z4hY6S5jxJj8QKRgzyDjRX5hDGcMYSzWRWGjr6pWc6fVvvHNpqqyj5XXbMcfW737beYZcd7EQau5HS74Ws6Ctx9XwFn2wHQjSUKLbfdFk',
|
||||
privKey_1H_0: 'e334a0ce3d573bd99fc4cd7e2065e39dc7851cb61da7f381431c253c3e230828',
|
||||
pubKey_1H_0: '02db35c363e2c904bba3cd0eadb6b8d68fc1d8e6160d632b8fb91a471b80e40c86'
|
||||
pubKey_1H_0: '02db35c363e2c904bba3cd0eadb6b8d68fc1d8e6160d632b8fb91a471b80e40c86',
|
||||
privKey_1_0: '89833f39998ff14f2cbb461365ccba30583abfd2a7845e0bb61849275802005d',
|
||||
pubKey_1_0: '022ef899f914c9b90a5544923a3aa6de5ddcd6c9d0b603b63ab9fd0c2cdc5d3772',
|
||||
}, {
|
||||
id: 'cf652642ebf997460d642231f9929c39257bcd4c42e9ecb312c226961e21c882',
|
||||
xPrivKey: 'xprv9s21ZrQH143K2XtR13LpXVAPaGTFsPY5dJfNvNTShfhdVW16vdDaYGjLwHmyKLCtv3F6h9NpKsAGusmKCZNQvKcYqYf9MjCLwJwsoEyAuge',
|
||||
|
|
Loading…
Reference in New Issue