net: refactor framer and parser.

This commit is contained in:
Christopher Jeffrey 2016-08-25 01:35:20 -07:00
parent 40e028f182
commit d663eef37e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 703 additions and 1537 deletions

File diff suppressed because it is too large Load Diff

View File

@ -997,6 +997,240 @@ NetworkAddress.prototype.toRaw = function toRaw(full, writer) {
return p;
};
/**
* Represents a `getutxos` packet.
* @exports GetUTXOsPacket
* @constructor
* @param {Boolean} mempool
* @param {Outpoint[]} prevout
* @property {Boolean} mempool
* @property {Outpoint[]} prevout
*/
function GetUTXOsPacket(mempool, prevout) {
if (!(this instanceof GetUTXOsPacket))
return new GetUTXOsPacket(mempool, prevout);
this.mempool = mempool || false;
this.prevout = prevout || [];
}
/**
* Serialize getutxos packet.
* @returns {Buffer}
*/
GetUTXOsPacket.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
var i;
p.writeU8(this.mempool ? 1 : 0);
p.writeVarint(this.prevout.length);
for (i = 0; i < this.prevout.length; i++)
this.prevout[i].toRaw(p);
if (!writer)
p = p.render();
return p;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
GetUTXOsPacket.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var i, count;
this.mempool = p.readU8() === 1;
count = p.readVarint();
for (i = 0; i < count; i++)
this.prevout.push(bcoin.outpoint.fromRaw(p));
return this;
};
/**
* Instantiate getutxos packet from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {GetBlocksPacket}
*/
GetUTXOsPacket.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new GetUTXOsPacket().fromRaw(data);
};
/**
* Represents a `utxos` packet.
* @exports UTXOsPacket
* @constructor
* @param {Object} options
* @property {Number} height
* @property {Hash} tip
* @property {Boolean[]} hits
* @property {Coin[]} coins
*/
function UTXOsPacket(options) {
if (!(this instanceof UTXOsPacket))
return new UTXOsPacket(options);
this.height = -1;
this.tip = constants.NULL_HASH;
this.hits = [];
this.coins = [];
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options.
* @private
* @param {Buffer} data
*/
UTXOsPacket.prototype.fromOptions = function fromOptions(options) {
if (options.height != null) {
assert(utils.isNumber(options.height));
this.height = options.height;
}
if (options.tip) {
assert(typeof options.tip === 'string');
this.tip = options.tip;
}
if (options.hits) {
assert(Array.isArray(options.hits));
this.hits = options.hits;
}
if (options.coins) {
assert(Array.isArray(options.coins));
this.coins = options.coins;
}
return this;
};
/**
* Instantiate utxos packet from options.
* @param {Buffer} data
* @param {String?} enc
* @returns {GetBlocksPacket}
*/
UTXOsPacket.fromOptions = function fromOptions(options) {
return new UTXOsPacket().fromOptions(options);
};
/**
* Serialize utxos packet.
* @returns {Buffer}
*/
UTXOsPacket.prototype.toRaw = function toRaw(writer) {
var p = bcoin.writer(writer);
var map = new Buffer((this.hits.length + 7) / 8 | 0);
var i, bit, oct, coin, height;
for (i = 0; i < this.hits.length; i++) {
bit = i % 8;
oct = (i - bit) / 8;
map[oct] |= +this.hits[i] << (7 - bit);
}
p.writeU32(this.height);
p.writeHash(this.tip);
p.writeVarBytes(map);
p.writeVarInt(this.coins.length);
for (i = 0; i < this.coins.length; i++) {
coin = this.coins[i];
height = coin.height;
if (height === -1)
height = 0x7fffffff;
p.writeU32(coin.version);
p.writeU32(height);
p.write64(coin.value);
p.writeVarBytes(coin.script.toRaw());
}
if (!writer)
p = p.render();
return p;
};
/**
* Inject properties from serialized data.
* @private
* @param {Buffer} data
*/
UTXOsPacket.prototype.fromRaw = function fromRaw(data) {
var p = bcoin.reader(data);
var i, bit, oct, coin, output;
var version, height, map, count;
this.height = p.readU32();
this.tip = p.readHash('hex');
map = p.readVarBytes();
count = p.readVarint();
for (i = 0; i < map.length * 8; i++) {
bit = i % 8;
oct = (i - bit) / 8;
this.hits.push((map[oct] >> (7 - bit)) & 1);
}
for (i = 0; i < count; i++) {
version = p.readU32();
height = p.readU32();
coin = new bcoin.coin();
if (height === 0x7fffffff)
height = -1;
output = bcoin.output.fromRaw(p);
coin.version = version;
coin.height = height;
coin.script = output.script;
coin.value = output.value;
this.coins.push(coin);
}
return this;
};
/**
* Instantiate utxos packet from serialized data.
* @param {Buffer} data
* @param {String?} enc
* @returns {GetBlocksPacket}
*/
UTXOsPacket.fromRaw = function fromRaw(data, enc) {
if (typeof data === 'string')
data = new Buffer(data, enc);
return new UTXOsPacket().fromRaw(data);
};
/*
* Expose
*/
@ -1005,4 +1239,6 @@ exports.VersionPacket = VersionPacket;
exports.GetBlocksPacket = GetBlocksPacket;
exports.AlertPacket = AlertPacket;
exports.RejectPacket = RejectPacket;
exports.GetUTXOsPacket = GetUTXOsPacket;
exports.UTXOsPacket = UTXOsPacket;
exports.NetworkAddress = NetworkAddress;

View File

@ -13,6 +13,7 @@ var utils = require('../utils/utils');
var assert = utils.assert;
var constants = require('../protocol/constants');
var BufferReader = require('../utils/reader');
var packets = require('./packets');
/**
* Protocol packet parser
@ -191,378 +192,109 @@ Parser.prototype.parseHeader = function parseHeader(h) {
return new Packet(cmd, this.waiting, chk);
};
/**
* Parse mempool packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
Parser.parseMempool = function parseMempool(p) {
return {};
};
/**
* Parse submitorder packet.
* @param {Buffer|BufferReader} p
* @returns {SubmitOrderPacket}
*/
Parser.parseSubmitOrder = function parseSubmitOrder(p) {
p = new BufferReader(p);
return {
hash: p.readHash('hex'),
tx: Parser.parseTX(p)
};
};
/**
* Parse checkorder packet.
* @param {Buffer|BufferReader} p
* @returns {SubmitOrderPacket}
*/
Parser.parseCheckOrder = function parseCheckOrder(p) {
return Parser.parseSubmitOrder(p);
};
/**
* Parse reply packet.
* @param {Buffer|BufferReader} p
* @returns {ReplyPacket}
*/
Parser.parseReply = function parseReply(p) {
p = new BufferReader(p);
return {
hash: p.readHash('hex'),
code: p.readU32(),
publicKey: p.readVarBytes()
};
};
/**
* Parse sendheaders packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
Parser.parseSendHeaders = function parseSendHeaders(p) {
return {};
};
/**
* Parse havewitness packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
Parser.parseHaveWitness = function parseHaveWitness(p) {
return {};
};
/**
* Parse getaddr packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
Parser.parseGetAddr = function parseGetAddr(p) {
return {};
};
/**
* Parse filterload packet.
* @param {Buffer|BufferReader} p
* @returns {Bloom}
*/
Parser.parseFilterLoad = function parseFilterLoad(p) {
return bcoin.bloom.fromRaw(p);
};
/**
* Parse filteradd packet.
* @param {Buffer|BufferReader} p
* @returns {FilterAddPacket}
*/
Parser.parseFilterAdd = function parseFilterAdd(p) {
p = new BufferReader(p);
return {
data: p.readVarBytes()
};
};
/**
* Parse filterclear packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
Parser.parseFilterClear = function parseFilterClear(p) {
return {};
};
/**
* Parse a payload.
* @param {String} cmd - Packet type.
* @param {Buffer} p - Payload.
* @param {Buffer} data - Payload.
* @returns {Object}
*/
Parser.prototype.parsePayload = function parsePayload(cmd, p) {
Parser.prototype.parsePayload = function parsePayload(cmd, data) {
switch (cmd) {
case 'version':
return Parser.parseVersion(p);
return packets.VersionPacket.fromRaw(data);
case 'verack':
return Parser.parseVerack(p);
case 'mempool':
return Parser.parseMempool(p);
case 'getaddr':
return Parser.parseGetAddr(p);
case 'submitorder':
return Parser.parseSubmitOrder(p);
case 'checkorder':
return Parser.parseCheckOrder(p);
case 'reply':
return Parser.parseReply(p);
case 'sendheaders':
return Parser.parseSendHeaders(p);
case 'havewitness':
return Parser.parseHaveWitness(p);
case 'filterload':
return Parser.parseFilterLoad(p);
case 'filteradd':
return Parser.parseFilterAdd(p);
case 'filterclear':
return Parser.parseFilterClear(p);
case 'inv':
return Parser.parseInv(p);
case 'getdata':
return Parser.parseGetData(p);
case 'notfound':
return Parser.parseNotFound(p);
case 'getheaders':
return Parser.parseGetHeaders(p);
case 'getblocks':
return Parser.parseGetBlocks(p);
case 'merkleblock':
return Parser.parseMerkleBlock(p);
case 'headers':
return Parser.parseHeaders(p);
case 'block':
return Parser.parseMemBlock(p);
case 'tx':
return Parser.parseTX(p);
case 'reject':
return Parser.parseReject(p);
case 'addr':
return Parser.parseAddr(p);
return null;
case 'ping':
return Parser.parsePing(p);
return parsePing(data);
case 'pong':
return Parser.parsePong(p);
return parsePing(data);
case 'alert':
return Parser.parseAlert(p);
return packets.AlertPacket.fromRaw(data);
case 'getaddr':
return null;
case 'addr':
return parseAddr(data);
case 'inv':
return parseInv(data);
case 'getdata':
return parseInv(data);
case 'notfound':
return parseInv(data);
case 'getblocks':
return packets.GetBlocksPacket.fromRaw(data);
case 'getheaders':
return packets.GetBlocksPacket.fromRaw(data);
case 'headers':
return parseHeaders(data);
case 'sendheaders':
return null;
case 'block':
return bcoin.memblock.fromRaw(data);
case 'tx':
return bcoin.tx.fromRaw(data);
case 'reject':
return packets.RejectPacket.fromRaw(data);
case 'mempool':
return null;
case 'filterload':
return bcoin.bloom.fromRaw(data);
case 'filteradd':
return parseFilterAdd(data);
case 'filterclear':
return null;
case 'merkleblock':
return bcoin.merkleblock.fromRaw(data);
case 'getutxos':
return Parser.parseGetUTXOs(p);
return packets.GetUTXOsPacket.fromRaw(data);
case 'utxos':
return Parser.parseUTXOs(p);
return packets.UTXOsPacket.fromRaw(data);
case 'havewitness':
return null;
case 'feefilter':
return Parser.parseFeeFilter(p);
case 'encinit':
return Parser.parseEncinit(p);
case 'encack':
return Parser.parseEncack(p);
return parseFeeFilter(data);
case 'sendcmpct':
return Parser.parseSendCmpct(p);
return bcoin.bip152.SendCompact.fromRaw(data);
case 'cmpctblock':
return Parser.parseCmpctBlock(p);
return bcoin.bip152.CompactBlock.fromRaw(data);
case 'getblocktxn':
return Parser.parseGetBlockTxn(p);
return bcoin.bip152.TXRequest.fromRaw(data);
case 'blocktxn':
return Parser.parseBlockTxn(p);
return bcoin.bip152.TXResponse.fromRaw(data);
case 'encinit':
assert(data.length >= 34);
return data;
case 'encack':
assert(data.length >= 33);
return data;
case 'authchallenge':
return Parser.parseAuthChallenge(p);
assert(data.length >= 32);
return data;
case 'authreply':
return Parser.parseAuthReply(p);
assert(data.length >= 64);
return data;
case 'authpropose':
return Parser.parseAuthPropose(p);
assert(data.length >= 32);
return data;
default:
return p;
return data;
}
};
/**
* Parse getutxos packet.
* @param {Buffer|BufferReader} p
* @returns {GetUTXOsPacket}
/*
* Helpers
*/
Parser.parseGetUTXOs = function parseGetUTXOs(p) {
var mempool, prevout, count, i;
function parsePing(data) {
var p = new BufferReader(data);
return p.readU64();
}
p = new BufferReader(p);
mempool = p.readU8() === 1;
prevout = [];
count = p.readVarint();
for (i = 0; i < count; i++)
prevout.push(bcoin.outpoint.fromRaw(p));
return {
mempool: mempool,
prevout: prevout
};
};
/**
* Parse utxos packet.
* @param {Buffer|BufferReader} p
* @returns {UTXOsPacket}
*/
Parser.parseUTXOs = function parseUTXOs(p) {
var chainHeight, tip, map, count, coins;
var coin, version, height, i, hits, j;
p = new BufferReader(p);
chainHeight = p.readU32();
tip = p.readHash('hex');
map = p.readVarBytes();
count = p.readVarint();
coins = [];
hits = [];
for (i = 0; i < map.length; i++) {
for (j = 0; j < 8; j++)
hits.push((map[i] >> j) & 1);
}
for (i = 0; i < count; i++) {
version = p.readU32();
height = p.readU32();
if (height === 0x7fffffff)
height = -1;
coin = bcoin.output.fromRaw(p);
coin.version = version;
coin.height = height;
coins.push(new bcoin.coin(coin));
}
return {
height: chainHeight,
tip: tip,
map: map,
coins: coins,
hits: hits
};
};
/**
* Parse ping packet.
* @param {Buffer|BufferReader} p
* @returns {PingPacket}
*/
Parser.parsePing = function parsePing(p) {
p = new BufferReader(p);
return {
nonce: p.readU64()
};
};
/**
* Parse pong packet.
* @param {Buffer|BufferReader} p
* @returns {PingPacket}
*/
Parser.parsePong = function parsePong(p) {
p = new BufferReader(p);
return {
nonce: p.readU64()
};
};
/**
* Parse version packet.
* @param {Buffer|BufferReader} p
* @returns {VersionPacket}
*/
Parser.parseVersion = function parseVersion(p) {
return bcoin.packets.VersionPacket.fromRaw(p);
};
/**
* Parse verack packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
Parser.parseVerack = function parseVerack(p) {
return {};
};
/**
* Parse notfound packet.
* @param {Buffer|BufferReader} p
* @returns {InvItem[]}
*/
Parser.parseNotFound = function parseNotFound(p) {
return Parser.parseInv(p);
};
/**
* Parse getdata packet.
* @param {Buffer|BufferReader} p
* @returns {InvItem[]}
*/
Parser.parseGetData = function parseGetData(p) {
return Parser.parseInv(p);
};
/**
* Parse getblocks packet.
* @param {Buffer|BufferReader} p
* @returns {GetBlocksPacket}
*/
Parser.parseGetBlocks = function parseGetBlocks(p) {
return bcoin.packets.GetBlocksPacket.fromRaw(p);
};
/**
* Parse getheaders packet.
* @param {Buffer|BufferReader} p
* @returns {GetBlocksPacket}
*/
Parser.parseGetHeaders = function parseGetHeaders(p) {
return bcoin.packets.GetBlocksPacket.fromRaw(p);
};
/**
* Parse inv packet.
* @param {Buffer|BufferReader} p
* @returns {InvItem[]}
*/
Parser.parseInv = function parseInv(p) {
function parseInv(data) {
var p = new BufferReader(data);
var items = [];
var i, count;
p = new BufferReader(p);
count = p.readVarint();
assert(count <= 50000, 'Item count too high.');
@ -571,174 +303,45 @@ Parser.parseInv = function parseInv(p) {
items.push(bcoin.invitem.fromRaw(p));
return items;
};
}
/**
* Parse merkleblock packet.
* @param {Buffer|BufferReader} p
* @returns {Block}
*/
Parser.parseMerkleBlock = function parseMerkleBlock(p) {
return bcoin.merkleblock.fromRaw(p);
};
/**
* Parse headers packet.
* @param {Buffer|BufferReader} p
* @returns {Headers[]}
*/
Parser.parseHeaders = function parseHeaders(p) {
function parseHeaders(data) {
var p = new BufferReader(data);
var headers = [];
var i, count;
p = new BufferReader(p);
count = p.readVarint();
for (i = 0; i < count; i++)
headers.push(bcoin.headers.fromRaw(p));
return headers;
};
}
/**
* Parse block packet.
* @param {Buffer|BufferReader} p
* @returns {Block}
*/
Parser.parseBlock = function parseBlock(p) {
return bcoin.block.fromRaw(p);
};
/**
* Parse block packet.
* @param {Buffer|BufferReader} p
* @returns {Block}
*/
Parser.parseMemBlock = function parseMemBlock(p) {
return bcoin.memblock.fromRaw(p);
};
/**
* Parse tx packet (will automatically switch to witness
* parsing if a witness transaction is detected).
* @param {Buffer|BufferReader} p
* @returns {TX}
*/
Parser.parseTX = function parseTX(p) {
return bcoin.tx.fromRaw(p);
};
/**
* Parse reject packet.
* @param {Buffer|BufferReader} p
* @returns {RejectPacket}
*/
Parser.parseReject = function parseReject(p) {
return bcoin.packets.RejectPacket.fromRaw(p);
};
/**
* Parse addr packet.
* @param {Buffer|BufferReader} p
* @returns {NetworkAddress[]}
*/
Parser.parseAddr = function parseAddr(p) {
function parseAddr(data) {
var p = new BufferReader(data);
var addrs = [];
var i, count;
p = new BufferReader(p);
count = p.readVarint();
assert(count <= 10000, 'Too many addresses.');
for (i = 0; i < count; i++)
addrs.push(bcoin.packets.NetworkAddress.fromRaw(p, true));
addrs.push(packets.NetworkAddress.fromRaw(p, true));
return addrs;
};
}
/**
* Parse mempool packet.
* @param {Buffer|BufferReader} p
* @returns {Object}
*/
function parseFeeFilter(data) {
var p = new BufferReader(data);
return p.read64N();
}
Parser.parseMempool = function parseMempool(p) {
return {};
};
/**
* Parse alert packet.
* @param {Buffer|BufferReader} p
* @returns {AlertPacket}
*/
Parser.parseAlert = function parseAlert(p) {
return bcoin.packets.AlertPacket.fromRaw(p);
};
/**
* Parse feefilter packet.
* @param {Buffer|BufferReader} p
* @returns {FeeFilterPacket}
*/
Parser.parseFeeFilter = function parseFeeFilter(p) {
p = new BufferReader(p);
return {
rate: p.read64N()
};
};
Parser.parseEncinit = function parseEncinit(p) {
// Handled elsewhere.
return p;
};
Parser.parseEncack = function parseEncack(p) {
// Handled elsewhere.
return p;
};
Parser.parseSendCmpct = function parseSendCmpct(p) {
return bcoin.bip152.SendCompact.fromRaw(p);
};
Parser.parseCmpctBlock = function parseCmpctBlock(p) {
return bcoin.bip152.CompactBlock.fromRaw(p);
};
Parser.parseGetBlockTxn = function parseGetBlockTxn(p) {
return bcoin.bip152.TXRequest.fromRaw(p);
};
Parser.parseBlockTxn = function parseBlockTxn(p) {
return bcoin.bip152.TXResponse.fromRaw(p);
};
Parser.parseAuthChallenge = function parseAuthChallenge(p) {
// Handled elsewhere.
return p;
};
Parser.parseAuthReply = function parseAuthReply(p) {
// Handled elsewhere.
return p;
};
Parser.parseAuthPropose = function parseAuthPropose(p) {
// Handled elsewhere.
return p;
};
function parseFilterAdd(data) {
var p = new BufferReader(data);
return p.readVarBytes();
}
/**
* Packet

View File

@ -20,6 +20,7 @@ var VersionPacket = bcoin.packets.VersionPacket;
var GetBlocksPacket = bcoin.packets.GetBlocksPacket;
var RejectPacket = bcoin.packets.RejectPacket;
var NetworkAddress = bcoin.packets.NetworkAddress;
var GetUTXOsPacket = bcoin.packets.GetUTXOsPacket;
/**
* Represents a remote peer.
@ -648,9 +649,7 @@ Peer.prototype.sendPing = function sendPing() {
this.lastPing = utils.ms();
this.challenge = utils.nonce();
this.write(this.framer.ping({
nonce: this.challenge
}));
this.write(this.framer.ping(this.challenge));
};
/**
@ -692,9 +691,7 @@ Peer.prototype.updateWatch = function updateWatch() {
*/
Peer.prototype.sendFeeRate = function sendFeeRate(rate) {
this.write(this.framer.feeFilter({
rate: rate
}));
this.write(this.framer.feeFilter(rate));
};
/**
@ -892,52 +889,39 @@ Peer.prototype._onPacket = function onPacket(packet) {
switch (cmd) {
case 'version':
return this._handleVersion(payload);
case 'inv':
return this._handleInv(payload);
case 'headers':
return this._handleHeaders(payload);
case 'getdata':
return this._handleGetData(payload);
case 'addr':
return this._handleAddr(payload);
case 'verack':
this.fire(cmd);
break;
case 'ping':
return this._handlePing(payload);
case 'pong':
return this._handlePong(payload);
case 'getaddr':
return this._handleGetAddr(payload);
case 'reject':
return this._handleReject(payload);
case 'alert':
return this._handleAlert(payload);
case 'getutxos':
return this._handleGetUTXOs(payload);
case 'utxos':
return this._handleUTXOs(payload);
case 'feefilter':
return this._handleFeeFilter(payload);
case 'getaddr':
return this._handleGetAddr();
case 'addr':
return this._handleAddr(payload);
case 'inv':
return this._handleInv(payload);
case 'getdata':
return this._handleGetData(payload);
case 'notfound':
this.fire(cmd, payload);
break;
case 'getblocks':
return this._handleGetBlocks(payload);
case 'getheaders':
return this._handleGetHeaders(payload);
case 'mempool':
return this._handleMempool(payload);
case 'filterload':
return this._handleFilterLoad(payload);
case 'filteradd':
return this._handleFilterAdd(payload);
case 'filterclear':
return this._handleFilterClear(payload);
case 'headers':
return this._handleHeaders(payload);
case 'sendheaders':
this.preferHeaders = true;
this.fire(cmd);
break;
case 'block':
this.fire(cmd, payload);
break;
case 'merkleblock':
payload.verifyPartial();
this.lastBlock = payload;
this.waiting = payload.matches.length;
if (this.waiting === 0)
this._flushMerkle();
break;
case 'tx':
if (this.lastBlock) {
if (this.lastBlock.hasTX(payload)) {
@ -949,20 +933,41 @@ Peer.prototype._onPacket = function onPacket(packet) {
}
this.fire(cmd, payload);
break;
case 'sendheaders':
this.preferHeaders = true;
this.fire(cmd, payload);
case 'reject':
return this._handleReject(payload);
case 'mempool':
return this._handleMempool();
case 'filterload':
return this._handleFilterLoad(payload);
case 'filteradd':
return this._handleFilterAdd(payload);
case 'filterclear':
return this._handleFilterClear();
case 'merkleblock':
payload.verifyPartial();
this.lastBlock = payload;
this.waiting = payload.matches.length;
if (this.waiting === 0)
this._flushMerkle();
break;
case 'getutxos':
return this._handleGetUTXOs(payload);
case 'utxos':
return this._handleUTXOs(payload);
case 'havewitness':
this.haveWitness = true;
this.fire(cmd, payload);
break;
case 'verack':
this.fire(cmd, payload);
break;
case 'notfound':
this.fire(cmd, payload);
this.fire(cmd);
break;
case 'feefilter':
return this._handleFeeFilter(payload);
case 'sendcmpct':
return this._handleSendCmpct(payload);
case 'cmpctblock':
return this._handleCmpctBlock(payload);
case 'getblocktxn':
return this._handleGetBlockTxn(payload);
case 'blocktxn':
return this._handleBlockTxn(payload);
case 'encinit':
return this._handleEncinit(payload);
case 'encack':
@ -973,14 +978,6 @@ Peer.prototype._onPacket = function onPacket(packet) {
return this._handleAuthReply(payload);
case 'authpropose':
return this._handleAuthPropose(payload);
case 'sendcmpct':
return this._handleSendCmpct(payload);
case 'cmpctblock':
return this._handleCmpctBlock(payload);
case 'getblocktxn':
return this._handleGetBlockTxn(payload);
case 'blocktxn':
return this._handleBlockTxn(payload);
default:
this.logger.warning('Unknown packet: %s.', cmd);
this.fire(cmd, payload);
@ -1018,13 +1015,13 @@ Peer.prototype.fire = function fire(cmd, payload) {
* @param {Object}
*/
Peer.prototype._handleFilterLoad = function _handleFilterLoad(payload) {
if (!payload.isWithinConstraints()) {
Peer.prototype._handleFilterLoad = function _handleFilterLoad(filter) {
if (!filter.isWithinConstraints()) {
this.setMisbehavior(100);
return;
}
this.spvFilter = payload;
this.spvFilter = filter;
this.relay = true;
};
@ -1034,14 +1031,14 @@ Peer.prototype._handleFilterLoad = function _handleFilterLoad(payload) {
* @param {Object}
*/
Peer.prototype._handleFilterAdd = function _handleFilterAdd(payload) {
if (payload.data.length > constants.script.MAX_PUSH) {
Peer.prototype._handleFilterAdd = function _handleFilterAdd(data) {
if (data.length > constants.script.MAX_PUSH) {
this.setMisbehavior(100);
return;
}
if (this.spvFilter)
this.spvFilter.add(payload.data);
this.spvFilter.add(data);
this.relay = true;
};
@ -1052,109 +1049,40 @@ Peer.prototype._handleFilterAdd = function _handleFilterAdd(payload) {
* @param {Object}
*/
Peer.prototype._handleFilterClear = function _handleFilterClear(payload) {
Peer.prototype._handleFilterClear = function _handleFilterClear() {
if (this.spvFilter)
this.spvFilter.reset();
this.relay = true;
};
/**
* Handle `utxos` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleUTXOs = function _handleUTXOs(payload) {
this.logger.debug('Received %d utxos (%s).',
payload.coins.length, this.hostname);
this.fire('utxos', payload);
};
/**
* Handle `feefilter` packet.
* @private
* @param {Object}
*/
Peer.prototype._handleFeeFilter = function _handleFeeFilter(payload) {
if (!(payload.rate >= 0 && payload.rate <= constants.MAX_MONEY)) {
Peer.prototype._handleFeeFilter = function _handleFeeFilter(rate) {
if (!(rate >= 0 && rate <= constants.MAX_MONEY)) {
this.setMisbehavior(100);
return;
}
this.feeRate = payload.rate;
this.feeRate = rate;
this.fire('feefilter', payload);
this.fire('feefilter', rate);
};
/**
* Request UTXOs from peer.
* @param {Outpoint[]} outpoints
* @param {Function} callback - Returns [Error, {@link Coin}[]].
*/
Peer.prototype.getUTXOs = function getUTXOs(outpoints, callback) {
var self = this;
var reqs = [];
var coins = [];
var i;
for (i = 0; i < outpoints.length; i += 15)
reqs.push(outpoints.slice(i, i + 15));
utils.forEachSerial(reqs, function(outpoints, next) {
self._getUTXOs(outpoints, function(err, coin) {
if (err)
return next(err);
coins = coins.concat(coin);
next();
});
}, function(err) {
if (err)
return callback(err);
callback(null, coins);
});
};
/**
* Send non-chunked getuxos to peer.
* Handle `utxos` packet.
* @private
* @param {Outpoint[]} outpoints
* @param {Function} callback
* @param {Object}
*/
Peer.prototype._getUTXOs = function _getUTXOs(outpoints, callback) {
var index = 0;
var i, prevout, coin;
this.request('utxos', function(err, payload) {
if (err)
return callback(err);
for (i = 0; i < payload.hits.length; i++) {
if (payload.hits[i]) {
prevout = outpoints[i];
coin = payload.coins[index++];
if (!prevout || !coin)
return callback(new Error('Malformed utxos message.'));
coin.hash = prevout.hash;
coin.index = prevout.index;
}
}
callback(null, payload.coins);
});
this.write(this.framer.getUTXOs({
mempool: true,
prevout: outpoints
}));
Peer.prototype._handleUTXOs = function _handleUTXOs(utxos) {
this.logger.debug('Received %d utxos (%s).',
utxos.coins.length, this.hostname);
this.fire('utxos', utxos);
};
/**
@ -1164,9 +1092,8 @@ Peer.prototype._getUTXOs = function _getUTXOs(outpoints, callback) {
Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) {
var self = this;
var coins = [];
var hits = [];
var unlock = this._lock(_handleGetUTXOs, [payload, utils.nop]);
var utxos;
if (!unlock)
return;
@ -1191,6 +1118,8 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) {
if (payload.prevout.length > 15)
return done();
utxos = new GetUTXOsPacket();
utils.forEachSerial(payload.prevout, function(prevout, next) {
var hash = prevout.hash;
var index = prevout.index;
@ -1200,13 +1129,13 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) {
coin = self.mempool.getCoin(hash, index);
if (coin) {
hits.push(1);
coins.push(coin);
utxos.hits.push(1);
utxos.coins.push(coin);
return next();
}
if (self.mempool.isSpent(hash, index)) {
hits.push(0);
utxos.hits.push(0);
return next();
}
}
@ -1216,12 +1145,12 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) {
return next(err);
if (!coin) {
hits.push(0);
utxos.hits.push(0);
return next();
}
hits.push(1);
coins.push(coin);
utxos.hits.push(1);
utxos.coins.push(coin);
next();
});
@ -1229,12 +1158,10 @@ Peer.prototype._handleGetUTXOs = function _handleGetUTXOs(payload) {
if (err)
return done(err);
self.write(self.framer.UTXOs({
height: self.chain.height,
tip: self.chain.tip.hash,
hits: hits,
coins: coins
}));
utxos.height = self.chain.height;
utxos.tip = self.chain.tip.hash;
self.write(self.framer.UTXOs(utxos));
done();
});
@ -1726,8 +1653,8 @@ Peer.prototype._handleAddr = function _handleAddr(addrs) {
* @param {Object}
*/
Peer.prototype._handlePing = function _handlePing(data) {
this.write(this.framer.pong(data));
Peer.prototype._handlePing = function _handlePing(nonce) {
this.write(this.framer.pong(nonce));
this.fire('ping', this.minPing);
};
@ -1737,7 +1664,7 @@ Peer.prototype._handlePing = function _handlePing(data) {
* @param {Object}
*/
Peer.prototype._handlePong = function _handlePong(data) {
Peer.prototype._handlePong = function _handlePong(nonce) {
var now = utils.ms();
if (!this.challenge) {
@ -1745,8 +1672,8 @@ Peer.prototype._handlePong = function _handlePong(data) {
return;
}
if (data.nonce.cmp(this.challenge) !== 0) {
if (data.nonce.cmpn(0) === 0) {
if (nonce.cmp(this.challenge) !== 0) {
if (nonce.cmpn(0) === 0) {
this.logger.debug('Peer sent a zero nonce (%s).', this.hostname);
this.challenge = null;
return;
@ -1879,15 +1806,15 @@ Peer.prototype._handleHeaders = function _handleHeaders(headers) {
* @param {Object}
*/
Peer.prototype._handleReject = function _handleReject(payload) {
Peer.prototype._handleReject = function _handleReject(details) {
var hash, entry;
this.fire('reject', payload);
this.fire('reject', details);
if (!payload.data)
if (!details.data)
return;
hash = payload.data;
hash = details.data;
entry = this.pool.invMap[hash];
if (!entry)
@ -1913,12 +1840,12 @@ Peer.prototype._handleAlert = function _handleAlert(alert) {
* @param {Object}
*/
Peer.prototype._handleEncinit = function _handleEncinit(payload) {
Peer.prototype._handleEncinit = function _handleEncinit(data) {
if (!this.bip151)
return;
try {
this.bip151.encinit(payload);
this.bip151.encinit(data);
} catch (e) {
this._error(e);
return;
@ -1926,7 +1853,7 @@ Peer.prototype._handleEncinit = function _handleEncinit(payload) {
this.write(this.framer.encack(this.bip151.toEncack()));
this.fire('encinit', payload);
this.fire('encinit', data);
};
/**
@ -1935,18 +1862,18 @@ Peer.prototype._handleEncinit = function _handleEncinit(payload) {
* @param {Object}
*/
Peer.prototype._handleEncack = function _handleEncack(payload) {
Peer.prototype._handleEncack = function _handleEncack(data) {
if (!this.bip151)
return;
try {
this.bip151.encack(payload);
this.bip151.encack(data);
} catch (e) {
this._error(e);
return;
}
this.fire('encack', payload);
this.fire('encack', data);
};
/**
@ -1955,14 +1882,14 @@ Peer.prototype._handleEncack = function _handleEncack(payload) {
* @param {Object}
*/
Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(payload) {
Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(data) {
var result;
if (!this.bip150)
return;
try {
result = this.bip150.challenge(payload);
result = this.bip150.challenge(data);
} catch (e) {
this._error(e);
return;
@ -1970,7 +1897,7 @@ Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(payload) {
this.write(this.framer.authReply(result));
this.fire('authchallenge', payload);
this.fire('authchallenge', data);
};
/**
@ -1979,14 +1906,14 @@ Peer.prototype._handleAuthChallenge = function _handleAuthChallenge(payload) {
* @param {Object}
*/
Peer.prototype._handleAuthReply = function _handleAuthReply(payload) {
Peer.prototype._handleAuthReply = function _handleAuthReply(data) {
var result;
if (!this.bip150)
return;
try {
result = this.bip150.reply(payload);
result = this.bip150.reply(data);
} catch (e) {
this._error(e);
return;
@ -1995,7 +1922,7 @@ Peer.prototype._handleAuthReply = function _handleAuthReply(payload) {
if (result)
this.write(this.framer.authPropose(result));
this.fire('authreply', payload);
this.fire('authreply', data);
};
/**
@ -2004,14 +1931,14 @@ Peer.prototype._handleAuthReply = function _handleAuthReply(payload) {
* @param {Object}
*/
Peer.prototype._handleAuthPropose = function _handleAuthPropose(payload) {
Peer.prototype._handleAuthPropose = function _handleAuthPropose(data) {
var result;
if (!this.bip150)
return;
try {
result = this.bip150.propose(payload);
result = this.bip150.propose(data);
} catch (e) {
this._error(e);
return;
@ -2019,7 +1946,7 @@ Peer.prototype._handleAuthPropose = function _handleAuthPropose(payload) {
this.write(this.framer.authChallenge(result));
this.fire('authpropose', payload);
this.fire('authpropose', data);
};
/**
@ -2028,21 +1955,25 @@ Peer.prototype._handleAuthPropose = function _handleAuthPropose(payload) {
* @param {Object}
*/
Peer.prototype._handleSendCmpct = function _handleSendCmpct(payload) {
if (payload.version !== 1) {
Peer.prototype._handleSendCmpct = function _handleSendCmpct(cmpct) {
if (cmpct.version !== 1) {
// Ignore
this.logger.info('Peer request compact blocks version %d (%s).',
cmpct.version, this.hostname);
return;
}
if (payload.mode !== 0) {
if (cmpct.mode !== 0) {
// Ignore (we can't do mode 1 yet).
this.logger.info('Peer request compact blocks mode %d (%s).',
cmpct.mode, this.hostname);
return;
}
this.logger.info('Peer initialized compact blocks (%s).', this.hostname);
this.compactMode = payload;
this.fire('sendcmpct', payload);
this.compactMode = cmpct;
this.fire('sendcmpct', cmpct);
};
/**
@ -2210,15 +2141,22 @@ Peer.prototype.sendAlert = function sendAlert(alert) {
Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
var packet = new GetBlocksPacket(locator, stop);
var height = -1;
var hash = null;
this.logger.debug(
'Requesting headers packet from peer with getheaders (%s).',
this.hostname);
this.logger.debug('Height: %d, Hash: %s, Stop: %s',
locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1,
locator && locator.length ? utils.revHex(locator[0]) : 0,
stop ? utils.revHex(stop) : 0);
if (packet.locator.length > 0) {
height = this.chain._getCachedHeight(packet.locator[0]);
hash = utils.revHex(packet.locator[0]);
}
if (stop)
stop = utils.revHex(stop);
this.logger.debug('Height: %d, Hash: %s, Stop: %s', height, hash, stop);
this.write(this.framer.getHeaders(packet));
};
@ -2231,15 +2169,22 @@ Peer.prototype.sendGetHeaders = function sendGetHeaders(locator, stop) {
Peer.prototype.sendGetBlocks = function getBlocks(locator, stop) {
var packet = new GetBlocksPacket(locator, stop);
var height = -1;
var hash = null;
this.logger.debug(
'Requesting inv packet from peer with getblocks (%s).',
this.hostname);
this.logger.debug('Height: %d, Hash: %s, Stop: %s',
locator && locator.length ? this.chain._getCachedHeight(locator[0]) : -1,
locator && locator.length ? utils.revHex(locator[0]) : 0,
stop ? utils.revHex(stop) : 0);
if (packet.locator.length > 0) {
height = this.chain._getCachedHeight(packet.locator[0]);
hash = utils.revHex(packet.locator[0]);
}
if (stop)
stop = utils.revHex(stop);
this.logger.debug('Height: %d, Hash: %s, Stop: %s', height, hash, stop);
this.write(this.framer.getBlocks(packet));
};

View File

@ -1709,60 +1709,6 @@ Pool.prototype.setFeeRate = function setFeeRate(rate) {
this.peers.regular[i].sendFeeRate(rate);
};
/**
* Request UTXOs from peer.
* @param {Outpoint[]} outpoints
* @param {Function} callback - Returns [Error, {@link Coin}[]].
*/
Pool.prototype.getUTXOs = function getUTXOs(outpoints, callback) {
var i, peer;
for (i = 0; i < this.peers.all.length; i++) {
peer = this.peers.all[i];
if (!peer.version)
continue;
if (peer.version.services & constants.services.GETUXO)
break;
}
if (i === this.peers.regular.length)
return utils.asyncify(callback)(new Error('No peer available.'));
peer.getUTXOs(outpoints, callback);
};
/**
* Attempt to fill transaction using getutxos (note: unreliable).
* @param {TX} tx
* @param {Function} callback - Returns [Error, {@link TX}].
*/
Pool.prototype.fillCoins = function fillCoins(tx, callback) {
var outpoints = [];
var i, input;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
if (!input.coin)
outpoints.push(input.prevout);
}
if (outpoints.length === 0)
return utils.asyncify(callback)(null, tx);
this.getUTXOs(outpoints, function(err, coins) {
if (err)
return callback(err);
tx.fillCoins(coins);
callback(null, tx);
});
};
/**
* Allocate a new loader host.
* @returns {NetworkAddress}