/* * @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 CDB from './db/block-db' import { TYPE_TRANSACTION } from '../constant/account'; import { DB_FORMAT } from '../constant/db-format'; import * as crypto from 'crypto' import { LoadContext, ContextTask, TeraBlock, SocketSendInfo } from '../interfaces/server'; import CNode from './node'; var MIN_POW_CHAINS = 2; global.COUNT_NODE_PROOF = 6; if (global.TEST_NETWORK) { MIN_POW_CHAINS = 1; global.COUNT_NODE_PROOF = 1; } export default class CRest extends CDB { LoadRestContext: LoadContext LoadHistoryContext: LoadContext BlockNumDB: number BlockNumDBMin: number UseTruncateBlockDB: number ContextSendLoadToBegin: { Time: number; MapSend: {}; BlockNum?: number; } constructor(SetKeyPair: crypto.ECDH, RunIP: string, RunPort: number, UseRNDHeader: boolean, bVirtual: boolean) { super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual) } CheckSyncRest() { var BlockNumTime = global.GetCurrentBlockNumByTime(); var Delta = BlockNumTime - this.BlockNumDB; if (Delta > global.REST_START_COUNT + global.DELTA_BLOCK_ACCOUNT_HASH + 500) { var BlockNumRest = GetCurrentRestNum(global.REST_START_COUNT + global.DELTA_BLOCK_ACCOUNT_HASH + 500); if (this.BlockNumDB >= this.BlockNumDBMin && this.BlockNumDB <= this.BlockNumDBMin + global.BLOCK_PROCESSING_LENGTH2) { } else if (BlockNumRest > this.BlockNumDB) { } else { this.LoadRestContext = undefined return; } this.LoadRestContext = { Mode: 0, BlockNum: BlockNumRest, BlockNumRest: BlockNumRest, WasDelta: Delta, BlockNumProof: BlockNumRest + global.DELTA_BLOCK_ACCOUNT_HASH, CountProof: global.COUNT_BLOCKS_FOR_LOAD, StartTimeHistory: Date.now(), MaxTimeOut: 3600 * 1000, LoopSyncRest: 1, SendGetHeaderCount: 0, ReceiveHeaderCount: 0, ArrProof: [], MapSend: {} } for (var i = 0; i < this.NodesArr.length; i++) { this.NodesArr[i].SendRestGetHeader = 0 } global.ToLog("**********START REST MODE: " + this.LoadRestContext.BlockNumProof) } else { this.LoadRestContext = undefined } } GetNextNode(task, keyid: string | number, checktime?: number | boolean, BlockNum?: number): any { //defiend in block-loader.ts(CBlock) } DataFromF(Info: SocketSendInfo, bSendFormat?: boolean): any { //defiend in block-loader.ts(CBlock) } AddToBan(Node: CNode, Str: string) { //defiend in server.ts(CTransport) } LoopSyncRest() { let Context = this.LoadRestContext; switch (Context.Mode) { case 0: if (!global.TX_PROCESS) { return; } var ArrNodes = this.GetActualNodes(); for (var i = 0; i < ArrNodes.length; i++) { var Node = ArrNodes[i]; if (!Node || Node.SendRestGetHeader) { continue; } Node.SendRestGetHeader = 1 global.ToLog("Send rest get header " + Context.BlockNumProof + " to " + global.NodeName(Node), 2) this.SendF(Node, { "Method": "GETBLOCKHEADER", "Data": { Foward: 1, BlockNum: Context.BlockNumProof, Hash: [] }, "Context": { F: this.RETBLOCKHEADER_REST.bind(this) }, }) Context.SendGetHeaderCount++ break; } if (Context.ReceiveHeaderCount >= global.COUNT_NODE_PROOF) { Context.Mode = 2 global.ToLog("Next mode: " + Context.Mode + " Receive:" + Context.ReceiveHeaderCount + "/" + Context.SendGetHeaderCount, 2) } break; case 1000: break; case 2: var MapSumPower = {}; for (var i = 0; i < Context.ArrProof.length; i++) { var Item = Context.ArrProof[i]; if (!MapSumPower[Item.SumPower]) MapSumPower[Item.SumPower] = 0 MapSumPower[Item.SumPower]++ } var MaxCount = 0, MaxPow = 0; for (var key in MapSumPower) { if (MapSumPower[key] >= MaxCount) { MaxCount = MapSumPower[key] MaxPow = parseInt(key) } } if (MaxCount < 2 || MaxPow === 0) { global.ToLog("****************************************************************** Error MaxPow=" + MaxPow + " - reload.") this.CheckSyncRest() return; } for (var i = 0; i < Context.ArrProof.length; i++) { var Item = Context.ArrProof[i]; if (Item.SumPower !== MaxPow) { var Str = "BAD SumPower: " + Item.SumPower + "/" + MaxPow; global.ToLog(Str + " from: " + global.NodeName(Item.Node), 2) } else if (Item.SumPower && Item.arr.length >= Context.CountProof) { Item.OK = 1 Context.BlockProof = Item.arr[0] } } Context.Mode++ global.ToLog("Next mode: " + Context.Mode + " SumPower:" + MaxPow, 2) break; case 3: if (global.TX_PROCESS && global.TX_PROCESS.RunRPC) { Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) var Block = { BlockNum: Context.BlockNumRest } as TeraBlock; this.BlockNumDB = Block.BlockNum this.BlockNumDBMin = Block.BlockNum this.WriteBlockHeaderDB(Block) this.UseTruncateBlockDB = undefined global.ToLog("Start run TXPrepareLoadRest", 2) global.TX_PROCESS.RunRPC("TXPrepareLoadRest", Block.BlockNum, function(Err, Params) { Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) }) } break; case 4: break; case 5: let BlockProof = Context.BlockProof; var SendCount = 0; if (BlockProof) for (var i = 0; i < Context.ArrProof.length; i++) { let Item = Context.ArrProof[i]; if (Item.OK) { SendCount++ global.ToLog("Send rest get block proof:" + BlockProof.BlockNum + " to " + global.NodeName(Item.Node), 2) this.SendF(Item.Node, { "Method": "GETBLOCK", "Data": { BlockNum: BlockProof.BlockNum, TreeHash: BlockProof.TreeHash }, "Context": { F: function(Info) { if (Context.TxProof) return; var Data = global.BufLib.GetObjectFromBuffer(Info.Data, DB_FORMAT.FORMAT_BLOCK_TRANSFER, global.WRK_BLOCK_TRANSFER); Info.Data = undefined if (Data.BlockNum !== BlockProof.BlockNum || global.CompareArr(Data.TreeHash, BlockProof.TreeHash) !== 0) { global.ToLog("Error get proof block from " + global.NodeName(Item.Node), 2) return; } var TreeHash = global.CalcTreeHashFromArrBody(Data.BlockNum, Data.arrContent); if (global.CompareArr(BlockProof.TreeHash, TreeHash) !== 0) { global.ToLog("Error TreeHash in proof block from " + global.NodeName(Item.Node), 2) return; } global.ToLog("GET BLOCK proof from " + global.NodeName(Item.Node), 2) var FindTx = undefined; for (var n = 0; n < Data.arrContent.length; n++) { var Body = Data.arrContent[n]; if (Body[0] === TYPE_TRANSACTION.TYPE_TRANSACTION_ACC_HASH) { try { FindTx = global.BufLib.GetObjectFromBuffer(Body, DB_FORMAT.FORMAT_ACCOUNT_HASH3, {}) } catch (e) { global.ToLog("Error parsing Body[" + n + "] block proof: " + e, 2) continue; } break; } } if (!FindTx) return; Context.TxProof = FindTx Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) Context.AccTaskList = [] Context.AccTaskFinished = 0 var AccCount = FindTx.AccountMax + 1; for (var n = 0; n < AccCount; n += global.MAX_ACCOUNTS_TRANSFER) { var Task = { StartNum: n, Count: global.MAX_ACCOUNTS_TRANSFER, Time: 0, MapSend: {} }; if (Task.StartNum + Task.Count > AccCount) Task.Count = AccCount - Task.StartNum Context.AccTaskList.push(Task) } Context.SmartTaskList = [] Context.SmartTaskFinished = 0 for (var n = 0; n < FindTx.SmartCount; n += global.MAX_SMARTS_TRANSFER) { var Task = { StartNum: n, Count: global.MAX_SMARTS_TRANSFER, Time: 0, MapSend: {} }; if (Task.StartNum + Task.Count > FindTx.SmartCount) Task.Count = FindTx.SmartCount - Task.StartNum Context.SmartTaskList.push(Task) } } }, }) if (SendCount >= 5) break; } } Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) break; case 6: break; case 7: if (Context.AccTaskFinished === Context.AccTaskList.length) { Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) break; } var CurTime = Date.now(); for (var i = 0; i < Context.AccTaskList.length; i++) { let Task = Context.AccTaskList[i]; var Delta = CurTime - Task.Time; if (Delta > 5 * 1000 && !Task.OK) { var Ret = this.GetNextNode(Task, "", 1); if (Ret.Result) { global.ToLog("Send GETREST Num:" + Task.StartNum + "-" + Task.Count + " to " + global.NodeName(Ret.Node), 2) var SELF = this; this.SendF(Ret.Node, { "Method": "GETREST", "Data": { BlockNum: Context.BlockNumRest, AccNum: Task.StartNum, Count: Task.Count, AccHash: Context.TxProof.AccHash }, "Context": { F: function(Info) { if (Task.OK) return; var Data = SELF.DataFromF(Info); if (!Data.Result) return; if (Data.Version !== 1) { global.ToLog("ERROR Version Result GETREST Num:" + Task.StartNum + " from " + global.NodeName(Info.Node), 2) return; } if (global.CompareArrL(Data.ProofHash, Context.TxProof.AccHash) !== 0) { global.ToLog("ERROR PROOF HASH Result GETREST Num:" + Task.StartNum + " Hash: " + global.GetHexFromArr(Data.ProofHash) + "/" + global.GetHexFromArr(Context.TxProof.AccHash) + " from " + global.NodeName(Info.Node), 2) return; } var ArrM = []; for (var i = 0; i < Data.Arr.length; i++) { ArrM[i] = global.shaarr(Data.Arr[i]) } var GetHash = global.CheckMerkleProof(Data.ProofArrL, ArrM, Data.ProofArrR); if (global.CompareArrL(GetHash, Context.TxProof.AccHash) !== 0) { global.ToLog("ERROR CALC PROOF HASH Result GETREST Num:" + Task.StartNum + " Hash: " + global.GetHexFromArr(GetHash) + "/" + global.GetHexFromArr(Context.TxProof.AccHash) + " from " + global.NodeName(Info.Node), 2) return; } global.ToLog("OK Result GETREST Num:" + Task.StartNum + " arr=" + Data.Arr.length + " from " + global.NodeName(Info.Node), 2) if (!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) { global.ToLog("ERROR global.TX_PROCESS") return; } Task.OK = true global.TX_PROCESS.RunRPC("TXWriteAccArr", { StartNum: Task.StartNum, Arr: Data.Arr }, function(Err, Params) { Context.AccTaskFinished++ }) } }, }) Task.Time = CurTime break; } } } break; case 8: if (Context.SmartTaskFinished === Context.SmartTaskList.length) { Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) break; } var CurTime = Date.now(); for (var i = 0; i < Context.SmartTaskList.length; i++) { let Task = Context.SmartTaskList[i]; var Delta = CurTime - Task.Time; if (Delta > 3 * 1000 && !Task.OK) { var Ret = this.GetNextNode(Task, "", 1); if (Ret.Result) { global.ToLog("Send GETSMART Num:" + Task.StartNum + "-" + Task.Count + " to " + global.NodeName(Ret.Node), 2) var SELF = this; this.SendF(Ret.Node, { "Method": "GETSMART", "Data": { BlockNum: Context.BlockNumRest, SmartNum: Task.StartNum, Count: Task.Count }, "Context": { F: function(Info) { if (Task.OK) return; var Data = SELF.DataFromF(Info); if (!Data.Result) return; global.ToLog("Result GETSMART Num:" + Task.StartNum + " arr=" + Data.Arr.length + " from " + global.NodeName(Info.Node), 2) Task.Node = Info.Node if (!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) return; Task.OK = true global.TX_PROCESS.RunRPC("TXWriteSmartArr", { StartNum: Task.StartNum, Arr: Data.Arr }, function(Err, Params) { Context.SmartTaskFinished++ }) } }, }) Task.Time = CurTime break; } } } break; case 9: if (!global.TX_PROCESS || !global.TX_PROCESS.RunRPC) return; var ErrSmartNum = CheckHashSmarts(Context.TxProof.SmartHash); if (ErrSmartNum > 0) { var Str = "Error hash in smart num: " + ErrSmartNum; global.ToLog(Str, 2) var t = Math.trunc(ErrSmartNum / global.MAX_SMARTS_TRANSFER); var Task = Context.SmartTaskList[t]; if (!Task) { global.ToLog("error task number: " + t) Context.Mode = 100 } else { Task.OK = false Context.Mode-- Context.SmartTaskFinished-- this.AddToBan(Task.Node, Str) } break; } var SELF = this; global.TX_PROCESS.RunRPC("TXWriteAccHash", {}, function(Err, Params) { if (!Params) return; if (global.CompareArr(Context.TxProof.AccHash, Params.AccHash) === 0 && global.CompareArr(Context.TxProof.SmartHash, Params.SmartHash) === 0) { Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) } else { global.ToLog("ERROR RESTS LOAD:") global.ToLog("Must AccHash:" + global.GetHexFromArr(Context.TxProof.AccHash)) global.ToLog("Must SmartHash:" + global.GetHexFromArr(Context.TxProof.SmartHash)) global.ToLog("Write AccHash:" + global.GetHexFromArr(Params.AccHash)) global.ToLog("Write SmartHash:" + global.GetHexFromArr(Params.SmartHash)) SELF.BlockNumDB = 0 SELF.BlockNumDBMin = 0 SELF.UseTruncateBlockDB = undefined global.TX_PROCESS.RunRPC("TXPrepareLoadRest", 0, function(Err, Params) { }) Context.Mode = 100 } }) Context.Mode++ global.ToLog("Next mode: " + Context.Mode, 2) break; case 10: break; case 11: var Context2 = this.LoadHistoryContext; Context2.BlockNum = this.LoadRestContext.BlockNumRest Context2.StartTimeHistory = Date.now() Context.Mode = 200 break; case 200: global.ToLog("Error state!") break; } } GetBlockArrFromBuffer_Load(BufRead: Buffer, Info?: SocketSendInfo): any { //defiend in block-loader.ts(CBlock) } RETBLOCKHEADER_REST(Info: SocketSendInfo, CurTime: number) { if (Info.Node.SendRestGetHeader === 2) return; Info.Node.SendRestGetHeader = 2 var Context = this.LoadRestContext; var BufRead = global.BufLib.GetReadBuffer(Info.Data); var arr = this.GetBlockArrFromBuffer_Load(BufRead, Info); global.ToLog("RETBLOCKHEADER_FOWARD SyncRest from " + global.NodeName(Info.Node) + " arr=" + arr.length, 2) Context.ReceiveHeaderCount++ var MinSumPow = 10 * Context.CountProof; var SumPower = 0; if (arr.length >= Context.CountProof) for (var i = 0; i < Context.CountProof; i++) { SumPower += arr[i].Power } if (SumPower <= MinSumPow) SumPower = 0 Context.ArrProof.push({ Node: Info.Node, SumPower: SumPower, arr: arr, BufRead: BufRead }) } static GETSMART_F() { return "{\ SmartNum:uint,\ Count:uint,\ }"; } static RETSMART_F() { return DB_FORMAT.FORMAT_SMART_TRANSFER; } static GETREST_F() { return "{\ BlockNum:uint,\ AccNum:uint,\ Count:uint,\ AccHash:hash,\ }"; } static RETREST_F() { return DB_FORMAT.FORMAT_REST_TRANSFER; } SendLoadToBegin() { return; if (!this.BlockNumDBMin) return; if (!this.ContextSendLoadToBegin) this.ContextSendLoadToBegin = { Time: 0, MapSend: {} } var Context = this.ContextSendLoadToBegin; var CurTime = Date.now(); var Delta = CurTime - Context.Time; if (Delta < 2 * 1000) return; var BlockDB = this.ReadBlockHeaderDB(this.BlockNumDBMin + 1); if (!BlockDB) return; Context.BlockNum = BlockDB.BlockNum var Ret = this.GetNextNode(Context, Context.BlockNum, 1); if (Ret.Result) { var Node = Ret.Node; global.ToLog("LOAD_TO_BEGIN - from: " + BlockDB.BlockNum + " to " + global.NodeName(Node), 2) Context.Time = CurTime this.SendF(Node, { "Method": "GETBLOCKHEADER", "Data": { Foward: 0, BlockNum: Context.BlockNum, Hash: BlockDB.Hash, IsSum: 0, Count: global.COUNT_HISTORY_BLOCKS_FOR_LOAD }, "Context": { F: function(Info) { global.ToLog("GET LOAD_TO_BEGIN from " + global.NodeName(Info.Node) + " Length=" + Info.Data.length, 2) } } }) } } }; function CheckHashSmarts(LastSumHash: Buffer) { global.DApps.Smart.Close(); var MaxNum = global.DApps.Smart.GetMaxNum(); var Item = global.DApps.Smart.DBSmart.Read(MaxNum); if (global.CompareArr(Item.SumHash, LastSumHash) !== 0) return MaxNum; var WorkStruct = {}; for (var Num = MaxNum; Num >= 1; Num--) { var PrevItem = global.DApps.Smart.DBSmart.Read(Num - 1); if (!PrevItem) return Num; var WasSumHash = Item.SumHash; Item.SumHash = []; var Buf = global.BufLib.GetBufferFromObject(Item, DB_FORMAT.FORMAT_SMART_ROW, 20000, WorkStruct); var Hash = global.sha3(Buf); var SumHash = global.sha3arr2(PrevItem.SumHash, Hash); if (global.CompareArr(SumHash, WasSumHash) !== 0) return Num; Item = PrevItem; } return 0; };