txdb-state-spent-flag

This commit is contained in:
Christopher Jeffrey 2016-10-18 05:34:23 -07:00
parent 11b2f1a7bc
commit f075767cdb
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD

View File

@ -136,12 +136,6 @@ 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);
},
@ -216,7 +210,6 @@ function TXDB(wallet) {
this.network = wallet.db.network;
this.options = wallet.db.options;
this.coinCache = new LRU(10000);
this.spentCache = new LRU(10000);
this.locked = {};
this.state = null;
@ -700,7 +693,7 @@ TXDB.prototype._add = co(function* add(tx) {
var hash = tx.hash('hex');
var path, account, existing;
var i, input, output, coin;
var prevout, key, spender, raw, details;
var prevout, key, spender, raw, credit, details;
assert(!tx.mutable, 'Cannot add mutable TX to wallet.');
@ -755,12 +748,13 @@ TXDB.prototype._add = co(function* add(tx) {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
coin = yield this.getCoin(prevout.hash, prevout.index);
credit = yield this.getCredit(prevout.hash, prevout.index);
// Only bother if this input is ours.
if (!coin)
if (!credit)
continue;
coin = credit.coin;
path = yield this.getPath(coin);
assert(path);
@ -775,8 +769,10 @@ TXDB.prototype._add = co(function* add(tx) {
this.pending.unconfirmed -= coin.value;
if (tx.height === -1) {
this.put(layout.S(prevout.hash, prevout.index), spender);
this.spentCache.set(key, spender);
credit.spent = true;
raw = credit.toRaw();
this.put(layout.c(prevout.hash, prevout.index), raw);
this.coinCache.set(key, raw);
} else {
this.pending.confirmed -= coin.value;
this.del(layout.c(prevout.hash, prevout.index));
@ -802,8 +798,9 @@ TXDB.prototype._add = co(function* add(tx) {
details.addOutput(i, path);
coin = Coin.fromTX(tx, i);
raw = coin.toRaw();
credit = Credit.fromTX(tx, i);
coin = credit.coin;
raw = credit.toRaw();
this.pending.unconfirmed += coin.value;
@ -978,28 +975,6 @@ TXDB.prototype.getSpent = co(function* getSpent(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.getSpending = co(function* getSpending(hash, index) {
var key = hash + index;
var data = this.spentCache.get(key);
if (data)
return Outpoint.fromRaw(data);
data = yield this.get(layout.S(hash, index));
if (!data)
return;
return Outpoint.fromRaw(data);
});
/**
* Test a whether a coin has been spent.
* @param {Hash} hash
@ -1012,25 +987,6 @@ TXDB.prototype.isSpent = co(function* isSpent(hash, index) {
return data != null;
});
/**
* 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 = hash + index;
var data = this.spentCache.get(key);
if (data)
return true;
data = yield this.get(layout.S(hash, index));
return data != null;
});
/**
* Attempt to confirm a transaction.
* @private
@ -1044,7 +1000,7 @@ TXDB.prototype.confirm = co(function* confirm(tx, existing) {
var hash = tx.hash('hex');
var i, account, output, coin;
var input, prevout, path, spender, coins;
var key, raw, details;
var key, raw, credit, details;
// Inject block properties.
existing.ts = tx.ts;
@ -1095,14 +1051,12 @@ TXDB.prototype.confirm = co(function* confirm(tx, existing) {
key = prevout.hash + prevout.index;
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.pending.coin--;
this.pending.confirmed -= coin.value;
this.spentCache.remove(key);
this.coinCache.remove(key);
}
}
@ -1121,13 +1075,14 @@ TXDB.prototype.confirm = co(function* confirm(tx, existing) {
// Update spent coin.
yield this.updateSpentCoin(tx, i);
coin = yield this.getCoin(hash, i);
credit = yield this.getCredit(hash, i);
if (!coin)
if (!credit)
continue;
coin = credit.coin;
coin.height = tx.height;
raw = coin.toRaw();
raw = credit.toRaw();
this.pending.confirmed += coin.value;
this.pending.coin++;
@ -1226,7 +1181,7 @@ TXDB.prototype.lazyRemove = co(function* lazyRemove(tx) {
TXDB.prototype.__remove = co(function* remove(tx) {
var hash = tx.hash('hex');
var i, path, account, key, prevout;
var input, output, coin, coins, raw, details;
var input, output, coin, coins, raw, credit, details;
details = new Details(this, tx);
@ -1254,6 +1209,8 @@ TXDB.prototype.__remove = co(function* remove(tx) {
path = yield this.getPath(coin);
assert(path);
credit = new Credit(coin);
details.addInput(i, path, coin);
this.pending.unconfirmed += coin.value;
@ -1261,15 +1218,17 @@ TXDB.prototype.__remove = co(function* remove(tx) {
this.del(layout.s(prevout.hash, prevout.index));
if (tx.height !== -1) {
raw = coin.toRaw();
raw = credit.toRaw();
this.pending.confirmed += coin.value;
this.pending.coin++;
this.put(layout.c(prevout.hash, prevout.index), raw);
this.put(layout.C(path.account, prevout.hash, prevout.index), DUMMY);
this.coinCache.set(key, raw);
} else {
this.del(layout.S(prevout.hash, prevout.index));
this.spentCache.remove(key);
credit.spent = false;
raw = credit.toRaw();
this.put(layout.c(prevout.hash, prevout.index), raw);
this.coinCache.set(key, raw);
}
this.del(layout.d(hash, i));
@ -1372,7 +1331,7 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx) {
var hash = tx.hash('hex');
var height = tx.height;
var i, account, output, key, coin, coins;
var input, prevout, path, spender, raw;
var input, prevout, path, spender, raw, credit;
if (height === -1)
return;
@ -1401,7 +1360,7 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx) {
assert(coin.height !== -1);
raw = coin.toRaw();
credit = new Credit(coin);
path = yield this.getPath(coin);
assert(path);
@ -1412,14 +1371,14 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx) {
spender = Outpoint.fromTX(tx, i).toRaw();
this.put(layout.S(prevout.hash, prevout.index), spender);
credit.spent = true;
raw = credit.toRaw();
this.put(layout.c(prevout.hash, prevout.index), raw);
this.put(layout.C(path.account, prevout.hash, prevout.index), DUMMY);
this.pending.coin++;
this.pending.confirmed += coin.value;
this.spentCache.set(key, spender);
this.coinCache.set(key, raw);
}
}
@ -1432,15 +1391,16 @@ TXDB.prototype.__unconfirm = co(function* unconfirm(tx) {
// Update spent coin.
yield this.updateSpentCoin(tx, i);
coin = yield this.getCoin(hash, i);
credit = yield this.getCredit(hash, i);
if (!coin)
if (!credit)
continue;
details.addOutput(i, path);
coin = credit.coin;
coin.height = -1;
raw = coin.toRaw();
raw = credit.toRaw();
this.pending.confirmed -= coin.value;
this.pending.coin++;
@ -1939,39 +1899,58 @@ TXDB.prototype.getPending = co(function* getPending(account) {
*/
TXDB.prototype.getCoins = co(function* getCoins(account, all) {
var credits = yield this.getCredits(account, all);
var coins = [];
var i, credit;
for (i = 0; i < credits.length; i++) {
credit = credits[i];
coins.push(credit.coin);
}
return coins;
});
/**
* Get coins.
* @param {Number?} account
* @returns {Promise} - Returns {@link Coin}[].
*/
TXDB.prototype.getCredits = co(function* getCredits(account, all) {
var self = this;
var out = [];
var i, coins, coin;
var i, credits, credit;
// Slow case
if (account != null)
return this.getAccountCoins(account, all);
return this.getAccountCredits(account, all);
// Fast case
coins = yield this.range({
credits = yield this.range({
gte: layout.c(constants.NULL_HASH, 0x00000000),
lte: layout.c(constants.HIGH_HASH, 0xffffffff),
parse: function(key, value) {
var parts = layout.cc(key);
var hash = parts[0];
var index = parts[1];
var coin = Coin.fromRaw(value);
var credit = Credit.fromRaw(value);
var ckey = hash + index;
coin.hash = hash;
coin.index = index;
credit.coin.hash = hash;
credit.coin.index = index;
self.coinCache.set(ckey, value);
return coin;
return credit;
}
});
if (all)
return coins;
return credits;
for (i = 0; i < coins.length; i++) {
coin = coins[i];
if (yield this.isSpending(coin.hash, coin.index))
for (i = 0; i < credits.length; i++) {
credit = credits[i];
if (credit.spent)
continue;
out.push(coin);
out.push(credit);
}
return out;
@ -1984,28 +1963,47 @@ TXDB.prototype.getCoins = co(function* getCoins(account, all) {
*/
TXDB.prototype.getAccountCoins = co(function* getAccountCoins(account, all) {
var prevout = yield this.getOutpoints(account);
var credits = yield this.getAccountCredits(account, all);
var coins = [];
var i, op, coin;
var i, credit;
for (i = 0; i < prevout.length; i++) {
op = prevout[i];
coin = yield this.getCoin(op.hash, op.index);
if (!coin)
continue;
if (!all) {
if (yield this.isSpending(coin.hash, coin.index))
continue;
}
coins.push(coin);
for (i = 0; i < credits.length; i++) {
credit = credits[i];
coins.push(credit.coin);
}
return coins;
});
/**
* Get coins by account.
* @param {Number} account
* @returns {Promise} - Returns {@link Coin}[].
*/
TXDB.prototype.getAccountCredits = co(function* getAccountCredits(account, all) {
var prevout = yield this.getOutpoints(account);
var credits = [];
var i, op, credit;
for (i = 0; i < prevout.length; i++) {
op = prevout[i];
credit = yield this.getCredit(op.hash, op.index);
if (!credit)
continue;
if (!all) {
if (credit.spent)
continue;
}
credits.push(credit);
}
return credits;
});
/**
* Fill a transaction with coins (all historical coins).
* @param {TX} tx
@ -2046,7 +2044,7 @@ TXDB.prototype.fillHistory = co(function* fillHistory(tx) {
*/
TXDB.prototype.fillCoins = co(function* fillCoins(tx) {
var i, input, prevout, coin;
var i, input, prevout, credit;
if (tx.isCoinbase())
return tx;
@ -2058,12 +2056,12 @@ TXDB.prototype.fillCoins = co(function* fillCoins(tx) {
if (input.coin)
continue;
coin = yield this.getCoin(prevout.hash, prevout.index);
credit = yield this.getCredit(prevout.hash, prevout.index);
if (!coin)
if (!credit)
continue;
if (yield this.isSpending(coin.hash, coin.index))
if (credit.spent)
continue;
input.coin = coin;
@ -2180,15 +2178,29 @@ TXDB.prototype.hasTX = function hasTX(hash) {
*/
TXDB.prototype.getCoin = co(function* getCoin(hash, index) {
var credit = yield this.getCredit(hash, index);
if (!credit)
return;
return credit.coin;
});
/**
* Get coin.
* @param {Hash} hash
* @param {Number} index
* @returns {Promise} - Returns {@link Coin}.
*/
TXDB.prototype.getCredit = co(function* getCredit(hash, index) {
var key = hash + index;
var data = this.coinCache.get(key);
var coin;
var credit;
if (data) {
coin = Coin.fromRaw(data);
coin.hash = hash;
coin.index = index;
return coin;
credit = Credit.fromRaw(data);
credit.coin.hash = hash;
credit.coin.index = index;
return credit;
}
data = yield this.get(layout.c(hash, index));
@ -2196,13 +2208,13 @@ TXDB.prototype.getCoin = co(function* getCoin(hash, index) {
if (!data)
return;
coin = Coin.fromRaw(data);
coin.hash = hash;
coin.index = index;
credit = Credit.fromRaw(data);
credit.coin.hash = hash;
credit.coin.index = index;
this.coinCache.set(key, data);
return coin;
return credit;
});
/**
@ -2288,18 +2300,18 @@ TXDB.prototype.getBalance = co(function* getBalance(account) {
*/
TXDB.prototype.getWalletBalance = co(function* getWalletBalance() {
var coins = yield this.getCoins(null, true);
var credits = yield this.getCredits(null, true);
var balance = new Balance(this.wallet.wid, this.wallet.id, -1);
var i, coin;
var i, credit;
for (i = 0; i < coins.length; i++) {
coin = coins[i];
for (i = 0; i < credits.length; i++) {
credit = credits[i];
if (coin.height !== -1)
balance.confirmed += coin.value;
if (credit.coin.height !== -1)
balance.confirmed += credit.coin.value;
if (!(yield this.isSpending(coin.hash, coin.index)))
balance.unconfirmed += coin.value;
if (!credit.spent)
balance.unconfirmed += credit.coin.value;
}
return balance;
@ -2312,18 +2324,18 @@ TXDB.prototype.getWalletBalance = co(function* getWalletBalance() {
*/
TXDB.prototype.getAccountBalance = co(function* getAccountBalance(account) {
var coins = yield this.getAccountCoins(account, true);
var credits = yield this.getAccountCredits(account, true);
var balance = new Balance(this.wallet.wid, this.wallet.id, account);
var i, coin;
var i, credit;
for (i = 0; i < coins.length; i++) {
coin = coins[i];
for (i = 0; i < credits.length; i++) {
credit = credits[i];
if (coin.height !== -1)
balance.confirmed += coin.value;
if (credit.coin.height !== -1)
balance.confirmed += credit.coin.value;
if (!(yield this.isSpending(coin.hash, coin.index)))
balance.unconfirmed += coin.value;
if (!credit.spent)
balance.unconfirmed += credit.coin.value;
}
return balance;
@ -2651,6 +2663,45 @@ DetailsMember.prototype.toJSON = function toJSON(network) {
};
};
function Credit(coin, spent) {
if (!(this instanceof Credit))
return new Credit(coin, spent);
this.coin = coin || new Coin();
this.spent = spent || false;
}
Credit.prototype.fromRaw = function fromRaw(data) {
var p = BufferReader(data);
this.coin.fromRaw(p);
this.spent = p.readU8() === 1;
return this;
};
Credit.fromRaw = function fromRaw(data) {
return new Credit().fromRaw(data);
};
Credit.prototype.toRaw = function toRaw(writer) {
var p = BufferWriter(writer);
this.coin.toRaw(p);
p.writeU8(this.spent ? 1 : 0);
if (!writer)
p = p.render();
return p;
};
Credit.prototype.fromTX = function fromTX(tx, i) {
this.coin.fromTX(tx, i);
this.spent = false;
return this;
};
Credit.fromTX = function fromTX(tx, i) {
return new Credit().fromTX(tx, i);
};
/*
* Expose