tera/Source/dapp/accounts.js

1297 lines
42 KiB
JavaScript
Raw Normal View History

2019-02-10 19:53:54 +00:00
/*
* @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
*/
"use strict";
const fs = require('fs');
const DBRow = require("../core/db/db-row");
const MAX_SUM_TER = 1e9;
const MAX_SUM_CENT = 1e9;
const TYPE_TRANSACTION_CREATE = 100;
const TYPE_TRANSACTION_TRANSFER = 105;
const TYPE_TRANSACTION_TRANSFER2 = 110;
const TYPE_TRANSACTION_TRANSFER3 = 111;
global.TYPE_TRANSACTION_ACC_HASH = 119;
global.FORMAT_CREATE = "{\
Type:byte,\
Currency:uint,\
PubKey:arr33,\
Name:str40,\
Adviser:uint,\
Smart:uint32,\
Reserve:arr3,\
}";
global.FORMAT_MONEY_TRANSFER = '{\
Type:byte,\
Currency:uint,\
FromID:uint,\
To:[{ID:uint,SumCOIN:uint,SumCENT:uint32}],\
Description:str,\
OperationID:uint,\
Sign:arr64,\
}';
const WorkStructTransfer = {};
global.FORMAT_MONEY_TRANSFER_BODY = FORMAT_MONEY_TRANSFER.replace("Sign:arr64,", "");
global.FORMAT_MONEY_TRANSFER2 = "{\
Type:byte,\
Version:byte,\
Currency:uint,\
FromID:uint,\
To:[{ID:uint,SumCOIN:uint,SumCENT:uint32}],\
Description:str,\
OperationID:uint,\
Sign:arr64,\
}";
const WorkStructTransfer2 = {};
global.FORMAT_MONEY_TRANSFER_BODY2 = FORMAT_MONEY_TRANSFER2.replace("Sign:arr64,", "");
global.FORMAT_MONEY_TRANSFER3 = "{\
Type:byte,\
Version:byte,\
Reserve:uint,\
FromID:uint,\
To:[{PubKey:tr,ID:uint,SumCOIN:uint,SumCENT:uint32}],\
Description:str,\
OperationID:uint,\
Body:tr,\
Sign:arr64,\
}";
const WorkStructTransfer3 = {};
global.FORMAT_MONEY_TRANSFER_BODY3 = FORMAT_MONEY_TRANSFER3.replace("Sign:arr64,", "");
global.FORMAT_ACCOUNT_HASH = "{\
Type:byte,\
BlockNum:uint,\
Hash:buffer32,\
}";
class MerkleDBRow extends DBRow
{
constructor(FileName, DataSize, Format, bReadOnly)
{
super(FileName, DataSize, Format, bReadOnly)
this.MerkleTree
this.MerkleArr = []
this.MerkleCalc = {}
}
CalcMerkleTree(bForceUpdate)
{
if(!this.MerkleTree || bForceUpdate)
{
this.MerkleCalc = {}
this.MerkleTree = {LevelsHash:[this.MerkleArr], RecalcCount:0}
for(var num = 0; num <= this.GetMaxNum(); num++)
{
var Buf = this.Read(num, 1);
if(!Buf)
{
if(global.WATCHDOG_DEV)
ToError("CalcMerkleTree: Break account reading on num: " + num)
break;
}
this.MerkleArr[num] = shaarr(Buf)
this.MerkleCalc[num] = 1
}
}
this.MerkleTree.RecalcCount = 0
UpdateMerklTree(this.MerkleTree, this.MerkleCalc, 0)
this.MerkleCalc = {}
return this.MerkleTree.Root;
}
Write(Data)
{
var RetBuf = {};
var bRes = DBRow.prototype.Write.call(this, Data, RetBuf);
if(bRes)
{
var Hash = shaarr(RetBuf.Buf);
this.MerkleArr[Data.Num] = Hash
this.MerkleCalc[Data.Num] = 1
}
return bRes;
}
Truncate(LastNum)
{
DBRow.prototype.Truncate.call(this, LastNum)
if(this.MerkleArr.length !== LastNum + 1)
{
this.MerkleArr.length = LastNum + 1
this.MerkleCalc[LastNum] = 1
}
}
};
class AccountApp extends require("./dapp")
{
constructor()
{
super()
var bReadOnly = (global.PROCESS_NAME !== "TX");
this.CreateTrCount = 0
this.FORMAT_ACCOUNT_ROW = "{\
Currency:uint,\
PubKey:arr33,\
Name:str40,\
Value:{SumCOIN:uint,SumCENT:uint32, OperationID:uint,Smart:uint32,Data:arr80},\
BlockNumCreate:uint,\
Adviser:uint,\
Reserve:arr9,\
}"
this.ACCOUNT_ROW_SIZE = 6 + 33 + 40 + (6 + 4 + 6 + 84) + 6 + 6 + 9
this.DBState = new MerkleDBRow("accounts-state", this.ACCOUNT_ROW_SIZE, this.FORMAT_ACCOUNT_ROW, bReadOnly)
this.DBAct = new DBRow("accounts-act", 6 + 6 + (6 + 4 + 6 + 6 + 84) + 1 + 11, "{ID:uint, BlockNum:uint,PrevValue:{SumCOIN:uint,SumCENT:uint32, Reserve0:uint, OperationID:uint,Smart:uint32,Data:arr80}, Mode:byte, TrNum:uint16, Reserve: arr9}",
bReadOnly)
this.DBActPrev = new DBRow("accounts-act-prev", this.DBAct.DataSize, this.DBAct.Format, bReadOnly)
if(global.READ_ONLY_DB)
return ;
this.DBAccountsHash = new DBRow("accounts-hash2", 6 + 32 + 32 + 10, "{BlockNum:uint, Hash:hash, SumHash:hash, Reserve: arr10}",
bReadOnly)
this.DBStateTX = new DBRow("accounts-tx", 6 + 94, "{BlockNum:uint, Reserve: arr94}", bReadOnly)
if(global.START_SERVER)
return ;
if(!bReadOnly)
this.Start()
setInterval(this.ControlActSize.bind(this), 60 * 1000)
}
Start(bClean)
{
if(global.LOCAL_RUN)
bClean = 1
if(!bClean && this.DBState.GetMaxNum() + 1 >= BLOCK_PROCESSING_LENGTH2)
return ;
this.DBState.MerkleTree = undefined
this.DBState.Truncate( - 1)
this.DBAct.Truncate( - 1)
this.DBActPrev.Truncate( - 1)
this.DBAccountsHash.Truncate( - 1)
this.DBStateTX.Truncate( - 1)
this.DBState.Write({Num:0, PubKey:[], Value:{BlockNum:1, SumCOIN:0.95 * TOTAL_TER_MONEY}, Name:"System account"})
for(var i = 1; i < 8; i++)
this.DBState.Write({Num:i, PubKey:[], Value:{BlockNum:1}, Name:""})
this.DBState.Write({Num:8, PubKey:GetArrFromHex(ARR_PUB_KEY[0]), Value:{BlockNum:1, SumCOIN:0.05 * TOTAL_TER_MONEY}, Name:"Founder account"})
this.DBState.Write({Num:9, PubKey:GetArrFromHex(ARR_PUB_KEY[1]), Value:{BlockNum:1, SumCOIN:0}, Name:"Developer account"})
for(var i = 10; i < BLOCK_PROCESSING_LENGTH2; i++)
this.DBState.Write({Num:i, PubKey:GetArrFromHex(ARR_PUB_KEY[i - 8]), Value:{BlockNum:1}, Name:""})
this.DBStateTX.Write({Num:0, BlockNum:0})
this.CalcMerkleTree(1)
ToLog("MAX_NUM:" + this.DBState.GetMaxNum())
}
Close()
{
this.DBState.Close()
this.DBActPrev.Close()
this.DBAct.Close()
if(this.DBAccountsHash)
this.DBAccountsHash.Close()
if(this.DBStateTX)
this.DBStateTX.Close()
}
ClearDataBase()
{
this.Start(1)
}
ControlActSize()
{
var MaxNum = this.DBAct.GetMaxNum();
if(MaxNum >= TRANSACTION_PROOF_COUNT * 2)
{
ToLog("Rename act files")
this.DBActPrev.CloseDBFile(this.DBActPrev.FileName)
this.DBAct.CloseDBFile(this.DBAct.FileName)
if(fs.existsSync(this.DBActPrev.FileNameFull))
{
var FileNameFull2 = this.DBActPrev.FileNameFull + "_del";
try
{
fs.renameSync(this.DBActPrev.FileNameFull, FileNameFull2)
}
catch(e)
{
ToLog("Can-t rename for delete act-file: " + FileNameFull2)
return ;
}
fs.unlinkSync(FileNameFull2)
}
try
{
fs.renameSync(this.DBAct.FileNameFull, this.DBActPrev.FileNameFull)
}
catch(e)
{
ToLog("Can-t rename act-file!")
return ;
}
}
}
OnDeleteBlock(Block)
{
if(Block.BlockNum < 1)
return ;
this.DeleteAct(Block.BlockNum)
}
OnWriteBlockStart(Block)
{
this.CreateTrCount = 0
if(Block.BlockNum < 1)
return ;
this.OnDeleteBlock(Block)
this.BeginBlock()
}
OnWriteBlockFinish(Block)
{
try
{
this.BeginTransaction()
this.DoCoinBaseTR(Block)
this.CommitTransaction(Block.BlockNum, 0xFFFF)
}
catch(e)
{
this.RollBackTransaction()
ToError("DoCoinBaseTR: " + e)
}
this.CommitBlock(Block)
}
OnWriteTransaction(Block, Body, BlockNum, TrNum, ContextFrom)
{
var Result;
try
{
Result = this.OnWriteTransactionTR(Block, Body, BlockNum, TrNum, ContextFrom)
}
catch(e)
{
Result = "" + e
}
if(Result !== true)
{
this.RollBackTransaction()
}
return Result;
}
OnWriteTransactionTR(Block, Body, BlockNum, TrNum, ContextFrom)
{
var Type = Body[0];
var Result;
switch(Type)
{
case TYPE_TRANSACTION_CREATE:
{
Result = this.TRCreateAccount(Body, BlockNum, TrNum, ContextFrom)
break;
}
case TYPE_TRANSACTION_TRANSFER:
{
Result = this.TRTransferMoney(Block, Body, BlockNum, TrNum, FORMAT_MONEY_TRANSFER, WorkStructTransfer)
break;
}
case TYPE_TRANSACTION_TRANSFER2:
{
Result = this.TRTransferMoney(Block, Body, BlockNum, TrNum, FORMAT_MONEY_TRANSFER2, WorkStructTransfer2)
break;
}
case TYPE_TRANSACTION_TRANSFER3:
{
Result = this.TRTransferMoney(Block, Body, BlockNum, TrNum, FORMAT_MONEY_TRANSFER3, WorkStructTransfer3)
break;
}
case TYPE_TRANSACTION_ACC_HASH:
{
if(global.LOCAL_RUN || global.TEST_NETWORK);
else
if(BlockNum < START_BLOCK_ACCOUNT_HASH + 200000)
break;
var BlockNumHash = BlockNum - DELTA_BLOCK_ACCOUNT_HASH;
if(!this.TRCheckAccountHash(Body, BlockNum, TrNum))
{
Result = "BAD ACCOUNT HASH"
ToLog("2. ****FIND BAD ACCOUNT HASH IN BLOCK: " + BlockNumHash + " DO BLOCK=" + BlockNum)
}
else
{
Result = true
this.NexdDeltaAccountNum = DELTA_BLOCK_ACCOUNT_HASH
SERVER.LastNumAccountHashOK = BlockNumHash
}
break;
}
}
return Result;
}
DoCoinBaseTR(Block)
{
if(Block.BlockNum < global.START_MINING)
return ;
if(!Block.PowHash)
throw "#121 ERROR NO Block.PowHash";
var SysData = this.ReadStateTR(0);
var SysBalance = SysData.Value.SumCOIN;
const REF_PERIOD_START = global.START_MINING;
const REF_PERIOD_END = 30 * 1000 * 1000;
var AccountID = ReadUintFromArr(Block.AddrHash, 0);
if(AccountID < 8)
return ;
var Data = this.ReadStateTR(AccountID);
if(Data && Data.Currency === 0 && Data.BlockNumCreate < Block.BlockNum)
{
var Power = GetPowPower(Block.PowHash);
var Sum = Power * Power * SysBalance / TOTAL_TER_MONEY / 100;
var CoinTotal = {SumCOIN:0, SumCENT:0};
var CoinSum = COIN_FROM_FLOAT(Sum);
if(!ISZERO(CoinSum))
{
if(Data.Adviser >= 8 && Block.BlockNum < REF_PERIOD_END)
{
var RefData = this.ReadStateTR(Data.Adviser);
if(RefData && RefData.BlockNumCreate < Block.BlockNum - REF_PERIOD_MINING)
{
var K = (REF_PERIOD_END - Block.BlockNum) / (REF_PERIOD_END - REF_PERIOD_START);
var CoinAdv = COIN_FROM_FLOAT(Sum * K);
this.SendMoneyTR(Block, 0, Data.Adviser, CoinAdv, Block.BlockNum, 0xFFFF, "", "Adviser coin base [" + AccountID + "]", 1)
ADD(CoinTotal, CoinAdv)
ADD(CoinSum, CoinAdv)
}
}
this.SendMoneyTR(Block, 0, AccountID, CoinSum, Block.BlockNum, 0xFFFF, "", "Coin base", 1)
ADD(CoinTotal, CoinSum)
var CoinDevelop = CopyObjValue(CoinTotal);
DIV(CoinDevelop, 100)
if(!ISZERO(CoinDevelop))
this.SendMoneyTR(Block, 0, 9, CoinDevelop, Block.BlockNum, 0xFFFF, "", "Developers support", 1)
}
}
}
GetVerifyTransaction(BlockNum, TrNum, Body)
{
var Type = Body[0];
var Find = 0;
switch(Type)
{
case TYPE_TRANSACTION_CREATE:
case TYPE_TRANSACTION_TRANSFER:
case TYPE_TRANSACTION_TRANSFER2:
case TYPE_TRANSACTION_TRANSFER3:
{
Find = 1
break;
}
case TYPE_TRANSACTION_ACC_HASH:
{
return 1;
}
default:
return 0;
}
if(Find)
{
var DBAct;
DBAct = this.DBAct
var Num = this.FindBlockInAct(DBAct, BlockNum);
if(Num === "NoHistory")
{
DBAct = this.DBActPrev
Num = this.FindBlockInAct(DBAct, BlockNum)
}
if(Num === "NoHistory")
return 0;
else
if(Num === "NoPresent")
return - 1;
while(true)
{
var Item = DBAct.Read(Num);
if(Item)
{
if(Item.BlockNum === BlockNum && Item.TrNum === TrNum)
break;
else
if(Item.BlockNum !== BlockNum)
return - 1;
}
else
{
return - 1;
}
Num++
}
}
return 1;
}
GetScriptTransaction(Body)
{
var Type = Body[0];
var format;
switch(Type)
{
case TYPE_TRANSACTION_CREATE:
{
format = FORMAT_CREATE
break;
}
case TYPE_TRANSACTION_TRANSFER:
{
format = FORMAT_MONEY_TRANSFER
break;
}
case TYPE_TRANSACTION_TRANSFER2:
{
format = FORMAT_MONEY_TRANSFER2
break;
}
case TYPE_TRANSACTION_TRANSFER3:
{
format = FORMAT_MONEY_TRANSFER3
break;
}
case TYPE_TRANSACTION_ACC_HASH:
{
format = FORMAT_ACCOUNT_HASH
break;
}
default:
return "";
}
try
{
var TR = BufLib.GetObjectFromBuffer(Body, format, {});
}
catch(e)
{
return "";
}
if(TR.Body && TR.Body.length)
{
var App = DAppByType[TR.Body[0]];
if(App)
{
TR.Body = JSON.parse(App.GetScriptTransaction(TR.Body))
}
}
ConvertBufferToStr(TR)
return JSON.stringify(TR, "", 2);
}
TRCheckAccountHash(Body, BlockNum, TrNum)
{
if(BlockNum % PERIOD_ACCOUNT_HASH !== 0)
return 1;
try
{
var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_ACCOUNT_HASH, {});
}
catch(e)
{
return 0;
}
if(BlockNum < START_BLOCK_ACCOUNT_HASH + 200000)
return 1;
var Item = this.GetAccountHashItem(TR.BlockNum);
if(Item)
{
if(CompareArr(Item.Hash, TR.Hash) === 0)
return 1;
else
return 0;
}
else
return 2;
}
TRCreateAccount(Body, BlockNum, TrNum, ContextFrom)
{
if(Body.length < 90)
return "Error length transaction";
var CheckMinPower = 1;
if(BlockNum >= 7000000 || global.LOCAL_RUN || global.TEST_NETWORK)
{
if(ContextFrom && ContextFrom.To.length === 1 && ContextFrom.To[0].ID === 0 && ContextFrom.To[0].SumCOIN >= PRICE_DAO(BlockNum).NewAccount)
{
CheckMinPower = 0
}
else
{
if(BlockNum % 10 !== 0)
return "The create transaction is not possible in this block: " + BlockNum;
if(this.CreateTrCount > 0)
return "The account creation transaction was already in this block: " + BlockNum;
}
}
this.CreateTrCount++
var power;
if(BlockNum >= global.BLOCKNUM_TICKET_ALGO)
{
var Tr = {body:Body};
SERVER.CheckCreateTransactionObject(Tr)
power = Tr.power
}
else
{
power = GetPowPower(shaarr(Body))
}
if(CheckMinPower)
{
var MinPower;
if(BlockNum < 2500000)
MinPower = MIN_POWER_POW_ACC_CREATE
else
if(BlockNum < 2800000)
MinPower = MIN_POWER_POW_ACC_CREATE + 2
else
MinPower = MIN_POWER_POW_ACC_CREATE + 3
if(power < MinPower)
return "Error min power POW for create account (update client)";
}
try
{
var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_CREATE, {});
}
catch(e)
{
return "Error transaction format";
}
if(BlockNum >= 3500000 && !TR.Name)
return "Account name required";
if(BlockNum >= 5700000 && !TR.Name.trim())
return "Account name required";
var Account = this.NewAccountTR(BlockNum, TrNum);
Account.Currency = TR.Currency
Account.PubKey = TR.PubKey
Account.Name = TR.Name
Account.Adviser = TR.Adviser
Account.Value.Smart = TR.Smart
this.WriteStateTR(Account, TrNum)
if(CompareArr(Account.PubKey, WALLET.PubKeyArr) === 0)
{
WALLET.OnCreateAccount(Account)
}
return true;
}
TRTransferMoney(Block, Body, BlockNum, TrNum, format_money_transfer, workstructtransfer)
{
if(Body.length < 103)
return "Error length transaction";
try
{
var TR = BufLib.GetObjectFromBuffer(Body, format_money_transfer, workstructtransfer);
}
catch(e)
{
return "Error transaction format";
}
if(!TR.Version)
TR.Version = 0
var Data = this.ReadStateTR(TR.FromID);
if(!Data)
return "Error sender's account ID: " + TR.FromID;
if(TR.Version < 3 && TR.Currency !== Data.Currency)
return "Error sender's currency";
if(TR.Version < 3)
{
if(TR.OperationID !== Data.Value.OperationID)
return "Error OperationID (expected: " + Data.Value.OperationID + " for ID: " + TR.FromID + ")";
}
else
{
if(TR.OperationID < Data.Value.OperationID)
return "Error OperationID (expected: " + Data.Value.OperationID + " for ID: " + TR.FromID + ")";
var MaxCountOperationID = 100;
if(BlockNum >= global.BLOCKNUM_TICKET_ALGO)
MaxCountOperationID = 1000000
if(TR.OperationID > Data.Value.OperationID + MaxCountOperationID)
return "Error too much OperationID (expected max: " + (Data.Value.OperationID + MaxCountOperationID) + " for ID: " + TR.FromID + ")";
}
if(BlockNum >= SMART_BLOCKNUM_START)
{
if(TR.To.length > 10)
return "The number of recipients has been exceeded (max=5, current count=" + TR.To.length + ")";
}
if(TR.Body && TR.Body.length && TR.To.length > 1)
{
return "Error - dapps transaction can not be used in a multiple transaction";
}
var TotalSum = {SumCOIN:0, SumCENT:0};
var MapItem = {};
var bWas = 0;
for(var i = 0; i < TR.To.length; i++)
{
var Item = TR.To[i];
if(Item.SumCOIN > MAX_SUM_TER)
return "Error MAX_SUM_COIN";
if(Item.SumCENT >= MAX_SUM_CENT)
return "Error MAX_SUM_CENT";
if(TR.Version < 3)
{
if(Item.ID === TR.FromID || MapItem[Item.ID])
continue;
MapItem[Item.ID] = 1
}
bWas = 1
ADD(TotalSum, Item)
}
if(!bWas && TR.Version < 3)
return "No significant recipients";
var ZeroSum = 0;
if(TotalSum.SumCOIN === 0 && TotalSum.SumCENT === 0)
{
if(TR.Version < 3)
return "No money transaction";
else
ZeroSum = 1
}
if(Data.Value.SumCOIN < TotalSum.SumCOIN || (Data.Value.SumCOIN === TotalSum.SumCOIN && Data.Value.SumCENT < TotalSum.SumCENT))
return "Not enough money on the account";
Data.Value.OperationID++
TR.Value = TotalSum
var arr = [];
MapItem = {}
var arrpub = [];
for(var i = 0; i < TR.To.length; i++)
{
var Item = TR.To[i];
var DataTo = this.ReadStateTR(Item.ID);
if(!DataTo)
return "Error receiver account ID: " + Item.ID;
if(!ZeroSum && Data.Currency !== DataTo.Currency)
return "Error receiver currency";
for(var j = 0; j < 33; j++)
arrpub[arrpub.length] = DataTo.PubKey[j]
if(DataTo.Value.Smart)
{
if(TR.To.length > 1)
return "Error - smart accounts can not be used in a multiple transaction";
}
if(TR.Version === 3 && Item.ID === 0 && Item.PubKey && Item.PubKey.length === 33)
{
if(Item.SumCOIN < PRICE_DAO(BlockNum).NewAccount)
return "Not enough money for create account with index: " + i;
var name = TR.Description;
var index = name.indexOf("\n");
if(index !== - 1)
name = name.substr(0, index)
var Account = this.NewAccountTR(BlockNum, TrNum);
Account.PubKey = Item.PubKey
Account.Name = name
this.WriteStateTR(Account, TrNum)
Item.ID = Account.Num
this.SendMoneyTR(Block, Data.Num, Account.Num, {SumCOIN:Item.SumCOIN, SumCENT:Item.SumCENT}, BlockNum, TrNum, TR.Description,
TR.Description, 1)
this.SendMoneyTR(Block, Account.Num, 0, {SumCOIN:PRICE_DAO(BlockNum).NewAccount, SumCENT:0}, BlockNum, TrNum, "Fee for create account",
"", 1)
}
else
{
if(TR.Version < 3)
{
if(Item.ID === TR.FromID || MapItem[Item.ID])
continue;
MapItem[Item.ID] = 1
}
this.SendMoneyTR(Block, Data.Num, DataTo.Num, {SumCOIN:Item.SumCOIN, SumCENT:Item.SumCENT}, BlockNum, TrNum, TR.Description,
TR.Description, 0)
arr.push(DataTo)
}
}
if(TR.Version < 3 && arr.length === 0)
return "No recipients";
var hash;
if(TR.Version === 2 || TR.Version === 3)
{
for(var j = 0; j < Body.length - 64 - 12; j++)
arrpub[arrpub.length] = Body[j]
hash = shabuf(arrpub)
}
else
if(!TR.Version)
{
hash = shabuf(Body.slice(0, Body.length - 64 - 12))
}
else
{
return "Error transaction version";
}
var Result = 0;
if(Data.PubKey[0] === 2 || Data.PubKey[0] === 3)
try
{
Result = secp256k1.verify(hash, TR.Sign, Data.PubKey)
}
catch(e)
{
}
if(!Result)
{
return "Error sign transaction";
}
if(TR.Body && TR.Body.length)
{
var App = DAppByType[TR.Body[0]];
if(App)
{
TR.FromPubKey = Data.PubKey
var Result = App.OnWriteTransaction(Block, TR.Body, BlockNum, TrNum, TR);
if(Result !== true)
return Result;
}
}
return true;
}
ReadState(Num)
{
var Data = this.DBState.Read(Num);
if(Data)
Data.WN = ""
return Data;
}
GetMinBlockAct()
{
var DBAct;
var MaxNum = this.DBActPrev.GetMaxNum();
if(MaxNum === - 1)
DBAct = this.DBAct
else
DBAct = this.DBActPrev
var Item = DBAct.Read(0);
if(!Item)
return - 1;
else
return Item.BlockNum;
}
DeleteAct(BlockNumFrom)
{
if(global.START_SERVER)
throw "DeleteAct START_SERVER";
if(BlockNumFrom > 0)
this.DBStateTX.Write({Num:0, BlockNum:BlockNumFrom - 1})
this.DeleteActOneDB(this.DBAct, BlockNumFrom)
this.DeleteActOneDB(this.DBActPrev, BlockNumFrom)
this.DBAccountsHash.Truncate(Math.trunc(BlockNumFrom / PERIOD_ACCOUNT_HASH))
WALLET.OnDeleteBlock(BlockNumFrom)
}
DeleteActOneDB(DBAct, BlockNum)
{
var MaxNum = DBAct.GetMaxNum();
if(MaxNum === - 1)
return ;
for(var num = MaxNum; num >= 0; num--)
{
var ItemCheck = DBAct.Read(num);
if(!ItemCheck)
{
ToLogTrace("!ItemCheck")
throw "ERRR DeleteActOneDB";
}
if(ItemCheck.BlockNum < BlockNum)
{
this.ProcessingDeleteAct(DBAct, num + 1)
return ;
}
}
this.ProcessingDeleteAct(DBAct, 0)
}
ProcessingDeleteAct(DBAct, StartNum)
{
var Map = {};
var bWas = 0;
var NumTruncateState;
for(var num = StartNum; true; num++)
{
var Item = DBAct.Read(num);
if(!Item)
break;
bWas = 1
if(Map[Item.ID])
continue;
Map[Item.ID] = 1
if(Item.Mode === 1)
{
if(!NumTruncateState)
NumTruncateState = Item.ID
}
else
{
var Data = this.DBState.Read(Item.ID);
Data.Value = Item.PrevValue
this.DBState.Write(Data)
}
}
if(bWas)
{
if(NumTruncateState)
{
this.DBState.Truncate(NumTruncateState - 1)
}
DBAct.Truncate(StartNum - 1)
}
}
FindBlockInAct(DBAct, BlockNum)
{
return DBAct.FastFindBlockNum(BlockNum);
}
GetHole()
{
return [{s:8300, f:186478}];
}
IsHole(num)
{
if(global.ALL_VIEW_ROWS)
return 0;
var ArrHole = this.GetHole();
for(var i = 0; i < ArrHole.length; i++)
if(num >= ArrHole[i].s && num <= ArrHole[i].f)
return 1;
return 0;
}
FindAccounts(PubKeyArr, map, nSet)
{
var Count = 0;
for(var num = 0; true; num++)
{
if(this.IsHole(num))
continue;
var Data = this.ReadState(num);
if(!Data)
break;
for(var i = 0; i < PubKeyArr.length; i++)
if(CompareArr(Data.PubKey, PubKeyArr[i]) === 0)
{
map[Data.Num] = i
Count++
}
}
return Count;
}
GetWalletAccountsByMap(map)
{
var arr = [];
for(var key in map)
{
var Num = parseInt(key);
var Data = this.ReadState(Num);
if(Data)
{
if(!Data.PubKeyStr)
Data.PubKeyStr = GetHexFromArr(Data.PubKey)
arr.push(Data)
Data.WN = map[key]
Data.Name = NormalizeName(Data.Name)
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 = Num
}
catch(e)
{
Data.SmartState = {}
}
}
}
}
return arr;
}
GetMaxAccount()
{
return this.DBState.GetMaxNum();
}
GetRowsAccounts(start, count, Filter, bGetState)
{
if(Filter)
{
Filter = Filter.trim()
}
var F;
if(Filter)
{
if(Filter.substring(0, 1) === "=")
{
Filter = Filter.substring(1)
try
{
F = CreateEval(Filter, "Cur,Currency,ID,Operation,Amount,Adviser,Name,PubKey,Smart,BlockNum")
}
catch(e)
{
F = undefined
ToLog("" + e)
}
}
else
{
Filter = Filter.toUpperCase()
}
}
var WasError = 0;
var arr = [];
for(var num = start; true; num++)
{
if(this.IsHole(num))
continue;
var Data = this.ReadState(num);
if(!Data)
break;
if(!Data.PubKeyStr)
Data.PubKeyStr = GetHexFromArr(Data.PubKey)
Data.Name = NormalizeName(Data.Name)
if(F)
{
var Cur = Data.Currency;
var Currency = Data.Currency;
var ID = Data.Num;
var Operation = Data.Value.OperationID;
var Amount = FLOAT_FROM_COIN(Data.Value);
var Adviser = Data.Adviser;
var Name = Data.Name;
var PubKey = GetHexFromArr(Data.PubKey);
var Smart = Data.Value.Smart;
try
{
if(!F(Cur, Currency, ID, Operation, Amount, Adviser, Name, PubKey, Smart, Data.BlockNumCreate))
continue;
}
catch(e)
{
if(!WasError)
ToLog("" + e)
WasError = 1
}
}
else
if(Filter)
{
var Amount = FLOAT_FROM_COIN(Data.Value);
var PubKey = GetHexFromArr(Data.PubKey);
var Str = "" + Data.Num + " " + Data.Value.OperationID + " " + Data.Name.toUpperCase() + " " + Data.Adviser + " " + Amount + " " + PubKey + " " + Smart + " " + Data.BlockNumCreate;
if(Str.indexOf(Filter) < 0)
continue;
}
if(bGetState)
{
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 = num
}
catch(e)
{
Data.SmartState = {}
}
}
}
arr.push(Data)
count--
if(count < 1)
break;
}
return arr;
}
GetActsMaxNum()
{
return this.DBActPrev.GetMaxNum() + this.DBAct.GetMaxNum();
}
GetActList(start, count)
{
var arr = [];
var num;
for(num = start; num < start + count; num++)
{
var Item = this.DBActPrev.Read(num);
if(!Item)
break;
Item.Num = "Prev." + Item.Num
if(Item.TrNum === 0xFFFF)
Item.TrNum = ""
arr.push(Item)
if(arr.length > count)
return arr;
}
start = num - this.DBActPrev.GetMaxNum() - 1
for(num = start; num < start + count; num++)
{
var Item = this.DBAct.Read(num);
if(!Item)
break;
Item.Num = Item.Num
if(Item.TrNum === 0xFFFF)
Item.TrNum = ""
arr.push(Item)
if(arr.length > count)
return arr;
}
return arr;
}
GetHashOrUndefined(BlockNum)
{
if(BlockNum % PERIOD_ACCOUNT_HASH !== 0)
return undefined;
var Item = this.GetAccountHashItem(BlockNum);
if(Item)
return Item.Hash;
else
return undefined;
}
GetAccountHashItem(BlockNum)
{
var Item = this.DBAccountsHash.Read(Math.trunc(BlockNum / PERIOD_ACCOUNT_HASH));
return Item;
}
GetHashedMaxBlockNum()
{
var Num = this.DBAccountsHash.GetMaxNum();
if(Num >= 0)
{
var Data = this.DBAccountsHash.Read(Num);
return Data.BlockNum;
}
else
return 0;
}
CalcHash(Block)
{
if(Block.BlockNum % PERIOD_ACCOUNT_HASH !== 0)
return ;
if(this.DBState.WasUpdate)
{
this.CalcMerkleTree()
}
var Hash = this.DBState.MerkleHash;
var Data = {Num:Block.BlockNum / PERIOD_ACCOUNT_HASH, BlockNum:Block.BlockNum, Hash:Hash, SumHash:Block.SumHash};
this.DBAccountsHash.Write(Data)
this.DBAccountsHash.Truncate(Block.BlockNum / PERIOD_ACCOUNT_HASH)
}
CalcMerkleTree(bForce)
{
this.DBState.MerkleHash = this.DBState.CalcMerkleTree(bForce)
this.DBState.WasUpdate = 0
}
GetAdviserByMiner(Map, Id)
{
var Adviser = Map[Id];
if(Adviser === undefined)
{
var Item = this.ReadState(Id);
if(Item)
Adviser = Item.Adviser
else
Adviser = 0
Map[Id] = Adviser
}
return Adviser;
}
BeginBlock()
{
this.DBChanges = {BlockMap:{}, BlockMaxAccount:this.GetMaxAccount(), BlockHistory:[], BlockEvent:[], }
}
BeginTransaction()
{
global.TickCounter = 35000
this.DBChanges.TRMap = {}
this.DBChanges.TRMaxAccount = this.DBChanges.BlockMaxAccount
this.DBChanges.RollBackTransaction = 0
this.DBChanges.TRHistory = []
this.DBChanges.TREvent = []
}
RollBackTransaction()
{
this.DBChanges.RollBackTransaction = 1
}
CommitBlock(Block)
{
var BlockNum = Block.BlockNum;
var DBChanges = this.DBChanges;
var arr = [];
for(var key in DBChanges.BlockMap)
{
key = ParseNum(key)
var Data = DBChanges.BlockMap[key];
if(Data.Changed)
{
arr.push(Data)
}
}
arr.sort(function (a,b)
{
return a.Num - b.Num;
})
for(var i = 0; i < arr.length; i++)
{
var Account = arr[i];
var BackLog = {Num:undefined, ID:Account.Num, BlockNum:BlockNum, PrevValue:Account.BackupValue, TrNum:Account.ChangeTrNum,
Mode:Account.New};
this.DBAct.Write(BackLog)
this.DBState.Write(Account)
}
for(var i = 0; i < DBChanges.BlockHistory.length; i++)
WALLET.OnDoHistoryAct(DBChanges.BlockHistory[i], BlockNum)
for(var i = 0; i < DBChanges.BlockEvent.length; i++)
{
var Data = DBChanges.BlockEvent[i];
var Has = global.TreeFindTX.LoadValue("Smart:" + Data.Smart, 1);
if(Has)
{
process.send({cmd:"DappEvent", Data:Data})
}
}
global.TickCounter = 0
this.DBChanges = undefined
if(BlockNum > 100)
{
var StateTX = this.DBStateTX.Read(0);
if(StateTX.BlockNum !== BlockNum - 1)
throw "ERROR SEQ STATETX";
}
this.CalcHash(Block)
this.DBStateTX.Write({Num:0, BlockNum:BlockNum})
}
CommitTransaction(BlockNum, TrNum)
{
var DBChanges = this.DBChanges;
if(DBChanges.RollBackTransaction)
return ;
DBChanges.BlockMaxAccount = DBChanges.TRMaxAccount
for(var key in DBChanges.TRMap)
{
key = ParseNum(key)
var Data = DBChanges.TRMap[key];
if(Data.Changed)
{
DBChanges.BlockMap[key] = Data
if(Data.New)
this.OnWriteNewAccountTR(Data, BlockNum, TrNum)
}
}
for(var i = 0; i < DBChanges.TRHistory.length; i++)
DBChanges.BlockHistory.push(DBChanges.TRHistory[i])
for(var i = 0; i < DBChanges.TREvent.length; i++)
{
DBChanges.BlockEvent.push(DBChanges.TREvent[i])
}
global.TickCounter = 0
}
OnWriteNewAccountTR(Data, BlockNum, TrNum)
{
if(BlockNum < SMART_BLOCKNUM_START)
Data.Value.Smart = 0
Data.BlockNumCreate = BlockNum
if(Data.Adviser > this.GetMaxAccount())
Data.Adviser = 0
if(Data.Value.Smart > DApps.Smart.GetMaxNum())
Data.Value.Smart = 0
if(Data.Currency > DApps.Smart.GetMaxNum())
Data.Currency = 0
if(Data.Currency)
{
var Smart = DApps.Smart.ReadSmart(Data.Currency);
if(!Smart || !Smart.TokenGenerate)
Data.Currency = 0
}
}
NewAccountTR(BlockNum, TrNum)
{
var DBChanges = this.DBChanges;
DBChanges.TRMaxAccount++
var Data = {Num:DBChanges.TRMaxAccount, New:1, Changed:1, ChangeTrNum:TrNum, BackupValue:{}, PubKey:[], Currency:0, Adviser:0,
Value:{SumCOIN:0, SumCENT:0, OperationID:0, Smart:0, Data:[]}};
this.DBChanges.TRMap[Data.Num] = Data
return Data;
}
ReadStateTR(Num)
{
Num = ParseNum(Num)
var TRMap = this.DBChanges.TRMap;
var Data = TRMap[Num];
if(!Data)
{
var Value;
var BlockMap = this.DBChanges.BlockMap;
var BData = BlockMap[Num];
if(!BData)
{
BData = this.DBState.Read(Num)
if(!BData)
return undefined;
BData.Num = Num
Value = BData.Value
BData.BackupValue = {SumCOIN:Value.SumCOIN, SumCENT:Value.SumCENT, OperationID:Value.OperationID, Smart:Value.Smart, Data:Value.Data}
BlockMap[Num] = BData
}
Value = BData.Value
Data = {Num:Num, Currency:BData.Currency, PubKey:BData.PubKey, Name:BData.Name, BlockNumCreate:BData.BlockNumCreate, Adviser:BData.Adviser,
Value:{SumCOIN:Value.SumCOIN, SumCENT:Value.SumCENT, OperationID:Value.OperationID, Smart:Value.Smart, Data:CopyArr(Value.Data)},
BackupValue:BData.BackupValue}
TRMap[Num] = Data
}
return Data;
}
WriteStateTR(Data, TrNum)
{
Data.Changed = 1
Data.ChangeTrNum = TrNum
}
SendMoneyTR(Block, FromID, ToID, CoinSum, BlockNum, TrNum, DescriptionFrom, DescriptionTo, OperationCount)
{
FromID = ParseNum(FromID)
ToID = ParseNum(ToID)
if(CoinSum.SumCENT >= 1e9)
{
throw "ERROR SumCENT>=1e9";
}
var FromData = this.ReadStateTR(FromID);
if(!FromData)
{
throw "Send: Error account FromNum: " + FromID;
}
if(!SUB(FromData.Value, CoinSum))
{
throw "Not enough money on the account ID:" + FromID;
}
this.WriteStateTR(FromData, TrNum)
if(WALLET.AccountMap[FromID] !== undefined)
{
this.DBChanges.TRHistory.push({Direct:"-", FromID:FromID, ToID:ToID, SumCOIN:CoinSum.SumCOIN, SumCENT:CoinSum.SumCENT, Description:DescriptionFrom,
FromOperationID:FromData.Value.OperationID, Currency:FromData.Currency})
}
var ToData = this.ReadStateTR(ToID);
if(!ToData)
{
throw "Send: Error account ToNum: " + ToID;
}
ADD(ToData.Value, CoinSum)
this.WriteStateTR(ToData, TrNum)
if(WALLET.AccountMap[ToID] !== undefined)
{
this.DBChanges.TRHistory.push({Direct:"+", FromID:FromID, ToID:ToID, SumCOIN:CoinSum.SumCOIN, SumCENT:CoinSum.SumCENT, Description:DescriptionTo,
FromOperationID:FromData.Value.OperationID, Currency:ToData.Currency})
}
FromData.Value.OperationID += OperationCount
if(FromData.Value.Smart)
{
var Context = {FromID:FromID, ToID:ToID, Description:DescriptionFrom, Value:CoinSum};
RunSmartMethod(Block, FromData.Value.Smart, FromData, BlockNum, TrNum, Context, "OnSend")
}
if(ToData.Value.Smart)
{
var Context = {FromID:FromID, ToID:ToID, Description:DescriptionTo, Value:CoinSum};
RunSmartMethod(Block, ToData.Value.Smart, ToData, BlockNum, TrNum, Context, "OnGet")
}
}
};
module.exports = AccountApp;
var App = new AccountApp;
DApps["Accounts"] = App;
DAppByType[TYPE_TRANSACTION_CREATE] = App;
DAppByType[TYPE_TRANSACTION_TRANSFER] = App;
DAppByType[TYPE_TRANSACTION_TRANSFER2] = App;
DAppByType[TYPE_TRANSACTION_TRANSFER3] = App;
DAppByType[TYPE_TRANSACTION_ACC_HASH] = App;