/* * @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://web.telegram.org/#/im?p=@terafoundation */ "use strict"; const crypto = require('crypto'); const CNode = require("./node"); global.PERIOD_FOR_RECONNECT = 3600 * 1000; global.CHECK_DELTA_TIME = {Num:0, bUse:0, StartBlockNum:0, EndBlockNum:0, bAddTime:0, DeltaTime:0, Sign:[]}; global.CHECK_POINT = {BlockNum:0, Hash:[], Sign:[]}; global.CODE_VERSION = {BlockNum:0, addrArr:[], LevelUpdate:0, BlockPeriod:0, VersionNum:UPDATE_CODE_VERSION_NUM, Hash:[], Sign:[], StartLoadVersionNum:0}; global.START_LOAD_CODE = {}; const MAX_PERIOD_GETNODES = 120 * 1000; global.MIN_PERIOD_PING = 4 * 1000; const MAX_PERIOD_PING = 120 * 1000; var MAX_PING_FOR_CONNECT = 300; var MAX_TIME_CORRECT = 3 * 3600 * 1000; global.MAX_WAIT_PERIOD_FOR_HOT = 4 * CONSENSUS_PERIOD_TIME; const PERIOD_FOR_START_CHECK_TIME = 300; module.exports = class CConnect extends require("./transfer-msg") { constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) { super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) this.StartTime = Date.now() this.WasNodesSort = false this.LevelNodes = [] this.NodesArr = [] this.NodesArrUnSort = [] this.NodesMap = {} this.NodesIPMap = {} this.WasNodesSort = true this.PerioadAfterCanStart = 0 this.КодДляРазработчикаХекс = GetHexFromArr(this.KeyPair.computeSecret(DEVELOP_PUB_KEY, null)) this.DO_CONSTANT() if(!global.ADDRLIST_MODE && !this.VirtualMode) { setInterval(this.StartPingPong.bind(this), 1000) setInterval(this.DeleteBadConnectingByTimer.bind(this), MAX_WAIT_PERIOD_FOR_STATUS / 2) setInterval(this.StartCheckTransferTree.bind(this), 1000) } setInterval(this.NodesArrSort.bind(this), 10000) } DO_CONSTANT() { this.CommonKey = GetHexFromArr(WALLET.HashProtect(global.COMMON_KEY)) this.KeyToNode = shaarr(global.COMMON_KEY) this.NameToNode = this.ValueToXOR("Name", global.NODES_NAME) } СтатДанныеОтладкиИзБлока() { var Массив = []; if(this.СтатБлок && this.СтатБлок.SeqHash) { WriteArrToArr(Массив, this.ValueToXORDevelop("Stat:BlockNum", this.СтатБлок.BlockNum, "uint"), 6) WriteArrToArr(Массив, this.ValueToXORDevelop("Stat:SeqHash", this.СтатБлок.SeqHash, "hash"), 32) WriteArrToArr(Массив, this.ValueToXORDevelop("Stat:AddrHash", this.СтатБлок.AddrHash, "hash"), 32) } return Массив; } ДоступенКлючРазработчика(Node) { if(Node.PubKey && WALLET.WalletOpen !== false && IsDeveloperAccount(WALLET.PubKeyArr)) { return 1; } return 0; } БлокИзДанных(Node, Arr) { var Block = {}; if(this.ДоступенКлючРазработчика(Node) && !IsZeroArr(Arr)) { var Data = BufLib.GetObjectFromBuffer(Arr, "{BlockNum:arr6,SeqHash:arr32,AddrHash:arr32}", {}); Block.BlockNum = this.ValueFromXORDevelop(Node, "Stat:BlockNum", Data.BlockNum, "uint") Block.SeqHash = this.ValueFromXORDevelop(Node, "Stat:SeqHash", Data.SeqHash, "hash") Block.AddrHash = this.ValueFromXORDevelop(Node, "Stat:AddrHash", Data.AddrHash, "hash") } return Block; } StartConnectTry(Node) { var Delta = Date.now() - Node.StartTimeConnect; if(Delta >= Node.NextConnectDelta && this.IsCanConnect(Node)) { if(!GetSocketStatus(Node.Socket)) { Node.StartTimeConnect = Date.now() if(Delta < 60 * 1000) Node.NextConnectDelta = Node.NextConnectDelta * 2 else Node.NextConnectDelta = Math.trunc(Node.NextConnectDelta * 1.2) Node.CreateConnect() } } } FindRunNodeContext(addrArr, ip, port, bUpdate) { var Node, addrStr; addrStr = GetHexFromAddres(addrArr) Node = this.NodesMap[addrStr] if(!Node) { var key = "" + ip + ":" + port; Node = this.NodesIPMap[key] if(!Node) { Node = this.GetNewNode(addrStr, ip, port) } } if(Node.addrStr !== addrStr) { delete this.NodesMap[Node.addrStr] this.NodesMap[addrStr] = Node Node.addrStrTemp = undefined } Node.addrArr = addrArr Node.addrStr = addrStr Node.ip = ip.trim() Node.port = port return Node; } CheckNodeMap(Node) { if(Node.addrStrTemp && Node.addrStrTemp !== Node.addrStr) { delete this.NodesMap[Node.addrStrTemp] var Node2 = this.NodesMap[Node.addrStr]; if(Node2 && Node2 !== Node) { Node2.Delete = 1 AddNodeInfo(Node2, "FIND DOUBLE!!") delete this.NodesMap[Node.addrStr] } this.NodesMap[Node.addrStr] = Node Node.addrStrTemp = undefined } } StartHandshake(Node) { return this.StartConnectTry(Node); } StartPingPong() { if(glStopNode) return ; if(global.CAN_START) this.PerioadAfterCanStart++ this.TimeDevCorrect() var arr = SERVER.GetActualNodes(); for(var i = 0; i < arr.length; i++) { var Node = arr[i]; if(this.IsCanConnect(Node) && !Node.IsAddrList) { if(Node.Hot) Node.NextPing = MIN_PERIOD_PING if(Node.NextPing < MIN_PERIOD_PING) Node.NextPing = MIN_PERIOD_PING var Delta = Date.now() - Node.PingStart; if(Delta >= Node.NextPing) { Node.PingStart = Date.now() Node.NextPing = Node.NextPing * 1.5 if(Node.NextPing > MAX_PERIOD_PING) Node.NextPing = MAX_PERIOD_PING if(!Node.PingNumber) Node.PingNumber = 0 Node.PingNumber++ var Context = {"StartTime":GetCurrentTime(0), PingNumber:Node.PingNumber}; this.SendF(Node, {"Method":"PING", "Context":Context, "Data":this.GetPingData(Node)}) } } } } GetPingData(Node) { var GrayAddres = 0; if(GrayConnect()) GrayAddres = 1 var BlockNumHash = Math.trunc((GetCurrentBlockNumByTime() - BLOCK_PROCESSING_LENGTH2) / PERIOD_ACCOUNT_HASH) * PERIOD_ACCOUNT_HASH; var AccountsHash = DApps.Accounts.GetHashOrUndefined(BlockNumHash); var CheckPointHashDB = []; if(CHECK_POINT.BlockNum && CHECK_POINT.BlockNum <= this.BlockNumDB) { var Block = this.ReadBlockHeaderFromMapDB(CHECK_POINT.BlockNum); if(Block) { CheckPointHashDB = Block.Hash } } var HashDB = []; if(this.BlockNumDB > 0) { var Block = this.ReadBlockHeaderFromMapDB(this.BlockNumDB); if(Block) HashDB = Block.Hash } var LevelCount = this.GetLevelEnum(Node); var StopGetBlock = global.STOPGETBLOCK; if(!StopGetBlock && this.BusyLevel) { if(Node.BlockProcessCount <= this.BusyLevel) StopGetBlock = 1 } var СтатДанные = []; var DirectMAccount = 0; var Ret = {VERSIONMAX:DEF_VERSION, FIRST_TIME_BLOCK:0, PingVersion:3, GrayConnect:GrayAddres, Reserve2:0, AutoCorrectTime:AUTO_CORRECT_TIME, LevelCount:LevelCount, Time:(GetCurrentTime() - 0), BlockNumDB:this.BlockNumDB, LoadHistoryMode:this.LoadHistoryMode, CanStart:global.CAN_START, CheckPoint:CHECK_POINT, Reserv3:[], Key:this.KeyToNode, Name:this.NameToNode, TrafficFree:this.SendTrafficFree, AccountBlockNum:BlockNumHash, AccountsHash:AccountsHash, MemoryUsage:Math.trunc(process.memoryUsage().heapTotal / 1024 / 1024), CheckDeltaTime:CHECK_DELTA_TIME, CodeVersion:CODE_VERSION, IsAddrList:global.ADDRLIST_MODE, CheckPointHashDB:CheckPointHashDB, PortWeb:HTTP_HOSTING_PORT, HashDB:HashDB, StopGetBlock:StopGetBlock, }; return Ret; } static PING_F(bSend) { return "{\ VERSIONMAX:str15,\ PingVersion:byte,\ GrayConnect:byte,\ Reserve2:byte,\ AutoCorrectTime:byte,\ LevelCount:uint16,\ Time:uint,\ BlockNumDB:uint,\ LoadHistoryMode:byte,\ CanStart:byte,\ CheckPoint:{BlockNum:uint,Hash:hash,Sign:arr64},\ Reserv3:arr38,\ Key:arr32,\ Name:arr32,\ TrafficFree:uint,\ AccountBlockNum:uint,\ AccountsHash:hash,\ MemoryUsage:uint,\ CheckDeltaTime:{Num:uint,bUse:byte,StartBlockNum:uint,EndBlockNum:uint,bAddTime:byte,DeltaTime:uint,Sign:arr64},\ CodeVersion:{BlockNum:uint,addrArr:arr32,LevelUpdate:byte,BlockPeriod:uint,VersionNum:uint,Hash:hash,Sign:arr64},\ IsAddrList:byte,\ CheckPointHashDB:hash,\ PortWeb:uint16,\ HashDB:hash,\ StopGetBlock:uint,\ }"; } static PONG_F(bSend) { return CConnect.PING_F(bSend); } PING(Info, CurTime) { this.DoPingData(Info, 1) this.SendF(Info.Node, {"Method":"PONG", "Context":Info.Context, "Data":this.GetPingData(Info.Node)}) } DoPingData(Info, bCheckPoint) { var Node = Info.Node; var Data = this.DataFromF(Info); Info.Node.VERSIONMAX = Data.VERSIONMAX if(Data.PingVersion >= 3 && global.COMMON_KEY && CompareArr(Data.Key, this.KeyToNode) === 0) { Node.Name = this.ValueFromXOR(Node, "Name", Data.Name) if(Node.BlockProcessCount < 5000000 + global.TRUST_PROCESS_COUNT) Node.BlockProcessCount = 5000000 + global.TRUST_PROCESS_COUNT } else { Node.Name = "" } Node.INFO = Data Node.INFO.WasPing = 1 Node.LevelCount = Data.LevelCount Node.LoadHistoryMode = Data.LoadHistoryMode Node.LastTime = GetCurrentTime() - 0 Node.NextConnectDelta = 1000 Node.StopGetBlock = Data.StopGetBlock Node.portweb = Data.PortWeb if(bCheckPoint) { this.CheckCheckPoint(Data, Info.Node) this.CheckCodeVersion(Data, Info.Node) this.CheckDeltaTime(Data, Info.Node) } } PONG(Info, CurTime) { var Data = this.DataFromF(Info); var Node = Info.Node; if(!Info.Context) return ; if(!Info.Context.StartTime) return ; if(Info.Context.PingNumber !== Node.PingNumber) return ; this.DoPingData(Info, 0) var DeltaTime = GetCurrentTime(0) - Info.Context.StartTime; Node.DeltaTimeM = DeltaTime Node.SumDeltaTime += DeltaTime Node.CountDeltaTime++ Node.DeltaTime = Math.trunc(Node.SumDeltaTime / Node.CountDeltaTime) if(!Node.DeltaTime) Node.DeltaTime = 1000 if(DeltaTime) { Node.DeltaGlobTime = GetCurrentTime() - (Data.Time + DeltaTime / 2) } this.CheckCheckPoint(Data, Info.Node) if(!START_LOAD_CODE.StartLoadVersionNum) START_LOAD_CODE.StartLoadVersionNum = 0 this.CheckCodeVersion(Data, Info.Node) if(!global.CAN_START) { if(DeltaTime > MAX_PING_FOR_CONNECT) ToLog("DeltaTime=" + DeltaTime + ">" + MAX_PING_FOR_CONNECT + " ms - " + NodeInfo(Node), 2) } var Times; if(DeltaTime <= MAX_PING_FOR_CONNECT) { Times = Node.Times if(!Times || Times.Count >= 10) { Times = {SumDelta:0, Count:0, AvgDelta:0, Arr:[]} Node.Times = Times } var Time1 = Data.Time; var Time2 = GetCurrentTime(); var Delta2 = - (Time2 - Time1 - DeltaTime / 2); Times.Arr.push(Delta2) Times.SumDelta += Delta2 Times.Count++ Times.AvgDelta = Times.SumDelta / Times.Count if(Times.Count >= 2) { Times.Arr.sort(function (a,b) { return Math.abs(a) - Math.abs(b); }) Node.AvgDelta = Times.Arr[0] } if(global.AUTO_CORRECT_TIME) { this.CorrectTime() } } else { } ADD_TO_STAT("MAX:PING_TIME", DeltaTime) if(!global.CAN_START) if(Times && Times.Count >= 1 && Times.AvgDelta <= 200) { ToLog("****************************************** CAN_START") global.CAN_START = true } this.CheckDeltaTime(Data, Info.Node) } CheckCheckPoint(Data, Node) { if(CREATE_ON_START) return ; if(Data.CheckPoint.BlockNum && Data.CheckPoint.BlockNum > CHECK_POINT.BlockNum) { var SignArr = arr2(Data.CheckPoint.Hash, GetArrFromValue(Data.CheckPoint.BlockNum)); if(CheckDevelopSign(SignArr, Data.CheckPoint.Sign)) { global.CHECK_POINT = Data.CheckPoint this.ResetNextPingAllNode() if(Data.CheckPoint.BlockNum < this.BlockNumDBMin) return ; var Block = this.ReadBlockHeaderDB(CHECK_POINT.BlockNum); if(Block && CompareArr(Block.Hash, CHECK_POINT.Hash) !== 0) { this.BlockNumDB = CHECK_POINT.BlockNum - 1 this.TruncateBlockDB(this.BlockNumDB) this.StartSyncBlockchain(Node, 0, 1) } } else { Node.NextConnectDelta = 60 * 1000 ToLog("Error Sign CheckPoint=" + Data.CheckPoint.BlockNum + " from " + NodeInfo(Node)) this.AddCheckErrCount(Node, 10, "Error Sign CheckPoint") } } } CheckDeltaTime(Data, Node) { if(global.AUTO_CORRECT_TIME) if(global.CAN_START && !CREATE_ON_START) { if(Data.CheckDeltaTime.Num > CHECK_DELTA_TIME.Num) { var SignArr = this.GetSignCheckDeltaTime(Data.CheckDeltaTime); if(CheckDevelopSign(SignArr, Data.CheckDeltaTime.Sign)) { global.CHECK_DELTA_TIME = Data.CheckDeltaTime } else { Node.NextConnectDelta = 60 * 1000 ToLog("Error Sign CheckDeltaTime Num=" + Data.CheckDeltaTime.Num + " from " + NodeInfo(Node)) this.AddCheckErrCount(Node, 10, "Error Sign CheckDeltaTime") } } } } CheckCodeVersion(Data, Node) { var CodeVersion = Data.CodeVersion; Node.VersionNum = CodeVersion.VersionNum if(CodeVersion.VersionNum >= MIN_CODE_VERSION_NUM) { Node.VersionOK = true } else { Node.VersionOK = false } if(Node.VersionOK) { Node.CanHot = true if(CHECK_POINT.BlockNum && Data.CheckPoint.BlockNum) if(CHECK_POINT.BlockNum !== Data.CheckPoint.BlockNum || CompareArr(CHECK_POINT.Hash, Data.CheckPoint.Hash) !== 0) { Node.CanHot = false Node.NextConnectDelta = 60 * 1000 } } else { Node.CanHot = false if(!Node.VersionOK) { Node.NextConnectDelta = 60 * 1000 } } var bLoadVer = 0; if(CodeVersion.BlockNum && (CodeVersion.BlockNum <= GetCurrentBlockNumByTime() || CodeVersion.BlockPeriod === 0) && CodeVersion.BlockNum > CODE_VERSION.BlockNum && !IsZeroArr(CodeVersion.Hash) && (CodeVersion.VersionNum > CODE_VERSION.VersionNum && CodeVersion.VersionNum > START_LOAD_CODE.StartLoadVersionNum || CodeVersion.VersionNum === CODE_VERSION.VersionNum && IsZeroArr(CODE_VERSION.Hash))) { bLoadVer = 1 } if(bLoadVer) { var Level = AddrLevelArrFromBegin(this.addrArr, CodeVersion.addrArr); if(CodeVersion.BlockPeriod) { var Delta = GetCurrentBlockNumByTime() - CodeVersion.BlockNum; Level += Delta / CodeVersion.BlockPeriod } if(Level >= CodeVersion.LevelUpdate) { var SignArr = arr2(CodeVersion.Hash, GetArrFromValue(CodeVersion.VersionNum)); if(CheckDevelopSign(SignArr, CodeVersion.Sign)) { ToLog("Get new CodeVersion = " + CodeVersion.VersionNum + " HASH:" + GetHexFromArr(CodeVersion.Hash).substr(0, 20)) if(CodeVersion.VersionNum > CODE_VERSION.VersionNum && CodeVersion.VersionNum > START_LOAD_CODE.StartLoadVersionNum) { this.StartLoadCode(Node, CodeVersion) } else { CODE_VERSION = CodeVersion } } else { ToLog("Error Sign CodeVersion=" + CodeVersion.VersionNum + " from " + NodeInfo(Node) + " HASH:" + GetHexFromArr(CodeVersion.Hash).substr(0, 20)) ToLog(JSON.stringify(CodeVersion)) this.AddCheckErrCount(Node, 10, "Error Sign CodeVersion") Node.NextConnectDelta = 60 * 1000 } } } } GetSignCheckDeltaTime(Data) { var Buf = BufLib.GetBufferFromObject(Data, "{Num:uint,bUse:byte,StartBlockNum:uint,EndBlockNum:uint,bAddTime:byte,DeltaTime:uint}", 1000, {}); return shaarr(Buf); } ResetNextPingAllNode() { var arr = SERVER.GetActualNodes(); for(var i = 0; i < arr.length; i++) { var Node2 = arr[i]; if(Node2 && Node2.NextPing > 5 * 1000) Node2.NextPing = 5 * 1000 } } StartDisconnectHot(Node, StrError, bDeleteHot) { AddNodeInfo(Node, "DisconnectHot:" + StrError) if(Node.Active && Node.Hot) { AddNodeInfo(Node, "SEND DISCONNECTHOT") this.Send(Node, {"Method":"DISCONNECTHOT", "Context":{}, "Data":StrError}, STR_TYPE) } this.DeleteNodeFromHot(Node) } DISCONNECTHOT(Info, CurTime) { this.DeleteNodeFromHot(Info.Node) ADD_TO_STAT("DISCONNECTHOT") AddNodeInfo(Info.Node, "GET DISCONNECTHOT:" + Info.Data) } StartGetNodes(Node) { if(glStopNode) return ; var Delta = Date.now() - Node.StartTimeGetNodes; if(Delta >= Node.NextGetNodesDelta) { Node.StartTimeGetNodes = Date.now() Node.NextGetNodesDelta = Math.min(Node.NextGetNodesDelta * 2, MAX_PERIOD_GETNODES) if(global.ADDRLIST_MODE) Node.NextGetNodesDelta = MAX_PERIOD_GETNODES this.Send(Node, {"Method":"GETNODES", "Context":{}, "Data":undefined}) } } GETNODES(Info, CurTime) { this.SendF(Info.Node, {"Method":"RETGETNODES", "Context":Info.Context, "Data":{arr:this.GetDirectNodesArray(false, 0, 1), IsAddrList:global.ADDRLIST_MODE, }}, MAX_NODES_RETURN * 250 + 300) } static RETGETNODES_F() { return "{arr:[\ {\ addrArr:arr32,\ ip:str20,\ port:uint16,\ portweb:uint16,\ LastTime:uint,\ DeltaTime:uint,\ Reserv:arr8\ }\ ],\ IsAddrList:byte}"; } RETGETNODES(Info, CurTime) { var Data = this.DataFromF(Info); var arr = Data.arr; if(arr && arr.length > 0) { for(var i = 0; i < arr.length; i++) { arr[i].addrStr = GetHexFromArr(arr[i].addrArr) var Item = this.AddToArrNodes(arr[i], true); if(Item) Item.LastTimeGetNode = CurTime - 0 } } Info.Node.IsAddrList = Data.IsAddrList AddNodeInfo(Info.Node, "RETGETNODES length=" + arr.length) } static RETGETNODES2_F() { return "{arr:[\ {\ addrStr:str64,\ ip:str30,\ port:uint16,\ portweb:uint16,\ LastTime:uint,\ DeltaTime:uint,\ StatData:arr70\ }\ ],\ IsAddrList:byte}"; } RETGETNODES2(Info, CurTime) { var Data = this.DataFromF(Info); var arr = Data.arr; if(arr && arr.length > 0) { for(var i = 0; i < arr.length; i++) { var Item = this.AddToArrNodes(arr[i], true); if(Item) Item.LastTimeGetNode = CurTime - 0 } } Info.Node.IsAddrList = Data.IsAddrList AddNodeInfo(Info.Node, "RETGETNODES2 length=" + arr.length) } GetNewNode(addrStr, ip, port) { var Node = new CNode(addrStr, ip, port); this.AddToArrNodes(Node, false) return Node; } IsCanConnect(Node) { if(Node.addrStr === this.addrStr || this.NodeInBan(Node) || Node.Delete || Node.Self || Node.DoubleConnection) return false; if(Node.ip === this.ip && Node.port === this.port) return false; if(this.addrStr === Node.addrStr) return false; return true; } GetDirectNodesArray(bAll, bWebPort, bGetAddrArr) { var ret = []; var Value = {addrStr:this.addrStr, ip:this.ip, port:this.port, LastTime:0, DeltaTime:0, Hot:true, BlockProcessCount:0, portweb:HTTP_HOSTING_PORT, }; if(bGetAddrArr) Value.addrArr = GetArrFromHex(Value.addrStr) ret.push(Value) var len = this.NodesArr.length; var UseRandom = 0; if(len > MAX_NODES_RETURN && !bAll) { UseRandom = 1 len = MAX_NODES_RETURN } var mapWasAdd = {}; var CurTime = GetCurrentTime(); for(var i = 0; i < len; i++) { var Item; if(UseRandom) { Item = this.NodesArr[random(this.NodesArr.length)] if(mapWasAdd[Item.addrStr]) { continue; } mapWasAdd[Item.addrStr] = 1 } else { Item = this.NodesArr[i] } if(bWebPort && !Item.portweb) continue; if(!this.IsCanConnect(Item)) continue; if(Item.GrayConnect) continue; if(Item.BlockProcessCount < 0) continue; if(!GrayConnect() && Item.LastTime - 0 < CurTime - 3600 * 1000) continue; var Value = {addrStr:Item.addrStr, ip:Item.ip, port:Item.port, FirstTime:Item.FirstTime, FirstTimeStr:Item.FirstTimeStr, LastTime:Item.LastTime - 0, DeltaTime:Item.DeltaTime, Hot:Item.Hot, BlockProcessCount:Item.BlockProcessCount, Name:Item.Name, portweb:Item.portweb, }; if(bGetAddrArr) Value.addrArr = GetArrFromHex(Value.addrStr) ret.push(Value) } return ret; } AddToArrNodes(Item) { if(Item.addrStr === "" || Item.addrStr === this.addrStr) return ; var Node; var key = Item.ip + ":" + Item.port; Node = this.NodesMap[Item.addrStr] if(!Node) { Node = this.NodesIPMap[key] } if(!Node) { if(Item instanceof CNode) Node = Item else Node = new CNode(Item.addrStr, Item.ip, Item.port) Node.id = this.NodesArr.length Node.addrArr = GetAddresFromHex(Node.addrStr) this.NodesMap[Node.addrStr] = Node this.NodesArr.push(Node) this.NodesArrUnSort.push(Node) ADD_TO_STAT("AddToNodes") } this.NodesMap[Node.addrStr] = Node this.NodesIPMap[key] = Node if(Node.addrArr && CompareArr(Node.addrArr, this.addrArr) === 0) { Node.Self = true } if(Item.BlockProcessCount) Node.BlockProcessCount = Item.BlockProcessCount if(Item.FirstTime) { Node.FirstTime = Item.FirstTime Node.FirstTimeStr = Item.FirstTimeStr } if(Item.Name) Node.Name = Item.Name if(Item.portweb) Node.portweb = Item.portweb return Node; } NodesArrSort() { this.NodesArr.sort(SortNodeBlockProcessCount) if((GrayConnect() || !this.LoadHistoryMode) && Date.now() - this.StartTime > 120 * 1000) { var arr = this.GetDirectNodesArray(true).slice(1, 200); SaveParams(GetDataPath("nodes.lst"), arr) } } LoadNodesFromFile() { var arr = LoadParams(GetDataPath("nodes.lst"), []); arr.sort(SortNodeBlockProcessCount) for(var i = 0; i < arr.length; i++) { if(arr[i].LastTime) { if(typeof arr[i].LastTime === "string") arr[i].LastTime = 0 } this.AddToArrNodes(arr[i], true) } } GetLevelEnum(Node) { var Level = this.AddrLevelNode(Node); var arr0 = this.LevelNodes[Level]; if(!arr0) { Node.LevelEnum = 1 return 1; } var arr = [].concat(arr0); var bWas = 0; for(var n = 0; n < arr.length; n++) { if(arr[n] === Node) { bWas = 1 break; } } if(!bWas) arr.push(Node) arr.sort(SortNodeBlockProcessCount) for(var n = 0; n < arr.length; n++) { if(arr[n] === Node) { Node.LevelEnum = 1 + n break; } } return Node.LevelEnum; } StartAddLevelConnect(Node) { if(!global.CAN_START) return ; ADD_TO_STAT("NETCONFIGURATION") if(Node.Active && Node.CanHot) this.SendF(Node, {"Method":"ADDLEVELCONNECT", "Context":{}, "Data":this.GetLevelEnum(Node)}) } static ADDLEVELCONNECT_F() { return "uint"; } ADDLEVELCONNECT(Info, CurTime) { Info.Node.LevelCount = this.DataFromF(Info) var ret; var Count; if(!global.CAN_START) return ; if(Info.Node.GrayConnect || GrayConnect()) return ; var Count = this.GetLevelEnum(Info.Node); var bAdd = this.AddLevelConnect(Info.Node); if(bAdd) { ret = {result:1, Count:Count} } else { ret = {result:0, Count:Count} } AddNodeInfo(Info.Node, "GET ADDLEVELCONNECT, DO bAdd=" + bAdd) this.SendF(Info.Node, {"Method":"RETADDLEVELCONNECT", "Context":Info.Context, "Data":ret}) } AddLevelConnect(Node) { if(!global.CAN_START) return false; var Level = this.AddrLevelNode(Node); Node.Hot = true var arr = this.LevelNodes[Level]; if(!arr) { arr = [] this.LevelNodes[Level] = arr } var bWas = 0; for(var i = 0; i < arr.length; i++) if(arr[i] === Node) { bWas = 1 } if(!bWas) arr.push(Node) Node.TransferCount = 0 if(this.LoadHistoryMode) Node.LastTimeTransfer = (GetCurrentTime() - 0) + 30 * 1000 else Node.LastTimeTransfer = (GetCurrentTime() - 0) + 10 * 1000 Node.CanHot = true this.CheckDisconnectHot(Level) if(!Node.CanHot) return false; this.SendGetMessage(Node) ADD_TO_STAT("NETCONFIGURATION") ADD_TO_STAT("AddLevelConnect") AddNodeInfo(Node, "Add Level connect") return true; } static RETADDLEVELCONNECT_F() { return "{result:byte,Count:uint}"; } RETADDLEVELCONNECT(Info, CurTime) { var Data = this.DataFromF(Info); AddNodeInfo(Info.Node, "GET RETADDLEVELCONNECT: " + Data.result) if(Data.result === 1) { this.AddLevelConnect(Info.Node) } else { this.AddCheckErrCount(Info.Node, 1) } Info.Node.LevelCount = Data.Count } DeleteBadConnectingByTimer() { if(glStopNode) return ; var CurTime = GetCurrentTime(); var arr = SERVER.NodesArr; for(var i = 0; i < arr.length; i++) { var Node = arr[i]; var Status = GetSocketStatus(Node.Socket); if(Node.Active && Status < 100) { var Delta = CurTime - Node.LastTime; if(Delta > MAX_WAIT_PERIOD_FOR_STATUS) { AddNodeInfo(Node, "Close bad connecting by time") this.DeleteNodeFromActive(Node) } } } } CheckDisconnectHot(Level) { var CurTime = GetCurrentTime() - 0; var MaxCountChilds; if(Level < 3) MaxCountChilds = 4 else MaxCountChilds = MAX_CONNECT_CHILD var arr = this.LevelNodes[Level]; if(arr) { for(var n = arr.length - 1; n >= 0; n--) { var Node = arr[n]; if(Node) { var DeltaTime = CurTime - Node.LastTimeTransfer; if(!Node.Hot || DeltaTime > MAX_WAIT_PERIOD_FOR_HOT) { this.StartDisconnectHot(Node, "TimeDisconnectHot") } } } arr.sort(SortNodeBlockProcessCount) var ChildCount = arr.length; for(var n = arr.length - 1; n >= MIN_CONNECT_CHILD; n--) { var Node = arr[n]; if(Node) { if(ChildCount > MaxCountChilds) { ChildCount-- Node.CanHot = false this.StartDisconnectHot(Node, "MAX_CONNECT_CHILD") ADD_TO_STAT("DisconnectChild") continue; } if(ChildCount > (MIN_CONNECT_CHILD) && Node.LevelCount > MIN_CONNECT_CHILD) { ChildCount-- Node.CanHot = false this.AddCheckErrCount(Node, 1) this.StartDisconnectHot(Node, "MIN_CONNECT_CHILD:" + Node.LevelCount + " LevelEnum:" + (n + 1)) ADD_TO_STAT("DisconnectChild") continue; } } } } } SetTime(NewTime) { ToLog("Set new time: " + NewTime) if(NewTime) { global.DELTA_CURRENT_TIME = NewTime - (GetCurrentTime(0) - 0) SAVE_CONST(true) } } static TIME_F() { return "{Time:uint, Sign:arr64}"; } SendTimeDev(Node) { if(!WALLET.WalletOpen) { ToLog("Error Wallet not open") return 0; } if(!this.SignCurrentTimeDev) { var SignArr = GetArrFromHex(SERVER.addrStr); this.SignCurrentTimeDev = secp256k1.sign(shabuf(SignArr), WALLET.KeyPair.getPrivateKey('')).signature } var Time = GetCurrentTime() - 0; ToLog("Send time: " + Time + " to " + NodeInfo(Node)) this.SendF(Node, {"Method":"TIME", "Data":{Time:Time, Sign:this.SignCurrentTimeDev}}) return 1; } SendTimeToAll() { var Count = 0; for(var i = 0; i < this.NodesArr.length; i++) { var Node = this.NodesArr[i]; if(Node.Active) { if(this.SendTimeDev(Node)) Count++ } } return Count; } TIME(Info, CurTime) { if(global.AUTO_CORRECT_TIME) { var Node = Info.Node; var Data = this.DataFromF(Info); var SignArr = GetArrFromHex(Node.addrStr); if(CheckDevelopSign(SignArr, Data.Sign)) { this.SetTime(Data.Time) } else { Node.NextConnectDelta = 60 * 1000 ToLog("Error Sign TIME from " + NodeInfo(Node)) this.AddCheckErrCount(Node, 10, "Error Sign TIME") } } } ConnectToAll() { var Count = 0; for(var i = 0; i < this.NodesArr.length; i++) { var Node = this.NodesArr[i]; if(!Node.Active && this.IsCanConnect(Node) && !Node.WasAddToConnect) { AddNodeInfo(Node, "To connect all") Node.NextConnectDelta = 1000 Node.WasAddToConnect = 1 ArrConnect.push(Node) Count++ } } return Count; } DisconnectAll() { var Count = 0; for(var i = 0; i < this.NodesArr.length; i++) { var Node = this.NodesArr[i]; if(Node.Active) { AddNodeInfo(Node, "Disconnect hot all") Node.NextConnectDelta = 10000 this.DeleteNodeFromActive(Node) Count++ } } return Count; } GetHotTimeNodes() { if(this.LoadHistoryMode || !global.CAN_START) return this.GetActualNodes(); else return this.GetHotNodes(); } CorrectTime() { var ArrNodes = this.GetHotTimeNodes(); var CountNodes = ArrNodes.length; var DeltaArr = []; var NodesSet = new Set(); for(var i = 0; i < ArrNodes.length; i++) { var Node = ArrNodes[i]; if(!Node.Times) continue; if(Node.Times.Count < 2) continue; if(this.PerioadAfterCanStart >= PERIOD_FOR_START_CHECK_TIME) if(Node.Times.Count < 5) continue; NodesSet.add(Node) } for(var Node of NodesSet) { DeltaArr.push(Node.Times.AvgDelta) } if(DeltaArr.length < 1) return ; if(DeltaArr.length < CountNodes / 2) return ; if(this.PerioadAfterCanStart >= PERIOD_FOR_START_CHECK_TIME) { if(DeltaArr.length < 3 * CountNodes / 4) return ; } DeltaArr.sort(function (a,b) { return a - b; }) var start, finish; if(Math.floor(DeltaArr.length / 2) === DeltaArr.length / 2) { start = DeltaArr.length / 2 - 1 finish = start + 1 } else { start = Math.floor(DeltaArr.length / 2) finish = start } var Sum = 0; var Count = 0; for(var i = start; i <= finish; i++) { Sum = Sum + DeltaArr[i] Count++ } var AvgDelta = Sum / Count; if(this.PerioadAfterCanStart < PERIOD_FOR_START_CHECK_TIME) { var KT = (PERIOD_FOR_START_CHECK_TIME - this.PerioadAfterCanStart) / PERIOD_FOR_START_CHECK_TIME; AvgDelta = AvgDelta * KT } else { MAX_TIME_CORRECT = 25 } if(AvgDelta < ( - MAX_TIME_CORRECT)) AvgDelta = - MAX_TIME_CORRECT else if(AvgDelta > MAX_TIME_CORRECT) AvgDelta = MAX_TIME_CORRECT AvgDelta = Math.trunc(AvgDelta) if(Math.abs(AvgDelta) < 15) { return ; } if(AvgDelta > 0) ADD_TO_STAT("CORRECT_TIME_UP", AvgDelta) else ADD_TO_STAT("CORRECT_TIME_DOWN", - AvgDelta) global.DELTA_CURRENT_TIME = Math.trunc(global.DELTA_CURRENT_TIME + AvgDelta) this.ClearTimeStat() SAVE_CONST() } ClearTimeStat() { var ArrNodes = this.GetHotTimeNodes(); for(var Node of ArrNodes) { Node.Times = undefined } } TimeDevCorrect() { if(CHECK_DELTA_TIME.bUse) { var BlockNum = GetCurrentBlockNumByTime(); if(CHECK_DELTA_TIME.StartBlockNum <= BlockNum && CHECK_DELTA_TIME.EndBlockNum > BlockNum) { if(!global.DELTA_CURRENT_TIME) global.DELTA_CURRENT_TIME = 0 var CorrectTime = 0; if(CHECK_DELTA_TIME.bAddTime) CorrectTime = CHECK_DELTA_TIME.DeltaTime else CorrectTime = - CHECK_DELTA_TIME.DeltaTime global.DELTA_CURRENT_TIME += CorrectTime this.ClearTimeStat() SAVE_CONST(true) } } } SetNodePrioritet(Node, Prioritet) { if(Node.Prioritet === Prioritet) return ; if(Node.addrArr) { var Item = this.ActualNodes.find(Node); if(Item) { this.ActualNodes.remove(Node) Node.Prioritet = Prioritet this.ActualNodes.insert(Node) } } Node.Prioritet = Prioritet } AddNodeToActive(Node) { if(Node.addrArr) { if(CompareArr(Node.addrArr, this.addrArr) === 0) { return ; } this.CheckNodeMap(Node) this.ActualNodes.insert(Node) } Node.ResetNode() Node.Active = true Node.NextConnectDelta = 1000 if(!Node.FirstTime) { Node.FirstTime = GetCurrentTime() - 0 Node.FirstTimeStr = "" + GetStrTimeUTC() } ADD_TO_STAT("AddToActive") } DeleteNodeFromActive(Node) { Node.Active = false if(Node.Hot) this.StartDisconnectHot(Node, "NotActive", 1) Node.Hot = false this.ActualNodes.remove(Node) CloseSocket(Node.Socket, "DeleteNodeFromActive") CloseSocket(Node.Socket2, "DeleteNodeFromActive") Node.ResetNode() Node.Socket = undefined Node.Socket2 = undefined } StartReconnect() { return ; var arr = this.GetActualNodes(); for(var i = 0; i < arr.length; i++) { var Node = arr[i]; if(Node.Socket && Node.Socket.ConnectToServer) { if(!Node.SocketStart) Node.SocketStart = Date.now() var DeltaTime = Date.now() - Node.SocketStart; if(DeltaTime >= PERIOD_FOR_RECONNECT) { if(random(100) >= 90) Node.CreateReconnection() } } } } IsLocalIP(addr) { if(addr.substr(0, 7) === "192.168" || addr.substr(0, 3) === "10.") return 1; else return 0; } GetActualsServerIP(bFlag) { var arr = this.GetActualNodes(); var Str = ""; arr.sort(function (a,b) { if(a.ip > b.ip) return - 1; else if(a.ip < b.ip) return 1; else return 0; }) if(bFlag) return arr; for(var i = 0; i < arr.length; i++) { Str += arr[i].ip + ", " } return Str.substr(0, Str.length - 2); } AddrLevelNode(Node) { if(Node.GrayConnect) return MAX_LEVEL_SPECIALIZATION - 1; return AddrLevelArr(this.addrArr, Node.addrArr); } GetNodesLevelCount() { var Count = 0; for(var i = 0; i < this.LevelNodes.length; i++) { var arr = this.LevelNodes[i]; for(var n = 0; arr && n < arr.length; n++) if(arr[n].Hot) { Count++ break; } } return Count; } GetHotNodes() { var ArrNodes = []; for(var L = 0; L < this.LevelNodes.length; L++) { var arr = this.LevelNodes[L]; for(let j = 0; arr && j < arr.length; j++) { ArrNodes.push(arr[j]) } } return ArrNodes; } DeleteNodeFromHot(Node) { if(Node.Hot) { Node.Hot = false } Node.CanHot = false for(var i = 0; i < this.LevelNodes.length; i++) { var arr = this.LevelNodes[i]; for(var n = 0; arr && n < arr.length; n++) if(arr[n] === Node) { arr.splice(n, 1) ADD_TO_STAT("DeleteLevelConnect") ADD_TO_STAT("NETCONFIGURATION") break; } } } DeleteAllNodesFromHot(Str) { for(var i = 0; i < this.LevelNodes.length; i++) { var arr = this.LevelNodes[i]; for(var n = 0; arr && n < arr.length; n++) { var Node = arr[n]; if(Node.Hot) { ADD_TO_STAT("DeleteAllNodesFromHot") this.StartDisconnectHot(Node, Str, 1) } } } } GetTransferTree() { var HotArr = []; for(var Level = 0; Level < this.LevelNodes.length; Level++) { var arr = this.LevelNodes[Level]; HotArr[Level] = [] for(var n = 0; arr && n < arr.length; n++) { var Node = arr[n]; if(Node) { Node.Hot = 1 Node.Level = Level HotArr[Level].push(Node) } } } var arr = this.NodesArr; for(var n = 0; arr && n < arr.length; n++) { var Node = arr[n]; if(!Node) continue; if(Node.Hot) continue; if(!this.IsCanConnect(Node)) continue; Node.Level = this.AddrLevelNode(Node) if(!HotArr[Node.Level]) HotArr[Node.Level] = [] HotArr[Node.Level].push(Node) } return HotArr; } DetectGrayMode() { if(global.NET_WORK_MODE) return ; var CurTime = Date.now(); var CountNodes = this.ActualNodes.size; if(CountNodes || this.StopDetectGrayMode) { this.SetDirectMode() this.StopDetectGrayMode = 1 return ; } if(!this.LastNotZeroNodesTime) this.LastNotZeroNodesTime = CurTime var DeltaTime = CurTime - this.LastNotZeroNodesTime; if(DeltaTime > 10 * 1000) { ToLog("DETECT GRAY MODE") if(!global.NET_WORK_MODE) { global.NET_WORK_MODE = {ip:"", port:""} } global.NET_WORK_MODE.UseDirectIP = 0 SAVE_CONST() } } SetDirectMode() { var CountNodes = this.ActualNodes.size; if(CountNodes && !global.NET_WORK_MODE) { global.NET_WORK_MODE = {ip:"", port:"30000"} global.NET_WORK_MODE.UseDirectIP = 1 SAVE_CONST() } } StartCheckTransferTree() { var ArrTree = this.GetTransferTree(); this.TransferTree = ArrTree var CurTime = Date.now(); if(GrayConnect()) { var MustCount = GetGrayServerConnections(); if(this.ActualNodes.size < MustCount) { this.NodesArr.sort(SortNodeBlockProcessCountGray) var WasDoConnect = 0; var arr = this.NodesArr; for(var n = 0; arr && n < arr.length; n++) { var Node = arr[n]; if(!Node) continue; if(!this.IsCanConnect(Node)) continue; var DeltaTime = CurTime - Node.StartTimeConnect; if(!Node.Active && WasDoConnect < 5 && !Node.WasAddToConnect && DeltaTime >= Node.NextConnectDelta) { AddNodeInfo(Node, "To connect") Node.WasAddToConnect = 1 ArrConnect.push(Node) WasDoConnect++ } } } while(this.ActualNodes.size > MustCount) { var Node = this.ActualNodes.max(); AddNodeInfo(Node, "DeleteFromActive") this.DeleteNodeFromActive(Node) } } else { this.DetectGrayMode() for(var Level = 0; Level < ArrTree.length; Level++) { var arr = ArrTree[Level]; if(!arr) continue; arr.sort(SortNodeBlockProcessCount) var WasDoConnect = 0; var WasDoHot = 0; var length = Math.min(arr.length, 10); for(var n = 0; n < length; n++) { var Node = arr[n]; var DeltaTime = CurTime - Node.StartTimeConnect; if(!Node.Active && WasDoConnect < 5 && !Node.WasAddToConnect && DeltaTime >= Node.NextConnectDelta) { AddNodeInfo(Node, "To connect") Node.WasAddToConnect = 1 ArrConnect.push(Node) WasDoConnect++ } DeltaTime = CurTime - Node.StartTimeHot if(Node.Active && !Node.Hot && WasDoHot < MIN_CONNECT_CHILD && DeltaTime > Node.NextHotDelta && !Node.GrayConnect) { AddNodeInfo(Node, "To hot level") this.StartAddLevelConnect(Node) Node.StartTimeHot = CurTime Node.NextHotDelta = Node.NextHotDelta * 2 WasDoHot++ } if(Node.Hot) WasDoHot++ } this.CheckDisconnectHot(Level) } } } ValueToXOR(StrType, Str) { var Arr1 = toUTF8Array(Str); var Arr2 = shaarr(this.CommonKey + ":" + this.addrStr + ":" + StrType); return WALLET.XORHash(Arr1, Arr2, 32); } ValueFromXOR(Node, StrType, Arr1) { var Arr2 = shaarr(this.CommonKey + ":" + Node.addrStr + ":" + StrType); var Arr = WALLET.XORHash(Arr1, Arr2, 32); var Str = Utf8ArrayToStr(Arr); return Str; } ValueToXORDevelop(StrName, Data, Type) { var Arr1; if(Type === "uint") { Arr1 = [] WriteUintToArr(Arr1, Data) } else if(Type === "hash") { Arr1 = Data } else if(Type === "str") { Arr1 = toUTF8Array(Data) } var Arr2 = shaarr(this.КодДляРазработчикаХекс + ":" + StrName); return WALLET.XORHash(Arr1, Arr2, Arr1.length); } ValueFromXORDevelop(Node, StrName, Arr1, Type) { if(!Node.КодДляРазработчикаХекс) { Node.КодДляРазработчикаХекс = GetHexFromArr(WALLET.KeyPair.computeSecret(Node.PubKey, null)) } var Arr2 = shaarr(Node.КодДляРазработчикаХекс + ":" + StrName); var Arr = WALLET.XORHash(Arr1, Arr2, Arr1.length); if(Type === "uint") { return ReadUintFromArr(Arr, 0); } else if(Type === "hash") { return Arr; } var Str = Utf8ArrayToStr(Arr); return Str; } }; function SortNodeBlockProcessCount(a,b) { if(b.BlockProcessCount !== a.BlockProcessCount) return b.BlockProcessCount - a.BlockProcessCount; if(a.DeltaTime !== b.DeltaTime) return a.DeltaTime - b.DeltaTime; return a.id - b.id; }; function SortNodeBlockProcessCountGray(a,b) { if(a.StartFindList !== b.StartFindList) return a.StartFindList - b.StartFindList; if(b.BlockProcessCount !== a.BlockProcessCount) return b.BlockProcessCount - a.BlockProcessCount; if(a.DeltaTime !== b.DeltaTime) return a.DeltaTime - b.DeltaTime; return a.id - b.id; }; function GetGrayServerConnections() { var Count = MAX_GRAY_CONNECTIONS_TO_SERVER; if(SERVER.LoadHistoryMode && SERVER.LoadHistoryMessage) Count = Count * 10; return Count; }; global.GetGrayServerConnections = GetGrayServerConnections; global.SortNodeBlockProcessCount = SortNodeBlockProcessCount;