coins: refactor.
This commit is contained in:
parent
a1af3ab980
commit
345f0c90ac
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -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)));
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user