txdb: new orphan resolution.

This commit is contained in:
Christopher Jeffrey 2016-10-19 05:19:21 -07:00
parent 321f4f6df6
commit 557c1044cc
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 134 additions and 51 deletions

View File

@ -514,7 +514,9 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx) {
if (spent) {
coin = yield this.getSpentCoin(spent, prevout);
assert(coin);
if (!coin)
continue;
if (this.options.verify && tx.height === -1) {
input.coin = coin;
@ -681,6 +683,66 @@ TXDB.prototype.unspendCredit = function unspendCredit(tx, i) {
this.del(layout.d(spender.hash, spender.index));
};
/**
* Write input record.
* @param {TX} tx
* @param {Number} i
*/
TXDB.prototype.writeInput = function writeInput(tx, i) {
var prevout = tx.inputs[i].prevout;
var spender = Outpoint.fromTX(tx, i);
this.put(layout.s(prevout.hash, prevout.index), spender.toRaw());
};
/**
* Remove input record.
* @param {TX} tx
* @param {Number} i
*/
TXDB.prototype.removeInput = function removeInput(tx, i) {
var prevout = tx.inputs[i].prevout;
this.del(layout.s(prevout.hash, prevout.index));
};
/**
* Resolve orphan input.
* @param {TX} tx
* @param {Number} i
* @param {Path} path
* @returns {Boolean}
*/
TXDB.prototype.resolveInput = co(function* resolveInput(tx, i, path) {
var hash = tx.hash('hex');
var spent = yield this.getSpent(hash, i);
var stx, credit;
if (!spent)
return false;
if (yield this.hasSpentCoin(spent))
return false;
stx = yield this.getTX(spent.hash);
assert(stx);
credit = Credit.fromTX(tx, i);
this.spendCredit(credit, stx, spent.index);
if (stx.height === -1) {
credit.spent = true;
this.saveCredit(credit, path);
this.pending.coin++;
if (tx.height !== -1)
this.pending.confirmed += credit.coin.value;
}
return true;
});
/**
* Test an entire transaction to see
* if any of its outpoints are a double-spend.
@ -847,8 +909,10 @@ TXDB.prototype._insert = co(function* insert(tx) {
prevout = input.prevout;
credit = yield this.getCredit(prevout.hash, prevout.index);
if (!credit)
if (!credit) {
this.writeInput(tx, i);
continue;
}
coin = credit.coin;
path = yield this.getPath(coin);
@ -880,10 +944,12 @@ TXDB.prototype._insert = co(function* insert(tx) {
details.setOutput(i, path);
if (yield this.resolveInput(tx, i, path))
continue;
credit = Credit.fromTX(tx, i);
this.pending.coin++;
this.pending.unconfirmed += output.value;
if (tx.height !== -1)
@ -893,25 +959,23 @@ TXDB.prototype._insert = co(function* insert(tx) {
}
this.put(layout.t(hash), tx.toExtended());
this.put(layout.m(tx.ps, hash), DUMMY);
if (tx.height === -1)
this.put(layout.p(hash), DUMMY);
else
this.put(layout.h(tx.height, hash), DUMMY);
this.put(layout.m(tx.ps, hash), DUMMY);
for (i = 0; i < details.accounts.length; i++) {
account = details.accounts[i];
this.put(layout.T(account, hash), DUMMY);
this.put(layout.M(account, tx.ps, hash), DUMMY);
if (tx.height === -1)
this.put(layout.P(account, hash), DUMMY);
else
this.put(layout.H(account, tx.height, hash), DUMMY);
this.put(layout.M(account, tx.ps, hash), DUMMY);
}
this.pending.tx++;
@ -990,6 +1054,9 @@ TXDB.prototype.confirm = co(function* confirm(tx) {
details.setOutput(i, path);
if (yield this.resolveInput(tx, i, path))
continue;
credit = yield this.getCredit(hash, i);
assert(credit);
@ -1105,8 +1172,10 @@ TXDB.prototype._remove = co(function* remove(tx) {
input = tx.inputs[i];
credit = credits[i];
if (!credit)
if (!credit) {
this.removeInput(tx, i);
continue;
}
coin = credit.coin;
path = yield this.getPath(coin);
@ -1147,25 +1216,23 @@ TXDB.prototype._remove = co(function* remove(tx) {
}
this.del(layout.t(hash));
this.del(layout.m(tx.ps, hash));
if (tx.height === -1)
this.del(layout.p(hash));
else
this.del(layout.h(tx.height, hash));
this.del(layout.m(tx.ps, hash));
for (i = 0; i < details.accounts.length; i++) {
account = details.accounts[i];
this.del(layout.T(account, hash));
this.del(layout.M(account, tx.ps, hash));
if (tx.height === -1)
this.del(layout.P(account, hash));
else
this.del(layout.H(account, tx.height, hash));
this.del(layout.M(account, tx.ps, hash));
}
this.pending.tx--;
@ -2216,6 +2283,16 @@ TXDB.prototype.getSpentCoin = co(function* getSpentCoin(spent, prevout) {
return coin;
});
/**
* Test whether the database has a spent coin.
* @param {Outpoint} spent
* @returns {Promise} - Returns {@link Coin}.
*/
TXDB.prototype.hasSpentCoin = function hasSpentCoin(spent) {
return this.has(layout.d(spent.hash, spent.index));
};
/**
* Update spent coin height in storage.
* @param {TX} tx - Sending transaction.

View File

@ -17,24 +17,27 @@ var KEY1 = 'xprv9s21ZrQH143K3Aj6xQBymM31Zb4BVc7wxqfUhMZrzewdDVCt'
var KEY2 = 'xprv9s21ZrQH143K3mqiSThzPtWAabQ22Pjp3uSNnZ53A5bQ4udp'
+ 'faKekc2m4AChLYH1XDzANhrSdxHYWUeTWjYJwFwWFyHkTMnMeAcW4JyRCZa';
var dummyInput = {
prevout: {
hash: constants.NULL_HASH,
index: 0
},
coin: {
version: 1,
height: 0,
value: constants.MAX_MONEY,
script: new bcoin.script([]),
coinbase: false,
hash: constants.NULL_HASH,
index: 0
},
script: new bcoin.script([]),
witness: new bcoin.witness([]),
sequence: 0xffffffff
};
function dummy() {
var hash = crypto.randomBytes(32).toString('hex');
return {
prevout: {
hash: hash,
index: 0
},
coin: {
version: 1,
height: 0,
value: constants.MAX_MONEY,
script: new bcoin.script(),
coinbase: false,
hash: hash,
index: 0
},
script: new bcoin.script(),
witness: new bcoin.witness(),
sequence: 0xffffffff
};
}
describe('Wallet', function() {
var walletdb, wallet, ewallet, ekey, doubleSpendWallet, doubleSpend;
@ -110,7 +113,7 @@ describe('Wallet', function() {
}]
});
src.addInput(dummyInput);
src.addInput(dummy());
tx = bcoin.mtx()
.addInput(src, 0)
@ -162,7 +165,7 @@ describe('Wallet', function() {
}]
});
src.addInput(dummyInput);
src.addInput(dummy());
tx = bcoin.mtx()
.addInput(src, 0)
@ -187,7 +190,7 @@ describe('Wallet', function() {
t1 = bcoin.mtx()
.addOutput(w.getAddress(), 50000)
.addOutput(w.getAddress(), 1000);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1.ts = utils.now();
t1.height = 1;
@ -347,7 +350,7 @@ describe('Wallet', function() {
t1 = bcoin.mtx()
.addOutput(w.getAddress(), 50000)
.addOutput(w.getAddress(), 1000);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1.ts = utils.now();
t1.height = 1;
@ -416,17 +419,20 @@ describe('Wallet', function() {
yield walletdb.addTX(t2);
balance = yield w.getBalance();
assert.equal(balance.unconfirmed, 71000);
//assert.equal(balance.unconfirmed, 71000);
assert.equal(balance.unconfirmed, 47000);
yield walletdb.addTX(t3);
balance = yield w.getBalance();
assert.equal(balance.unconfirmed, 69000);
//assert.equal(balance.unconfirmed, 69000);
assert.equal(balance.unconfirmed, 22000);
yield walletdb.addTX(f1);
balance = yield w.getBalance();
assert.equal(balance.unconfirmed, 58000);
//assert.equal(balance.unconfirmed, 58000);
assert.equal(balance.unconfirmed, 11000);
txs = yield w.getHistory();
assert(txs.some(function(tx) {
@ -478,7 +484,7 @@ describe('Wallet', function() {
.addOutput(w1.getAddress(), 5460)
.addOutput(w1.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -520,7 +526,7 @@ describe('Wallet', function() {
.addOutput(w1.getAddress(), 5460)
.addOutput(w1.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -578,7 +584,7 @@ describe('Wallet', function() {
.addOutput(w1.getAddress(), 5460)
.addOutput(w1.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
// Coinbase
@ -588,7 +594,7 @@ describe('Wallet', function() {
.addOutput(w2.getAddress(), 5460)
.addOutput(w2.getAddress(), 5460);
t2.addInput(dummyInput);
t2.addInput(dummy());
t2 = t2.toTX();
yield walletdb.addTX(t1);
@ -707,7 +713,7 @@ describe('Wallet', function() {
else
utx.addOutput({ address: addr, value: 5460 * 10 });
utx.addInput(dummyInput);
utx.addInput(dummy());
utx = utx.toTX();
// Simulate a confirmation
@ -816,7 +822,7 @@ describe('Wallet', function() {
.addOutput(rec.getAddress(), 5460)
.addOutput(rec.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -878,7 +884,7 @@ describe('Wallet', function() {
.addOutput(w.getAddress(), 5460)
.addOutput(account.receive.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -903,7 +909,7 @@ describe('Wallet', function() {
.addOutput(account.receive.getAddress(), 5460);
t1.ps = 0xdeadbeef;
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -927,7 +933,7 @@ describe('Wallet', function() {
.addOutput(w.getAddress(), 5460)
.addOutput(w.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -963,7 +969,7 @@ describe('Wallet', function() {
.addOutput(w1.getAddress(), 5460)
.addOutput(w1.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -992,7 +998,7 @@ describe('Wallet', function() {
.addOutput(w1.getAddress(), 5460)
.addOutput(w1.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -1065,7 +1071,7 @@ describe('Wallet', function() {
.addOutput(key.getAddress(), 5460)
.addOutput(key.getAddress(), 5460);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1 = t1.toTX();
yield walletdb.addTX(t1);
@ -1194,7 +1200,7 @@ describe('Wallet', function() {
// Coinbase
t1 = bcoin.mtx()
.addOutput(addr, 50000);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1.height = 1;
yield alice.sign(t1);
@ -1267,7 +1273,7 @@ describe('Wallet', function() {
// Coinbase
t1 = bcoin.mtx()
.addOutput(addr, 50000);
t1.addInput(dummyInput);
t1.addInput(dummy());
t1.height = 1;
yield alice.sign(t1);