enable both bip44/45 & p2sh/pkh
This commit is contained in:
parent
8d026b85a5
commit
97e63f9c6f
|
@ -130,10 +130,10 @@ ExpressApp.prototype.start = function(opts, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
// DEPRECATED
|
||||
router.post('/v1/wallets/', function(req, res) {
|
||||
var server = getServer(req, res);
|
||||
req.body.supportBIP44 = false;
|
||||
req.body.supportP2PKH = false;
|
||||
req.body.supportBIP44AndP2PKH = false;
|
||||
server.createWallet(req.body, function(err, walletId) {
|
||||
if (err) return returnError(err, res, req);
|
||||
res.json({
|
||||
|
@ -144,8 +144,7 @@ ExpressApp.prototype.start = function(opts, cb) {
|
|||
|
||||
router.post('/v2/wallets/', function(req, res) {
|
||||
var server = getServer(req, res);
|
||||
req.body.supportBIP44 = true;
|
||||
req.body.supportP2PKH = true;
|
||||
req.body.supportBIP44AndP2PKH = true;
|
||||
server.createWallet(req.body, function(err, walletId) {
|
||||
if (err) return returnError(err, res, req);
|
||||
res.json({
|
||||
|
@ -163,6 +162,7 @@ ExpressApp.prototype.start = function(opts, cb) {
|
|||
});
|
||||
});
|
||||
|
||||
// DEPRECATED
|
||||
router.post('/v1/wallets/:id/copayers/', function(req, res) {
|
||||
req.body.walletId = req.params['id'];
|
||||
var server = getServer(req, res);
|
||||
|
@ -173,6 +173,17 @@ ExpressApp.prototype.start = function(opts, cb) {
|
|||
});
|
||||
});
|
||||
|
||||
router.post('/v2/wallets/:id/copayers/', function(req, res) {
|
||||
req.body.walletId = req.params['id'];
|
||||
req.body.supportBIP44AndP2PKH = true;
|
||||
var server = getServer(req, res);
|
||||
server.joinWallet(req.body, function(err, result) {
|
||||
if (err) return returnError(err, res, req);
|
||||
|
||||
res.json(result);
|
||||
});
|
||||
});
|
||||
|
||||
// DEPRECATED
|
||||
router.get('/v1/wallets/', function(req, res) {
|
||||
getServerWithAuth(req, res, function(server) {
|
||||
|
|
|
@ -203,8 +203,7 @@ WalletService.prototype._runLocked = function(cb, task) {
|
|||
* @param {number} opts.n - Total copayers.
|
||||
* @param {string} opts.pubKey - Public key to verify copayers joining have access to the wallet secret.
|
||||
* @param {string} [opts.network = 'livenet'] - The Bitcoin network for this wallet.
|
||||
* @param {string} [opts.supportBIP44 = false] - Client supports BIP44 paths for 1-of-1 wallets.
|
||||
* @param {string} [opts.supportP2PKH = false] - Client supports P2PKH address type for 1-of-1 wallets.
|
||||
* @param {string} [opts.supportBIP44AndP2PKH = false] - Client supports BIP44 & P2PKH for new wallets.
|
||||
*/
|
||||
WalletService.prototype.createWallet = function(opts, cb) {
|
||||
var self = this,
|
||||
|
@ -221,11 +220,8 @@ WalletService.prototype.createWallet = function(opts, cb) {
|
|||
if (!_.contains(['livenet', 'testnet'], opts.network))
|
||||
return cb(new ClientError('Invalid network'));
|
||||
|
||||
var derivationStrategy = (opts.n == 1 && opts.supportBIP44) ?
|
||||
WalletUtils.DERIVATION_STRATEGIES.BIP44 : WalletUtils.DERIVATION_STRATEGIES.BIP45;
|
||||
|
||||
var addressType = (opts.n == 1 && opts.supportP2PKH) ?
|
||||
WalletUtils.SCRIPT_TYPES.P2PKH : WalletUtils.SCRIPT_TYPES.P2SH;
|
||||
var derivationStrategy = opts.supportBIP44AndP2PKH ? WalletUtils.DERIVATION_STRATEGIES.BIP44 : WalletUtils.DERIVATION_STRATEGIES.BIP45;
|
||||
var addressType = (opts.n == 1 && opts.supportBIP44AndP2PKH) ? WalletUtils.SCRIPT_TYPES.P2PKH : WalletUtils.SCRIPT_TYPES.P2SH;
|
||||
|
||||
try {
|
||||
pubKey = new PublicKey.fromString(opts.pubKey);
|
||||
|
@ -412,8 +408,6 @@ WalletService.prototype._notify = function(type, data, opts, cb) {
|
|||
WalletService.prototype._addCopayerToWallet = function(wallet, opts, cb) {
|
||||
var self = this;
|
||||
|
||||
if (wallet.copayers.length == wallet.n) return cb(Errors.WALLET_FULL);
|
||||
|
||||
var copayer = Model.Copayer.create({
|
||||
name: opts.name,
|
||||
copayerIndex: wallet.copayers.length,
|
||||
|
@ -559,6 +553,7 @@ WalletService.prototype._clientSupportsTXPv2 = function() {
|
|||
* @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 that the copayer joining knows the wallet secret.
|
||||
* @param {string} opts.customData - (optional) Custom data for this copayer.
|
||||
* @param {string} [opts.supportBIP44AndP2PKH = false] - Client supports BIP44 & P2PKH for joining wallets.
|
||||
*/
|
||||
WalletService.prototype.joinWallet = function(opts, cb) {
|
||||
var self = this;
|
||||
|
@ -575,11 +570,25 @@ WalletService.prototype.joinWallet = function(opts, cb) {
|
|||
if (err) return cb(err);
|
||||
if (!wallet) return cb(Errors.WALLET_NOT_FOUND);
|
||||
|
||||
if (opts.supportBIP44AndP2PKH) {
|
||||
// New client trying to join legacy wallet
|
||||
if (wallet.derivationStrategy == WalletUtils.DERIVATION_STRATEGIES.BIP45) {
|
||||
return cb(new ClientError('The wallet you are trying to join was created with an older version of the client app.'));
|
||||
}
|
||||
} else {
|
||||
// Legacy client trying to join new wallet
|
||||
if (wallet.derivationStrategy == WalletUtils.DERIVATION_STRATEGIES.BIP44) {
|
||||
return cb(new ClientError(Errors.codes.UPGRADE_NEEDED, 'To join this wallet you need to upgrade your client app.'));
|
||||
}
|
||||
}
|
||||
|
||||
var hash = WalletUtils.getCopayerHash(opts.name, opts.xPubKey, opts.requestPubKey);
|
||||
if (!self._verifySignature(hash, opts.copayerSignature, wallet.pubKey)) {
|
||||
return cb(new ClientError());
|
||||
}
|
||||
|
||||
if (wallet.copayers.length == wallet.n) return cb(Errors.WALLET_FULL);
|
||||
|
||||
if (_.find(wallet.copayers, {
|
||||
xPubKey: opts.xPubKey
|
||||
})) return cb(Errors.COPAYER_IN_WALLET);
|
||||
|
@ -1209,7 +1218,7 @@ WalletService.prototype.createTx = function(opts, cb) {
|
|||
excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos,
|
||||
derivationStrategy: wallet.derivationStrategy,
|
||||
addressType: wallet.addressType,
|
||||
customData: opts.customData
|
||||
customData: opts.customData,
|
||||
};
|
||||
|
||||
if (signingKey.selfSigned) {
|
||||
|
|
|
@ -96,15 +96,13 @@ helpers.createAndJoinWallet = function(m, n, opts, cb) {
|
|||
var copayerIds = [];
|
||||
var offset = opts.offset || 0;
|
||||
|
||||
var supportBIP44 = _.isBoolean(opts.supportBIP44) ? opts.supportBIP44 : true
|
||||
var supportP2PKH = _.isBoolean(opts.supportP2PKH) ? opts.supportP2PKH : true
|
||||
var supportBIP44AndP2PKH = _.isBoolean(opts.supportBIP44AndP2PKH) ? opts.supportBIP44AndP2PKH : true;
|
||||
var walletOpts = {
|
||||
name: 'a wallet',
|
||||
m: m,
|
||||
n: n,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
supportBIP44: supportBIP44,
|
||||
supportP2PKH: supportP2PKH,
|
||||
supportBIP44AndP2PKH: supportBIP44AndP2PKH,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, walletId) {
|
||||
if (err) return cb(err);
|
||||
|
@ -114,9 +112,10 @@ helpers.createAndJoinWallet = function(m, n, opts, cb) {
|
|||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'copayer ' + (i + 1),
|
||||
xPubKey: (n == 1 && supportBIP44) ? copayerData.xPubKey_44H_0H_0H : copayerData.xPubKey_45H,
|
||||
xPubKey: supportBIP44AndP2PKH ? copayerData.xPubKey_44H_0H_0H : copayerData.xPubKey_45H,
|
||||
requestPubKey: copayerData.pubKey_1H_0,
|
||||
customData: 'custom data ' + (i + 1),
|
||||
supportBIP44AndP2PKH: supportBIP44AndP2PKH,
|
||||
});
|
||||
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
|
@ -135,6 +134,7 @@ helpers.createAndJoinWallet = function(m, n, opts, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
helpers.randomTXID = function() {
|
||||
return Bitcore.crypto.Hash.sha256(new Buffer(Math.random() * 100000)).toString('hex');;
|
||||
};
|
||||
|
@ -509,7 +509,7 @@ describe('Wallet service', function() {
|
|||
txpId = txp.id;
|
||||
async.eachSeries(_.range(2), function(i, next) {
|
||||
var copayer = TestData.copayers[i];
|
||||
helpers.getAuthServer(copayer.id45, function(server) {
|
||||
helpers.getAuthServer(copayer.id44, function(server) {
|
||||
var signatures = helpers.clientSign(txp, copayer.xPrivKey);
|
||||
server.signTx({
|
||||
txProposalId: txp.id,
|
||||
|
@ -567,7 +567,7 @@ describe('Wallet service', function() {
|
|||
txpId = txp.id;
|
||||
async.eachSeries(_.range(1, 3), function(i, next) {
|
||||
var copayer = TestData.copayers[i];
|
||||
helpers.getAuthServer(copayer.id45, function(server) {
|
||||
helpers.getAuthServer(copayer.id44, function(server) {
|
||||
server.rejectTx({
|
||||
txProposalId: txp.id,
|
||||
}, next);
|
||||
|
@ -952,6 +952,21 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
|
||||
it('should fail to join with mismatching address derivation strategy', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
name: 'me',
|
||||
xPubKey: TestData.copayers[0].xPubKey_45H,
|
||||
requestPubKey: TestData.copayers[0].pubKey_1H_0,
|
||||
supportBIP44AndP2PKH: true,
|
||||
});
|
||||
server.joinWallet(copayerOpts, function(err, result) {
|
||||
should.exist(err);
|
||||
err.message.should.contain('The wallet you are trying to join was created with an older version of the client app');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should join existing wallet', function(done) {
|
||||
var copayerOpts = helpers.getSignedCopayerOpts({
|
||||
walletId: walletId,
|
||||
|
@ -1028,6 +1043,7 @@ describe('Wallet service', function() {
|
|||
name: 'me',
|
||||
xPubKey: TestData.copayers[1].xPubKey_44H_0H_0H,
|
||||
requestPubKey: TestData.copayers[1].pubKey_1H_0,
|
||||
supportBIP44AndP2PKH: true,
|
||||
});
|
||||
server.joinWallet(copayerOpts, function(err) {
|
||||
should.exist(err);
|
||||
|
@ -1176,8 +1192,7 @@ describe('Wallet service', function() {
|
|||
m: 1,
|
||||
n: 1,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
supportBIP44: true,
|
||||
supportP2PKH: true,
|
||||
supportBIP44AndP2PKH: true,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, wid) {
|
||||
should.not.exist(err);
|
||||
|
@ -1195,6 +1210,7 @@ describe('Wallet service', function() {
|
|||
m: 1,
|
||||
n: 1,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
supportBIP44AndP2PKH: false,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, wid) {
|
||||
should.not.exist(err);
|
||||
|
@ -1206,14 +1222,31 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
it('should always use BIP45 & P2SH for shared wallets', function(done) {
|
||||
it('should use BIP44 & P2SH for shared wallet if supported', function(done) {
|
||||
var walletOpts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
supportBIP44: true,
|
||||
supportP2PKH: true,
|
||||
supportBIP44AndP2PKH: true,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, wid) {
|
||||
should.not.exist(err);
|
||||
server.storage.fetchWallet(wid, function(err, wallet) {
|
||||
should.not.exist(err);
|
||||
wallet.derivationStrategy.should.equal('BIP44');
|
||||
wallet.addressType.should.equal('P2SH');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
it('should use BIP45 & P2SH for shared wallet if supported', function(done) {
|
||||
var walletOpts = {
|
||||
name: 'my wallet',
|
||||
m: 2,
|
||||
n: 3,
|
||||
pubKey: TestData.keyPair.pub,
|
||||
supportBIP44AndP2PKH: false,
|
||||
};
|
||||
server.createWallet(walletOpts, function(err, wid) {
|
||||
should.not.exist(err);
|
||||
|
@ -1278,7 +1311,6 @@ describe('Wallet service', function() {
|
|||
should.exist(status.wallet.copayers[0].requestPubKey);
|
||||
should.exist(status.wallet.copayers[0].signature);
|
||||
should.exist(status.wallet.copayers[0].requestPubKey);
|
||||
should.exist(status.wallet.copayers[0].addressManager);
|
||||
should.exist(status.wallet.copayers[0].customData);
|
||||
// Do not return other copayer's custom data
|
||||
_.each(_.rest(status.wallet.copayers), function(copayer) {
|
||||
|
@ -1353,7 +1385,9 @@ describe('Wallet service', function() {
|
|||
|
||||
describe('shared wallets (BIP45)', function() {
|
||||
beforeEach(function(done) {
|
||||
helpers.createAndJoinWallet(2, 2, function(s, w) {
|
||||
helpers.createAndJoinWallet(2, 2, {
|
||||
supportBIP44AndP2PKH: false
|
||||
}, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
done();
|
||||
|
@ -1395,6 +1429,52 @@ describe('Wallet service', function() {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('shared wallets (BIP44)', function() {
|
||||
beforeEach(function(done) {
|
||||
helpers.createAndJoinWallet(2, 2, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create address', function(done) {
|
||||
server.createAddress({}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
should.exist(address);
|
||||
address.walletId.should.equal(wallet.id);
|
||||
address.network.should.equal('livenet');
|
||||
address.address.should.equal('36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r');
|
||||
address.isChange.should.be.false;
|
||||
address.path.should.equal('m/0/0');
|
||||
server.getNotifications({}, function(err, notifications) {
|
||||
should.not.exist(err);
|
||||
var notif = _.find(notifications, {
|
||||
type: 'NewAddress'
|
||||
});
|
||||
should.exist(notif);
|
||||
notif.data.address.should.equal(address.address);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should create many addresses on simultaneous requests', function(done) {
|
||||
var N = 5;
|
||||
async.map(_.range(N), function(i, cb) {
|
||||
server.createAddress({}, cb);
|
||||
}, function(err, addresses) {
|
||||
addresses.length.should.equal(N);
|
||||
_.each(_.range(N), function(i) {
|
||||
addresses[i].path.should.equal('m/0/' + i);
|
||||
});
|
||||
// No two identical addresses
|
||||
_.uniq(_.pluck(addresses, 'address')).length.should.equal(N);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not create address if unable to store it', function(done) {
|
||||
sinon.stub(server.storage, 'storeAddressAndWallet').yields('dummy error');
|
||||
|
@ -1667,10 +1747,10 @@ describe('Wallet service', function() {
|
|||
reqPrivKey = new Bitcore.PrivateKey();
|
||||
var requestPubKey = reqPrivKey.toPublicKey();
|
||||
|
||||
var xPrivKey = TestData.copayers[0].xPrivKey_45H;
|
||||
var xPrivKey = TestData.copayers[0].xPrivKey_44H_0H_0H;
|
||||
var sig = WalletUtils.signRequestPubKey(requestPubKey, xPrivKey);
|
||||
|
||||
var copayerId = WalletUtils.xPubToCopayerId(TestData.copayers[0].xPubKey_45H);
|
||||
var copayerId = WalletUtils.xPubToCopayerId(TestData.copayers[0].xPubKey_44H_0H_0H);
|
||||
opts = {
|
||||
copayerId: copayerId,
|
||||
requestPubKey: requestPubKey,
|
||||
|
@ -1681,9 +1761,7 @@ describe('Wallet service', function() {
|
|||
|
||||
describe('#addAccess 1-1', function() {
|
||||
beforeEach(function(done) {
|
||||
helpers.createAndJoinWallet(1, 1, {
|
||||
supportBIP44: false
|
||||
}, function(s, w) {
|
||||
helpers.createAndJoinWallet(1, 1, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
|
||||
|
@ -4496,7 +4574,9 @@ describe('Wallet service', function() {
|
|||
WalletService.SCAN_CONFIG.scanWindow = 2;
|
||||
WalletService.SCAN_CONFIG.derivationDelay = 0;
|
||||
|
||||
helpers.createAndJoinWallet(1, 2, function(s, w) {
|
||||
helpers.createAndJoinWallet(1, 2, {
|
||||
supportBIP44AndP2PKH: false
|
||||
}, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
done();
|
||||
|
@ -4584,7 +4664,7 @@ describe('Wallet service', function() {
|
|||
WalletService.SCAN_CONFIG.derivationDelay = 0;
|
||||
|
||||
helpers.createAndJoinWallet(1, 1, {
|
||||
supportP2PKH: false
|
||||
supportBIP44AndP2PKH: false
|
||||
}, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
|
@ -4598,17 +4678,17 @@ describe('Wallet service', function() {
|
|||
|
||||
it('should start an asynchronous scan', function(done) {
|
||||
helpers.stubAddressActivity(
|
||||
['3J4J9nkFpzQjUGDh5hLKMKztFSPWMKejKE', // m/0/0
|
||||
'384JHSf9kVBs3yXsPwCzEScRs395u8hwxj', // m/0/2
|
||||
'3NgXBiMQvwcRU8khVoPFJ6gsbGg9ZYrRzH', // m/1/0
|
||||
['3GvvHimEMk2GBZnPxTF89GHZL6QhZjUZVs', // m/2147483647/0/0
|
||||
'37pd1jjTUiGBh8JL2hKLDgsyrhBoiz5vsi', // m/2147483647/0/2
|
||||
'3C3tBn8Sr1wHTp2brMgYsj9ncB7R7paYuB', // m/2147483647/1/0
|
||||
]);
|
||||
var expectedPaths = [
|
||||
'm/0/0',
|
||||
'm/0/1',
|
||||
'm/0/2',
|
||||
'm/0/3',
|
||||
'm/1/0',
|
||||
'm/1/1',
|
||||
'm/2147483647/0/0',
|
||||
'm/2147483647/0/1',
|
||||
'm/2147483647/0/2',
|
||||
'm/2147483647/0/3',
|
||||
'm/2147483647/1/0',
|
||||
'm/2147483647/1/1',
|
||||
];
|
||||
server.messageBroker.onMessage(function(n) {
|
||||
if (n.type == 'ScanFinished') {
|
||||
|
@ -4623,7 +4703,7 @@ describe('Wallet service', function() {
|
|||
_.difference(paths, expectedPaths).length.should.equal(0);
|
||||
server.createAddress({}, function(err, address) {
|
||||
should.not.exist(err);
|
||||
address.path.should.equal('m/0/4');
|
||||
address.path.should.equal('m/2147483647/0/4');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue