fcoin/lib/bcoin/protocol/framer.js
2014-05-02 14:50:21 +04:00

209 lines
4.6 KiB
JavaScript

var assert = require('assert');
var bcoin = require('../../bcoin');
var constants = require('./constants');
var utils = bcoin.utils;
var writeU32 = utils.writeU32;
var writeAscii = utils.writeAscii;
function Framer() {
if (!(this instanceof Framer))
return new Framer();
}
module.exports = Framer;
Framer.prototype.header = function header(cmd, payload) {
assert(cmd.length < 12);
assert(payload.length <= 0xffffffff);
var h = new Array(24);
// Magic value
writeU32(h, constants.magic, 0);
// Command
var len = writeAscii(h, cmd, 4);
for (var i = 4 + len; i < 4 + 12; i++)
h[i] = 0;
// Payload length
writeU32(h, payload.length, 16);
// Checksum
utils.copy(utils.checksum(payload), h, 20);
return h;
};
Framer.prototype.packet = function packet(cmd, payload) {
var h = this.header(cmd, payload);
return h.concat(payload);
};
Framer.prototype._addr = function addr(buf, off) {
writeU32(buf, 1, off);
writeU32(buf, 0, off + 4);
writeU32(buf, 0, off + 8);
writeU32(buf, 0, off + 12);
writeU32(buf, 0xffff0000, off + 16);
writeU32(buf, 0, off + 20);
buf[off + 24] = 0;
buf[off + 25] = 0;
};
Framer.prototype.version = function version(packet) {
var p = new Array(86);
if (!packet)
packet = {};
// Version
writeU32(p, constants.version, 0);
// Services
writeU32(p, constants.services.network, 4);
writeU32(p, 0, 8);
// Timestamp
var ts = ((+new Date) / 1000) | 0;
writeU32(p, ts, 12);
writeU32(p, 0, 16);
// Remote and local addresses
this._addr(p, 20);
this._addr(p, 46);
// Nonce, very dramatic
writeU32(p, (Math.random() * 0xffffffff) | 0, 72);
writeU32(p, (Math.random() * 0xffffffff) | 0, 76);
// No user-agent
p[80] = 0;
// Start height
writeU32(p, packet.height, 81);
// Relay
p[85] = packet.relay ? 1 : 0;
return this.packet('version', p);
};
Framer.prototype.verack = function verack() {
return this.packet('verack', []);
};
function varint(arr, value, off) {
if (!off)
off = 0;
if (value < 0xfd) {
arr[off] = value;
return 1;
} else if (value <= 0xffff) {
arr[off] = 0xfd;
arr[off + 1] = value & 0xff;
arr[off + 2] = value >>> 8;
return 3;
} else if (value <= 0xffffffff) {
arr[off] = 0xfd;
arr[off + 3] = value & 0xff;
arr[off + 4] = (value >>> 8) & 0xff;
arr[off + 5] = (value >>> 16) & 0xff;
arr[off + 6] = value >>> 24;
return 5;
} else {
throw new Error('64bit varint not supported yet');
}
}
Framer.prototype._inv = function _inv(command, items) {
var res = [];
var off = varint(res, items.length, 0);
assert(items.length <= 50000);
for (var i = 0; i < items.length; i++) {
// Type
off += writeU32(res, constants.inv[items[i].type], off);
// Hash
var hash = items[i].hash;
if (typeof hash === 'string')
hash = utils.toArray(hash, 'hex');
assert.equal(hash.length, 32);
res = res.concat(hash);
off += hash.length;
}
return this.packet(command, res);
};
Framer.prototype.inv = function inv(items) {
return this._inv('inv', items);
};
Framer.prototype.getData = function getData(items) {
return this._inv('getdata', items);
};
Framer.prototype.notFound = function notFound(items) {
return this._inv('notfound', items);
};
Framer.prototype.ping = function ping(nonce) {
return this.packet('ping', nonce);
};
Framer.prototype.pong = function pong(nonce) {
return this.packet('pong', nonce.slice(0, 8));
};
Framer.prototype.filterLoad = function filterLoad(bloom, update) {
var filter = bloom.toArray();
var before = [];
varint(before, filter.length, 0)
var after = new Array(9);
// Number of hash functions
writeU32(after, bloom.n, 0);
// nTweak
writeU32(after, bloom.tweak, 4);
// nFlags
after[8] = constants.filterFlags[update];
var r = this.packet('filterload', before.concat(filter, after));
return r;
};
Framer.prototype.filterClear = function filterClear() {
return this.packet('filterclear', []);
};
Framer.prototype.getBlocks = function getBlocks(hashes, stop) {
var p = [];
writeU32(p, constants.version, 0);
var off = 4 + varint(p, hashes.length, 4);
p.length = off + 32 * (hashes.length + 1);
for (var i = 0; i < hashes.length; i++) {
var hash = hashes[i];
if (typeof hash === 'string')
hash = utils.toArray(hash, 'hex');
var len = utils.copy(hash, p, off);
for (; len < 32; len++)
p[off + len] = 0;
off += len;
}
var len = stop ? utils.copy(stop, p, off) : 0;
for (; len < 32; len++)
p[off + len] = 0;
assert.equal(off + len, p.length);
return this.packet('getblocks', p);
};