1
0
Fork 0
tera/Source/core/block-exchange.js

1270 lines
45 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";
require('./library');
require('./crypto-library');
const TX_PROCESS_TIME = 100;
const TX_DELTA_PROCESS_TIME = 300;
global.CAN_START = false;
global.StrWarn = "";
global.SUM_LIST_LENGTH = 2 * BLOCK_PROCESSING_LENGTH;
global.CONSENSUS_TIK_TIME = CONSENSUS_PERIOD_TIME / 10;
global.CONSENSUS_CHECK_TIME = CONSENSUS_PERIOD_TIME / 20;
const PERIOD_FOR_NEXT_SEND = CONSENSUS_TIK_TIME * 3;
global.BLOCK_DELTA_ACTIVATE = 0;
global.TIME_END_EXCHANGE = - 3;
global.TIME_START_POW = - 4;
global.TIME_START_SAVE = - 4;
global.TIME_START_LOAD = global.TIME_START_SAVE - 4;
var FORMAT_DATA_TRANSFER = "{\
Version:uint16,\
BlockNum:uint,\
Reserv1:uint32,\
MaxPOW:[{BlockNum:uint,AddrHash:hash,SeqHash:hash}],\
Reserv2:uint32,\
BaseBlockNum:uint,\
MaxSumID:[{BlockNum:uint,SumHash:hash,SumListID:[uint16]}],\
BlockList:[{ID:uint16, AddrHash:hash,SeqHash:hash}],\
TicketArray:[{HashTicket:arr10}],\
TxArray:[{body:tr}],\
NoSendTx:uint,\
}";
const WorkStructSend = {};
module.exports = class CConsensus extends require("./block-exchange2")
{
constructor(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
{
super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
this.CurrentBlockNum = 0
this.SendBlockID = 0
this.RelayMode = false
this.TreeSendPacket = new RBTree(CompareItemHash)
if(!global.ADDRLIST_MODE && !this.VirtualMode)
{
this.idBlockChainTimer = setInterval(this.StartBlockChain.bind(this), CONSENSUS_PERIOD_TIME - 5)
setInterval(this.DoTransfer.bind(this), CONSENSUS_CHECK_TIME)
}
}
StartBlockChain()
{
this.OnStartSecond()
var CurTimeNum = GetCurrentTime() - CONSENSUS_PERIOD_TIME / 2;
var StartTimeNum = Math.floor((CurTimeNum + CONSENSUS_PERIOD_TIME) / CONSENSUS_PERIOD_TIME) * CONSENSUS_PERIOD_TIME;
var DeltaForStart = StartTimeNum - CurTimeNum;
if(DeltaForStart < (CONSENSUS_PERIOD_TIME - 5))
{
var self = this;
if(self.idBlockChainTimer)
clearInterval(self.idBlockChainTimer)
self.idBlockChainTimer = 0
setTimeout(function ()
{
self.idBlockChainTimer = setInterval(self.StartBlockChain.bind(self), CONSENSUS_PERIOD_TIME)
self.OnStartSecond()
}, DeltaForStart)
}
}
OnStartSecond0()
{
PrepareStatEverySecond()
this.AddStatOnTimer()
this.DoBlockChain()
}
CreateBlockContext()
{
var Context = {};
Context.AddInfo = AddInfoBlock.bind(Context)
Context.Active = false
Context.TransferFromAddr = {}
Context.LevelsTransfer = []
Context.ErrRun = ""
Context.PowTxTree = new RBTree(CompareItemTimePow)
Context.bSave = false
Context.PrevHash = undefined
Context.TreeHash = undefined
Context.MaxPOW = {}
Context.MaxSum = {}
Context.SumPow = 0
Context.Power = 0
Context.TrCount = 0
Context.TrDataPos = 0
Context.TrDataLen = 0
Context.Info = "Create at:" + GetStrOnlyTimeUTC()
var Transfer;
var TransferM2;
var LocalLevel = 0;
var Levels = this.LevelNodes;
for(let L = 0; L < Levels.length; L++)
{
var arr = Levels[L];
if(arr && arr.length > 0)
{
Transfer = {LocalLevel:LocalLevel, TreeLevel:L, SendCount:0, GetCount:0, TransferNodes:{}, WasGet:false, WasSend:false, MustDeltaTime:CONSENSUS_TIK_TIME * (2 + MAX_LEVEL_SPECIALIZATION - L),
}
LocalLevel++
Context.LevelsTransfer.push(Transfer)
Context.StartLevel = Context.LevelsTransfer.length - 1
for(let j = 0; j < arr.length; j++)
{
var Node = arr[j];
var Addr = Node.addrStr;
if(!Transfer.TransferNodes[Addr])
{
let Item = {Node:Node, SendCount:0, GetCount:0, addrStr:Addr, TreeLevel:L, GetTiming:3 * CONSENSUS_PERIOD_TIME, };
Transfer.TransferNodes[Addr] = Item
}
Context.TransferFromAddr[Addr] = Transfer
}
}
}
Context.MLevelSend = Context.StartLevel
return Context;
}
StartConsensus()
{
if(!CAN_START)
return ;
var StartBlockNum = GetCurrentBlockNumByTime();
if(StartBlockNum < BLOCK_PROCESSING_LENGTH2)
return ;
this.CurrentBlockNum = StartBlockNum
if(!this.WasReloadSenderMapFromDB)
this.ReloadSenderMapFromDB()
var Block0 = this.GetBlockContext(StartBlockNum - BLOCK_DELTA_ACTIVATE);
if(!Block0.Active)
{
AddInfoBlock(Block0, "Activate")
this.StartBlock(Block0)
}
else
{
AddInfoBlock(Block0, "Was Active")
}
}
TrToInfo(Block, Array, StrInfo)
{
var Str = "";
for(var i = 0; i < Array.length; i++)
{
var Item = Array[i];
this.CheckCreateTransactionObject(Item)
Str += this.GetStrFromHashShort(shaarr(Item.body)) + "(" + Item.body.length + "),"
}
AddInfoBlock(Block, "" + StrInfo + ": Arr=[" + Str + "]")
}
TRANSFER(Info, CurTime)
{
var startTime = process.hrtime();
var Data = this.DataFromF(Info);
var Node = Info.Node;
Node.TransferBlockNum = Data.BlockNum
Node.CurBlockNum = Data.BaseBlockNum + Data.BlockNum
if(Data.Version !== 5)
return ;
var Block = this.GetBlockContext(Data.BlockNum);
if(!Block || Block.StartLevel === undefined)
{
ADD_TO_STAT("TRANSFER_ERR_STARTLEVEL")
this.AddCheckErrCount(Node, 1, "Err GetBlockContext")
return ;
}
if(!Block.Active)
this.StartBlock(Block)
var Key = Node.addrStr;
var Transfer = Block.TransferFromAddr[Key];
if(!Transfer)
{
ADD_TO_STAT("NO_TRANSFER")
this.AddCheckErrCount(Node, 1, "Err Transfer")
return ;
}
Transfer.WasGet = true
if(global.DoTxLog && Data.TicketArray.length)
ToLog("TRANSFER BlockNum:" + Block.BlockNum + " TicketArray=" + Data.TicketArray.length + " from " + NodeName(Node))
if(global.DoTxLog && Data.TxArray.length)
ToLog("TRANSFER BlockNum:" + Block.BlockNum + " TxArray=" + Data.TxArray.length + " from " + NodeName(Node))
this.ToMaxPOWList(Data.MaxPOW)
this.ToMaxSumList(this.GetMaxSumListFromID(Node, Data.MaxSumID, Data.BlockList))
var WasNewAdd = 0;
if(Data.TxArray.length)
{
for(var i = 0; i < Data.TxArray.length; i++)
{
var Tr = Data.TxArray[i];
var Res = this.AddTrToBlockQuote(Block, Tr);
if(Res === 1)
WasNewAdd = 1
}
}
ADD_TO_STAT_TIME("TRANSFER_MS", startTime)
var Delta = Date.now() - this.StartLoadBlockTime;
if(Delta > 10 * 1000 && Node.TransferCount > 10)
{
Node.BlockProcessCount++
Node.NextHotDelta = 10 * 1000
}
Node.TransferCount++
Node.LastTimeTransfer = GetCurrentTime() - 0
var Item = Transfer.TransferNodes[Key];
Item.GetTiming = GetCurrentTime(Block.DELTA_CURRENT_TIME) - Block.StartTimeNum
if(!Block.TransferNodesCount)
Block.TransferNodesCount = 0
Block.TransferNodesCount++
}
DoTransfer0()
{
var MaxPOWList;
var MaxSumList;
var start = this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH;
var finish = this.GetLastCorrectBlockNum();
for(var b = start; b <= finish; b++)
{
var Block = this.GetBlock(b);
if(!Block)
continue;
if(Block.StartLevel === undefined || Block.MLevelSend === undefined)
continue;
if(!Block.Active)
continue;
if(Block.MLevelSend < 0)
{
this.CheckEndExchange(Block)
continue;
}
if(Block.EndExchange)
continue;
var Transfer = Block.LevelsTransfer[Block.MLevelSend];
if(!Transfer.WasSend)
{
if(!MaxPOWList)
{
MaxPOWList = this.GetMaxPOWList()
MaxSumList = this.GetMaxSumList()
}
var ArrT = this.GetArrayFromTxTree(Block);
this.SendDataTransfer(Transfer, ArrT, MaxPOWList, MaxSumList, Block)
}
Transfer.WasSend = true
var bNext = Transfer.WasGet;
if(!bNext)
{
var CurTimeNum = GetCurrentTime(Block.DELTA_CURRENT_TIME) - 0;
var DeltaTime = CurTimeNum - Block.StartTimeNum;
if(DeltaTime > Transfer.MustDeltaTime)
{
bNext = true
Block.ErrRun = "" + Transfer.LocalLevel + " " + Block.ErrRun
for(var Addr in Transfer.TransferNodes)
{
var Item = Transfer.TransferNodes[Addr];
ADD_TO_STAT("TRANSFER_TIME_OUT")
this.AddCheckErrCount(Item.Node, 1, "TRANSFER_TIME_OUT")
}
ADD_TO_STAT("TimeOutLevel")
}
}
if(bNext)
{
if(Block.MLevelSend === 0)
{
Block.EndExchangeTime = Date.now()
this.CheckEndExchange(Block)
}
Block.MLevelSend--
}
}
}
CheckEndExchange(Block)
{
if(Block.EndExchange)
return ;
this.CreateTreeHash(Block)
}
SendDataTransfer(Transfer, ArrT, MaxPOWList, MaxSumList, Block)
{
for(var Addr in Transfer.TransferNodes)
{
var Item = Transfer.TransferNodes[Addr];
Transfer.SendCount++
var arrPow = [];
for(var i = 0; i < MaxPOWList.length; i++)
{
var elem = MaxPOWList[i];
var Str = "POW:" + Item.Node.id + elem.BlockNum + "-" + GetHexFromArr(elem.AddrHash);
var bWasSend = global.TreeBlockBuf.LoadValue(Str, 1);
if(!bWasSend)
{
global.TreeBlockBuf.SaveValue(Str, true)
arrPow.push(elem)
}
}
var arrSum = [];
for(var i = 0; i < MaxSumList.length; i++)
{
var elem = MaxSumList[i];
var Str = "MAX:" + Item.Node.id + elem.BlockNum + "-" + GetHexFromArr(elem.SumHash);
var bWasSend = global.TreeBlockBuf.LoadValue(Str, 1);
if(!bWasSend)
{
global.TreeBlockBuf.SaveValue(Str, true)
arrSum.push(elem)
}
}
var Arr = ArrT;
if(global.DoTxLog)
ToLog("SEND TRANSFER BlockNum:" + Block.BlockNum + " Arr=" + Arr.length + " to " + NodeName(Item.Node))
var BufData = this.CreateTransferBuffer(Arr, arrPow, arrSum, Block, Item.Node);
this.Send(Item.Node, {"Method":"TRANSFER", "Context":{}, "Data":BufData}, 1)
if(!Block.JobListTX)
Block.JobListTX = []
Block.JobListTX.push({Node:Item.Node, TreeLevel:Item.TreeLevel, Time:Date.now()})
}
}
CanSendTest()
{
return 1;
}
GetMaxSumListFromID(Node, MaxSumID, BlockList)
{
var Str0 = "GETBL:" + Node.id;
for(var i = 0; i < BlockList.length; i++)
{
var elemBlockList = BlockList[i];
global.TreeBlockBuf.SaveValue(Str0 + elemBlockList.ID, elemBlockList)
}
var MaxSum = [];
MaxSum:
for(var i = 0; i < MaxSumID.length; i++)
{
var elem = MaxSumID[i];
var Arr = [];
for(var n = 0; n < elem.SumListID.length; n++)
{
var elemBlockList = global.TreeBlockBuf.LoadValue(Str0 + elem.SumListID[n], 1);
if(elemBlockList === undefined)
{
continue MaxSum;
}
Arr.push(elemBlockList)
}
elem.SumList = Arr
MaxSum.push(elem)
}
return MaxSum;
}
CreateTransferBuffer(ArrT, MaxPOWList, MaxSumList, Block, Node)
{
var Data;
var MaxSumID = [];
var BlockList = [];
for(var i = 0; i < MaxSumList.length; i++)
{
var elem0 = MaxSumList[i];
var ArrID = [];
for(var n = 0; n < elem0.SumList.length; n++)
{
var elemBlockList = elem0.SumList[n];
var Str = "BL:" + Node.id + GetHexFromArr(elemBlockList.Hash3);
var ID = global.TreeBlockBuf.LoadValue(Str, 1);
if(ID === undefined)
{
this.SendBlockID++
ID = this.SendBlockID % 65535
global.TreeBlockBuf.SaveValue(Str, ID)
elemBlockList.ID = ID
BlockList.push(elemBlockList)
}
ArrID.push(ID)
}
MaxSumID.push({BlockNum:elem0.BlockNum, SumHash:elem0.SumHash, SumListID:ArrID})
}
var ArrTx = ArrT;
Data = {"Version":5, "BlockNum":Block.BlockNum, "Reserv1":0, "MaxPOW":MaxPOWList, "Reserv2":0, "BaseBlockNum":this.CurrentBlockNum - Block.BlockNum,
"MaxSumID":MaxSumID, "BlockList":BlockList, "TicketArray":[], "TxArray":ArrTx, "NoSendTx":Node.NoSendTx, }
var BufWrite = BufLib.GetBufferFromObject(Data, FORMAT_DATA_TRANSFER, MAX_BLOCK_SIZE + 30000, WorkStructSend);
return BufWrite;
}
static
TRANSFER_F()
{
return FORMAT_DATA_TRANSFER;
}
CheckingMaxPowOther(Block)
{
var POW = Block.MaxPOW;
if(POW && POW.Hash && CompareArr(POW.PowHash, Block.PowHash) < 0)
{
var LoadBlockNum = Block.BlockNum;
var LoadHash = POW.Hash;
var StrKey = this.GetStrFromHashShort(LoadHash);
var StrHashWas = this.GetStrFromHashShort(Block.Hash);
this.StartLoadBlockHeader(LoadHash, LoadBlockNum, "START OTHER:" + StrKey + " WAS:" + StrHashWas, false)
AddInfoBlock(Block, "REQ H: " + StrKey)
}
Block.CheckMaxPow = true
}
AddToMaxPOW(Block, item, Node)
{
if(Block && item)
{
if(!Block.MaxPOW)
Block.MaxPOW = {}
var POW = Block.MaxPOW;
if(!Block.PrevHash)
return ;
item.BlockNum = Block.BlockNum
item.PrevHash = Block.PrevHash
CalcHashBlockFromSeqAddr(item, Block.PrevHash, global.MINING_VERSION_NUM)
if(POW.SeqHash === undefined || CompareArr(item.PowHash, POW.PowHash) < 0)
{
POW.AddrHash = item.AddrHash
POW.Hash = item.Hash
POW.PowHash = item.PowHash
POW.PrevHash = item.PrevHash
POW.TreeHash = item.TreeHash
POW.SeqHash = item.SeqHash
}
if(Block.SeqHash && CompareArr(item.SeqHash, Block.SeqHash) === 0)
{
if(POW.LocalSeqHash === undefined || CompareArr(POW.LocalSeqHash, Block.SeqHash) !== 0 || CompareArr(item.PowHash, POW.PowLocalHash) < 0)
{
POW.LocalAddrHash = item.AddrHash
POW.PowLocalHash = item.PowHash
POW.LocalSeqHash = Block.SeqHash
}
}
var wasLider;
if(POW.MaxTree)
wasLider = POW.MaxTree.min()
this.AddPOWToMaxTree(POW, item)
if(wasLider)
{
var newLider = POW.MaxTree.min();
if(newLider !== wasLider)
{
var Power = GetPowPower(newLider.PowHash);
AddInfoBlock(Block, "MaxPOW: " + Power)
}
}
}
}
AddPOWToMaxTree(POW, item)
{
if(!POW.MaxTree)
{
POW.MaxTree = new RBTree(function (a,b)
{
return CompareArr(a.PowHash, b.PowHash);
})
}
if(!POW.MaxTree.find(item))
{
POW.MaxTree.insert(item)
if(POW.MaxTree.size > 12)
{
var maxitem = POW.MaxTree.max();
POW.MaxTree.remove(maxitem)
}
}
}
GetMaxPOWList()
{
var arr = [];
var start, finish;
start = this.CurrentBlockNum + TIME_START_SAVE - 2
finish = this.CurrentBlockNum
for(var b = start; b < finish; b++)
{
var Block = this.GetBlock(b);
if(Block && Block.Prepared && Block.MaxPOW)
{
if(Block.MaxPOW && Block.MaxPOW.MaxTree)
{
this.RecreateMaxPOW(Block)
var it = Block.MaxPOW.MaxTree.iterator(), Item;
while((Item = it.next()) !== null)
{
arr.push(Item)
}
}
}
}
return arr;
}
ToMaxPOWList(Arr)
{
for(var i = 0; i < Arr.length; i++)
{
var item = Arr[i];
if(item && item.BlockNum >= this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH && item.BlockNum < this.CurrentBlockNum)
{
var Block = this.GetBlock(item.BlockNum);
this.AddToMaxPOW(Block, item)
}
}
}
RecreateMaxPOW(Block)
{
if(Block.MaxPOW && Block.MaxPOW.MaxTree)
{
var Tree = Block.MaxPOW.MaxTree;
var it = Tree.iterator(), Item;
while((Item = it.next()) !== null)
{
if(!Item.PrevHash)
ToLog("NO Item.PrevHash in " + Block.BlockNum)
if(Item.PrevHash && CompareArr(Item.PrevHash, Block.PrevHash) !== 0)
{
Tree.remove(Item)
it = Tree.iterator()
}
}
}
Block.CheckMaxSum = false
}
CheckMaxSum(Block)
{
var POW = Block.MaxSum;
var List = this.GetBlockList(Block.BlockNum);
var SumPow = this.GetSumFromList(List, Block.BlockNum);
if(POW && POW.SumHash && POW.SumPow > SumPow)
{
var LoadBlockNum = Block.BlockNum;
var LoadHash = POW.SumHash;
var StrKey = this.GetStrFromHashShort(LoadHash);
if(this.StartLoadBlockHeader(LoadHash, LoadBlockNum, "START POW:" + POW.SumPow + ">" + SumPow + " SH:" + StrKey, true))
AddInfoBlock(Block, "REQ SH: " + StrKey)
}
Block.CheckMaxSum = true
}
AddToMaxSum(Block, item)
{
if(Block && item)
{
if(!Block.MaxSum)
Block.MaxSum = {}
var POW = Block.MaxSum;
var SumPow = this.GetSumFromList(item.SumList, Block.BlockNum);
if(POW.SumHash === undefined || SumPow > POW.SumPow)
{
POW.SumPow = SumPow
POW.SumHash = item.SumHash
POW.SumList = item.SumList
AddInfoBlock(Block, "SumPow:" + POW.SumPow)
Block.CheckMaxSum = false
}
return SumPow;
}
return 0;
}
GetMaxSumList()
{
var Arr = [];
var start, finish;
start = this.CurrentBlockNum + TIME_START_LOAD - 2
finish = this.CurrentBlockNum
for(var b = start; b <= finish; b++)
{
var Block = this.GetBlock(b);
if(Block && Block.bSave && Block.MaxSum && Block.MaxSum.SumHash)
{
var POW = Block.MaxSum;
var item = {BlockNum:Block.BlockNum, SumHash:POW.SumHash, SumList:POW.SumList, };
Arr.push(item)
}
}
return Arr;
}
ToMaxSumList(Arr)
{
var start, finish;
start = this.CurrentBlockNum + TIME_START_LOAD - 2
finish = this.CurrentBlockNum
for(var i = 0; i < Arr.length; i++)
{
var item = Arr[i];
if(item && item.BlockNum >= start && item.BlockNum <= finish)
{
var Block = this.GetBlock(item.BlockNum);
if(Block)
{
this.AddToMaxSum(Block, item)
this.CheckMaxSum(Block)
}
}
}
}
GetBlockList(CurBlockNum)
{
var arr = [];
for(var b = CurBlockNum - SUM_LIST_LENGTH + 1; b <= CurBlockNum; b++)
{
var Block = this.GetBlock(b);
if(Block && Block.bSave)
{
var item = {AddrHash:Block.AddrHash, SeqHash:Block.SeqHash, };
arr.push(item)
}
else
{
return [];
}
}
return arr;
}
GetSumFromList(arr, CurBlockNum)
{
var SumPow = 0;
if(arr.length !== SUM_LIST_LENGTH)
return SumPow;
var CountLoad = 0;
var BlockNumStart = CurBlockNum - arr.length + 1;
for(var i = 0; i < arr.length; i++)
{
var Item = arr[i];
if(Item)
{
Item.BlockNum = BlockNumStart + i
var Value = GetHashFromSeqAddr(Item.SeqHash, Item.AddrHash, Item.BlockNum, undefined, global.MINING_VERSION_NUM);
SumPow += GetPowPower(Value.PowHash)
Item.Hash3 = Value.Hash
}
else
{
break;
}
}
return SumPow;
}
GetArrayFromTxTree(Block)
{
if(!Block.PowTxTree)
return [];
var BufLength = 0;
var arr = [];
var it = Block.PowTxTree.iterator(), Item;
while((Item = it.next()) !== null)
{
arr.push(Item)
BufLength += Item.body.length
if(BufLength > MAX_BLOCK_SIZE)
break;
}
return arr;
}
CheckPrioritetTx(Tr)
{
if(Tr.Prioritet === undefined)
{
var Body = Tr.body;
Tr.Prioritet = MAX_LENGTH_SENDER_MAP
var App = DAppByType[Body[0]];
if(App)
{
Tr.SenderNum = App.GetSenderNum(Tr.num, Body)
if(Tr.SenderNum && Tr.SenderNum > 0)
{
Tr.Prioritet = this.GetSenderPrioritet(Tr.num, Tr.SenderNum)
Tr.TimePow = Tr.Prioritet + Tr.power
}
}
}
}
AddToQuote(Tree, Tr)
{
this.CheckPrioritetTx(Tr)
var Tr0 = Tree.find(Tr);
if(Tr0)
{
return 3;
}
else
{
Tree.insert(Tr)
if(Tree.size > MAX_TRANSACTION_LIMIT)
{
var maxitem = Tree.max();
Tree.remove(maxitem)
if(CompareArr(maxitem.HashPow, Tr.HashPow) === 0)
return 0;
}
return 1;
}
}
AddTrToBlockQuote(Block, Tr, bTTAdd)
{
if(Block.PowTxTree)
{
var Res = this.IsValidTransaction(Tr, Block.BlockNum);
if(Res >= 1)
{
Res = this.AddToQuote(Block.PowTxTree, Tr)
}
return Res;
}
}
GetBlockContext(BlockNum)
{
if(BlockNum === undefined || !this.IsCorrectBlockNum(BlockNum))
return undefined;
var Context = this.GetBlock(BlockNum);
if(!Context || !Context.StartTimeNum)
{
Context = this.CreateBlockContext()
Context.BlockNum = BlockNum
Context.DELTA_CURRENT_TIME = GetDeltaCurrentTime()
Context.StartTimeNum = (BlockNum - 1 + BLOCK_DELTA_ACTIVATE) * CONSENSUS_PERIOD_TIME + START_NETWORK_DATE
this.BlockChain[BlockNum] = Context
}
if(!Context.TransferFromAddr)
{
Context.TransferFromAddr = {}
Context.LevelsTransfer = []
}
return Context;
}
StartBlock(Block)
{
Block.Active = true
}
IsCorrectBlockNum(BlockNum)
{
var start = this.CurrentBlockNum - BLOCK_PROCESSING_LENGTH;
var finish = this.GetLastCorrectBlockNum();
if(BlockNum < start || BlockNum > finish)
{
return false;
}
return true;
}
GetLastCorrectBlockNum()
{
return this.CurrentBlockNum + 4;
}
GetStrSendCount(Block)
{
if(!Block)
return "";
var Str = "";
var Count = 0;
for(var L = 0; L < Block.LevelsTransfer.length; L++)
{
var Transfer = Block.LevelsTransfer[L];
Str = Str + "," + Transfer.SendCount
if(typeof Transfer.SendCount === "number")
Count = Count + Transfer.SendCount
}
return "" + Count + ":[" + Str.substr(1) + "]";
}
GetStrGetCount(Block)
{
if(!Block)
return "";
var Str = "";
var Count = 0;
for(var L = 0; L < Block.LevelsTransfer.length; L++)
{
var Transfer = Block.LevelsTransfer[L];
Str = Str + "," + Transfer.GetCount
Count = Count + Transfer.GetCount
}
return "" + Count + ":[" + Str.substr(1) + "]";
}
ToStrBlocks(DopStr)
{
var num = Math.floor(this.CurrentBlockNum / 3) * 3;
var start = num - BLOCK_PROCESSING_LENGTH2 + 2;
var finish = this.CurrentBlockNum;
if(!DopStr)
DopStr = ""
var Str = "";
for(var b = start; b <= finish; b++)
{
var hashStr = "";
var Block = this.GetBlock(b);
if(Block && Block.ErrRun)
{
if(Block.ErrRun)
hashStr = Block.ErrRun.substr(0, 5)
else
if(Block && Block.TreeHash)
hashStr = "-" + GetHexFromAddres(Block.TreeHash).substr(0, 3) + "-"
}
else
if(Block && Block.TreeHash)
{
hashStr = GetHexFromAddres(Block.TreeHash).substr(0, 5)
}
Str = Str + "|" + (hashStr + " ").substr(0, 5)
}
Str = Str.substr(1)
ToInfo("" + finish + " -> " + Str + " " + DopStr)
}
PreparePOWHash(Block)
{
if(!Block.TreeHash)
Block.TreeHash = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
var PrevHash = this.GetPrevHash(Block);
if(!PrevHash)
{
AddInfoBlock(Block, "-err prev hash-")
return false;
}
Block.PrevHash = PrevHash
Block.SeqHash = this.GetSeqHash(Block.BlockNum, Block.PrevHash, Block.TreeHash)
this.CreatePOWNew(Block)
Block.Prepared = true
if(global.USE_MINING && !Block.StartMining)
{
Block.StartMining = true
AddInfoBlock(Block, "-send mining-")
global.SetCalcPOW(Block, "FastCalcBlock")
}
return true;
}
CalcTreeHashFromArrTr(BlockNum, arrTr)
{
var arrHASH = [];
for(var i = 0; i < arrTr.length; i++)
{
var Tr = arrTr[i];
arrHASH.push(Tr.HASH)
}
var Tree = CalcMerklFromArray(BlockNum, arrHASH);
return Tree.Root;
}
CreateTreeHash(Block)
{
if(Block.EndExchange)
return ;
Block.EndExchange = true
if(Block.bSave)
return ;
var PrevBlock = this.GetBlock(Block.BlockNum - 1);
if(PrevBlock && !PrevBlock.EndExchange && !PrevBlock.bSave)
{
AddInfoBlock(Block, "Prev Not End Exchange")
return ;
}
AddInfoBlock(Block, "End Exchange,N=" + Block.TransferNodesCount)
var arrContent = [];
var arrHASH = [];
var arrTr = this.GetArrayFromTxTree(Block);
this.AddDAppTransactions(Block.BlockNum, arrTr)
for(var i = 0; i < arrTr.length; i++)
{
var Tr = arrTr[i];
arrContent.push(Tr.body)
arrHASH.push(Tr.HASH)
}
var Tree = CalcMerklFromArray(Block.BlockNum, arrHASH);
Block.TreeHash = Tree.Root
Block.arrContent = arrContent
Block.TrCount = Block.arrContent.length
}
WatchdogSaved(BlockNum)
{
var Block = this.GetBlock(BlockNum);
if(!Block)
{
ToLog("#1 WatchdogSaved: no BlockNum=" + BlockNum)
return ;
}
if(Block.bSave)
{
var BlockDB = this.ReadBlockDB(BlockNum);
if(!BlockDB)
{
Block.bSave = false
return ;
}
if(CompareArr(BlockDB.Hash, Block.Hash) !== 0)
{
AddInfoBlock(Block, "=ERR:WATCHDOG=")
ToLog("#3 WatchdogSaved: Error Hash on Num=" + BlockNum)
return ;
}
if(CompareArr(BlockDB.SumHash, Block.SumHash) !== 0)
{
AddInfoBlock(Block, "=ERR:WATCHDOG=")
ToLog("#4 WatchdogSaved: Error SumHash on Num=" + BlockNum)
return ;
}
if(CompareArr(BlockDB.SeqHash, Block.SeqHash) !== 0)
{
AddInfoBlock(Block, "=ERR:WATCHDOG=")
ToLog("#5 WatchdogSaved: Error SeqHash on Num=" + BlockNum)
return ;
}
var PrevHash = this.GetPrevHash(Block);
if(!PrevHash)
{
AddInfoBlock(Block, "=ERR:WATCHDOG=")
ToLog("#6 WatchdogSaved: Error PrevHash on Num=" + BlockNum)
return ;
}
var SeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash);
if(CompareArr(SeqHash, Block.SeqHash) !== 0)
{
AddInfoBlock(Block, "=ERR:WATCHDOG=")
ToLog("#7 WatchdogSaved: Error SeqHash on Num=" + BlockNum)
return ;
}
PrevHash = this.GetPrevHashDB(BlockDB)
SeqHash = this.GetSeqHash(BlockDB.BlockNum, PrevHash, BlockDB.TreeHash)
if(CompareArr(SeqHash, BlockDB.SeqHash) !== 0)
{
AddInfoBlock(Block, "=ERR:WATCHDOG=")
ToLog("#8 WatchdogSaved: Error SeqHash on Num=" + BlockNum)
return ;
}
}
}
DoBlockChain()
{
if(glStopNode)
return ;
if(!CAN_START)
return ;
this.StartConsensus()
var CURRENTBLOCKNUM = this.CurrentBlockNum;
if(GrayConnect())
{
if(!this.LoadHistoryMode)
this.StartSyncBlockchain(undefined, 1)
return ;
}
if(this.LoadHistoryMode)
return ;
var bWasSave = false;
var LoadBlockNum;
var LoadHash;
var start_save = CURRENTBLOCKNUM + TIME_START_SAVE;
for(var BlockNum = CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH2; BlockNum > BLOCK_PROCESSING_LENGTH2 && BlockNum < CURRENTBLOCKNUM; BlockNum++)
{
var Block = this.GetBlock(BlockNum);
if(!Block)
{
Block = this.GetBlockContext(BlockNum)
if(!Block)
{
continue;
}
}
if(Block.bSave)
{
var BlockDB = this.ReadBlockDB(BlockNum);
if(!BlockDB)
{
Block.bSave = false
}
}
if(global.WATCHDOG_DEV)
this.WatchdogSaved(Block.BlockNum)
if(Block.bSave)
{
bWasSave = true
if(Block.MaxSum && !Block.CheckMaxSum)
{
AddInfoBlock(Block, "CheckMaxSum")
this.CheckMaxSum(Block)
}
if(BlockNum <= CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH * 4)
{
Block.TransferFromAddr = undefined
Block.LevelsTransfer = undefined
Block.mapData = undefined
Block.MaxPOW = undefined
Block.MaxSum = undefined
Block.arrContent = undefined
if(Block.PowTxTree)
{
Block.PowTxTree.clear()
Block.PowTxTree = undefined
}
}
continue;
}
var PrevBlock = this.GetBlock(BlockNum - 1);
if(!PrevBlock)
{
Block.HasErr = 1
AddInfoBlock(Block, "!PrevBlock")
continue;
}
if(BlockNum >= CURRENTBLOCKNUM + TIME_END_EXCHANGE)
{
if(!Block.Active)
{
AddInfoBlock(Block, "WAIT ACTIVATE")
continue;
}
else
if(!Block.EndExchange)
{
AddInfoBlock(Block, "WAIT EXCHANGE")
continue;
}
}
if(BlockNum === CURRENTBLOCKNUM + TIME_START_POW || Block.EndExchange)
if(!Block.Prepared)
{
if(!Block.EndExchange)
this.CreateTreeHash(Block)
AddInfoBlock(Block, "Start POW")
this.PreparePOWHash(Block)
if(!Block.Prepared)
AddInfoBlock(Block, "!!Prepared")
continue;
}
if(!Block.EndExchange)
{
AddInfoBlock(Block, "Not EndExchange")
Block.HasErr = 1
Block.Prepared = 0
this.CreateTreeHash(Block)
}
if(!Block.Prepared)
{
Block.HasErr = 1
AddInfoBlock(Block, "Not was Prepared")
this.PreparePOWHash(Block)
if(!Block.Prepared)
continue;
}
{
var PrevHash = this.GetPrevHash(Block);
if(!PrevHash)
{
Block.HasErr = 1
continue;
}
var SeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash);
if(CompareArr(SeqHash, Block.SeqHash) !== 0)
{
Block.HasErr = 1
AddInfoBlock(Block, "New fast pow")
this.PreparePOWHash(Block)
}
if(Block.MaxPOW && Block.MaxPOW.SeqHash && Block.MaxPOW.AddrHash && Block.MaxPOW.LocalSeqHash && CompareArr(Block.SeqHash,
Block.MaxPOW.LocalSeqHash) === 0)
{
if(CompareArr(Block.SeqHash, Block.MaxPOW.LocalSeqHash) === 0 && CompareArr(Block.MaxPOW.PowLocalHash, Block.PowHash) < 0)
{
Block.AddrHash = Block.MaxPOW.LocalAddrHash
CalcHashBlockFromSeqAddr(Block, Block.PrevHash, global.MINING_VERSION_NUM)
AddInfoBlock(Block, "->Local lider:" + GetPowPower(Block.PowHash))
}
if(CompareArr(Block.SeqHash, Block.MaxPOW.SeqHash) === 0 && CompareArr(Block.MaxPOW.AddrHash, Block.AddrHash) !== 0 && CompareArr(Block.MaxPOW.PowHash,
Block.PowHash) < 0)
{
Block.AddrHash = Block.MaxPOW.AddrHash
CalcHashBlockFromSeqAddr(Block, Block.PrevHash, global.MINING_VERSION_NUM)
AddInfoBlock(Block, "->Max lider")
}
}
else
{
Block.HasErr = 1
AddInfoBlock(Block, "ERROR MaxPOW")
}
if(Block.MaxPOW && Block.MaxPOW.SeqHash && !Block.CheckMaxPow && !Block.CheckMaxSum && CompareArr(Block.SeqHash, Block.MaxPOW.SeqHash) !== 0)
{
AddInfoBlock(Block, "CheckMaxPow")
this.CheckingMaxPowOther(Block)
}
if(BlockNum > start_save)
continue;
if(PrevBlock.bSave && this.BlockNumDB + 1 >= Block.BlockNum)
{
this.AddToStatBlockConfirmation(Block)
var Power = GetPowPower(Block.PowHash);
if(this.WriteBlockDB(Block))
{
if(Block.arrContent && Block.arrContent.length)
ADD_TO_STAT("MAX:TRANSACTION_COUNT", Block.arrContent.length)
AddInfoBlock(Block, "SAVE:" + Power + " TH:" + this.GetStrFromHashShort(Block.TreeHash).substr(0, 4))
}
else
{
Block.HasErr = 1
AddInfoBlock(Block, "ERROR WRITE DB")
}
this.AddToMaxSum(Block, {SumHash:Block.SumHash, SumList:this.GetBlockList(Block.BlockNum), })
if(typeof global.RESYNC_CONDITION === "object")
{
if(!this.OwnBlockCount)
this.OwnBlockCount = 0
var Miner = ReadUintFromArr(Block.AddrHash, 0);
var MultK = RESYNC_CONDITION.K_POW;
var MaxBlocks = RESYNC_CONDITION.OWN_BLOCKS;
if(Miner === GENERATE_BLOCK_ACCOUNT)
{
this.OwnBlockCount++
if(this.OwnBlockCount >= MaxBlocks)
{
var PrevSumPow = this.GetAvgPowBlock(Block.BlockNum - 2 * MaxBlocks, MaxBlocks);
var CurrentPow = this.GetAvgPowBlock(Block.BlockNum - MaxBlocks, MaxBlocks);
if(CurrentPow === 0 || PrevSumPow - CurrentPow >= MultK)
{
ToLog("START RESYNC CONDITION")
this.OwnBlockCount = 0
this.StartSyncBlockchain()
return ;
}
}
}
else
{
this.OwnBlockCount = 0
}
}
}
else
{
Block.HasErr = 1
if(!PrevBlock.bSave)
AddInfoBlock(Block, "Prev block not saved")
else
AddInfoBlock(Block, "Low BlockNumDB")
}
}
}
var MaxNumBlockDB = this.GetMaxNumBlockDB();
if(CURRENTBLOCKNUM + BLOCK_PROCESSING_LENGTH2 > MaxNumBlockDB && CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH2 < MaxNumBlockDB)
for(var BlockNum = CURRENTBLOCKNUM - BLOCK_PROCESSING_LENGTH2; BlockNum > BLOCK_PROCESSING_LENGTH2 && BlockNum < start_save; BlockNum++)
{
var Block = this.GetBlock(BlockNum);
if(Block && !Block.bSave && Block.TrCount && Block.TreeHash && !IsZeroArr(Block.TreeHash) && !Block.WasSaveDataTree)
{
this.PreSaveDataTreeToDB(Block)
Block.WasSaveDataTree = 1
AddInfoBlock(Block, "*PRESAVE DATA TREE*")
ToLog("PRESAVE DATA: " + Block.BlockNum, 2)
}
}
this.RelayMode = !bWasSave
this.FREE_MEM_BLOCKS(CURRENTBLOCKNUM - BLOCK_COUNT_IN_MEMORY)
}
GetAvgPowBlock(StartNum, CountNum)
{
var Count = 0;
var SumPow = 0;
for(var Num = StartNum; Num < StartNum + CountNum; Num++)
{
var Block = this.GetBlock(Num);
if(Block && Block.bSave)
{
var Power = GetPowPower(Block.PowHash);
SumPow += Power
Count++
}
}
if(!Count)
return 0;
else
return SumPow / Count;
}
CreatePOWNew(Block)
{
CreateHashMinimal(Block, GENERATE_BLOCK_ACCOUNT)
this.AddToMaxPOW(Block, {SeqHash:Block.SeqHash, AddrHash:Block.AddrHash, PrevHash:Block.PrevHash, TreeHash:Block.TreeHash,
})
}
SetNoPOW(BlockNumFrom, bReload, RefBlockNum)
{
var CurNum = BlockNumFrom;
var finish = this.GetLastCorrectBlockNum();
while(true)
{
var BlockMem = this.BlockChain[CurNum];
if(BlockMem)
{
if(BlockMem.Prepared)
{
AddInfoBlock(BlockMem, "-reset POW:" + RefBlockNum + "/" + bReload)
BlockMem.bSave = false
BlockMem.Prepared = false
BlockMem.StartMining = false
this.PreparePOWHash(BlockMem)
}
this.RecreateMaxPOW(BlockMem)
}
if(!BlockMem && CurNum > finish)
break;
CurNum++
}
}
MiningProcess(msg)
{
var BlockMining = this.GetBlock(msg.BlockNum);
if(!BlockMining)
{
return ;
}
if(!BlockMining.StartMining || BlockMining.bSave)
return ;
if(BlockMining && BlockMining.Hash && BlockMining.SeqHash && CompareArr(BlockMining.SeqHash, msg.SeqHash) === 0)
{
var ValueOld = GetHashFromSeqAddr(BlockMining.SeqHash, BlockMining.AddrHash, BlockMining.BlockNum);
var ValueMsg = GetHashFromSeqAddr(msg.SeqHash, msg.AddrHash, BlockMining.BlockNum);
var bWas = 0;
if(CompareArr(ValueOld.Hash1, ValueMsg.Hash1) > 0)
{
var Nonce1 = ReadUintFromArr(msg.AddrHash, 12);
var DeltaNum1 = ReadUint16FromArr(msg.AddrHash, 24);
WriteUintToArrOnPos(BlockMining.AddrHash, Nonce1, 12)
WriteUint16ToArrOnPos(BlockMining.AddrHash, DeltaNum1, 24)
bWas += 1
}
if(CompareArr(ValueOld.Hash2, ValueMsg.Hash2) > 0)
{
var Nonce0 = ReadUintFromArr(msg.AddrHash, 6);
var Nonce2 = ReadUintFromArr(msg.AddrHash, 18);
var DeltaNum2 = ReadUint16FromArr(msg.AddrHash, 26);
WriteUintToArrOnPos(BlockMining.AddrHash, Nonce0, 6)
WriteUintToArrOnPos(BlockMining.AddrHash, Nonce2, 18)
WriteUint16ToArrOnPos(BlockMining.AddrHash, DeltaNum2, 26)
bWas += 2
}
if(!bWas)
return ;
var ValueNew = GetHashFromSeqAddr(BlockMining.SeqHash, BlockMining.AddrHash, BlockMining.BlockNum);
BlockMining.Hash = ValueNew.Hash
BlockMining.PowHash = ValueNew.PowHash
BlockMining.Power = GetPowPower(BlockMining.PowHash)
ADD_TO_STAT("MAX:POWER", BlockMining.Power)
var Power = GetPowPower(BlockMining.PowHash);
var HashCount = Math.pow(2, Power);
ADD_HASH_RATE(HashCount)
AddInfoBlock(BlockMining, "Set POW: " + Power)
this.SetNoPOW(BlockMining.BlockNum + 8, 0, BlockMining.BlockNum)
this.AddToMaxPOW(BlockMining, {SeqHash:BlockMining.SeqHash, AddrHash:BlockMining.AddrHash, PrevHash:BlockMining.PrevHash, TreeHash:BlockMining.TreeHash,
})
}
}
};
global.TreeBlockBuf = new STreeBuffer(50 * 1000, CompareItemHashSimple, "string");
var PrevTimeIdle = 0;
OnTimeIdle();
function OnTimeIdle()
{
var CurTime = Date.now();
var Delta = CurTime - PrevTimeIdle;
if(Delta <= 51)
{
ADD_TO_STAT("TIME_IDLE", 5);
}
setTimeout(OnTimeIdle, 49);
PrevTimeIdle = CurTime;
};