coins: refactor.

This commit is contained in:
Christopher Jeffrey 2017-07-02 18:47:44 -07:00
parent a1af3ab980
commit 345f0c90ac
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 77 additions and 98 deletions

View File

@ -84,7 +84,7 @@ ChainDB.prototype.open = async function open() {
this.logger.info('Opening ChainDB...'); this.logger.info('Opening ChainDB...');
await this.db.open(); await this.db.open();
await this.db.checkVersion('V', 3); await this.db.checkVersion('V', 4);
state = await this.getState(); state = await this.getState();
@ -1685,23 +1685,19 @@ ChainDB.prototype.removeBlock = async function removeBlock(entry) {
ChainDB.prototype.saveView = function saveView(view) { ChainDB.prototype.saveView = function saveView(view) {
for (let [hash, coins] of view.map) { for (let [hash, coins] of view.map) {
for (let i = 0; i < coins.outputs.length; i++) { for (let [index, coin] of coins.outputs) {
let coin = coins.outputs[i];
let raw; let raw;
if (!coin)
continue;
if (coin.spent) { if (coin.spent) {
this.del(layout.c(hash, i)); this.del(layout.c(hash, index));
this.coinCache.unpush(hash + i); this.coinCache.unpush(hash + index);
continue; continue;
} }
raw = coin.toRaw(); raw = coin.toRaw();
this.put(layout.c(hash, i), raw); this.put(layout.c(hash, index), raw);
this.coinCache.push(hash + i, raw); this.coinCache.push(hash + index, raw);
} }
} }
}; };

View File

@ -21,7 +21,7 @@ function Coins(options) {
if (!(this instanceof Coins)) if (!(this instanceof Coins))
return new Coins(options); return new Coins(options);
this.outputs = []; this.outputs = new Map();
} }
/** /**
@ -33,12 +33,9 @@ function Coins(options) {
Coins.prototype.add = function add(index, coin) { Coins.prototype.add = function add(index, coin) {
assert(index >= 0); assert(index >= 0);
while (this.outputs.length <= index) assert(!this.outputs.has(index));
this.outputs.push(null);
assert(!this.outputs[index]); this.outputs.set(index, coin);
this.outputs[index] = coin;
}; };
/** /**
@ -69,10 +66,7 @@ Coins.prototype.addCoin = function addCoin(coin) {
*/ */
Coins.prototype.has = function has(index) { Coins.prototype.has = function has(index) {
if (index >= this.outputs.length) return this.outputs.has(index);
return false;
return this.outputs[index] != null;
}; };
/** /**
@ -82,12 +76,7 @@ Coins.prototype.has = function has(index) {
*/ */
Coins.prototype.isUnspent = function isUnspent(index) { Coins.prototype.isUnspent = function isUnspent(index) {
let coin; let coin = this.outputs.get(index);
if (index >= this.outputs.length)
return false;
coin = this.outputs[index];
if (!coin || coin.spent) if (!coin || coin.spent)
return false; return false;
@ -102,10 +91,7 @@ Coins.prototype.isUnspent = function isUnspent(index) {
*/ */
Coins.prototype.get = function get(index) { Coins.prototype.get = function get(index) {
if (index >= this.outputs.length) return this.outputs.get(index);
return;
return this.outputs[index];
}; };
/** /**
@ -115,10 +101,12 @@ Coins.prototype.get = function get(index) {
*/ */
Coins.prototype.getOutput = function getOutput(index) { Coins.prototype.getOutput = function getOutput(index) {
if (index >= this.outputs.length) let coin = this.outputs.get(index);
if (!coin)
return; return;
return this.outputs[index].output; return coin.output;
}; };
/** /**
@ -128,10 +116,12 @@ Coins.prototype.getOutput = function getOutput(index) {
*/ */
Coins.prototype.getCoin = function getCoin(prevout) { Coins.prototype.getCoin = function getCoin(prevout) {
if (prevout.index >= this.outputs.length) let coin = this.outputs.get(prevout.index);
if (!coin)
return; return;
return this.outputs[prevout.index].toCoin(prevout); return coin.toCoin(prevout);
}; };
/** /**
@ -163,8 +153,7 @@ Coins.prototype.remove = function remove(index) {
if (!coin) if (!coin)
return false; return false;
this.outputs[index] = null; this.outputs.delete(index);
this.cleanup();
return coin; return coin;
}; };
@ -175,25 +164,16 @@ Coins.prototype.remove = function remove(index) {
*/ */
Coins.prototype.length = function length() { Coins.prototype.length = function length() {
let len = this.outputs.length; let len = -1;
while (len > 0 && !this.isUnspent(len - 1)) for (let [index, coin] of this.outputs) {
len--; if (!coin.spent) {
if (index > len)
len = index;
}
}
return len; return len + 1;
};
/**
* Cleanup spent outputs (remove pruned).
*/
Coins.prototype.cleanup = function cleanup() {
let len = this.outputs.length;
while (len > 0 && !this.outputs[len - 1])
len--;
this.outputs.length = len;
}; };
/** /**
@ -202,7 +182,7 @@ Coins.prototype.cleanup = function cleanup() {
*/ */
Coins.prototype.isEmpty = function isEmpty() { Coins.prototype.isEmpty = function isEmpty() {
return this.length() === 0; return this.outputs.size === 0;
}; };
/** /**
@ -218,16 +198,12 @@ Coins.prototype.fromTX = function fromTX(tx, height) {
for (let i = 0; i < tx.outputs.length; i++) { for (let i = 0; i < tx.outputs.length; i++) {
let output = tx.outputs[i]; let output = tx.outputs[i];
if (output.script.isUnspendable()) { if (output.script.isUnspendable())
this.outputs.push(null);
continue; continue;
}
this.outputs.push(CoinEntry.fromTX(tx, i, height)); this.outputs.set(i, CoinEntry.fromTX(tx, i, height));
} }
this.cleanup();
return this; return this;
}; };

View File

@ -93,7 +93,7 @@ CoinView.prototype.addTX = function addTX(tx, height) {
CoinView.prototype.removeTX = function removeTX(tx, height) { CoinView.prototype.removeTX = function removeTX(tx, height) {
let coins = Coins.fromTX(tx, height); let coins = Coins.fromTX(tx, height);
for (let coin of coins.outputs) for (let coin of coins.outputs.values())
coin.spent = true; coin.spent = true;
return this.add(tx.hash('hex'), coins); return this.add(tx.hash('hex'), coins);
@ -173,12 +173,7 @@ CoinView.prototype.addOutput = function addOutput(prevout, output) {
*/ */
CoinView.prototype.spendOutput = function spendOutput(prevout) { CoinView.prototype.spendOutput = function spendOutput(prevout) {
let coins = this.get(prevout.hash); return this.spend(prevout.hash, prevout.index);
if (!coins)
return false;
return this.spendFrom(coins, prevout.index);
}; };
/** /**
@ -198,12 +193,17 @@ CoinView.prototype.removeOutput = function removeOutput(prevout) {
/** /**
* Spend a coin from coins object. * Spend a coin from coins object.
* @param {Coins} coins * @param {Hash} hash
* @param {Number} index * @param {Number} index
* @returns {Boolean} * @returns {Boolean}
*/ */
CoinView.prototype.spendFrom = function spendFrom(coins, index) { CoinView.prototype.spend = function spend(hash, index) {
let coins = this.get(hash);
if (!coins)
return false;
let coin = coins.spend(index); let coin = coins.spend(index);
if (!coin) if (!coin)
@ -313,19 +313,20 @@ CoinView.prototype.isCoinbase = function isCoinbase(input) {
*/ */
CoinView.prototype.readCoin = async function readCoin(db, input) { CoinView.prototype.readCoin = async function readCoin(db, input) {
let coin = this.hasEntry(input); let coin = this.getEntry(input);
let prevout = input.prevout; let prevout = input.prevout;
if (!coin) { if (coin)
coin = await db.readCoin(prevout); return coin;
if (!coin) coin = await db.readCoin(prevout);
return;
return this.addEntry(prevout, coin); if (!coin)
} return null;
return this.get(prevout.hash); this.addEntry(prevout, coin);
return coin;
}; };
/** /**
@ -336,7 +337,7 @@ CoinView.prototype.readCoin = async function readCoin(db, input) {
* @returns {Promise} - Returns {Boolean}. * @returns {Promise} - Returns {Boolean}.
*/ */
CoinView.prototype.ensureInputs = async function ensureInputs(db, tx) { CoinView.prototype.readInputs = async function readInputs(db, tx) {
let found = true; let found = true;
for (let input of tx.inputs) { for (let input of tx.inputs) {
@ -356,33 +357,39 @@ CoinView.prototype.ensureInputs = async function ensureInputs(db, tx) {
*/ */
CoinView.prototype.spendInputs = async function spendInputs(db, tx) { CoinView.prototype.spendInputs = async function spendInputs(db, tx) {
if (tx.inputs.length < 4) {
let jobs = [];
let coins;
for (let input of tx.inputs)
jobs.push(this.readCoin(db, input));
coins = await Promise.all(jobs);
for (let coin of coins) {
if (!coin || coin.spent)
return false;
coin.spent = true;
this.undo.push(coin);
}
return true;
}
for (let input of tx.inputs) { for (let input of tx.inputs) {
let coins = await this.readCoin(db, input); let coin = await this.readCoin(db, input);
if (!coins) if (!coin || coin.spent)
return false; return false;
if (!this.spendFrom(coins, input.prevout.index)) coin.spent = true;
return false; this.undo.push(coin);
} }
return true; return true;
}; };
/**
* Convert collection to an array.
* @returns {Coins[]}
*/
CoinView.prototype.toArray = function toArray() {
let out = [];
for (let coins of this.map.values())
out.push(coins);
return out;
};
/** /**
* Calculate serialization size. * Calculate serialization size.
* @returns {Number} * @returns {Number}

View File

@ -38,7 +38,7 @@ describe('Coins', function() {
view.addTX(tx1, 1); view.addTX(tx1, 1);
coins = view.get(hash); coins = view.get(hash);
assert.equal(coins.outputs.length, tx1.outputs.length); assert.equal(coins.outputs.size, tx1.outputs.length);
entry = coins.get(0); entry = coins.get(0);
assert(entry); assert(entry);
@ -103,7 +103,7 @@ describe('Coins', function() {
coins = res.get(prev.hash); coins = res.get(prev.hash);
assert.strictEqual(coins.length(), 2); assert.strictEqual(coins.length(), 2);
assert.strictEqual(coins.get(0), null); assert.strictEqual(coins.get(0), undefined);
deepCoinsEqual(coins.get(1), reserialize(coins.get(1))); deepCoinsEqual(coins.get(1), reserialize(coins.get(1)));
}); });
}); });