Skip to content

Commit

Permalink
Merge 2fd89c6 into a1409dc
Browse files Browse the repository at this point in the history
  • Loading branch information
pinheadmz committed Jan 4, 2021
2 parents a1409dc + 2fd89c6 commit 3c40b2c
Show file tree
Hide file tree
Showing 4 changed files with 377 additions and 0 deletions.
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## unreleased


### Node changes

- `FullNode` now parses option `--min-weight=<number>` (`min-weight: <number>` in
Expand All @@ -11,6 +12,17 @@ the miner will add transactions up to the minimum weight that would normally be
ignored for being "free" (paying a fee below policy limit). The default value is
raised from `0` to `5000` (a 1-in, 2-out BID transaction has a weight of about `889`).

### Wallet API changes

- Adds new wallet HTTP endpoint `/deepclean` that requires a parameter
`I_HAVE_BACKED_UP_MY_WALLET=true`. This action wipes out balance and transaction
history in the wallet DB but retains key hashes and name maps. It should be used
only if the wallet state has been corrupted by issues like the
[reserved name registration bug](https://github.com/handshake-org/hsd/issues/454)
or the
[locked coins balance after FINALIZE bug](https://github.com/handshake-org/hsd/pull/464).
After the corrupt data has been cleared, **a walletDB rescan is required**.

### Wallet changes

- Fixes a bug that ignored the effect of sending or receiving a FINALIZE on a
Expand Down
20 changes: 20 additions & 0 deletions lib/wallet/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,26 @@ class HTTP extends Server {
await this.wdb.rescan(height);
});

// Deep Clean
this.post('/deepclean', async (req, res) => {
if (!req.admin) {
res.json(403);
return;
}

const valid = Validator.fromRequest(req);
const disclaimer = valid.bool('I_HAVE_BACKED_UP_MY_WALLET', false);

enforce(
disclaimer,
'Deep Clean requires I_HAVE_BACKED_UP_MY_WALLET=true'
);

res.json(200, { success: true });

await this.wdb.deepClean();
});

// Resend
this.post('/resend', async (req, res) => {
if (!req.admin) {
Expand Down
87 changes: 87 additions & 0 deletions lib/wallet/walletdb.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,93 @@ class WalletDB extends EventEmitter {
}
}

/**
* Deep Clean:
* Keep all keys, account data, wallet maps (name and path).
* Dump all TX history and balance state.
* A rescan will be required but is not initiated automatically.
* @returns {Promise}
*/

async deepClean() {
const unlock1 = await this.txLock.lock();
const unlock2 = await this.writeLock.lock();
const unlock3 = await this.readLock.lock();
try {
return await this._deepClean();
} finally {
unlock3();
unlock2();
unlock1();
}
}

/**
* Deep Clean (without locks):
* Keep all keys, account data, wallet maps (name and path).
* Dump all TX history and balance state.
* A rescan will be required but is not initiated automatically.
* @returns {Promise}
*/

async _deepClean() {
this.logger.warning('Initiating Deep Clean...');

const b = this.db.batch();
const removeRange = (opt) => {
return this.db.iterator(opt).each(key => b.del(key));
};

this.logger.warning('Clearing block map, tx map and outpoint map...');
// b[height] -> block->wid map
await removeRange({
gte: layout.b.min(),
lte: layout.b.max()
});

// o[hash][index] -> outpoint->wid map
await removeRange({
gte: layout.o.min(),
lte: layout.o.max()
});

// T[hash] -> tx->wid map
await removeRange({
gte: layout.T.min(),
lte: layout.T.max()
});

const wids = await this.db.keys({
gte: layout.W.min(),
lte: layout.W.max(),
parse: key => layout.W.decode(key)[0]
});

for (const wid of wids) {
const wallet = await this.get(wid);
this.logger.warning(
'Clearing all tx history for wallet: %s (%d)',
wallet.id, wid
);

// remove all txdb data *except* blinds ('v')
const key = 'v'.charCodeAt();
const prefix = layout.t.encode(wid);
await removeRange({
gte: Buffer.concat([prefix, Buffer.alloc(1)]),
lt: Buffer.concat([prefix, Buffer.from([key])])
});
await removeRange({
gt: Buffer.concat([prefix, Buffer.from([key + 1])]),
lte: Buffer.concat([prefix, Buffer.from([0xff])])
});
}

await b.write();

this.logger.warning('Deep Clean complete. A rescan is now required.');
}

/**
* Force a rescan.
* @param {Number} height
Expand Down
Loading

0 comments on commit 3c40b2c

Please sign in to comment.