mempool. txdb.
This commit is contained in:
parent
a52d62a71e
commit
e205c70f97
@ -291,6 +291,8 @@ Block.prototype.inspect = function inspect() {
|
||||
type: this.type,
|
||||
height: this.height,
|
||||
hash: utils.revHex(this.hash('hex')),
|
||||
size: this.getSize(),
|
||||
virtualSize: this.getVirtualSize(),
|
||||
date: new Date(this.ts * 1000).toISOString(),
|
||||
version: this.version,
|
||||
prevBlock: utils.revHex(this.prevBlock),
|
||||
|
||||
@ -164,6 +164,9 @@ Chain.prototype._init = function _init() {
|
||||
|
||||
self.loaded = true;
|
||||
self.emit('open');
|
||||
|
||||
if (self.isFull())
|
||||
self.emit('full');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1203,7 +1206,11 @@ Chain.prototype.add = function add(initial, peer, callback, force) {
|
||||
bcoin.profiler.snapshot();
|
||||
|
||||
utils.nextTick(function() {
|
||||
if (self.isFull())
|
||||
self.emit('full');
|
||||
|
||||
unlock();
|
||||
|
||||
if (err)
|
||||
callback(err);
|
||||
else
|
||||
|
||||
@ -38,13 +38,18 @@ Fullnode.prototype._init = function _init() {
|
||||
this.chain = new bcoin.chain(this, {
|
||||
preload: false,
|
||||
fsync: false,
|
||||
spv: false,
|
||||
prune: this.options.prune,
|
||||
useCheckpoints: this.options.useCheckpoints
|
||||
});
|
||||
|
||||
// Mempool needs access to blockdb.
|
||||
this.mempool = new bcoin.mempool(this, {
|
||||
rbf: false
|
||||
limitFree: this.options.limitFree,
|
||||
limitFreeRelay: this.options.limitFreeRelay,
|
||||
requireStandard: this.options.requireStandard,
|
||||
rejectInsaneFees: this.options.rejectInsaneFees,
|
||||
replaceByFee: this.options.replaceByFee
|
||||
});
|
||||
|
||||
// Pool needs access to the chain.
|
||||
@ -92,12 +97,12 @@ Fullnode.prototype._init = function _init() {
|
||||
self.emit('error', err);
|
||||
});
|
||||
|
||||
// this.on('tx', function(tx) {
|
||||
// self.walletdb.addTX(tx, function(err) {
|
||||
// if (err)
|
||||
// self.emit('error', err);
|
||||
// });
|
||||
// });
|
||||
this.on('tx', function(tx) {
|
||||
self.walletdb.addTX(tx, function(err) {
|
||||
if (err)
|
||||
self.emit('error', err);
|
||||
});
|
||||
});
|
||||
|
||||
// Emit events for valid blocks and TXs.
|
||||
this.chain.on('block', function(block) {
|
||||
@ -112,14 +117,14 @@ Fullnode.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
// Update the mempool.
|
||||
// this.chain.on('add block', function(block) {
|
||||
// self.mempool.addBlock(block);
|
||||
// });
|
||||
this.chain.on('add block', function(block) {
|
||||
self.mempool.addBlock(block);
|
||||
});
|
||||
|
||||
// this.chain.on('remove block', function(block) {
|
||||
// self.mempool.removeBlock(block);
|
||||
// self.walletdb.removeBlock(block);
|
||||
// });
|
||||
this.chain.on('remove block', function(block) {
|
||||
self.mempool.removeBlock(block);
|
||||
self.walletdb.removeBlock(block);
|
||||
});
|
||||
|
||||
function load(err) {
|
||||
if (err)
|
||||
@ -335,25 +340,33 @@ Fullnode.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
|
||||
Fullnode.prototype.fillCoin = function fillCoin(tx, callback) {
|
||||
var self = this;
|
||||
|
||||
this.mempool.fillCoin(tx, function(err, filled) {
|
||||
this.mempool.tx.isDoubleSpend(tx, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (filled)
|
||||
return callback(null, tx);
|
||||
if (result)
|
||||
return callback(null, tx, true);
|
||||
|
||||
self.chain.db.fillCoin(tx, callback);
|
||||
self.mempool.fillCoin(tx, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (tx.hasPrevout())
|
||||
return callback(null, tx);
|
||||
|
||||
self.chain.db.fillCoin(tx, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Fullnode.prototype.fillTX = function fillTX(tx, callback) {
|
||||
var self = this;
|
||||
|
||||
this.mempool.fillTX(tx, function(err, filled) {
|
||||
this.mempool.fillTX(tx, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (filled)
|
||||
if (tx.hasPrevout())
|
||||
return callback(null, tx);
|
||||
|
||||
self.chain.db.fillTX(tx, callback);
|
||||
|
||||
@ -85,8 +85,13 @@ NodeServer.prototype._init = function _init() {
|
||||
if (params.receiveDepth)
|
||||
options.receiveDepth = params.receiveDepth >>> 0;
|
||||
|
||||
if (params.address)
|
||||
if (params.address) {
|
||||
params.addresses = params.address;
|
||||
options.address = params.address;
|
||||
}
|
||||
|
||||
if (params.value)
|
||||
options.value = utils.satoshi(params.value);
|
||||
|
||||
if (params.addresses) {
|
||||
if (typeof params.addresses === 'string')
|
||||
@ -95,6 +100,9 @@ NodeServer.prototype._init = function _init() {
|
||||
options.addresses = params.addresses;
|
||||
}
|
||||
|
||||
if (params.passphrase)
|
||||
options.passphrase = params.passphrase;
|
||||
|
||||
if (params.tx) {
|
||||
try {
|
||||
options.tx = bcoin.tx.fromRaw(params.tx, 'hex');
|
||||
@ -241,6 +249,35 @@ NodeServer.prototype._init = function _init() {
|
||||
});
|
||||
});
|
||||
|
||||
this.post('/wallet/:id/send', function(req, res, next, send) {
|
||||
self.walletdb.get(req.options.id, req.options.passphrase, function(err, wallet) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
if (!wallet)
|
||||
return send(404);
|
||||
|
||||
wallet.createTX({
|
||||
address: req.options.address,
|
||||
value: req.options.value
|
||||
}, function(err, tx) {
|
||||
wallet.destroy();
|
||||
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
utils.print(tx);
|
||||
return send(200, tx.toJSON());
|
||||
self.pool.sendTX(tx, function(err) {
|
||||
if (err)
|
||||
return next(err);
|
||||
|
||||
send(200, tx.toJSON());
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Update wallet / sync address depth
|
||||
this.put('/wallet/:id', function(req, res, next, send) {
|
||||
var id = req.options.id;
|
||||
@ -361,9 +398,12 @@ NodeServer.prototype._init = function _init() {
|
||||
|
||||
// Broadcast TX
|
||||
this.post('/broadcast', function(req, res, next, send) {
|
||||
var tx = req.options.tx;
|
||||
self.pool.broadcast(tx);
|
||||
send(200, { success: true });
|
||||
self.pool.sendTX(tx, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
send(200, { success: true });
|
||||
});
|
||||
});
|
||||
|
||||
this.server.on('error', function(err) {
|
||||
|
||||
@ -59,9 +59,9 @@ function Mempool(node, options) {
|
||||
this.freeCount = 0;
|
||||
this.lastTime = 0;
|
||||
|
||||
this.limitFree = this.options.limitFree !== false;
|
||||
this.limitFreeRelay = this.options.limitFreeRelay || 15;
|
||||
this.requireStandard = this.options.requireStandard !== false;
|
||||
this.limitFree = this.options.limitFree !== false;
|
||||
this.rejectInsaneFees = this.options.rejectInsaneFees !== false;
|
||||
|
||||
Mempool.global = this;
|
||||
@ -208,24 +208,18 @@ Mempool.prototype.addTX = function addTX(tx, peer, callback, force) {
|
||||
if (exists)
|
||||
return callback();
|
||||
|
||||
self.node.fillCoin(tx, function(err) {
|
||||
self.node.fillCoin(tx, function(err, tx, doubleSpend) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!tx.hasPrevout()) {
|
||||
return self.tx.isDoubleSpend(tx, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (result) {
|
||||
peer.sendReject(tx, 'bad-txns-inputs-spent', 0);
|
||||
return callback(new VerifyError('bad-txns-inputs-spent', 0));
|
||||
}
|
||||
|
||||
return self.storeOrphan(tx, callback);
|
||||
});
|
||||
if (doubleSpend) {
|
||||
peer.sendReject(tx, 'bad-txns-inputs-spent', 0);
|
||||
return callback(new VerifyError('bad-txns-inputs-spent', 0));
|
||||
}
|
||||
|
||||
if (!tx.hasPrevout())
|
||||
return self.storeOrphan(tx, callback);
|
||||
|
||||
self.verify(tx, function(err) {
|
||||
if (err) {
|
||||
if (err.type === 'VerifyError' && err.score >= 0)
|
||||
@ -247,6 +241,8 @@ Mempool.prototype.addUnchecked = function addUnchecked(tx, peer, callback) {
|
||||
|
||||
self.emit('tx', tx);
|
||||
|
||||
utils.debug('Added tx %s to the mempool.', tx.rhash);
|
||||
|
||||
self.resolveOrphans(tx, function(err, resolved) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
@ -26,7 +26,7 @@ function MTX(options) {
|
||||
|
||||
this.options = options;
|
||||
|
||||
this.type = 'mtx';
|
||||
this.type = 'tx';
|
||||
this.version = options.version || 1;
|
||||
this.inputs = [];
|
||||
this.outputs = [];
|
||||
|
||||
@ -228,7 +228,7 @@ Peer.prototype.broadcast = function broadcast(items) {
|
||||
clearInterval(old.interval);
|
||||
}
|
||||
|
||||
var inv = this.framer.inv([{
|
||||
var inv = this.framer.inv([{
|
||||
type: item.type,
|
||||
hash: item.hash()
|
||||
}]);
|
||||
|
||||
@ -213,17 +213,20 @@ Pool.prototype._init = function _init() {
|
||||
self.resolveOrphan(self.peers.load, null, data.hash);
|
||||
});
|
||||
|
||||
this.chain.on('full', function() {
|
||||
self._stopTimer();
|
||||
self._stopInterval();
|
||||
if (!self.synced)
|
||||
self.getMempool();
|
||||
self.synced = true;
|
||||
self.emit('full');
|
||||
utils.debug('Chain is fully synced (height=%d).', self.chain.height);
|
||||
});
|
||||
|
||||
(this.options.wallets || []).forEach(function(wallet) {
|
||||
self.addWallet(wallet);
|
||||
});
|
||||
|
||||
// Chain is full and up-to-date
|
||||
if (this.chain.isFull()) {
|
||||
this.synced = true;
|
||||
this.emit('full');
|
||||
utils.debug('Chain is fully synced (height=%d).', this.chain.height);
|
||||
}
|
||||
|
||||
this.startServer();
|
||||
};
|
||||
|
||||
@ -341,14 +344,8 @@ Pool.prototype._startTimer = function _startTimer() {
|
||||
return;
|
||||
|
||||
// Chain is full and up-to-date
|
||||
if (self.chain.isFull()) {
|
||||
self._stopTimer();
|
||||
self._stopInterval();
|
||||
self.synced = true;
|
||||
self.emit('full');
|
||||
utils.debug('Chain is fully synced (height=%d).', self.chain.height);
|
||||
if (self.chain.isFull())
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.peers.load) {
|
||||
self.peers.load.destroy();
|
||||
@ -376,6 +373,10 @@ Pool.prototype._startInterval = function _startInterval() {
|
||||
if (!self.syncing)
|
||||
return;
|
||||
|
||||
// Chain is full and up-to-date
|
||||
if (self.chain.isFull())
|
||||
return;
|
||||
|
||||
utils.debug('Stall recovery: loading again.');
|
||||
// self._load();
|
||||
}
|
||||
@ -889,7 +890,7 @@ Pool.prototype._handleTX = function _handleTX(tx, peer, callback) {
|
||||
return callback(err);
|
||||
|
||||
addMempool(tx, peer, function(err) {
|
||||
if (err && self.synced)
|
||||
if (err)
|
||||
utils.debug('Mempool error: %s', err.message);
|
||||
|
||||
if (updated || tx.block)
|
||||
@ -1649,11 +1650,9 @@ Pool.prototype.getTX = function getTX(hash, range, callback) {
|
||||
};
|
||||
|
||||
Pool.prototype.sendTX = function sendTX(tx, callback) {
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
// Failsafe to avoid getting banned by bitcoind nodes.
|
||||
if (!bcoin.mempool.checkTX(tx))
|
||||
return callback(new Error('CheckTransaction failed.'));
|
||||
return utils.asyncify(callback)(new Error('CheckTransaction failed.'));
|
||||
|
||||
return this.broadcast(tx, callback);
|
||||
};
|
||||
@ -1677,14 +1676,17 @@ Pool.prototype.broadcast = function broadcast(msg, callback) {
|
||||
|
||||
this.inv.list.push(entry);
|
||||
|
||||
this.peers.regular.forEach(function(peer) {
|
||||
this.peers.all.forEach(function(peer) {
|
||||
var result = peer.broadcast(msg);
|
||||
if (!result)
|
||||
return;
|
||||
|
||||
result[0].once('request', function() {
|
||||
e.emit('ack', peer);
|
||||
callback();
|
||||
// Give them a chance to send a reject.
|
||||
setTimeout(function() {
|
||||
callback();
|
||||
}, 100);
|
||||
});
|
||||
|
||||
result[0].once('reject', function(payload) {
|
||||
|
||||
@ -585,7 +585,7 @@ Framer.reject = function reject(details, writer) {
|
||||
p.writeU8(constants.reject[details.ccode] || constants.reject.malformed);
|
||||
p.writeVarString(details.reason || '', 'ascii');
|
||||
if (details.data)
|
||||
p.writeBytes(details.data);
|
||||
p.writeHash(details.data);
|
||||
|
||||
if (!writer)
|
||||
p = p.render();
|
||||
|
||||
@ -1083,6 +1083,8 @@ TX.prototype.inspect = function inspect() {
|
||||
type: this.type,
|
||||
hash: utils.revHex(this.hash('hex')),
|
||||
witnessHash: utils.revHex(this.witnessHash('hex')),
|
||||
size: this.getSize(),
|
||||
virtualSize: this.getVirtualSize(),
|
||||
height: this.height,
|
||||
value: utils.btc(this.getValue()),
|
||||
fee: utils.btc(this.getFee()),
|
||||
|
||||
@ -371,12 +371,14 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
|
||||
prefix + 's/t/'
|
||||
+ input.prevout.hash
|
||||
+ '/' + input.prevout.index,
|
||||
DUMMY);
|
||||
tx.hash());
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
input.output = null;
|
||||
|
||||
// Only add orphans if this input is ours.
|
||||
if (self.options.mapAddress) {
|
||||
if (!address || !map[address].length)
|
||||
@ -385,16 +387,24 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
|
||||
|
||||
self.isSpent(input.prevout.hash, input.prevout.index, function(err, result) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return next(err);
|
||||
|
||||
// Are we double-spending?
|
||||
if (result)
|
||||
return callback(new Error('Transaction is double-spending.'));
|
||||
if (result) {
|
||||
if (!self.options.indexSpent)
|
||||
return next(new Error('Transaction is double-spending.'));
|
||||
return self._removeSpenders(hash, function(err) {
|
||||
if (err)
|
||||
return next(err);
|
||||
batch.clear();
|
||||
self._add(tx, map, callback, true);
|
||||
});
|
||||
}
|
||||
|
||||
// Add orphan, if no parent transaction is yet known
|
||||
self._addOrphan(key, hash, i, function(err, orphans) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return next(err);
|
||||
|
||||
batch.put(prefix + 'o/' + key, orphans);
|
||||
|
||||
@ -515,6 +525,64 @@ TXPool.prototype._add = function add(tx, map, callback, force) {
|
||||
}, true);
|
||||
};
|
||||
|
||||
TXPool.prototype._removeSpenders = function removeSpenders(hash, callback) {
|
||||
var self = this;
|
||||
this.getTX(hash, function(err, tx) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!tx)
|
||||
return callback(new Error('Could not find spender.'));
|
||||
|
||||
if (tx.ts !== 0)
|
||||
return callback(new Error('Transaction is double-spending.'));
|
||||
|
||||
utils.forEachSerial(tx.outputs, function(next, output, i) {
|
||||
self.isSpent(hash, i, function(err, spent) {
|
||||
if (err)
|
||||
return next(err);
|
||||
if (spent)
|
||||
return self._removeSpenders(spent, next);
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return self.lazyRemove(tx, callback, true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
TXPool.prototype._collectSpenders = function _collectSpenders(tx, callback, spenders) {
|
||||
var self = this;
|
||||
var hash = tx.hash('hex');
|
||||
|
||||
if (!spenders)
|
||||
spenders = [];
|
||||
|
||||
utils.forEachSerial(tx.outputs, function(next, output, i) {
|
||||
self.isSpent(hash, i, function(err, spentBy) {
|
||||
if (err)
|
||||
return next(err);
|
||||
if (spentBy) {
|
||||
spenders.push(spentBy);
|
||||
return self.getTX(spentBy, function(err, tx) {
|
||||
if (err)
|
||||
return next(err);
|
||||
if (!tx)
|
||||
return next();
|
||||
return self._removeSpenders(tx, next, spenders);
|
||||
});
|
||||
}
|
||||
next();
|
||||
});
|
||||
}, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
return callback(null, spenders);
|
||||
});
|
||||
};
|
||||
|
||||
TXPool.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) {
|
||||
var self = this;
|
||||
utils.everySerial(tx.inputs, function(input, next) {
|
||||
@ -541,7 +609,7 @@ TXPool.prototype.isSpent = function isSpent(hash, index, callback, checkCoin) {
|
||||
if (err && err.type !== 'NotFoundError')
|
||||
return callback(err);
|
||||
|
||||
return callback(null, !!exists);
|
||||
return callback(null, exists ? exists.toString('hex') : null);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ WalletDB.prototype._init = function _init() {
|
||||
});
|
||||
|
||||
this.tx = new bcoin.txdb('w', this.db, {
|
||||
indexSpent: false,
|
||||
indexSpent: true,
|
||||
indexExtra: true,
|
||||
indexAddress: true,
|
||||
mapAddress: true,
|
||||
@ -438,6 +438,9 @@ WalletDB.prototype.create = function create(options, callback) {
|
||||
}
|
||||
done();
|
||||
} else {
|
||||
if (bcoin.protocol.network.type === 'segnet')
|
||||
options.witness = options.witness !== false;
|
||||
|
||||
options.provider = new Provider(self);
|
||||
wallet = new bcoin.wallet(options);
|
||||
self.saveJSON(wallet.id, wallet.toJSON(), done);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user