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) {
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) {
return layout.ha(0x70, hash);
},
@ -206,7 +212,7 @@ function TXDB(wallet) {
this.logger = wallet.db.logger;
this.network = wallet.db.network;
this.options = wallet.db.options;
this.coinCache = new LRU(10000);
this.coinCache = new LRU.Nil(10000);
this.locked = {};
this.state = null;
@ -750,10 +756,16 @@ TXDB.prototype._add = co(function* add(tx, info) {
key = prevout.hash + prevout.index;
spender = Outpoint.fromTX(tx, i).toRaw();
this.put(layout.s(prevout.hash, prevout.index), spender);
this.del(layout.c(prevout.hash, prevout.index));
this.del(layout.C(path.account, prevout.hash, prevout.index));
if (tx.height === -1) {
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.pending.sub(coin);
@ -908,6 +920,23 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
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.
* @private
@ -921,7 +950,8 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
TXDB.prototype.confirm = co(function* confirm(tx, info) {
var hash = tx.hash('hex');
var i, account, existing, output, coin;
var address, key;
var input, prevout, path, spender, coins;
var address, key, spent;
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);
}
// 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++) {
output = tx.outputs[i];
key = hash + i;
@ -964,13 +1029,16 @@ TXDB.prototype.confirm = co(function* confirm(tx, info) {
if (!info.hasPath(output))
continue;
coin = yield this.getCoin(hash, i);
spent = yield this.isSpending(prevout.hash, prevout.index);
// Update spent coin.
if (!coin) {
yield this.updateSpentCoin(tx, i);
if (spent)
yield this.updateSpentCoin(spent, tx, i);
coin = yield this.getCoin(hash, i);
if (!coin)
continue;
}
this.pending.confirm(coin.value);
@ -1113,10 +1181,13 @@ TXDB.prototype.__remove = co(function* remove(tx, info) {
coin = coin.toRaw();
this.put(layout.c(prevout.hash, prevout.index), coin);
this.put(layout.C(path.account, prevout.hash, prevout.index), DUMMY);
if (tx.height !== -1) {
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.s(prevout.hash, prevout.index));
this.coinCache.set(key, coin);
}
@ -1224,6 +1295,35 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
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++) {
output = tx.outputs[i];
key = hash + i;
@ -1231,7 +1331,8 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx, info) {
// Update spent coin.
if (!coin) {
yield this.updateSpentCoin(tx, i);
var spent = yield this.isSpent(prevout.hash, prevout.index);
yield this.updateSpentCoin(spent, tx, i);
continue;
}
@ -1727,15 +1828,17 @@ TXDB.prototype.getPending = co(function* getPending(account) {
* @returns {Promise} - Returns {@link Coin}[].
*/
TXDB.prototype.getCoins = function getCoins(account) {
TXDB.prototype.getCoins = co(function* getCoins(account) {
var self = this;
var out = [];
var i, coins, coin;
// Slow case
if (account != null)
return this.getAccountCoins(account);
// Fast case
return this.range({
coins = yield this.range({
gte: layout.c(constants.NULL_HASH, 0x00000000),
lte: layout.c(constants.HIGH_HASH, 0xffffffff),
parse: function(key, value) {
@ -1750,7 +1853,16 @@ TXDB.prototype.getCoins = function getCoins(account) {
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.
@ -1988,14 +2100,13 @@ TXDB.prototype.getSpentCoin = co(function* getSpentCoin(spent, prevout) {
* @returns {Promise}
*/
TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, i) {
var prevout = Outpoint.fromTX(tx, i);
var spent = yield this.isSpent(prevout.hash, prevout.index);
var coin;
TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(spent, tx, i) {
var prevout, coin;
if (!spent)
return;
prevout = Outpoint.fromTX(tx, i);
coin = yield this.getSpentCoin(spent, prevout);
if (!coin)