wallet: refactor mappings to use maps and sets.
This commit is contained in:
parent
02e4dda012
commit
3f9d57f680
@ -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);
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
Reference in New Issue
Block a user