wallet: refactor mappings to use maps and sets.

This commit is contained in:
Christopher Jeffrey 2017-07-25 13:39:38 -07:00
parent 02e4dda012
commit 3f9d57f680
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
3 changed files with 82 additions and 73 deletions

View File

@ -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);

View File

@ -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
*/

View File

@ -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.