tera/src/core/node.ts

619 lines
20 KiB
TypeScript

/*
* @project: TERA
* @version: Development (beta)
* @license: MIT (not for evil)
* @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com]
* Web: https://terafoundation.org
* Twitter: https://twitter.com/terafoundation
* Telegram: https://t.me/terafoundation
*/
"use strict";
import "./library"
import * as net from "net"
import { secp256k1, RBTree } from '../core/library'
var ConnectIDCount = 1;
export class TeraSocket extends net.Socket {
ConnectToServer: boolean;
Node: CNode;
ConnectID: string;
WasClose: number;
SocketStatus: number;
Buf: Buffer;
SocketNum: number;
SocketPrioritet: number;
}
export default class CNode {
addrStr
ip
port
StartFindList
WhiteConnect
GrayConnect
POW
FirstTime
FirstTimeStr
LastTime
LastTimeError
LastTimeTransfer
FromIP
FromPort
Active
Hot
CanHot
CountChildConnect
BlockProcessCount
VersionOK
VersionNum
Delete
DeltaBan
Name
Info
PrevInfo
StartTimeHot
NextHotDelta
LastTimeGetNode
DeltaGlobTime
CountDeltaTime
DeltaTime
SumDeltaTime
TransferCount
StopGetBlock
LevelCount
LevelEnum
TimeMap
bInit
INFO
DoubleConnectCount
StartTimeConnect
NextConnectDelta
StartTimeGetNodes
NextGetNodesDelta
PingStart
NextPing
SendBlockArr
LoadBlockArr
SendBlockCount
LoadBlockCount
SendBlockCountAll
LoadBlockCountAll
WantHardTrafficArr
WantHardTraffic
CanHardTraffic
BufWriteLength
BufWrite
SendPacket: RBTree<any>
ConnectCount
TrafficArr
SendTrafficCurrent
SendTrafficLimit
ErrCount
ErrCountAll
SendPacketNum
Socket: TeraSocket
Socket2: TeraSocket
ReconnectFromServer
SecretForReconnect
Self: boolean
addrStrTemp
DoubleConnection
PubKey
addrArr
SocketStart
WaitConnectFromServer
WaitConnectIP
constructor(addrStr, ip, port) {
this.addrStr = addrStr
this.ip = ip.trim()
this.port = port
this.StartFindList = 0
this.WhiteConnect = 0
this.GrayConnect = 0
this.POW = 0
this.FirstTime = 0
this.FirstTimeStr = ""
this.LastTime = 0
this.LastTimeError = 0
this.LastTimeTransfer = 0
this.FromIP = undefined
this.FromPort = undefined
this.Active = false
this.Hot = false
this.CanHot = false
this.CountChildConnect = 0
this.BlockProcessCount = 0
this.VersionOK = false
this.VersionNum = 0
this.Delete = 0
this.DeltaBan = 300
this.Name = ""
this.Info = ""
this.PrevInfo = ""
this.StartTimeHot = 0
this.NextHotDelta = 1000
this.ResetNode()
}
ResetNode() {
this.LastTimeGetNode = 0
this.DeltaGlobTime = 0
this.CountDeltaTime = 0
this.DeltaTime = 1000
this.SumDeltaTime = 0
this.TransferCount = 0
this.StopGetBlock = 0
this.LevelCount = 0
this.LevelEnum = 100
this.TimeMap = {}
this.bInit = 1
this.INFO = {}
this.DoubleConnectCount = 0
this.StartTimeConnect = 0
this.NextConnectDelta = 1000
this.StartTimeGetNodes = 0
this.NextGetNodesDelta = 1000
this.PingStart = 0
this.NextPing = global.MIN_PERIOD_PING
this.SendBlockArr = []
this.LoadBlockArr = []
this.SendBlockCount = 0
this.LoadBlockCount = 0
this.SendBlockCountAll = 0
this.LoadBlockCountAll = 0
this.WantHardTrafficArr = []
this.WantHardTraffic = 0
this.CanHardTraffic = 0
this.BufWriteLength = 0
this.BufWrite = Buffer.alloc(0)
this.SendPacket = new RBTree(function(a, b) {
return b.PacketNum - a.PacketNum;
} as any)
this.ConnectCount = 0
this.TrafficArr = []
this.SendTrafficCurrent = 0
this.SendTrafficLimit = 0
this.ErrCount = 0
this.ErrCountAll = 0
var Prioritet = this.BlockProcessCount;
global.SERVER.SetNodePrioritet(this, Prioritet)
this.SendPacketNum = 0
}
ConnectStatus() {
if (this.Socket)
return GetSocketStatus(this.Socket);
else
return 0;
}
CreateConnect() {
delete global.SERVER.BAN_IP[this.ip]
let NODE = this;
if (NODE.ConnectStatus()) {
if (NODE.ConnectStatus() === 100)
global.SERVER.AddNodeToActive(NODE)
return;
}
global.AddNodeInfo(NODE, "===CreateConnect===")
global.CloseSocket(NODE.Socket, "CreateConnect")
NODE.SocketStart = Date.now()
NODE.Socket = net.createConnection(NODE.port, NODE.ip, function() {
if (NODE.Socket) {
global.socketInit(NODE.Socket, "s")
global.AddNodeInfo(NODE, "OK connected *" + NODE.Socket.ConnectID)
NODE.Socket.ConnectToServer = true
SetSocketStatus(NODE.Socket, 2)
}
}) as TeraSocket
SetSocketStatus(NODE.Socket, 1)
NODE.Socket.Node = NODE
NODE.Socket.ConnectID = "~C" + ConnectIDCount
ConnectIDCount++
this.SetEventsProcessing(NODE.Socket, 0)
}
CreateReconnection() {
let NODE = this;
global.AddNodeInfo(NODE, "===CreateReconnection===")
global.CloseSocket(NODE.Socket2, "CreateReconnection")
NODE.SocketStart = Date.now()
NODE.Socket2 = net.createConnection(NODE.port, NODE.ip, function() {
if (NODE.Socket2) {
global.socketInit(NODE.Socket2, "s")
global.AddNodeInfo(NODE, "OK Reconnected *" + NODE.Socket2.ConnectID)
NODE.Socket2.ConnectToServer = true
SetSocketStatus(NODE.Socket2, 2)
}
}) as TeraSocket
SetSocketStatus(NODE.Socket2, 1)
NODE.Socket2.Node = NODE
NODE.Socket2.ConnectID = "~R" + ConnectIDCount
ConnectIDCount++
this.SetEventsProcessing(NODE.Socket2, 1)
}
SwapSockets() {
if (!this.Socket2)
return;
var SocketOld = this.Socket;
this.Socket = this.Socket2
this.Socket2 = undefined
this.Socket.Node = this
SetSocketStatus(this.Socket, 100)
this.Socket.Buf = SocketOld.Buf
global.SERVER.LoadBufSocketList.remove(SocketOld)
global.SERVER.LoadBufSocketList.insert(this.Socket)
SocketOld.Buf = undefined
SocketOld.WasClose = 1
SocketOld.Node = undefined
this.ErrCount = 0
}
SetEventsProcessing(Socket, Reconnection) {
let SOCKET = Socket;
let NODE = this;
let RECONNECTION = Reconnection;
SOCKET.on('data', function(data) {
if (Socket.WasClose)
return;
if (GetSocketStatus(SOCKET) === 2) {
SetSocketStatus(SOCKET, 3)
var Buf = global.SERVER.GetDataFromBuf(data);
if (Buf) {
var Res = NODE.SendPOWFromClientToServer(SOCKET, Buf.Data);
if (Res) {
return;
}
}
global.CloseSocket(SOCKET, Buf ? "Method=" + Buf.Method : "=CLIENT ON DATA=")
}
else
if (GetSocketStatus(SOCKET) === 3) {
var Buf = global.SERVER.GetDataFromBuf(data);
if (Buf) {
var Str = Buf.Data;
if (Str && Str.substr(0, 24) === "WAIT_CONNECT_FROM_SERVER") {
global.AddNodeInfo(NODE, "2. CLIENT OK POW")
global.CloseSocket(SOCKET, "WAIT_CONNECT_FROM_SERVER")
NODE.WaitConnectFromServer = 1
NODE.WaitConnectIP = NODE.ip
try {
NODE.SecretForReconnect = global.GetArrFromHex(Str.substr(25))
}
catch (e) {
NODE.SecretForReconnect = []
global.ToLog(e)
}
}
else
if (Str === "OK") {
NODE.NextConnectDelta = 1000
SetSocketStatus(SOCKET, 100)
global.AddNodeInfo(NODE, "4. CLIENT OK CONNECT")
if (RECONNECTION) {
if (NODE.Socket)
SetSocketStatus(NODE.Socket, 200)
}
else {
if (!NODE.Active)
global.SERVER.AddNodeToActive(NODE)
}
return;
}
else
if (Str === "SELF") {
NODE.Self = true
}
else
if (Str === "DOUBLE") {
}
else {
global.AddNodeInfo(NODE, "ERROR:" + Str)
}
}
global.CloseSocket(SOCKET, Buf ? "Method=" + Buf.Method + ":" + Str : "=CLIENT ON DATA=")
}
else {
global.socketRead(Socket, data)
global.SERVER.OnGetFromTCP(NODE, Socket, data)
}
})
SOCKET.on('end', function() {
if (GetSocketStatus(SOCKET))
global.AddNodeInfo(NODE, "Get socket end *" + SOCKET.ConnectID + " Stat: " + SocketStatistic(SOCKET))
if (GetSocketStatus(SOCKET) === 200) {
NODE.SwapSockets()
SOCKET.WasClose = 1
}
})
SOCKET.on('close', function(err) {
if (SOCKET.ConnectID && GetSocketStatus(SOCKET))
global.AddNodeInfo(NODE, "Get socket close *" + SOCKET.ConnectID + " Stat: " + SocketStatistic(SOCKET))
if (!SOCKET.WasClose) {
if (GetSocketStatus(SOCKET) >= 2) {
global.CloseSocket(SOCKET, "GET CLOSE")
}
}
SetSocketStatus(SOCKET, 0)
})
SOCKET.on('error', function(err) {
if (GetSocketStatus(SOCKET) >= 2) {
global.SERVER.AddCheckErrCount(NODE, 1, "ERR##1 : socket")
global.ADD_TO_STAT("ERRORS")
}
})
}
SendPOWFromClientToServer(Socket, data) {
var Node = this;
if (Node.ReconnectFromServer) {
Node.ReconnectFromServer = 0
var Info = this.GetPOWClientData(0);
Info.Reconnect = 1
Info.SecretForReconnect = Node.SecretForReconnect
var BufWrite = global.BufLib.GetBufferFromObject(Info, global.FORMAT_POW_TO_SERVER, 1200, {});
var BufAll = global.SERVER.GetBufFromData("POW_CONNECT7", BufWrite, 1);
Socket.write(BufAll)
return 1;
}
try {
var Buf = global.BufLib.GetObjectFromBuffer(data, global.FORMAT_POW_TO_CLIENT, {});
} catch (e) {
global.SERVER.SendCloseSocket(Socket, "FORMAT_POW_TO_CLIENT")
return 0;
}
if (global.CompareArr(Buf.addrArr, global.SERVER.addrArr) === 0) {
Node.Self = true
global.AddNodeInfo(Node, "END: SELF")
global.SERVER.SendCloseSocket(Socket, "SELF")
return;
}
var addrStr = global.GetHexFromAddres(Buf.addrArr);
if (!Node.StartFindList && addrStr !== Node.addrStr) {
global.AddNodeInfo(Node, "END: CHANGED ADDR: " + Node.addrStr.substr(0, 16) + "->" + addrStr.substr(0, 16))
global.SERVER.SendCloseSocket(Socket, "ADDRESS_HAS_BEEN_CHANGED")
return;
}
if (Node.addrStrTemp) {
global.AddNodeInfo(Node, "Set Addr = " + addrStr)
Node.addrStr = addrStr
global.SERVER.CheckNodeMap(Node)
}
var Result = false;
if (Buf.PubKeyType === 2 || Buf.PubKeyType === 3) {
Result = secp256k1.verify(Buffer.from(global.shaarr(addrStr)), Buffer.from(Buf.Sign), Buffer.from([Buf.PubKeyType].concat(Buf.addrArr)))
if (!Result) {
Result = secp256k1.verify(Buffer.from(global.sha3(addrStr)), Buffer.from(Buf.Sign), Buffer.from([Buf.PubKeyType].concat(Buf.addrArr)))
}
}
if (!Result) {
global.ToLog("END: ERROR_SIGN_SERVER ADDR: " + addrStr.substr(0, 16) + " from ip: " + Socket.remoteAddress)
global.AddNodeInfo(Node, "END: ERROR_SIGN_SERVER ADDR: " + addrStr.substr(0, 16) + " from ip: " + Socket.remoteAddress)
global.SERVER.SendCloseSocket(Socket, "ERROR_SIGN_SERVER")
return;
}
if (Buf.MIN_POWER_POW_HANDSHAKE > 1 + global.MIN_POWER_POW_HANDSHAKE) {
global.ToLog("END: BIG_MIN_POWER_POW_HANDSHAKE ADDR: " + addrStr.substr(0, 16) + " from ip: " + Socket.remoteAddress)
return 0;
}
var TestNode = global.SERVER.NodesMap[addrStr];
if (TestNode && TestNode !== Node) {
if (GetSocketStatus(TestNode.Socket)) {
global.AddNodeInfo(Node, "DoubleConnection find")
Node.DoubleConnection = true
return 0;
}
else {
global.AddNodeInfo(Node, "DoubleConnection find")
TestNode.DoubleConnection = true
}
}
Node.PubKey = Buffer.from([Buf.PubKeyType].concat(Buf.addrArr))
Node.addrArr = Buf.addrArr
Node.addrStr = addrStr
if (global.CompareArr(global.SERVER.addrArr, Node.addrArr) === 0) {
Node.Self = true
return 0;
}
var Hash = global.shaarr2(Buf.addrArr, Buf.HashRND);
var nonce = global.CreateNoncePOWExternMinPower(Hash, 0, Buf.MIN_POWER_POW_HANDSHAKE);
var Info;
if (global.WALLET.WalletOpen && global.IsDeveloperAccount(global.WALLET.PubKeyArr)) {
Info = this.GetPOWClientData(0)
Info.Reconnect = 255
Info.Sign = secp256k1.sign(global.SHA3BUF(Hash), global.WALLET.KeyPair.getPrivateKey('')).signature
Result = global.CheckDevelopSign(Hash, Info.Sign)
if (!Result) {
throw "ERROR DEVELOPSIGN!";
}
}
else {
Info = this.GetPOWClientData(nonce)
Info.PubKeyType = global.SERVER.PubKeyType
Info.Sign = secp256k1.sign(Buffer.from(Hash), global.SERVER.KeyPair.getPrivateKey('')).signature
}
var BufWrite = global.BufLib.GetBufferFromObject(Info, global.FORMAT_POW_TO_SERVER, 1200, {});
var BufAll = global.SERVER.GetBufFromData("POW_CONNECT6", BufWrite, 1);
Socket.write(BufAll)
return 1;
}
GetPOWClientData(nonce) {
var Node = this;
var Info: any = {};
Info.DEF_NETWORK = global.GetNetworkName()
Info.DEF_VERSION = global.DEF_VERSION
Info.DEF_CLIENT = global.DEF_CLIENT
Info.addrArr = global.SERVER.addrArr
Info.ToIP = Node.ip
Info.ToPort = Node.port
Info.FromIP = global.SERVER.ip
Info.FromPort = global.SERVER.port
Info.nonce = nonce
Info.Reconnect = 0
Info.SendBytes = 0
Info.SecretForReconnect = []
Info.Reserv = []
if (global.GrayConnect())
Info.GrayConnect = 1
return Info;
}
write(BufWrite) {
if (!this.Socket)
return;
global.socketWrite(this.Socket, BufWrite)
try {
this.Socket.write(BufWrite)
}
catch (e) {
global.ToError(e)
this.Socket.WasClose = 1
this.Socket.SocketStatus = 0
this.Socket.Node = undefined
}
}
};
global.socketInit = function(Socket, Str) {
if (!Socket)
return;
Socket.GetBytes = 0;
Socket.SendBytes = 0;
Socket.ConnectID = "" + ConnectIDCount + Str;
ConnectIDCount++;
};
global.socketRead = function(Socket, Buf) {
Socket.GetBytes += Buf.length;
};
global.socketWrite = function(Socket, Buf) {
Socket.SendBytes += Buf.length;
};
global.CloseSocket = function(Socket, StrError?, bHide?) {
if (!Socket || Socket.WasClose) {
if (Socket)
Socket.SocketStatus = 0;
return;
}
var Node = Socket.Node;
if (Socket.Node && Socket.Node.Socket2 === Socket && Socket.Node.Socket && Socket.Node.Socket.SocketStatus === 200)
SetSocketStatus(Socket.Node.Socket, 100);
var StrNode = NodeInfo(Socket.Node);
Socket.WasClose = 1;
Socket.SocketStatus = 0;
Socket.Node = undefined;
Socket.end();
if (!bHide)
global.AddNodeInfo(Node, "CLOSE " + StrNode + " *" + Socket.ConnectID + " - " + StrError);
};
function SetSocketStatus(Socket, Status) {
if (Socket && Socket.SocketStatus !== Status) {
if (Status === 100 && (Socket.SocketStatus !== 3 && Socket.SocketStatus !== 200)) {
global.ToLogTrace("===================ERROR=================== " + Status);
return;
}
if (Status === 100 && Socket.Node)
Socket.Node.LastTime = global.GetCurrentTime() - 0;
Socket.SocketStatus = Status;
Socket.TimeStatus = Date.now();
}
};
function GetSocketStatus(Socket) {
if (Socket && Socket.SocketStatus) {
if (Socket.SocketStatus !== 100) {
var Delta = Date.now() - Socket.TimeStatus;
if (Delta > global.MAX_WAIT_PERIOD_FOR_STATUS) {
global.CloseSocket(Socket, "MAX_WAIT_PERIOD_FOR_STATUS = " + Socket.SocketStatus + " time = " + Delta);
}
}
return Socket.SocketStatus;
}
else {
return 0;
}
};
function SocketInfo(Socket) {
if (Socket)
return "*" + Socket.ConnectID;
else
return "";
};
function SocketStatistic(Socket) {
if (!Socket)
return "";
var Str = "";
if (!Socket.SendBytes)
Socket.SendBytes = 0;
if (!Socket.GetBytes)
Socket.GetBytes = 0;
if (Socket.SendBytes)
Str += " Send=" + Socket.SendBytes;
if (Socket.GetBytes)
Str += " Get=" + Socket.GetBytes;
if (GetSocketStatus(Socket))
Str += " SocketStatus=" + GetSocketStatus(Socket);
if (Str === "")
Str = "0";
return Str;
};
function NodeInfo(Node) {
if (Node)
return "" + Node.ip + ":" + Node.port + " " + SocketInfo(Node.Socket);
else
return "";
};
function NodeName(Node) {
if (!Node)
return "";
if (Node.Name)
return Node.Name;
if (global.LOCAL_RUN)
return "" + Node.port;
else {
return "" + Node.ip + ":" + Node.addrStr.substr(0, 6);
}
};
function FindNodeByAddr(Addr, bConnect) {
var Node = global.SERVER.NodesMap[Addr.trim()];
if (Node && Node.ConnectStatus() === 100)
return Node;
if (Node && bConnect) {
Node.NextConnectDelta = 1000;
global.SERVER.StartConnectTry(Node);
return false;
}
return undefined;
};
function AddNodeInfo(Node, Str, bSet) {
if (!global.STAT_MODE)
return;
if (!Node)
return;
if (!Node.Info)
Node.Info = "";
if (bSet) {
Node.Info = "";
}
else {
if (Node.Socket && Node.Socket.Info) {
Node.Info += Node.Socket.Info + "\n";
Node.Socket.Info = "";
}
}
if (Node.Info.length > 1000) {
Node.PrevInfo = Node.Info;
Node.Info = "";
}
{
var timesend = global.GetStrOnlyTimeUTC();
Str = timesend + " " + Str;
Node.Info += Str + "\n";
}
};
global.SocketStatistic = SocketStatistic;
global.GetSocketStatus = GetSocketStatus;
global.SetSocketStatus = SetSocketStatus;
global.NodeInfo = NodeInfo;
global.NodeName = NodeName;
global.SocketInfo = SocketInfo;
global.FindNodeByAddr = FindNodeByAddr;
global.AddNodeInfo = AddNodeInfo;