/*
 * @project: TERA
 * @version: Development (beta)
 * @license: MIT (not for evil)
 * @copyright: Yuriy Ivanov 2017-2019 [progr76@gmail.com]
 * Web: https://terafoundation.org
 * Twitter: https://twitter.com/terafoundation
 * Telegram: https://web.telegram.org/#/im?p=@terafoundation
*/

global.PROCESS_NAME = "WEB";
const crypto = require('crypto');
const http = require('http'), net = require('net'), url = require('url'), fs = require('fs'), querystring = require('querystring');
global.MAX_STAT_PERIOD = 60;
require("../core/constant");
global.MAX_STAT_PERIOD = 60;
global.DATA_PATH = GetNormalPathString(global.DATA_PATH);
global.CODE_PATH = GetNormalPathString(global.CODE_PATH);
require("../core/library");
require("../core/geo");
global.READ_ONLY_DB = 1;
global.MAX_STAT_PERIOD = 60;
var HostNodeList = [];
var AllNodeList = [];
var NodeBlockChain = [];
var LastAlive = Date.now();
setTimeout(function ()
{
    setInterval(CheckAlive, 1000);
}, 20000);
setInterval(function ()
{
    process.send({cmd:"Alive"});
}, 1000);
process.send({cmd:"online", message:"OK"});
process.on('message', function (msg)
{
    LastAlive = Date.now();
    switch(msg.cmd)
    {
        case "ALive":
            break;
        case "Exit":
            process.exit(0);
            break;
        case "call":
            var Err = 0;
            var Ret;
            try
            {
                Ret = global[msg.Name](msg.Params);
            }
            catch(e)
            {
                Err = 1;
                Ret = "" + e;
            }
            if(msg.id)
                process.send({cmd:"retcall", id:msg.id, Err:Err, Params:Ret});
            break;
        case "retcall":
            var F = GlobalRunMap[msg.id];
            if(F)
            {
                delete GlobalRunMap[msg.id];
                F(msg.Err, msg.Params);
            }
            break;
        case "Stat":
            ADD_TO_STAT(msg.Name, msg.Value);
            break;
        case "NodeList":
            HostNodeList = msg.Value;
            AllNodeList = msg.ValueAll;
            break;
        case "NodeBlockChain":
            NodeBlockChain = msg.Value;
            break;
        case "DappEvent":
            {
                AddDappEventToGlobalMap(msg.Data);
                break;
            }
        case "ToLogClient":
            {
                ToLogClient0(msg.Str, msg.StrKey, msg.bFinal);
                break;
            }
        case "RetFindTX":
            {
                if(msg.WebID)
                {
                    var F = global.GlobalRunMap[msg.WebID];
                    if(F)
                    {
                        delete global.GlobalRunMap[msg.WebID];
                        F(msg.Result, msg.ResultStr);
                        break;
                    }
                }
                ArrLogClient.push({text:msg.ResultStr, key:msg.TX, final:msg.bFinal, });
                break;
            }
    }
});

function CheckAlive()
{
    if(global.NOALIVE)
        return ;
    var Delta = Date.now() - LastAlive;
    if(Delta > CHECK_STOP_CHILD_PROCESS)
    {
        ToLog("HOSTING: ALIVE TIMEOUT Stop and exit: " + Delta + "/" + global.CHECK_STOP_CHILD_PROCESS, 0);
        process.exit(0);
        return ;
    }
};
process.on('uncaughtException', function (err)
{
    ToError(err.stack);
    ToLog(err.stack);
    TO_ERROR_LOG("HOSTING", 777, err);
    ToLog("-----------------HOSTING EXIT------------------", 0);
    process.exit();
});
process.on('error', function (err)
{
    ToError("HOSTING:\n" + err.stack);
    ToLog(err.stack);
});
if(!global.HTTP_HOSTING_PORT)
{
    ToLogTrace("global.HTTP_HOSTING_PORT=" + global.HTTP_HOSTING_PORT);
    process.exit();
}
var CServerDB = require("../core/db/block-db");
var KeyPair = crypto.createECDH('secp256k1');
KeyPair.setPrivateKey(Buffer.from([77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 77, 77, 77, 77, 77]));
global.SERVER = new CServerDB(KeyPair, undefined, undefined, false, true);
global.HTTP_PORT_NUMBER = 0;
require("../core/html-server");
require("../core/transaction-validator");
global.STAT_MODE = 1;
setInterval(PrepareStatEverySecond, 1000);
var HostingServer;
if(global.HTTPS_HOSTING_DOMAIN)
{
    var file_sert = GetDataPath("sertif.lst");
    CheckCreateDir(GetDataPath("tmp"));
    var greenlock = require('greenlock').create({version:'draft-12', server:'https://acme-v02.api.letsencrypt.org/directory', configDir:GetDataPath('tmp'),
    });
    var redir = require('redirect-https')();
    require('http').createServer(greenlock.middleware(redir)).listen(80);
    var GetNewSert = 1;
    if(fs.existsSync(file_sert))
    {
        var certs = LoadParams(file_sert, {});
        var Delta = certs.expiresAt - Date.now();
        if(Delta > 7 * 86000 * 1000)
        {
            ToLog("USE OLD SERT. ExpiresAt: " + new Date(certs.expiresAt));
            GetNewSert = 0;
            var tlsOptions = {key:certs.privkey, cert:certs.cert + '\r\n' + certs.chain};
            HostingServer = require('https').createServer(tlsOptions, MainHTTPFunction);
            RunListenServer();
        }
    }
    if(GetNewSert)
    {
        ToLog("Start get new SERT", 0);
        var opts = {domains:[global.HTTPS_HOSTING_DOMAIN], email:'progr76@gmail.com', agreeTos:true, communityMember:true, };
        greenlock.register(opts).then(function (certs)
        {
            SaveParams(file_sert, certs);
            var tlsOptions = {key:certs.privkey, cert:certs.cert + '\r\n' + certs.chain};
            HostingServer = require('https').createServer(tlsOptions, MainHTTPFunction);
            RunListenServer();
        }, function (err)
        {
            ToError(err);
        });
    }
}
else
{
    HostingServer = http.createServer(MainHTTPFunction);
    RunListenServer();
}

function MainHTTPFunction(request,response)
{
    if(!request.socket || !request.socket.remoteAddress)
        return ;
    SetSafeResponce(response);
    var DataURL = url.parse(request.url);
    var Params = querystring.parse(DataURL.query);
    var Path = querystring.unescape(DataURL.pathname);
    var Type = request.method;
    if(Type === "POST")
    {
        let Response = response;
        let postData = "";
        request.addListener("data", function (postDataChunk)
        {
            if(postData.length <= 12000 && postDataChunk.length <= 12000)
                postData += postDataChunk;
            else
            {
                var Str = "Error postDataChunk.length=" + postDataChunk.length;
                ToLog(Str, 0);
                Response.writeHead(405, {'Content-Type':'text/html'});
                Response.end(Str);
            }
        });
        request.addListener("end", function ()
        {
            var Data;
            if(postData && postData.length)
            {
                try
                {
                    Data = JSON.parse(postData);
                }
                catch(e)
                {
                    Response.writeHead(405, {'Content-Type':'text/html'});
                    Response.end("Error data parsing");
                }
            }
            DoCommandNew(response, Type, Path, Data);
        });
    }
    else
    {
        DoCommandNew(response, Type, Path, Params);
    }
};
var bWasRun = 0;

function RunListenServer()
{
    HostingServer.on('error', function (err)
    {
        if(err.code === 'EADDRINUSE')
        {
            ToLogClient('Port ' + global.HTTP_HOSTING_PORT + ' in use, retrying...');
            if(HostingServer.Server)
                HostingServer.Server.close();
            setTimeout(function ()
            {
                RunListenServer();
            }, 5000);
            return ;
        }
        ToError("H##6");
        ToError(err);
    });
    ToLogClient("Prepare to run WEB-server on port: " + global.HTTP_HOSTING_PORT);
    HostingServer.listen(global.HTTP_HOSTING_PORT, '0.0.0.0', function ()
    {
        if(!bWasRun)
            ToLogClient("Run WEB-server on port: " + global.HTTP_HOSTING_PORT);
        bWasRun = 1;
    });
};
var LangPathMap = {};
LangPathMap["ru"] = 1;
var WalletFileMap = {};
WalletFileMap["coinlib.js"] = 1;
WalletFileMap["client.js"] = 1;
WalletFileMap["diagram.js"] = 1;
WalletFileMap["sha3.js"] = 1;
WalletFileMap["terahashlib.js"] = 1;
WalletFileMap["wallet-web.js"] = 1;
WalletFileMap["wallet-lib.js"] = 1;
WalletFileMap["crypto-client.js"] = 1;
WalletFileMap["dapp-inner.js"] = 1;
WalletFileMap["marked.js"] = 1;
WalletFileMap["highlight.js"] = 1;
WalletFileMap["highlight-js.js"] = 1;
WalletFileMap["highlight-html.js"] = 1;
WalletFileMap["codes.css"] = 1;
WalletFileMap["sign-lib-min.js"] = 1;
WalletFileMap["buttons.css"] = 1;
WalletFileMap["style.css"] = 1;
WalletFileMap["wallet.css"] = 1;
WalletFileMap["history.html"] = 1;
WalletFileMap["blockviewer.html"] = 1;
WalletFileMap["web-wallet.html"] = 1;
global.WebApi2 = {};
global.HostingCaller = {};

function DoCommandNew(response,Type,Path,Params)
{
    if(Path.substring(0, 1) === "/")
        Path = Path.substring(1);
    var ArrPath = Path.split('/', 3);
    var Caller = HostingCaller;
    var Method = ArrPath[0];
    if(ArrPath[0] === "api")
    {
        if(ArrPath[1] === "v2")
        {
            if(!global.USE_HARD_API_V2)
            {
                response.writeHead(200, {'Content-Type':'text/plain', 'Access-Control-Allow-Origin':"*"});
                response.end(JSON.stringify({result:0, text:"You must set const USE_HARD_API_V2:1"}));
                return ;
            }
            Caller = WebApi2;
        }
        Method = ArrPath[2];
    }
    var F = Caller[Method];
    if(F)
    {
        response.writeHead(200, {'Content-Type':'text/plain', 'Access-Control-Allow-Origin':"*"});
        var Ret;
        try
        {
            Ret = F(Params, response);
        }
        catch(e)
        {
            Ret = {result:0, text:e.message, text2:e.stack};
        }
        if(Ret === null)
            return ;
        try
        {
            var Str;
            if(typeof Ret === "object")
                Str = JSON.stringify(Ret);
            else
                Str = Ret;
            response.end(Str);
        }
        catch(e)
        {
            ToLog("ERR PATH:" + Path);
            ToLog(e);
            response.end();
        }
        return ;
    }
    Method = Method.toLowerCase();
    if(Method === "dapp" && ArrPath.length === 2)
        Method = "DappTemplateFile";
    switch(Method)
    {
        case "":
            SendWebFile(response, "./SITE/index.html", undefined, true);
            break;
        case "file":
            SendBlockFile(response, ArrPath[1], ArrPath[2]);
            break;
        case "DappTemplateFile":
            DappTemplateFile(response, ArrPath[1]);
            break;
        case "smart":
            DappSmartCodeFile(response, ArrPath[1]);
            break;
        default:
            {
                var Name = ArrPath[ArrPath.length - 1];
                if(typeof Name !== "string")
                    Name = "ErrorPath";
                else
                    if(Name.indexOf("..") >= 0 || Name.indexOf("\\") >= 0 || Name.indexOf("/") >= 0)
                        Name = "ErrorFilePath";
                if(Name.indexOf(".") < 0)
                    Name += ".html";
                var PrefixPath;
                if(Method === "files")
                {
                    PrefixPath = "../FILES";
                    Name = PrefixPath + "/" + Name;
                    SendWebFile(response, Name, Path);
                    return ;
                }
                else
                    if(LangPathMap[Method])
                    {
                        PrefixPath = "./SITE/" + Method;
                    }
                    else
                        if(WalletFileMap[Name])
                            PrefixPath = "./HTML";
                        else
                            PrefixPath = "./SITE";
                var type = Path.substr(Path.length - 3, 3);
                switch(type)
                {
                    case ".js":
                        Name = PrefixPath + "/JS/" + Name;
                        break;
                    case "css":
                        Name = PrefixPath + "/CSS/" + Name;
                        break;
                    case "wav":
                    case "mp3":
                        Name = PrefixPath + "/SOUND/" + Name;
                        break;
                    case "svg":
                    case "png":
                    case "gif":
                    case "jpg":
                    case "ico":
                        Name = PrefixPath + "/PIC/" + Name;
                        break;
                    case "pdf":
                    case "zip":
                    case "exe":
                    case "msi":
                        Name = PrefixPath + "/FILES/" + Name;
                        break;
                    default:
                        Name = PrefixPath + "/" + Name;
                        break;
                }
                SendWebFile(response, Name, Path);
                break;
            }
    }
};
HostingCaller.GetCurrentInfo = function (Params)
{
    var Ret = {result:1, VersionNum:global.UPDATE_CODE_VERSION_NUM, MaxNumBlockDB:SERVER.GetMaxNumBlockDB(), CurBlockNum:GetCurrentBlockNumByTime(),
        MaxAccID:DApps.Accounts.GetMaxAccount(), MaxDappsID:DApps.Smart.GetMaxNum(), NETWORK:global.NETWORK, CurTime:Date.now(), DELTA_CURRENT_TIME:DELTA_CURRENT_TIME,
        MIN_POWER_POW_TR:MIN_POWER_POW_TR, FIRST_TIME_BLOCK:FIRST_TIME_BLOCK, CONSENSUS_PERIOD_TIME:CONSENSUS_PERIOD_TIME, };
    if(typeof Params === "object" && Params.Diagram == 1)
    {
        var arrNames = ["MAX:ALL_NODES", "MAX:HASH_RATE_G"];
        Ret.arr = GET_STATDIAGRAMS(arrNames);
    }
    if(typeof Params === "object" && Params.BlockChain == 1)
    {
        Ret.BlockChain = NodeBlockChain;
    }
    if(typeof Params === "object" && Params.ArrLog == 1)
    {
        var ArrLog = [];
        for(var i = 0; i < ArrLogClient.length; i++)
        {
            var Item = ArrLogClient[i];
            if(!Item.final)
                continue;
            ArrLog.push(Item);
        }
        Ret.ArrLog = ArrLog;
    }
    return Ret;
};
var MaxCountViewRows = global.HTTP_MAX_COUNT_ROWS;
HostingCaller.GetAccountList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = DApps.Accounts.GetRowsAccounts(ParseNum(Params.StartNum), ParseNum(Params.CountNum));
    return {result:1, arr:arr};
};
HostingCaller.GetAccount = function (id)
{
    id = ParseNum(id);
    var arr = DApps.Accounts.GetRowsAccounts(id, 1);
    return {Item:arr[0], result:1};
};
HostingCaller.GetBlockList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = SERVER.GetRows(ParseNum(Params.StartNum), ParseNum(Params.CountNum));
    return {result:1, arr:arr};
};
HostingCaller.GetTransactionList = function (Params)
{
    return HostingCaller.GetTransactionAll(Params);
};
HostingCaller.GetTransactionAll = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    if(Params.Param3)
        Params.BlockNum = Params.Param3;
    var arr = SERVER.GetTrRows(ParseNum(Params.BlockNum), ParseNum(Params.StartNum), ParseNum(Params.CountNum));
    return {result:1, arr:arr};
};
HostingCaller.GetDappList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = DApps.Smart.GetRows(ParseNum(Params.StartNum), ParseNum(Params.CountNum));
    return {result:1, arr:arr};
};
HostingCaller.GetNodeList = function (Params)
{
    var arr = [];
    var List;
    if(typeof Params === "object" && Params.All)
        List = AllNodeList;
    else
        List = HostNodeList;
    var MaxNodes = 20;
    var len = List.length;
    var UseRandom = 0;
    if(len > MaxNodes)
    {
        UseRandom = 1;
        len = MaxNodes;
    }
    var mapWasAdd = {};
    for(var i = 0; i < len; i++)
    {
        var Item;
        if(UseRandom)
        {
            var num = random(List.length);
            Item = List[num];
            if(mapWasAdd[Item.ip])
            {
                continue;
            }
            mapWasAdd[Item.ip] = 1;
        }
        else
        {
            Item = List[i];
        }
        var Value = {ip:Item.ip, port:Item.portweb, };
        if(typeof Params === "object" && Params.Geo)
        {
            if(!Item.Geo)
                SetGeoLocation(Item);
            Value.latitude = Item.latitude;
            Value.longitude = Item.longitude;
            Value.name = Item.name;
        }
        arr.push(Value);
    }
    return {result:1, arr:arr};
};
var AccountKeyMap = {};
var LastMaxNum = 0;
HostingCaller.GetAccountListByKey = function (Params,ppp,bRet)
{
    if(typeof Params !== "object" || !Params.Key)
        return {result:0, arr:[]};
    var Accounts = DApps.Accounts;
    for(var num = LastMaxNum; true; num++)
    {
        if(Accounts.IsHole(num))
            continue;
        var Data = Accounts.ReadState(num);
        if(!Data)
            break;
        var StrKey = GetHexFromArr(Data.PubKey);
        Data.Next = AccountKeyMap[StrKey];
        AccountKeyMap[StrKey] = Data;
    }
    LastMaxNum = num;
    var arr = [];
    var Item = AccountKeyMap[Params.Key];
    while(Item)
    {
        var Data = Accounts.ReadState(Item.Num);
        if(!Data)
            continue;
        if(!Data.PubKeyStr)
            Data.PubKeyStr = GetHexFromArr(Data.PubKey);
        if(Data.Currency)
            Data.CurrencyObj = DApps.Smart.ReadSimple(Data.Currency);
        if(Data.Value.Smart)
        {
            Data.SmartObj = DApps.Smart.ReadSimple(Data.Value.Smart);
            try
            {
                Data.SmartState = BufLib.GetObjectFromBuffer(Data.Value.Data, Data.SmartObj.StateFormat, {});
                if(typeof Data.SmartState === "object")
                    Data.SmartState.Num = Item.Num;
            }
            catch(e)
            {
                Data.SmartState = {};
            }
        }
        arr.unshift(Data);
        Item = Item.Next;
        if(arr.length >= 30)
            break;
    }
    var Ret = {result:1, arr:arr};
    if(bRet)
        return Ret;
    var Context = GetUserContext(Params);
    var StrInfo = JSON.stringify(Ret);
    if(!Params.AllData && Context.PrevAccountList === StrInfo)
    {
        return {result:0, cache:1};
    }
    Context.PrevAccountList = StrInfo;
    Context.NumAccountList++;
    return StrInfo;
};
HostingCaller.SendTransactionHex = function (Params,response)
{
    if(typeof Params !== "object" || !Params.Hex)
        return {result:0, text:"object requre"};
    process.RunRPC("AddTransactionFromWeb", {HexValue:Params.Hex}, function (Err,text)
    {
        var Result = {result:!Err, text:text};
        var Str = JSON.stringify(Result);
        response.end(Str);
    });
    return null;
};
HostingCaller.DappSmartHTMLFile = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    return HTTPCaller.DappSmartHTMLFile(Params);
};
HostingCaller.DappBlockFile = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    return HTTPCaller.DappBlockFile(Params);
};
HostingCaller.DappInfo = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    var SmartNum = ParseNum(Params.Smart);
    process.send({cmd:"SetSmartEvent", Smart:SmartNum});
    var Context = GetUserContext(Params);
    var Ret = HTTPCaller.DappInfo(Params, undefined, 1);
    Ret.PubKey = undefined;
    var StrInfo = JSON.stringify(Ret);
    if(!Params.AllData && Context.PrevDappInfo === StrInfo)
    {
        return {result:0, cache:1};
    }
    Context.PrevDappInfo = StrInfo;
    Context.NumDappInfo++;
    Context.LastTime = Date.now();
    Ret.NumDappInfo = Context.NumDappInfo;
    Ret.CurTime = Date.now();
    Ret.CurBlockNum = GetCurrentBlockNumByTime();
    Ret.BlockNumDB = SERVER.BlockNumDB;
    Ret.MaxAccID = DApps.Accounts.GetMaxAccount();
    Ret.MaxDappsID = DApps.Smart.GetMaxNum();
    return Ret;
};
HostingCaller.DappWalletList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    var Ret = HostingCaller.GetAccountListByKey(Params, undefined, 1);
    var Smart = ParseNum(Params.Smart);
    var arr = [];
    for(var i = 0; i < Ret.arr.length; i++)
    {
        if(Ret.arr[i].Value.Smart === Smart || Params.AllAccounts)
        {
            arr.push(Ret.arr[i]);
        }
    }
    Ret.arr = arr;
    return Ret;
};
HTTPCaller.DappWalletList = HostingCaller.DappWalletList;
HostingCaller.DappAccountList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = DApps.Accounts.GetRowsAccounts(ParseNum(Params.StartNum), ParseNum(Params.CountNum), undefined, 1);
    return {arr:arr, result:1};
};
HostingCaller.DappSmartList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = DApps.Smart.GetRows(ParseNum(Params.StartNum), ParseNum(Params.CountNum), undefined, undefined, Params.GetAllData,
    Params.TokenGenerate);
    return {arr:arr, result:1};
};
HostingCaller.DappBlockList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = SERVER.GetRows(ParseNum(Params.StartNum), ParseNum(Params.CountNum));
    return {arr:arr, result:1};
};
HostingCaller.DappTransactionList = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    if(Params.CountNum > MaxCountViewRows)
        Params.CountNum = MaxCountViewRows;
    if(!Params.CountNum)
        Params.CountNum = 1;
    var arr = SERVER.GetTrRows(ParseNum(Params.BlockNum), ParseNum(Params.StartNum), ParseNum(Params.CountNum));
    return {arr:arr, result:1};
};
HostingCaller.DappStaticCall = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    return HTTPCaller.DappStaticCall(Params);
};
HostingCaller.GetHistoryTransactions = function (Params)
{
    if(typeof Params !== "object")
        return {result:0};
    return HTTPCaller.GetHistoryTransactions(Params);
};
global.GlobalRunID = 0;
global.GlobalRunMap = {};
process.RunRPC = function (Name,Params,F)
{
    if(F)
    {
        GlobalRunID++;
        try
        {
            process.send({cmd:"call", id:GlobalRunID, Name:Name, Params:Params});
            GlobalRunMap[GlobalRunID] = F;
        }
        catch(e)
        {
        }
    }
    else
    {
        process.send({cmd:"call", id:0, Name:Name, Params:Params});
    }
};
setInterval(function ()
{
    DApps.Accounts.Close();
    DApps.Smart.DBSmart.Close();
    global.BlockDB.CloseDBFile("block-header");
    global.BlockDB.CloseDBFile("block-body");
}, 500);
setInterval(function ()
{
    var MaxNumBlockDB = SERVER.GetMaxNumBlockDB();
    var arr = SERVER.GetStatBlockchain("POWER_BLOCKCHAIN", 100);
    if(arr.length)
    {
        var SumPow = 0;
        var Count = 0;
        for(var i = arr.length - 100; i < arr.length; i++)
            if(arr[i])
            {
                SumPow += arr[i];
                Count++;
            }
        var AvgPow = SumPow / Count;
        var HashRate = Math.pow(2, AvgPow) / 1024 / 1024 / 1024;
        ADD_TO_STAT("MAX:HASH_RATE_G", HashRate);
    }
    var Count = COUNT_BLOCK_PROOF + 16 - 1;
    if(MaxNumBlockDB > Count)
    {
        var StartNum = MaxNumBlockDB - Count;
        var BufWrite = SERVER.BlockChainToBuf(StartNum, StartNum, MaxNumBlockDB);
        NodeBlockChain = BufWrite;
    }
}, 1000);
require("./api-exchange.js");
try
{
    require("../SITE/JS/web-addon.js");
}
catch(e)
{
}
