tera/src/core/transfer-msg.ts

227 lines
8.0 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 { RBTree } from './library';
import * as crypto from 'crypto'
const MAX_MESSAGE_COUNT = 1000;
import CSmartContract from './transaction-validator'
import { TeraTr, SocketSendInfo } from '../interfaces/server';
import CNode from './node';
export default class CMessages extends CSmartContract {
MemPoolMsg: RBTree<any>[]
constructor(SetKeyPair: crypto.ECDH, RunIP: string, RunPort: number, UseRNDHeader: boolean, bVirtual: boolean) {
super(SetKeyPair, RunIP, RunPort, UseRNDHeader, bVirtual)
this.MemPoolMsg = []
for (var i = 0; i <= global.MAX_LEVEL_SPECIALIZATION; i++)
this.MemPoolMsg[i] = new RBTree(global.CompareItemTimePow)
}
AddMsgToQuote(Msg: TeraTr) {
var Tree = this.MemPoolMsg[Msg.Level];
if (Tree) {
if (Tree.insert(Msg)) {
if (Tree.size > global.MEM_POOL_MSG_COUNT) {
var maxitem = Tree.max();
Tree.remove(maxitem)
if (maxitem === Msg)
return 0;
}
return 1;
}
else {
return 3;
}
}
return 0;
}
IsValidMsg(Msg: TeraTr) {
this.CheckCreateMsgHASH(Msg)
if (Msg.power < global.MIN_POWER_POW_MSG)
return - 1;
if (Msg.time > this.CurrentBlockNum)
return - 1;
return 1;
}
CheckCreateMsgHASH(Msg: TeraTr) {
if (!Msg.HashPow) {
Msg.HASH = global.sha3(Msg.body)
Msg.HashPow = global.GetHashWithValues(Msg.HASH, Msg.nonce, Msg.time)
Msg.power = global.GetPowPower(Msg.HashPow)
Msg.TimePow = Msg.time + Msg.power - Math.log2(Msg.body.length / 128)
Msg.Level = global.AddrLevelArr(this.addrArr, Msg.addrArr)
if (Msg.Level >= global.MAX_LEVEL_SPECIALIZATION)
Msg.Level = global.MAX_LEVEL_SPECIALIZATION
}
}
CreateMsgFromBody(Body: any, ToAddr: any) {
var HASH = global.sha3(Body);
var Msg = {
HASH: HASH,
body: Body,
addrArr: ToAddr,
nonce: global.CreateNoncePOWExtern(HASH, this.CurrentBlockNum, 3 * (1 << global.MIN_POWER_POW_MSG)),
time: this.CurrentBlockNum,
};
this.CheckCreateMsgHASH(Msg)
return Msg;
}
SendMessage(Body: any, ToAddr: any) {
var Msg = this.CreateMsgFromBody(Body, ToAddr);
this.SendMessageNext(Msg)
}
SendMessageNext(Msg) {
var CountNodes = 3;
var LevelStart = Msg.Level;
if (global.CompareArr(this.addrArr, Msg.addrArr) === 0)
return false;
for (var L = LevelStart; L >= 0; L--)
if (this.LevelNodes[L] && this.LevelNodes[L].length) {
var arr = this.LevelNodes[L];
for (var j = 0; arr && j < arr.length; j++) {
var Node = arr[j];
this.SendF(Node, { "Method": "MESSAGE", "Data": { Arr: [Msg] } })
CountNodes--
if (CountNodes <= 0)
break;
}
}
return true;
}
static MESSAGE_F() {
return "{Arr:[{addrArr:hash,body:tr,nonce:uint,time:uint}]}";
}
MESSAGE(Info: SocketSendInfo, CurTime: number) {
var Data = this.DataFromF(Info);
var arr = Data.Arr;
for (var i = 0; i < arr.length; i++) {
var Msg = arr[i];
if (this.IsValidMsg(Msg)) {
if (global.CompareArr(this.addrArr, Msg.addrArr) === 0) {
var App = global.DAppByType[Msg.body[0]];
if (App) {
//@ts-ignore
App.OnMessage(Msg, BlockNum, i)
}
}
else {
if (this.AddMsgToQuote(Msg) === 1) {
this.SendMessageNext(Msg)
}
}
}
}
}
SendGetMessage(Node: CNode) {
var Context = { "SendGetMessage": 1 };
this.Send(Node, { "Method": "GETMESSAGE", "Context": Context, "Data": undefined })
}
GETMESSAGE(Info: SocketSendInfo, CurTime: number) {
var arr = [];
var BufLength = 300;
var Level = global.AddrLevelArr(this.addrArr, Info.Node.addrArr);
var Tree = this.MemPoolMsg[Level];
if (Tree) {
var it = Tree.iterator(), Item;
while ((Item = it.next()) !== null) {
if (arr.length >= MAX_MESSAGE_COUNT)
break;
arr.push(Item)
BufLength += Item.body.length + 50
}
}
this.SendF(Info.Node, { "Method": "MESSAGE", "Context": Info.Context, "Data": { Arr: arr } }, BufLength)
}
AddTransaction(Tr: TeraTr, ToAll?: boolean) {
Tr.ToAll = ToAll
var Res = this.IsValidTransaction(Tr, this.CurrentBlockNum);
if (Res <= 0 && Res !== - 3)
return Res;
if (Tr.num < this.CurrentBlockNum)
return - 3;
var delta = Tr.num - this.CurrentBlockNum;
if (delta > 3) {
if (delta < 15) {
let TR = Tr;
let SELF = this;
setTimeout(function() {
var Res = SELF.AddTransaction(TR, TR.ToAll);
if (TR.ToAll)
global.ToLogClient("#3 Added " + TrName(TR) + " for block: " + TR.num + " on timer Res=" + Res)
}, (delta - 3) * 1000)
if (Tr.ToAll)
global.ToLogClient("#2 Added " + TrName(Tr) + " for block: " + Tr.num + " to timer. Send transaction after " + (delta - 3) + " sec")
return 4;
}
return - 3;
}
var Block = this.GetBlockContext(Tr.num);
if (!Block)
return - 5;
if (Block.Active) {
Res = - 3
} else {
Res = this.AddTrToBlockQuote(Block, Tr)
if (Tr.ToAll)
this.SendTransaction(Tr)
}
ToLogContext("#1 Add " + TrName(Tr) + " for Block: " + Tr.num + " Res=" + Res)
return Res;
}
SendTransaction(Tr: TeraTr) {
if (!Tr.ToAll)
return;
var CurTime = global.GetCurrentTime(0) - 0;
var Count;
if (global.GrayConnect())
Count = Math.trunc(global.MAX_GRAY_CONNECTIONS_TO_SERVER / 2)
else
Count = Math.min(this.ActualNodes.size, 16)
if (Count < 2)
Count = 2
var ArrNodes = this.GetActualNodes();
for (var i = 0; i < ArrNodes.length; i++) {
var Node = ArrNodes[i];
if (!Node)
continue;
if (Node.TaskLastSend) {
var Delta = CurTime - Node.TaskLastSend;
if (Delta < global.PERIOD_GET_BLOCK || Node.StopGetBlock) {
continue;
}
}
Node.TaskLastSend = CurTime
this.SendF(Node, { "Method": "TRANSACTION", "Data": Tr }, Tr.body.length + 1000)
ToLogContext("Send " + TrName(Tr) + " to " + global.NodeName(Node))
Count--
if (Count <= 0)
break;
}
}
static TRANSACTION_F() {
return "{body:tr}";
}
TRANSACTION(Info: SocketSendInfo, CurTime: number) {
var Tr = this.DataFromF(Info);
ToLogContext("Receive " + TrName(Tr) + " from " + global.NodeName(Info.Node))
this.AddTransaction(Tr, false)
}
};
function ToLogContext(Str: string) {
};
function TrName(Tr: TeraTr) {
if (!Tr.HASH)
global.SERVER.CheckCreateTransactionObject(Tr);
var Str = global.GetHexFromArr(Tr.HASH);
return "Tx:" + Str.substr(0, 8);
};
global.TrName = TrName;