add packet objects.

This commit is contained in:
Christopher Jeffrey 2016-06-22 11:44:43 -07:00
parent 09e5d93a15
commit 5773d5b445
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
16 changed files with 1248 additions and 836 deletions

View File

@ -11,6 +11,7 @@ var bcoin = require('./env');
var constants = bcoin.protocol.constants;
var utils = bcoin.utils;
var assert = utils.assert;
var InvItem = bcoin.packets.InvItem;
/**
* The class which all block-like objects inherit from.
@ -237,10 +238,7 @@ AbstractBlock.prototype.__defineGetter__('rhash', function() {
*/
AbstractBlock.prototype.toInv = function toInv() {
return {
type: constants.inv.BLOCK,
hash: this.hash('hex')
};
return new InvItem(constants.inv.BLOCK, this.hash('hex'));
};
/**

View File

@ -362,7 +362,7 @@ Address.fromScript = function fromScript(script) {
*/
Address.prototype.fromHash = function fromHash(hash, type, version, network) {
var p, prefix, nversion;
var prefix, nversion;
if (typeof hash === 'string')
hash = new Buffer(hash, 'hex');

View File

@ -14,6 +14,7 @@ var utils = require('./utils');
var assert = utils.assert;
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
var InvItem = bcoin.packets.InvItem;
/**
* Represents an entry in the chain. Unlike
@ -505,10 +506,7 @@ ChainEntry.prototype.toHeaders = function toHeaders() {
*/
ChainEntry.prototype.toInv = function toInv() {
return {
type: constants.inv.BLOCK,
hash: this.hash
};
return new InvItem(constants.inv.BLOCK, this.hash);
};
/**

View File

@ -142,6 +142,7 @@ function Environment(options) {
this.uri = require('./uri');
this.protocol = require('./protocol');
this.packets = this.protocol.packets;
this.network = require('./network');
this.errors = require('./errors');
this.ldb = require('./ldb');
@ -182,7 +183,6 @@ function Environment(options) {
this.walletdb = require('./walletdb');
this.provider = this.walletdb.Provider;
this.peer = require('./peer');
this.networkaddress = this.peer.NetworkAddress;
this.pool = require('./pool');
this.miner = require('./miner');
this.minerblock = this.miner.MinerBlock;

View File

@ -472,13 +472,18 @@ Input.prototype.inspect = function inspect() {
*/
Input.prototype.toJSON = function toJSON() {
var address = this.getAddress();
if (address)
address = address.toBase58();
return {
prevout: this.prevout.toJSON(),
coin: this.coin ? this.coin.toJSON() : null,
script: this.script.toJSON(),
witness: this.witness.toJSON(),
sequence: this.sequence,
address: this.getAddress().toBase58()
address: address
};
};

View File

@ -965,7 +965,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
var index = 0;
var tx = this.clone();
var outputValue = tx.getOutputValue();
var tryFree, i, size, change, fee, min, output;
var tryFree, size, change, fee;
if (!options)
options = {};

View File

@ -164,10 +164,15 @@ Output.prototype.inspect = function inspect() {
*/
Output.prototype.toJSON = function toJSON() {
var address = this.getAddress();
if (address)
address = address.toBase58();
return {
value: utils.btc(this.value),
script: this.script.toJSON(),
address: this.getAddress().toBase58()
address: address
};
};

View File

@ -13,6 +13,11 @@ var utils = require('./utils');
var IP = require('./ip');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var InvItem = bcoin.packets.InvItem;
var VersionPacket = bcoin.packets.VersionPacket;
var GetBlocksPacket = bcoin.packets.GetBlocksPacket;
var RejectPacket = bcoin.packets.RejectPacket;
var NetworkAddress = bcoin.packets.NetworkAddress;
/**
* Represents a remote peer.
@ -288,17 +293,7 @@ Peer.prototype._onConnect = function _onConnect() {
});
// Say hello.
this.write(this.framer.version({
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
remote: new NetworkAddress(),
local: this.pool.address,
nonce: this.pool.localNonce,
agent: constants.USER_AGENT,
height: this.chain.height,
relay: this.options.relay,
}));
this.sendVersion();
// Advertise our address.
if (this.pool.address.host !== '0.0.0.0'
@ -402,8 +397,7 @@ Peer.prototype.announce = function announce(items) {
*/
Peer.prototype.sendInv = function sendInv(items) {
var inv = [];
var i, item, chunk;
var i, chunk;
if (this.destroyed)
return;
@ -411,20 +405,17 @@ Peer.prototype.sendInv = function sendInv(items) {
if (!Array.isArray(items))
items = [items];
for (i = 0; i < items.length; i++) {
item = items[i];
this.invFilter.add(item.hash, 'hex');
inv.push(item);
}
for (i = 0; i < items.length; i++)
this.invFilter.add(items[i].hash, 'hex');
if (inv.length === 0)
if (items.length === 0)
return;
bcoin.debug('Serving %d inv items to %s.',
inv.length, this.hostname);
items.length, this.hostname);
for (i = 0; i < inv.length; i += 50000) {
chunk = inv.slice(i, i + 50000);
for (i = 0; i < items.length; i += 50000) {
chunk = items.slice(i, i + 50000);
this.write(this.framer.inv(chunk));
}
};
@ -435,8 +426,7 @@ Peer.prototype.sendInv = function sendInv(items) {
*/
Peer.prototype.sendHeaders = function sendHeaders(items) {
var headers = [];
var i, item, chunk;
var i, chunk;
if (this.destroyed)
return;
@ -444,24 +434,41 @@ Peer.prototype.sendHeaders = function sendHeaders(items) {
if (!Array.isArray(items))
items = [items];
for (i = 0; i < items.length; i++) {
item = items[i];
this.invFilter.add(item.hash());
headers.push(item);
}
for (i = 0; i < items.length; i++)
this.invFilter.add(items[i].hash());
if (headers.length === 0)
if (items.length === 0)
return;
bcoin.debug('Serving %d headers to %s.',
headers.length, this.hostname);
items.length, this.hostname);
for (i = 0; i < headers.length; i += 2000) {
chunk = headers.slice(i, i + 2000);
for (i = 0; i < items.length; i += 2000) {
chunk = items.slice(i, i + 2000);
this.write(this.framer.headers(chunk));
}
};
/**
* Send a `version` packet.
*/
Peer.prototype.sendVersion = function sendVersion() {
var packet = new VersionPacket({
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
recv: new NetworkAddress(),
from: this.pool.address,
nonce: this.pool.localNonce,
agent: constants.USER_AGENT,
height: this.chain.height,
relay: this.options.relay
});
this.write(this.framer.version(packet));
};
/**
* Send a `ping` packet.
*/
@ -678,7 +685,17 @@ Peer.prototype.response = function response(cmd, payload) {
*/
Peer.prototype.getData = function getData(items) {
this.write(this.framer.getData(items));
var data = new Array(items.length);
var i, item;
for (i = 0; i < items.length; i++) {
item = items[i];
if (item.toInv)
item = item.toInv();
data[i] = item;
}
this.write(this.framer.getData(data));
};
/**
@ -1121,7 +1138,7 @@ Peer.prototype._handleGetHeaders = function _handleGetHeaders(payload) {
});
}
if (!payload.locator)
if (payload.locator.length === 0)
return collect(null, payload.stop);
this.chain.findLocator(payload.locator, function(err, hash) {
@ -1185,7 +1202,7 @@ Peer.prototype._handleGetBlocks = function _handleGetBlocks(payload) {
if (!hash)
return done();
blocks.push({ type: constants.inv.BLOCK, hash: hash });
blocks.push(new InvItem(constants.inv.BLOCK, hash));
if (hash === payload.stop)
return done();
@ -1207,27 +1224,25 @@ Peer.prototype._handleGetBlocks = function _handleGetBlocks(payload) {
* @param {Object}
*/
Peer.prototype._handleVersion = function _handleVersion(payload) {
Peer.prototype._handleVersion = function _handleVersion(version) {
var self = this;
var version = payload.version;
var services = payload.services;
if (this.network.type !== 'regtest') {
if (payload.nonce.cmp(this.pool.localNonce) === 0) {
if (version.nonce.cmp(this.pool.localNonce) === 0) {
this._error('We connected to ourself. Oops.');
this.setMisbehavior(100);
return;
}
}
if (version < constants.MIN_VERSION) {
if (version.version < constants.MIN_VERSION) {
this._error('Peer doesn\'t support required protocol version.');
this.setMisbehavior(100);
return;
}
if (this.options.headers) {
if (version < 31800) {
if (!version.hasHeaders()) {
this._error('Peer doesn\'t support getheaders.');
this.setMisbehavior(100);
return;
@ -1235,7 +1250,7 @@ Peer.prototype._handleVersion = function _handleVersion(payload) {
}
if (this.options.network) {
if (!(services & constants.services.NETWORK)) {
if (!version.hasNetwork()) {
this._error('Peer does not support network services.');
this.setMisbehavior(100);
return;
@ -1243,7 +1258,7 @@ Peer.prototype._handleVersion = function _handleVersion(payload) {
}
if (this.options.spv) {
if (version < 70011 || !(services & constants.services.BLOOM)) {
if (!version.hasBloom()) {
this._error('Peer does not support bip37.');
this.setMisbehavior(100);
return;
@ -1251,7 +1266,7 @@ Peer.prototype._handleVersion = function _handleVersion(payload) {
}
if (this.options.witness) {
if (!(services & constants.services.WITNESS)) {
if (!version.hasWitness()) {
this.request('havewitness', function(err) {
if (err) {
self._error('Peer does not support segregated witness.');
@ -1261,16 +1276,16 @@ Peer.prototype._handleVersion = function _handleVersion(payload) {
}
}
if (payload.witness)
if (version.hasWitness())
this.haveWitness = true;
if (payload.relay === false)
if (version.relay === false)
this.relay = false;
// ACK
this.write(this.framer.verack());
this.version = payload;
this.fire('version', payload);
this.version = version;
this.fire('version', version);
};
/**
@ -1310,7 +1325,7 @@ Peer.prototype._handleMempool = function _handleMempool() {
return done(err);
for (i = 0; i < hashes.length; i++)
items.push({ type: constants.inv.TX, hash: hashes[i] });
items.push(new InvItem(constants.inv.TX, hashes[i]));
bcoin.debug('Sending mempool snapshot (%s).', self.hostname);
@ -1396,7 +1411,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
if (type === constants.inv.TX) {
if (!self.mempool) {
notfound.push({ type: constants.inv.TX, hash: hash });
notfound.push(new InvItem(constants.inv.TX, hash));
return next();
}
return self.mempool.getEntry(hash, function(err, entry) {
@ -1430,11 +1445,11 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
if (type === constants.inv.BLOCK) {
if (self.chain.db.options.spv) {
notfound.push({ type: constants.inv.BLOCK, hash: hash });
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
if (self.chain.db.options.prune) {
notfound.push({ type: constants.inv.BLOCK, hash: hash });
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
return self.chain.db.getBlock(hash, function(err, block) {
@ -1442,7 +1457,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
return next(err);
if (!block) {
notfound.push({ type: constants.inv.BLOCK, hash: hash });
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
@ -1453,10 +1468,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
self.write(data);
if (hash === self.hashContinue) {
self.sendInv({
type: constants.inv.BLOCK,
hash: self.chain.tip.hash
});
self.sendInv(new InvItem(constants.inv.BLOCK, self.chain.tip.hash));
self.hashContinue = null;
}
@ -1466,11 +1478,11 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
if (type === constants.inv.FILTERED_BLOCK) {
if (self.chain.db.options.spv) {
notfound.push({ type: constants.inv.BLOCK, hash: hash });
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
if (self.chain.db.options.prune) {
notfound.push({ type: constants.inv.BLOCK, hash: hash });
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
return self.chain.db.getBlock(hash, function(err, block) {
@ -1478,7 +1490,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
return next(err);
if (!block) {
notfound.push({ type: constants.inv.BLOCK, hash: hash });
notfound.push(new InvItem(constants.inv.BLOCK, hash));
return next();
}
@ -1497,10 +1509,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
}
if (hash === self.hashContinue) {
self.sendInv({
type: constants.inv.BLOCK,
hash: self.chain.tip.hash
});
self.sendInv(new InvItem(constants.inv.BLOCK, self.chain.tip.hash));
self.hashContinue = null;
}
@ -1508,7 +1517,7 @@ Peer.prototype._handleGetData = function _handleGetData(items) {
});
}
notfound.push({ type: type, hash: hash });
notfound.push(new InvItem(type, hash));
return next();
}, function(err) {
@ -1725,24 +1734,21 @@ Peer.prototype._handleReject = function _handleReject(payload) {
* @param {Object}
*/
Peer.prototype._handleAlert = function _handleAlert(details) {
this.invFilter.add(details.hash, 'hex');
this.fire('alert', details);
Peer.prototype._handleAlert = function _handleAlert(alert) {
this.invFilter.add(alert.hash());
this.fire('alert', alert);
};
/**
* Send an `alert` to peer.
* @param {AlertPacket} details
* @param {Buffer|KeyPair} [key=network.alertPrivateKey]
* @param {AlertPacket} alert
*/
Peer.prototype.sendAlert = function sendAlert(details, key) {
var data = bcoin.protocol.framer.alert(details, key);
if (!this.invFilter.added(details.hash, 'hex'))
Peer.prototype.sendAlert = function sendAlert(alert) {
if (!this.invFilter.added(alert.hash()))
return;
this.write(this.framer.packet('alert', data));
this.write(this.framer.alert(alert));
};
/**
@ -1753,6 +1759,8 @@ Peer.prototype.sendAlert = function sendAlert(details, key) {
*/
Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
var packet = new GetBlocksPacket(locator, stop);
bcoin.debug(
'Requesting headers packet from peer with getheaders (%s).',
this.hostname);
@ -1762,7 +1770,7 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
locator && locator.length ? utils.revHex(locator[0]) : 0,
stop ? utils.revHex(stop) : 0);
this.write(this.framer.getHeaders({ locator: locator, stop: stop }));
this.write(this.framer.getHeaders(packet));
};
/**
@ -1772,6 +1780,8 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
*/
Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
var packet = new GetBlocksPacket(locator, stop);
bcoin.debug(
'Requesting inv packet from peer with getblocks (%s).',
this.hostname);
@ -1781,7 +1791,7 @@ Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
locator && locator.length ? utils.revHex(locator[0]) : 0,
stop ? utils.revHex(stop) : 0);
this.write(this.framer.getBlocks({ locator: locator, stop: stop }));
this.write(this.framer.getBlocks(packet));
};
/**
@ -1798,15 +1808,27 @@ Peer.prototype.sendMempool = function sendMempool() {
/**
* Send `reject` to peer.
* @param {Object} details - See {@link Framer.reject}.
* @param {Object} reject - See {@link Framer.reject}.
*/
Peer.prototype.sendReject = function sendReject(details) {
Peer.prototype.sendReject = function sendReject(code, reason, obj) {
var reject = RejectPacket.fromReason(code, reason, obj);
if (obj) {
bcoin.debug('Rejecting %s %s (%s): ccode=%s reason=%s.',
reject.message, obj.rhash, this.hostname, code, reason);
this.pool.rejects.add(obj.hash());
} else {
bcoin.debug('Rejecting packet from %s: ccode=%s reason=%s.',
this.hostname, code, reason);
}
bcoin.debug(
'Sending reject packet to peer (%s).',
this.hostname);
this.write(this.framer.reject(details));
this.write(this.framer.reject(reject));
};
/**
@ -1837,34 +1859,8 @@ Peer.prototype.setMisbehavior = function setMisbehavior(score) {
*/
Peer.prototype.reject = function reject(obj, code, reason, score) {
var type;
if (obj) {
type = (obj instanceof bcoin.tx) ? 'tx' : 'block';
bcoin.debug('Rejecting %s %s (%s): ccode=%s reason=%s.',
type, obj.rhash, this.hostname, code, reason);
this.sendReject({
message: type,
ccode: code,
reason: reason,
data: obj.hash()
});
this.pool.rejects.add(obj.hash());
} else {
bcoin.debug('Rejecting packet from %s: ccode=%s reason=%s.',
this.hostname, code, reason);
this.sendReject({
ccode: code,
reason: reason,
data: null
});
}
if (score != null)
this.sendReject(code, reason, obj);
if (score > 0)
this.setMisbehavior(score);
};
@ -1994,224 +1990,6 @@ Peer.prototype.inspect = function inspect() {
+ '>';
};
/**
* Represents a network address.
* @exports NetworkAddress
* @constructor
* @param {NakedNetworkAddress} options
* @property {Number} id
* @property {Host} host
* @property {Number} port
* @property {Number} services
* @property {Number} ts
*/
function NetworkAddress(options) {
if (!(this instanceof NetworkAddress))
return new NetworkAddress(options);
this.id = NetworkAddress.uid++;
this.host = '0.0.0.0';
this.port = 0;
this.services = 0;
this.ts = 0;
if (options)
this.fromOptions(options);
}
/**
* Globally incremented unique id.
* @private
* @type {Number}
*/
NetworkAddress.uid = 0;
/**
* Inject properties from options object.
* @private
* @param {Object} options
*/
NetworkAddress.prototype.fromOptions = function fromOptions(options) {
var host = options.host;
if (IP.version(host) !== -1)
host = IP.normalize(host);
assert(typeof options.host === 'string');
assert(typeof options.port === 'number');
assert(typeof options.services === 'number');
assert(typeof options.ts === 'number');
this.host = host;
this.port = options.port;
this.services = options.services;
this.ts = options.ts;
return this;
};
/**
* Instantiate network address from options.
* @param {Object} options
* @returns {NetworkAddress}
*/
NetworkAddress.fromOptions = function fromOptions(options) {
return new NetworkAddress().fromOptions(options);
};
/**
* Test whether the `host` field is an ip address.
* @returns {Boolean}
*/
NetworkAddress.prototype.isIP = function isIP() {
return IP.version(this.host) !== -1;
};
/**
* Test whether the NETWORK service bit is set.
* @returns {Boolean}
*/
NetworkAddress.prototype.hasNetwork = function hasNetwork() {
return (this.services & constants.services.NETWORK) !== 0;
};
/**
* Test whether the BLOOM service bit is set.
* @returns {Boolean}
*/
NetworkAddress.prototype.hasBloom = function hasBloom() {
return (this.services & constants.services.BLOOM) !== 0;
};
/**
* Test whether the GETUTXO service bit is set.
* @returns {Boolean}
*/
NetworkAddress.prototype.hasUTXO = function hasUTXO() {
return (this.services & constants.services.GETUTXO) !== 0;
};
/**
* Test whether the WITNESS service bit is set.
* @returns {Boolean}
*/
NetworkAddress.prototype.hasWitness = function hasWitness() {
return (this.services & constants.services.WITNESS) !== 0;
};
/**
* Inspect the network address.
* @returns {Object}
*/
NetworkAddress.prototype.inspect = function inspect() {
return '<NetworkAddress:'
+ ' id=' + this.id
+ ' hostname=' + IP.hostname(this.host, this.port)
+ ' services=' + this.services.toString(2)
+ ' date=' + utils.date(this.ts)
+ '>';
};
/**
* Inject properties from hostname and network.
* @private
* @param {String} hostname
* @param {Network|NetworkType} network
*/
NetworkAddress.prototype.fromHostname = function fromHostname(hostname, network) {
var address = IP.parseHost(hostname);
network = bcoin.network.get(network);
this.host = address.host;
this.port = address.port || network.port;
this.services = constants.services.NETWORK
| constants.services.BLOOM
| constants.services.WITNESS;
this.ts = bcoin.now();
return this;
};
/**
* Instantiate a network address
* from a hostname (i.e. 127.0.0.1:8333).
* @param {String} hostname
* @param {(Network|NetworkType)?} network
* @returns {NetworkAddress}
*/
NetworkAddress.fromHostname = function fromHostname(hostname, network) {
return new NetworkAddress().fromHostname(hostname, network);
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
* @param {Boolean?} full - Include timestamp.
*/
NetworkAddress.prototype.fromRaw = function fromRaw(data, full) {
var p = bcoin.reader(data);
var now = bcoin.now();
// only version >= 31402
this.ts = full ? p.readU32() : 0;
this.services = p.readU53();
this.host = IP.toString(p.readBytes(16));
this.port = p.readU16BE();
if (this.ts <= 100000000 || this.ts > now + 10 * 60)
this.ts = now - 5 * 24 * 60 * 60;
return this;
};
/**
* Insantiate a network address from serialized data.
* @param {Buffer} data
* @param {Boolean?} full - Include timestamp.
* @returns {NetworkAddress}
*/
NetworkAddress.fromRaw = function fromRaw(data, full) {
return new NetworkAddress().fromRaw(data, full);
};
/**
* Serialize network address.
* @param {Boolean} full - Include timestamp.
* @returns {Buffer}
*/
NetworkAddress.prototype.toRaw = function toRaw(full, writer) {
var p = bcoin.writer(writer);
if (full)
p.writeU32(this.ts);
p.writeU64(this.services);
p.writeBytes(IP.toBuffer(this.host));
p.writeU16BE(this.port);
if (!writer)
p = p.render();
return p;
};
/*
* Helpers
*/
@ -2224,7 +2002,4 @@ function compare(a, b) {
* Expose
*/
exports = Peer;
exports.NetworkAddress = NetworkAddress;
module.exports = exports;
module.exports = Peer;

View File

@ -14,7 +14,8 @@ var IP = require('./ip');
var assert = utils.assert;
var constants = bcoin.protocol.constants;
var VerifyError = bcoin.errors.VerifyError;
var NetworkAddress = bcoin.peer.NetworkAddress;
var NetworkAddress = bcoin.packets.NetworkAddress;
var InvItem = bcoin.packets.InvItem;
/**
* A pool of peers for handling all network activity.
@ -887,21 +888,20 @@ Pool.prototype.sendMempool = function sendMempool() {
/**
* Send `alert` to all peers.
* @param {AlertPacket} details
* @param {Buffer|KeyPair} [key=network.alertPrivateKey]
* @param {AlertPacket} alert
*/
Pool.prototype.sendAlert = function sendAlert(details, key) {
Pool.prototype.sendAlert = function sendAlert(alert) {
var i;
if (this.peers.load)
this.peers.load.sendAlert(details, key);
this.peers.load.sendAlert(alert);
for (i = 0; i < this.peers.regular.length; i++)
this.peers.regular[i].sendAlert(details, key);
this.peers.regular[i].sendAlert(alert);
for (i = 0; i < this.peers.leeches.length; i++)
this.peers.leeches[i].sendAlert(details, key);
this.peers.leeches[i].sendAlert(alert);
};
/**
@ -1012,8 +1012,8 @@ Pool.prototype._createPeer = function _createPeer(options) {
self.emit('reject', payload, peer);
});
peer.on('alert', function(details) {
self._handleAlert(details, peer);
peer.on('alert', function(alert) {
self._handleAlert(alert, peer);
});
peer.on('notfound', function(items) {
@ -1120,38 +1120,36 @@ Pool.prototype._createPeer = function _createPeer(options) {
/**
* Handle an alert packet.
* @private
* @param {AlertPacket} details
* @param {AlertPacket} alert
* @param {Peer} peer
*/
Pool.prototype._handleAlert = function _handleAlert(details, peer) {
var hash = new Buffer(details.hash, 'hex');
var signature = details.signature;
Pool.prototype._handleAlert = function _handleAlert(alert, peer) {
var now = bcoin.now();
if (!this.rejects.added(hash))
if (!this.rejects.added(alert.hash()))
return;
if (!bcoin.ec.verify(hash, signature, this.network.alertKey)) {
if (!alert.verify(this.network.alertKey)) {
bcoin.debug('Peer sent a phony alert packet (%s).', peer.hostname);
// Let's look at it because why not?
bcoin.debug(details);
bcoin.debug(alert);
peer.setMisbehavior(100);
return;
}
if (now >= details.relayUntil || now >= details.expiration) {
if (now >= alert.relayUntil || now >= alert.expiration) {
bcoin.debug('Peer sent an expired alert packet (%s).', peer.hostname);
bcoin.debug(details);
bcoin.debug(alert);
return;
}
bcoin.debug('Received alert from peer (%s).', peer.hostname);
bcoin.debug(details);
bcoin.debug(alert);
this.sendAlert(details);
this.sendAlert(alert);
this.emit('alert', details, peer);
this.emit('alert', alert, peer);
};
/**
@ -2111,6 +2109,15 @@ LoadRequest.prototype.inspect = function inspect() {
+ '>';
};
/**
* Convert load request to an inv item.
* @returns {InvItem}
*/
LoadRequest.prototype.toInv = function toInv() {
return new InvItem(this.type, this.hash);
};
/**
* Represents an item that is broadcasted via an inv/getdata cycle.
* @exports BroadcastItem
@ -2256,7 +2263,7 @@ BroadcastItem.prototype.send = function send(peer, witness) {
// Failsafe - we never want to relay coinbases.
// They are an insta-ban from any bitcoind node.
if (this.msg.isCoinbase()) {
peer.write(peer.framer.notFound([this]));
peer.write(peer.framer.notFound([this.toInv()]));
bcoin.debug('Failsafe: tried to relay a coinbase.');
this.finish(new Error('Coinbase.'));
return true;
@ -2317,6 +2324,7 @@ BroadcastItem.prototype.reject = function reject(peer) {
/**
* Inspect the broadcast item.
* @returns {String}
*/
BroadcastItem.prototype.inspect = function inspect() {
@ -2327,6 +2335,15 @@ BroadcastItem.prototype.inspect = function inspect() {
+ '>';
};
/**
* Convert broadcast item to an inv item.
* @returns {InvItem}
*/
BroadcastItem.prototype.toInv = function toInv() {
return new InvItem(this.type, this.hash);
};
/*
* Helpers
*/

View File

@ -8,7 +8,6 @@
'use strict';
var bcoin = require('../env');
var constants = require('./constants');
var utils = require('../utils');
var assert = utils.assert;
var BufferWriter = require('../writer');
@ -391,9 +390,8 @@ Framer.prototype.addr = function addr(peers) {
* @returns {Buffer} alert packet.
*/
Framer.prototype.alert = function alert(options) {
options.network = this.network;
return this.packet('alert', Framer.alert(options));
Framer.prototype.alert = function _alert(alert) {
return this.packet('alert', Framer.alert(alert));
};
/**
@ -413,23 +411,8 @@ Framer.prototype.feeFilter = function feeFilter(options) {
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.version = function version(options, writer) {
var p = new BufferWriter(writer);
p.write32(options.version);
p.writeU64(options.services);
p.write64(options.ts);
options.remote.toRaw(false, p);
options.local.toRaw(false, p);
p.writeU64(options.nonce);
p.writeVarString(options.agent, 'ascii');
p.write32(options.height || 0);
p.writeU8(options.relay ? 1 : 0);
if (!writer)
p = p.render();
return p;
Framer.version = function _version(version, writer) {
return version.toRaw(writer);
};
/**
@ -441,7 +424,7 @@ Framer.verack = function verack() {
return DUMMY;
};
/**
/**
* Create an inv, getdata, or notfound packet.
* @private
* @param {InvItem[]} items
@ -450,21 +433,14 @@ Framer.verack = function verack() {
Framer._inv = function _inv(items, writer) {
var p = new BufferWriter(writer);
var type;
var i;
assert(items.length <= 50000);
p.writeVarint(items.length);
for (i = 0; i < items.length; i++) {
type = items[i].type;
if (typeof type === 'string')
type = constants.inv[items[i].type.toUpperCase()];
assert(constants.invByVal[type] != null);
p.writeU32(type);
p.writeHash(items[i].hash);
}
for (i = 0; i < items.length; i++)
items[i].toRaw(p);
if (!writer)
p = p.render();
@ -550,7 +526,7 @@ Framer.filterLoad = function filterLoad(filter, writer) {
*/
Framer.getHeaders = function getHeaders(data, writer) {
return Framer._getBlocks(data, writer, true);
return data.toRaw(writer);
};
/**
@ -561,51 +537,7 @@ Framer.getHeaders = function getHeaders(data, writer) {
*/
Framer.getBlocks = function getBlocks(data, writer) {
return Framer._getBlocks(data, writer, false);
};
/**
* Create a getblocks or getheaders packet.
* @private
* @param {GetBlocksPacket} data
* @param {BufferWriter|null} writer
* @param {Boolean} headers
* @returns {Buffer}
*/
Framer._getBlocks = function _getBlocks(data, writer, headers) {
var version = data.version;
var locator = data.locator;
var stop = data.stop;
var p, i;
if (!version)
version = constants.VERSION;
if (!locator) {
if (headers)
locator = [];
else
assert(false, 'getblocks requires a locator');
}
if (!stop)
stop = constants.ZERO_HASH;
p = new BufferWriter(writer);
p.writeU32(version);
p.writeVarint(locator.length);
for (i = 0; i < locator.length; i++)
p.writeHash(locator[i]);
p.writeHash(stop);
if (!writer)
p = p.render();
return p;
return data.toRaw(writer);
};
/**
@ -694,30 +626,8 @@ Framer.headers = function _headers(headers, writer) {
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.reject = function reject(details, writer) {
var p = new BufferWriter(writer);
var ccode = details.ccode;
if (typeof ccode === 'string')
ccode = constants.reject[ccode.toUpperCase()];
if (!ccode)
ccode = constants.reject.INVALID;
if (ccode >= constants.reject.INTERNAL)
ccode = constants.reject.INVALID;
p.writeVarString(details.message || '', 'ascii');
p.writeU8(ccode);
p.writeVarString(details.reason || '', 'ascii');
if (details.data)
p.writeHash(details.data);
if (!writer)
p = p.render();
return p;
Framer.reject = function _reject(reject, writer) {
return reject.toRaw(writer);
};
/**
@ -750,104 +660,8 @@ Framer.addr = function addr(hosts, writer) {
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.alert = function alert(data, key, writer) {
var network = bcoin.network.get(data.network);
var p, i, payload, hash, time;
var version, relayUntil, expiration, id, cancel;
var cancels, minVer, maxVer, subVers;
var priority, comment, statusBar, reserved;
if (!key && network.alertPrivateKey)
key = network.alertPrivateKey;
if (!data.payload) {
p = new BufferWriter();
time = bcoin.now() + 7 * 86400;
version = data.version;
relayUntil = data.relayUntil;
expiration = data.expiration;
id = data.id;
cancels = data.cancels || [];
minVer = data.minVer;
maxVer = data.maxVer;
subVers = data.subVers || [];
priority = data.priority;
comment = data.comment || '';
statusBar = data.statusBar || '';
reserved = data.reserved || '';
if (version == null)
version = 1;
if (relayUntil == null)
relayUntil = time;
if (expiration == null)
expiration = time;
if (id == null)
id = 1;
if (cancel == null)
cancel = 0;
if (minVer == null)
minVer = 10000;
if (maxVer == null)
maxVer = constants.VERSION;
if (priority == null)
priority = 100;
p.write32(version);
p.write64(relayUntil);
p.write64(expiration);
p.write32(id);
p.write32(cancel);
p.writeVarint(cancels.length);
for (i = 0; i < cancels.length; i++)
p.write32(cancels[i]);
p.write32(minVer);
p.write32(maxVer);
p.writeVarint(subVers.length);
for (i = 0; i < subVers.length; i++)
p.writeVarString(subVers[i], 'ascii');
p.write32(priority);
p.writeVarString(comment, 'ascii');
p.writeVarString(statusBar, 'ascii');
p.writeVarString(reserved, 'ascii');
payload = p.render();
} else {
payload = data.payload;
}
if (!data.signature) {
assert(key, 'No key or signature.');
hash = utils.hash256(payload);
data.signature = bcoin.ec.sign(hash, key);
}
if (!data.hash) {
if (!hash)
hash = utils.hash256(payload);
data.hash = hash.toString('hex');
}
p = new BufferWriter(writer);
p.writeVarBytes(payload);
p.writeVarBytes(data.signature);
if (!writer)
p = p.render();
return p;
Framer.alert = function _alert(alert, writer) {
return alert.toRaw(writer);
};
/**
@ -879,7 +693,7 @@ Framer.getAddr = function getAddr() {
Framer.getUTXOs = function getUTXOs(data, writer) {
var p = new BufferWriter(writer);
var i, prevout;
var i;
p.writeU8(data.mempool ? 1 : 0);
p.writeVarint(data.prevout.length);

View File

@ -11,3 +11,4 @@ exports.constants = require('./constants');
exports.network = require('./network');
exports.framer = require('./framer');
exports.parser = require('./parser');
exports.packets = require('./packets');

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,6 @@
'use strict';
var bcoin = require('../env');
var bn = require('bn.js');
var EventEmitter = require('events').EventEmitter;
var utils = require('../utils');
var assert = utils.assert;
@ -101,8 +100,8 @@ Parser.prototype.parse = function parse(chunk) {
return this._error('Packet too large: %dmb.', utils.mb(chunk.length));
}
if (this.packet === null) {
this.packet = this.parseHeader(chunk) || {};
if (!this.packet) {
this.packet = this.parseHeader(chunk);
return;
}
@ -138,7 +137,7 @@ Parser.prototype.parse = function parse(chunk) {
*/
Parser.prototype.parseHeader = function parseHeader(h) {
var i, magic, cmd;
var i, magic, cmd, chk;
magic = h.readUInt32LE(0, true);
@ -160,11 +159,9 @@ Parser.prototype.parseHeader = function parseHeader(h) {
return this._error('Packet length too large: %dmb', utils.mb(this.waiting));
}
return {
cmd: cmd,
length: this.waiting,
checksum: h.readUInt32LE(20, true)
};
chk = h.readUInt32LE(20, true);
return new Packet(cmd, this.waiting, chk);
};
/**
@ -457,56 +454,7 @@ Parser.parsePong = function parsePong(p) {
*/
Parser.parseVersion = function parseVersion(p) {
var version, services, ts, recv, from, nonce, agent, height, relay;
p = new BufferReader(p);
version = p.read32();
services = p.readU53();
ts = p.read53();
recv = bcoin.networkaddress.fromRaw(p, false);
if (p.left() > 0) {
from = bcoin.networkaddress.fromRaw(p, false);
nonce = p.readU64();
} else {
from = {};
nonce = new bn(0);
}
if (p.left() > 0)
agent = p.readVarString('ascii', 256);
else
agent = '';
if (p.left() > 0)
height = p.read32();
else
height = 0;
if (p.left() > 0)
relay = p.readU8() === 1;
else
relay = true;
if (version === 10300)
version = 300;
assert(version >= 0, 'Version is negative.');
assert(ts >= 0, 'Timestamp is negative.');
assert(height >= 0, 'Height is negative.');
return {
version: version,
services: services,
ts: ts,
local: recv,
remote: from,
nonce: nonce,
agent: agent,
height: height,
relay: relay
};
return bcoin.packets.VersionPacket.fromRaw(p);
};
/**
@ -539,30 +487,6 @@ Parser.parseGetData = function parseGetData(p) {
return Parser.parseInv(p);
};
Parser._parseGetBlocks = function _parseGetBlocks(p) {
var version, count, locator, i, stop;
p = new BufferReader(p);
version = p.readU32();
count = p.readVarint();
locator = [];
for (i = 0; i < count; i++)
locator.push(p.readHash('hex'));
stop = p.readHash('hex');
if (stop === constants.NULL_HASH)
stop = null;
return {
version: version,
locator: locator,
stop: stop
};
};
/**
* Parse getblocks packet.
* @param {Buffer|BufferReader} p
@ -570,9 +494,7 @@ Parser._parseGetBlocks = function _parseGetBlocks(p) {
*/
Parser.parseGetBlocks = function parseGetBlocks(p) {
var data = Parser._parseGetBlocks(p);
assert(data.locator.length > 0, 'getblocks requires a locator.');
return data;
return bcoin.packets.GetBlocksPacket.fromRaw(p);
};
/**
@ -582,10 +504,7 @@ Parser.parseGetBlocks = function parseGetBlocks(p) {
*/
Parser.parseGetHeaders = function parseGetHeaders(p) {
var data = Parser._parseGetBlocks(p);
if (data.locator.length === 0)
data.locator = null;
return data;
return bcoin.packets.GetBlocksPacket.fromRaw(p);
};
/**
@ -604,12 +523,8 @@ Parser.parseInv = function parseInv(p) {
assert(count <= 50000, 'Item count too high.');
for (i = 0; i < count; i++) {
items.push({
type: p.readU32(),
hash: p.readHash('hex')
});
}
for (i = 0; i < count; i++)
items.push(bcoin.packets.InvItem.fromRaw(p));
return items;
};
@ -682,25 +597,7 @@ Parser.parseTX = function parseTX(p) {
*/
Parser.parseReject = function parseReject(p) {
var message, ccode, reason, data;
p = new BufferReader(p);
message = p.readVarString('ascii', 12);
ccode = p.readU8();
reason = p.readVarString('ascii', 111);
if (message === 'block' || message === 'tx')
data = p.readHash('hex');
else
data = null;
return {
message: message,
ccode: constants.rejectByVal[ccode] || ccode,
reason: reason,
data: data
};
return bcoin.packets.RejectPacket.fromRaw(p);
};
/**
@ -720,7 +617,7 @@ Parser.parseAddr = function parseAddr(p) {
assert(count <= 10000, 'Too many addresses.');
for (i = 0; i < count; i++)
addrs.push(bcoin.networkaddress.fromRaw(p, true));
addrs.push(bcoin.packets.NetworkAddress.fromRaw(p, true));
return addrs;
};
@ -742,56 +639,7 @@ Parser.parseMempool = function parseMempool(p) {
*/
Parser.parseAlert = function parseAlert(p) {
var version, relayUntil, expiration, id, cancel;
var cancels, count, i, minVer, maxVer, subVers;
var priority, comment, statusBar, reserved;
var payload, signature;
p = new BufferReader(p);
payload = p.readVarBytes();
signature = p.readVarBytes();
p = new BufferReader(payload);
version = p.read32();
relayUntil = p.read53();
expiration = p.read53();
id = p.read32();
cancel = p.read32();
cancels = [];
count = p.readVarint();
for (i = 0; i < count; i++)
cancels.push(p.read32());
minVer = p.read32();
maxVer = p.read32();
subVers = [];
count = p.readVarint();
for (i = 0; i < count; i++)
subVers.push(p.readVarString('ascii'));
priority = p.read32();
comment = p.readVarString('ascii');
statusBar = p.readVarString('ascii');
reserved = p.readVarString('ascii');
return {
hash: utils.hash256(payload).toString('hex'),
version: version,
relayUntil: relayUntil,
expiration: expiration,
id: id,
cancel: cancel,
cancels: cancels,
minVer: minVer,
maxVer: maxVer,
subVers: subVers,
priority: priority,
comment: comment,
statusBar: statusBar,
reserved: reserved,
payload: payload,
signature: signature
};
return bcoin.packets.AlertPacket.fromRaw(p);
};
/**
@ -807,6 +655,19 @@ Parser.parseFeeFilter = function parseFeeFilter(p) {
};
};
/**
* Packet
* @constructor
* @private
*/
function Packet(cmd, size, checksum) {
this.cmd = cmd;
this.size = size;
this.checksum = checksum;
this.payload = null;
}
/*
* Expose
*/

View File

@ -14,6 +14,7 @@ var constants = bcoin.protocol.constants;
var Script = bcoin.script;
var Stack = bcoin.stack;
var BufferWriter = require('./writer');
var InvItem = bcoin.packets.InvItem;
/**
* A static transaction object.
@ -1802,10 +1803,7 @@ TX.prototype.__defineGetter__('wtxid', function() {
*/
TX.prototype.toInv = function toInv() {
return {
type: constants.inv.TX,
hash: this.hash('hex')
};
return new InvItem(constants.inv.TX, this.hash('hex'));
};
/**

View File

@ -13,13 +13,6 @@
* @global
*/
/**
* @typedef {Object} InvItem
* @property {Number|String} type - Inv type. See {@link constants.inv}.
* @property {Hash|Buffer} hash
* @global
*/
/**
* One of {@link module:constants.inv}.
* @typedef {Number|String} InvType
@ -41,29 +34,15 @@
* @global
*/
/**
* @typedef {Object} ParsedAddress
* @property {Number?} version - Witness program version (-1 if not present).
* @property {AddressType} type
* @property {Buffer} hash
* @global
*/
/**
* A bitfield containing locktime flags.
* @typedef {Number} LockFlags
* @global
*/
/**
* A map of addresses ({@link Base58Address} -> value).
* @typedef {Object} AddressMap
* @global
*/
/**
* A map of address hashes ({@link Hash} -> value).
* @typedef {Object} AddressHashMap
* @typedef {Object} AddressMap
* @global
*/
@ -96,8 +75,8 @@
*/
/**
* Hex-string hash.
* @typedef {String} Hash
* Buffer or hex-string hash.
* @typedef {Buffer|String} Hash
* @global
*/
@ -246,38 +225,6 @@
* @global
*/
/**
* @typedef {Object} NakedNetworkAddress
* @property {Number?} ts - Timestamp.
* @property {Number?} services - Service bits.
* @property {String?} host - IP address (IPv6 or IPv4).
* @property {Number?} port - Port.
* @global
*/
/**
* @typedef {Object} VersionPacket
* @property {Number} version - Protocol version.
* @property {Number} services - Service bits.
* @property {Number} ts - Timestamp of discovery.
* @property {NakedNetworkAddress} local - Our address.
* @property {NakedNetworkAddress} remote - Their address.
* @property {BN} nonce
* @property {String} agent - User agent string.
* @property {Number} height - Chain height.
* @property {Boolean} relay - Whether transactions
* should be relayed immediately.
* @global
*/
/**
* @typedef {Object} GetBlocksPacket
* @property {Number} version - Protocol version.
* @property {Hash[]} locator - Chain locator.
* @property {Hash} stop - Hash to stop at.
* @global
*/
/**
* @typedef {Object} NakedBlock
* @property {Number} version - Transaction version. Note that BCoin reads
@ -345,38 +292,7 @@
/**
* @typedef {Object} NakedWitness
* @param {Buffer[]} items - Stack items.
* @global
*/
/**
* @typedef {Object} RejectPacket
* @param {(Number|String)?} ccode - Code
* (see {@link constants.reject}).
* @param {String?} msg - Message.
* @param {String?} reason - Reason.
* @param {(Hash|Buffer)?} data - Transaction or block hash.
* @global
*/
/**
* @typedef {Object} AlertPacket
* @property {Number} version
* @property {Number} relayUntil
* @property {Number} expiration
* @property {Number} id
* @property {Number} cancel
* @property {Number[]} cancels
* @property {Number} minVer
* @property {Number} maxVer
* @property {String[]} subVers
* @property {Number} priority
* @property {String} comment
* @property {String} statusBar
* @property {String?} reserved
* @property {Buffer?} payload - Payload.
* @property {Buffer?} signature - Payload signature.
* @property {Buffer?} key - Private key to sign with.
* @property {Buffer[]} items - Stack items.
* @global
*/

View File

@ -31,7 +31,7 @@ describe('Protocol', function() {
});
}
var v1 = {
var v1 = bcoin.packets.VersionPacket.fromOptions({
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
@ -41,7 +41,7 @@ describe('Protocol', function() {
agent: constants.USER_AGENT,
height: 0,
relay: false
};
});
packetTest('version', v1, function(payload) {
assert.equal(payload.version, constants.VERSION);
@ -50,7 +50,7 @@ describe('Protocol', function() {
assert.equal(payload.relay, false);
});
var v2 = {
var v2 = bcoin.packets.VersionPacket.fromOptions({
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
@ -60,7 +60,7 @@ describe('Protocol', function() {
agent: constants.USER_AGENT,
height: 10,
relay: true
};
});
packetTest('version', v2, function(payload) {
assert.equal(payload.version, constants.VERSION);
@ -205,14 +205,13 @@ describe('Protocol', function() {
var p = new bcoin.reader(alertData);
p.start();
while (p.left()) {
var details = bcoin.protocol.parser.parseAlert(p);
var hash = utils.hash256(details.payload);
var signature = details.signature;
assert(bcoin.ec.verify(hash, signature, network.alertKey));
delete details.payload;
var data = bcoin.protocol.framer.alert(details);
details = bcoin.protocol.parser.parseAlert(data);
assert(bcoin.ec.verify(hash, signature, network.alertKey));
var alert = bcoin.protocol.parser.parseAlert(p);
assert(alert.verify(network.alertKey));
alert._payload = null;
alert._hash = null;
var data = bcoin.protocol.framer.alert(alert);
alert = bcoin.protocol.parser.parseAlert(data);
assert(alert.verify(network.alertKey));
}
p.end();
});