txdb: spent state.

This commit is contained in:
Christopher Jeffrey 2016-10-17 17:51:39 -07:00
parent b1996b9717
commit 4232cdc6b9
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -136,6 +136,12 @@ var layout = {
ss: function ss(key) { ss: function ss(key) {
return layout.hii(key); return layout.hii(key);
}, },
S: function S(hash, index) {
return layout.hi(0x53, hash, index);
},
Ss: function Ss(key) {
return layout.hii(key);
},
p: function p(hash) { p: function p(hash) {
return layout.ha(0x70, hash); return layout.ha(0x70, hash);
}, },
@ -206,7 +212,7 @@ function TXDB(wallet) {
this.logger = wallet.db.logger; this.logger = wallet.db.logger;
this.network = wallet.db.network; this.network = wallet.db.network;
this.options = wallet.db.options; this.options = wallet.db.options;
this.coinCache = new LRU(10000); this.coinCache = new LRU.Nil(10000);
this.locked = {}; this.locked = {};
this.state = null; this.state = null;
@ -750,10 +756,16 @@ TXDB.prototype._add = co(function* add(tx, info) {
key = prevout.hash + prevout.index; key = prevout.hash + prevout.index;
spender = Outpoint.fromTX(tx, i).toRaw(); spender = Outpoint.fromTX(tx, i).toRaw();
this.put(layout.s(prevout.hash, prevout.index), spender); this.put(layout.s(prevout.hash, prevout.index), spender);
this.del(layout.c(prevout.hash, prevout.index)); if (tx.height === -1) {
this.del(layout.C(path.account, prevout.hash, prevout.index)); this.put(layout.S(prevout.hash, prevout.index), spender);
} else {
this.del(layout.c(prevout.hash, prevout.index));
this.del(layout.C(path.account, prevout.hash, prevout.index));
}
this.put(layout.d(hash, i), coin.toRaw()); this.put(layout.d(hash, i), coin.toRaw());
this.pending.sub(coin); this.pending.sub(coin);
@ -908,6 +920,23 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
return Outpoint.fromRaw(data); return Outpoint.fromRaw(data);
}); });
/**
* Test a whether a coin has been spent.
* @param {Hash} hash
* @param {Number} index
* @returns {Promise} - Returns Boolean.
*/
TXDB.prototype.isSpending = co(function* isSpending(hash, index) {
var key = layout.S(hash, index);
var data = yield this.get(key);
if (!data)
return;
return Outpoint.fromRaw(data);
});
/** /**
* Attempt to confirm a transaction. * Attempt to confirm a transaction.
* @private * @private
@ -921,7 +950,8 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
TXDB.prototype.confirm = co(function* confirm(tx, info) { TXDB.prototype.confirm = co(function* confirm(tx, info) {
var hash = tx.hash('hex'); var hash = tx.hash('hex');
var i, account, existing, output, coin; var i, account, existing, output, coin;
var address, key; var input, prevout, path, spender, coins;
var address, key, spent;
existing = yield this.getTX(hash); existing = yield this.getTX(hash);
@ -956,6 +986,41 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
this.put(layout.H(account, tx.height, hash), DUMMY); this.put(layout.H(account, tx.height, hash), DUMMY);
} }
// Consume unspent money or add orphans
if (!tx.isCoinbase()) {
coins = yield this.fillHistory(tx);
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
coin = coins[i];
if (!coin) {
coin = yield this.getCoin(prevout.hash, prevout.index);
if (!coin)
continue;
this.put(layout.d(hash, i), coin.toRaw());
this.pending.sub(coin);
}
// Only bother if this input is ours.
path = info.getPath(coin);
assert(path);
key = prevout.hash + prevout.index;
spender = Outpoint.fromTX(tx, i).toRaw();
this.del(layout.S(prevout.hash, prevout.index));
this.del(layout.c(prevout.hash, prevout.index));
this.del(layout.C(path.account, prevout.hash, prevout.index));
this.coinCache.remove(key);
}
}
for (i = 0; i < tx.outputs.length; i++) { for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i]; output = tx.outputs[i];
key = hash + i; key = hash + i;
@ -964,13 +1029,16 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
if (!info.hasPath(output)) if (!info.hasPath(output))
continue; continue;
coin = yield this.getCoin(hash, i); spent = yield this.isSpending(prevout.hash, prevout.index);
// Update spent coin. // Update spent coin.
if (!coin) { if (spent)
yield this.updateSpentCoin(tx, i); yield this.updateSpentCoin(spent, tx, i);
coin = yield this.getCoin(hash, i);
if (!coin)
continue; continue;
}
this.pending.confirm(coin.value); this.pending.confirm(coin.value);
@ -1113,10 +1181,13 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
coin = coin.toRaw(); coin = coin.toRaw();
this.put(layout.c(prevout.hash, prevout.index), coin); if (tx.height !== -1) {
this.put(layout.C(path.account, prevout.hash, prevout.index), DUMMY); this.put(layout.c(prevout.hash, prevout.index), coin);
this.put(layout.C(path.account, prevout.hash, prevout.index), DUMMY);
} else {
this.del(layout.S(prevout.hash, prevout.index));
}
this.del(layout.d(hash, i)); this.del(layout.d(hash, i));
this.del(layout.s(prevout.hash, prevout.index));
this.coinCache.set(key, coin); this.coinCache.set(key, coin);
} }
@ -1224,6 +1295,35 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
this.del(layout.H(account, height, hash)); this.del(layout.H(account, height, hash));
} }
var input, prevout, coin, path, spender;
// Consume unspent money or add orphans
if (!tx.isCoinbase()) {
var coins = yield this.fillHistory(tx);
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
coin = coins[i];
// Only bother if this input is ours.
if (!coin)
continue;
path = info.getPath(coin);
assert(path);
key = prevout.hash + prevout.index;
spender = Outpoint.fromTX(tx, i).toRaw();
this.put(layout.S(prevout.hash, prevout.index), spender);
this.put(layout.c(prevout.hash, prevout.index), coin.toRaw());
this.put(layout.C(path.account, prevout.hash, prevout.index), DUMMY);
this.coinCache.remove(key);
}
}
for (i = 0; i < tx.outputs.length; i++) { for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i]; output = tx.outputs[i];
key = hash + i; key = hash + i;
@ -1231,7 +1331,8 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
// Update spent coin. // Update spent coin.
if (!coin) { if (!coin) {
yield this.updateSpentCoin(tx, i); var spent = yield this.isSpent(prevout.hash, prevout.index);
yield this.updateSpentCoin(spent, tx, i);
continue; continue;
} }
@ -1727,15 +1828,17 @@ TXDB.prototype.getPending = co(function* getPending(account) {
* @returns {Promise} - Returns {@link Coin}[]. * @returns {Promise} - Returns {@link Coin}[].
*/ */
TXDB.prototype.getCoins = function getCoins(account) { TXDB.prototype.getCoins = co(function* getCoins(account) {
var self = this; var self = this;
var out = [];
var i, coins, coin;
// Slow case // Slow case
if (account != null) if (account != null)
return this.getAccountCoins(account); return this.getAccountCoins(account);
// Fast case // Fast case
return this.range({ coins = yield this.range({
gte: layout.c(constants.NULL_HASH, 0x00000000), gte: layout.c(constants.NULL_HASH, 0x00000000),
lte: layout.c(constants.HIGH_HASH, 0xffffffff), lte: layout.c(constants.HIGH_HASH, 0xffffffff),
parse: function(key, value) { parse: function(key, value) {
@ -1750,7 +1853,16 @@ TXDB.prototype.getCoins = function getCoins(account) {
return coin; return coin;
} }
}); });
};
for (i = 0; i < coins.length; i++) {
coin = coins[i];
if (yield this.isSpending(coin.hash, coin.index))
continue;
out.push(coin);
}
return out;
});
/** /**
* Get coins by account. * Get coins by account.
@ -1988,14 +2100,13 @@ TXDB.prototype.getSpentCoin = co(function* getSpentCoin(spent, prevout) {
* @returns {Promise} * @returns {Promise}
*/ */
TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, i) { TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(spent, tx, i) {
var prevout = Outpoint.fromTX(tx, i); var prevout, coin;
var spent = yield this.isSpent(prevout.hash, prevout.index);
var coin;
if (!spent) if (!spent)
return; return;
prevout = Outpoint.fromTX(tx, i);
coin = yield this.getSpentCoin(spent, prevout); coin = yield this.getSpentCoin(spent, prevout);
if (!coin) if (!coin)