FIX: HD wallets not can handle small gaps in hierarchy (closes #388)

This commit is contained in:
Overtorment 2019-03-30 21:59:37 +00:00
parent 36e5aaa275
commit d520da5cff
2 changed files with 25 additions and 12 deletions

View file

@ -65,14 +65,11 @@ it('can create a Segwit HD (BIP49)', async function() {
assert.strictEqual(hd._getInternalAddressByIndex(hd.next_free_change_address_index), freeChangeAddress);
});
it.skip('HD (BIP49) can work with a gap', async function() {
it('HD (BIP49) can work with a gap', async function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 240 * 1000;
let hd = new HDSegwitP2SHWallet();
hd._xpub = 'ypub6XRzrn3HB1tjhhvrHbk1vnXCecZEdXohGzCk3GXwwbDoJ3VBzZ34jNGWbC6WrS7idXrYjjXEzcPDX5VqnHEnuNf5VAXgLfSaytMkJ2rwVqy'; // has gap
await hd.fetchBalance();
console.log(hd.getBalance());
console.log('hd.next_free_address_index = ', hd.next_free_address_index);
console.log('hd.next_free_change_address_index = ', hd.next_free_change_address_index);
// for (let c = 0; c < 5; c++) {
// console.log('internal', c, hd._getInternalAddressByIndex(c));
@ -84,7 +81,6 @@ it.skip('HD (BIP49) can work with a gap', async function() {
await hd.fetchTransactions();
console.log('hd.transactions.length=', hd.transactions.length);
assert.ok(hd.transactions.length >= 3);
assert.ok(hd.next_free_address_index >= 4);
});
it.skip('Segwit HD (BIP49) can batch fetch many txs', async function() {

View file

@ -19,6 +19,7 @@ export class AbstractHDWallet extends LegacyWallet {
this._xpub = ''; // cache
this.usedAddresses = [];
this._address_to_wif_cache = {};
this.gap_limit = 3;
}
generate() {
@ -347,8 +348,6 @@ export class AbstractHDWallet extends LegacyWallet {
async fetchBalance() {
try {
// doing binary search for last used externa address
let that = this;
// refactor me
@ -371,8 +370,6 @@ export class AbstractHDWallet extends LegacyWallet {
return binarySearchIterationForInternalAddress(index, maxUsedIndex, minUnusedIndex, depth + 1);
}
this.next_free_change_address_index = await binarySearchIterationForInternalAddress(100);
// refactor me
// eslint-disable-next-line
async function binarySearchIterationForExternalAddress(index, maxUsedIndex = 0, minUnusedIndex = 100500100, depth = 0) {
@ -393,14 +390,34 @@ export class AbstractHDWallet extends LegacyWallet {
return binarySearchIterationForExternalAddress(index, maxUsedIndex, minUnusedIndex, depth + 1);
}
this.next_free_address_index = await binarySearchIterationForExternalAddress(100);
if (this.next_free_change_address_index === 0 && this.next_free_address_index === 0) {
// assuming that this is freshly imported/created wallet, with no internal variables set
// wild guess - its completely empty wallet:
let completelyEmptyWallet = false;
let txs = await BlueElectrum.getTransactionsByAddress(that._getInternalAddressByIndex(0));
if (txs.length === 0) {
let txs2 = await BlueElectrum.getTransactionsByAddress(that._getExternalAddressByIndex(0));
if (txs2.length === 0) {
// yep, completely empty wallet
completelyEmptyWallet = true;
}
}
// wrong guess. will have to rescan
if (!completelyEmptyWallet) {
// so doing binary search for last used address:
this.next_free_change_address_index = await binarySearchIterationForInternalAddress(100);
this.next_free_address_index = await binarySearchIterationForExternalAddress(100);
}
}
this.usedAddresses = [];
// generating all involved addresses:
for (let c = 0; c < this.next_free_address_index; c++) {
for (let c = 0; c < this.next_free_address_index + this.gap_limit; c++) {
this.usedAddresses.push(this._getExternalAddressByIndex(c));
}
for (let c = 0; c < this.next_free_change_address_index; c++) {
for (let c = 0; c < this.next_free_change_address_index + this.gap_limit; c++) {
this.usedAddresses.push(this._getInternalAddressByIndex(c));
}