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

View File

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

View File

@ -93,7 +93,7 @@ CoinView.prototype.addTX = function addTX(tx, height) {
CoinView.prototype.removeTX = function removeTX(tx, height) {
let coins = Coins.fromTX(tx, height);
for (let coin of coins.outputs)
for (let coin of coins.outputs.values())
coin.spent = true;
return this.add(tx.hash('hex'), coins);
@ -173,12 +173,7 @@ CoinView.prototype.addOutput = function addOutput(prevout, output) {
*/
CoinView.prototype.spendOutput = function spendOutput(prevout) {
let coins = this.get(prevout.hash);
if (!coins)
return false;
return this.spendFrom(coins, prevout.index);
return this.spend(prevout.hash, prevout.index);
};
/**
@ -198,12 +193,17 @@ CoinView.prototype.removeOutput = function removeOutput(prevout) {
/**
* Spend a coin from coins object.
* @param {Coins} coins
* @param {Hash} hash
* @param {Number} index
* @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);
if (!coin)
@ -313,19 +313,20 @@ CoinView.prototype.isCoinbase = function isCoinbase(input) {
*/
CoinView.prototype.readCoin = async function readCoin(db, input) {
let coin = this.hasEntry(input);
let coin = this.getEntry(input);
let prevout = input.prevout;
if (!coin) {
coin = await db.readCoin(prevout);
if (coin)
return coin;
if (!coin)
return;
coin = await db.readCoin(prevout);
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}.
*/
CoinView.prototype.ensureInputs = async function ensureInputs(db, tx) {
CoinView.prototype.readInputs = async function readInputs(db, tx) {
let found = true;
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) {
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) {
let coins = await this.readCoin(db, input);
let coin = await this.readCoin(db, input);
if (!coins)
if (!coin || coin.spent)
return false;
if (!this.spendFrom(coins, input.prevout.index))
return false;
coin.spent = true;
this.undo.push(coin);
}
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.
* @returns {Number}

View File

@ -38,7 +38,7 @@ describe('Coins', function() {
view.addTX(tx1, 1);
coins = view.get(hash);
assert.equal(coins.outputs.length, tx1.outputs.length);
assert.equal(coins.outputs.size, tx1.outputs.length);
entry = coins.get(0);
assert(entry);
@ -103,7 +103,7 @@ describe('Coins', function() {
coins = res.get(prev.hash);
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)));
});
});