int-cli
Version:
INT is the new generation of bottom-up created system of IoT and blockchain
408 lines (407 loc) • 14.9 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const error_code_1 = require("../error_code");
const connection_1 = require("./connection");
const writer_1 = require("./writer");
const reader_1 = require("./reader");
const events_1 = require("events");
let assert = require('assert');
const version_1 = require("./version");
const reader_2 = require("../lib/reader");
const writer_2 = require("../lib/writer");
const logger_util_1 = require("../lib/logger_util");
const util_1 = require("util");
var CMD_TYPE;
(function (CMD_TYPE) {
CMD_TYPE[CMD_TYPE["version"] = 1] = "version";
CMD_TYPE[CMD_TYPE["versionAck"] = 2] = "versionAck";
CMD_TYPE[CMD_TYPE["userCmd"] = 16] = "userCmd";
})(CMD_TYPE = exports.CMD_TYPE || (exports.CMD_TYPE = {}));
class INode extends events_1.EventEmitter {
constructor(options) {
super();
this.m_inConn = [];
this.m_outConn = [];
this.m_remoteMap = new Map();
this.m_peerid = options.peerid;
this.m_network = options.network;
this.m_logger = logger_util_1.initLogger(options);
}
async randomPeers(count, excludes) {
return { err: error_code_1.ErrorCode.RESULT_NO_IMP, peers: [] };
}
static isValidPeerid(peerid) {
return -1 === peerid.indexOf('^');
}
static isValidNetwork(network) {
return -1 === network.indexOf('^');
}
static fullPeerid(network, peerid) {
return `${network}^${peerid}`;
}
static splitFullPeerid(fpeerid) {
const spliter = fpeerid.indexOf('^');
if (-1 === spliter) {
return undefined;
}
const parts = fpeerid.split('^');
return { network: parts[0], peerid: parts[1] };
}
set genesisHash(genesis_hash) {
this.m_genesis = genesis_hash;
}
set logger(logger) {
if (!logger) {
return;
}
this.m_logger = logger;
}
get logger() {
return this.m_logger;
}
get peerid() {
return this.m_peerid;
}
get network() {
return this.m_network;
}
async init() {
}
dumpConns() {
let ret = [];
this.m_inConn.forEach((element) => {
ret.push(` <= ${element.remote}`);
});
this.m_outConn.forEach((element) => {
ret.push(` => ${element.remote}`);
});
return ret;
}
addBlackList(peerid, ip) {
return;
}
uninit() {
this.removeAllListeners('inbound');
this.removeAllListeners('error');
this.removeAllListeners('ban');
let ops = [];
for (let conn of this.m_inConn) {
ops.push(conn.destroy());
}
for (let conn of this.m_outConn) {
ops.push(conn.destroy());
}
this.m_inConn = [];
this.m_outConn = [];
this.m_remoteMap.clear();
return Promise.all(ops);
}
async listen() {
return error_code_1.ErrorCode.RESULT_NO_IMP;
}
async connectTo(peerid) {
let result = await this._connectTo(peerid);
if (!result.conn) {
return { err: result.err, peerid };
}
let conn = result.conn;
conn.remote = peerid;
conn.network = this.network;
let ver = new version_1.Version();
conn.version = ver;
if (!this.m_genesis || !this.m_peerid) {
this.m_logger.error(`connectTo failed for genesis or peerid not set`);
assert(false, `${this.m_peerid} has not set genesis`);
return { err: error_code_1.ErrorCode.RESULT_INVALID_STATE, peerid };
}
ver.genesis = this.m_genesis;
ver.peerid = this.m_peerid;
let err = await new Promise((resolve) => {
conn.once('pkg', (pkg) => {
conn.removeListener('error', fn);
if (pkg.header.cmdType === CMD_TYPE.versionAck) {
if (pkg.body.isSupport) {
// 忽略网络传输时间
let nTimeDelta = pkg.body.timestamp - Date.now();
conn.setTimeDelta(nTimeDelta);
resolve(error_code_1.ErrorCode.RESULT_OK);
}
else {
this.logger.warn(`close conn ${conn.id} to ${peerid} by unSupport`);
conn.destroy();
resolve(error_code_1.ErrorCode.RESULT_VER_NOT_SUPPORT);
}
}
else {
this.logger.warn(`close conn ${conn.id} to ${peerid} by non versionAck pkg`);
conn.destroy();
resolve(error_code_1.ErrorCode.RESULT_INVALID_STATE);
}
});
let writer = new writer_2.BufferWriter();
let encodeErr = ver.encode(writer);
if (encodeErr) {
this.m_logger.error(`version instance encode failed `, ver);
resolve(encodeErr);
return;
}
let buf = writer.render();
let verWriter = writer_1.PackageStreamWriter.fromPackage(CMD_TYPE.version, {}, buf.length).writeData(buf);
conn.addPendingWriter(verWriter);
let fn = (_conn, _err) => {
_conn.close();
resolve(_err);
};
conn.once('error', fn);
});
if (err) {
return { err, peerid };
}
let other = this.getConnection(peerid);
if (other) {
if (conn.version.compare(other.version) > 0) {
this.logger.warn(`close conn ${conn.id} to ${peerid} by already exist conn`);
conn.destroy();
return { err: error_code_1.ErrorCode.RESULT_ALREADY_EXIST, peerid };
}
else {
this.logger.warn(`close other conn ${conn.id} to ${peerid} by already exist conn`);
this.closeConnection(other, true);
}
}
this.m_outConn.push(result.conn);
this.m_remoteMap.set(peerid, result.conn);
conn.on('error', (_conn, _err) => {
this.closeConnection(result.conn);
this.emit('error', result.conn, _err);
});
return { err: error_code_1.ErrorCode.RESULT_OK, peerid, conn };
}
async broadcast(writer, options) {
let nSend = 0;
let nMax = 999999999;
if (options && !util_1.isNullOrUndefined(options.count)) {
if (!options.count) {
return { err: error_code_1.ErrorCode.RESULT_OK, count: 0 };
}
nMax = options.count;
}
let conns = this.m_inConn.slice(0);
conns.push(...this.m_outConn);
if (!conns.length) {
return { err: error_code_1.ErrorCode.RESULT_OK, count: 0 };
}
let rstart = Math.floor(Math.random() * (conns.length - 1));
for (let i = rstart; i < conns.length; ++i) {
const conn = conns[i];
if (nSend === nMax) {
return { err: error_code_1.ErrorCode.RESULT_OK, count: nSend };
}
if (!options || !options.filter || options.filter(conn)) {
conn.addPendingWriter(writer.clone());
nSend++;
}
}
for (let i = 0; i < rstart; ++i) {
const conn = conns[i];
if (nSend === nMax) {
return { err: error_code_1.ErrorCode.RESULT_OK, count: nSend };
}
if (!options || !options.filter || options.filter(conn)) {
conn.addPendingWriter(writer.clone());
nSend++;
}
}
return { err: error_code_1.ErrorCode.RESULT_OK, count: nSend };
}
isInbound(conn) {
for (let c of this.m_inConn) {
if (c === conn) {
return true;
}
}
return false;
}
getOutbounds() {
const c = this.m_outConn;
return c;
}
getInbounds() {
const c = this.m_inConn;
return c;
}
getConnnectionCount() {
return this.m_outConn.length + this.m_inConn.length;
}
getConnection(remote) {
return this.m_remoteMap.get(remote);
}
isOutbound(conn) {
for (let c of this.m_outConn) {
if (c === conn) {
return true;
}
}
return false;
}
banConnection(remote) {
let conn = this.m_remoteMap.get(remote);
if (conn) {
this.closeConnection(conn, true);
}
}
closeConnection(conn, destroy = false) {
conn.removeAllListeners('error');
conn.removeAllListeners('pkg');
let index = 0;
do {
for (let c of this.m_outConn) {
if (c === conn) {
this.m_outConn.splice(index, 1);
break;
}
index++;
}
index = 0;
for (let c of this.m_inConn) {
if (c === conn) {
this.m_inConn.splice(index, 1);
break;
}
index++;
}
} while (false);
this.m_remoteMap.delete(conn.remote);
if (destroy) {
conn.destroy();
}
else {
conn.close();
}
}
on(event, listener) {
return super.on(event, listener);
}
once(event, listener) {
return super.once(event, listener);
}
_onInbound(inbound) {
inbound.once('pkg', (pkg) => {
inbound.removeListener('error', fn);
if (pkg.header.cmdType === CMD_TYPE.version) {
let buff = pkg.data[0];
let dataReader = new reader_2.BufferReader(buff);
let ver = new version_1.Version();
inbound.version = ver;
let err = ver.decode(dataReader);
if (err) {
this.m_logger.warn(`recv version in invalid format from ${inbound.remote} `);
inbound.destroy();
return;
}
// 检查对方包里的genesis_hash是否对应得上
if (ver.genesis !== this.m_genesis) {
this.m_logger.warn(`recv version genesis ${ver.genesis} not match ${this.m_genesis} from ${inbound.remote} `);
inbound.destroy();
return;
}
// 忽略网络传输时间
let nTimeDelta = ver.timestamp - Date.now();
inbound.remote = ver.peerid;
inbound.network = this.network;
inbound.setTimeDelta(nTimeDelta);
let isSupport = true;
let ackWriter = writer_1.PackageStreamWriter.fromPackage(CMD_TYPE.versionAck, { isSupport, timestamp: Date.now() }, 0);
inbound.addPendingWriter(ackWriter);
if (!isSupport) {
this.m_logger.warn(`close inbound conn ${inbound.id} to ${inbound.fullRemote} by unSupport`);
inbound.destroy();
return;
}
let other = this.getConnection(inbound.remote);
if (other) {
if (inbound.version.compare(other.version) > 0) {
this.m_logger.warn(`close inbound conn ${inbound.id} to ${inbound.fullRemote} by already exist`);
inbound.destroy();
return;
}
else {
this.m_logger.warn(`close other conn ${inbound.id} to ${inbound.fullRemote} by already exist`);
this.closeConnection(other, true);
}
}
this.m_inConn.push(inbound);
this.m_remoteMap.set(ver.peerid, inbound);
inbound.on('error', (conn, _err) => {
this.closeConnection(inbound);
this.emit('error', inbound, _err);
});
this.emit('inbound', inbound);
}
else {
this.m_logger.warn(`close inbound conn ${inbound.id} to ${inbound.fullRemote} by non version pkg`);
inbound.destroy();
}
});
let fn = () => {
inbound.close();
};
inbound.once('error', fn);
}
async _connectTo(peerid) {
return { err: error_code_1.ErrorCode.RESULT_NO_IMP };
}
_connectionType() {
return connection_1.IConnection;
}
_nodeConnectionType() {
let superClass = this._connectionType();
return class extends superClass {
constructor(...args) {
assert(args.length);
let thisNode = args[0];
super(...(args.slice(1)));
this.m_pendingWriters = [];
this.m_reader = new reader_1.PackageStreamReader();
this.m_reader.start(this);
this.m_reader.on('pkg', (pkg) => {
super.emit('pkg', pkg);
});
// 接收到 reader的传出来的error 事件后, emit ban事件, 给上层的chain_node去做处理
// 这里只需要emit给上层, 最好不要处理其他逻辑
this.m_reader.on('error', (err, column) => {
let remote = this.remote;
thisNode.emit('ban', remote);
});
}
get fullRemote() {
return INode.fullPeerid(this.network, this.remote);
}
addPendingWriter(writer) {
let onFinish = () => {
let _writer = this.m_pendingWriters.splice(0, 1)[0];
_writer.close();
if (this.m_pendingWriters.length) {
this.m_pendingWriters[0].on(writer_1.WRITER_EVENT.finish, onFinish);
this.m_pendingWriters[0].on(writer_1.WRITER_EVENT.error, onFinish);
this.m_pendingWriters[0].bind(this);
}
};
if (!this.m_pendingWriters.length) {
writer.on(writer_1.WRITER_EVENT.finish, onFinish);
writer.on(writer_1.WRITER_EVENT.error, onFinish);
writer.bind(this);
}
this.m_pendingWriters.push(writer);
}
async close() {
for (let w of this.m_pendingWriters) {
w.close();
}
this.m_pendingWriters = [];
return await super.close();
}
};
}
}
exports.INode = INode;