diff --git a/lib/wallet/records.js b/lib/wallet/records.js index 460ae1c7..4279d5b3 100644 --- a/lib/wallet/records.js +++ b/lib/wallet/records.js @@ -237,8 +237,7 @@ function BlockMapRecord(height) { return new BlockMapRecord(height); this.height = height != null ? height : -1; - this.txs = []; - this.index = new Map(); + this.txs = new Map(); } /** @@ -255,8 +254,7 @@ BlockMapRecord.prototype.fromRaw = function fromRaw(data) { for (let i = 0; i < count; i++) { let hash = br.readHash('hex'); let tx = TXMapRecord.fromReader(hash, br); - this.txs.push(tx); - this.index.set(tx.hash, tx); + this.txs.set(tx.hash, tx); } return this; @@ -283,7 +281,7 @@ BlockMapRecord.prototype.getSize = function getSize() { size += 4; - for (let tx of this.txs) { + for (let tx of this.txs.values()) { size += 32; size += tx.getSize(); } @@ -301,10 +299,10 @@ BlockMapRecord.prototype.toRaw = function toRaw() { let size = this.getSize(); let bw = new StaticWriter(size); - bw.writeU32(this.txs.length); + bw.writeU32(this.txs.size); - for (let tx of this.txs) { - bw.writeHash(tx.hash); + for (let [hash, tx] of this.txs) { + bw.writeHash(hash); tx.toWriter(bw); } @@ -319,14 +317,11 @@ BlockMapRecord.prototype.toRaw = function toRaw() { */ BlockMapRecord.prototype.add = function add(hash, wid) { - let tx = this.index.get(hash); + let tx = this.txs.get(hash); if (!tx) { tx = new TXMapRecord(hash); - tx.wids.push(wid); - this.txs.push(tx); - this.index.set(tx.hash, tx); - return true; + this.txs.set(tx.hash, tx); } return tx.add(wid); @@ -340,7 +335,7 @@ BlockMapRecord.prototype.add = function add(hash, wid) { */ BlockMapRecord.prototype.remove = function remove(hash, wid) { - let tx = this.index.get(hash); + let tx = this.txs.get(hash); if (!tx) return false; @@ -348,15 +343,26 @@ BlockMapRecord.prototype.remove = function remove(hash, wid) { if (!tx.remove(wid)) return false; - if (tx.wids.length === 0) { - let result = util.binaryRemove(this.txs, tx, cmpid); - assert(result); - this.index.delete(tx.hash); - } + if (tx.wids.size === 0) + this.txs.delete(tx.hash); return true; }; +/** + * Convert tx map to an array. + * @returns {Array} + */ + +BlockMapRecord.prototype.toArray = function toArray() { + let txs = []; + + for (let tx of this.txs.values()) + txs.push(tx); + + return txs; +}; + /** * TX Hash * @constructor @@ -364,18 +370,19 @@ BlockMapRecord.prototype.remove = function remove(hash, wid) { function TXMapRecord(hash, wids) { this.hash = hash || encoding.NULL_HASH; - this.wids = wids || []; - this.id = TXMapRecord.id++; + this.wids = wids || new Set(); } -TXMapRecord.id = 0; - TXMapRecord.prototype.add = function add(wid) { - return util.binaryInsert(this.wids, wid, cmp, true) !== -1; + if (this.wids.has(wid)) + return false; + + this.wids.add(wid); + return true; }; TXMapRecord.prototype.remove = function remove(wid) { - return util.binaryRemove(this.wids, wid, cmp); + return this.wids.delete(wid); }; TXMapRecord.prototype.toWriter = function toWriter(bw) { @@ -416,15 +423,19 @@ TXMapRecord.fromRaw = function fromRaw(hash, data) { function OutpointMapRecord(hash, index, wids) { this.hash = hash || encoding.NULL_HASH; this.index = index != null ? index : -1; - this.wids = wids || []; + this.wids = wids || new Set(); } OutpointMapRecord.prototype.add = function add(wid) { - return util.binaryInsert(this.wids, wid, cmp, true) !== -1; + if (this.wids.has(wid)) + return false; + + this.wids.add(wid); + return true; }; OutpointMapRecord.prototype.remove = function remove(wid) { - return util.binaryRemove(this.wids, wid, cmp); + return this.wids.delete(wid); }; OutpointMapRecord.prototype.toWriter = function toWriter(bw) { @@ -464,15 +475,19 @@ OutpointMapRecord.fromRaw = function fromRaw(hash, index, data) { function PathMapRecord(hash, wids) { this.hash = hash || encoding.NULL_HASH; - this.wids = wids || []; + this.wids = wids || new Set(); } PathMapRecord.prototype.add = function add(wid) { - return util.binaryInsert(this.wids, wid, cmp, true) !== -1; + if (this.wids.has(wid)) + return false; + + this.wids.add(wid); + return true; }; PathMapRecord.prototype.remove = function remove(wid) { - return util.binaryRemove(this.wids, wid, cmp); + return this.wids.delete(wid); }; PathMapRecord.prototype.toWriter = function toWriter(bw) { @@ -701,30 +716,22 @@ TXRecord.fromRaw = function fromRaw(data) { * Helpers */ -function cmp(a, b) { - return a - b; -} - -function cmpid(a, b) { - return a.id - b.id; -} - function parseWallets(br) { let count = br.readU32(); - let wids = []; + let wids = new Set(); for (let i = 0; i < count; i++) - wids.push(br.readU32()); + wids.add(br.readU32()); return wids; } function sizeWallets(wids) { - return 4 + wids.length * 4; + return 4 + wids.size * 4; } function serializeWallets(bw, wids) { - bw.writeU32(wids.length); + bw.writeU32(wids.size); for (let wid of wids) bw.writeU32(wid); diff --git a/lib/wallet/txdb.js b/lib/wallet/txdb.js index aef5f0bc..9ccd2f58 100644 --- a/lib/wallet/txdb.js +++ b/lib/wallet/txdb.js @@ -520,7 +520,7 @@ TXDB.prototype.removeOutpointMap = async function removeOutpointMap(hash, i) { if (!map.remove(this.wallet.wid)) return; - if (map.wids.length === 0) { + if (map.wids.size === 0) { this.walletdb.unwriteOutpointMap(this.wallet, hash, i); return; } @@ -563,7 +563,7 @@ TXDB.prototype.removeBlockMap = async function removeBlockMap(hash, height) { if (!block.remove(hash, this.wallet.wid)) return; - if (block.txs.length === 0) { + if (block.txs.size === 0) { this.walletdb.unwriteBlockMap(this.wallet, height); return; } @@ -795,6 +795,7 @@ TXDB.prototype.insert = async function insert(wtx, block) { let hash = wtx.hash; let height = block ? block.height : -1; let details = new Details(this, wtx, block); + let accounts = new Set(); let own = false; let updated = false; @@ -838,6 +839,7 @@ TXDB.prototype.insert = async function insert(wtx, block) { // Build the tx details object // as we go, for speed. details.setInput(i, path, coin); + accounts.add(path.account); // Write an undo coin for the credit // and add it to the stxo set. @@ -886,6 +888,7 @@ TXDB.prototype.insert = async function insert(wtx, block) { continue; details.setOutput(i, path); + accounts.add(path.account); // Attempt to resolve an input we // did not know was ours at the time. @@ -928,7 +931,7 @@ TXDB.prototype.insert = async function insert(wtx, block) { // Do some secondary indexing for account-based // queries. This saves us a lot of time for // queries later. - for (let account of details.accounts) { + for (let account of accounts) { this.put(layout.T(account, hash), null); this.put(layout.M(account, wtx.ps, hash), null); @@ -1013,6 +1016,7 @@ TXDB.prototype._confirm = async function confirm(wtx, block) { let hash = wtx.hash; let height = block.height; let details = new Details(this, wtx, block); + let accounts = new Set(); wtx.setBlock(block); @@ -1055,6 +1059,7 @@ TXDB.prototype._confirm = async function confirm(wtx, block) { assert(path); details.setInput(i, path, coin); + accounts.add(path.account); // We can now safely remove the credit // entirely, now that we know it's also @@ -1075,6 +1080,7 @@ TXDB.prototype._confirm = async function confirm(wtx, block) { continue; details.setOutput(i, path); + accounts.add(path.account); credit = await this.getCredit(hash, i); assert(credit); @@ -1107,7 +1113,7 @@ TXDB.prototype._confirm = async function confirm(wtx, block) { this.put(layout.h(height, hash), null); // Secondary indexing also needs to change. - for (let account of details.accounts) { + for (let account of accounts) { this.del(layout.P(account, hash)); this.put(layout.H(account, height, hash), null); } @@ -1157,6 +1163,7 @@ TXDB.prototype.erase = async function erase(wtx, block) { let hash = wtx.hash; let height = block ? block.height : -1; let details = new Details(this, wtx, block); + let accounts = new Set(); if (!tx.isCoinbase()) { // We need to undo every part of the @@ -1181,6 +1188,7 @@ TXDB.prototype.erase = async function erase(wtx, block) { assert(path); details.setInput(i, path, coin); + accounts.add(path.account); // Recalculate the balance, remove // from stxo set, remove the undo @@ -1207,6 +1215,7 @@ TXDB.prototype.erase = async function erase(wtx, block) { continue; details.setOutput(i, path); + accounts.add(path.account); credit = Credit.fromTX(tx, i, height); @@ -1233,7 +1242,7 @@ TXDB.prototype.erase = async function erase(wtx, block) { this.del(layout.h(height, hash)); // Remove all secondary indexing. - for (let account of details.accounts) { + for (let account of accounts) { this.del(layout.T(account, hash)); this.del(layout.M(account, wtx.ps, hash)); @@ -1354,6 +1363,7 @@ TXDB.prototype.disconnect = async function disconnect(wtx, block) { let hash = wtx.hash; let height = block.height; let details = new Details(this, wtx, block); + let accounts = new Set(); assert(block); @@ -1380,6 +1390,7 @@ TXDB.prototype.disconnect = async function disconnect(wtx, block) { assert(path); details.setInput(i, path, coin); + accounts.add(path.account); this.pending.confirmed += coin.value; @@ -1412,6 +1423,7 @@ TXDB.prototype.disconnect = async function disconnect(wtx, block) { await this.updateSpentCoin(tx, i, height); details.setOutput(i, path); + accounts.add(path.account); // Update coin height and confirmed // balance. Save once again. @@ -1434,7 +1446,7 @@ TXDB.prototype.disconnect = async function disconnect(wtx, block) { this.del(layout.h(height, hash)); // Secondary indexing also needs to change. - for (let account of details.accounts) { + for (let account of accounts) { this.put(layout.P(account, hash), null); this.del(layout.H(account, height, hash)); } @@ -2856,7 +2868,6 @@ function Details(txdb, wtx, block) { this.inputs = []; this.outputs = []; - this.accounts = []; this.init(); } @@ -2896,10 +2907,8 @@ Details.prototype.setInput = function setInput(i, path, coin) { member.address = coin.getAddress(); } - if (path) { + if (path) member.path = path; - util.binaryInsert(this.accounts, path.account, cmp, true); - } }; /** @@ -2911,10 +2920,8 @@ Details.prototype.setInput = function setInput(i, path, coin) { Details.prototype.setOutput = function setOutput(i, path) { let member = this.outputs[i]; - if (path) { + if (path) member.path = path; - util.binaryInsert(this.accounts, path.account, cmp, true); - } }; /** @@ -3220,14 +3227,6 @@ BlockRecord.fromMeta = function fromMeta(block) { return new BlockRecord().fromMeta(block); }; -/* - * Helpers - */ - -function cmp(a, b) { - return a - b; -} - /* * Expose */ diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index bd044012..aee1e5dd 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -1880,11 +1880,12 @@ WalletDB.prototype.revert = async function revert(target) { try { let height = layout.bb(item.key); let block = BlockMapRecord.fromRaw(height, item.value); + let txs = block.toArray(); - total += block.txs.length; + total += txs.length; - for (let i = block.txs.length - 1; i >= 0; i--) { - let tx = block.txs[i]; + for (let i = txs.length - 1; i >= 0; i--) { + let tx = txs[i]; await this._unconfirm(tx); } } catch (e) { @@ -1988,7 +1989,7 @@ WalletDB.prototype.removeBlock = async function removeBlock(entry) { WalletDB.prototype._removeBlock = async function removeBlock(entry) { let tip = BlockMeta.fromEntry(entry); - let prev, block; + let prev, block, txs; if (tip.height > this.state.height) { this.logger.warning( @@ -2013,8 +2014,10 @@ WalletDB.prototype._removeBlock = async function removeBlock(entry) { return 0; } - for (let i = block.txs.length - 1; i >= 0; i--) { - let tx = block.txs[i]; + txs = block.toArray(); + + for (let i = txs.length - 1; i >= 0; i--) { + let tx = txs[i]; await this._unconfirm(tx); } @@ -2022,9 +2025,9 @@ WalletDB.prototype._removeBlock = async function removeBlock(entry) { await this.syncState(prev); this.logger.warning('Disconnected wallet block %s (tx=%d).', - util.revHex(tip.hash), block.txs.length); + util.revHex(tip.hash), block.txs.size); - return block.txs.length; + return block.txs.size; }; /** @@ -2086,7 +2089,7 @@ WalletDB.prototype._insert = async function insert(tx, block) { this.logger.info( 'Incoming transaction for %d wallets in WalletDB (%s).', - wids.length, tx.txid()); + wids.size, tx.txid()); // If this is our first transaction // in a block, set the start block here.