wasm_interface.cpp 42.7 KB
Newer Older
1 2 3 4
/**
 *  @file
 *  @copyright defined in eos/LICENSE.txt
 */
5
#include <boost/function.hpp>
6
#include <boost/multiprecision/cpp_bin_float.hpp>
7
#include <eos/chain/wasm_interface.hpp>
8
#include <eos/chain/chain_controller.hpp>
9
#include <eos/chain/rate_limiting_object.hpp>
10 11 12 13 14 15 16 17 18
#include "Platform/Platform.h"
#include "WAST/WAST.h"
#include "Runtime/Runtime.h"
#include "Runtime/Linker.h"
#include "Runtime/Intrinsics.h"
#include "IR/Module.h"
#include "IR/Operators.h"
#include "IR/Validate.h"
#include <eos/chain/key_value_object.hpp>
19
#include <eos/chain/account_object.hpp>
B
Brian Johnson 已提交
20 21
#include <eos/chain/balance_object.hpp>
#include <eos/chain/staked_balance_objects.hpp>
22
#include <eos/types/abi_serializer.hpp>
23
#include <chrono>
24
#include <boost/lexical_cast.hpp>
25
#include <fc/utf8.hpp>
26

P
Pravin 已提交
27
namespace eosio { namespace chain {
28 29
   using namespace IR;
   using namespace Runtime;
30
   typedef boost::multiprecision::cpp_bin_float_50 DOUBLE;
31
   const uint32_t bytes_per_mbyte = 1024 * 1024;
32

B
Brian Johnson 已提交
33 34 35
   class wasm_memory
   {
   public:
B
Brian Johnson 已提交
36 37 38
      explicit wasm_memory(wasm_interface& interface);
      wasm_memory(const wasm_memory&) = delete;
      wasm_memory(wasm_memory&&) = delete;
B
Brian Johnson 已提交
39 40 41 42 43 44 45 46 47 48 49 50
      ~wasm_memory();
      U32 sbrk(I32 num_bytes);
   private:
      static U32 limit_32bit_address(Uptr address);

      static const U32 _max_memory = 1024 * 1024;
      wasm_interface& _wasm_interface;
      Uptr _num_pages;
      const U32 _min_bytes;
      U32 _num_bytes;
   };

B
Brian Johnson 已提交
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
   // account.h/hpp expected account API balance interchange format
   // must match account.hpp account_balance definition
   PACKED_STRUCT(
   struct account_balance
   {
      /**
      * Name of the account who's balance this is
      */
      account_name account;

      /**
      * Balance for this account
      */
      asset eos_balance;

      /**
      * Staked balance for this account
      */
      asset staked_balance;

      /**
      * Unstaking balance for this account
      */
      asset unstaking_balance;

      /**
      * Time at which last unstaking occurred for this account
      */
      time last_unstaking_time;
   })

82 83 84
   wasm_interface::wasm_interface() {
   }

85
   wasm_interface::key_type wasm_interface::to_key_type(const types::type_name& type_name)
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
   {
      if ("str" == type_name)
         return str;
      if ("i64" == type_name)
         return i64;
      if ("i128i128" == type_name)
         return i128i128;
      if ("i64i64i64" == type_name)
         return i64i64i64;

      return invalid_key_type;
   }

   std::string wasm_interface::to_type_name(key_type key_type)
   {
      switch (key_type)
      {
      case str:
         return "str";
      case i64:
         return "i64";
      case i128i128:
         return "i128i128";
      case i64i64i64:
         return "i64i64i64";
      default:
         return std::string("<invalid key type - ") + boost::lexical_cast<std::string>(int(key_type)) + ">";
      }
   }

116
#ifdef NDEBUG
117
   const int CHECKTIME_LIMIT = 3000;
118
#else
119
   const int CHECKTIME_LIMIT = 18000;
120 121
#endif

122
   void checktime(int64_t duration, uint32_t checktime_limit)
B
Brian Johnson 已提交
123
   {
124
      if (duration > checktime_limit) {
B
Brian Johnson 已提交
125 126 127
         wlog("checktime called ${d}", ("d", duration));
         throw checktime_exceeded();
      }
128
   }
B
Brian Johnson 已提交
129 130

DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) {
131
   checktime(wasm_interface::get().current_execution_time(), wasm_interface::get().checktime_limit);
132
}
B
Brian Johnson 已提交
133

134 135
   template <typename Function, typename KeyType, int numberOfKeys>
   int32_t validate(int32_t valueptr, int32_t valuelen, Function func) {
M
Matias Romeo 已提交
136

137
      static const uint32_t keylen = numberOfKeys*sizeof(KeyType);
M
Matias Romeo 已提交
138

139 140 141
      FC_ASSERT( valuelen >= keylen, "insufficient data passed" );

      auto& wasm  = wasm_interface::get();
M
Matias Romeo 已提交
142
      FC_ASSERT( wasm.current_apply_context, "no apply context found" );
143 144

      char* value = memoryArrayPtr<char>( wasm.current_memory, valueptr, valuelen );
145
      KeyType*  keys = reinterpret_cast<KeyType*>(value);
146 147 148 149
      
      valuelen -= keylen;
      value    += keylen;

150
      return func(wasm.current_apply_context, keys, value, valuelen);
M
Matias Romeo 已提交
151 152
   }

M
Matias Romeo 已提交
153 154 155 156 157 158 159 160 161 162 163 164 165 166
   template <typename Function>
   int32_t validate_str(int32_t keyptr, int32_t keylen, int32_t valueptr, int32_t valuelen, Function func) {

      auto& wasm  = wasm_interface::get();
      FC_ASSERT( wasm.current_apply_context, "no apply context found" );

      char* key   = memoryArrayPtr<char>( wasm.current_memory, keyptr, keylen );
      char* value = memoryArrayPtr<char>( wasm.current_memory, valueptr, valuelen );

      std::string keys(key, keylen);

      return func(wasm.current_apply_context, &keys, value, valuelen);
   }

167 168 169 170 171 172 173 174
   int64_t round_to_byte_boundary(int64_t bytes)
   {
      const unsigned int byte_boundary = sizeof(uint64_t);
      int64_t remainder = bytes % byte_boundary;
      if (remainder > 0)
         bytes += byte_boundary - remainder;
      return bytes;
   }
M
Matias Romeo 已提交
175

176
#define VERIFY_TABLE(TYPE) \
177
   const auto table_name = name(table); \
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
   auto& wasm  = wasm_interface::get(); \
   if (wasm.table_key_types) \
   { \
      auto table_key = wasm.table_key_types->find(table_name); \
      if (table_key == wasm.table_key_types->end()) \
      { \
         FC_ASSERT(!wasm.tables_fixed, "abi did not define table ${t}", ("t", table_name)); \
         wasm.table_key_types->emplace(std::make_pair(table_name,wasm_interface::TYPE)); \
      } \
      else \
      { \
         FC_ASSERT(wasm_interface::TYPE == table_key->second, "abi definition for ${table} expects \"${type}\", but code is requesting \"" #TYPE "\"", ("table",table_name)("type",wasm_interface::to_type_name(table_key->second))); \
      } \
   }

193 194
#define READ_RECORD(READFUNC, INDEX, SCOPE) \
   auto lambda = [&](apply_context* ctx, INDEX::value_type::key_type* keys, char *data, uint32_t datalen) -> int32_t { \
195
      auto res = ctx->READFUNC<INDEX, SCOPE>( name(scope), name(code), table_name, keys, data, datalen); \
196 197 198 199 200 201 202
      if (res >= 0) res += INDEX::value_type::number_of_keys*sizeof(INDEX::value_type::key_type); \
      return res; \
   }; \
   return validate<decltype(lambda), INDEX::value_type::key_type, INDEX::value_type::number_of_keys>(valueptr, valuelen, lambda);

#define UPDATE_RECORD(UPDATEFUNC, INDEX, DATASIZE) \
   auto lambda = [&](apply_context* ctx, INDEX::value_type::key_type* keys, char *data, uint32_t datalen) -> int32_t { \
203
      return ctx->UPDATEFUNC<INDEX::value_type>( name(scope), name(ctx->code.value), table_name, keys, data, datalen); \
204
   }; \
205 206 207
   const int32_t ret = validate<decltype(lambda), INDEX::value_type::key_type, INDEX::value_type::number_of_keys>(valueptr, DATASIZE, lambda);

#define DEFINE_RECORD_UPDATE_FUNCTIONS(OBJTYPE, INDEX, KEY_SIZE) \
208
   DEFINE_INTRINSIC_FUNCTION4(env,store_##OBJTYPE,store_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \
209
      VERIFY_TABLE(OBJTYPE) \
210
      UPDATE_RECORD(store_record, INDEX, valuelen); \
211 212 213
      /* ret is -1 if record created, or else it is the length of the data portion of the originally stored */ \
      /* structure (it does not include the key portion */ \
      const bool created = (ret == -1); \
214
      int64_t& storage = wasm_interface::get().table_storage; \
215
      if (created) \
216
         storage += round_to_byte_boundary(valuelen) + wasm_interface::get().row_overhead_db_limit_bytes; \
217 218
      else \
         /* need to calculate the difference between the original rounded byte size and the new rounded byte size */ \
219 220 221 222 223
         storage += round_to_byte_boundary(valuelen) - round_to_byte_boundary(KEY_SIZE + ret); \
\
      EOS_ASSERT(storage <= (wasm_interface::get().per_code_account_max_db_limit_mbytes * bytes_per_mbyte), \
                 tx_code_db_limit_exceeded, \
                 "Database limit exceeded for account=${name}",("name", name(wasm_interface::get().current_apply_context->code.value))); \
224
      return created ? 1 : 0; \
225 226
   } \
   DEFINE_INTRINSIC_FUNCTION4(env,update_##OBJTYPE,update_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \
227
      VERIFY_TABLE(OBJTYPE) \
228
      UPDATE_RECORD(update_record, INDEX, valuelen); \
229 230 231
      /* ret is -1 if record created, or else it is the length of the data portion of the originally stored */ \
      /* structure (it does not include the key portion */ \
      if (ret == -1) return 0; \
232
      int64_t& storage = wasm_interface::get().table_storage; \
233
      /* need to calculate the difference between the original rounded byte size and the new rounded byte size */ \
234 235 236 237 238
      storage += round_to_byte_boundary(valuelen) - round_to_byte_boundary(KEY_SIZE + ret); \
      \
      EOS_ASSERT(storage <= (wasm_interface::get().per_code_account_max_db_limit_mbytes * bytes_per_mbyte), \
                 tx_code_db_limit_exceeded, \
                 "Database limit exceeded for account=${name}",("name", name(wasm_interface::get().current_apply_context->code.value))); \
239
      return 1; \
240 241
   } \
   DEFINE_INTRINSIC_FUNCTION3(env,remove_##OBJTYPE,remove_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr) { \
242
      VERIFY_TABLE(OBJTYPE) \
243
      UPDATE_RECORD(remove_record, INDEX, sizeof(typename INDEX::value_type::key_type)*INDEX::value_type::number_of_keys); \
244 245 246
      /* ret is -1 if record created, or else it is the length of the data portion of the originally stored */ \
      /* structure (it does not include the key portion */ \
      if (ret == -1) return 0; \
247 248 249 250 251 252
      int64_t& storage = wasm_interface::get().table_storage; \
      storage -= round_to_byte_boundary(KEY_SIZE + ret) + wasm_interface::get().row_overhead_db_limit_bytes; \
      \
      EOS_ASSERT(storage <= (wasm_interface::get().per_code_account_max_db_limit_mbytes * bytes_per_mbyte), \
                 tx_code_db_limit_exceeded, \
                 "Database limit exceeded for account=${name}",("name", name(wasm_interface::get().current_apply_context->code.value))); \
253
      return 1; \
254
   }
M
Matias Romeo 已提交
255

256 257 258 259
#define DEFINE_RECORD_READ_FUNCTION(OBJTYPE, ACTION, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_INTRINSIC_FUNCTION5(env,ACTION##_##FUNCPREFIX##OBJTYPE,ACTION##_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
      VERIFY_TABLE(OBJTYPE) \
      READ_RECORD(ACTION##_record, INDEX, SCOPE); \
260
   }
M
Matias Romeo 已提交
261

262 263 264 265 266 267 268 269 270
#define DEFINE_RECORD_READ_FUNCTIONS(OBJTYPE, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, load, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, front, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, back, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, next, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, previous, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, lower_bound, FUNCPREFIX, INDEX, SCOPE) \
   DEFINE_RECORD_READ_FUNCTION(OBJTYPE, upper_bound, FUNCPREFIX, INDEX, SCOPE)

271
DEFINE_RECORD_UPDATE_FUNCTIONS(i64, key_value_index, 8);
272
DEFINE_RECORD_READ_FUNCTIONS(i64,,key_value_index, by_scope_primary);
M
Matias Romeo 已提交
273
      
274
DEFINE_RECORD_UPDATE_FUNCTIONS(i128i128, key128x128_value_index, 32);
275 276
DEFINE_RECORD_READ_FUNCTIONS(i128i128, primary_,   key128x128_value_index, by_scope_primary);
DEFINE_RECORD_READ_FUNCTIONS(i128i128, secondary_, key128x128_value_index, by_scope_secondary);
M
Matias Romeo 已提交
277

278
DEFINE_RECORD_UPDATE_FUNCTIONS(i64i64i64, key64x64x64_value_index, 24);
279 280 281
DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, primary_,   key64x64x64_value_index, by_scope_primary);
DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, secondary_, key64x64x64_value_index, by_scope_secondary);
DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_,  key64x64x64_value_index, by_scope_tertiary);
M
Matias Romeo 已提交
282

M
Matias Romeo 已提交
283 284

#define UPDATE_RECORD_STR(FUNCTION) \
285
  VERIFY_TABLE(str) \
M
Matias Romeo 已提交
286
  auto lambda = [&](apply_context* ctx, std::string* keys, char *data, uint32_t datalen) -> int32_t { \
287
    return ctx->FUNCTION<keystr_value_object>( name(scope), name(ctx->code.value), table_name, keys, data, datalen); \
M
Matias Romeo 已提交
288
  }; \
289
  const int32_t ret = validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda);
M
Matias Romeo 已提交
290 291

#define READ_RECORD_STR(FUNCTION) \
292
  VERIFY_TABLE(str) \
M
Matias Romeo 已提交
293
  auto lambda = [&](apply_context* ctx, std::string* keys, char *data, uint32_t datalen) -> int32_t { \
294
    auto res = ctx->FUNCTION<keystr_value_index, by_scope_primary>( name(scope), name(code), table_name, keys, data, datalen); \
M
Matias Romeo 已提交
295 296 297 298 299
    return res; \
  }; \
  return validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda);

DEFINE_INTRINSIC_FUNCTION6(env,store_str,store_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
300 301
   UPDATE_RECORD_STR(store_record)
   const bool created = (ret == -1);
302
   auto& storage = wasm_interface::get().table_storage;
303 304
   if (created)
   {
305
      storage += round_to_byte_boundary(keylen + valuelen) + wasm_interface::get().row_overhead_db_limit_bytes;
306 307 308
   }
   else
      // need to calculate the difference between the original rounded byte size and the new rounded byte size
309 310 311 312 313
      storage += round_to_byte_boundary(keylen + valuelen) - round_to_byte_boundary(keylen + ret);

   EOS_ASSERT(storage <= (wasm_interface::get().per_code_account_max_db_limit_mbytes * bytes_per_mbyte),
              tx_code_db_limit_exceeded,
              "Database limit exceeded for account=${name}",("name", name(wasm_interface::get().current_apply_context->code.value)));
314 315

   return created ? 1 : 0;
M
Matias Romeo 已提交
316 317
}
DEFINE_INTRINSIC_FUNCTION6(env,update_str,update_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
318 319
   UPDATE_RECORD_STR(update_record)
   if (ret == -1) return 0;
320
   auto& storage = wasm_interface::get().table_storage;
321
   // need to calculate the difference between the original rounded byte size and the new rounded byte size
322 323 324 325 326
   storage += round_to_byte_boundary(keylen + valuelen) - round_to_byte_boundary(keylen + ret);

   EOS_ASSERT(storage <= (wasm_interface::get().per_code_account_max_db_limit_mbytes * bytes_per_mbyte),
              tx_code_db_limit_exceeded,
              "Database limit exceeded for account=${name}",("name", name(wasm_interface::get().current_apply_context->code.value)));
327
   return 1;
M
Matias Romeo 已提交
328 329
}
DEFINE_INTRINSIC_FUNCTION4(env,remove_str,remove_str,i32,i64,scope,i64,table,i32,keyptr,i32,keylen) {
330 331 332
   int32_t valueptr=0, valuelen=0;
   UPDATE_RECORD_STR(remove_record)
   if (ret == -1) return 0;
333 334 335 336 337 338
   auto& storage = wasm_interface::get().table_storage;
   storage -= round_to_byte_boundary(keylen + ret) + wasm_interface::get().row_overhead_db_limit_bytes;

   EOS_ASSERT(storage <= (wasm_interface::get().per_code_account_max_db_limit_mbytes * bytes_per_mbyte),
              tx_code_db_limit_exceeded,
              "Database limit exceeded for account=${name}",("name", name(wasm_interface::get().current_apply_context->code.value)));
339
   return 1;
M
Matias Romeo 已提交
340 341 342 343 344
}

DEFINE_INTRINSIC_FUNCTION7(env,load_str,load_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
  READ_RECORD_STR(load_record)
}
345 346
DEFINE_INTRINSIC_FUNCTION5(env,front_str,front_str,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) {
  int32_t keyptr=0, keylen=0;
M
Matias Romeo 已提交
347 348
  READ_RECORD_STR(front_record)
}
349 350
DEFINE_INTRINSIC_FUNCTION5(env,back_str,back_str,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) {
  int32_t keyptr=0, keylen=0;
M
Matias Romeo 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
  READ_RECORD_STR(back_record)
}
DEFINE_INTRINSIC_FUNCTION7(env,next_str,next_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
  READ_RECORD_STR(next_record)
}
DEFINE_INTRINSIC_FUNCTION7(env,previous_str,previous_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
  READ_RECORD_STR(previous_record)
}
DEFINE_INTRINSIC_FUNCTION7(env,lower_bound_str,lower_bound_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
  READ_RECORD_STR(lower_bound_record)
}
DEFINE_INTRINSIC_FUNCTION7(env,upper_bound_str,upper_bound_str,i32,i64,scope,i64,code,i64,table,i32,keyptr,i32,keylen,i32,valueptr,i32,valuelen) {
  READ_RECORD_STR(upper_bound_record)
}

366 367 368 369 370 371 372 373 374 375
DEFINE_INTRINSIC_FUNCTION3(env, assert_is_utf8,assert_is_utf8,none,i32,dataptr,i32,datalen,i32,msg) {
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;

  const char* str = &memoryRef<const char>( mem, dataptr );
  const bool test = fc::is_utf8(std::string( str, datalen ));

  FC_ASSERT( test, "assertion failed: ${s}", ("s",msg) );
}

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398
DEFINE_INTRINSIC_FUNCTION3(env, assert_sha256,assert_sha256,none,i32,dataptr,i32,datalen,i32,hash) {
   FC_ASSERT( datalen > 0 );

   auto& wasm  = wasm_interface::get();
   auto  mem          = wasm.current_memory;

   char* data = memoryArrayPtr<char>( mem, dataptr, datalen );
   const auto& v = memoryRef<fc::sha256>( mem, hash );

   auto result = fc::sha256::hash( data, datalen );
   FC_ASSERT( result == v, "hash miss match" );
}

DEFINE_INTRINSIC_FUNCTION3(env,sha256,sha256,none,i32,dataptr,i32,datalen,i32,hash) {
   FC_ASSERT( datalen > 0 );

   auto& wasm  = wasm_interface::get();
   auto  mem          = wasm.current_memory;

   char* data = memoryArrayPtr<char>( mem, dataptr, datalen );
   auto& v = memoryRef<fc::sha256>( mem, hash );
   v  = fc::sha256::hash( data, datalen );
}
399

400 401 402
DEFINE_INTRINSIC_FUNCTION2(env,multeq_i128,multeq_i128,none,i32,self,i32,other) {
   auto& wasm  = wasm_interface::get();
   auto  mem   = wasm.current_memory;
403 404
   auto& v = memoryRef<unsigned __int128>( mem, self );
   const auto& o = memoryRef<const unsigned __int128>( mem, other );
405 406
   v *= o;
}
407

408
DEFINE_INTRINSIC_FUNCTION2(env,diveq_i128,diveq_i128,none,i32,self,i32,other) {
409
   auto& wasm  = wasm_interface::get();
410
   auto  mem          = wasm.current_memory;
411 412
   auto& v = memoryRef<unsigned __int128>( mem, self );
   const auto& o = memoryRef<const unsigned __int128>( mem, other );
413 414 415
   FC_ASSERT( o != 0, "divide by zero" );
   v /= o;
}
416

M
Matias Romeo 已提交
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464
DEFINE_INTRINSIC_FUNCTION2(env,double_add,double_add,i64,i64,a,i64,b) {
   DOUBLE c = DOUBLE(*reinterpret_cast<double *>(&a))
            + DOUBLE(*reinterpret_cast<double *>(&b));
   double res = c.convert_to<double>();
   return *reinterpret_cast<uint64_t *>(&res);
}

DEFINE_INTRINSIC_FUNCTION2(env,double_mult,double_mult,i64,i64,a,i64,b) {
   DOUBLE c = DOUBLE(*reinterpret_cast<double *>(&a))
            * DOUBLE(*reinterpret_cast<double *>(&b));
   double res = c.convert_to<double>();
   return *reinterpret_cast<uint64_t *>(&res);
}

DEFINE_INTRINSIC_FUNCTION2(env,double_div,double_div,i64,i64,a,i64,b) {
   auto divisor = DOUBLE(*reinterpret_cast<double *>(&b));
   FC_ASSERT( divisor != 0, "divide by zero" );

   DOUBLE c = DOUBLE(*reinterpret_cast<double *>(&a)) / divisor;
   double res = c.convert_to<double>();
   return *reinterpret_cast<uint64_t *>(&res);
}

DEFINE_INTRINSIC_FUNCTION2(env,double_lt,double_lt,i32,i64,a,i64,b) {
   return DOUBLE(*reinterpret_cast<double *>(&a))
        < DOUBLE(*reinterpret_cast<double *>(&b));
}

DEFINE_INTRINSIC_FUNCTION2(env,double_eq,double_eq,i32,i64,a,i64,b) {
   return DOUBLE(*reinterpret_cast<double *>(&a))
       == DOUBLE(*reinterpret_cast<double *>(&b));
}

DEFINE_INTRINSIC_FUNCTION2(env,double_gt,double_gt,i32,i64,a,i64,b) {
   return DOUBLE(*reinterpret_cast<double *>(&a))
        > DOUBLE(*reinterpret_cast<double *>(&b));
}

DEFINE_INTRINSIC_FUNCTION1(env,double_to_i64,double_to_i64,i64,i64,a) {
   return DOUBLE(*reinterpret_cast<double *>(&a))
          .convert_to<uint64_t>();
}

DEFINE_INTRINSIC_FUNCTION1(env,i64_to_double,i64_to_double,i64,i64,a) {
   double res = DOUBLE(a).convert_to<double>();
   return *reinterpret_cast<uint64_t *>(&res);
}

465
DEFINE_INTRINSIC_FUNCTION2(env,get_active_producers,get_active_producers,none,i32,producers,i32,datalen) {
M
Matias Romeo 已提交
466 467
   auto& wasm    = wasm_interface::get();
   auto  mem     = wasm.current_memory;
468
   types::account_name* dst = memoryArrayPtr<types::account_name>( mem, producers, datalen );
M
Matias Romeo 已提交
469 470 471
   return wasm_interface::get().current_validate_context->get_active_producers(dst, datalen);
}

472 473 474
DEFINE_INTRINSIC_FUNCTION0(env,now,now,i32) {
   return wasm_interface::get().current_validate_context->controller.head_block_time().sec_since_epoch();
}
475

476
DEFINE_INTRINSIC_FUNCTION0(env,current_code,current_code,i64) {
477 478 479 480
   auto& wasm  = wasm_interface::get();
   return wasm.current_validate_context->code.value;
}

481
DEFINE_INTRINSIC_FUNCTION1(env,require_auth,require_auth,none,i64,account) {
482
   wasm_interface::get().current_validate_context->require_authorization( name(account) );
483
}
484

485
DEFINE_INTRINSIC_FUNCTION1(env,require_notice,require_notice,none,i64,account) {
486
   wasm_interface::get().current_apply_context->require_recipient( account );
487
}
488

489 490 491
DEFINE_INTRINSIC_FUNCTION3(env,memcpy,memcpy,i32,i32,dstp,i32,srcp,i32,len) {
   auto& wasm          = wasm_interface::get();
   auto  mem           = wasm.current_memory;
D
Daniel Larimer 已提交
492 493 494
   char* dst           = memoryArrayPtr<char>( mem, dstp, len);
   const char* src     = memoryArrayPtr<const char>( mem, srcp, len );
   FC_ASSERT( len > 0 );
495 496

   if( dst > src )
D
Daniel Larimer 已提交
497
      FC_ASSERT( dst >= (src + len), "overlap of memory range is undefined", ("d",dstp)("s",srcp)("l",len) );
498
   else
D
Daniel Larimer 已提交
499 500
      FC_ASSERT( src >= (dst + len), "overlap of memory range is undefined", ("d",dstp)("s",srcp)("l",len) );

501 502 503 504
   memcpy( dst, src, uint32_t(len) );
   return dstp;
}

A
Andrianto Lie 已提交
505 506 507 508 509 510 511 512 513 514 515
DEFINE_INTRINSIC_FUNCTION3(env,memcmp,memcmp,i32,i32,dstp,i32,srcp,i32,len) {
   auto& wasm          = wasm_interface::get();
   auto  mem           = wasm.current_memory;
   char* dst           = memoryArrayPtr<char>( mem, dstp, len);
   const char* src     = memoryArrayPtr<const char>( mem, srcp, len );
   FC_ASSERT( len > 0 );

   return memcmp( dst, src, uint32_t(len) );
}


516 517 518 519 520 521 522 523 524 525
DEFINE_INTRINSIC_FUNCTION3(env,memset,memset,i32,i32,rel_ptr,i32,value,i32,len) {
   auto& wasm          = wasm_interface::get();
   auto  mem           = wasm.current_memory;
   char* ptr           = memoryArrayPtr<char>( mem, rel_ptr, len);
   FC_ASSERT( len > 0 );

   memset( ptr, value, len );
   return rel_ptr;
}

B
Brian Johnson 已提交
526 527 528 529 530 531
DEFINE_INTRINSIC_FUNCTION1(env,sbrk,sbrk,i32,i32,num_bytes) {
   auto& wasm          = wasm_interface::get();

   FC_ASSERT( num_bytes >= 0, "sbrk can only allocate memory, not reduce" );
   FC_ASSERT( wasm.current_memory_management != nullptr, "sbrk can only be called during the scope of wasm_interface::vm_call" );
   U32 previous_bytes_allocated = wasm.current_memory_management->sbrk(num_bytes);
532
   checktime(wasm.current_execution_time(), wasm_interface::get().checktime_limit);
B
Brian Johnson 已提交
533 534 535
   return previous_bytes_allocated;
}

536

537
/**
538
 * transaction C API implementation
539 540 541
 * @{
 */ 

542
DEFINE_INTRINSIC_FUNCTION0(env,transaction_create,transaction_create,i32) {
543 544 545 546
   auto& ptrx = wasm_interface::get().current_apply_context->create_pending_transaction();
   return ptrx.handle;
}

547
static void emplace_scope(const name& scope, std::vector<name>& scopes) {
548 549 550 551 552 553
   auto i = std::upper_bound( scopes.begin(), scopes.end(), scope);
   if (i == scopes.begin() || *(i - 1) != scope ) {
     scopes.insert(i, scope);
   }
}

554
DEFINE_INTRINSIC_FUNCTION3(env,transaction_require_scope,transaction_require_scope,none,i32,handle,i64,scope,i32,readOnly) {
555 556
   auto& ptrx = wasm_interface::get().current_apply_context->get_pending_transaction(handle);
   if(readOnly == 0) {
557
      emplace_scope(scope, ptrx.scope);
558
   } else {
559
      emplace_scope(scope, ptrx.read_scope);
560 561 562 563 564
   }

   ptrx.check_size();
}

565
DEFINE_INTRINSIC_FUNCTION2(env,transaction_add_message,transaction_add_message,none,i32,handle,i32,msg_handle) {
566 567 568 569 570 571
   auto apply_context  = wasm_interface::get().current_apply_context;
   auto& ptrx = apply_context->get_pending_transaction(handle);
   auto& pmsg = apply_context->get_pending_message(msg_handle);
   ptrx.messages.emplace_back(pmsg);
   ptrx.check_size();
   apply_context->release_pending_message(msg_handle);
572 573
}

574
DEFINE_INTRINSIC_FUNCTION1(env,transaction_send,transaction_send,none,i32,handle) {
575 576 577 578 579 580
   auto apply_context  = wasm_interface::get().current_apply_context;
   auto& ptrx = apply_context->get_pending_transaction(handle);

   EOS_ASSERT(ptrx.messages.size() > 0, tx_unknown_argument,
      "Attempting to send a transaction with no messages");

581 582 583
   EOS_ASSERT(false, api_not_supported,
      "transaction_send is unsupported in this release");

584 585 586 587
   apply_context->deferred_transactions.emplace_back(ptrx);
   apply_context->release_pending_transaction(handle);
}

588
DEFINE_INTRINSIC_FUNCTION1(env,transaction_drop,transaction_drop,none,i32,handle) {
589
   wasm_interface::get().current_apply_context->release_pending_transaction(handle);
590 591
}

592
DEFINE_INTRINSIC_FUNCTION4(env,message_create,message_create,i32,i64,code,i64,type,i32,data,i32,length) {
593 594
   auto& wasm  = wasm_interface::get();
   auto  mem   = wasm.current_memory;
595
   
596 597
   EOS_ASSERT( length >= 0, tx_unknown_argument,
      "Pushing a message with a negative length" );
598

599
   bytes payload;
600 601 602 603 604 605 606 607 608 609
   if (length > 0) {
      try {
         // memoryArrayPtr checks that the entire array of bytes is valid and
         // within the bounds of the memory segment so that transactions cannot pass
         // bad values in attempts to read improper memory
         const char* buffer = memoryArrayPtr<const char>( mem, uint32_t(data), uint32_t(length) );
         payload.insert(payload.end(), buffer, buffer + length);
      } catch( const Runtime::Exception& e ) {
         FC_THROW_EXCEPTION(tx_unknown_argument, "Message data is not a valid memory range");
      }
610
   }
611

612
   auto& pmsg = wasm.current_apply_context->create_pending_message(name(code), name(type), payload);
613
   return pmsg.handle;
614 615
}

616
DEFINE_INTRINSIC_FUNCTION3(env,message_require_permission,message_require_permission,none,i32,handle,i64,account,i64,permission) {
617
   auto apply_context  = wasm_interface::get().current_apply_context;
618 619
   // if this is not sent from the code account with the permission of "code" then we must
   // presently have the permission to add it, otherwise its a failure
620 621
   if (!(account == apply_context->code.value && name(permission) == name("code"))) {
      apply_context->require_authorization(name(account), name(permission));
622
   }
623
   auto& pmsg = apply_context->get_pending_message(handle);
624
   pmsg.authorization.emplace_back(name(account), name(permission));
D
Daniel Larimer 已提交
625
}
626

627
DEFINE_INTRINSIC_FUNCTION1(env,message_send,message_send,none,i32,handle) {
628
   auto apply_context  = wasm_interface::get().current_apply_context;
629
   auto& pmsg = apply_context->get_pending_message(handle);
630

631 632
   apply_context->inline_messages.emplace_back(pmsg);
   apply_context->release_pending_message(handle);
633 634
}

635
DEFINE_INTRINSIC_FUNCTION1(env,message_drop,message_drop,none,i32,handle) {
636
   wasm_interface::get().current_apply_context->release_pending_message(handle);
637 638 639
}

/**
640
 * @} transaction C API implementation
641
 */ 
642 643 644



645
DEFINE_INTRINSIC_FUNCTION2(env,read_message,read_message,i32,i32,destptr,i32,destsize) {
646
   FC_ASSERT( destsize > 0 );
647

648 649
   wasm_interface& wasm = wasm_interface::get();
   auto  mem   = wasm.current_memory;
650
   char* begin = memoryArrayPtr<char>( mem, destptr, uint32_t(destsize) );
651 652

   int minlen = std::min<int>(wasm.current_validate_context->msg.data.size(), destsize);
653

D
Daniel Larimer 已提交
654
//   wdump((destsize)(wasm.current_validate_context->msg.data.size()));
655 656 657 658
   memcpy( begin, wasm.current_validate_context->msg.data.data(), minlen );
   return minlen;
}

659
DEFINE_INTRINSIC_FUNCTION2(env,assert,assert,none,i32,test,i32,msg) {
660 661
   const char* m = &Runtime::memoryRef<char>( wasm_interface::get().current_memory, msg );
  std::string message( m );
662
  if( !test ) edump((message));
663
  FC_ASSERT( test, "assertion failed: ${s}", ("s",message)("ptr",msg) );
664
}
665

666
DEFINE_INTRINSIC_FUNCTION0(env,message_size,message_size,i32) {
667 668 669
   return wasm_interface::get().current_validate_context->msg.data.size();
}

670
DEFINE_INTRINSIC_FUNCTION1(env,malloc,malloc,i32,i32,size) {
671 672 673 674 675 676 677 678
   FC_ASSERT( size > 0 );
   int32_t& end = Runtime::memoryRef<int32_t>( Runtime::getDefaultMemory(wasm_interface::get().current_module), 0);
   int32_t old_end = end;
   end += 8*((size+7)/8);
   FC_ASSERT( end > old_end );
   return old_end;
}

679
DEFINE_INTRINSIC_FUNCTION1(env,printi,printi,none,i64,val) {
680
  std::cerr << uint64_t(val);
681
}
M
Matias Romeo 已提交
682 683 684
DEFINE_INTRINSIC_FUNCTION1(env,printd,printd,none,i64,val) {
  std::cerr << DOUBLE(*reinterpret_cast<double *>(&val));
}
685

686 687 688
DEFINE_INTRINSIC_FUNCTION1(env,printi128,printi128,none,i32,val) {
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;
689 690 691
  auto& value = memoryRef<unsigned __int128>( mem, val );
  fc::uint128_t v(value>>64, uint64_t(value) );
  std::cerr << fc::variant(v).get_string();
692 693
}
DEFINE_INTRINSIC_FUNCTION1(env,printn,printn,none,i64,val) {
694
  std::cerr << name(val).to_string();
695
}
696

697
DEFINE_INTRINSIC_FUNCTION1(env,prints,prints,none,i32,charptr) {
698 699 700
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;

701
  const char* str = &memoryRef<const char>( mem, charptr );
702

D
Daniel Larimer 已提交
703
  std::cerr << std::string( str, strnlen(str, wasm.current_state->mem_end-charptr) );
704 705
}

A
Andrianto Lie 已提交
706 707 708 709 710 711 712 713 714
DEFINE_INTRINSIC_FUNCTION2(env,prints_l,prints_l,none,i32,charptr,i32,len) {
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;

  const char* str = &memoryRef<const char>( mem, charptr );

  std::cerr << std::string( str, len );
}

M
Matias Romeo 已提交
715 716 717
DEFINE_INTRINSIC_FUNCTION2(env,printhex,printhex,none,i32,data,i32,datalen) {
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;
A
Andrianto Lie 已提交
718

M
Matias Romeo 已提交
719
  char* buff = memoryArrayPtr<char>(mem, data, datalen);
720
  std::cerr << fc::to_hex(buff, datalen);
M
Matias Romeo 已提交
721 722 723
}


724
DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
725 726
}

B
Brian Johnson 已提交
727 728 729 730 731 732 733 734 735
DEFINE_INTRINSIC_FUNCTION2(env,account_balance_get,account_balance_get,i32,i32,charptr,i32,len) {
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;

  const uint32_t account_balance_size = sizeof(account_balance);
  FC_ASSERT( len == account_balance_size, "passed in len ${len} is not equal to the size of an account_balance struct == ${real_len}", ("len",len)("real_len",account_balance_size) );

  account_balance& total_balance = memoryRef<account_balance>( mem, charptr );

736 737
  wasm.current_apply_context->require_scope(total_balance.account);

B
Brian Johnson 已提交
738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
  auto& db = wasm.current_apply_context->db;
  auto* balance        = db.find< balance_object,by_owner_name >( total_balance.account );
  auto* staked_balance = db.find<staked_balance_object,by_owner_name>( total_balance.account );

  if (balance == nullptr || staked_balance == nullptr)
     return false;

  total_balance.eos_balance          = asset(balance->balance, EOS_SYMBOL);
  total_balance.staked_balance       = asset(staked_balance->staked_balance);
  total_balance.unstaking_balance    = asset(staked_balance->unstaking_balance);
  total_balance.last_unstaking_time  = staked_balance->last_unstaking_time;

  return true;
}

753 754
   wasm_interface& wasm_interface::get() {
      static wasm_interface*  wasm = nullptr;
755
      if( !wasm )
756 757
      {
         wlog( "Runtime::init" );
758
         Runtime::init();
759 760 761 762 763 764 765 766 767 768 769 770 771
         wasm = new wasm_interface();
      }
      return *wasm;
   }



   struct RootResolver : Runtime::Resolver
   {
      std::map<std::string,Resolver*> moduleNameToResolverMap;

     bool resolve(const std::string& moduleName,const std::string& exportName,ObjectType type,ObjectInstance*& outObject) override
     {
772 773 774 775
         // Try to resolve an intrinsic first.
         if(IntrinsicResolver::singleton.resolve(moduleName,exportName,type,outObject)) { return true; }
         FC_ASSERT( !"unresolvable", "${module}.${export}", ("module",moduleName)("export",exportName) );
         return false;
776 777 778
     }
   };

779 780 781 782 783
   int64_t wasm_interface::current_execution_time()
   {
      return (fc::time_point::now() - checktimeStart).count();
   }

784 785

   char* wasm_interface::vm_allocate( int bytes ) {
786 787
      FunctionInstance* alloc_function = asFunctionNullable(getInstanceExport(current_module,"alloc"));
      const FunctionType* functionType = getFunctionType(alloc_function);
788
      FC_ASSERT( functionType->parameters.size() == 1 );
789
      std::vector<Value> invokeArgs(1);
790 791
      invokeArgs[0] = U32(bytes);

792
      checktimeStart = fc::time_point::now();
793

794 795 796 797 798 799 800 801 802
      auto result = Runtime::invokeFunction(alloc_function,invokeArgs);

      return &memoryRef<char>( current_memory, result.i32 );
   }

   U32 wasm_interface::vm_pointer_to_offset( char* ptr ) {
      return U32(ptr - &memoryRef<char>(current_memory,0));
   }

D
Daniel Larimer 已提交
803
   void  wasm_interface::vm_call( const char* name ) {
804
   try {
B
Brian Johnson 已提交
805
      std::unique_ptr<wasm_memory> wasm_memory_mgmt;
806
      try {
D
Daniel Larimer 已提交
807
         /*
808
         name += "_" + std::string( current_validate_context->msg.code ) + "_";
809
         name += std::string( current_validate_context->msg.type );
D
Daniel Larimer 已提交
810 811 812
         */
         /// TODO: cache this somehow
         FunctionInstance* call = asFunctionNullable(getInstanceExport(current_module,name) );
813 814 815 816
         if( !call ) {
            //wlog( "unable to find call ${name}", ("name",name));
            return;
         }
D
Daniel Larimer 已提交
817
         //FC_ASSERT( apply, "no entry point found for ${call}", ("call", std::string(name))  );
818

D
Daniel Larimer 已提交
819
         FC_ASSERT( getFunctionType(call)->parameters.size() == 2 );
820

D
Daniel Larimer 已提交
821
  //       idump((current_validate_context->msg.code)(current_validate_context->msg.type)(current_validate_context->code));
822
         std::vector<Value> args = { Value(uint64_t(current_validate_context->msg.code)),
D
Daniel Larimer 已提交
823
                                     Value(uint64_t(current_validate_context->msg.type)) };
824 825 826 827 828 829

         auto& state = *current_state;
         char* memstart = &memoryRef<char>( current_memory, 0 );
         memset( memstart + state.mem_end, 0, ((1<<16) - state.mem_end) );
         memcpy( memstart, state.init_memory.data(), state.mem_end);

830
         checktimeStart = fc::time_point::now();
B
Brian Johnson 已提交
831
         wasm_memory_mgmt.reset(new wasm_memory(*this));
832

D
Daniel Larimer 已提交
833
         Runtime::invokeFunction(call,args);
B
Brian Johnson 已提交
834
         wasm_memory_mgmt.reset();
835
         checktime(current_execution_time(), checktime_limit);
836 837
      } catch( const Runtime::Exception& e ) {
          edump((std::string(describeExceptionCause(e.cause))));
838 839
          edump((e.callStack));
          throw;
840
      }
841 842
   } FC_CAPTURE_AND_RETHROW( (name)(current_validate_context->msg.type) ) }

D
Daniel Larimer 已提交
843
   void  wasm_interface::vm_apply()        { vm_call("apply" );          }
844

845
   void  wasm_interface::vm_onInit()
846 847
   { try {
      try {
848 849 850 851 852
         FunctionInstance* apply = asFunctionNullable(getInstanceExport(current_module,"init"));
         if( !apply ) {
            elog( "no onInit method found" );
            return; /// if not found then it is a no-op
         }
853

854
         checktimeStart = fc::time_point::now();
855

856 857
         const FunctionType* functionType = getFunctionType(apply);
         FC_ASSERT( functionType->parameters.size() == 0 );
858

859
         std::vector<Value> args(0);
860

861
         Runtime::invokeFunction(apply,args);
862
      } catch( const Runtime::Exception& e ) {
863 864 865
         edump((std::string(describeExceptionCause(e.cause))));
         edump((e.callStack));
         throw;
866
      }
867
   } FC_CAPTURE_AND_RETHROW() }
868

869
   void wasm_interface::validate( apply_context& c ) {
D
Daniel Larimer 已提交
870
      /*
871 872 873 874
      current_validate_context       = &c;
      current_precondition_context   = nullptr;
      current_apply_context          = nullptr;

D
Daniel Larimer 已提交
875
      load( c.code, c.db );
876
      vm_validate();
D
Daniel Larimer 已提交
877
      */
878
   }
879
   void wasm_interface::precondition( apply_context& c ) {
880 881
   try {

D
Daniel Larimer 已提交
882
      /*
883 884 885
      current_validate_context       = &c;
      current_precondition_context   = &c;

D
Daniel Larimer 已提交
886
      load( c.code, c.db );
887
      vm_precondition();
D
Daniel Larimer 已提交
888
      */
889 890

   } FC_CAPTURE_AND_RETHROW() }
891 892


893
   void wasm_interface::apply( apply_context& c, uint32_t execution_time, bool received_block ) {
894
    try {
895 896 897
      current_validate_context       = &c;
      current_precondition_context   = &c;
      current_apply_context          = &c;
898
      checktime_limit                = execution_time;
899

D
Daniel Larimer 已提交
900
      load( c.code, c.db );
901 902 903 904
      // if this is a received_block, then ignore the table_key_types
      if (received_block)
         table_key_types = nullptr;

905 906 907 908 909 910 911 912 913 914 915
      auto rate_limiting = c.db.find<rate_limiting_object, by_name>(c.code);
      if (rate_limiting == nullptr)
      {
         c.mutable_db.create<rate_limiting_object>([code=c.code](rate_limiting_object& rlo) {
            rlo.name = code;
            rlo.per_code_account_db_bytes = 0;
         });
      }
      else
         table_storage = rate_limiting->per_code_account_db_bytes;

916 917
      vm_apply();

918
      EOS_ASSERT(table_storage <= (per_code_account_max_db_limit_mbytes * bytes_per_mbyte), tx_code_db_limit_exceeded, "Database limit exceeded for account=${name}",("name", c.code));
919

920 921 922 923
      rate_limiting = c.db.find<rate_limiting_object, by_name>(c.code);
      c.mutable_db.modify(*rate_limiting, [storage=this->table_storage] (rate_limiting_object& rlo) {
         rlo.per_code_account_db_bytes = storage;
      });
924 925 926 927
   } FC_CAPTURE_AND_RETHROW() }

   void wasm_interface::init( apply_context& c ) {
    try {
928 929 930
      current_validate_context       = &c;
      current_precondition_context   = &c;
      current_apply_context          = &c;
931
      checktime_limit                = CHECKTIME_LIMIT;
932

D
Daniel Larimer 已提交
933
      load( c.code, c.db );
934 935 936 937 938 939 940 941 942 943 944 945

      auto rate_limiting = c.db.find<rate_limiting_object, by_name>(c.code);
      if (rate_limiting == nullptr)
      {
         c.mutable_db.create<rate_limiting_object>([code=c.code](rate_limiting_object& rlo) {
            rlo.name = code;
            rlo.per_code_account_db_bytes = 0;
         });
      }
      else
         table_storage = rate_limiting->per_code_account_db_bytes;

946
      vm_onInit();
947

948 949 950 951 952 953
      EOS_ASSERT(table_storage <= (per_code_account_max_db_limit_mbytes * bytes_per_mbyte), tx_code_db_limit_exceeded, "Database limit exceeded for account=${name}",("name", c.code));

      rate_limiting = c.db.find<rate_limiting_object, by_name>(c.code);
      c.mutable_db.modify(*rate_limiting, [storage=this->table_storage] (rate_limiting_object& rlo) {
         rlo.per_code_account_db_bytes = storage;
      });
954 955
   } FC_CAPTURE_AND_RETHROW() }

956

957

958
   void wasm_interface::load( const account_name& name, const chainbase::database& db ) {
959
      const auto& recipient = db.get<account_object,by_name>( name );
960
  //    idump(("recipient")(name(name))(recipient.code_version));
961 962 963

      auto& state = instances[name];
      if( state.code_version != recipient.code_version ) {
964 965
        if( state.instance ) {
           /// TODO: free existing instance and module
966
#warning TODO: free existing module if the code has been updated, currently leak memory
967 968 969 970 971 972
           state.instance     = nullptr;
           state.module       = nullptr;
           state.code_version = fc::sha256();
        }
        state.module = new IR::Module();
        state.table_key_types.clear();
973 974 975

        try
        {
976
//          wlog( "LOADING CODE" );
977
          const auto start = fc::time_point::now();
978
          Serialization::MemoryInputStream stream((const U8*)recipient.code.data(),recipient.code.size());
979
          WASM::serializeWithInjection(stream,*state.module);
980 981 982 983 984

          RootResolver rootResolver;
          LinkResult linkResult = linkModule(*state.module,rootResolver);
          state.instance = instantiateModule( *state.module, std::move(linkResult.resolvedImports) );
          FC_ASSERT( state.instance );
985
          const auto llvm_time = fc::time_point::now();
986

987
          current_memory = Runtime::getDefaultMemory(state.instance);
988
            
989
          char* memstart = &memoryRef<char>( current_memory, 0 );
990
         // state.init_memory.resize(1<<16); /// TODO: actually get memory size
991 992 993 994 995 996 997 998
          const auto allocated_memory = Runtime::getDefaultMemorySize(state.instance);
          for( uint64_t i = 0; i < allocated_memory; ++i )
          {
             if( memstart[i] )
             {
                state.mem_end = i+1;
             }
          }
999
          //ilog( "INIT MEMORY: ${size}", ("size", state.mem_end) );
1000 1001 1002

          state.init_memory.resize(state.mem_end);
          memcpy( state.init_memory.data(), memstart, state.mem_end ); //state.init_memory.size() );
1003
          //std::cerr <<"\n";
1004
          state.code_version = recipient.code_version;
1005
//          idump((state.code_version));
1006
          const auto init_time = fc::time_point::now();
1007

1008 1009
          types::abi abi;
          if( types::abi_serializer::to_abi(recipient.abi, abi) )
1010 1011 1012 1013
          {
             state.tables_fixed = true;
             for(auto& table : abi.tables)
             {
1014
                const auto key_type = to_key_type(table.index_type);
1015
                if (key_type == invalid_key_type)
1016 1017
                   throw Serialization::FatalSerializationException("For code \"" + (std::string)name + "\" index_type of \"" +
                                                                    table.index_type + "\" referenced but not supported");
1018

1019
                state.table_key_types.emplace(std::make_pair(table.table_name, key_type));
1020 1021
             }
          }
1022
          ilog("wasm_interface::load times llvm:${llvm} ms, init:${init} ms, abi:${abi} ms",
B
Brian Johnson 已提交
1023
               ("llvm",(llvm_time-start).count()/1000)("init",(init_time-llvm_time).count()/1000)("abi",(fc::time_point::now()-init_time).count()/1000));
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042
        }
        catch(Serialization::FatalSerializationException exception)
        {
          std::cerr << "Error deserializing WebAssembly binary file:" << std::endl;
          std::cerr << exception.message << std::endl;
          throw;
        }
        catch(IR::ValidationException exception)
        {
          std::cerr << "Error validating WebAssembly binary file:" << std::endl;
          std::cerr << exception.message << std::endl;
          throw;
        }
        catch(std::bad_alloc)
        {
          std::cerr << "Memory allocation failed: input is likely malformed" << std::endl;
          throw;
        }
      }
1043 1044 1045 1046 1047
      current_module      = state.instance;
      current_memory      = getDefaultMemory( current_module );
      current_state       = &state;
      table_key_types     = &state.table_key_types;
      tables_fixed        = state.tables_fixed;
1048
      table_storage       = 0;
1049
   }
1050

B
Brian Johnson 已提交
1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
   wasm_memory::wasm_memory(wasm_interface& interface)
   : _wasm_interface(interface)
   , _num_pages(Runtime::getMemoryNumPages(interface.current_memory))
   , _min_bytes(limit_32bit_address(_num_pages << numBytesPerPageLog2))
   {
      _wasm_interface.current_memory_management = this;
      _num_bytes = _min_bytes;
   }

   wasm_memory::~wasm_memory()
   {
      if (_num_bytes > _min_bytes)
         sbrk((I32)_min_bytes - (I32)_num_bytes);

      _wasm_interface.current_memory_management = nullptr;
   }

   U32 wasm_memory::sbrk(I32 num_bytes)
   {
      const U32 previous_num_bytes = _num_bytes;
      if(Runtime::getMemoryNumPages(_wasm_interface.current_memory) != _num_pages)
P
Pravin 已提交
1072
         throw eosio::chain::page_memory_error();
B
Brian Johnson 已提交
1073 1074 1075 1076

      // Round the absolute value of num_bytes to an alignment boundary, and ensure it won't allocate too much or too little memory.
      num_bytes = (num_bytes + 7) & ~7;
      if(num_bytes > 0 && previous_num_bytes > _max_memory - num_bytes)
P
Pravin 已提交
1077
         throw eosio::chain::page_memory_error();
B
Brian Johnson 已提交
1078
      else if(num_bytes < 0 && previous_num_bytes < _min_bytes - num_bytes)
P
Pravin 已提交
1079
         throw eosio::chain::page_memory_error();
B
Brian Johnson 已提交
1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100

      // Update the number of bytes allocated, and compute the number of pages needed for it.
      _num_bytes += num_bytes;
      const Uptr num_desired_pages = (_num_bytes + IR::numBytesPerPage - 1) >> IR::numBytesPerPageLog2;

      // Grow or shrink the memory object to the desired number of pages.
      if(num_desired_pages > _num_pages)
         Runtime::growMemory(_wasm_interface.current_memory, num_desired_pages - _num_pages);
      else if(num_desired_pages < _num_pages)
         Runtime::shrinkMemory(_wasm_interface.current_memory, _num_pages - num_desired_pages);

      _num_pages = num_desired_pages;

      return previous_num_bytes;
   }

   U32 wasm_memory::limit_32bit_address(Uptr address)
   {
      return (U32)(address > UINT32_MAX ? UINT32_MAX : address);
   }

1101
} }