text: ﻿/**
   * lua module injector header in hsrcore
   * @author zoowii@hsrcore
   */
  
  #ifndef hsrcore_lua_api_h
  #define hsrcore_lua_api_h
  
  #include <glua/lprefix.h>
  
  #include <setjmp.h>
  #include <stdlib.h>
  #include <string.h>
  #include <string>
  #include <vector>
  #include <list>
  #include <set>
  #include <map>
  #include <unordered_map>
  #include <memory>
  
  #include <glua/lua.h>
  
  #define LOG_INFO(...)  fprintf(stderr, "[INFO] " ##__VA_ARGS__)
  
  #define ERROR_INFO(...) fprintf(stderr, "[ERROR] " ##__VA_ARGS__)
  
  #define GLUA_SOURCE_FILE_EXTENTION_NAME ".glua"
  
  #define LUA_MODULE_BYTE_STREAM_BUF_SIZE 1024*1024
  
  #define LUA_VM_EXCEPTION_STRNG_MAX_LENGTH 256
  
  #define LUA_EXCEPTION_MULTILINE_STRNG_MAX_LENGTH 4096
  
  #define CONTRACT_ID_MAX_LENGTH 50
  
  #define CONTRACT_NAME_MAX_LENGTH 50
  
  #define CONTRACT_MAX_OFFLINE_API_COUNT	1024
  
  #define LUA_FUNCTION_MAX_LOCALVARS_COUNT 128
  
  #define LUA_MAX_LOCALVARNAME_LENGTH	128
  
  // emit抛出的不同eventTypeName最大数量
  #define EMIT_EVENT_NAME_MAX_COUNT 50
  
  //级别 临时 永久
  #define CONTRACT_LEVEL_TEMP 1
  #define CONTRACT_LEVEL_FOREVER 2
  
  //状态 有效 删除
  #define CONTRACT_STATE_VALID 1
  #define CONTRACT_STATE_DELETED 2
  
  // 在blockchain中是否使用新语法
  #define USE_TYPE_CHECK true 
  
  // 合约的storage的record类型最多可以包含多少个属性
  #define CONTRACT_STORAGE_PROPERTIES_MAX_COUNT 256
  
  // 链给glua提供的全局API每次API调用增加指令累计执行数的数量
  #define CHAIN_GLUA_API_EACH_INSTRUCTIONS_COUNT 50
  
  // 外部管理的对象类型枚举，用在register_object_in_pool中
  enum GluaOutsideObjectTypes
  {
  	OUTSIDE_STREAM_STORAGE_TYPE = 0
  };
  
  typedef enum GluaStorageValueType
  {
      LVALUE_INTEGER = 0, LVALUE_NUMBER = 1, LVALUE_TABLE = 2, LVALUE_STRING = 3,
      LVALUE_BOOLEAN = 4, LVALUE_USERDATA = 5, LVALUE_NIL = 6, LVALUE_ARRAY = 7
      ,LVALUE_STREAM = 9
  
  	, LVALUE_NOT_SUPPORT = 100
  } GluaStorageValueType;
  
  namespace hsrcore {
      namespace blockchain {
  
          enum StorageValueTypes
          {
              storage_value_null = 0,
              storage_value_int = 1,
              storage_value_number = 2,
              storage_value_bool = 3,
              storage_value_string = 4,
  			storage_value_stream = 5,
  
              storage_value_unknown_table = 50,
              storage_value_int_table = 51,
              storage_value_number_table = 52,
              storage_value_bool_table = 53,
              storage_value_string_table = 54,
  			storage_value_stream_table = 55,
  
              storage_value_unknown_array = 100,
              storage_value_int_array = 101,
              storage_value_number_array = 102,
              storage_value_bool_array = 103,
              storage_value_string_array = 104,
  			storage_value_stream_array = 105,
  
              storage_value_userdata = 201,
              storage_value_not_support = 202
          };
  
  
          inline bool is_any_base_storage_value_type(StorageValueTypes type)
          {
              return ((type >= StorageValueTypes::storage_value_int)
                  && (type <= StorageValueTypes::storage_value_stream));
          }
  
          inline bool is_any_table_storage_value_type(StorageValueTypes type)
          {
              return (type >= StorageValueTypes::storage_value_unknown_table)
                  && (type <= StorageValueTypes::storage_value_stream_table);
          }
  
          inline bool is_any_array_storage_value_type(StorageValueTypes type)
          {
              return (type >= StorageValueTypes::storage_value_unknown_array)
                  && (type <= StorageValueTypes::storage_value_stream_array);
          }
  
          inline StorageValueTypes get_storage_base_type(StorageValueTypes type)
          {
              switch (type)
              {
              case StorageValueTypes::storage_value_bool:
              case StorageValueTypes::storage_value_bool_array:
              case StorageValueTypes::storage_value_bool_table:
                  return StorageValueTypes::storage_value_bool;
              case StorageValueTypes::storage_value_int:
              case StorageValueTypes::storage_value_int_array:
              case StorageValueTypes::storage_value_int_table:
                  return StorageValueTypes::storage_value_int;
              case StorageValueTypes::storage_value_number:
              case StorageValueTypes::storage_value_number_array:
              case StorageValueTypes::storage_value_number_table:
                  return StorageValueTypes::storage_value_number;
              case StorageValueTypes::storage_value_string:
              case StorageValueTypes::storage_value_string_array:
              case StorageValueTypes::storage_value_string_table:
                  return StorageValueTypes::storage_value_string;
  			case StorageValueTypes::storage_value_stream:
  			case StorageValueTypes::storage_value_stream_array:
  			case StorageValueTypes::storage_value_stream_table:
  				return StorageValueTypes::storage_value_stream;
              default:
                  return StorageValueTypes::storage_value_null;
              }
          }
  
          inline StorageValueTypes get_item_type_in_table_or_array(StorageValueTypes type)
          {
              switch (type)
              {
              case StorageValueTypes::storage_value_int_table:
              case StorageValueTypes::storage_value_int_array:
                  return StorageValueTypes::storage_value_int;
              case StorageValueTypes::storage_value_bool_table:
              case StorageValueTypes::storage_value_bool_array:
                  return StorageValueTypes::storage_value_bool;
              case StorageValueTypes::storage_value_number_table:
              case StorageValueTypes::storage_value_number_array:
                  return StorageValueTypes::storage_value_number;
              case StorageValueTypes::storage_value_string_table:
              case StorageValueTypes::storage_value_string_array:
                  return StorageValueTypes::storage_value_string;
  			case StorageValueTypes::storage_value_stream_table:
  			case StorageValueTypes::storage_value_stream_array:
  				return StorageValueTypes::storage_value_stream;
              case StorageValueTypes::storage_value_unknown_table:
              case StorageValueTypes::storage_value_unknown_array:
                  return StorageValueTypes::storage_value_null;
              default:
                  return StorageValueTypes::storage_value_null;
              }
          }
  
      }
  }
  
  enum GluaTypeInfoEnum
  {
  	LTI_OBJECT = 0,
  	LTI_NIL = 1,
  	LTI_STRING = 2,
  	LTI_INT = 3,
  	LTI_NUMBER = 4,
  	LTI_BOOL = 5,
  	LTI_TABLE = 6,
  	LTI_FUNCTION = 7, // coroutine as function type
  	LTI_UNION = 8,
  	LTI_RECORD = 9, // 新语法, type <RecordName> = { <name> : <type> , ... }
  	LTI_GENERIC = 10, // 新语法，泛型类型
  	LTI_ARRAY = 11, // 新语法，列表类型
  	LTI_MAP = 12, // 新语法，单纯的哈希表，并且key类型只能是字符串
  	LTI_LITERIAL_TYPE = 13, // 新语法，literal type 字符串/数字/布尔值的字面值的union的类型，比如: "Male" | "Female"
  	LTI_STREAM = 14, // Stream类型，没有直接的字面量
  	LTI_UNDEFINED = 100 // 未定义值，类似undefined
  };
  
  class GluaModuleByteStream {
  public:
      bool is_bytes;
      std::vector<char> buff;
      std::vector<std::string> contract_apis;
      std::vector<std::string> offline_apis;
      std::vector<std::string> contract_emit_events;
      std::string contract_id;
      std::string contract_name;
      int  contract_level;
      int  contract_state;
      // 合约中storage的类型
      std::map<std::string, hsrcore::blockchain::StorageValueTypes> contract_storage_properties;
  
  	// 合约中各API的API名称的参数类型列表
  	std::map<std::string, std::vector<GluaTypeInfoEnum>> contract_api_arg_types;
  
  public:
      GluaModuleByteStream();
      virtual ~GluaModuleByteStream();
  };
  
  typedef GluaModuleByteStream *GluaModuleByteStreamP;
  
  class GluaContractInfo
  {
  public:
      std::vector<std::string> contract_apis;
  };
  
  #define HSRCORE_API_NO_ERROR 0
  #define HSRCORE_API_SIMPLE_ERROR 1
  #define HSRCORE_API_MEMORY_ERROR 2
  #define HSRCORE_API_LVM_ERROR 3
  #define HSRCORE_API_PARSER_ERROR 4
  #define HSRCORE_API_COMPILE_ERROR 5
  #define HSRCORE_API_LVM_LIMIT_OVER_ERROR 6
  #define HSRCORE_API_THROW_ERROR 7   // glua代码中主动抛出的错误
  
  
  // storage structs
  #define LUA_STORAGE_CHANGELIST_KEY "__lua_storage_changelist__"
  #define LUA_STORAGE_READ_TABLES_KEY "__lua_storage_read_tables__"
  
  #define GLUA_OUTSIDE_OBJECT_POOLS_KEY "__glua_outside_object_pools__"
  
  #define lua_storage_is_table(t) (hsrcore::blockchain::is_any_table_storage_value_type(t)||hsrcore::blockchain::is_any_array_storage_value_type(t))
  #define lua_storage_is_array(t) (hsrcore::blockchain::is_any_array_storage_value_type(t))
  #define lua_storage_is_hashtable(t) (hsrcore::blockchain::is_any_table_storage_value_type(t))
  
  struct GluaStorageValue;
  
  struct lua_table_binary_function
  {	// base class for binary functions
      typedef std::string first_argument_type;
      typedef std::string second_argument_type;
      typedef bool result_type;
  };
  
  struct lua_table_less
      : public lua_table_binary_function
  {	// functor for operator<
      bool operator()(const std::string& _Left, const std::string& _Right) const
      {	// apply operator< to operands
          if (_Left.length() != _Right.length())
              return _Left.length() < _Right.length();
          return _Left < _Right;
      }
  };
  
  typedef std::map<std::string, struct GluaStorageValue, struct lua_table_less> GluaTableMap;
  
  typedef GluaTableMap* GluaTableMapP;
  
  struct GluaStorageChangeItem;
  
  
  typedef union GluaStorageValueUnion
  {
      lua_Integer int_value;
      lua_Number number_value;
      bool bool_value;
      char *string_value;
      GluaTableMapP table_value;
      void *userdata_value;
      void *pointer_value;
  } GluaStorageValueUnion;
  
  typedef struct GluaStorageValue
  {
      hsrcore::blockchain::StorageValueTypes type;
      union GluaStorageValueUnion value;
      inline GluaStorageValue()
      {
          type = hsrcore::blockchain::StorageValueTypes::storage_value_null;
      }
      inline static GluaStorageValue from_int(int val)
      {
          GluaStorageValue sv;
          sv.type = hsrcore::blockchain::StorageValueTypes::storage_value_int;
          sv.value.int_value = val;
          return sv;
      }
      inline static GluaStorageValue from_string(char *val)
      {
          GluaStorageValue sv;
          sv.type = hsrcore::blockchain::StorageValueTypes::storage_value_string;
          sv.value.string_value = val;
          return sv;
      }
  
  	// 尝试类型转换
  	inline void try_parse_type(hsrcore::blockchain::StorageValueTypes new_type)
      {
  	    switch(new_type)
  	    {
  		case hsrcore::blockchain::StorageValueTypes::storage_value_int:
  			try_parse_to_int_type();
  			break;
  		case hsrcore::blockchain::StorageValueTypes::storage_value_number:
  			try_parse_to_number_type();
  			break;
  		default:
  			break;
  	    }
      }
  
  	// 尝试当成int处理
  	inline void try_parse_to_int_type()
      {
  	    if(type == hsrcore::blockchain::StorageValueTypes::storage_value_number)
  	    {
  			value.int_value = (lua_Integer)value.number_value;
  			type = hsrcore::blockchain::StorageValueTypes::storage_value_int;
  	    }
      }
  
  	// 尝试当成number处理
  	inline void try_parse_to_number_type()
      {
  		if (type == hsrcore::blockchain::StorageValueTypes::storage_value_int)
  		{
  			value.number_value = (lua_Number)value.int_value;
  			type = hsrcore::blockchain::StorageValueTypes::storage_value_number;
  		}
      }
  
      inline static bool is_same_base_type_with_type_parse(hsrcore::blockchain::StorageValueTypes type1, hsrcore::blockchain::StorageValueTypes type2)
      {
          return type1 == type2;
      }
      inline bool equals(GluaStorageValue &other)
      {
          if (type != other.type)
              return false;
          switch (type)
          {
          case hsrcore::blockchain::StorageValueTypes::storage_value_string:
              return strcmp(value.string_value, other.value.string_value) == 0;
          case hsrcore::blockchain::StorageValueTypes::storage_value_int:
              return value.int_value == other.value.int_value;
          case hsrcore::blockchain::StorageValueTypes::storage_value_number:
              return value.number_value == other.value.number_value;
          case hsrcore::blockchain::StorageValueTypes::storage_value_bool:
              return value.bool_value == other.value.bool_value;
              return value.userdata_value == other.value.userdata_value;
          case hsrcore::blockchain::StorageValueTypes::storage_value_null:
              return true;
          default: {
              if (hsrcore::blockchain::is_any_table_storage_value_type(type)
                  || hsrcore::blockchain::is_any_array_storage_value_type(type))
              {
                  if (value.table_value->size() != other.value.table_value->size())
                      return false;
  
                  for (const auto &p1 : *(value.table_value))
                  {
                      auto found = other.value.table_value->find(p1.first);
                      if (found == other.value.table_value->end())
                          return false;
                      auto this_item = p1.second;
                      auto other_item = found->second;
                      if (!this_item.equals(other_item))
                          return false;
                  }
                  return true;
              }
              return false;
          }
          }
      }
  } GluaStorageValue;
  
  typedef struct GluaStorageChangeItem
  {
      std::string contract_id;
      std::string key;
      struct GluaStorageValue before;
      struct GluaStorageValue after;
  } GluaStorageChangeItem;
  
  
  
  typedef std::list<GluaStorageChangeItem> GluaStorageChangeList;
  
  typedef std::list<GluaStorageChangeItem> GluaStorageTableReadList;
  
  struct GluaStorageValue lua_type_to_storage_value_type(lua_State *L, int index);
  
  bool luaL_commit_storage_changes(lua_State *L);
  
  bool lua_push_storage_value(lua_State *L, const GluaStorageValue &value);
  
  typedef std::unordered_map<std::string, GluaStorageChangeItem> ContractChangesMap;
  
  typedef std::shared_ptr<ContractChangesMap> ContractChangesMapP;
  
  typedef std::unordered_map<std::string, ContractChangesMapP> AllContractsChangesMap;
  
  
  namespace hsrcore {
      namespace blockchain{
          struct Code;
      }
  
      namespace lua {
          namespace api {
  
            class IGluaChainApi
            {
            public:
              /**
              * check whether the contract apis limit over, in this lua_State
              * @param L the lua stack
              * @return TRUE(1 or not 0) if over limit(will break the vm), FALSE(0) if not over limit
              */
              virtual int check_contract_api_instructions_over_limit(lua_State *L) = 0;
  
              /**
               * whether exception happen in L
               */
              virtual bool has_exception(lua_State *L) = 0;
  
              /**
              * clear exception marked
              */
              virtual void clear_exceptions(lua_State *L) = 0;
  
  
              /**
              * when exception happened, use this api to tell hsrcore
              * @param L the lua stack
              * @param code error code, 0 is OK, other is different error
              * @param error_format error info string, will be released by lua
              * @param ... error arguments
              */
              virtual void throw_exception(lua_State *L, int code, const char *error_format, ...) = 0;
  
              /**
              * get contract info stored before from hsrcore api
              * @param name contract name
              * @param contract_info_ret this api save the contract's api name array here if found, this var will be free by this api
              * @return TRUE(1 or not 0) if success, FALSE(0) if failed
              */
              virtual int get_stored_contract_info(lua_State *L, const char *name, std::shared_ptr<GluaContractInfo> contract_info_ret) = 0;
  
              virtual int get_stored_contract_info_by_address(lua_State *L, const char *address, std::shared_ptr<GluaContractInfo> contract_info_ret) = 0;
  
              virtual std::shared_ptr<GluaModuleByteStream> get_bytestream_from_code(lua_State *L, const hsrcore::blockchain::Code& code) = 0;
              /**
              * load contract lua byte stream from hsrcore api
              */
              virtual std::shared_ptr<GluaModuleByteStream> open_contract(lua_State *L, const char *name) = 0;
  
              virtual std::shared_ptr<GluaModuleByteStream> open_contract_by_address(lua_State *L, const char *address) = 0;
  
              /**
               * get contract address/id from hsrcore by contract name
               */
              virtual void get_contract_address_by_name(lua_State *L, const char *name, char *address, size_t *address_size) = 0;
  
              /*
               * check whether the contract exist
               */
              virtual bool check_contract_exist(lua_State *L, const char *name) = 0;
  
              /**
               * check contract exist by ID string address
               */
              virtual bool check_contract_exist_by_address(lua_State *L, const char *address) = 0;
  
              /**
               * register new storage name of contract to hsrcore
               */
              virtual bool register_storage(lua_State *L, const char *contract_name, const char *name) = 0;
  
              virtual GluaStorageValue get_storage_value_from_hsrcore(lua_State *L, const char *contract_name, std::string name) = 0;
  
              virtual GluaStorageValue get_storage_value_from_hsrcore_by_address(lua_State *L, const char *contract_address, std::string name) = 0;
  
              /**
               * after lua merge storage changes in lua_State, use the function to store the merged changes of storage to hsrcore
               */
              virtual bool commit_storage_changes_to_hsrcore(lua_State *L, AllContractsChangesMap &changes) = 0;
  
              /**
               * 注册对象地址到关联lua_State的外部对象池（在外部创建的各种类型的对象）中，不同类型的对象需要一个单独的对象池
               * lua_State被释放的时候需要释放所有对象池中所有对象
               * 返回值是glua中用来操作这个对象的句柄/指针，目前只能是对象地址本身，以lightuserdata的形式存在于glua中
               */
              virtual intptr_t register_object_in_pool(lua_State *L, intptr_t object_addr, GluaOutsideObjectTypes type) = 0;
  
              /**
               * 判断某个对象(register_object_in_pool的返回对象，一般实现为对象的内存地址)是否是关联lua_State中某个类型的对象池中的对象（从而可以判断是否可以强制转换)
               * 如果找到，返回对象地址，否则返回0
               */
              virtual intptr_t is_object_in_pool(lua_State *L, intptr_t object_key, GluaOutsideObjectTypes type) = 0;
  
              /**
               * 释放lua_State关联的所有外部对象池中的对象已经对象池本身
               */
              virtual void release_objects_in_pool(lua_State *L) = 0;
  
              /************************************************************************/
              /* transfer asset from contract by account address                      */
              /************************************************************************/
              virtual lua_Integer transfer_from_contract_to_address(lua_State *L, const char *contract_address, const char *to_address,
                const char *asset_type, int64_t amount) = 0;
  
              /************************************************************************/
              /* transfer asset from contract by account name on chain                */
              /************************************************************************/
              virtual lua_Integer transfer_from_contract_to_public_account(lua_State *L, const char *contract_address, const char *to_account_name,
                const char *asset_type, int64_t amount) = 0;
  
              virtual int64_t get_contract_balance_amount(lua_State *L, const char *contract_address, const char* asset_symbol) = 0;
              virtual int64_t get_transaction_fee(lua_State *L) = 0;
              virtual uint32_t get_chain_now(lua_State *L) = 0;
              virtual uint32_t get_chain_random(lua_State *L) = 0;
              virtual std::string get_transaction_id(lua_State *L) = 0;
              virtual uint32_t get_header_block_num(lua_State *L) = 0;
              virtual uint32_t wait_for_future_random(lua_State *L, int next) = 0;
  
              virtual int32_t get_waited(lua_State *L, uint32_t num) = 0;
  
              virtual void emit(lua_State *L, const char* contract_id, const char* event_name, const char* event_param) = 0;
  			virtual bool is_valid_address(lua_State *L, const char *address_str) = 0;
  			virtual bool is_valid_contract_address(lua_State *L, const char *address_str) = 0;
  			virtual const char *get_system_asset_symbol(lua_State *L) = 0;
            };
  
  
            extern IGluaChainApi *global_glua_chain_api;
  
          }
      }
  }
  
  
  #endif
  