add mempool object.
This commit is contained in:
parent
17e25804de
commit
fb8be11eac
@ -56,6 +56,7 @@ bcoin.ramdisk = require('./bcoin/ramdisk');
|
||||
bcoin.chainblock = require('./bcoin/chainblock');
|
||||
bcoin.chaindb = require('./bcoin/chaindb');
|
||||
bcoin.chain = require('./bcoin/chain');
|
||||
bcoin.mempool = require('./bcoin/mempool');
|
||||
bcoin.keypair = require('./bcoin/keypair');
|
||||
bcoin.address = require('./bcoin/address');
|
||||
bcoin.wallet = require('./bcoin/wallet');
|
||||
|
||||
@ -20,6 +20,9 @@ var fs = bcoin.fs;
|
||||
*/
|
||||
|
||||
function ChainBlock(chain, data) {
|
||||
if (!(this instanceof ChainBlock))
|
||||
return new ChainBlock(chain, data);
|
||||
|
||||
this.chain = chain;
|
||||
this.hash = data.hash;
|
||||
this.version = data.version;
|
||||
|
||||
@ -23,7 +23,7 @@ var BLOCK_SIZE = bcoin.chainblock.BLOCK_SIZE;
|
||||
|
||||
function ChainDB(chain, options) {
|
||||
if (!(this instanceof ChainDB))
|
||||
return new ChainDB(chain);
|
||||
return new ChainDB(chain, options);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
264
lib/bcoin/mempool.js
Normal file
264
lib/bcoin/mempool.js
Normal file
@ -0,0 +1,264 @@
|
||||
/**
|
||||
* mempool.js - mempool for bcoin
|
||||
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
|
||||
* https://github.com/indutny/bcoin
|
||||
*/
|
||||
|
||||
var inherits = require('inherits');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var bcoin = require('../bcoin');
|
||||
var bn = require('bn.js');
|
||||
var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
var utils = bcoin.utils;
|
||||
var assert = utils.assert;
|
||||
var fs = bcoin.fs;
|
||||
|
||||
/**
|
||||
* Mempool
|
||||
*/
|
||||
|
||||
function Mempool(pool, options) {
|
||||
if (!(this instanceof Mempool))
|
||||
return new Mempool(pool, options);
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
this.options = options;
|
||||
this.pool = pool;
|
||||
this.storage = bcoin.db;
|
||||
|
||||
this.txs = {};
|
||||
this.prevout = {};
|
||||
this.size = 0;
|
||||
this.count = 0;
|
||||
this.locked = false;
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
Mempool.prototype._init = function _init() {
|
||||
var self = this;
|
||||
|
||||
// Remove now-mined transactions
|
||||
this.pool.on('block', function(block) {
|
||||
block.txs.forEach(function(tx) {
|
||||
var mtx = self.get(tx);
|
||||
if (!mtx)
|
||||
return;
|
||||
|
||||
mtx.ps = 0;
|
||||
mtx.ts = block.ts;
|
||||
mtx.block = block.hash('hex');
|
||||
|
||||
self.remove(mtx);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Mempool.prototype.get = function get(hash) {
|
||||
if (hash instanceof bcoin.tx)
|
||||
hash = hash.hash('hex');
|
||||
return this.txs[hash];
|
||||
};
|
||||
|
||||
Mempool.prototype.getAll = function getAll(hash) {
|
||||
return Object.keys(this.txs).map(function(key) {
|
||||
return this.txs[key];
|
||||
}, this);
|
||||
};
|
||||
|
||||
Mempool.prototype.has = function has(hash) {
|
||||
return !!this.get(hash);
|
||||
};
|
||||
|
||||
Mempool.prototype.add = function add(tx, peer, callback) {
|
||||
var self = this;
|
||||
var hash = tx.hash('hex');
|
||||
|
||||
assert(tx.ts === 0);
|
||||
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
if (this.locked)
|
||||
return callback(new Error('Mempool is locked.'));
|
||||
|
||||
if (this.count >= 50000)
|
||||
return callback(new Error('Mempool is full.'));
|
||||
|
||||
if (this.size >= 20 * 1024 * 1024)
|
||||
return callback(new Error('Mempool is full.'));
|
||||
|
||||
if (this.txs[hash])
|
||||
return callback(new Error('Already have TX.'));
|
||||
|
||||
this._lockTX(tx);
|
||||
|
||||
this.storage.fillTX(tx, function(err) {
|
||||
var i, input, dup, height, ts, priority;
|
||||
|
||||
self._unlockTX(tx);
|
||||
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
if (!tx.hasPrevout()) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('Previous outputs not found.'));
|
||||
}
|
||||
|
||||
if (!tx.isStandard()) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX is not standard.'));
|
||||
}
|
||||
|
||||
if (!tx.isStandardInputs()) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX inputs are not standard.'));
|
||||
}
|
||||
|
||||
if (tx.getOutputValue().cmp(tx.getInputValue()) > 0) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX is spending coins that it does not have.'));
|
||||
}
|
||||
|
||||
height = self.pool.chain.height() + 1;
|
||||
ts = utils.now();
|
||||
if (!tx.isFinal(height, ts)) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX is not final.'));
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
if (input.output.spent) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX is spending old outputs.'));
|
||||
}
|
||||
dup = self.prevout[input.prevout.hash];
|
||||
if (dup) {
|
||||
// Replace-by-fee
|
||||
if (input.sequence === 0xffffffff - 1) {
|
||||
if (dup.getFee().cmp(tx.getFee()) < 0) {
|
||||
self.remove(dup);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX is double spending.'));
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
if (output.value.cmpn(0) < 0) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX is spending negative coins.'));
|
||||
}
|
||||
}
|
||||
|
||||
if (!tx.verify(true)) {
|
||||
pool.setMisbehavior(peer, 100);
|
||||
return callback(new Error('TX did not verify.'));
|
||||
}
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
self.prevout[input.prevout.hash] = tx;
|
||||
}
|
||||
|
||||
// Possibly do something bitcoinxt-like here with priority
|
||||
priority = tx.getPriority();
|
||||
|
||||
self.txs[hash] = tx;
|
||||
self.count++;
|
||||
self.size += tx.getSize();
|
||||
|
||||
self.storage.saveMempoolTX(tx, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Lock a tx to prevent race conditions
|
||||
Mempool.prototype._lockTX = function _lockTX(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, input;
|
||||
|
||||
if (!this.txs[hash])
|
||||
this.txs[hash] = tx;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
if (!this.prevout[input.prevout.hash])
|
||||
this.prevout[input.prevout.hash] = tx;
|
||||
}
|
||||
};
|
||||
|
||||
Mempool.prototype._unlockTX = function _unlockTX(tx) {
|
||||
var hash = tx.hash('hex');
|
||||
var i, input;
|
||||
|
||||
if (this.txs[hash] === tx)
|
||||
delete this.txs[hash];
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
if (this.prevout[input.prevout.hash] === tx)
|
||||
delete this.prevout[input.prevout.hash];
|
||||
}
|
||||
};
|
||||
|
||||
Mempool.prototype.remove = function remove(hash, callback) {
|
||||
var self = this;
|
||||
var tx, input;
|
||||
|
||||
callback = utils.asyncify(callback);
|
||||
|
||||
if (hash instanceof bcoin.tx)
|
||||
hash = hash.hash('hex');
|
||||
|
||||
tx = this.txs[hash];
|
||||
|
||||
if (!tx)
|
||||
return callback(new Error('TX does not exist in mempool.'));
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
if (this.prevout[input.prevout.hash] === tx)
|
||||
delete this.prevout[input.prevout.hash];
|
||||
}
|
||||
|
||||
delete this.txs[hash];
|
||||
|
||||
this.count--;
|
||||
this.size -= tx.getSize();
|
||||
|
||||
this.storage.removeMempoolTX(tx, function(err) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback();
|
||||
});
|
||||
};
|
||||
|
||||
// Need to lock the mempool when
|
||||
// downloading a new block.
|
||||
Mempool.prototype.lock = function lock() {
|
||||
this.locked = true;
|
||||
};
|
||||
|
||||
Mempool.prototype.unlock = function unlock() {
|
||||
this.locked = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Mempool;
|
||||
Loading…
Reference in New Issue
Block a user