2019-07-12 12:45:46 +00:00
|
|
|
/*
|
|
|
|
* @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,\
|
2019-09-03 01:01:40 +00:00
|
|
|
Fixed:byte,\
|
|
|
|
CentName:str5,\
|
|
|
|
Reserve:arr14,\
|
2019-07-12 12:45:46 +00:00
|
|
|
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,\
|
2019-09-03 01:01:40 +00:00
|
|
|
Fixed:byte,\
|
|
|
|
CentName:str5,\
|
|
|
|
Reserve:arr14,\
|
2019-07-12 12:45:46 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-09-03 01:01:40 +00:00
|
|
|
if(BlockNum < global.UPDATE_CODE_2)
|
|
|
|
{
|
|
|
|
Smart.Reserve = []
|
|
|
|
}
|
2019-07-12 12:45:46 +00:00
|
|
|
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;
|
|
|
|
}
|
2019-09-03 01:01:40 +00:00
|
|
|
ReadSimple(Num, bTokenDescription)
|
2019-07-12 12:45:46 +00:00
|
|
|
{
|
|
|
|
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
|
2019-09-03 01:01:40 +00:00
|
|
|
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}})
|
2019-07-12 12:45:46 +00:00
|
|
|
this.DBSmart.SetMap("SIMPLE" + Num, Smart)
|
|
|
|
}
|
|
|
|
}
|
2019-09-03 01:01:40 +00:00
|
|
|
if(bTokenDescription)
|
|
|
|
this.AddCurrencyTokenDescription(Smart)
|
2019-07-12 12:45:46 +00:00
|
|
|
return Smart;
|
|
|
|
}
|
2019-09-03 01:01:40 +00:00
|
|
|
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
|
|
|
|
}
|
2019-07-12 12:45:46 +00:00
|
|
|
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 = {};
|
2019-08-26 06:20:28 +00:00
|
|
|
if(BlockNum >= UPDATE_CODE_1 && !PayContext)
|
|
|
|
{
|
|
|
|
PayContext = {FromID:0, ToID:Account.Num, Description:"", Value:{SumCOIN:0, SumCENT:0}};
|
|
|
|
}
|
2019-07-12 12:45:46 +00:00
|
|
|
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};
|
|
|
|
}
|
2019-09-03 01:01:40 +00:00
|
|
|
if(Block.BlockNum === 0)
|
2019-07-12 12:45:46 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
};
|
2019-09-03 01:01:40 +00:00
|
|
|
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};
|
|
|
|
}
|
|
|
|
};
|
2019-07-12 12:45:46 +00:00
|
|
|
|
|
|
|
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;
|
2019-09-03 01:01:40 +00:00
|
|
|
}, get Fixed()
|
|
|
|
{
|
|
|
|
return Data.Fixed;
|
|
|
|
}, get CoinName()
|
|
|
|
{
|
|
|
|
return Data.ShortName;
|
|
|
|
}, get CentName()
|
|
|
|
{
|
|
|
|
return Data.CentName;
|
2019-07-12 12:45:46 +00:00
|
|
|
}, };
|
|
|
|
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);
|
2019-09-03 01:01:40 +00:00
|
|
|
Object.freeze($ReadSmart);
|
2019-07-12 12:45:46 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-09-03 01:01:40 +00:00
|
|
|
CHECKSUM(CoinSum);
|
2019-07-12 12:45:46 +00:00
|
|
|
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);
|
2019-07-19 04:26:00 +00:00
|
|
|
CHECKSUM(CoinSum);
|
2019-07-12 12:45:46 +00:00
|
|
|
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)
|
|
|
|
{
|
2019-09-03 01:01:40 +00:00
|
|
|
throw "Different currencies. Accounts: " + RunContext.Account.Num + " and " + ToID;
|
2019-07-12 12:45:46 +00:00
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
2019-09-03 01:01:40 +00:00
|
|
|
throw "Different currencies. Accounts: " + FromID + " and " + ToID;
|
2019-07-12 12:45:46 +00:00
|
|
|
}
|
|
|
|
if(FromData.Value.Smart !== RunContext.Smart.Num)
|
|
|
|
{
|
2019-08-19 06:00:17 +00:00
|
|
|
throw "The account: " + FromID + " does not belong to the smart-contract: " + RunContext.Smart.Num + ", access is denied";
|
2019-07-12 12:45:46 +00:00
|
|
|
}
|
|
|
|
if(typeof CoinSum === "number")
|
|
|
|
{
|
|
|
|
CoinSum = COIN_FROM_FLOAT(CoinSum);
|
|
|
|
}
|
2019-07-19 04:26:00 +00:00
|
|
|
CHECKSUM(CoinSum);
|
2019-07-12 12:45:46 +00:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2019-09-03 01:01:40 +00:00
|
|
|
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);
|
|
|
|
};
|
|
|
|
|
2019-07-12 12:45:46 +00:00
|
|
|
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)
|
|
|
|
{
|
2019-08-19 06:00:17 +00:00
|
|
|
throw "The account: " + ID + " does not belong to the smart-contract: " + Smart.Num + ", access to change state is denied";
|
2019-07-12 12:45:46 +00:00
|
|
|
}
|
|
|
|
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;
|