txdb: spent state.
This commit is contained in:
parent
b1996b9717
commit
4232cdc6b9
@ -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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user