tera/src/core/rest-loader.ts

503 lines
25 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 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;
};