tera/Source/core/rest-loader.js

515 lines
23 KiB
JavaScript

/*
* @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";
var MIN_POW_CHAINS = 2;
global.COUNT_NODE_PROOF = 6;
if(global.TEST_NETWORK)
{
MIN_POW_CHAINS = 1;
global.COUNT_NODE_PROOF = 1;
}
module.exports = class CRest extends require("./db/block-db")
{
constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
{
super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
}
CheckSyncRest()
{
var BlockNumTime = GetCurrentBlockNumByTime();
var Delta = BlockNumTime - this.BlockNumDB;
if(Delta > REST_START_COUNT + DELTA_BLOCK_ACCOUNT_HASH + 500)
{
var BlockNumRest = GetCurrentRestNum(REST_START_COUNT + DELTA_BLOCK_ACCOUNT_HASH + 500);
if(this.BlockNumDB >= this.BlockNumDBMin && this.BlockNumDB <= this.BlockNumDBMin + 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 + DELTA_BLOCK_ACCOUNT_HASH,
CountProof:COUNT_BLOCKS_FOR_LOAD, StartTimeHistory:Date.now(), MaxTimeOut:600 * 1000, LoopSyncRest:1, SendGetHeaderCount:0,
ReceiveHeaderCount:0, ArrProof:[], MapSend:{}}
for(var i = 0; i < this.NodesArr.length; i++)
{
this.NodesArr[i].SendRestGetHeader = 0
}
ToLog("**********START REST MODE: " + this.LoadRestContext.BlockNumProof)
}
else
{
this.LoadRestContext = undefined
}
}
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
ToLog("Send rest get header " + Context.BlockNumProof + " to " + 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 >= COUNT_NODE_PROOF)
{
Context.Mode = 2
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 < MIN_POW_CHAINS || MaxPow === 0)
{
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;
ToLog(Str + " from: " + NodeName(Item.Node), 2)
}
else
if(Item.SumPower && Item.arr.length >= Context.CountProof)
{
Item.OK = 1
Context.BlockProof = Item.arr[0]
}
}
Context.Mode++
ToLog("Next mode: " + Context.Mode + " SumPower:" + MaxPow, 2)
break;
case 3:
if(global.TX_PROCESS && global.TX_PROCESS.RunRPC)
{
Context.Mode++
ToLog("Next mode: " + Context.Mode, 2)
var Block = {BlockNum:Context.BlockNumRest};
this.BlockNumDB = Block.BlockNum
this.BlockNumDBMin = Block.BlockNum
this.WriteBlockHeaderDB(Block)
this.UseTruncateBlockDB = undefined
ToLog("Start run TXPrepareLoadRest", 2)
global.TX_PROCESS.RunRPC("TXPrepareLoadRest", Block.BlockNum, function (Err,Params)
{
Context.Mode++
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++
ToLog("Send rest get block proof:" + BlockProof.BlockNum + " to " + 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 = BufLib.GetObjectFromBuffer(Info.Data, FORMAT_BLOCK_TRANSFER, WRK_BLOCK_TRANSFER);
Info.Data = undefined
if(Data.BlockNum !== BlockProof.BlockNum || CompareArr(Data.TreeHash, BlockProof.TreeHash) !== 0)
{
ToLog("Error get proof block from " + NodeName(Item.Node), 2)
return ;
}
var TreeHash = CalcTreeHashFromArrBody(Data.BlockNum, Data.arrContent);
if(CompareArr(BlockProof.TreeHash, TreeHash) !== 0)
{
ToLog("Error TreeHash in proof block from " + NodeName(Item.Node), 2)
return ;
}
ToLog("GET BLOCK proof from " + 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_ACC_HASH)
{
try
{
FindTx = BufLib.GetObjectFromBuffer(Body, FORMAT_ACCOUNT_HASH3, {})
}
catch(e)
{
ToLog("Error parsing Body[" + n + "] block proof: " + e, 2)
continue;
}
break;
}
}
if(!FindTx)
return ;
Context.TxProof = FindTx
Context.Mode++
ToLog("Next mode: " + Context.Mode, 2)
Context.AccTaskList = []
Context.AccTaskFinished = 0
var AccCount = FindTx.AccountMax + 1;
for(var n = 0; n < AccCount; n += MAX_ACCOUNTS_TRANSFER)
{
var Task = {StartNum:n, Count: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 += MAX_SMARTS_TRANSFER)
{
var Task = {StartNum:n, Count: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++
ToLog("Next mode: " + Context.Mode, 2)
break;
case 6:
break;
case 7:
if(Context.AccTaskFinished === Context.AccTaskList.length)
{
Context.Mode++
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)
{
ToLog("Send GETREST Num:" + Task.StartNum + "-" + Task.Count + " to " + 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)
{
ToLog("ERROR Version Result GETREST Num:" + Task.StartNum + " from " + NodeName(Info.Node), 2)
return ;
}
if(CompareArrL(Data.ProofHash, Context.TxProof.AccHash) !== 0)
{
ToLog("ERROR PROOF HASH Result GETREST Num:" + Task.StartNum + " Hash: " + GetHexFromArr(Data.ProofHash) + "/" + GetHexFromArr(Context.TxProof.AccHash) + " from " + NodeName(Info.Node),
2)
return ;
}
var ArrM = [];
for(var i = 0; i < Data.Arr.length; i++)
{
ArrM[i] = shaarr(Data.Arr[i])
}
var GetHash = CheckMerkleProof(Data.ProofArrL, ArrM, Data.ProofArrR);
if(CompareArrL(GetHash, Context.TxProof.AccHash) !== 0)
{
ToLog("ERROR CALC PROOF HASH Result GETREST Num:" + Task.StartNum + " Hash: " + GetHexFromArr(GetHash) + "/" + GetHexFromArr(Context.TxProof.AccHash) + " from " + NodeName(Info.Node),
2)
return ;
}
ToLog("OK Result GETREST Num:" + Task.StartNum + " arr=" + Data.Arr.length + " from " + NodeName(Info.Node), 2)
if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC)
{
ToLog("ERROR global.TX_PROCESS")
return ;
}
Task.OK = 1
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++
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)
{
ToLog("Send GETSMART Num:" + Task.StartNum + "-" + Task.Count + " to " + 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 ;
ToLog("Result GETSMART Num:" + Task.StartNum + " arr=" + Data.Arr.length + " from " + NodeName(Info.Node), 2)
Task.Node = Info.Node
if(!global.TX_PROCESS || !global.TX_PROCESS.RunRPC)
return ;
Task.OK = 1
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;
ToLog(Str, 2)
var t = Math.trunc(ErrSmartNum / MAX_SMARTS_TRANSFER);
var Task = Context.SmartTaskList[t];
if(!Task)
{
ToLog("error task number: " + t)
Context.Mode = 100
}
else
{
Task.OK = 0
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(CompareArr(Context.TxProof.AccHash, Params.AccHash) === 0 && CompareArr(Context.TxProof.SmartHash, Params.SmartHash) === 0)
{
Context.Mode++
ToLog("Next mode: " + Context.Mode, 2)
}
else
{
ToLog("ERROR RESTS LOAD:")
ToLog("Must AccHash:" + GetHexFromArr(Context.TxProof.AccHash))
ToLog("Must SmartHash:" + GetHexFromArr(Context.TxProof.SmartHash))
ToLog("Write AccHash:" + GetHexFromArr(Params.AccHash))
ToLog("Write SmartHash:" + 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++
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:
ToLog("Error state!")
break;
}
}
RETBLOCKHEADER_REST(Info, CurTime)
{
if(Info.Node.SendRestGetHeader === 2)
return ;
Info.Node.SendRestGetHeader = 2
var Context = this.LoadRestContext;
var BufRead = BufLib.GetReadBuffer(Info.Data);
var arr = this.GetBlockArrFromBuffer_Load(BufRead, Info);
ToLog("RETBLOCKHEADER_FOWARD SyncRest from " + 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 global.FORMAT_SMART_TRANSFER;
}
static
GETREST_F()
{
return "{\
BlockNum:uint,\
AccNum:uint,\
Count:uint,\
AccHash:hash,\
}";
}
static
RETREST_F()
{
return global.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;
ToLog("LOAD_TO_BEGIN - from: " + BlockDB.BlockNum + " to " + 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)
{
ToLog("GET LOAD_TO_BEGIN from " + NodeName(Info.Node) + " Length=" + Info.Data.length, 2)
}}})
}
}
};
function CheckHashSmarts(LastSumHash)
{
DApps.Smart.Close();
var MaxNum = DApps.Smart.GetMaxNum();
var Item = DApps.Smart.DBSmart.Read(MaxNum);
if(CompareArr(Item.SumHash, LastSumHash) !== 0)
return MaxNum;
var WorkStruct = {};
for(var Num = MaxNum; Num >= 1; Num--)
{
var PrevItem = DApps.Smart.DBSmart.Read(Num - 1);
if(!PrevItem)
return Num;
var WasSumHash = Item.SumHash;
Item.SumHash = [];
var Buf = BufLib.GetBufferFromObject(Item, DApps.Smart.FORMAT_ROW, 20000, WorkStruct);
var Hash = sha3(Buf);
var SumHash = sha3arr2(PrevItem.SumHash, Hash);
if(CompareArr(SumHash, WasSumHash) !== 0)
return Num;
Item = PrevItem;
}
return 0;
};