diff --git a/lib/wallet/layout-browser.js b/lib/wallet/layout-browser.js index b932df39..f92d6128 100644 --- a/lib/wallet/layout-browser.js +++ b/lib/wallet/layout-browser.js @@ -88,7 +88,9 @@ layouts.walletdb = { }, Tt: function Tt(key) { return [key.slice(1, 65)]; - } + }, + t: 't', + u: 'u' }; layouts.txdb = { diff --git a/lib/wallet/layout.js b/lib/wallet/layout.js index 434712c5..8ad047e2 100644 --- a/lib/wallet/layout.js +++ b/lib/wallet/layout.js @@ -176,7 +176,9 @@ layouts.walletdb = { assert(Buffer.isBuffer(key)); assert(key.length === 33); return key.toString('hex', 1, 33); - } + }, + t: Buffer.from([0x74]), + u: Buffer.from([0x75]) }; /* @@ -185,7 +187,6 @@ layouts.walletdb = { * c[hash][index] -> coin * d[hash][index] -> undo coin * s[hash][index] -> spent by hash - * o[hash][index] -> orphan inputs * p[hash] -> dummy (pending flag) * m[time][hash] -> dummy (tx by time) * h[height][hash] -> dummy (tx by height) diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index dae2ac1b..022a3303 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -804,6 +804,136 @@ class WalletDB extends EventEmitter { this.saveAccount(b, account); } + /** + * Remove a wallet. + * @param {WalletID} wid + * @returns {Promise} + */ + + async remove(id) { + const wid = await this.getWID(id); + + if (!wid) + return false; + + const unlock1 = await this.readLock.lock(wid); + const unlock2 = await this.txLock.lock(); + + try { + return await this._remove(wid); + } finally { + unlock2(); + unlock1(); + } + } + + /** + * Remove a wallet (without a lock). + * @private + * @param {WalletID} wid + * @returns {Promise} + */ + + async _remove(wid) { + const data = await this.db.get(layout.w(wid)); + + if (!data) + return false; + + const {id} = Wallet.fromRaw(this, data); + + const b = this.db.batch(); + + b.del(layout.w(wid)); + b.del(layout.l(id)); + + const pathIter = this.db.iterator({ + gte: layout.P(wid, encoding.NULL_HASH), + lte: layout.P(wid, encoding.HIGH_HASH), + keys: true + }); + + await pathIter.each(async (key, value) => { + const hash = layout.Pp(key); + b.del(key); + return this.removePathMap(b, hash, wid); + }); + + const removeRange = async (opt) => { + return this.db.iterator(opt).each(key => b.del(key)); + }; + + await removeRange({ + gte: layout.r(wid, 0x00000000, encoding.NULL_HASH), + lte: layout.r(wid, 0xffffffff, encoding.HIGH_HASH) + }); + + await removeRange({ + gte: layout.a(wid, 0x00000000), + lte: layout.a(wid, 0xffffffff) + }); + + await removeRange({ + gte: layout.i(wid, '\x00'), + lte: layout.i(wid, '\xff') + }); + + await removeRange({ + gte: layout.n(wid, 0x00000000), + lte: layout.n(wid, 0xffffffff) + }); + + await removeRange({ + gte: layout.t, + lt: layout.u + }); + + const tlayout = layouts.txdb; + const prefix = tlayout.prefix(wid); + const bucket = this.db.bucket(prefix); + + const biter = bucket.iterator({ + gte: tlayout.b(0x00000000), + lte: tlayout.b(0xffffffff), + keys: true + }); + + await biter.each(async (key, value) => { + const height = layout.bb(key); + return this.removeBlockMap(b, height, wid); + }); + + const siter = bucket.iterator({ + gte: tlayout.s(encoding.NULL_HASH, 0x00000000), + lte: tlayout.s(encoding.HIGH_HASH, 0xffffffff), + keys: true + }); + + await siter.each(async (key, value) => { + const [hash, index] = layout.ss(key); + return this.removeOutpointMap(b, hash, index, wid); + }); + + const piter = bucket.iterator({ + gte: tlayout.p(encoding.NULL_HASH), + lte: tlayout.p(encoding.HIGH_HASH), + keys: true + }); + + await piter.each(async (key, value) => { + const hash = layout.pp(key); + return this.removeTXMap(b, hash, wid); + }); + + const w = await this._get(wid); + await w.destroy(); + this.unregister(w); + + await b.write(); + + return true; + } + /** * Get a wallet with token auth first. * @param {WalletID} wid