Merge pull request #339 from isocolsky/bip44

Bip44
This commit is contained in:
Matias Alejo Garcia 2015-09-01 13:11:12 -03:00
commit 806c20069a
9 changed files with 640 additions and 242 deletions

View File

@ -1,6 +1,9 @@
var _ = require('lodash');
var $ = require('preconditions').singleton();
var SHARED_INDEX = 0x80000000 - 1;
var WalletUtils = require('bitcore-wallet-utils');
var BIP45_SHARED_INDEX = 0x80000000 - 1;
function AddressManager() {};
@ -9,19 +12,22 @@ AddressManager.create = function(opts) {
var x = new AddressManager();
x.version = '1.0.0';
x.version = 2;
x.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
$.checkState(_.contains(_.values(WalletUtils.DERIVATION_STRATEGIES), x.derivationStrategy));
x.receiveAddressIndex = 0;
x.changeAddressIndex = 0;
x.copayerIndex = (opts && _.isNumber(opts.copayerIndex)) ? opts.copayerIndex : SHARED_INDEX;
x.copayerIndex = _.isNumber(opts.copayerIndex) ? opts.copayerIndex : BIP45_SHARED_INDEX;
return x;
};
AddressManager.fromObj = function(obj) {
var x = new AddressManager();
x.version = obj.version;
x.derivationStrategy = obj.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
x.receiveAddressIndex = obj.receiveAddressIndex;
x.changeAddressIndex = obj.changeAddressIndex;
x.copayerIndex = obj.copayerIndex;
@ -29,6 +35,9 @@ AddressManager.fromObj = function(obj) {
return x;
};
AddressManager.supportsCopayerBranches = function(derivationStrategy) {
return derivationStrategy == WalletUtils.DERIVATION_STRATEGIES.BIP45;
};
AddressManager.prototype._incrementIndex = function(isChange) {
if (isChange) {
@ -49,7 +58,7 @@ AddressManager.prototype.rewindIndex = function(isChange, n) {
AddressManager.prototype.getCurrentAddressPath = function(isChange) {
return 'm/' +
this.copayerIndex + '/' +
(this.derivationStrategy == WalletUtils.DERIVATION_STRATEGIES.BIP45 ? this.copayerIndex + '/' : '') +
(isChange ? 1 : 0) + '/' +
(isChange ? this.changeAddressIndex : this.receiveAddressIndex);
};

View File

@ -36,9 +36,13 @@ Copayer.create = function(opts) {
signature: opts.signature,
}];
x.addressManager = AddressManager.create({
copayerIndex: opts.copayerIndex
});
var derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
if (AddressManager.supportsCopayerBranches(derivationStrategy)) {
x.addressManager = AddressManager.create({
derivationStrategy: derivationStrategy,
copayerIndex: opts.copayerIndex,
});
}
x.customData = opts.customData;
@ -66,7 +70,9 @@ Copayer.fromObj = function(obj) {
x.requestPubKeys = obj.requestPubKeys;
}
x.addressManager = AddressManager.fromObj(obj.addressManager);
if (obj.addressManager) {
x.addressManager = AddressManager.fromObj(obj.addressManager);
}
x.customData = obj.customData;
return x;

View File

@ -71,6 +71,7 @@ TxProposal.create = function(opts) {
x.excludeUnconfirmedUtxos = opts.excludeUnconfirmedUtxos;
x.proposalSignaturePubKey = opts.proposalSignaturePubKey;
x.proposalSignaturePubKeySig = opts.proposalSignaturePubKeySig;
x.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
if (_.isFunction(TxProposal._create[x.type])) {
TxProposal._create[x.type](x, opts);
@ -117,6 +118,7 @@ TxProposal.fromObj = function(obj) {
x.excludeUnconfirmedUtxos = obj.excludeUnconfirmedUtxos;
x.proposalSignaturePubKey = obj.proposalSignaturePubKey;
x.proposalSignaturePubKeySig = obj.proposalSignaturePubKeySig;
x.derivationStrategy = obj.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
return x;
};

View File

@ -29,7 +29,10 @@ Wallet.create = function(opts) {
x.copayers = [];
x.pubKey = opts.pubKey;
x.network = opts.network;
x.addressManager = AddressManager.create();
x.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
x.addressManager = AddressManager.create({
derivationStrategy: x.derivationStrategy,
});
x.scanStatus = null;
return x;
@ -51,6 +54,7 @@ Wallet.fromObj = function(obj) {
});
x.pubKey = obj.pubKey;
x.network = obj.network;
x.derivationStrategy = obj.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
x.addressManager = AddressManager.fromObj(obj.addressManager);
x.scanStatus = obj.scanStatus;

View File

@ -203,6 +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.derivationStrategy = 'BIP45'] - Strategy used for address derivation (BIP44, BIP45).
*/
WalletService.prototype.createWallet = function(opts, cb) {
var self = this,
@ -215,10 +216,14 @@ WalletService.prototype.createWallet = function(opts, cb) {
if (!Wallet.verifyCopayerLimits(opts.m, opts.n))
return cb(new ClientError('Invalid combination of required copayers / total copayers'));
var network = opts.network || 'livenet';
if (network != 'livenet' && network != 'testnet')
opts.network = opts.network || 'livenet';
if (!_.contains(['livenet', 'testnet'], opts.network))
return cb(new ClientError('Invalid network'));
opts.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
if (!_.contains(_.values(WalletUtils.DERIVATION_STRATEGIES), opts.derivationStrategy))
return cb(new ClientError('Invalid address derivation strategy'));
try {
pubKey = new PublicKey.fromString(opts.pubKey);
} catch (ex) {
@ -239,15 +244,16 @@ WalletService.prototype.createWallet = function(opts, cb) {
},
function(acb) {
var wallet = Wallet.create({
id: opts.id,
name: opts.name,
m: opts.m,
n: opts.n,
network: network,
network: opts.network,
pubKey: pubKey.toString(),
id: opts.id,
derivationStrategy: opts.derivationStrategy,
});
self.storage.storeWallet(wallet, function(err) {
log.debug('Wallet created', wallet.id, network);
log.debug('Wallet created', wallet.id, opts.network);
newWallet = wallet;
return acb(err);
});
@ -411,6 +417,7 @@ WalletService.prototype._addCopayerToWallet = function(wallet, opts, cb) {
requestPubKey: opts.requestPubKey,
signature: opts.copayerSignature,
customData: opts.customData,
derivationStrategy: opts.derivationStrategy,
});
self.storage.fetchCopayerLookup(copayer.id, function(err, res) {
if (err) return cb(err);
@ -505,6 +512,48 @@ WalletService.prototype.addAccess = function(opts, cb) {
});
};
WalletService.prototype._parseClientVersion = function() {
function parse(version) {
var v = {};
if (!version) return null;
var x = version.split('-');
if (x.length != 2) {
v.agent = version;
return v;
}
v.agent = _.contains(['bwc', 'bws'], x[0]) ? 'bwc' : x[0];
x = x[1].split('.');
v.major = parseInt(x[0]);
v.minor = parseInt(x[1]);
v.patch = parseInt(x[2]);
return v;
};
if (_.isUndefined(this.parsedClientVersion)) {
this.parsedClientVersion = parse(this.clientVersion);
}
return this.parsedClientVersion;
};
WalletService.prototype._clientSupportsBIP44 = function() {
var version = this._parseClientVersion();
if (!version) return false;
if (version.agent != 'bwc') return true; // Asume 3rd party clients are up-to-date
if (version.major == 0 && version.minor <= 1) return false;
return true;
};
WalletService.prototype._clientSupportsTXPv2 = function() {
var version = this._parseClientVersion();
if (!version) return false;
if (version.agent != 'bwc') return true; // Asume 3rd party clients are up-to-date
if (version.major == 0 && version.minor == 0) return false;
return true;
};
/**
* Joins a wallet in creation.
* @param {Object} opts
@ -514,6 +563,7 @@ WalletService.prototype.addAccess = function(opts, cb) {
* @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.derivationStrategy = 'BIP45'] - Strategy used for address derivation (BIP44, BIP45).
*/
WalletService.prototype.joinWallet = function(opts, cb) {
var self = this;
@ -524,12 +574,21 @@ WalletService.prototype.joinWallet = function(opts, cb) {
if (_.isEmpty(opts.name))
return cb(new ClientError('Invalid copayer name'));
opts.derivationStrategy = opts.derivationStrategy || WalletUtils.DERIVATION_STRATEGIES.BIP45;
if (!_.contains(_.values(WalletUtils.DERIVATION_STRATEGIES), opts.derivationStrategy))
return cb(new ClientError('Invalid address derivation strategy'));
self.walletId = opts.walletId;
self._runLocked(cb, function(cb) {
self.storage.fetchWallet(opts.walletId, function(err, wallet) {
if (err) return cb(err);
if (!wallet) return cb(Errors.WALLET_NOT_FOUND);
if (wallet.addressManager.derivationStrategy != opts.derivationStrategy) {
if (!self._clientSupportsBIP44()) {
return cb(new ClientError(Errors.codes.UPGRADE_NEEDED, 'To join this wallet, you need to upgrade your client app.'));
}
return cb(new ClientError('Incompatible address derivation strategy'));
}
var hash = WalletUtils.getCopayerHash(opts.name, opts.xPubKey, opts.requestPubKey);
if (!self._verifySignature(hash, opts.copayerSignature, wallet.pubKey)) {
@ -941,7 +1000,10 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
self.getUtxos({}, function(err, utxos) {
if (err) return cb(err);
var excludeIndex = _.reduce(utxosToExclude, function(res, val) { res[val] = val; return res; }, {});
var excludeIndex = _.reduce(utxosToExclude, function(res, val) {
res[val] = val;
return res;
}, {});
utxos = _.reject(utxos, function(utxo) {
return excludeIndex[utxo.txid + ":" + utxo.vout];
@ -1160,6 +1222,7 @@ WalletService.prototype.createTx = function(opts, cb) {
requiredRejections: Math.min(wallet.m, wallet.n - wallet.m + 1),
walletN: wallet.n,
excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos,
derivationStrategy: wallet.addressManager.derivationStrategy,
};
if (signingKey.selfSigned) {
@ -1169,7 +1232,7 @@ WalletService.prototype.createTx = function(opts, cb) {
var txp = Model.TxProposal.create(txOpts);
if (!self.clientVersion || /^bw.-0\.0\./.test(self.clientVersion)) {
if (!self._clientSupportsTXPv2()) {
txp.version = '1.0.1';
}
@ -1338,7 +1401,7 @@ WalletService.prototype.signTx = function(opts, cb) {
}, function(err, txp) {
if (err) return cb(err);
if (!self.clientVersion || /^bw.-0\.0\./.test(self.clientVersion)) {
if (!self._clientSupportsTXPv2()) {
if (!_.startsWith(txp.version, '1.')) {
return cb(new ClientError(Errors.codes.UPGRADE_NEEDED, 'To sign this spend proposal you need to upgrade your client app.'));
}
@ -1817,10 +1880,12 @@ WalletService.prototype.scan = function(opts, cb) {
});
if (opts.includeCopayerBranches) {
_.each(wallet.copayers, function(copayer) {
derivators.push({
derive: _.bind(copayer.createAddress, copayer, wallet, isChange),
rewind: _.bind(copayer.addressManager.rewindIndex, copayer.addressManager, isChange),
});
if (copayer.addressManager) {
derivators.push({
derive: _.bind(copayer.createAddress, copayer, wallet, isChange),
rewind: _.bind(copayer.addressManager.rewindIndex, copayer.addressManager, isChange),
});
}
});
}
});

View File

@ -2,7 +2,7 @@
"name": "bitcore-wallet-service",
"description": "A service for Mutisig HD Bitcoin Wallets",
"author": "BitPay Inc",
"version": "0.1.10",
"version": "0.2.0",
"keywords": [
"bitcoin",
"copay",
@ -20,7 +20,7 @@
"dependencies": {
"async": "^0.9.0",
"bitcore": "0.13.0",
"bitcore-wallet-utils": "0.1.2",
"bitcore-wallet-utils": "0.2.0",
"body-parser": "^1.11.0",
"coveralls": "^2.11.2",
"email-validator": "^1.0.1",

View File

@ -52,18 +52,25 @@ helpers._generateCopayersTestData = function(n) {
var xpriv_45H = xpriv.derive(45, true);
var xpub_45H = Bitcore.HDPublicKey(xpriv_45H);
var id = WalletUtils.xPubToCopayerId(xpub_45H.toString());
var id45 = WalletUtils.xPubToCopayerId(xpub_45H.toString());
var xpriv_44H_0H_0H = xpriv.derive(44, true).derive(0, true).derive(0, true);
var xpub_44H_0H_0H = Bitcore.HDPublicKey(xpriv_44H_0H_0H);
var id44 = WalletUtils.xPubToCopayerId(xpub_44H_0H_0H.toString());
var xpriv_1H = xpriv.derive(1, true);
var xpub_1H = Bitcore.HDPublicKey(xpriv_1H);
var priv = xpriv_1H.derive(0).privateKey;
var pub = xpub_1H.derive(0).publicKey;
console.log('{id: ', "'" + id + "',");
console.log('{id44: ', "'" + id44 + "',");
console.log('id45: ', "'" + id45 + "',");
console.log('xPrivKey: ', "'" + xpriv.toString() + "',");
console.log('xPubKey: ', "'" + xpub.toString() + "',");
console.log('xPrivKey_45H: ', "'" + xpriv_45H.toString() + "',");
console.log('xPubKey_45H: ', "'" + xpub_45H.toString() + "',");
console.log('xPrivKey_44H_0H_0H: ', "'" + xpriv_44H_0H_0H.toString() + "',");
console.log('xPubKey_44H_0H_0H: ', "'" + xpub_44H_0H_0H.toString() + "',");
console.log('xPrivKey_1H: ', "'" + xpriv_1H.toString() + "',");
console.log('xPubKey_1H: ', "'" + xpub_1H.toString() + "',");
console.log('privKey_1H_0: ', "'" + priv.toString() + "',");
@ -94,19 +101,22 @@ helpers.createAndJoinWallet = function(m, n, opts, cb) {
m: m,
n: n,
pubKey: TestData.keyPair.pub,
derivationStrategy: opts.derivationStrategy || 'BIP44',
};
server.createWallet(walletOpts, function(err, walletId) {
if (err) return cb(err);
async.each(_.range(n), function(i, cb) {
var copayerData = TestData.copayers[i + offset];
var bip = opts.derivationStrategy || 'BIP44';
var copayerOpts = helpers.getSignedCopayerOpts({
walletId: walletId,
name: 'copayer ' + (i + 1),
xPubKey: TestData.copayers[i + offset].xPubKey_45H,
requestPubKey: TestData.copayers[i + offset].pubKey_1H_0,
xPubKey: bip == 'BIP44' ? copayerData.xPubKey_44H_0H_0H : copayerData.xPubKey_45H,
requestPubKey: copayerData.pubKey_1H_0,
customData: 'custom data ' + (i + 1),
derivationStrategy: bip,
});
server.joinWallet(copayerOpts, function(err, result) {
should.not.exist(err);
copayerIds.push(result.copayerId);
@ -469,7 +479,7 @@ describe('Wallet service', function() {
txpId = txp.id;
async.eachSeries(_.range(2), function(i, next) {
var copayer = TestData.copayers[i];
helpers.getAuthServer(copayer.id, function(server) {
helpers.getAuthServer(copayer.id44, function(server) {
var signatures = helpers.clientSign(txp, copayer.xPrivKey);
server.signTx({
txProposalId: txp.id,
@ -525,7 +535,7 @@ describe('Wallet service', function() {
txpId = txp.id;
async.eachSeries(_.range(1, 3), function(i, next) {
var copayer = TestData.copayers[i];
helpers.getAuthServer(copayer.id, function(server) {
helpers.getAuthServer(copayer.id44, function(server) {
server.rejectTx({
txProposalId: txp.id,
}, next);
@ -733,11 +743,13 @@ describe('Wallet service', function() {
});
it('should fail when requesting for non-existent copayer', function(done) {
WalletService.getInstanceWithAuth({
copayerId: 'ads',
message: TestData.message.text,
signature: TestData.message.signature,
}, function(err, server) {
var message = 'hello world';
var opts = {
copayerId: 'dummy',
message: message,
signature: WalletUtils.signMessage(message, TestData.copayers[0].privKey_1H_0),
};
WalletService.getInstanceWithAuth(opts, function(err, server) {
err.code.should.equal('NOT_AUTHORIZED');
err.message.should.contain('Copayer not found');
done();
@ -783,8 +795,7 @@ describe('Wallet service', function() {
});
});
it('should create wallet with given id', function(done) {
it('should create wallet with given id', function(done) {
var opts = {
name: 'my wallet',
m: 2,
@ -983,6 +994,7 @@ describe('Wallet service', function() {
name: 'me',
xPubKey: TestData.copayers[1].xPubKey_45H,
requestPubKey: TestData.copayers[1].pubKey_1H_0,
derivationStrategy: 'BIP44',
});
server.joinWallet(copayerOpts, function(err) {
should.exist(err);
@ -1120,10 +1132,99 @@ describe('Wallet service', function() {
});
});
describe('Address derivation strategy', function() {
var server;
beforeEach(function() {
server = WalletService.getInstance({
clientVersion: 'bwc-0.2.0',
});
});
it('should fail to join BIP45 wallet with BIP44 copayer opts', function(done) {
var walletOpts = {
name: 'my wallet',
m: 2,
n: 3,
pubKey: TestData.keyPair.pub,
derivationStrategy: 'BIP45',
};
server.createWallet(walletOpts, function(err, wid) {
should.not.exist(err);
var copayerOpts = helpers.getSignedCopayerOpts({
walletId: wid,
name: 'me',
xPubKey: TestData.copayers[0].xPubKey_44H_0H_0H,
requestPubKey: TestData.copayers[0].pubKey_1H_0,
customData: 'dummy custom data',
derivationStrategy: 'BIP44',
});
server.joinWallet(copayerOpts, function(err, result) {
should.exist(err);
err.message.toLowerCase().should.contain('address derivation strategy');
done();
});
});
});
it('should fail to join BIP44 wallet with BIP45 copayer opts', function(done) {
var walletOpts = {
name: 'my wallet',
m: 2,
n: 3,
pubKey: TestData.keyPair.pub,
derivationStrategy: 'BIP44',
};
server.createWallet(walletOpts, function(err, wid) {
should.not.exist(err);
var copayerOpts = helpers.getSignedCopayerOpts({
walletId: wid,
name: 'me',
xPubKey: TestData.copayers[0].xPubKey_45H,
requestPubKey: TestData.copayers[0].pubKey_1H_0,
customData: 'dummy custom data',
derivationStrategy: 'BIP45',
});
server.joinWallet(copayerOpts, function(err, result) {
should.exist(err);
err.message.toLowerCase().should.contain('address derivation strategy');
done();
});
});
});
it('should require upgrade when joining BIP44 wallet with BIP45 copayer opts from old client app', function(done) {
var walletOpts = {
name: 'my wallet',
m: 2,
n: 3,
pubKey: TestData.keyPair.pub,
derivationStrategy: 'BIP44',
};
server.createWallet(walletOpts, function(err, wid) {
should.not.exist(err);
var copayerOpts = helpers.getSignedCopayerOpts({
walletId: wid,
name: 'me',
xPubKey: TestData.copayers[0].xPubKey_45H,
requestPubKey: TestData.copayers[0].pubKey_1H_0,
customData: 'dummy custom data',
derivationStrategy: 'BIP45',
});
server = WalletService.getInstance({
clientVersion: 'bwc-0.1.4',
});
server.joinWallet(copayerOpts, function(err, result) {
should.exist(err);
err.code.should.equal('UPGRADE_NEEDED');
done();
});
});
});
});
describe('#getStatus', function() {
var server, wallet;
beforeEach(function(done) {
helpers.createAndJoinWallet(1, 1, function(s, w) {
helpers.createAndJoinWallet(1, 1, {
derivationStrategy: 'BIP45'
}, function(s, w) {
server = s;
wallet = w;
done();
@ -1211,21 +1312,23 @@ describe('Wallet service', function() {
});
it('should successfully verify message signature', function(done) {
var message = 'hello world';
var opts = {
message: TestData.message.text,
signature: TestData.message.signature,
message: message,
signature: WalletUtils.signMessage(message, TestData.copayers[0].privKey_1H_0),
};
server.verifyMessageSignature(opts, function(err, isValid) {
should.not.exist(err);
isValid.should.equal(true);
isValid.should.be.true;
done();
});
});
it('should fail to verify message signature for different copayer', function(done) {
var message = 'hello world';
var opts = {
message: TestData.message.text,
signature: TestData.message.signature,
message: message,
signature: WalletUtils.signMessage(message, TestData.copayers[0].privKey_1H_0),
};
helpers.getAuthServer(wallet.copayers[1].id, function(server) {
server.verifyMessageSignature(opts, function(err, isValid) {
@ -1253,16 +1356,16 @@ describe('Wallet service', function() {
should.exist(address);
address.walletId.should.equal(wallet.id);
address.network.should.equal('livenet');
address.address.should.equal('3KxttbKQQPWmpsnXZ3rB4mgJTuLnVR7frg');
address.address.should.equal('36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r');
address.isChange.should.be.false;
address.path.should.equal('m/2147483647/0/0');
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('3KxttbKQQPWmpsnXZ3rB4mgJTuLnVR7frg');
notif.data.address.should.equal('36q2G5FMGvJbPgAVEaiyAsFGmpkhPKwk2r');
done();
});
});
@ -1275,7 +1378,7 @@ describe('Wallet service', function() {
}, function(err, addresses) {
addresses.length.should.equal(N);
_.each(_.range(N), function(i) {
addresses[i].path.should.equal('m/2147483647/0/' + i);
addresses[i].path.should.equal('m/0/' + i);
});
// No two identical addresses
_.uniq(_.pluck(addresses, 'address')).length.should.equal(N);
@ -1296,13 +1399,59 @@ describe('Wallet service', function() {
server.createAddress({}, function(err, address) {
should.not.exist(err);
should.exist(address);
address.address.should.equal('3KxttbKQQPWmpsnXZ3rB4mgJTuLnVR7frg');
address.path.should.equal('m/2147483647/0/0');
done();
});
});
});
});
describe('BIP45', function() {
beforeEach(function(done) {
helpers.createAndJoinWallet(2, 2, {
derivationStrategy: 'BIP45'
}, 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('3BVJZ4CYzeTtawDtgwHvWV5jbvnXtYe97i');
address.isChange.should.be.false;
address.path.should.equal('m/2147483647/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('3BVJZ4CYzeTtawDtgwHvWV5jbvnXtYe97i');
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/2147483647/0/' + i);
});
// No two identical addresses
_.uniq(_.pluck(addresses, 'address')).length.should.equal(N);
done();
});
});
});
});
describe('Preferences', function() {
@ -1508,10 +1657,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,
@ -4132,17 +4281,17 @@ describe('Wallet service', function() {
it('should scan main addresses', function(done) {
helpers.stubAddressActivity(
['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A', // m/2147483647/0/0
'3NezgtNbuDzL2sFhnfxyVy8bHp4v6ud252', // m/2147483647/0/2
'3CQ2hCMUu1SCPVPMpfCCuT3nAfHGiHV1o7', // m/2147483647/1/0
['3Jc2hcN13vGBCdWR4SMNRoL9EUiBHuf4LT', // m/0/0
'3Pk2Cn3TW6JLhGEnHg8iM2qtmx2T1uHyZM', // m/0/2
'3GqrwYwyLt1czKMvvqFwYj2ZeooiHtbgbc', // m/1/0
]);
var expectedPaths = [
'm/2147483647/0/0',
'm/2147483647/0/1',
'm/2147483647/0/2',
'm/2147483647/0/3',
'm/2147483647/1/0',
'm/2147483647/1/1',
'm/0/0',
'm/0/1',
'm/0/2',
'm/0/3',
'm/1/0',
'm/1/1',
];
server.scan({}, function(err) {
should.not.exist(err);
@ -4156,46 +4305,13 @@ 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/2147483647/0/4');
address.path.should.equal('m/0/4');
done();
});
});
});
});
});
it('should scan main addresses & copayer addresses', function(done) {
helpers.stubAddressActivity(
['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A', // m/2147483647/0/0
'3CQ2hCMUu1SCPVPMpfCCuT3nAfHGiHV1o7', // m/2147483647/1/0
'3BYHznBmosYxUj1NWcjdFKX2tdsH7UT1YG', // m/0/0/1
'3Eg1uPkGnwyU42bRiaDuo6Cu9bjjhoG7Sh', // m/1/1/0
'3AYmZ63tMd2AHN8QLfu5D2nfRzCH66psWx', // m/1/0/0
]);
var expectedPaths = [
'm/2147483647/0/0',
'm/2147483647/0/1',
'm/2147483647/1/0',
'm/2147483647/1/1',
'm/0/0/0',
'm/0/0/1',
'm/1/0/0',
'm/1/0/1',
'm/1/1/0',
'm/1/1/1',
];
server.scan({
includeCopayerBranches: true
}, function(err) {
should.not.exist(err);
server.storage.fetchAddresses(wallet.id, function(err, addresses) {
should.exist(addresses);
addresses.length.should.equal(expectedPaths.length);
var paths = _.pluck(addresses, 'path');
_.difference(paths, expectedPaths).length.should.equal(0);
done();
})
});
});
it('should restore wallet balance', function(done) {
async.waterfall([
@ -4253,6 +4369,93 @@ describe('Wallet service', function() {
});
});
});
describe('BIP45', function() {
beforeEach(function(done) {
this.timeout(5000);
WalletService.SCAN_CONFIG.scanWindow = 2;
WalletService.SCAN_CONFIG.derivationDelay = 0;
helpers.createAndJoinWallet(1, 2, {
derivationStrategy: 'BIP45'
}, function(s, w) {
server = s;
wallet = w;
done();
});
});
afterEach(function() {
WalletService.SCAN_CONFIG = scanConfigOld;
});
it('should scan main addresses', function(done) {
helpers.stubAddressActivity(
['39AA1Y2VvPJhV3RFbc7cKbUax1WgkPwweR', // m/2147483647/0/0
'3QX2MNSijnhCALBmUVnDo5UGPj3SEGASWx', // m/2147483647/0/2
'3MzGaz4KKX66w8ShKaR536ZqzVvREBqqYu', // m/2147483647/1/0
]);
var expectedPaths = [
'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.scan({}, function(err) {
should.not.exist(err);
server.getWallet({}, function(err, wallet) {
should.not.exist(err);
wallet.scanStatus.should.equal('success');
server.storage.fetchAddresses(wallet.id, function(err, addresses) {
should.exist(addresses);
addresses.length.should.equal(expectedPaths.length);
var paths = _.pluck(addresses, 'path');
_.difference(paths, expectedPaths).length.should.equal(0);
server.createAddress({}, function(err, address) {
should.not.exist(err);
address.path.should.equal('m/2147483647/0/4');
done();
});
});
});
});
});
it('should scan main addresses & copayer addresses', function(done) {
helpers.stubAddressActivity(
['39AA1Y2VvPJhV3RFbc7cKbUax1WgkPwweR', // m/2147483647/0/0
'3MzGaz4KKX66w8ShKaR536ZqzVvREBqqYu', // m/2147483647/1/0
'3BYoynejwBH9q4Jhr9m9P5YTnLTu57US6g', // m/0/0/1
'37Pb8c32hzm16tCZaVHj4Dtjva45L2a3A3', // m/1/1/0
'32TB2n283YsXdseMqUm9zHSRcfS5JxTWxx', // m/1/0/0
]);
var expectedPaths = [
'm/2147483647/0/0',
'm/2147483647/0/1',
'm/2147483647/1/0',
'm/2147483647/1/1',
'm/0/0/0',
'm/0/0/1',
'm/1/0/0',
'm/1/0/1',
'm/1/1/0',
'm/1/1/1',
];
server.scan({
includeCopayerBranches: true
}, function(err) {
should.not.exist(err);
server.storage.fetchAddresses(wallet.id, function(err, addresses) {
should.exist(addresses);
addresses.length.should.equal(expectedPaths.length);
var paths = _.pluck(addresses, 'path');
_.difference(paths, expectedPaths).length.should.equal(0);
done();
})
});
});
});
});
describe('#startScan', function() {
@ -4275,18 +4478,18 @@ describe('Wallet service', function() {
});
it('should start an asynchronous scan', function(done) {
helpers.stubAddressActivity([
'3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A', // m/2147483647/0/0
'3NezgtNbuDzL2sFhnfxyVy8bHp4v6ud252', // m/2147483647/0/2
'3CQ2hCMUu1SCPVPMpfCCuT3nAfHGiHV1o7', // m/2147483647/1/1
]);
helpers.stubAddressActivity(
['3Jc2hcN13vGBCdWR4SMNRoL9EUiBHuf4LT', // m/0/0
'3Pk2Cn3TW6JLhGEnHg8iM2qtmx2T1uHyZM', // m/0/2
'3GqrwYwyLt1czKMvvqFwYj2ZeooiHtbgbc', // m/1/0
]);
var expectedPaths = [
'm/2147483647/0/0',
'm/2147483647/0/1',
'm/2147483647/0/2',
'm/2147483647/0/3',
'm/2147483647/1/0',
'm/2147483647/1/1',
'm/0/0',
'm/0/1',
'm/0/2',
'm/0/3',
'm/1/0',
'm/1/1',
];
server.messageBroker.onMessage(function(n) {
if (n.type == 'ScanFinished') {
@ -4301,7 +4504,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/2147483647/0/4');
address.path.should.equal('m/0/4');
done();
});
})

View File

@ -8,53 +8,140 @@ var AddressManager = require('../../lib/model/addressmanager');
describe('AddressManager', function() {
describe('#getCurrentAddressPath', function() {
it('should return a valid BIP32 path for given index', function() {
var am = AddressManager.create({
copayerIndex: 4
});
am.getCurrentAddressPath(false).should.equal('m/4/0/0');
am.getCurrentAddressPath(true).should.equal('m/4/1/0');
describe('#create', function() {
it('should create BIP45 address manager by default', function() {
var am = AddressManager.create();
am.derivationStrategy.should.equal('BIP45');
});
});
describe('#getCurrentAddressPath', function() {
describe('#fromObj', function() {
it('should assume legacy address manager uses BIP45', function() {
var obj = {
version: '1.0.0',
receiveAddressIndex: 2,
changeAddressIndex: 0,
copayerIndex: 4
};
var am = AddressManager.fromObj(obj);
am.derivationStrategy.should.equal('BIP45');
am.getCurrentAddressPath(false).should.equal('m/4/0/2');
});
});
describe('#supportsCopayerBranches', function() {
it('should return true for BIP45 & false for BIP44', function() {
AddressManager.supportsCopayerBranches('BIP45').should.be.true;
AddressManager.supportsCopayerBranches('BIP44').should.be.false;
});
});
describe('BIP45', function() {
describe('#getCurrentAddressPath', function() {
it('should return a valid BIP32 path for given index', function() {
var am = AddressManager.create({
copayerIndex: 4,
});
am.getCurrentAddressPath(false).should.equal('m/4/0/0');
am.getCurrentAddressPath(true).should.equal('m/4/1/0');
});
});
it('should return a valid BIP32 path for defaut Index', function() {
var am = AddressManager.create();
var am = AddressManager.create({});
am.getCurrentAddressPath(false).should.equal('m/2147483647/0/0');
am.getCurrentAddressPath(true).should.equal('m/2147483647/1/0');
});
});
describe('#getNewAddressPath', function() {
it('should return a new valid BIP32 path for given index', function() {
var am = AddressManager.create({
copayerIndex: 2
describe('#getNewAddressPath', function() {
it('should return a new valid BIP32 path for given index', function() {
var am = AddressManager.create({
copayerIndex: 2,
});
am.getNewAddressPath(false).should.equal('m/2/0/0');
am.getNewAddressPath(true).should.equal('m/2/1/0');
am.getNewAddressPath(false).should.equal('m/2/0/1');
am.getNewAddressPath(true).should.equal('m/2/1/1');
});
});
describe('#rewindIndex', function() {
it('should rewind main index', function() {
var am = AddressManager.create({});
am.getNewAddressPath(false).should.equal('m/2147483647/0/0');
am.getNewAddressPath(false).should.equal('m/2147483647/0/1');
am.getNewAddressPath(false).should.equal('m/2147483647/0/2');
am.rewindIndex(false, 2);
am.getNewAddressPath(false).should.equal('m/2147483647/0/1');
});
it('should rewind change index', function() {
var am = AddressManager.create({});
am.getNewAddressPath(true).should.equal('m/2147483647/1/0');
am.rewindIndex(false, 1);
am.getNewAddressPath(true).should.equal('m/2147483647/1/1');
am.rewindIndex(true, 2);
am.getNewAddressPath(true).should.equal('m/2147483647/1/0');
});
it('should stop at 0', function() {
var am = AddressManager.create({});
am.getNewAddressPath(false).should.equal('m/2147483647/0/0');
am.rewindIndex(false, 20);
am.getNewAddressPath(false).should.equal('m/2147483647/0/0');
});
am.getNewAddressPath(false).should.equal('m/2/0/0');
am.getNewAddressPath(true).should.equal('m/2/1/0');
});
});
describe('#rewindIndex', function() {
it('should rewind main index', function() {
var am = AddressManager.create({});
am.getNewAddressPath(false).should.equal('m/2147483647/0/0');
am.getNewAddressPath(false).should.equal('m/2147483647/0/1');
am.getNewAddressPath(false).should.equal('m/2147483647/0/2');
am.rewindIndex(false, 2);
am.getNewAddressPath(false).should.equal('m/2147483647/0/1');
describe('BIP44', function() {
describe('#getCurrentAddressPath', function() {
it('should return first address path', function() {
var am = AddressManager.create({
derivationStrategy: 'BIP44',
});
am.getCurrentAddressPath(false).should.equal('m/0/0');
am.getCurrentAddressPath(true).should.equal('m/1/0');
});
it('should return address path independently of copayerIndex', function() {
var am = AddressManager.create({
derivationStrategy: 'BIP44',
copayerIndex: 4,
});
am.getCurrentAddressPath(false).should.equal('m/0/0');
am.getCurrentAddressPath(true).should.equal('m/1/0');
});
});
it('should rewind change index', function() {
var am = AddressManager.create({});
am.getNewAddressPath(true).should.equal('m/2147483647/1/0');
am.rewindIndex(false, 1);
am.getNewAddressPath(true).should.equal('m/2147483647/1/1');
am.rewindIndex(true, 2);
am.getNewAddressPath(true).should.equal('m/2147483647/1/0');
describe('#getNewAddressPath', function() {
it('should return a new path', function() {
var am = AddressManager.create({
derivationStrategy: 'BIP44',
});
am.getNewAddressPath(false).should.equal('m/0/0');
am.getNewAddressPath(true).should.equal('m/1/0');
am.getNewAddressPath(false).should.equal('m/0/1');
am.getNewAddressPath(true).should.equal('m/1/1');
});
});
it('should stop at 0', function() {
var am = AddressManager.create({});
am.getNewAddressPath(false).should.equal('m/2147483647/0/0');
am.rewindIndex(false, 20);
am.getNewAddressPath(false).should.equal('m/2147483647/0/0');
describe('#rewindIndex', function() {
it('should rewind main index', function() {
var am = AddressManager.create({
derivationStrategy: 'BIP44',
});
am.getNewAddressPath(false).should.equal('m/0/0');
am.getNewAddressPath(false).should.equal('m/0/1');
am.getNewAddressPath(false).should.equal('m/0/2');
am.rewindIndex(false, 2);
am.getNewAddressPath(false).should.equal('m/0/1');
});
it('should rewind change index', function() {
var am = AddressManager.create({
derivationStrategy: 'BIP44',
});
am.getNewAddressPath(true).should.equal('m/1/0');
am.rewindIndex(false, 1);
am.getNewAddressPath(true).should.equal('m/1/1');
am.rewindIndex(true, 2);
am.getNewAddressPath(true).should.equal('m/1/0');
});
it('should stop at 0', function() {
var am = AddressManager.create({
derivationStrategy: 'BIP44',
});
am.getNewAddressPath(false).should.equal('m/0/0');
am.rewindIndex(false, 20);
am.getNewAddressPath(false).should.equal('m/0/0');
});
});
});
});

View File

@ -3,113 +3,136 @@ var keyPair = {
pub: '026092daeed8ecb2212869395770e956ffc9bf453f803e700f64ffa70c97a00d80',
};
var message = {
text: 'hello world',
signature: '30440220091598edbe4b45d41b551c941f24401fd3d72a8ccd18721dbfc58be9bdc234d802203870d36cf1fee89bb78cf161334849774c5684d287a74a9bd6681ee5e012150d', // Signed with copayer[0].privKey_1H_0
};
var copayers = [{
id: 'ec2dd28c76367b6cfbf4a8d7cc17c9473fd78c995092a83e1776e1be43a30dc6',
xPrivKey: 'xprv9s21ZrQH143K2TjAA6HcG4p2eikKc4qQMVekTPqwNcesQ89nFZHzHKmekbwMKeiK6cPSzmqAavdemUS6VUzhGc5Uf4TCtjBa9qwDfbnWQcJ',
xPubKey: 'xpub661MyMwAqRbcEwodG7pcdCkmCkap1XZFiiaMFnFYvxBrGvUvo6cEq868bukLYM938wPm1CMzFuvbDQbA2tJvsWKgTz4xDKTyEfbQvBbo1AR',
xPrivKey_45H: 'xprv9uZiJ2ZYFNQ3RDBvjtz1HcivbfWbncMAb6EvKKose8rUbRo9dwX5hnThGodivCDuV8s48xZk7DZSuNpYb9ZwCpsatsnibRyirQbqqroftMP',
xPubKey_45H: 'xpub68Z4hY6S5jxLdhGPqvX1ekff9hM6C551xKAX7iDVCUPTUE8JBUqLFanB8697EoXbwN5rjDLHgPWkbofr8otNeb8Kr8SSh8pCiNMp7sWkBj4',
xPrivKey_1H: 'xprv9uZiJ2ZYFNQ1WeKrKfTxrbUnXfNnCtpbdHVuUMSVGB57nhbmjqWbJBQbgGNa2jSbrgQX9NFHXtAoQ3y1hsEgwjKHxUU52ZYTC6m5kjXoNpJ',
xPubKey_1H: 'xpub68Z4hY6S5jxJj8QKRgzyDjRX5hDGcMYSzWRWGjr6pWc6fVvvHNpqqyj5XXbMcfW737beYZcd7EQau5HS74Ws6Ctx9XwFn2wHQjSUKLbfdFk',
privKey_1H_0: 'e334a0ce3d573bd99fc4cd7e2065e39dc7851cb61da7f381431c253c3e230828',
pubKey_1H_0: '02db35c363e2c904bba3cd0eadb6b8d68fc1d8e6160d632b8fb91a471b80e40c86',
privKey_1_0: '89833f39998ff14f2cbb461365ccba30583abfd2a7845e0bb61849275802005d',
pubKey_1_0: '022ef899f914c9b90a5544923a3aa6de5ddcd6c9d0b603b63ab9fd0c2cdc5d3772',
id44: '626452e5e0e35df4d9ae4d3e60653c9ae9a814f00c84dc40f5887069b18e2110',
id45: 'e7467366d5754be2b7d386c9737ab87214c26314bdc3489702e09c719be1bdb7',
xPrivKey: 'xprv9s21ZrQH143K2n4rV4AtAJFptEmd1tNMKCcSyQBCSuN5eq1dCUhcv6KQJS49joRxu8NNdFxy8yuwTtzCPNYUZvVGC7EPRm2st2cvE7oyTbB',
xPubKey: 'xpub661MyMwAqRbcFG9Kb5htXSCZSGc7RM6CgRY3mnap1Eu4XdLmk21sTtdt9iWAiL64KazU3QWrYEYSRAKgLvkMRe8JMxffDvt4AhCDzyMsnsT',
xPrivKey_45H: 'xprv9upyD5bqT9HDgybvt15zN7VZk9TyTzenFuBktJfyweXtVSr9eNYpnBWQQvzxaW8kDWLeiddLu9brYma5WdtD1Maev4aGUfJjiy9EBc1QSFg',
xPubKey_45H: 'xpub68pKcb8jHWqWuTgPz2czjFSJJBJTsTNdd87Mgh5bVz4sNFBJBus5KyptGBWgA4V6LGCi12s4Mw4S1JC2GkqX4NJ4kfQ47XqRZLbyM2DY9Jd',
xPrivKey_44H_0H_0H: 'xprv9zWRZ7CXrC4z9xA9RRBFXohmPKbyCajWaCNTHPtwNeJwTnysHG5QK7WMqpNLVtvqGxts7WNcNtqBLfdaFdCGknDPXjLKt2E2BUrPaFDqrLh',
xPubKey_44H_0H_0H: 'xpub6DVmxcjRgZdHNSEcXSiFtweVwMSTc3TMwRJ45nJYvyqvLbK1poPerupqh87rSoz27wvckb1CKnGZoLmLXSZyNGZtVd7neqSvdwJL6fceQpe',
xPrivKey_1H: 'xprv9upyD5bqT9HBkWym7TvTH3njEzTnjrtkLB2sg3DD2CxxA5hZKGee1sYJUtD8C4QaeATLXQ33TirRzRhuTGDBA6XRoYDMwfXAj1KSmGyNBio',
xPubKey_1H: 'xpub68pKcb8jHWqUy14EDVTTeBjTo2JH9KcbhPxUURcpaYVw2t2hroxtZfrnLBw1bWzBrHbEJA48QmZ8DB9gTvhphKSitC15SiYx9k2ncGh55Hq',
privKey_1H_0: 'a710be25950738a7d13637e2e09affd7f579a3479fd7cc024bd9459f8fba6659',
pubKey_1H_0: '026e3020913420a5b9425952627f0a074c9235e7a329869b322061f786e997ae0d'
}, {
id: 'cf652642ebf997460d642231f9929c39257bcd4c42e9ecb312c226961e21c882',
xPrivKey: 'xprv9s21ZrQH143K2XtR13LpXVAPaGTFsPY5dJfNvNTShfhdVW16vdDaYGjLwHmyKLCtv3F6h9NpKsAGusmKCZNQvKcYqYf9MjCLwJwsoEyAuge',
xPubKey: 'xpub661MyMwAqRbcF1xt74sptd788JHkGrFvzXayiks4G1EcNJLFUAXq653pncPaLt6vcEARqSHopD8PHT5PWkDryuZNBYpxX5wUdeV7DtgjKoL',
xPrivKey_45H: 'xprv9v8AzYcRrUwmJQGbDDKXvydEG2NZZDqXn9J3B6au2S8yhLfJyQidhZgMfTpFxBqAkUxgtTDc9JWFt4JaTmwKNLGxr6oSKjpaheQ5EG8jHz6',
xPubKey_45H: 'xpub697XQ49KgrW4WtM4KErYJ7Zxp4D3xgZP9NDdyUzWamfxa8zTWx2tFMzqWktiJG1yDo6mYpp8NAySfzRS8JoJyz2Br26vARWY9d9QAJdYFYh',
xPrivKey_1H: 'xprv9v8AzYcRrUwjNBGAL5pzBaLvDBwepj9YVDM5UHjZXYYdoAxi9uaJjALUKPRcMxZZWq265CZuEfhTbwE6BD1tndRGESV4Jbr4c3pLanhnZHm',
xPubKey_1H: 'xpub697XQ49KgrW2afLdS7MzYiHemDn9EBsPrSGgGg9B5t5cfyHrhStZGxexAdekvK84XDZGunARoEzzHWmPjPwaRkKJwV2KWiRcHxVPfwwWKtg',
privKey_1H_0: '64274141d3ed98d3ee159409939627bd32229eac8b29cddc4c744c4e7f20235f',
pubKey_1H_0: '02787e683b17a4729e4e9405f80b74fb19b369133dd1c98b801230ae6603cc28aa'
id44: '842c048066e7d10ae1bbf67edccf69f2e5ff9a754d0c2b5524f0d01a87d6acbb',
id45: 'ee75154b646277c8d0d256fc1a0aa0470e4c3435497f208092c865737040b55b',
xPrivKey: 'xprv9s21ZrQH143K3BwkLceWNLUsgES15JoZuv8BZfnmDRcCGtDooUAPhY8KovhCWcRLXUun5AYL5vVtUNRrmPEibtfk9ongxAGLXZzEHifpvwZ',
xPubKey: 'xpub661MyMwAqRbcFg2DSeBWjURcEGGVUmXRH93nN4CNmm9B9gYxM1UeFLSofD6gtMnRYeucgPjfrWNxaAEhiT4di6HLty8Un6aheCKev4REvhZ',
xPrivKey_45H: 'xprv9uHco76yN7arptqnjGr7LiC9VqvXhTYjYeZ4k9MU6kfrX8zBme1gRM2jbBxxt3zfeKBkmpQK4FQ2XFXXkJd2rBE2qoPdMjfC4xvxJ8iqqPW',
xPubKey_45H: 'xpub68GyCcdsCV9A3NvFqJP7hr8t3sm26vGausUfYXm5f6CqPwKLKBKvy9MDSUA6ot6WMzXCWFyWCFfZyhcqK4uZGPZL83rKW9KfuGLPJ6P38Ex',
xPrivKey_44H_0H_0H: 'xprv9yBDgTV58cC2XLCp3bGpe6tvmVHxJdE2GTkqM64TUjgRdp4vBnaF4QoMrsz3BjmFQDEx4nPaC4tfk7GUrNFpJZmQgxvp1H7FoveXA7rAPqH',
xPubKey_44H_0H_0H: 'xpub6CAa5y1xxykKjpHH9coq1EqfKX8Si5wsdggS9UU535DQWcQ4jKtVcD7qi9TCvhV31N3QCLdEaftLjNdXv8R72XiorEnrTfn1ULjA35RgQVp',
xPrivKey_1H: 'xprv9uHco76yN7aptKFWZW9Cc9KQhwDbFMDeK1C5jBTrN8JKUinAzAZt9G7fL5rcDBQYtXWT7umm4r91tx3kJRZKPKVkd35dzgfwMBLUxBHneAY',
xPubKey_1H: 'xpub68GyCcdsCV986oKyfXgCyHG9Fy45eowVgE7gXZsTvTqJMX7KXht8h4S9BLnHyWx8YbT8kRX5v4xnZAZvHmdUSdQhvZ2q97PG8m8Ef86yjzK',
privKey_1H_0: 'ee062ce6dc5ece50e8110646b5e858c98dba9315cdfdd19da85ab0d33dcac74a',
pubKey_1H_0: '02c679bf169233a273dec87fae5a1830481866c4e96a350d56346ac267808c905d'
}, {
id: 'ff98c0b625e7824219f6a8054470a332ce1bdc9bb5a46a57eb424ed0848c0183',
xPrivKey: 'xprv9s21ZrQH143K3F4gznfMiq1rFLNAJbWnC9HJdkdrL7xpt4YxeLWRJTPXFvMn5Ky8rqaunWNTxwrLF6Nhptjyq8mAJvmc13R3fRJLDWWmgon',
xPubKey: 'xpub661MyMwAqRbcFj9A6pCN5xxaoNCei4EdZNCuS93TtTVokrt7BspfrFi17DdM2Z4HYYseVoZ2nmeu5mCvsn8gYGyW94m7EfXQDbXymLw2Yi1',
xPrivKey_45H: 'xprv9vUaCxyzkmLHHUpZZFUvh9oqzm9R6UMCTsJn8F7mDN3AR7rwLobojATJYgSx3pJjrv3EWCaSYeBjhouiM1m9Y9kAHAku3ZiAxZyWRxQZyHf',
xPubKey_45H: 'xpub69TvcUWtb8taVxu2fH1w4HkaYnyuVw53q6ENvdXNmha9HvC5tLv4GxmnPxemekKx8jVx7kmbtATgH5KXEMibhpZh7x2NN8AXcwF7FvJmvXo',
xPrivKey_1H: 'xprv9vUaCxyzkmLFMyQhaN3knpLSWHeqTbNbxVa9cHgJ88m287K6iLazaT5CYBgCaW6PEJy7bKeDZ2Wi8UsWnzND9QtzSy9CFWdgMxZAJ3mP5hW',
xPubKey_1H: 'xpub69TvcUWtb8tYaTVAgPam9xHB4KVKs46TKiVkQg5ugUHzzueFFsuF8FPgPTLeRcZmq3EwbrnYGX6gHmShDNL1YgNqjo5ctqw5Z7WXe9LSErX',
privKey_1H_0: '25ac29ca9bffe835bdafed55feebe606c7250e2b399cd3d95347a746dd6d3388',
pubKey_1H_0: '031a69ea8a3d487743c7d6acdcecc151c685fd25f8f41b327f4c25d8b21f6c80ba'
id44: '719f4ee61c691fbf0ebefa34e2151a1a3dbe39cf2fa4a498cb6af53600d30d1a',
id45: 'acd666d7c677d9f2c85b55a5fad1610fe272eac46ef7a577c7aeeab0b1474e43',
xPrivKey: 'xprv9s21ZrQH143K3xgLzxd6SuWqG5Zp1iUmyGgSsJVhdQNeTzAqBFvXXLZqZzFZqocTx4HD9vUVYU27At5i8q46LmBXXL97fo4H9C3tHm4BnjY',
xPubKey: 'xpub661MyMwAqRbcGSkp6zA6p3TZp7QJRBCdLVc3fguKBjudLnVyioEn58tKRFPmGMkdGJWMX69mgZWHKrKmpQ3fwBXeFjLc5Sd2rnxcQthSW42',
xPrivKey_45H: 'xprv9vGShhcT8rDFomkiF72D5UXt5BsVydntE7rYbKaa9qtscnWVHaZSPkV9N9SkDkhWbDvkmTGVLYKJf23Z8QjXMRRMTwQYLj26VkPbsKxnUwV',
xPubKey_45H: 'xpub69Fo7D9LyDmZ2FqBM8ZDScUcdDhzP6WjbLn9PhzBiBRrVaqdq7sgwYodDPUzXyGxpEfTCQyTAA9baAwCApmTiing2S7KTQTmLZjw3Wmaojn',
xPrivKey_44H_0H_0H: 'xprv9yVpGpTwwtc5aNLkACpuAWmyBUZy1hYy22WQ58Cx614fM7oHpYyjrWbYbExMtZa3sTTywU9mziYBoSZtR4rXVhvfvoVmqZPsYysvpYNUDrk',
xPubKey_44H_0H_0H: 'xpub6CVAgKzqnGANnrRDGEMuXeihjWQTRAGpPFRzsWcZeLbeDv8SN6HzQJv2SWxg2QSQ7GABwgZJb9KzK8yTXB44CRkqy3cMGEEzniWvBKRCGr5',
xPrivKey_1H: 'xprv9vGShhcT8rDDu2FvheGgrGvcg5JLQce7kXqC2VnhyYdnggErZA2KJjDwSvQhFZtBVH8T7QQd7JTDXVSvgEijvoYdvCeoRyjQL5EnHRUZgre',
xPubKey_1H: 'xpub69Fo7D9LyDmX7WLPofohDQsME78pp5My7kknptCKXtAmZUa16hLZrXYRJCL7wLWtTWzMvEn1HKQ8uzp4iLbkS72WPJsLTLayGPw7HGh6hEW',
privKey_1H_0: '5009c8488e9a364fc24a999d99a81ae955271de1d06d46c2f2f09e20c6281b04',
pubKey_1H_0: '03338a3b7c08e9d9832e1baff0758e08f9cc691497dd6e91d4c191cd960fb2f043'
}, {
id: 'ab0bc4e4c251376d733d9ba63242fb0ca258b0dff4def1f67e2a5e0441b86a8f',
xPrivKey: 'xprv9s21ZrQH143K24Vs7f7xhvrMgojXW5444PtT7Qkw3opZ2Dwpxwz5tERpSMtEa4YTgrfDhRqzw8X9b4TnCKrhcoZiPRgJDQaH9Lz8LEvLSpF',
xPubKey: 'xpub661MyMwAqRbcEYaLDgey54o6Eqa1uXmuRcp3uoAYc9MXu2GyWVJLS2kJHeTLPTohioPuLNadiYZDBRXzzeZLFTvZTgEvrSHDTExFuutcPX3',
xPrivKey_45H: 'xprv9tzvDbfzt6dy5aiWx2qovpw2sxLUHm5zM3ZBX3AH1oTEa7PjZoHWhfSZp5LnmMayDCHJEM4ATCDWECkQUjgxYjnEczMjyvujwzUAWXNMy8L',
xPubKey_45H: 'xpub67zGd7CtiUCGJ4nz44NpHxsmRzAxhDoqiGUnKRZta8zDSuit7LbmFTm3fMTBmgk4aiZyGMswwdEiTr3DPwhc6Gu7EEgaDyRk9G6VTTmMNTw',
xPrivKey_1H: 'xprv9tzvDbfzt6dw8EKieKg6hLykKuvTB4nKocQNp977xoQr6TCxDqqdDf5oK21i3FFjPG2jCtSxuGnJpBYnpmtHEZyRuxuG1RmLC3NVvVKzYEe',
xPubKey_1H: 'xpub67zGd7CtiUCELiQBkMD74UvUswkwaXWBAqKycXWjX8wpyFY6mP9smTQHAFFY8vUDcEnoUfbgUJsktwQ96igzGNfT22ujQFiSKBFAqzkwpcW',
privKey_1H_0: '5ff5b3cab4ae8f1487b75ee3b688d0cb54d11db3d21044ae6eaabd04a2a379c9',
pubKey_1H_0: '030da118db806d1758983ab078c8b5e5651270cba8a587e34337eb0b9b5e555b21'
id44: 'e225a29864060823df67b98432b070a40aad1bf9af517005b0b5fe09c96e29c9',
id45: 'c65a89f64794cb7e1886c7010a32dd6fa362d3e81710bac32e97e325b9109fd8',
xPrivKey: 'xprv9s21ZrQH143K48nfuK14gKJtML7eQzV2dAH1RaqAMj8v2zs79uaavA9UTWMxpBdgbMH2mhJLeKGq8AFA6GDnFyWP4rLmknqZAfgFFV718vo',
xPubKey: 'xpub661MyMwAqRbcGcs91LY53TFcuMx8pTCszPCcDyEmv4ftuoCFhStqTxTxJoy35yjp2H3qQtxDYGe1gtkZu4T7mR7ARK1MLYte2fptZVt6hkD',
xPrivKey_45H: 'xprv9vYQ7HC96LzvDwJ2r99m4ALTEtXTfUCHPbs7gpPzWboFP3NFgzMjXPf9Pp7D3vmTUqRtsZfSfVghvUhLKGGAs3LdVf9rf89TgRAvqAWMSwN',
xPubKey_45H: 'xpub69XkWnj2viZDSRNVxAgmRJHBnvMx4vv8kpniVCoc4wLEFqhQEXfz5BydF79LoErgaokMLv7u2sRNsoiJqjr5nyfBqJGykfR3kvCex7z8Ar3',
xPrivKey_44H_0H_0H: 'xprv9yn1LP3ViLTXUYgZP5sDFG94H6sd8v5AKRBbd35Ub5FjPq2HuzC9bAb6LReTXRSA4XTtYvzwpiktvuRrLY98df2KZMCuyZxXUB7FpBQ2ndA',
xPubKey_44H_0H_0H: 'xpub6CmMjtaPYi1ph2m2V7QDcQ5nq8i7YNo1ge7CRRV69QniGdMSTXWQ8xuaBiNt74nJCMZjaeNgRbMiZmdgrEqogvjARnBx8k4y853MZGAkjmo',
xPrivKey_1H: 'xprv9vYQ7HC96LztKQouECporZgezbjktKyhipWavsJvEJ4ok577RxVcmnyeRUUbAqmq5FnuhDie62Z962xNvF9JwAXoJZx2VeJPUWMPHsuoAkM',
xPubKey_1H: 'xpub69XkWnj2viZBXttNLEMpDhdPYdaFHnhZ63SBjFiXndbncsSFyVosKbJ8GjGMT7LX1rF6aisHM2YmFKye7bR3xQn5Bj875iGxyVrbRF7k5N8',
privKey_1H_0: '460ee692f05de66b5d8e2fa1d005a8b6bdb1442e2ce6b3facfcee2f9012c9474',
pubKey_1H_0: '03d0e0c526619b158aac9a8de8082f439df43d389ec50cb54386c3d87cfde4c99b'
}, {
id: '65fa0d2c2bd1d09da631e12c746562687d43364104e65bc7e0009c9cd6efb9f8',
xPrivKey: 'xprv9s21ZrQH143K44coj9AbkrT5B43PjRY91RnobCw8WGQVY7VvUDDA6VauoYs2PsKrW8MWQb9qUi2KgoWatAKf2qGZLBAT5wo98zxDsm4wog8',
xPubKey: 'xpub661MyMwAqRbcGYhGqAhc7zPoj5st8tFzNeiQPbLk4bwUQuq51kXQeHuPeqb4wxi8zkrzVThRKkBEGoT5H3M4kfFrdER1BfNCr3AV7rxSHB2',
xPrivKey_45H: 'xprv9uAoUcLPWHxJS36XQi8Mt3rywiVXK5MsY4bn6hLgyPVb8WDoqW81vvXrN7kwR9r52GTDEs6CZBXdAoEbqEbEsLxPVfCRRFZSzAnXFpaxnoy',
xPubKey_45H: 'xpub68A9t7sHLfWbeXAzWjfNFBoiVkL1iY5iuHXNu5kJXj2a1JYxP3SGUirLDPueKxxHmee12FBnjcQzspG2f8KkCqoV8vaUiVt75L4Bhsqffep',
xPrivKey_1H: 'xprv9uAoUcLPWHxGY9eCE8gXxRSTUkVpjfVWg6LQaopBQHvv8LbPCzzQHcu7mi6hWsV6EGnNqPX8xxJSwroeBiRj3TVfsGH2etA6ZNtDZqskL2h',
xPubKey_1H: 'xpub68A9t7sHLfWZkdifLADYKZPC2nLK98DN3KG1PCDnxdTu18vXkYJeqRDbd1MTj2mBjYPCix49qz2wsBWAKuPZ5crUMLemYPK7mZd5GpF4s8D',
privKey_1H_0: '259dddf1b0bcc4ab005002de457ce118771cb0ebfa8711f2b65bafec09ccfa22',
pubKey_1H_0: '02e3a4d6d1d2043e4643c1505a7dfd30c27d2b34863e7dbaacd50f896dfb16dd86'
id44: '120416cd4c427a7e4d94213cebe242f56a06bc6dd5c5c6cae27dc920a0ddf1fb',
id45: '65ae087eb9efdc7e0ada3a7ef954285e9e5ba4b8c7ab2d36747ddd286f7a334f',
xPrivKey: 'xprv9s21ZrQH143K44Bb9G3EVNmLfAUKjTBAA2YtKxF4zc8SLV1o15JBoddhGHE9PGLXePMbEsSjCCvTvP3fUv6yMXZrnHigBboRBn2DmNoJkJg',
xPubKey: 'xpub661MyMwAqRbcGYG4FHaErWi5DCJp8uu1XFUV8LegYwfRDHLwYccSMRxB7Z3L1NgKychKdXQvbVEyDhSwNnNnnNKh9mBEAdQ5tv2guK8ywKU',
xPrivKey_45H: 'xprv9vKBPQfZS4jWfLtSAoSeBHdLU1yJicKVmhTdbewKwQWU2CPdm61scXvKXHaMshmDEBaspUc994GAzZK1x7PtfzZ2PGtGHHhJhDn3yoT8gAi',
xPubKey_45H: 'xpub69JXnvCTGSHospxuGpyeYRa523oo853M8vPEQ3LwVk3StzinJdL8ALEoNa8AwwJsAWrfFtwfDgTCB9o1kKwn9n2tdN9nbdrw2zs5rNqQpM2',
xPrivKey_44H_0H_0H: 'xprv9xuBGYz7dmjgyroeDKMKyjSCiWyqskWavGjLPJbC9W32TkTy8NqXX4y5WQKcK24SnGiDMpUhFUNqjh284RjHoNYC2gUKagKfVfWZqn1gey4',
xPubKey_44H_0H_0H: 'xpub6BtXg4X1U9HzCLt7KLtLLsNwGYpLHDESHVewBgzohqa1LYo7fv9n4sHZMdzdBh5pxiAgLyUvsAqAQ5J1CRK4uA9htCWbKbVxFsfMEij3MDA',
xPrivKey_1H: 'xprv9vKBPQfZS4jUjK546Cjyf8ZgtEvRhQyjfuE89DAnYatevTusA8wwXiDxEFPSFk4fp8wwC5nRGmkSnTi9ascW9LJjujm2g36eGocNMtEsDwX',
xPubKey_1H: 'xpub69JXnvCTGSHmwo9XCEGz2GWRSGkv6shb389iwbaQ6vRdoGF1hgGC5WYS5YhxaM6RpfzsbPJqfR7iCLdEMSMUYvhaG4tvmEsn8ztwtysjgp8',
privKey_1H_0: '7a5158b92d9ed4cb9644ddbd472b43428832a5f3bb91a481532a081908e62b2e',
pubKey_1H_0: '02b47d5c977c93c883f369165ebc2b564d14a52712ec6892f7097fa99e0d36ca20'
}, {
id: '551984256819ededc0ee851f99546fc1856d20f883960c868917626091128d30',
xPrivKey: 'xprv9s21ZrQH143K27WsnsbtSync63dMn5ADYT7yf8ARMGtG5CyhAxhcNfjrx9yYUYTApZUfkLqvgYzeLwB5K56pfWkLxvy8wSK4Bog5o8XUKJ6',
xPubKey: 'xpub661MyMwAqRbcEbbLtu8tp7jLe5TrBXt4ug3aTWa2ucREx1JqiW1rvU4LoSwm4B1DoXhkGSTRSKpSUYQVENP9AYX3EHAP4oyeGtGBeKJ7hwd',
xPrivKey_45H: 'xprv9vdUpVGJA9iQf5GDq7eR34N75DjytYj5adyggnqdA9fCCfgofiQEELA7o4UiNQ2zf3xjHyeNDhQhib7RQjTeYnapWxu1eSQdCrExzb9J8v7',
xPubKey_45H: 'xpub69cqDzoBzXGhsZLgw9BRQCJqdFaUJ1SvwruHVBFEiVCB5U1xDFiUn8UbeKmXemb5ENTgJhoxYKe4yQazwsXFScehhMrc5vAXUVK1mQfyVST',
xPrivKey_1H: 'xprv9vdUpVGJA9iNgurZGgm6HhVHF7GQbcpWbezkQcCnwCdz5Tigip2AyEtg2Jz98e7Mthm6yEK72CzNagyMyCGQNuYhErB1Er1ykTpBKeTjsan',
xPubKey_1H: 'xpub69cqDzoBzXGfuPw2NiJ6eqS1o96u15YMxsvMCzcQVYAxxG3qGMLRX3D9sZxY3V2CLNX5rZZYB4QUg5zLb961c7CPD46fM9M4rVatc65qeF6',
privKey_1H_0: 'c8e4cd009e0ed12564eb96b2ffc68181716748ec9a7f6b218b08587337438bba',
pubKey_1H_0: '032137a03bc2cbba134fc3a1529358b56ca0764f699f6e3cc74118044f9bfad33b'
id44: '85de9f025ee190fab7cb1bd9b6772c64df26188ce705d4f258c5adaf7bc610f9',
id45: 'dacc5c350cef4449a3ca12939711c7449d0d6189e5e7f33cff60095a7a29b0f9',
xPrivKey: 'xprv9s21ZrQH143K48PpVxrh71KdViTFhAaiDSVtNFkmbWNYjwwwPbTrcqoVXsgBfue3Gq9b71hQeEbk67JgtTBcpYgKLF8pTwVnGz56f1BaCYt',
xPubKey: 'xpub661MyMwAqRbcGcUHbzPhU9GN3kHk6dJZafRVAeAP9quXckH5w8n7Ae7yP8e2Zh6SPPKFn2K6oE3GBpcz9QzfJTNRWXbY7w1L3nGLE5beZL1',
xPrivKey_45H: 'xprv9u8SxxhEJB4pzk6P3VKwmRRFqgHX94kBHDTBDaFXeaMS7dggkQcf2Tt14AvTvMJYjpogB2bVgmkHEW4Ze1iVzZeF5S5FLj6zpqwyrwh9yRW',
xPubKey_45H: 'xpub687oNUE88Yd8DEAr9Wrx8ZMzPi81YXU2eSNn1xf9CutQzS1qHwvuaGCUuTHJXkvzrRSiRtuJid3LsPaS3hUAdahdy3ZpQCcsLEQjC2Q2tNT',
xPrivKey_44H_0H_0H: 'xprv9yTxyEKAvyMLaocWZ4rXUUC7xWUqzhcvDJc2Ms7fndKaSfityJdaGrhLCSsLZz2E9NG3VkhbbsYwZVW9J5W3BQegyWWNsuEKmkdCsr9DRkM',
xPubKey_44H_0H_0H: 'xpub6CTKNjr4mLudoHgyf6PXqc8rWYKLQALmaXXdAFXHLxrZKU43Wqwppf1p3kj8sC9b9Sx5uqXfnATdvz5jeFYKELnngksCzwABLfNcW8CPkfg',
xPrivKey_1H: 'xprv9u8SxxhEJB4o6KfWV3csVTsJzhePBtMz8CmJCcn83vaWSiydgv6CADdbxjq3YsnusKPyPw1G235KibW67VjdbJ2ZoTG4nFA6ivwPRBmScKW',
xPubKey_1H: 'xpub687oNUE88Yd6Jojyb59srbp3YjUsbM5qVRgu11BjcG7VKXJnETQSi1x5p3onkhodzQcydFLTkZ3ym35UmAAWrjLY4rDAt2RjHKidx6AzgVz',
privKey_1H_0: '3c49816d4e83d8758f89e8e104e3566a8a61426a9b7d4945b34212fbbb8e8290',
pubKey_1H_0: '0307ab8c0d8eea1fe3c3781050a69e71f9e7c8cc8476a77103e08a461506a0e780'
}, {
id: '364783ba3fa7f82a188ad665ddee376a0abe930194b1dfc148199b54205b992b',
xPrivKey: 'xprv9s21ZrQH143K2scQXETcb8fMvaao2ApiBWsuYiLz1GLr3T5UWi8bmEvpVWvTGEYjcMEnHEET4RrouGhYo3qBX4yuLXJ9q6viSFykJcGmNXn',
xPubKey: 'xpub661MyMwAqRbcFMgsdFzcxGc6UcRHRdYZYjoWM6kbZbspvFQd4FSrK3FJLpwdhqsEL3PYLarn1Av24qDwxfizXkgaKmvLAdJ16MPao2o4MNq',
xPrivKey_45H: 'xprv9u2Yq2Sc1xugbPWe4LBefYGiRKbytz9vpbZgWdqk1fZKoLSeTFC5Qb6ospfKzbmNai2TCEX1uRtgfaXgnnwUFyGxEUtjwVq8s39kQcv8SxU',
xPubKey_45H: 'xpub681uEXyVrLTyosb7AMif2gDSyMSUJSsnBpVHK2FMa16Jg8mnznWKxPRHj5kWjpHXiJUutUZqAmy6EpomMGRP5im9U95jq85aMwyS7YcJVim',
xPrivKey_1H: 'xprv9u2Yq2Sc1xueeYe6GSDWWr7LUJTyjzcf1EjWPLEmT6T12je2PPk8D47h65RFC5i2Xqfj5og2FAJFRSHNaa2dwBtJi4AoBDSqL1NLhyXLyGf',
xPubKey_1H: 'xpub681uEXyVrLTws2iZNTkWsz452LJU9TLWNTf7BieP1RyyuXyAvw4NkrSAwMmgHcYNTYYwtdMQPLQKXL8JeEzHAfAhYVbA2xqjjkfCnoAd4tC',
privKey_1H_0: '97456bcde0704e054a63a96c11853ba412ab7c33e2ef9fa8661a66db2ce6a94d',
pubKey_1H_0: '022edcbe1542c789bb0c39e6fef3cd15823d396e355b05139659a059d08da62e50'
id44: '4d0c1eaab0aafc08aea7328f9ed1d3fc2812791ad2ebb9cbc1a8537b51b18afa',
id45: '9129a0454adcf659f4f9d65a9b4dc4f9793bd1f59664268b56a7ef73f29f1b8a',
xPrivKey: 'xprv9s21ZrQH143K3pgRcRBRnmcxNkNNLmJrpneMkEXY6o5TWBuJLMfdRpAWdb2cG3yxbL4DxfpUnQpjfQUmwPdVrRGoDJmtAf5u8cyqKCoDV97',
xPubKey: 'xpub661MyMwAqRbcGJktiSiS9uZgvnCrkE2iC1ZxYcw9f8cSNzESstysycUzUsDCU6KnnjR29VZ1eRAXDgEXfYxGw1B9E7VLSAcHa9UuifSozmy',
xPrivKey_45H: 'xprv9um7h3RnANyeDTTn7SCVckpA65rWTL7MExcta2iz8BaDqxNic6Gmjgv9GfYVs95x5tQJUpeJiuoApbnrwZQQZja5uUQxaySV7wMBV3P27qn',
xPubKey_45H: 'xpub68kU6YxfzkXwRwYFDTjVytkte7gzrnqCcBYVNR8bgX7Cikhs9db2HVEd7yFQVRV19xJ93DHh64fr5jDyWssXGuSt5AsiBurY3eaffcwjafa',
xPrivKey_44H_0H_0H: 'xprv9z9zXfC9effsNS9zXSD4XBUjuEqh6d5ofeF3xqSZPuq1DwAhTDZVuzpNwWfzTgpTm42s6DicHaG2eWesUQ8Cm8Es7kKyt1tCq6W9VM6ot4H',
xPubKey_44H_0H_0H: 'xpub6D9LwAj3V3EAavETdTk4tKRUTGgBW5of2sAemDrAxFMz6jVqzkskTo8rnq5DsoAnTyqnukWsPG7JueVLwLAN6vLXA4MpYceDPQbA6WuFfiV',
xPrivKey_1H: 'xprv9um7h3RnANycGJkyD9iAZy2BUkMpWr6SDV5WzgNYTRjAqX4WpYS7b8Xn8hGLiwmUhCvXnwhdhPEVejSpGVKFD1mxLDy7NSJ5C5oK4SELwJu',
xPubKey_1H: 'xpub68kU6YxfzkXuUnqSKBFAw6xv2nCJvJpHai17o4nA1mG9iKPfN5kN8vrFyzU2hYTBcVcDNoxcDbyqcauCxYJZpQLFzDnaHY5Uej3FCvYwU1P',
privKey_1H_0: '87f8a2b92dd04d2782c3d40a34f09f2ab42076bd02b81fbe4a4a72f87ad2e6df',
pubKey_1H_0: '02a0370d6f1213ab3390ac666585614ad71146f3f28ec326e2e779f999c1a497eb'
}, {
id: '23892f5b034019d81397f97570a1ea5b9f2977654269623f42abbdb33fe7c4e0',
xPrivKey: 'xprv9s21ZrQH143K4bhBXV3MvydmC9WKrJ4pY4w9zTyihwunrPsSim2vb5VDsX7zVzLPLXVTN6y2Gc5y7qpYr8Vdow3v3a14zgccPobib5Q5jik',
xPubKey: 'xpub661MyMwAqRbcH5medWaNJ7aVkBLpFknfuHrknrPLGHSmjCCbGJMB8sohipo1DSXpR7xcnZfsUooP6k8RVoQQ7Ewtmj7jnLbCQDGcBi9Sr3L',
xPrivKey_45H: 'xprv9twC9XfPNCFMmqBU3UyVLByyFH9QgmUuwy1gpteoAiLN4rSdG3hcLHjqXSqf36JXwavTFNhawGPYYtZUch6qJNoXfZ2XAeyeeWwrnnGdpD7',
xPubKey_45H: 'xpub67vYZ3CHCZoezKFw9WWVhKvhoJyu6ECmKBwHdH4Qj3sLwemmob1rt64KNkQVNrFhy85WAcokstPsA3JPz6qTHqumg98Lzo6ccL7wKCL3sDt',
xPrivKey_1H: 'xprv9twC9XfPNCFKozsRsSNChbiQJpRBkjVZoL4McXGyWQRD3dkkazNW7LhFP85DeM5DKw6AgfJPX7F2VggCznFqbsbUXUdwd7QEpTRYb8Agas3',
xPubKey_1H: 'xpub67vYZ3CHCZod2UwtyTuD4jf8rrFgACDRAYyxQugb4jxBvS5u8Xgkf91jESCqi5jydqLYeATXbk58bM6vfa3kviLHuyFKMVM1KSWB1uWRBdB',
privKey_1H_0: '5f0011757ae447218cc11b0845b87b7ac9742c8dc37de8d10d590fb096005430',
pubKey_1H_0: '027e8c73e3c176c7fe992de80fa891a5b73f82151f11b1f9e97ce7b902b20f81b6'
id44: '5ae7b75deb3b4d7e251f1fc5613904c9ef8548af7601d93ef668299be4f75ddd',
id45: '37b81e2544b43ce7f37a132a748426e1566ecbb758564d4d7d07b716fbe1b368',
xPrivKey: 'xprv9s21ZrQH143K3nvcmdjDDDZbDJHpfWZCUiunwraZdcamYcafHvUnZfV51fivH9FPyfo12NyKH5JDxGLsQePyWKtTiJx3pkEaiwxsMLkVapp',
xPubKey: 'xpub661MyMwAqRbcGH15sfGDaMWKmL8K4yH3qwqPkEzBBx7kRQuoqTo37ToYrvLJh7JpV5FQSverERMcdF4HcP1UCiie2ayeMXRq67zr75PzMKs',
xPrivKey_45H: 'xprv9twbreBMkWvwgk9kvzrdygVdir22YSi1dMwMd3GzdhjAD6seKqFidYpehhNR2JL3p41Yeun8ELYAs9pCvKqeH1mu2tQc4PZgWKAUJpaanKo',
xPubKey_45H: 'xpub67vxG9iFatVEuEEE32PeLpSNGsrWwuRrzarxRRgcC3G95uCnsNZyBM98Z145fPbqMgjVG4fPpKrsU7hbq33cpmPXAFF23VMCp7e9ucCGm2v',
xPrivKey_44H_0H_0H: 'xprv9yKWzK3qGKoWD7vkUpPhHQSUvyPrFBL2nBkZHDQPqjZfiR3ws6tL7LoGtrUqSye8cBeCFUzHZhG4i8RPqQuVQGVXgG6dGi2sor5SLh8bN59',
xPubKey_44H_0H_0H: 'xpub6CJsPpaj6hMoRc1DaqvheYPDV1ELee3t9QgA5bp1Q56ebDP6QeCaf97kk9SZPvxDkxt9RGmZBNdDyAyyHMfWDXYmJgAd1dutwgVaLQraVC4',
xPrivKey_1H: 'xprv9twbreBMkWvukJn57LL7aqa4vY9Zg8w8A19uUuDFqzuauaHoRmVTaETtfArRsvzdz41FY9pgLXJspuAF1nadXPTRSBgpNKoUwPM4eWTehSp',
xPubKey_1H: 'xpub67vxG9iFatVCxnrYDMs7wyWoUZz45beyXE5WHHcsQLSZnNcwyJoi82nNWSfrNKeqRnGpCuFGcsTuHi6zEBucxRJDgZbfyny2sUs88ZaMey1',
privKey_1H_0: '66230b6b8b65725162ea43313fcc233f4f0dd135cea00d04b73a84d3f681ef25',
pubKey_1H_0: '03f148bde0784c80051acd159b28a30022e685aca56418f8f50100d9f8a0192c37'
}, {
id: '5f69e118def0b38ee88c8d47c7254136d081221bfd2c6c3d9d4e7890bc60c22b',
xPrivKey: 'xprv9s21ZrQH143K3bFvCtYiQZr9seRAjj5sBH3NsvF2mut5NtuSL6CVWhYiKjsdDCXLgxwYB3GUsXqpSfmkjSEoghASHxvS4fNPwbkwk3cn1DM',
xPubKey: 'xpub661MyMwAqRbcG5LPJv5imhntRgFf9BoiYVxygJeeLFR4FhEasdWk4VsCB3EEw6meT2hGYTE2dN6x1597ekpFJp5wtqoNo25QFe8LBGZatsN',
xPrivKey_45H: 'xprv9ugBxZE4n42UY137XuYcWPEf1WrtN7Gyj7LEpDyQXwhPQ6iHtXe9LuBXyMyPgFABe9sagz3KKfTXGsJJnDycv3bZv2vZLibnLZyCX5ug5PT',
xPubKey_45H: 'xpub68fYN4kxcRamkV7adw5csXBPZYhNmZzq6LFqccP26HENGu3SS4xPthW1pdKoSrb4KVonrxpE1pPvi3CjzmB1X19xpmE2dSJwdBLw98Au1EA',
xPrivKey_1H: 'xprv9ugBxZE4n42Sb78sFa42f8K3mSCm5buKAXTz4Yo7oJvfRFtuy5AtedbsdGMtFULvesJ7KjJtuq8iUv3gxksYFohBHf8jEBihrx4ZkFqLivb',
xPubKey_1H: 'xpub68fYN4kxcRajobDLMbb32GFnKU3FV4dAXkParwCjMeTeJ4E4WcV9CRvMUX3t7dUu381jjAir4hB588mN4mFkWCVtoDSH8RhEPCzXJtvzHZG',
privKey_1H_0: '084f923954681d1bfc6622ff69fe225c89561f3f8976832acb436c6b0dc6dd01',
pubKey_1H_0: '029f7003e5e0a3c53db12439b4b7862c471d4e2fd56879497022cc420ce7a13fee'
id44: '98e78a9cb2ab340a245c5082897eadb28c367319f97b93e7b51b4d5ca5cdc68e',
id45: 'e1557d3421a8884fe007674f3f0b6f0feafa76289a0edcc5ec736161b4d02257',
xPrivKey: 'xprv9s21ZrQH143K2uYgqtYtphEQkFAgiWSqahFUWjgCdKykJagiNDz6Lf7xRVQdtZ7MvkhX9V3pEcK3xTAWZ6Y6ecJqrXnCpzrH9GSHn8wyrT5',
xPubKey: 'xpub661MyMwAqRbcFPd9wv5uBqB9JH1B7yAgwvB5K85pBfWjBP1rumJLtTSSGnCdsJSXfwmTyexsRjbUhzB4J6LWfL8mC2Ka117JrnXetyCzk3r',
xPrivKey_45H: 'xprv9vQyc56VRojih8sHwa4gitBJsva7TiRnMg3pBUxFR8NRGNxAbAr3vEb9iH8fxTZmmEnvzLee5DVUckDYJKfRnuxVz6Nm4Wpq1mbHE3K7WeK',
xPubKey_45H: 'xpub69QL1adPGBJ1ucwm3bbh6283RxQbsB9dityQysMryTuQ9BHK8iAJU2udZZNN2t3MNSGnzFiu97BUCjjMUAXqb4caURCMEStMorDU3y3NtgB',
xPrivKey_44H_0H_0H: 'xprv9yowiefD68DG79Xh3J6wAo8u5aTwZ32bsa5jtB4qgKS7r6Tszm42ovSgu7naNPCwT2wt42BXFR4sviY6PEpihhz8LRoHVj2v8WRydzEzqfd',
xPubKey_44H_0H_0H: 'xpub6CoJ8AC6vVmZKdcA9KdwXw5ddcJRxVkTEo1LgZUTEey6ito2YJNHMimAkPuaZ1NCZ1iYAadqQrCvYvrwDzb6wdJpjWWK4EBzmg6mUtVdJZ5',
xPrivKey_1H: 'xprv9vQyc56VRojgmQtXPaDpf5Y1zzmA3aH1SQUwbxqJDJEbjbZSHzpuXmabA2Lx5WThvw1srKUcNez2U4ovSh237mi6bNMufk5XrUcYbXLam2u',
xPubKey_1H: 'xpub69QL1adPGBHyytxzVbkq2DUkZ2beT2zrodQYQMEumdmacPtaqY9A5Zu51KMp7cEgWVsZCADGtqFiLoVzaarkMLXxiVYBSebSBSMZWYkNVr9',
privKey_1H_0: '9e215580c8e5876215ad101ded325bcacc5ab9d97b26e8fdfab89ef5bb6e0ab7',
pubKey_1H_0: '0265d33caaa128a77cc38ab8751c7d730e0274a212f1f65b73f637eddb3a3fb151'
}, {
id: 'ebd86096bf0f7902e293f9d399361204ba5a4bf484c4a7bf3e29f8891f96dca2',
xPrivKey: 'xprv9s21ZrQH143K2JQLtqcR7QgEXwhwhLWS63pAsEgmRqPu4x9d1oPcv14qTSbV19LWzFTfMbJ2nieS7oCrBxgSKF5hmqFeBCYwFh2kSV8pFRy',
xPubKey: 'xpub661MyMwAqRbcEnUozs9RUYcy5yYS6oEHTGjmfd6NzAvswkUmZLhsToPKJj4moN2BSSonQDTL9qFYtGsqoRAEwHcxrkwqBfkPBncm23QqrAg',
xPrivKey_45H: 'xprv9uj2TGqrHjzvdEQrYby5oGHPT96YBzSNUPHe82g9j6xqcJhx19KhWoPu6RxE4EisvuUTjBAwmsTnxYyT9EuSQJvyrWntQkiQ3QSjLx3XSRZ',
xPubKey_45H: 'xpub68iNrnNk87ZDqiVKedW6AQE81Aw2bTADqcDEvR5mHSVpV736Ygdx4biNwiSFYNFzTY6UziVFffDc92iL6WmANV16SexCX8WfGcDcRvaPJfh',
xPrivKey_1H: 'xprv9uj2TGqrHjztgbraNVLwq38Smz8Mi1MQE6yN3MXQSj4DaqkyfF1Td8cxpiH4Pd88X6K8qcveM7zpGxFwmi5hB8sQGn3Xnjesojznikr6n9e',
xPubKey_1H: 'xpub68iNrnNk87ZBu5w3UWsxCB5BL1xr7U5FbKtxqjw214bCTe68CnKiAvwSfzRQkT4hp27XRHRAewgYiV6uVKGgyoiFnKQaVpDWgiexFf5f4zD',
privKey_1H_0: '8c0552de8b785bf395794e88fbe6e0b87e74dbb49282e00acd98f44064913c41',
pubKey_1H_0: '034433ac6358faebed43ae5e48061b6796a7ec4523fad547e13d16433140557831'
id44: 'f716dbeec58e44c698b34c2d81bae4699ed5a5a522281733ec50aa03caf76a19',
id45: '8a6d840580549a34422c9b150dbd1e96e369c5db69ee736caab95616f8abb22b',
xPrivKey: 'xprv9s21ZrQH143K2wcRMP75tAEL5JnUx4xU2AbUBQzVVUDP7DHZJkjF3kaRE7tcnPLLLL9PGjYTWTJmCQPaQ4GGzgWEUFJ6snwJG9YnQHBFRNR',
xPubKey: 'xpub661MyMwAqRbcFRgtTQe6FJB4dLcyMXgKPPX4yoQ73okMz1chrJ3VbYtu5PRTxMBGuXt6eyqwAuG2BEBzQPLc1x8gnSQiATS3GRzKi1BuQAR',
xPrivKey_45H: 'xprv9vDicbkxQi4VK49bqmGuj4FSMSuzubjDVBqqwjBxJzUJ9JhQDtF45Nb1ex13D82uqkLyM6YhknAHZ2s4GjuYxQGySECGU82eGkrKi1Pct9v',
xPubKey_45H: 'xpub69D527HrF5cnXYE4wnov6CCAuUkVK4T4rQmSk7bZsL1H272YmRZJdAuVWEfsWKeSfVVTA4NTfyKt4ewZ6gtdPN1RuG5fRj45RYHT5LjvwsJ',
xPrivKey_44H_0H_0H: 'xprv9ynVNvxjepWN96d9wsT1jbWR6iu3YSZNSMeSWLgUqrnKKtZNquZvkd24qNc5wbSj3rvmW7WerhFutju6orHZfena88LFqzn4FUvSMJU928t',
xPubKey_44H_0H_0H: 'xpub6CmqnSVdVC4fMahd3tz26jT9ekjXwuHDoaa3Jj66QCKJCgtXPStBJRLYgfp5TcrH195VmbZUQGdYDFbYDpYKj7a7XsB34WmVDbCFSyAFty7',
xPrivKey_1H: 'xprv9vDicbkxQi4TNYTwjWnLG6kwMMEmndxZbcVRaHxoLtJQYyB5e5napwNeN7xkHhRMqrUEU1ARW6AQH4zsDzj3ra19J5Wjb4UMTxMeXWUAd37',
xPubKey_1H: 'xpub69D527HrF5ckb2YQqYKLdEhfuP5GC6gQxqR2NgNQuDqPRmWEBd6qNjh8DRJfYzQ4Y2WCw3NWR9Bn3YZkKa2yQiCG2xWKSMnMfg71pFEjCWd',
privKey_1H_0: '95951f0e40d31bafe54a3098bd0ed898d370cc5d52a9318d7b7b14568da6cb5c',
pubKey_1H_0: '0266cdb57b8a4d7c1b5b20ddeea43705420c6e3aef2c2979a3768b7b585839a0d3'
}, ];
var history = [{
@ -188,6 +211,5 @@ var history = [{
}];
module.exports.keyPair = keyPair;
module.exports.message = message;
module.exports.copayers = copayers;
module.exports.history = history;