add packet objects.
This commit is contained in:
parent
09e5d93a15
commit
5773d5b445
@ -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'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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 = {};
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -11,3 +11,4 @@ exports.constants = require('./constants');
|
||||
exports.network = require('./network');
|
||||
exports.framer = require('./framer');
|
||||
exports.parser = require('./parser');
|
||||
exports.packets = require('./packets');
|
||||
|
||||
1025
lib/bcoin/protocol/packets.js
Normal file
1025
lib/bcoin/protocol/packets.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
*/
|
||||
|
||||
@ -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'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
|
||||
@ -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();
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user