/* * @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"; const LOC_ADD_NAME = "$"; require("../HTML/JS/lexer.js"); global.TickCounter = 0; const DBRow = require("../core/db/db-row"); const TYPE_TRANSACTION_SMART_CREATE = 130; global.TYPE_TRANSACTION_SMART_RUN = 135; const TYPE_TRANSACTION_SMART_CHANGE = 140; global.FORMAT_SMART_CREATE = "{\ Type:byte,\ TokenGenerate:byte,\ StartValue:uint,\ OwnerPubKey:byte,\ ISIN:str,\ Zip:byte,\ AccountLength:byte,\ StateFormat:str,\ Category1:byte,\ Category2:byte,\ Category3:byte,\ Fixed:byte,\ CentName:str5,\ Reserve:arr14,\ IconBlockNum:uint,\ IconTrNum:uint16,\ ShortName:str5,\ Name:str,\ Description:str,\ Code:str,\ HTML:str,\ }"; const WorkStructCreate = {}; global.FORMAT_SMART_RUN = "{\ Type:byte,\ Account:uint,\ MethodName:str,\ Params:str,\ FromNum:uint,\ OperationID:uint,\ Reserve:arr10,\ Sign:arr64,\ }"; const WorkStructRun = {}; global.FORMAT_SMART_CHANGE = "{\ Type:byte,\ Account:uint,\ Smart:uint32,\ Reserve:arr10,\ FromNum:uint,\ OperationID:uint,\ Sign:arr64,\ }"; const WorkStructChange = {}; class SmartApp extends require("./dapp") { constructor() { super() var bReadOnly = (global.PROCESS_NAME !== "TX"); this.FORMAT_ROW = "{\ Version:byte,\ TokenGenerate:byte,\ ISIN:str12,\ Zip:byte,\ BlockNum:uint,\ TrNum:uint16,\ IconBlockNum:uint,\ IconTrNum:uint16,\ ShortName:str5,\ Name:str40,\ Account:uint,\ AccountLength:byte,\ Category1:byte,\ Category2:byte,\ Category3:byte,\ Owner:uint,\ Fixed:byte,\ CentName:str5,\ Reserve:arr14,\ StateFormat:str,\ Description:str,\ Code:str,\ HTML:str,\ SumHash:hash,\ }" this.ROW_SIZE = 2 * (1 << 13) this.DBSmart = new DBRow("smart", this.ROW_SIZE, this.FORMAT_ROW, bReadOnly) this.InitHole() if(!bReadOnly) this.Start() } Start() { if(this.GetMaxNum() + 1 >= 7) return ; this.DBSmartWrite({Num:0, ShortName:"TERA", Name:"TERA", Description:"TERA", BlockNum:0, TokenGenerate:1, Account:0, Category1:0}) for(var i = 1; i < 8; i++) this.DBSmartWrite({Num:i, ShortName:"", Name:"", Description:"", BlockNum:0, TokenGenerate:1, Account:i, Category1:0}) } Close() { this.DBSmart.Close() } ClearDataBase() { this.DBSmart.Truncate( - 1) this.Start() } GetSenderNum(BlockNum, Body) { var Type = Body[0]; if(Type && Body.length > 90) { switch(Type) { case TYPE_TRANSACTION_SMART_RUN: var len = 1 + 6; len += 2 + Body[len] + Body[len + 1] * 256 if(len + 64 > Body.length) return 0; len += 2 + Body[len] + Body[len + 1] * 256 if(len + 64 > Body.length) return 0; var Num = ReadUintFromArr(Body, len); return Num; case TYPE_TRANSACTION_SMART_CHANGE: var Num = ReadUintFromArr(Body, 1); return Num; } } return 0; } OnDeleteBlock(Block) { if(Block.BlockNum < 1) return ; this.DBSmart.DeleteHistory(Block.BlockNum) } OnWriteBlockStart(Block) { if(Block.BlockNum < 1) return ; this.OnDeleteBlock(Block) } OnWriteBlockFinish(Block) { } OnWriteTransaction(Block, Body, BlockNum, TrNum, ContextFrom) { var Type = Body[0]; if(!ContextFrom) { DApps.Accounts.BeginTransaction() } var Result; try { switch(Type) { case TYPE_TRANSACTION_SMART_CREATE: Result = this.TRCreateSmart(Block, Body, BlockNum, TrNum, ContextFrom) break; case TYPE_TRANSACTION_SMART_RUN: Result = this.TRRunSmart(Block, Body, BlockNum, TrNum, ContextFrom) break; case TYPE_TRANSACTION_SMART_CHANGE: Result = this.TRChangeSmart(Block, Body, BlockNum, TrNum, ContextFrom) break; } } catch(e) { Result = "" + e if(global.WATCHDOG_DEV) ToErrorTx("BlockNum:" + BlockNum + ":" + e) } return Result; } GetScriptTransaction(Body) { var Type = Body[0]; var format; if(Type === TYPE_TRANSACTION_SMART_CREATE) format = FORMAT_SMART_CREATE else if(Type === TYPE_TRANSACTION_SMART_RUN) format = FORMAT_SMART_RUN else if(Type === TYPE_TRANSACTION_SMART_CHANGE) format = FORMAT_SMART_CHANGE if(!format) return ""; var TR = BufLib.GetObjectFromBuffer(Body, format, {}); ConvertBufferToStr(TR) return JSON.stringify(TR, "", 2); } GetVerifyTransaction(Block, BlockNum, TrNum, Body) { return 1; } TRCreateSmart(Block, Body, BlockNum, TrNum, ContextFrom) { if(!ContextFrom) return "Pay context required"; if(Body.length < 31) return "Error length transaction (min size)"; if(Body.length > 16000) return "Error length transaction (max size)"; if(BlockNum < SMART_BLOCKNUM_START) return "Error block num"; var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_SMART_CREATE, WorkStructCreate); if(!TR.Name.trim()) return "Name required"; if(TR.AccountLength > 50) return "Error AccountLength=" + TR.AccountLength; if(TR.AccountLength < 1) TR.AccountLength = 1 var AddAccount = TR.AccountLength - 1; var Price; if(TR.TokenGenerate) Price = PRICE_DAO(BlockNum).NewTokenSmart else Price = PRICE_DAO(BlockNum).NewSmart Price += AddAccount * PRICE_DAO(BlockNum).NewAccount if(!(ContextFrom && ContextFrom.To.length === 1 && ContextFrom.To[0].ID === 0 && ContextFrom.To[0].SumCOIN >= Price)) { return "Not money in the transaction"; } ContextFrom.ToID = ContextFrom.To[0].ID var Smart = TR; Smart.Version = 0 Smart.Zip = 0 Smart.BlockNum = BlockNum Smart.TrNum = TrNum Smart.Num = undefined Smart.Owner = ContextFrom.FromID this.DBSmart.CheckNewNum(Smart) var Account = DApps.Accounts.NewAccountTR(BlockNum, TrNum); Account.Value.Smart = Smart.Num Account.Name = TR.Name if(Smart.TokenGenerate) { Account.Currency = Smart.Num Account.Value.SumCOIN = TR.StartValue } if(TR.OwnerPubKey) Account.PubKey = ContextFrom.FromPubKey DApps.Accounts.WriteStateTR(Account, TrNum) for(var i = 0; i < AddAccount; i++) { var CurAccount = DApps.Accounts.NewAccountTR(BlockNum, TrNum); CurAccount.Value.Smart = Smart.Num CurAccount.Name = TR.Name if(Smart.TokenGenerate) CurAccount.Currency = Smart.Num if(TR.OwnerPubKey) CurAccount.PubKey = ContextFrom.FromPubKey DApps.Accounts.WriteStateTR(CurAccount, TrNum) } Smart.Account = Account.Num this.DBSmart.DeleteMap("EVAL" + Smart.Num) try { RunSmartMethod(Block, Smart, Account, BlockNum, TrNum, ContextFrom, "OnCreate") } catch(e) { this.DBSmart.DeleteMap("EVAL" + Smart.Num) return e; } if(BlockNum < global.UPDATE_CODE_2) { Smart.Reserve = [] } this.DBSmartWrite(Smart) return true; } CheckSignFrom(Body, TR, BlockNum, TrNum) { var ContextFrom = {FromID:TR.FromNum}; var AccountFrom = DApps.Accounts.ReadStateTR(TR.FromNum); if(!AccountFrom) return "Error account FromNum: " + TR.FromNum; if(TR.OperationID < AccountFrom.Value.OperationID) return "Error OperationID (expected: " + AccountFrom.Value.OperationID + " for ID: " + TR.FromNum + ")"; var MaxCountOperationID = 100; if(BlockNum >= global.BLOCKNUM_TICKET_ALGO) MaxCountOperationID = 1000000 if(TR.OperationID > AccountFrom.Value.OperationID + MaxCountOperationID) return "Error too much OperationID (expected max: " + (AccountFrom.Value.OperationID + MaxCountOperationID) + " for ID: " + TR.FromNum + ")"; var hash = SHA3BUF(Body.slice(0, Body.length - 64 - 12), BlockNum); var Result = 0; if(AccountFrom.PubKey[0] === 2 || AccountFrom.PubKey[0] === 3) try { Result = secp256k1.verify(hash, TR.Sign, AccountFrom.PubKey) } catch(e) { } if(!Result) { return "Error sign transaction"; } if(BlockNum >= 13000000) { AccountFrom.Value.OperationID = TR.OperationID + 1 DApps.Accounts.WriteStateTR(AccountFrom, TrNum) } else if(AccountFrom.Value.OperationID !== TR.OperationID) { AccountFrom.Value.OperationID = TR.OperationID DApps.Accounts.WriteStateTR(AccountFrom, TrNum) } return ContextFrom; } TRRunSmart(Block, Body, BlockNum, TrNum, ContextFrom) { if(Body.length < 100) return "Error length transaction (min size)"; if(BlockNum < SMART_BLOCKNUM_START) return "Error block num"; var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_SMART_RUN, WorkStructRun); var Account = DApps.Accounts.ReadStateTR(TR.Account); if(!Account) return "RunSmart: Error account Num: " + TR.Account; if(!ContextFrom && TR.FromNum) { var ResultCheck = this.CheckSignFrom(Body, TR, BlockNum, TrNum); if(typeof ResultCheck === "string") return ResultCheck; ContextFrom = ResultCheck } try { var Params = JSON.parse(TR.Params); RunSmartMethod(Block, Account.Value.Smart, Account, BlockNum, TrNum, ContextFrom, TR.MethodName, Params, 1) } catch(e) { return e; } return true; } TRChangeSmart(Block, Body, BlockNum, TrNum, ContextFrom) { if(Body.length < 21) return "Error length transaction (min size)"; if(BlockNum < SMART_BLOCKNUM_START) return "Error block num"; var TR = BufLib.GetObjectFromBuffer(Body, FORMAT_SMART_CHANGE, WorkStructChange); if(!ContextFrom) { var ResultCheck = this.CheckSignFrom(Body, TR, BlockNum, TrNum); if(typeof ResultCheck === "string") return ResultCheck; ContextFrom = ResultCheck } if(TR.Smart > this.GetMaxNum()) TR.Smart = 0 if(ContextFrom.FromID !== TR.Account) return "ChangeSmart: Error account FromNum: " + TR.Account; var Account = DApps.Accounts.ReadStateTR(TR.Account); if(!Account) return "Error read account Num: " + TR.Account; if(BlockNum >= 13000000) { if(Account.Value.Smart === TR.Smart) return "The value has not changed"; } if(Account.Value.Smart) { var Smart = this.ReadSmart(Account.Value.Smart); if(Smart.Account === TR.Account) return "Can't change base account"; try { RunSmartMethod(Block, Account.Value.Smart, Account, BlockNum, TrNum, ContextFrom, "OnDeleteSmart") } catch(e) { return e; } } Account.Value.Smart = TR.Smart Account.Value.Data = [] DApps.Accounts.WriteStateTR(Account, TrNum) if(Account.Value.Smart) { try { RunSmartMethod(Block, Account.Value.Smart, Account, BlockNum, TrNum, ContextFrom, "OnSetSmart") } catch(e) { return e; } } return true; } GetRows(start, count, Filter, Category, GetAllData, bTokenGenerate) { if(Filter) { Filter = Filter.trim() Filter = Filter.toUpperCase() } if(Category) Category = ParseNum(Category) var WasError = 0; var arr = []; var Data; for(var num = start; true; num++) { if(this.IsHole(num)) continue; if(GetAllData) Data = this.ReadSmart(num) else Data = this.ReadSimple(num) if(!Data) break; if(bTokenGenerate && !Data.TokenGenerate) continue; if(Category) { if(Data.Category1 !== Category && Data.Category2 !== Category && Data.Category3 !== Category) continue; } if(Filter) { var Str = "" + Data.ShortName.toUpperCase() + Data.ISIN.toUpperCase() + Data.Name.toUpperCase() + Data.Description.toUpperCase(); if(Data.TokenGenerate) Str += "TOKEN GENERATE" if(Str.indexOf(Filter) < 0) continue; } var CanAdd = 1; var DataState = DApps.Accounts.ReadState(Data.Account); if(DataState && !global.ALL_VIEW_ROWS) { Data.BaseState = DApps.Accounts.GetSmartState(DataState, Data.StateFormat) if(typeof Data.BaseState === "object" && Data.BaseState.HTMLBlock === 404) CanAdd = 0 } if(CanAdd) { arr.push(Data) } count-- if(count < 1) break; } return arr; } GetMaxNum() { return this.DBSmart.GetMaxNum(); } DBSmartWrite(Item) { var PrevNum; if(Item.Num === undefined) PrevNum = this.GetMaxNum() else PrevNum = Item.Num - 1 Item.SumHash = [] var Buf = BufLib.GetBufferFromObject(Item, this.FORMAT_ROW, 20000, {}); var Hash = sha3(Buf); if(PrevNum < 0) Item.SumHash = Hash else { var PrevItem = this.DBSmart.Read(PrevNum); if(!PrevItem) { throw "!PrevItem of Smart num = " + PrevNum; } Item.SumHash = sha3arr2(PrevItem.SumHash, Hash) } this.DBSmart.Write(Item) } ReadSmart(Num) { Num = ParseNum(Num) var Smart = this.DBSmart.GetMap("ITEM" + Num); if(!Smart) { Smart = this.DBSmart.Read(Num) if(Smart) { if(!Smart.WorkStruct) Smart.WorkStruct = {} Smart.CodeLength = Smart.Code.length Smart.HTMLLength = Smart.HTML.length this.DBSmart.SetMap("ITEM" + Num, Smart) } } return Smart; } ReadSimple(Num, bTokenDescription) { var Smart = this.DBSmart.GetMap("SIMPLE" + Num); if(!Smart) { Smart = this.DBSmart.Read(Num) if(Smart) { Smart.CodeLength = Smart.Code.length Smart.HTMLLength = Smart.HTML.length Object.defineProperties(Smart, {Reserve:{configurable:true, enumerable:false}}) Object.defineProperties(Smart, {Code:{configurable:true, enumerable:false}}) Object.defineProperties(Smart, {HTML:{configurable:true, enumerable:false}}) Object.defineProperties(Smart, {Description:{configurable:true, enumerable:false}}) this.DBSmart.SetMap("SIMPLE" + Num, Smart) } } if(bTokenDescription) this.AddCurrencyTokenDescription(Smart) return Smart; } AddCurrencyTokenDescription(Smart) { if(!Smart.Num) return ; if(!this.MapTokenDescription) this.MapTokenDescription = {} var Item = this.MapTokenDescription[Smart.Num]; var Time = Date.now(); if(!Item) { Item = {Time:Time, Description:""} this.MapTokenDescription[Smart.Num] = Item } if(Time - Item.Time > 5 * 1000) { Item.Time = Time var Params = undefined; var BlockNum = GetCurrentBlockNumByTime(); if(BlockNum < UPDATE_CODE_2) { try { var Account = DApps.Accounts.ReadState(Smart.Account); if(Smart.StateFormat) { var State = BufLib.GetObjectFromBuffer(Account.Value.Data, Smart.StateFormat, {}, 1); Params = {State:State, PayCur:GET_SMART(DApps.Smart.ReadSmart(State.PayCurrency)), OpnCur:GET_SMART(DApps.Smart.ReadSmart(State.Currency)), } } } catch(e) { } } else { Params = {} } var Result; if(Params) Result = RunStaticSmartMethod(Smart.Account, "GetTokenDescription", Params) if(Result && Result.result) { Item.TokenDescription = Result.RetValue } else { Item.TokenDescription = "" Item.Time = Time + 1000 * 1000 } } Smart.TokenDescription = Item.TokenDescription } InitHole() { if(global.LOCAL_RUN || global.TEST_NETWORK) this.RowHole = {} else this.RowHole = {"10":1, "19":1, "22":1, "23":1, "24":1, "26":1, "27":1, "29":1, "30":1, "34":1, "56":1, "57":1} for(var Num = 0; Num < 8; Num++) this.RowHole[Num] = 1 } IsHole(num) { if(global.ALL_VIEW_ROWS) return 0; return this.RowHole[num]; } }; function GetParsing(Str) { LexerJS.ParseCode(Str); var Code = LexerJS.stream; for(var key in LexerJS.FunctionMap) { Code += ";\nfunclist." + key + "=" + LOC_ADD_NAME + key; } for(var key in LexerJS.ExternMap) { Code += ";\npublist." + key + "=" + LOC_ADD_NAME + key; } Code += "\n\ var context;\ funclist.SetContext=function(cont){context=cont;};\ "; return Code; }; function GetSmartEvalContext(Smart) { var EvalContext = DApps.Smart.DBSmart.GetMap("EVAL" + Smart.Num); if(0) if(Smart.Num === 26) { const fs = require("fs"); var Path = "./dapp-smart/test-test.js"; Smart.Code = fs.readFileSync(Path, {encoding:"utf8"}); EvalContext = undefined; } if(!EvalContext) { var CodeLex = GetParsing(Smart.Code); var publist = {}; var funclist = {}; eval(CodeLex); EvalContext = {publist:publist, funclist:funclist}; for(var key in funclist) { Object.freeze(funclist[key]); } Object.freeze(funclist); Object.freeze(publist); DApps.Smart.DBSmart.SetMap("EVAL" + Smart.Num, EvalContext); } return EvalContext; }; var RunContext = undefined; global.RunSmartMethod = RunSmartMethod; function RunSmartMethod(Block,SmartOrSmartID,Account,BlockNum,TrNum,PayContext,MethodName,Params,bPublic) { var Smart = SmartOrSmartID; if(typeof SmartOrSmartID === "number") { Smart = DApps.Smart.ReadSmart(SmartOrSmartID); if(!Smart) { if(bPublic) throw "Smart does not exist. Error id number: " + SmartOrSmartID; else return ; } } var EvalContext = GetSmartEvalContext(Smart); if(!EvalContext.funclist[MethodName] || (bPublic && !EvalContext.publist[MethodName])) { if(bPublic) throw "Method '" + MethodName + "' not found in smart contract"; else return ; } var context = {}; if(BlockNum >= UPDATE_CODE_1 && !PayContext) { PayContext = {FromID:0, ToID:Account.Num, Description:"", Value:{SumCOIN:0, SumCENT:0}}; } if(PayContext) { context.BlockNum = BlockNum; context.BlockHash = CopyArr(Block.Hash); context.BlockAddrHash = CopyArr(Block.AddrHash); context.TrNum = TrNum; context.Account = GET_ACCOUNT(Account); context.Smart = GET_SMART(Smart); context.FromNum = PayContext.FromID; context.ToNum = PayContext.ToID; context.Description = PayContext.Description; if(PayContext.Value) context.Value = {SumCOIN:PayContext.Value.SumCOIN, SumCENT:PayContext.Value.SumCENT}; } if(Block.BlockNum === 0) { context.GetBlockHeader = StaticGetBlockHeader; context.GetBlockNumDB = StaticGetBlockNumDB; context.GetSmart = StaticGetSmart; } var LocalRunContext = {Block:Block, Smart:Smart, Account:Account, BlockNum:BlockNum, TrNum:TrNum, context:context}; var RetValue; var _RunContext = RunContext; RunContext = LocalRunContext; EvalContext.funclist.SetContext(RunContext.context); try { RetValue = EvalContext.funclist[MethodName](Params); } catch(e) { throw e; } finally { RunContext = _RunContext; } return RetValue; }; var glBlock0; global.RunStaticSmartMethod = RunStaticSmartMethod; function RunStaticSmartMethod(AccountNum,MethodName,Params) { DApps.Accounts.BeginBlock(); DApps.Accounts.BeginTransaction(); global.TickCounter = 100000; var Account = DApps.Accounts.ReadStateTR(AccountNum); if(!Account) { return {result:0, RetValue:"Error account Num: " + AccountNum}; } if(!glBlock0) glBlock0 = SERVER.ReadBlockHeaderDB(0); try { var BlockNum = GetCurrentBlockNumByTime(); var RetValue = RunSmartMethod(glBlock0, Account.Value.Smart, Account, BlockNum, 0, undefined, MethodName, Params, 1); return {result:1, RetValue:RetValue}; } catch(e) { return {result:0, RetValue:"" + e}; } }; function GET_ACCOUNT(Obj) { let Data = Obj; var GET_PROP = {get Num() { return Data.Num; }, get Currency() { return Data.Currency; }, get PubKey() { return CopyArr(Data.PubKey); }, get Name() { return Data.Name; }, get BlockNumCreate() { return Data.BlockNumCreate; }, get Adviser() { return Data.Adviser; }, get Smart() { return Data.Smart; }, get Value() { return {SumCOIN:Data.Value.SumCOIN, SumCENT:Data.Value.SumCENT, OperationID:Data.Value.OperationID, Smart:Data.Value.Smart}; }, }; return GET_PROP; }; function GET_SMART(Obj) { let Data = Obj; var GET_PROP = {get Num() { return Data.Num; }, get Version() { return Data.Version; }, get TokenGenerate() { return Data.TokenGenerate; }, get ISIN() { return Data.ISIN; }, get Zip() { return Data.Zip; }, get BlockNum() { return Data.BlockNum; }, get TrNum() { return Data.TrNum; }, get IconBlockNum() { return Data.IconBlockNum; }, get IconTrNum() { return Data.IconTrNum; }, get ShortName() { return Data.ShortName; }, get Name() { return Data.Name; }, get Description() { return Data.Description; }, get Account() { return Data.Account; }, get AccountLength() { return Data.AccountLength; }, get Owner() { return Data.Owner; }, get Code() { return Data.Code; }, get HTML() { return Data.HTML; }, get Fixed() { return Data.Fixed; }, get CoinName() { return Data.ShortName; }, get CentName() { return Data.CentName; }, }; return GET_PROP; }; function InitEval() { $Math.abs = function () { DO(6); return Math.abs.apply(Math, arguments); }; $Math.acos = function () { DO(16); return Math.acos.apply(Math, arguments); }; $Math.acosh = function () { DO(9); return Math.acosh.apply(Math, arguments); }; $Math.asin = function () { DO(19); return Math.asin.apply(Math, arguments); }; $Math.asinh = function () { DO(32); return Math.asinh.apply(Math, arguments); }; $Math.atan = function () { DO(13); return Math.atan.apply(Math, arguments); }; $Math.atanh = function () { DO(30); return Math.atanh.apply(Math, arguments); }; $Math.atan2 = function () { DO(15); return Math.atan2.apply(Math, arguments); }; $Math.ceil = function () { DO(6); return Math.ceil.apply(Math, arguments); }; $Math.cbrt = function () { DO(22); return Math.cbrt.apply(Math, arguments); }; $Math.expm1 = function () { DO(18); return Math.expm1.apply(Math, arguments); }; $Math.clz32 = function () { DO(5); return Math.clz32.apply(Math, arguments); }; $Math.cos = function () { DO(12); return Math.cos.apply(Math, arguments); }; $Math.cosh = function () { DO(20); return Math.cosh.apply(Math, arguments); }; $Math.exp = function () { DO(16); return Math.exp.apply(Math, arguments); }; $Math.floor = function () { DO(7); return Math.floor.apply(Math, arguments); }; $Math.fround = function () { DO(6); return Math.fround.apply(Math, arguments); }; $Math.hypot = function () { DO(56); return Math.hypot.apply(Math, arguments); }; $Math.imul = function () { DO(3); return Math.imul.apply(Math, arguments); }; $Math.log = function () { DO(10); return Math.log.apply(Math, arguments); }; $Math.log1p = function () { DO(23); return Math.log1p.apply(Math, arguments); }; $Math.log2 = function () { DO(19); return Math.log2.apply(Math, arguments); }; $Math.log10 = function () { DO(16); return Math.log10.apply(Math, arguments); }; $Math.max = function () { DO(6); return Math.max.apply(Math, arguments); }; $Math.min = function () { DO(6); return Math.min.apply(Math, arguments); }; $Math.pow = function () { DO(40); return Math.pow.apply(Math, arguments); }; $Math.round = function () { DO(7); return Math.round.apply(Math, arguments); }; $Math.sign = function () { DO(5); return Math.sign.apply(Math, arguments); }; $Math.sin = function () { DO(10); return Math.sin.apply(Math, arguments); }; $Math.sinh = function () { DO(24); return Math.sinh.apply(Math, arguments); }; $Math.sqrt = function () { DO(6); return Math.sqrt.apply(Math, arguments); }; $Math.tan = function () { DO(13); return Math.tan.apply(Math, arguments); }; $Math.tanh = function () { DO(24); return Math.tanh.apply(Math, arguments); }; $Math.trunc = function () { DO(6); return Math.trunc.apply(Math, arguments); }; $Math.random = function () { DO(1); return 0; }; Object.freeze($SetValue); Object.freeze($Send); Object.freeze($Move); Object.freeze($Event); Object.freeze($ReadAccount); Object.freeze($ReadState); Object.freeze($WriteState); Object.freeze($GetMaxAccount); Object.freeze($ADD); Object.freeze($SUB); Object.freeze($ISZERO); Object.freeze($FLOAT_FROM_COIN); Object.freeze($COIN_FROM_FLOAT); Object.freeze($COIN_FROM_STRING); Object.freeze($GetHexFromArr); Object.freeze($GetArrFromHex); Object.freeze($sha); Object.freeze($ReadSmart); Object.freeze($isFinite); Object.freeze($isNaN); Object.freeze($parseFloat); Object.freeze($parseInt); Object.freeze($parseUint); Object.freeze($String); Object.freeze($Number); Object.freeze($Boolean); var arr = Object.getOwnPropertyNames(JSON); for(var name of arr) { $JSON[name] = JSON[name]; } FreezeObjectChilds($Math); Object.freeze($Math); FreezeObjectChilds($JSON); Object.freeze($JSON); FreezeObjectChilds(Number.prototype); FreezeObjectChilds(String.prototype); FreezeObjectChilds(Boolean.prototype); FreezeObjectChilds(Array.prototype); FreezeObjectChilds(Object.prototype); }; function FreezeObjectChilds(Value) { var arr = Object.getOwnPropertyNames(Value); for(var name of arr) { Object.freeze(Value[name]); } }; function ChangePrototype() { var Array_prototype_concat = Array.prototype.concat; var Array_prototype_toString = Array.prototype.toString; Array.prototype.concat = function () { if(RunContext) throw "Error Access denied: concat"; else return Array_prototype_concat.apply(this, arguments); }; Array.prototype.toString = function () { if(RunContext) throw "Error Access denied: toString"; else return Array_prototype_toString.apply(this, arguments); }; Array.prototype.toLocaleString = Array.prototype.toString; Number.prototype.toLocaleString = function () { return this.toString(); }; String.prototype.toLocaleLowerCase = String.prototype.toLowerCase; String.prototype.toLocaleUpperCase = String.prototype.toUpperCase; var String_prototype_localeCompare = String.prototype.localeCompare; String.prototype.localeCompare = function () { if(RunContext) throw "Error Access denied: localeCompare"; else return String_prototype_localeCompare.apply(this, arguments); }; var String_prototype_match = String.prototype.match; String.prototype.match = function () { if(RunContext) throw "Error Access denied: match"; else return String_prototype_match.apply(this, arguments); }; var String_prototype_repeat = String.prototype.repeat; String.prototype.repeat = function () { if(RunContext) throw "Error Access denied: repeat"; else return String_prototype_repeat.apply(this, arguments); }; var String_prototype_search = String.prototype.search; String.prototype.search = function () { if(RunContext) throw "Error Access denied: search"; else return String_prototype_search.apply(this, arguments); }; var String_prototype_padStart = String.prototype.padStart; String.prototype.padStart = function () { if(RunContext) throw "Error Access denied: padStart"; else return String_prototype_padStart.apply(this, arguments); }; var String_prototype_padEnd = String.prototype.padEnd; String.prototype.padEnd = function () { if(RunContext) throw "Error Access denied: padEnd"; else return String_prototype_padEnd.apply(this, arguments); }; String.prototype.right = function (count) { if(this.length > count) return this.substr(this.length - count, count); else return this.substr(0, this.length); }; }; const MAX_LENGTH_STRING = 5000; const $Math = {}; const $JSON = {}; function DO(Count) { global.TickCounter -= Count; if(global.TickCounter < 0) throw new Error("Stop the execution code. The limit of ticks is over."); }; function $SetValue(ID,CoinSum) { DO(3000); ID = ParseNum(ID); if(!RunContext.Smart.TokenGenerate) { throw "The smart-contract is not token generate, access to change values is denied"; } var ToData = DApps.Accounts.ReadStateTR(ID); if(!ToData) { throw "Account does not exist.Error id number: " + ID; } if(ToData.Currency !== RunContext.Smart.Num) { throw "The account currency does not belong to the smart-contract, access to change values is denied"; } if(typeof CoinSum === "number") { CoinSum = COIN_FROM_FLOAT(CoinSum); } CHECKSUM(CoinSum); if(CoinSum.SumCENT >= 1e9) { throw "ERROR SumCENT>=1e9"; } if(CoinSum.SumCOIN < 0 || CoinSum.SumCENT < 0) { throw "ERROR Sum<0"; } ToData.Value.SumCOIN = Math.trunc(CoinSum.SumCOIN); ToData.Value.SumCENT = Math.trunc(CoinSum.SumCENT); DApps.Accounts.WriteStateTR(ToData, RunContext.TrNum); return true; }; function $Send(ToID,CoinSum,Description) { DO(3000); ToID = ParseNum(ToID); if(typeof CoinSum === "number") CoinSum = COIN_FROM_FLOAT(CoinSum); CHECKSUM(CoinSum); if(CoinSum.SumCENT >= 1e9) { throw "ERROR SumCENT>=1e9"; } if(CoinSum.SumCOIN < 0 || CoinSum.SumCENT < 0) { throw "ERROR Sum<0"; } var ToData = DApps.Accounts.ReadStateTR(ToID); if(!ToData) { throw "Error ToID - the account number does not exist."; } if(RunContext.Account.Currency !== ToData.Currency) { throw "Different currencies. Accounts: " + RunContext.Account.Num + " and " + ToID; } DApps.Accounts.SendMoneyTR(RunContext.Block, RunContext.Account.Num, ToID, CoinSum, RunContext.BlockNum, RunContext.TrNum, Description, Description, 1); }; function $Move(FromID,ToID,CoinSum,Description) { DO(3000); FromID = ParseNum(FromID); ToID = ParseNum(ToID); var FromData = DApps.Accounts.ReadStateTR(FromID); if(!FromData) { throw "Error FromID - the account number does not exist."; } var ToData = DApps.Accounts.ReadStateTR(ToID); if(!ToData) { throw "Error ToID - the account number does not exist."; } if(FromData.Currency !== ToData.Currency) { throw "Different currencies. Accounts: " + FromID + " and " + ToID; } if(FromData.Value.Smart !== RunContext.Smart.Num) { throw "The account: " + FromID + " does not belong to the smart-contract: " + RunContext.Smart.Num + ", access is denied"; } if(typeof CoinSum === "number") { CoinSum = COIN_FROM_FLOAT(CoinSum); } CHECKSUM(CoinSum); if(CoinSum.SumCENT >= 1e9) { throw "ERROR SumCENT>=1e9"; } if(CoinSum.SumCOIN < 0 || CoinSum.SumCENT < 0) { throw "ERROR Sum<0"; } CoinSum.SumCOIN = Math.trunc(CoinSum.SumCOIN); CoinSum.SumCENT = Math.trunc(CoinSum.SumCENT); DApps.Accounts.SendMoneyTR(RunContext.Block, FromID, ToID, CoinSum, RunContext.BlockNum, RunContext.TrNum, Description, Description, 1); }; function $Event(Description) { DO(50); DApps.Accounts.DBChanges.TREvent.push({Description:Description, Smart:RunContext.Smart.Num, Account:RunContext.Account.Num, BlockNum:RunContext.BlockNum, TrNum:RunContext.TrNum}); if(global.DebugEvent) DebugEvent(Description); if(global.CurTrItem) { ToLogClient(Description, global.CurTrItem, false); } }; function $ReadAccount(ID) { DO(900); ID = ParseNum(ID); var Account = DApps.Accounts.ReadStateTR(ID); if(!Account) throw "Error read account Num: " + ID; return GET_ACCOUNT(Account); }; function $ReadSmart(ID) { if(RunContext.BlockNum < global.UPDATE_CODE_2) { throw "Method call not available"; } DO(900); ID = ParseNum(ID); var Smart = DApps.Smart.ReadSmart(ID); if(!Smart) throw "Error smart ID: " + ID; return GET_SMART(Smart); }; function $ReadState(ID) { DO(900); ID = ParseNum(ID); var Account = DApps.Accounts.ReadStateTR(ID); if(!Account) throw "Error read state account Num: " + ID; var Smart; if(Account.Value.Smart === RunContext.Smart.Num) { Smart = RunContext.Smart; } else { DO(100); var Smart = DApps.Smart.ReadSmart(Account.Value.Smart); if(!Smart) { throw "Error smart ID: " + Account.Value.Smart; } } var Data; if(Smart.StateFormat) Data = BufLib.GetObjectFromBuffer(Account.Value.Data, Smart.StateFormat, Smart.WorkStruct, 1); else Data = {}; if(typeof Data === "object") Data.Num = ID; return Data; }; function $WriteState(Obj,ID) { DO(3000); if(ID === undefined) ID = Obj.Num; ID = ParseNum(ID); var Account = DApps.Accounts.ReadStateTR(ID); if(!Account) throw "Error write account Num: " + ID; var Smart = RunContext.Smart; if(Account.Value.Smart !== Smart.Num) { throw "The account: " + ID + " does not belong to the smart-contract: " + Smart.Num + ", access to change state is denied"; } Account.Value.Data = BufLib.GetBufferFromObject(Obj, Smart.StateFormat, 80, Smart.WorkStruct, 1); DApps.Accounts.WriteStateTR(Account, RunContext.TrNum); }; function $GetMaxAccount() { DO(20); return DApps.Accounts.DBChanges.TRMaxAccount; }; function $ADD(Coin,Value2) { DO(5); return ADD(Coin, Value2); }; function $SUB(Coin,Value2) { DO(5); return SUB(Coin, Value2); }; function $ISZERO(Coin) { DO(5); if(Coin.SumCOIN === 0 && Coin.SumCENT === 0) return true; else return false; }; function $FLOAT_FROM_COIN(Coin) { DO(5); return FLOAT_FROM_COIN(Coin); }; function $COIN_FROM_FLOAT(Sum) { DO(20); return COIN_FROM_FLOAT(Sum); }; function $COIN_FROM_STRING(Sum) { DO(20); return COIN_FROM_STRING(Sum); }; function $require(SmartNum) { DO(2000); SmartNum = ParseNum(SmartNum); var Smart = DApps.Smart.ReadSmart(SmartNum); if(!Smart) { throw "Smart does not exist. Error id number: " + SmartNum; } var EvalContext = GetSmartEvalContext(Smart); EvalContext.funclist.SetContext(RunContext.context); return EvalContext.publist; }; function $GetHexFromArr(Arr) { DO(20); return GetHexFromArr(Arr); }; function $GetArrFromHex(Str) { DO(20); return GetArrFromHex(Str); }; function $sha(Str) { DO(1000); return shaarr(Str); }; function $isFinite(a) { DO(5); return isFinite(a); }; function $isNaN(a) { DO(5); return isNaN(a); }; function $parseFloat(a) { DO(10); var Num = parseFloat(a); if(!Num) Num = 0; if(isNaN(Num)) Num = 0; return Num; }; function $parseInt(a) { DO(10); var Num = parseInt(a); if(!Num) Num = 0; if(isNaN(Num)) Num = 0; return Num; }; function $parseUint(a) { DO(10); return ParseNum(a); }; function $String(a) { DO(5); return String(a); }; function $Number(a) { DO(5); return Number(a); }; function $Boolean(a) { DO(5); return Boolean(a); }; function CHKL(Str) { if(typeof Str === "string" && Str.length > MAX_LENGTH_STRING) throw new Error("Invalid string length:" + Str.length); return Str; }; var BlockRandomInit; var m_w = 123456789; var m_z = 987654321; var mask = 0xffffffff; function MathRandom() { DO(5); function seed(i) { m_w = i; m_z = 987654321; }; function random() { m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask; m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask; var result = ((m_z << 16) + m_w) & mask; result /= 4294967296; return result + 0.5; }; if(BlockRandomInit === RunContext.Block.BlockNum) return random(); BlockRandomInit = RunContext.Block.BlockNum; RunContext.Block.Hash; return 0; }; function StaticGetBlockHeader(BlockNum) { DO(100); return SERVER.ReadBlockHeaderDB(BlockNum); }; function StaticGetBlockNumDB() { return SERVER.GetMaxNumBlockDB(); }; function StaticGetSmart(Num) { DO(100); var Smart = DApps.Smart.ReadSmart(Num); return GET_SMART(Smart); }; ChangePrototype(); InitEval(); module.exports = SmartApp; var App = new SmartApp; DApps["Smart"] = App; DAppByType[TYPE_TRANSACTION_SMART_CREATE] = App; DAppByType[TYPE_TRANSACTION_SMART_RUN] = App; DAppByType[TYPE_TRANSACTION_SMART_CHANGE] = App;