txdb-state-spent-flag
This commit is contained in:
parent
11b2f1a7bc
commit
f075767cdb
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user