forked from circlecloud/tera
1411 lines
56 KiB
TypeScript
1411 lines
56 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 CBlock from './block-loader'
|
|
import { RBTree } from './library'
|
|
import { STreeBuffer } from './base';
|
|
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 * global.BLOCK_PROCESSING_LENGTH;
|
|
global.CONSENSUS_TIK_TIME = global.CONSENSUS_PERIOD_TIME / 10;
|
|
global.CONSENSUS_CHECK_TIME = global.CONSENSUS_PERIOD_TIME / 20;
|
|
const PERIOD_FOR_NEXT_SEND = global.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 = {};
|
|
export default class CConsensus extends CBlock {
|
|
CurrentBlockNum
|
|
SendBlockID
|
|
RelayMode
|
|
TreeSendPacket
|
|
idBlockChainTimer
|
|
OwnBlockCount
|
|
LevelNodes
|
|
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(global.CompareItemHash)
|
|
if (!global.ADDRLIST_MODE && !this.VirtualMode) {
|
|
this.idBlockChainTimer = setInterval(this.StartBlockChain.bind(this), global.CONSENSUS_PERIOD_TIME - 5)
|
|
setInterval(this.DoTransfer.bind(this), global.CONSENSUS_CHECK_TIME)
|
|
}
|
|
}
|
|
StartBlockChain() {
|
|
this.OnStartSecond()
|
|
var CurTimeNum = global.GetCurrentTime() - global.CONSENSUS_PERIOD_TIME / 2;
|
|
var StartTimeNum = Math.floor((CurTimeNum + global.CONSENSUS_PERIOD_TIME) / global.CONSENSUS_PERIOD_TIME) * global.CONSENSUS_PERIOD_TIME;
|
|
var DeltaForStart = StartTimeNum - CurTimeNum;
|
|
if (DeltaForStart < (global.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), global.CONSENSUS_PERIOD_TIME)
|
|
self.OnStartSecond()
|
|
}, DeltaForStart)
|
|
}
|
|
}
|
|
OnStartSecond() {
|
|
global.PrepareStatEverySecond()
|
|
this.AddStatOnTimer()
|
|
this.DoBlockChain()
|
|
}
|
|
CreateBlockContext() {
|
|
var Context: any = {};
|
|
Context.AddInfo = global.AddInfoBlock.bind(Context)
|
|
Context.Active = false
|
|
Context.TransferFromAddr = {}
|
|
Context.LevelsTransfer = []
|
|
Context.ErrRun = ""
|
|
Context.PowTxTree = new RBTree(global.CompareItemTimePow)
|
|
Context.PowTicketTree = new RBTree(global.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:" + global.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: global.CONSENSUS_TIK_TIME * (2 + global.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 * global.CONSENSUS_PERIOD_TIME, };
|
|
Transfer.TransferNodes[Addr] = Item
|
|
}
|
|
Context.TransferFromAddr[Addr] = Transfer
|
|
}
|
|
}
|
|
}
|
|
Context.MLevelSend = Context.StartLevel
|
|
return Context;
|
|
}
|
|
StartConsensus() {
|
|
if (!global.CAN_START)
|
|
return;
|
|
var StartBlockNum = global.GetCurrentBlockNumByTime();
|
|
if (StartBlockNum < global.BLOCK_PROCESSING_LENGTH2)
|
|
return;
|
|
this.CurrentBlockNum = StartBlockNum
|
|
var Block0 = this.GetBlockContext(StartBlockNum - global.BLOCK_DELTA_ACTIVATE);
|
|
if (!Block0.Active) {
|
|
global.AddInfoBlock(Block0, "Activate")
|
|
this.StartBlock(Block0)
|
|
} else {
|
|
global.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(global.shaarr(Item.body)) + "(" + Item.body.length + "),"
|
|
}
|
|
global.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) {
|
|
global.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) {
|
|
global.ADD_TO_STAT("NO_TRANSFER")
|
|
this.AddCheckErrCount(Node, 1, "Err Transfer")
|
|
return;
|
|
}
|
|
Transfer.WasGet = true
|
|
if (global.DoTxLog && Data.TicketArray.length)
|
|
global.ToLog("TRANSFER BlockNum:" + Block.BlockNum + " TicketArray=" + Data.TicketArray.length + " from " + global.NodeName(Node))
|
|
if (global.DoTxLog && Data.TxArray.length)
|
|
global.ToLog("TRANSFER BlockNum:" + Block.BlockNum + " TxArray=" + Data.TxArray.length + " from " + global.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
|
|
}
|
|
if (global.USE_CHECK_SENDING && Res > 0) {
|
|
var Tt = Block.PowTxTree.find(Tr);
|
|
if (Tt) {
|
|
if (!Tt.NodesList)
|
|
Tt.NodesList = []
|
|
Tt.NodesList.push(Node)
|
|
Tt.TreeLevel = Transfer.TreeLevel
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (var i = 0; i < Data.TicketArray.length; i++) {
|
|
var Tr = this.AddTicketToBlockQuote(Block, Data.TicketArray[i]);
|
|
if (Tr) {
|
|
if (!Tr.NodesList)
|
|
Tr.NodesList = []
|
|
Tr.NodesList.push(Node)
|
|
}
|
|
}
|
|
}
|
|
global.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 = global.GetCurrentTime() - 0
|
|
var Item = Transfer.TransferNodes[Key];
|
|
Item.GetTiming = global.GetCurrentTime(Block.DELTA_CURRENT_TIME) - Block.StartTimeNum
|
|
if (!Block.TransferNodesCount)
|
|
Block.TransferNodesCount = 0
|
|
Block.TransferNodesCount++
|
|
}
|
|
DoTransfer() {
|
|
if (global.glStopNode)
|
|
return;
|
|
if (!global.CAN_START)
|
|
return;
|
|
var MaxPOWList;
|
|
var MaxSumList;
|
|
var start = this.CurrentBlockNum - global.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 (global.USE_TICKET)
|
|
this.DoJobListTX(Block)
|
|
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;
|
|
if (global.USE_TICKET)
|
|
ArrT = this.GetArrayFromTicketTree(Block)
|
|
else
|
|
ArrT = this.GetArrayFromTxTree(Block)
|
|
this.SendDataTransfer(Transfer, ArrT, MaxPOWList, MaxSumList, Block)
|
|
}
|
|
Transfer.WasSend = true
|
|
var bNext = Transfer.WasGet;
|
|
if (!bNext) {
|
|
var CurTimeNum = global.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];
|
|
global.ADD_TO_STAT("TRANSFER_TIME_OUT")
|
|
this.AddCheckErrCount(Item.Node, 1, "TRANSFER_TIME_OUT")
|
|
}
|
|
global.ADD_TO_STAT("TimeOutLevel")
|
|
}
|
|
}
|
|
if (bNext) {
|
|
if (Block.MLevelSend === 0) {
|
|
Block.EndExchangeTime = Date.now()
|
|
if (!global.USE_TICKET)
|
|
this.CheckEndExchange(Block)
|
|
}
|
|
Block.MLevelSend--
|
|
}
|
|
}
|
|
}
|
|
CheckEndExchange(Block) {
|
|
if (Block.EndExchange)
|
|
return;
|
|
if (!global.USE_TICKET) {
|
|
this.CreateTreeHash(Block)
|
|
return;
|
|
}
|
|
if (!Block.JobListTX || !Block.EndExchangeTime)
|
|
return;
|
|
var CurTime = Date.now();
|
|
var Delta = CurTime - Block.EndExchangeTime;
|
|
if (Delta >= TX_DELTA_PROCESS_TIME * 2) {
|
|
if (global.DoTxLog)
|
|
global.ToLog("END:" + Block.BlockNum + " -> CreateTreeHash")
|
|
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 + "-" + global.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 + "-" + global.GetHexFromArr(elem.SumHash);
|
|
var bWasSend = global.TreeBlockBuf.LoadValue(Str, 1);
|
|
if (!bWasSend) {
|
|
global.TreeBlockBuf.SaveValue(Str, true)
|
|
arrSum.push(elem)
|
|
}
|
|
}
|
|
var Arr;
|
|
if (global.USE_CHECK_SENDING)
|
|
Arr = this.FilterArrForSendNode(Block, Item.Node, ArrT, global.USE_TICKET)
|
|
else
|
|
Arr = ArrT
|
|
if (global.USE_LEVEL_WAY) {
|
|
var Arr2 = [];
|
|
for (var t = 0; t < Arr.length; t++) {
|
|
var Tr = Arr[t];
|
|
if (Tr.TreeLevel !== Transfer.TreeLevel)
|
|
Arr2.push(Tr)
|
|
}
|
|
Arr = Arr2
|
|
}
|
|
if (global.DoTxLog)
|
|
global.ToLog("SEND TRANSFER BlockNum:" + Block.BlockNum + " Arr=" + Arr.length + " to " + global.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() })
|
|
}
|
|
}
|
|
Send(Node, Info, TypeData?) {
|
|
// defiend in server.ts
|
|
}
|
|
TRANSFERTX(Info, CurTime) {
|
|
var Data = this.DataFromF(Info);
|
|
var Node = Info.Node;
|
|
var Block = this.GetBlockContext(Data.BlockNum);
|
|
if (!Block) {
|
|
return;
|
|
}
|
|
if (global.DoTxLog)
|
|
global.ToLog("TRANSFERTX BlockNum:" + Block.BlockNum + " Array=" + Data.Array.length + " from " + global.NodeName(Node))
|
|
for (var i = 0; i < Data.Array.length; i++) {
|
|
this.AddTrToBlockQuote(Block, Data.Array[i], 1)
|
|
}
|
|
}
|
|
static TRANSFERTX_F() {
|
|
return "{BlockNum:uint, Array:[{body:tr}]}";
|
|
}
|
|
static GETTRANSFERTX_F() {
|
|
return "{BlockNum:uint, TicketArray:[{HashTicket:arr10}]}";
|
|
}
|
|
GETTRANSFERTX(Info, CurTime) {
|
|
return;
|
|
var Data = this.DataFromF(Info);
|
|
var Node = Info.Node;
|
|
var Block = this.GetBlockContext(Data.BlockNum);
|
|
if (!Block) {
|
|
return;
|
|
}
|
|
this.SendTrByTickets(Info, Node, Block, Data.TicketArray, 10)
|
|
}
|
|
CanSendTest() {
|
|
return 1;
|
|
}
|
|
SendTrByTickets(Info, Node, Block, ArrTT, CountTrySend) {
|
|
if (!Block.PowTxTree)
|
|
return;
|
|
var Arr = [];
|
|
var bFindTT = 0;
|
|
var BufLength = 0;
|
|
for (var i = 0; i < ArrTT.length; i++) {
|
|
var Tr = ArrTT[i];
|
|
this.CheckCreateTicketObject(Tr, Block.BlockNum)
|
|
var Tr0 = Block.PowTxTree.find(Tr);
|
|
if (Tr0) {
|
|
if (Tr0.IsTx) {
|
|
if (this.CanSendTest())
|
|
Arr.push(Tr0)
|
|
BufLength += Tr0.body.length
|
|
if (BufLength > global.MAX_BLOCK_SIZE)
|
|
break;
|
|
}
|
|
else {
|
|
bFindTT = 1
|
|
}
|
|
}
|
|
}
|
|
if (bFindTT && CountTrySend) {
|
|
let SELF = this;
|
|
setTimeout(function() {
|
|
SELF.SendTrByTickets(Info, Node, Block, ArrTT, CountTrySend - 1)
|
|
}, 100)
|
|
return;
|
|
}
|
|
if (!Arr.length)
|
|
return;
|
|
var SendData = { "Method": "RETTRANSFERTX", "Context": Info.Context, "Data": { BlockNum: Block.BlockNum, Array: Arr, } };
|
|
this.SendF(Node, SendData, global.MAX_BLOCK_SIZE + 1000)
|
|
}
|
|
static
|
|
RETTRANSFERTX_F() {
|
|
return "{BlockNum:uint,Array:[{body:tr}]}";
|
|
}
|
|
RETTRANSFERTX(Info, CurTime) {
|
|
return;
|
|
var Data = this.DataFromF(Info);
|
|
var Node = Info.Node;
|
|
Node.TransferBlockNum = Data.BlockNum
|
|
var Block = this.GetBlockContext(Data.BlockNum);
|
|
if (!Block || !Block.PowTxTree) {
|
|
return;
|
|
}
|
|
if (global.DoTxLog)
|
|
global.ToLog("RETTRANSFERTX BlockNum:" + Block.BlockNum + " Array=" + Data.Array.length + " from " + global.NodeName(Node))
|
|
for (var i = 0; i < Data.Array.length; i++) {
|
|
var Tr = Data.Array[i];
|
|
this.CheckCreateTransactionObject(Tr)
|
|
var Tr0 = Block.PowTxTree.find(Tr);
|
|
if (Tr0) {
|
|
if (!Tr0.IsTx) {
|
|
Tr0.IsTx = 1
|
|
Tr0.body = Tr.body
|
|
Tr0.HASH = Tr.HASH
|
|
}
|
|
}
|
|
}
|
|
}
|
|
DoJobListTX(Block) {
|
|
if (Block.EndExchange || !Block.JobListTX || !Block.PowTicketTree)
|
|
return;
|
|
var ArrTx;
|
|
var CurTime = Date.now();
|
|
for (var i = 0; i < Block.JobListTX.length; i++) {
|
|
var JobItem = Block.JobListTX[i];
|
|
var Delta = CurTime - JobItem.Time;
|
|
if (!JobItem.WasSend && Delta >= TX_DELTA_PROCESS_TIME) {
|
|
JobItem.WasSend = 1
|
|
if (!ArrTx)
|
|
ArrTx = this.GetArrayFromTxTree(Block)
|
|
var Arr = this.FilterArrForSendNode(Block, JobItem.Node, ArrTx);
|
|
if (!Arr.length)
|
|
return;
|
|
if (global.DoTxLog)
|
|
global.ToLog("DoJobListTX BlockNum:" + Block.BlockNum + " Arr=" + Arr.length + " to " + global.NodeName(JobItem.Node))
|
|
var SendData = { "Method": "TRANSFERTX", "Context": {}, "Data": { BlockNum: Block.BlockNum, Array: Arr, } };
|
|
this.SendF(JobItem.Node, SendData, global.MAX_BLOCK_SIZE + 1000)
|
|
}
|
|
}
|
|
}
|
|
FindNodeTicket(Block, Tr, Node, bTt) {
|
|
var Ticket;
|
|
if (bTt)
|
|
Ticket = Tr
|
|
else
|
|
Ticket = Block.PowTicketTree.find(Tr)
|
|
if (Ticket && Ticket.NodesList) {
|
|
for (var n = 0; n < Ticket.NodesList.length; n++) {
|
|
var NodeItem = Ticket.NodesList[n];
|
|
if (NodeItem === Node) {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
FilterArrForSendNode(Block, Node, ArrTx, bTt?) {
|
|
var Arr = [];
|
|
for (var t = 0; t < ArrTx.length; t++) {
|
|
var Tr = ArrTx[t];
|
|
if (this.FindNodeTicket(Block, Tr, Node, bTt))
|
|
continue;
|
|
Arr.push(Tr)
|
|
}
|
|
return Arr;
|
|
}
|
|
CheckEndExchange00(Block) {
|
|
if (Block.EndExchange)
|
|
return;
|
|
var CurTime = Date.now();
|
|
var CanEnd = 1;
|
|
var bSend = 0;
|
|
var it = Block.PowTxTree.iterator(), Tr;
|
|
while ((Tr = it.next()) !== null) {
|
|
if (!Tr.IsTx) {
|
|
if (!Tr.LastProcessTime)
|
|
throw "!Tr.LastProcessTime";
|
|
var Delta = CurTime - Tr.LastProcessTime;
|
|
if (Delta < TX_PROCESS_TIME * 10) {
|
|
bSend = 1
|
|
CanEnd = 0
|
|
break;
|
|
} else {
|
|
var Stop = 1;
|
|
}
|
|
}
|
|
}
|
|
if (CanEnd) {
|
|
if (global.DoTxLog)
|
|
global.ToLog("END:" + Block.BlockNum + " -> CreateTreeHash")
|
|
this.CreateTreeHash(Block)
|
|
}
|
|
else
|
|
if (bSend) {
|
|
this.CheckTxExchange(Block, bSend)
|
|
}
|
|
}
|
|
CheckTxExchange(Block, b?) {
|
|
return;
|
|
if (Block.EndExchange)
|
|
return;
|
|
if (!Block.PowTxTree)
|
|
return;
|
|
var CurTime = Date.now();
|
|
var ArrNodesArr = [];
|
|
var it = Block.PowTxTree.iterator(), Tr;
|
|
while ((Tr = it.next()) !== null) {
|
|
if (!Tr.IsTx) {
|
|
if (!Tr.LastProcessTime)
|
|
throw "!Tr.LastProcessTime";
|
|
var Delta = CurTime - Tr.LastProcessTime;
|
|
if (Delta >= TX_PROCESS_TIME) {
|
|
for (var i = 0; i < Tr.Nodes.length; i++) {
|
|
var TrNode = Tr.Nodes[i];
|
|
var LocDelta = CurTime - TrNode.Time;
|
|
if (!TrNode.WasSend && LocDelta >= TX_PROCESS_TIME) {
|
|
var FindArr = undefined;
|
|
for (var n = 0; n < ArrNodesArr.length; n++) {
|
|
var ElArr = ArrNodesArr[n];
|
|
if (ElArr.Node === TrNode.Node) {
|
|
FindArr = ElArr
|
|
break;
|
|
}
|
|
}
|
|
if (!FindArr) {
|
|
FindArr = { Node: TrNode.Node, Arr: [] }
|
|
ArrNodesArr.push(FindArr)
|
|
}
|
|
Tr.LastProcessTime = CurTime
|
|
TrNode.WasSend = 1
|
|
FindArr.Arr.push(Tr)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (var n = 0; n < ArrNodesArr.length; n++) {
|
|
var ElArr = ArrNodesArr[n];
|
|
if (global.DoTxLog)
|
|
global.ToLog("CheckTxExchange BlockNum:" + Block.BlockNum + " Array=" + ElArr.Arr.length + " to " + global.NodeName(ElArr.Node))
|
|
var SendData = { "Method": "GETTRANSFERTX", "Context": {}, "Data": { BlockNum: Block.BlockNum, TicketArray: ElArr.Arr, } };
|
|
this.SendF(ElArr.Node, SendData, ElArr.Arr.length * global.TR_TICKET_HASH_LENGTH + 1000)
|
|
}
|
|
}
|
|
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 + global.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 ArrTt, ArrTx;
|
|
if (global.USE_TICKET) {
|
|
ArrTt = ArrT
|
|
ArrTx = []
|
|
}
|
|
else {
|
|
ArrTt = []
|
|
ArrTx = ArrT
|
|
}
|
|
Data = {
|
|
"Version": 5, "BlockNum": Block.BlockNum, "Reserv1": 0, "MaxPOW": MaxPOWList, "Reserv2": 0, "BaseBlockNum": this.CurrentBlockNum - Block.BlockNum,
|
|
"MaxSumID": MaxSumID, "BlockList": BlockList, "TicketArray": ArrTt, "TxArray": ArrTx, "NoSendTx": Node.NoSendTx,
|
|
}
|
|
var BufWrite = global.BufLib.GetBufferFromObject(Data, FORMAT_DATA_TRANSFER, global.MAX_BLOCK_SIZE + 30000, WorkStructSend);
|
|
return BufWrite;
|
|
}
|
|
static
|
|
TRANSFER_F() {
|
|
return FORMAT_DATA_TRANSFER;
|
|
}
|
|
CheckingMaxPowOther(Block) {
|
|
var POW = Block.MaxPOW;
|
|
if (POW && POW.Hash && global.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)
|
|
global.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
|
|
global.CalcHashBlockFromSeqAddr(item, Block.PrevHash, global.MINING_VERSION_NUM)
|
|
if (POW.SeqHash === undefined || global.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 && global.CompareArr(item.SeqHash, Block.SeqHash) === 0) {
|
|
if (POW.LocalSeqHash === undefined || global.CompareArr(POW.LocalSeqHash, Block.SeqHash) !== 0 || global.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 = global.GetPowPower(newLider.PowHash);
|
|
global.AddInfoBlock(Block, "MaxPOW: " + Power)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
AddPOWToMaxTree(POW, item) {
|
|
if (!POW.MaxTree) {
|
|
POW.MaxTree = new RBTree(function(a: any, b: any) {
|
|
return global.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 + global.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 - global.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)
|
|
global.ToLog("NO Item.PrevHash in " + Block.BlockNum)
|
|
if (Item.PrevHash && global.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))
|
|
global.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
|
|
global.AddInfoBlock(Block, "SumPow:" + POW.SumPow)
|
|
Block.CheckMaxSum = false
|
|
}
|
|
return SumPow;
|
|
}
|
|
return 0;
|
|
}
|
|
GetMaxSumList() {
|
|
var Arr = [];
|
|
var start, finish;
|
|
start = this.CurrentBlockNum + global.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 + global.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 - global.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 !== global.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 = global.GetHashFromSeqAddr(Item.SeqHash, Item.AddrHash, Item.BlockNum, undefined, global.MINING_VERSION_NUM);
|
|
SumPow += global.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 > global.MAX_BLOCK_SIZE)
|
|
break;
|
|
}
|
|
return arr;
|
|
}
|
|
GetArrayFromTicketTree(Block) {
|
|
if (!Block.PowTicketTree)
|
|
return [];
|
|
var arr = [];
|
|
var it = Block.PowTicketTree.iterator(), Item;
|
|
while ((Item = it.next()) !== null) {
|
|
arr.push(Item)
|
|
}
|
|
return arr;
|
|
}
|
|
CheckPrioritetTx(Tr) {
|
|
if (Tr.Prioritet === undefined) {
|
|
var Body = Tr.body;
|
|
Tr.Prioritet = global.MAX_LENGTH_SENDER_MAP
|
|
var App = global.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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
GetSenderPrioritet(BlockNum, SenderNum) {
|
|
//defiend in transaction-validator.ts(CSmartContract)
|
|
}
|
|
AddToQuote(Tree, Tr) {
|
|
this.CheckPrioritetTx(Tr)
|
|
var Tr0 = Tree.find(Tr);
|
|
if (Tr0) {
|
|
return 3;
|
|
}
|
|
else {
|
|
Tree.insert(Tr)
|
|
if (Tree.size > global.MAX_TRANSACTION_LIMIT) {
|
|
var maxitem = Tree.max();
|
|
Tree.remove(maxitem)
|
|
if (global.CompareArr(maxitem.HashPow, Tr.HashPow) === 0)
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
}
|
|
IsValidTicket(Tr, BlockNum): any {
|
|
// defiend in transaction-validator.ts(CSmartContract)
|
|
}
|
|
IsValidTransaction(Tr, BlockNum): any {
|
|
// defiend in transaction-validator.ts(CSmartContract)
|
|
}
|
|
AddDAppTransactions(BlockNum, Arr) {
|
|
// defiend in transaction-validator.ts(CSmartContract)
|
|
}
|
|
AddTicketToBlockQuote(Block, Tr) {
|
|
if (Block.PowTicketTree) {
|
|
var Res = this.IsValidTicket(Tr, Block.BlockNum);
|
|
if (Res >= 1) {
|
|
Res = this.AddToQuote(Block.PowTicketTree, Tr)
|
|
if (Res)
|
|
return Block.PowTicketTree.find(Tr);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
AddTrToBlockQuote(Block, Tr, bTTAdd?) {
|
|
if (Block.PowTxTree) {
|
|
var Res = this.IsValidTransaction(Tr, Block.BlockNum);
|
|
if (Res >= 1) {
|
|
if (bTTAdd) {
|
|
Res = this.AddToQuote(Block.PowTicketTree, Tr)
|
|
if (Res <= 0)
|
|
return Res;
|
|
}
|
|
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 = global.GetDeltaCurrentTime()
|
|
Context.StartTimeNum = (BlockNum - 1 + global.BLOCK_DELTA_ACTIVATE) * global.CONSENSUS_PERIOD_TIME + global.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 - global.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 - global.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 = "-" + global.GetHexFromAddres(Block.TreeHash).substr(0, 3) + "-"
|
|
}
|
|
else
|
|
if (Block && Block.TreeHash) {
|
|
hashStr = global.GetHexFromAddres(Block.TreeHash).substr(0, 5)
|
|
}
|
|
Str = Str + "|" + (hashStr + " ").substr(0, 5)
|
|
}
|
|
Str = Str.substr(1)
|
|
global.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) {
|
|
global.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
|
|
global.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 = global.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) {
|
|
global.AddInfoBlock(Block, "Prev Not End Exchange")
|
|
return;
|
|
}
|
|
global.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 = global.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) {
|
|
global.ToLog("#1 WatchdogSaved: no BlockNum=" + BlockNum)
|
|
return;
|
|
}
|
|
if (Block.bSave) {
|
|
var BlockDB = this.ReadBlockDB(BlockNum);
|
|
if (!BlockDB) {
|
|
Block.bSave = false
|
|
return;
|
|
}
|
|
if (global.CompareArr(BlockDB.Hash, Block.Hash) !== 0) {
|
|
global.AddInfoBlock(Block, "=ERR:WATCHDOG=")
|
|
global.ToLog("#3 WatchdogSaved: Error Hash on Num=" + BlockNum)
|
|
return;
|
|
}
|
|
if (global.CompareArr(BlockDB.SumHash, Block.SumHash) !== 0) {
|
|
global.AddInfoBlock(Block, "=ERR:WATCHDOG=")
|
|
global.ToLog("#4 WatchdogSaved: Error SumHash on Num=" + BlockNum)
|
|
return;
|
|
}
|
|
if (global.CompareArr(BlockDB.SeqHash, Block.SeqHash) !== 0) {
|
|
global.AddInfoBlock(Block, "=ERR:WATCHDOG=")
|
|
global.ToLog("#5 WatchdogSaved: Error SeqHash on Num=" + BlockNum)
|
|
return;
|
|
}
|
|
var PrevHash = this.GetPrevHash(Block);
|
|
if (!PrevHash) {
|
|
global.AddInfoBlock(Block, "=ERR:WATCHDOG=")
|
|
global.ToLog("#6 WatchdogSaved: Error PrevHash on Num=" + BlockNum)
|
|
return;
|
|
}
|
|
var SeqHash = this.GetSeqHash(Block.BlockNum, PrevHash, Block.TreeHash);
|
|
if (global.CompareArr(SeqHash, Block.SeqHash) !== 0) {
|
|
global.AddInfoBlock(Block, "=ERR:WATCHDOG=")
|
|
global.ToLog("#7 WatchdogSaved: Error SeqHash on Num=" + BlockNum)
|
|
return;
|
|
}
|
|
PrevHash = this.GetPrevHashDB(BlockDB)
|
|
SeqHash = this.GetSeqHash(BlockDB.BlockNum, PrevHash, BlockDB.TreeHash)
|
|
if (global.CompareArr(SeqHash, BlockDB.SeqHash) !== 0) {
|
|
global.AddInfoBlock(Block, "=ERR:WATCHDOG=")
|
|
global.ToLog("#8 WatchdogSaved: Error SeqHash on Num=" + BlockNum)
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
DoBlockChain() {
|
|
if (global.glStopNode)
|
|
return;
|
|
if (!global.CAN_START)
|
|
return;
|
|
this.StartConsensus()
|
|
var CURRENTBLOCKNUM = this.CurrentBlockNum;
|
|
if (global.GrayConnect()) {
|
|
if (!this.LoadHistoryMode)
|
|
this.StartSyncBlockchain(undefined, 1)
|
|
return;
|
|
}
|
|
if (this.LoadHistoryMode)
|
|
return;
|
|
var bWasSave = false;
|
|
var LoadBlockNum;
|
|
var LoadHash;
|
|
var start_save = CURRENTBLOCKNUM + global.TIME_START_SAVE;
|
|
for (var BlockNum = CURRENTBLOCKNUM - global.BLOCK_PROCESSING_LENGTH2; BlockNum > global.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) {
|
|
global.AddInfoBlock(Block, "CheckMaxSum")
|
|
this.CheckMaxSum(Block)
|
|
}
|
|
if (BlockNum <= CURRENTBLOCKNUM - global.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
|
|
global.AddInfoBlock(Block, "!PrevBlock")
|
|
continue;
|
|
}
|
|
if (BlockNum >= CURRENTBLOCKNUM + global.TIME_END_EXCHANGE) {
|
|
if (!Block.Active) {
|
|
global.AddInfoBlock(Block, "WAIT ACTIVATE")
|
|
continue;
|
|
}
|
|
else
|
|
if (!Block.EndExchange) {
|
|
global.AddInfoBlock(Block, "WAIT EXCHANGE")
|
|
continue;
|
|
}
|
|
}
|
|
if (BlockNum === CURRENTBLOCKNUM + global.TIME_START_POW || Block.EndExchange)
|
|
if (!Block.Prepared) {
|
|
if (!Block.EndExchange)
|
|
this.CreateTreeHash(Block)
|
|
global.AddInfoBlock(Block, "Start POW")
|
|
this.PreparePOWHash(Block)
|
|
if (!Block.Prepared)
|
|
global.AddInfoBlock(Block, "!!Prepared")
|
|
continue;
|
|
}
|
|
if (!Block.EndExchange) {
|
|
global.AddInfoBlock(Block, "Not EndExchange")
|
|
Block.HasErr = 1
|
|
Block.Prepared = 0
|
|
this.CreateTreeHash(Block)
|
|
}
|
|
if (!Block.Prepared) {
|
|
Block.HasErr = 1
|
|
global.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 (global.CompareArr(SeqHash, Block.SeqHash) !== 0) {
|
|
Block.HasErr = 1
|
|
global.AddInfoBlock(Block, "New fast pow")
|
|
this.PreparePOWHash(Block)
|
|
}
|
|
if (Block.MaxPOW && Block.MaxPOW.SeqHash && Block.MaxPOW.AddrHash && Block.MaxPOW.LocalSeqHash && global.CompareArr(Block.SeqHash,
|
|
Block.MaxPOW.LocalSeqHash) === 0) {
|
|
if (global.CompareArr(Block.SeqHash, Block.MaxPOW.LocalSeqHash) === 0 && global.CompareArr(Block.MaxPOW.PowLocalHash, Block.PowHash) < 0) {
|
|
Block.AddrHash = Block.MaxPOW.LocalAddrHash
|
|
global.CalcHashBlockFromSeqAddr(Block, Block.PrevHash, global.MINING_VERSION_NUM)
|
|
global.AddInfoBlock(Block, "->Local lider:" + global.GetPowPower(Block.PowHash))
|
|
}
|
|
if (global.CompareArr(Block.SeqHash, Block.MaxPOW.SeqHash) === 0 && global.CompareArr(Block.MaxPOW.AddrHash, Block.AddrHash) !== 0 && global.CompareArr(Block.MaxPOW.PowHash,
|
|
Block.PowHash) < 0) {
|
|
Block.AddrHash = Block.MaxPOW.AddrHash
|
|
global.CalcHashBlockFromSeqAddr(Block, Block.PrevHash, global.MINING_VERSION_NUM)
|
|
global.AddInfoBlock(Block, "->Max lider")
|
|
}
|
|
}
|
|
else {
|
|
Block.HasErr = 1
|
|
global.AddInfoBlock(Block, "ERROR MaxPOW")
|
|
}
|
|
if (Block.MaxPOW && Block.MaxPOW.SeqHash && !Block.CheckMaxPow && !Block.CheckMaxSum && global.CompareArr(Block.SeqHash, Block.MaxPOW.SeqHash) !== 0) {
|
|
global.AddInfoBlock(Block, "CheckMaxPow")
|
|
this.CheckingMaxPowOther(Block)
|
|
}
|
|
if (BlockNum > start_save)
|
|
continue;
|
|
if (PrevBlock.bSave && this.BlockNumDB + 1 >= Block.BlockNum) {
|
|
this.AddToStatBlockConfirmation(Block)
|
|
var Power = global.GetPowPower(Block.PowHash);
|
|
if (this.WriteBlockDB(Block)) {
|
|
if (Block.arrContent && Block.arrContent.length)
|
|
global.ADD_TO_STAT("MAX:TRANSACTION_COUNT", Block.arrContent.length)
|
|
global.AddInfoBlock(Block, "SAVE:" + Power + " TH:" + this.GetStrFromHashShort(Block.TreeHash).substr(0, 4))
|
|
}
|
|
else {
|
|
Block.HasErr = 1
|
|
global.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 = global.ReadUintFromArr(Block.AddrHash, 0);
|
|
var MultK = global.RESYNC_CONDITION.K_POW;
|
|
var MaxBlocks = global.RESYNC_CONDITION.OWN_BLOCKS;
|
|
if (Miner === global.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) {
|
|
global.ToLog("START RESYNC CONDITION")
|
|
this.OwnBlockCount = 0
|
|
this.StartSyncBlockchain()
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
this.OwnBlockCount = 0
|
|
}
|
|
}
|
|
} else {
|
|
Block.HasErr = 1
|
|
if (!PrevBlock.bSave)
|
|
global.AddInfoBlock(Block, "Prev block not saved")
|
|
else
|
|
global.AddInfoBlock(Block, "Low BlockNumDB")
|
|
}
|
|
}
|
|
}
|
|
var MaxNumBlockDB = this.GetMaxNumBlockDB();
|
|
if (CURRENTBLOCKNUM + global.BLOCK_PROCESSING_LENGTH2 > MaxNumBlockDB && CURRENTBLOCKNUM - global.BLOCK_PROCESSING_LENGTH2 < MaxNumBlockDB)
|
|
for (var BlockNum = CURRENTBLOCKNUM - global.BLOCK_PROCESSING_LENGTH2; BlockNum > global.BLOCK_PROCESSING_LENGTH2 && BlockNum < start_save; BlockNum++) {
|
|
var Block = this.GetBlock(BlockNum);
|
|
if (Block && !Block.bSave && Block.TrCount && Block.TreeHash && !global.IsZeroArr(Block.TreeHash) && !Block.WasSaveDataTree) {
|
|
this.PreSaveDataTreeToDB(Block)
|
|
Block.WasSaveDataTree = 1
|
|
global.AddInfoBlock(Block, "*PRESAVE DATA TREE*")
|
|
global.ToLog("PRESAVE DATA: " + Block.BlockNum, 2)
|
|
}
|
|
}
|
|
this.RelayMode = !bWasSave
|
|
this.FREE_MEM_BLOCKS(CURRENTBLOCKNUM - global.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 = global.GetPowPower(Block.PowHash);
|
|
SumPow += Power
|
|
Count++
|
|
}
|
|
}
|
|
if (!Count)
|
|
return 0;
|
|
else
|
|
return SumPow / Count;
|
|
}
|
|
CreatePOWNew(Block) {
|
|
CreateHashMinimal(Block, global.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) {
|
|
global.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 && global.CompareArr(BlockMining.SeqHash, msg.SeqHash) === 0) {
|
|
var ValueOld = global.GetHashFromSeqAddr(BlockMining.SeqHash, BlockMining.AddrHash, BlockMining.BlockNum);
|
|
var ValueMsg = global.GetHashFromSeqAddr(msg.SeqHash, msg.AddrHash, BlockMining.BlockNum);
|
|
var bWas = 0;
|
|
if (global.CompareArr(ValueOld.Hash1, ValueMsg.Hash1) > 0) {
|
|
var Nonce1 = global.ReadUintFromArr(msg.AddrHash, 12);
|
|
var DeltaNum1 = global.ReadUint16FromArr(msg.AddrHash, 24);
|
|
global.WriteUintToArrOnPos(BlockMining.AddrHash, Nonce1, 12)
|
|
global.WriteUint16ToArrOnPos(BlockMining.AddrHash, DeltaNum1, 24)
|
|
bWas += 1
|
|
}
|
|
if (global.CompareArr(ValueOld.Hash2, ValueMsg.Hash2) > 0) {
|
|
var Nonce0 = global.ReadUintFromArr(msg.AddrHash, 6);
|
|
var Nonce2 = global.ReadUintFromArr(msg.AddrHash, 18);
|
|
var DeltaNum2 = global.ReadUint16FromArr(msg.AddrHash, 26);
|
|
global.WriteUintToArrOnPos(BlockMining.AddrHash, Nonce0, 6)
|
|
global.WriteUintToArrOnPos(BlockMining.AddrHash, Nonce2, 18)
|
|
global.WriteUint16ToArrOnPos(BlockMining.AddrHash, DeltaNum2, 26)
|
|
bWas += 2
|
|
}
|
|
if (!bWas)
|
|
return;
|
|
var ValueNew = global.GetHashFromSeqAddr(BlockMining.SeqHash, BlockMining.AddrHash, BlockMining.BlockNum);
|
|
BlockMining.Hash = ValueNew.Hash
|
|
BlockMining.PowHash = ValueNew.PowHash
|
|
BlockMining.Power = global.GetPowPower(BlockMining.PowHash)
|
|
global.ADD_TO_STAT("MAX:POWER", BlockMining.Power)
|
|
var Power = global.GetPowPower(BlockMining.PowHash);
|
|
var HashCount = Math.pow(2, Power);
|
|
global.ADD_HASH_RATE(HashCount)
|
|
global.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, global.CompareItemHashSimple, "string");
|
|
var PrevTimeIdle = 0;
|
|
OnTimeIdle();
|
|
|
|
function OnTimeIdle() {
|
|
var CurTime = Date.now();
|
|
var Delta = CurTime - PrevTimeIdle;
|
|
if (Delta <= 51) {
|
|
global.ADD_TO_STAT("TIME_IDLE", 5);
|
|
}
|
|
setTimeout(OnTimeIdle, 49);
|
|
PrevTimeIdle = CurTime;
|
|
};
|