improve tx-pool.
This commit is contained in:
parent
7bb20437bc
commit
1761266ba9
@ -1053,15 +1053,12 @@ Pool.prototype.isWatched = function(tx, bloom) {
|
||||
return false;
|
||||
};
|
||||
|
||||
Pool.prototype.addWallet = function addWallet(w, defaultTs) {
|
||||
Pool.prototype.addWallet = function addWallet(w) {
|
||||
var self = this;
|
||||
var e;
|
||||
|
||||
if (this.loading)
|
||||
return this.once('load', this.addWallet.bind(this, w, defaultTs));
|
||||
|
||||
if (w.loading)
|
||||
return w.once('load', this.addWallet.bind(this, w, defaultTs));
|
||||
return this.once('load', this.addWallet.bind(this, w));
|
||||
|
||||
if (this.wallets.indexOf(w) !== -1)
|
||||
return false;
|
||||
@ -1071,7 +1068,7 @@ Pool.prototype.addWallet = function addWallet(w, defaultTs) {
|
||||
|
||||
e = new EventEmitter();
|
||||
|
||||
function search(ts) {
|
||||
function search() {
|
||||
// Relay pending TXs
|
||||
// NOTE: It is important to do it after search, because search could
|
||||
// add TS to pending TXs, thus making them confirmed
|
||||
@ -1082,17 +1079,21 @@ Pool.prototype.addWallet = function addWallet(w, defaultTs) {
|
||||
if (self.options.fullNode)
|
||||
return;
|
||||
|
||||
// Search for last week by default
|
||||
if (!ts)
|
||||
ts = defaultTs || (utils.now() - 7 * 24 * 3600);
|
||||
if (self._pendingSearch)
|
||||
return;
|
||||
|
||||
self.searchWallet(ts);
|
||||
self._pendingSearch = true;
|
||||
|
||||
utils.nextTick(function() {
|
||||
self._pendingSearch = false;
|
||||
self.searchWallet();
|
||||
});
|
||||
}
|
||||
|
||||
if (w.loaded)
|
||||
search(w.lastTs);
|
||||
if (w.loading)
|
||||
w.once('load', search);
|
||||
else
|
||||
w.once('load', function() { search(w.lastTs) });
|
||||
search();
|
||||
|
||||
return e;
|
||||
};
|
||||
@ -1160,16 +1161,21 @@ Pool.prototype.unwatchWallet = function unwatchWallet(w) {
|
||||
delete w._poolOnRemove;
|
||||
};
|
||||
|
||||
Pool.prototype.searchWallet = function(w) {
|
||||
Pool.prototype.searchWallet = function(w, h) {
|
||||
var self = this;
|
||||
var ts;
|
||||
var ts, height;
|
||||
|
||||
assert(!this.loading);
|
||||
|
||||
if (this.options.fullNode)
|
||||
return;
|
||||
|
||||
if (!w) {
|
||||
if (w == null) {
|
||||
height = this.wallets.reduce(function(ts, w) {
|
||||
if (w.lastHeight < height)
|
||||
return w.lastHeight;
|
||||
return ts;
|
||||
}, Infinity);
|
||||
ts = this.wallets.reduce(function(ts, w) {
|
||||
if (w.lastTs < ts)
|
||||
return w.lastTs;
|
||||
@ -1178,18 +1184,40 @@ Pool.prototype.searchWallet = function(w) {
|
||||
assert(ts !== Infinity);
|
||||
} else if (typeof w === 'number') {
|
||||
ts = w;
|
||||
height = h;
|
||||
} else {
|
||||
if (!w.loaded) {
|
||||
if (w.loading) {
|
||||
w.once('load', function() {
|
||||
self.searchWallet(w);
|
||||
});
|
||||
return;
|
||||
}
|
||||
ts = w.lastTs;
|
||||
if (!ts)
|
||||
ts = utils.now() - 7 * 24 * 3600;
|
||||
height = w.lastHeight;
|
||||
}
|
||||
|
||||
// Always prefer height
|
||||
if (height > 0) {
|
||||
// Back one week
|
||||
if (!height || height === -1)
|
||||
height = this.chain.height() - (7 * 24 * 6);
|
||||
|
||||
utils.nextTick(function() {
|
||||
utils.debug('Wallet height: %s', height);
|
||||
utils.debug(
|
||||
'Reverted chain to height=%d',
|
||||
self.chain.height()
|
||||
);
|
||||
});
|
||||
|
||||
this.chain.resetHeight(height);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ts)
|
||||
ts = utils.now() - 7 * 24 * 3600;
|
||||
|
||||
utils.nextTick(function() {
|
||||
utils.debug('Wallet time: %s', new Date(ts * 1000));
|
||||
utils.debug(
|
||||
|
||||
@ -30,6 +30,7 @@ function TXPool(wallet) {
|
||||
this._unspent = {};
|
||||
this._orphans = {};
|
||||
this._lastTs = 0;
|
||||
this._lastHeight = 0;
|
||||
this._loaded = false;
|
||||
this._addresses = {};
|
||||
this._sent = new bn(0);
|
||||
@ -76,22 +77,20 @@ TXPool.prototype._init = function init() {
|
||||
|
||||
s.on('end', function() {
|
||||
self._loaded = true;
|
||||
self.emit('load', self._lastTs);
|
||||
self.emit('load', self._lastTs, self._lastHeight);
|
||||
});
|
||||
};
|
||||
|
||||
TXPool.prototype.add = function add(tx, noWrite, strict) {
|
||||
var hash = tx.hash('hex');
|
||||
var updated;
|
||||
var i, input, key, unspent, index, orphan;
|
||||
var out, key, orphans, some;
|
||||
var updated = false;
|
||||
var i, input, output, unspent, index, orphan;
|
||||
var key, orphans, some;
|
||||
|
||||
this._wallet.fillPrevout(tx);
|
||||
|
||||
if (strict) {
|
||||
if (!this._wallet.ownInput(tx) && !this._wallet.ownOutput(tx))
|
||||
return false;
|
||||
}
|
||||
if (!this._wallet.ownInput(tx) && !this._wallet.ownOutput(tx))
|
||||
return false;
|
||||
|
||||
// Ignore stale pending transactions
|
||||
if (tx.ts === 0 && tx.ps + 2 * 24 * 3600 < utils.now()) {
|
||||
@ -107,14 +106,15 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
|
||||
this._all[hash].ts = tx.ts;
|
||||
this._all[hash].block = tx.block;
|
||||
this._storeTX(hash, tx, noWrite);
|
||||
this.emit('tx', tx);
|
||||
this._lastTs = Math.max(tx.ts, this._lastTs);
|
||||
this._lastHeight = Math.max(tx.getHeight(), this._lastHeight);
|
||||
this.emit('update', this._lastTs, this._lastHeight, tx);
|
||||
this.emit('confirmed', tx);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
this._all[hash] = tx;
|
||||
|
||||
updated = false;
|
||||
this._all[hash] = tx;
|
||||
|
||||
// Consume unspent money or add orphans
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
@ -143,15 +143,8 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
|
||||
}
|
||||
|
||||
// Only add orphans if this input is ours.
|
||||
// If there is no previous output, there's no way to truly
|
||||
// verify this is ours, so we assume it is. If we add the
|
||||
// signature checking code to ownInput for p2sh and p2pk,
|
||||
// we could in theory use ownInput here (and down below)
|
||||
// instead.
|
||||
if (input.output) {
|
||||
if (!this._wallet.ownOutput(input.output))
|
||||
continue;
|
||||
}
|
||||
if (!this._wallet.ownInput(input))
|
||||
continue;
|
||||
|
||||
// Add orphan, if no parent transaction is yet known
|
||||
orphan = { tx: tx, index: input.prevout.index };
|
||||
@ -161,16 +154,6 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
|
||||
this._orphans[key] = [orphan];
|
||||
}
|
||||
|
||||
if (!this._wallet.ownOutput(tx)) {
|
||||
if (updated)
|
||||
this.emit('update', this._lastTs, tx);
|
||||
|
||||
// Save spending TXs without adding unspents
|
||||
// if (this._wallet.ownInput(tx))
|
||||
this._storeTX(hash, tx, noWrite);
|
||||
return;
|
||||
}
|
||||
|
||||
function checkOrphan(orphan) {
|
||||
var index = orphan.tx._inputIndex(tx.hash('hex'), orphan.index);
|
||||
assert(index !== -1);
|
||||
@ -184,13 +167,14 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
|
||||
this._removeTX(orphan.tx, noWrite);
|
||||
return false;
|
||||
}
|
||||
|
||||
this._addInput(orphan.tx, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Add unspent outputs or fullfill orphans
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
out = tx.outputs[i];
|
||||
output = tx.outputs[i];
|
||||
|
||||
// Do not add unspents for outputs that aren't ours.
|
||||
if (!this._wallet.ownOutput(tx, i))
|
||||
@ -216,16 +200,41 @@ TXPool.prototype.add = function add(tx, noWrite, strict) {
|
||||
}
|
||||
|
||||
this._lastTs = Math.max(tx.ts, this._lastTs);
|
||||
this._lastHeight = Math.max(tx.getHeight(), this._lastHeight);
|
||||
if (updated)
|
||||
this.emit('update', this._lastTs, tx);
|
||||
|
||||
this._storeTX(hash, tx, noWrite);
|
||||
this.emit('update', this._lastTs, this._lastHeight, tx);
|
||||
|
||||
this.emit('tx', tx);
|
||||
|
||||
if (tx.ts !== 0)
|
||||
this.emit('confirmed', tx);
|
||||
|
||||
this._storeTX(hash, tx, noWrite);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
TXPool.prototype.getTX = function getTX(hash) {
|
||||
return this._all[hash];
|
||||
};
|
||||
|
||||
TXPool.prototype.getUnspent = function getUnspent(hash, index) {
|
||||
return this._unspent[hash + '/' + index];
|
||||
};
|
||||
|
||||
TXPool.prototype.addUnspent = function addUnspent(coin) {
|
||||
var id = coin.hash + '/' + coin.index;
|
||||
if (!this._unspent[id]) {
|
||||
this._unspent[id] = coin;
|
||||
this._addOutput(coin);
|
||||
this._lastHeight = Math.max(coin.height, this._lastHeight);
|
||||
this.emit('update', this._lastTs, this._lastHeight);
|
||||
// Weird workaround to get addresses to update
|
||||
if (coin.height !== -1)
|
||||
this.emit('confirmed', coin);
|
||||
}
|
||||
};
|
||||
|
||||
TXPool.prototype._storeTX = function _storeTX(hash, tx, noWrite) {
|
||||
var self = this;
|
||||
|
||||
@ -285,11 +294,17 @@ TXPool.prototype.getAll = function getAll(address) {
|
||||
};
|
||||
|
||||
TXPool.prototype._addOutput = function _addOutput(tx, i, remove) {
|
||||
var output = tx.outputs[i];
|
||||
var address;
|
||||
if ((tx instanceof bcoin.output) || (tx instanceof bcoin.coin)) {
|
||||
var output = tx;
|
||||
if (!this._wallet.ownOutput(output))
|
||||
return;
|
||||
} else {
|
||||
var output = tx.outputs[i];
|
||||
var address;
|
||||
|
||||
if (!this._wallet.ownOutput(tx, i))
|
||||
return;
|
||||
if (!this._wallet.ownOutput(tx, i))
|
||||
return;
|
||||
}
|
||||
|
||||
address = output.getAddress();
|
||||
|
||||
|
||||
@ -1435,13 +1435,16 @@ TX.prototype.hasPrevout = function hasPrevout() {
|
||||
});
|
||||
};
|
||||
|
||||
TX.prototype.fillPrevout = function fillPrevout(txs) {
|
||||
TX.prototype.fillPrevout = function fillPrevout(txs, unspent) {
|
||||
var inputs;
|
||||
|
||||
if (txs instanceof bcoin.txPool)
|
||||
if (txs instanceof bcoin.txPool) {
|
||||
unspent = txs._unspent;
|
||||
txs = txs._all;
|
||||
else if (txs instanceof bcoin.wallet)
|
||||
} else if (txs instanceof bcoin.wallet) {
|
||||
unspent = txs.tx._unspent;
|
||||
txs = txs.tx._all;
|
||||
}
|
||||
|
||||
if (Array.isArray(txs)) {
|
||||
txs = txs.reduce(function(out, tx) {
|
||||
@ -1450,9 +1453,24 @@ TX.prototype.fillPrevout = function fillPrevout(txs) {
|
||||
}, {});
|
||||
}
|
||||
|
||||
if (Array.isArray(unspent)) {
|
||||
unspent = unspent.reduce(function(out, coin) {
|
||||
out[coin.hash + '/' + coin.index] = coin;
|
||||
return out;
|
||||
}, {});
|
||||
}
|
||||
|
||||
inputs = this.inputs.filter(function(input) {
|
||||
if (!input.output && txs[input.prevout.hash])
|
||||
input.output = bcoin.coin(txs[input.prevout.hash], input.prevout.index);
|
||||
var key;
|
||||
|
||||
if (!input.output) {
|
||||
key = input.prevout.hash + '/' + input.prevout.index;
|
||||
if (unspent && unspent[key])
|
||||
input.output = unspent[key];
|
||||
else if (txs && txs[input.prevout.hash])
|
||||
input.output = bcoin.coin(txs[input.prevout.hash], input.prevout.index);
|
||||
}
|
||||
|
||||
return !!input.output;
|
||||
}, this);
|
||||
|
||||
|
||||
@ -151,6 +151,7 @@ function Wallet(options) {
|
||||
this.storage = options.storage;
|
||||
this.loading = true;
|
||||
this.lastTs = 0;
|
||||
this.lastHeight = 0;
|
||||
|
||||
// This is a chicken and egg problem for BIP45. Real address keys cannot be
|
||||
// generated until all shared keys have been added to the wallet. The flow of
|
||||
@ -476,36 +477,42 @@ Wallet.prototype._init = function init() {
|
||||
|
||||
if (this.tx._loaded) {
|
||||
this.loading = false;
|
||||
this._pruneAddresses();
|
||||
// this._pruneAddresses();
|
||||
return;
|
||||
}
|
||||
|
||||
// Notify owners about new accepted transactions
|
||||
this.tx.on('update', function(lastTs, tx) {
|
||||
this.tx.on('update', function(lastTs, lastHeight, tx) {
|
||||
var b = this.getBalance();
|
||||
if (prevBalance && prevBalance.cmp(b) !== 0)
|
||||
self.emit('balance', b);
|
||||
self.emit('update', tx);
|
||||
if (tx)
|
||||
self.emit('update', tx);
|
||||
self.lastTs = Math.max(lastTs, self.lastTs);
|
||||
self.lastHeight = Math.max(lastHeight, self.lastHeight);
|
||||
prevBalance = b;
|
||||
});
|
||||
|
||||
this.tx.on('tx', function(tx) {
|
||||
// TX using this address was confirmed.
|
||||
// Allocate a new address.
|
||||
if (tx.block) {
|
||||
if (self.currentAddress.ownOutput(tx))
|
||||
self.currentAddress = self.createAddress();
|
||||
if (self.changeAddress.ownOutput(tx))
|
||||
self.changeAddress = self.createAddress(true);
|
||||
self._pruneAddresses();
|
||||
}
|
||||
self.emit('tx', tx);
|
||||
});
|
||||
|
||||
this.tx.once('load', function(ts) {
|
||||
this.tx.on('confirmed', function(tx) {
|
||||
// TX using this address was confirmed.
|
||||
// Allocate a new address.
|
||||
if (self.currentAddress.ownOutput(tx))
|
||||
self.currentAddress = self.createAddress();
|
||||
if (self.changeAddress.ownOutput(tx))
|
||||
self.changeAddress = self.createAddress(true);
|
||||
// self._pruneAddresses();
|
||||
self.emit('confirmed', tx);
|
||||
});
|
||||
|
||||
this.tx.once('load', function(ts, height) {
|
||||
self.loading = false;
|
||||
self.lastTs = ts;
|
||||
self._pruneAddresses();
|
||||
self.lastHeight = height;
|
||||
// self._pruneAddresses();
|
||||
self.emit('load', ts);
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user