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...');
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user