wasm_interface.cpp 41.0 KB
Newer Older
B
Bart Wyatt 已提交
1 2
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/apply_context.hpp>
3
#include <eosio/chain/chain_controller.hpp>
4
#include <eosio/chain/exceptions.hpp>
D
Daniel Larimer 已提交
5
#include <boost/core/ignore_unused.hpp>
6 7
#include <eosio/chain/wasm_interface_private.hpp>
#include <fc/exception/exception.hpp>
8
#include <fc/crypto/sha1.hpp>
9
#include <fc/io/raw.hpp>
10
#include <fc/utf8.hpp>
D
Daniel Larimer 已提交
11 12 13

#include <Runtime/Runtime.h>
#include "IR/Module.h"
14 15 16 17
#include "Platform/Platform.h"
#include "WAST/WAST.h"
#include "IR/Operators.h"
#include "IR/Validate.h"
18
#include "IR/Types.h"
19 20 21
#include "Runtime/Runtime.h"
#include "Runtime/Linker.h"
#include "Runtime/Intrinsics.h"
D
Daniel Larimer 已提交
22

23 24 25
#include <boost/asio.hpp>
#include <boost/bind.hpp>

26
#include <mutex>
27 28 29
#include <thread>
#include <condition_variable>

30

31

32 33
using namespace IR;
using namespace Runtime;
34
using boost::asio::io_service;
D
Daniel Larimer 已提交
35

36
namespace eosio { namespace chain {
37
   using namespace contracts;
38 39
   using namespace webassembly;
   using namespace webassembly::common;
40

41 42 43 44 45 46 47 48
   /**
    *  Implementation class for the wasm cache
    *  it is responsible for compiling and storing instances of wasm code for use
    *
    */
   struct wasm_cache_impl {
      wasm_cache_impl()
      {
49
         Runtime::init();
50
      }
M
Matias Romeo 已提交
51

52 53 54 55 56 57 58 59
      /**
       * this must wait for all work to be done otherwise it may destroy memory
       * referenced by other threads
       *
       * Expectations on wasm_cache dictate that all available code has been
       * returned before this can be destroyed
       */
      ~wasm_cache_impl() {
60
         freeUnreferencedObjects({});
61
      }
M
Matias Romeo 已提交
62

63 64 65 66 67 68 69 70 71 72 73 74 75
      /**
       * internal tracking structure which deduplicates memory images
       * and tracks available vs in-use entries.
       *
       * The instances array has two sections, "available" instances
       * are in the front of the vector and anything at an index of
       * available_instances or greater is considered "in use"
       *
       * instances are stored as pointers so that their positions
       * in the array can be moved without invaliding references to
       * the instance handed out to other threads
       */
      struct code_info {
76 77
         explicit code_info(wavm::info&& wavm_info) :wavm_info(wavm_info) {}
         wavm::info   wavm_info;
78 79 80 81 82 83

         // all existing instances of this code
         vector<unique_ptr<wasm_cache::entry>> instances;
         size_t available_instances = 0;
      };

84 85 86
      using optional_info_ref = optional<std::reference_wrapper<code_info>>;
      using optional_entry_ref = optional<std::reference_wrapper<wasm_cache::entry>>;

87 88 89 90 91 92 93 94
      /**
       * Convenience method for running code with the _cache_lock and releaseint that lock
       * when the code completes
       *
       * @param f - lambda to execute
       * @return - varies depending on the signature of the lambda
       */
      template<typename F>
95 96
      auto with_lock(std::mutex &l, F f) {
         std::lock_guard<std::mutex> lock(l);
97 98 99 100 101 102 103 104 105
         return f();
      };

      /**
       * Fetch the tracking struct given a code_id if it exists
       *
       * @param code_id
       * @return
       */
106
      optional_info_ref fetch_info(const digest_type& code_id) {
107
         return with_lock(_cache_lock, [&,this](){
108 109
            auto iter = _cache.find(code_id);
            if (iter != _cache.end()) {
110
               return optional_info_ref(iter->second);
111 112
            }

113
            return optional_info_ref();
114 115
         });
      }
M
Matias Romeo 已提交
116

117 118 119 120 121
      /**
       * Opportunistically fetch an available instance of the code;
       * @param code_id - the id of the code to fetch
       * @return - reference to the entry when one is available
       */
122
      optional_entry_ref try_fetch_entry(const digest_type& code_id) {
123
         return with_lock(_cache_lock, [&,this](){
124 125
            auto iter = _cache.find(code_id);
            if (iter != _cache.end() && iter->second.available_instances > 0) {
126
               auto &ptr = iter->second.instances.at(--(iter->second.available_instances));
127
               return optional_entry_ref(*ptr);
128 129
            }

130
            return optional_entry_ref();
131
         });
D
Daniel Larimer 已提交
132
      }
M
Matias Romeo 已提交
133

134 135 136 137 138 139 140 141 142 143 144 145
      /**
       * Fetch a copy of the code, this is guaranteed to return an entry IF the code is compilable.
       * In order to do that in safe way this code may cause the calling thread to sleep while a new
       * version of the code is compiled and inserted into the cache
       *
       * @param code_id - the id of the code to fetch
       * @param wasm_binary - the binary for the wasm
       * @param wasm_binary_size - the size of the binary
       * @return reference to a usable cache entry
       */
      wasm_cache::entry& fetch_entry(const digest_type& code_id, const char* wasm_binary, size_t wasm_binary_size) {
         std::condition_variable condition;
146
         optional_entry_ref result;
147 148 149 150
         std::exception_ptr error;

         // compilation is not thread safe, so we dispatch it to a io_service running on a single thread to
         // queue up and synchronize compilations
151
         with_lock(_compile_lock, [&,this](){
152 153 154 155 156 157
            // check to see if someone returned what we need before making a new one
            auto pending_result = try_fetch_entry(code_id);
            std::exception_ptr pending_error;

            if (!pending_result) {
               // time to compile a brand new (maybe first) copy of this code
B
Bart Wyatt 已提交
158

159 160
               fc::optional<wavm::entry> wavm;
               fc::optional<wavm::info> wavm_info;
161 162

               try {
B
Bart Wyatt 已提交
163
                  /// TODO: make validation generic
164 165
                  wavm = wavm::entry::build(wasm_binary, wasm_binary_size);
                  wavm_info.emplace(*wavm);
166
                  
167 168 169 170 171 172
               } catch (...) {
                  pending_error = std::current_exception();
               }

               if (pending_error == nullptr) {
                  // grab the lock and put this in the cache as unavailble
173
                  with_lock(_cache_lock, [&,this]() {
174
                     // find or create a new entry
175
                     auto iter = _cache.emplace(code_id, code_info(std::move(*wavm_info))).first;
176

177
                     iter->second.instances.emplace_back(std::make_unique<wasm_cache::entry>(std::move(*wavm)));
178
                     pending_result = optional_entry_ref(*iter->second.instances.back().get());
179 180 181 182
                  });
               }
            }

183 184 185 186 187
           if (pending_error != nullptr) {
              error = pending_error;
           } else {
              result = pending_result;
           }
188

189 190
         });

191

192 193 194 195
         try {
            if (error != nullptr) {
               std::rethrow_exception(error);
            } else {
196
               return (*result).get();
197 198
            }
         } FC_RETHROW_EXCEPTIONS(error, "error compiling WASM for code with hash: ${code_id}", ("code_id", code_id));
D
Daniel Larimer 已提交
199
      }
200

201 202 203 204 205 206 207 208 209
      /**
       * return an entry to the cache.  The entry is presumed to come back in a "dirty" state and must be
       * sanitized before returning to the "available" state.  This sanitization is done asynchronously so
       * as not to delay the current executing thread.
       *
       * @param code_id - the code Id associated with the instance
       * @param entry - the entry to return
       */
      void return_entry(const digest_type& code_id, wasm_cache::entry& entry) {
210 211
        // sanitize by reseting the memory that may now be dirty
        auto& info = (*fetch_info(code_id)).get();
212
        entry.wavm.reset(info.wavm_info);
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229

        // under a lock, put this entry back in the available instances side of the instances vector
        with_lock(_cache_lock, [&,this](){
           // walk the vector and find this entry
           auto iter = info.instances.begin();
           while (iter->get() != &entry) {
              ++iter;
           }

           FC_ASSERT(iter != info.instances.end(), "Checking in a WASM enty that was not created properly!");

           auto first_unavailable = (info.instances.begin() + info.available_instances);
           if (iter != first_unavailable) {
              std::swap(iter, first_unavailable);
           }
           info.available_instances++;
        });
D
Daniel Larimer 已提交
230
      }
231

232 233 234
      // mapping of digest to an entry for the code
      map<digest_type, code_info> _cache;
      std::mutex _cache_lock;
B
Brian Johnson 已提交
235

236 237
      // compilation lock
      std::mutex _compile_lock;
238
   };
B
Brian Johnson 已提交
239

240 241 242
   wasm_cache::wasm_cache()
      :_my( new wasm_cache_impl() ) {
   }
243

244 245
   wasm_cache::~wasm_cache() = default;

246
   wasm_cache::entry &wasm_cache::checkout( const digest_type& code_id, const char* wasm_binary, size_t wasm_binary_size ) {
247
      // see if there is an available entry in the cache
248
      auto result = _my->try_fetch_entry(code_id);
249
      if (result) {
250
         return (*result).get();
D
Daniel Larimer 已提交
251
      }
252 253 254 255 256
      return _my->fetch_entry(code_id, wasm_binary, wasm_binary_size);
   }


   void wasm_cache::checkin(const digest_type& code_id, entry& code ) {
257
      MemoryInstance* default_mem = Runtime::getDefaultMemory(code.wavm.instance);
258 259
      if(default_mem)
         Runtime::shrinkMemory(default_mem, Runtime::getMemoryNumPages(default_mem) - 1);
260
      _my->return_entry(code_id, code);
261
   }
262

263 264 265 266 267
   /**
    * RAII wrapper to make sure that the context is cleaned up on exception
    */
   struct scoped_context {
      template<typename ...Args>
B
Bart Wyatt 已提交
268
      scoped_context(optional<wasm_context> &context, Args&&... args)
269 270
      :context(context)
      {
271
         context.emplace( std::forward<Args>(args)... );
272 273 274 275 276 277 278 279 280
      }

      ~scoped_context() {
         context.reset();
      }

      optional<wasm_context>& context;
   };

281 282 283 284
   wasm_interface::wasm_interface()
      :my( new wasm_interface_impl() ) {
   }

285 286 287 288 289 290 291 292
   wasm_interface& wasm_interface::get() {
      thread_local wasm_interface* single = nullptr;
      if( !single ) {
         single = new wasm_interface();
      }
      return *single;
   }

293
   void wasm_interface::apply( wasm_cache::entry& code, apply_context& context ) {
B
Bart Wyatt 已提交
294
      auto context_guard = scoped_context(my->current_context, code, context);
295
      code.wavm.call_apply(context);
296 297 298
   }

   void wasm_interface::error( wasm_cache::entry& code, apply_context& context ) {
B
Bart Wyatt 已提交
299
      auto context_guard = scoped_context(my->current_context, code, context);
300
      code.wavm.call_error(context);
301 302
   }

303 304 305 306 307
   wasm_context& common::intrinsics_accessor::get_context(wasm_interface &wasm) {
      FC_ASSERT(wasm.my->current_context.valid());
      return *wasm.my->current_context;
   }

308 309
   const wavm::entry& wavm::entry::get(wasm_interface& wasm) {
      return common::intrinsics_accessor::get_context(wasm).code.wavm;
310 311
   }

312
#if defined(assert)
313
   #undef assert
314
#endif
315

B
Bart Wyatt 已提交
316
class context_aware_api {
317
   public:
B
Bart Wyatt 已提交
318
      context_aware_api(wasm_interface& wasm)
B
Bart Wyatt 已提交
319
      :context(intrinsics_accessor::get_context(wasm).context), code(intrinsics_accessor::get_context(wasm).code)
320
      {}
321

B
Bart Wyatt 已提交
322
   protected:
B
Bucky Kittinger 已提交
323
      wasm_cache::entry& code;
B
Bucky Kittinger 已提交
324
      apply_context&     context;
B
Bart Wyatt 已提交
325
};
326

D
Daniel Larimer 已提交
327 328
class privileged_api : public context_aware_api {
   public:
329 330 331 332 333
      privileged_api( wasm_interface& wasm )
      :context_aware_api(wasm)
      {
         FC_ASSERT( context.privileged, "${code} does not have permission to call this API", ("code",context.receiver) );
      }
334

D
Daniel Larimer 已提交
335 336 337 338 339 340 341
      /**
       *  This should schedule the feature to be activated once the
       *  block that includes this call is irreversible. It should
       *  fail if the feature is already pending.
       *
       *  Feature name should be base32 encoded name. 
       */
342
      void activate_feature( int64_t feature_name ) {
343
         FC_ASSERT( !"Unsupported Harfork Detected" );
D
Daniel Larimer 已提交
344
      }
345

D
Daniel Larimer 已提交
346 347 348 349 350 351
      /**
       * This should return true if a feature is active and irreversible, false if not.
       *
       * Irreversiblity by fork-database is not consensus safe, therefore, this defines
       * irreversiblity only by block headers not by BFT short-cut.
       */
352
      int is_feature_active( int64_t feature_name ) {
D
Daniel Larimer 已提交
353 354
         return false;
      }
355

D
Daniel Larimer 已提交
356
      void set_resource_limits( account_name account, 
357 358
                                int64_t ram_bytes, int64_t net_weight, int64_t cpu_weight,
                                int64_t cpu_usec_per_period ) {
359 360 361 362 363 364 365 366 367 368 369 370
         auto& buo = context.db.get<bandwidth_usage_object,by_owner>( account );
         FC_ASSERT( buo.db_usage <= ram_bytes, "attempt to free to much space" );

         auto& gdp = context.controller.get_dynamic_global_properties();
         context.mutable_db.modify( gdp, [&]( auto& p ) {
           p.total_net_weight -= buo.net_weight;
           p.total_net_weight += net_weight;
           p.total_cpu_weight -= buo.cpu_weight;
           p.total_cpu_weight += cpu_weight;
           p.total_db_reserved -= buo.db_reserved_capacity;
           p.total_db_reserved += ram_bytes;
         });
371

372 373 374 375 376
         context.mutable_db.modify( buo, [&]( auto& o ){
            o.net_weight = net_weight;
            o.cpu_weight = cpu_weight;
            o.db_reserved_capacity = ram_bytes;
         });
D
Daniel Larimer 已提交
377
      }
378

379

D
Daniel Larimer 已提交
380
      void get_resource_limits( account_name account, 
381
                                uint64_t& ram_bytes, uint64_t& net_weight, uint64_t cpu_weight ) {
D
Daniel Larimer 已提交
382 383
      }
                                               
384
      void set_active_producers( array_ptr<char> packed_producer_schedule, size_t datalen) {
385 386 387 388 389 390 391 392
         datastream<const char*> ds( packed_producer_schedule, datalen );
         producer_schedule_type psch;
         fc::raw::unpack(ds, psch);

         context.mutable_db.modify( context.controller.get_global_properties(), 
            [&]( auto& gprops ) {
                 gprops.new_active_producers = psch;
         });
D
Daniel Larimer 已提交
393
      }
394

395 396 397 398 399 400 401 402 403 404 405
      bool is_privileged( account_name n )const {
         return context.db.get<account_object, by_name>( n ).privileged;
      }
      bool is_frozen( account_name n )const {
         return context.db.get<account_object, by_name>( n ).frozen;
      }
      void set_privileged( account_name n, bool is_priv ) {
         const auto& a = context.db.get<account_object, by_name>( n );
         context.mutable_db.modify( a, [&]( auto& ma ){
            ma.privileged = is_priv;
         });
D
Daniel Larimer 已提交
406
      }
407

408 409 410 411 412
      void freeze_account( account_name n , bool should_freeze ) {
         const auto& a = context.db.get<account_object, by_name>( n );
         context.mutable_db.modify( a, [&]( auto& ma ){
            ma.frozen = should_freeze;
         });
D
Daniel Larimer 已提交
413 414 415 416
      }

      /// TODO: add inline/deferred with support for arbitrary permissions rather than code/current auth
};
B
Brian Johnson 已提交
417

418 419 420 421
class checktime_api : public context_aware_api {
public:
   using context_aware_api::context_aware_api;

422 423
   void checktime(uint32_t instruction_count) {
      context.checktime(instruction_count);
B
Brian Johnson 已提交
424
   }
425
};
426

427 428 429
class producer_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;
430

431
      int get_active_producers(array_ptr<chain::account_name> producers, size_t datalen) {
432
         auto active_producers = context.get_active_producers();
433 434 435 436
         size_t len = active_producers.size() * sizeof(chain::account_name);
         size_t cpy_len = std::min(datalen, len);
         memcpy(producers, active_producers.data(), cpy_len);
         return len;
437 438 439 440
      }
};

class crypto_api : public context_aware_api {
441
   public:
442
      using context_aware_api::context_aware_api;
443

444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
      /**
       * This method can be optimized out during replay as it has
       * no possible side effects other than "passing". 
       */
      void assert_recover_key( fc::sha256& digest, 
                        array_ptr<char> sig, size_t siglen,
                        array_ptr<char> pub, size_t publen ) {
         fc::crypto::signature s;
         fc::crypto::public_key p;
         datastream<const char*> ds( sig, siglen );
         datastream<const char*> pubds( pub, publen );

         fc::raw::unpack(ds, s);
         fc::raw::unpack(ds, p);

         auto check = fc::crypto::public_key( s, digest, false );
         FC_ASSERT( check == p, "Error expected key different than recovered key" );
      }

      int recover_key( fc::sha256& digest, 
                        array_ptr<char> sig, size_t siglen,
                        array_ptr<char> pub, size_t publen ) {
         fc::crypto::signature s;
         datastream<const char*> ds( sig, siglen );
         datastream<char*> pubds( pub, publen );

         fc::raw::unpack(ds, s);
         fc::raw::pack( pubds, fc::crypto::public_key( s, digest, false ) );
         return pubds.tellp();
      }

475 476 477 478 479
      void assert_sha256(array_ptr<char> data, size_t datalen, const fc::sha256& hash_val) {
         auto result = fc::sha256::hash( data, datalen );
         FC_ASSERT( result == hash_val, "hash miss match" );
      }

480 481 482 483
      void sha1(array_ptr<char> data, size_t datalen, fc::sha1& hash_val) {
         hash_val = fc::sha1::hash( data, datalen );
      }

484 485 486
      void sha256(array_ptr<char> data, size_t datalen, fc::sha256& hash_val) {
         hash_val = fc::sha256::hash( data, datalen );
      }
487 488 489 490 491 492 493 494

      void sha512(array_ptr<char> data, size_t datalen, fc::sha512& hash_val) {
         hash_val = fc::sha512::hash( data, datalen );
      }

      void ripemd160(array_ptr<char> data, size_t datalen, fc::ripemd160& hash_val) {
         hash_val = fc::ripemd160::hash( data, datalen );
      }
495 496 497 498 499 500 501 502 503 504 505
};

class string_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;

      void assert_is_utf8(array_ptr<const char> str, size_t datalen, null_terminated_ptr msg) {
         const bool test = fc::is_utf8(std::string( str, datalen ));

         FC_ASSERT( test, "assertion failed: ${s}", ("s",msg.value) );
      }
B
Bart Wyatt 已提交
506 507 508 509 510 511
};

class system_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;

512 513
      void abort() {
         edump(("abort() called"));
514
         FC_ASSERT( false, "abort() called");
515 516
      }

517
      void eosio_assert(bool condition, null_terminated_ptr str) {
B
Bart Wyatt 已提交
518 519 520 521
         std::string message( str );
         if( !condition ) edump((message));
         FC_ASSERT( condition, "assertion failed: ${s}", ("s",message));
      }
522 523 524 525

      fc::time_point_sec now() {
         return context.controller.head_block_time();
      }
B
Bart Wyatt 已提交
526 527 528 529 530 531
};

class action_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;

532 533
      int read_action(array_ptr<char> memory, size_t size) {
         FC_ASSERT(size > 0);
534
         int minlen = std::min<size_t>(context.act.data.size(), size);
535 536 537 538
         memcpy((void *)memory, context.act.data.data(), minlen);
         return minlen;
      }

B
Bart Wyatt 已提交
539 540
      int action_size() {
         return context.act.data.size();
541 542
      }

B
Bart Wyatt 已提交
543 544 545
      const name& current_receiver() {
         return context.receiver;
      }
546 547

      fc::time_point_sec publication_time() {
B
Bart Wyatt 已提交
548
         return context.trx_meta.published;
549 550 551
      }

      name current_sender() {
B
Bart Wyatt 已提交
552 553
         if (context.trx_meta.sender) {
            return *context.trx_meta.sender;
554 555 556 557
         } else {
            return name();
         }
      }
558
};
B
Brian Johnson 已提交
559

B
Bart Wyatt 已提交
560 561 562 563
class console_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;

564 565
      void prints(null_terminated_ptr str) {
         context.console_append<const char*>(str);
B
Bart Wyatt 已提交
566 567 568
      }

      void prints_l(array_ptr<const char> str, size_t str_len ) {
569
         context.console_append(string(str, str_len));
B
Bart Wyatt 已提交
570 571 572
      }

      void printi(uint64_t val) {
573
         context.console_append(val);
B
Bart Wyatt 已提交
574 575 576 577
      }

      void printi128(const unsigned __int128& val) {
         fc::uint128_t v(val>>64, uint64_t(val) );
578
         context.console_append(fc::variant(v).get_string());
B
Bart Wyatt 已提交
579 580 581
      }

      void printd( wasm_double val ) {
582
         context.console_append(val.str());
B
Bart Wyatt 已提交
583 584 585
      }

      void printn(const name& value) {
586
         context.console_append(value.to_string());
B
Bart Wyatt 已提交
587 588 589
      }

      void printhex(array_ptr<const char> data, size_t data_len ) {
590
         context.console_append(fc::to_hex(data, data_len));
B
Bart Wyatt 已提交
591 592 593
      }
};

D
Daniel Larimer 已提交
594 595 596 597 598 599 600 601 602 603 604 605 606
class database_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;

      int db_store_i64( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, array_ptr<const char> buffer, size_t buffer_size ) {
         return context.db_store_i64( scope, table, payer, id, buffer, buffer_size );
      }
      void db_update_i64( int itr, uint64_t payer, array_ptr<const char> buffer, size_t buffer_size ) {
         context.db_update_i64( itr, payer, buffer, buffer_size );
      }
      void db_remove_i64( int itr ) {
         context.db_remove_i64( itr );
      }
607 608 609
      int db_get_i64( int itr, array_ptr<char> buffer, size_t buffer_size ) {
         return context.db_get_i64( itr, buffer, buffer_size );
      }
610 611
      int db_next_i64( int itr, uint64_t& primary ) {
         return context.db_next_i64(itr, primary);
612
      }
613 614
      int db_previous_i64( int itr, uint64_t& primary ) {
         return context.db_previous_i64(itr, primary);
D
Daniel Larimer 已提交
615 616 617 618 619 620 621 622 623 624
      }
      int db_find_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) { 
         return context.db_find_i64( code, scope, table, id ); 
      }
      int db_lowerbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) { 
         return context.db_lowerbound_i64( code, scope, table, id ); 
      }
      int db_upperbound_i64( uint64_t code, uint64_t scope, uint64_t table, uint64_t id ) { 
         return context.db_lowerbound_i64( code, scope, table, id ); 
      }
625 626 627 628 629 630 631 632 633 634

      int db_idx64_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const uint64_t& secondary ) {
         return context.idx64.store( scope, table, payer, id, secondary );
      }
      void db_idx64_update( int iterator, uint64_t payer, const uint64_t& secondary ) {
         return context.idx64.update( iterator, payer, secondary );
      }
      void db_idx64_remove( int iterator ) {
         return context.idx64.remove( iterator );
      }
635 636 637 638
      int db_idx64_find_secondary( uint64_t code, uint64_t scope, uint64_t table, uint64_t& secondary, uint64_t& primary ) {
         return context.idx64.find_secondary(code, scope, table, secondary, primary);
      }
      int db_idx64_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t& secondary, uint64_t primary ) {
D
Daniel Larimer 已提交
639
         return context.idx64.find_secondary(code, scope, table, secondary, primary);
640
      }
641

D
Daniel Larimer 已提交
642
      int db_idx64_lowerbound( uint64_t code, uint64_t scope, uint64_t table,  uint64_t& secondary, uint64_t& primary ) {
D
Daniel Larimer 已提交
643
         return context.idx64.lowerbound_secondary(code, scope, table, secondary, primary);
644
      }
D
Daniel Larimer 已提交
645
      int db_idx64_upperbound( uint64_t code, uint64_t scope, uint64_t table,  uint64_t& secondary, uint64_t& primary ) {
D
Daniel Larimer 已提交
646
         return context.idx64.upperbound_secondary(code, scope, table, secondary, primary);
647
      }
648 649
      int db_idx64_next( int iterator, uint64_t& primary  ) {
         return context.idx64.next_secondary(iterator, primary);
650
      }
651
      int db_idx64_previous( int iterator, uint64_t& primary ) {
D
Daniel Larimer 已提交
652
         return context.idx64.previous_secondary(iterator, primary);
653
      }
654

655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
      /*
      int db_idx64_next( int iterator, uint64_t& primary ) {
      }
      int db_idx64_prev( int iterator, uint64_t& primary ) {
      }
      int db_idx64_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint64_t& secondary, uint64_t primary ) {
      }
      int db_idx64_find_secondary( uint64_t code, uint64_t scope, uint64_t table, uint64_t& secondary, uint64_t& primary ) {
      }
      int db_idx64_lowerbound( uint64_t code, uint64_t scope, uint64_t table, uint64_t& secondary, uint64_t& primary ) {
      }
      int db_idx64_upperbound( uint64_t code, uint64_t scope, uint64_t table, uint64_t& secondary, uint64_t& primary ) {
      }
      */


671 672 673 674 675 676 677 678 679
      int db_idx128_store( uint64_t scope, uint64_t table, uint64_t payer, uint64_t id, const uint128_t& secondary ) {
         return context.idx128.store( scope, table, payer, id, secondary );
      }
      void db_idx128_update( int iterator, uint64_t payer, const uint128_t& secondary ) {
         return context.idx128.update( iterator, payer, secondary );
      }
      void db_idx128_remove( int iterator ) {
         return context.idx128.remove( iterator );
      }
680 681 682 683
      int db_idx128_find_primary( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t primary ) {
         return context.idx128.find_primary( code, scope, table, secondary, primary );
      }
      int db_idx128_find_secondary( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t& primary ) {
D
Daniel Larimer 已提交
684
         return context.idx128.find_secondary(code, scope, table, secondary, primary);
685
      }
D
Daniel Larimer 已提交
686 687
      int db_idx128_lowerbound( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t& primary ) {
         return context.idx128.lowerbound_secondary(code, scope, table, secondary, primary);
688
      }
D
Daniel Larimer 已提交
689
      int db_idx128_upperbound( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t& primary ) {
D
Daniel Larimer 已提交
690
         return context.idx128.upperbound_secondary(code, scope, table, secondary, primary);
691
      }
692 693
      int db_idx128_next( int iterator, uint64_t& primary ) {
         return context.idx128.next_secondary(iterator, primary);
694
      }
695 696
      int db_idx128_previous( int iterator, uint64_t& primary ) {
         return context.idx128.previous_secondary(iterator, primary);
697
      }
698 699 700 701 702 703 704 705 706 707 708 709 710

   /*
      int db_idx128_next( int iterator, uint64_t& primary ) {
      }
      int db_idx128_prev( int iterator, uint64_t& primary ) {
      }
      int db_idx128_find_secondary( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t& primary ) {
      }
      int db_idx128_lowerbound( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t& primary ) {
      }
      int db_idx128_upperbound( uint64_t code, uint64_t scope, uint64_t table, uint128_t& secondary, uint64_t& primary ) {
      }
      */
D
Daniel Larimer 已提交
711 712 713 714
};



715 716 717 718 719
template<typename ObjectType>
class db_api : public context_aware_api {
   using KeyType = typename ObjectType::key_type;
   static constexpr int KeyCount = ObjectType::number_of_keys;
   using KeyArrayType = KeyType[KeyCount];
720
   using ContextMethodType = int(apply_context::*)(const table_id_object&, const account_name&, const KeyType*, const char*, size_t);
721 722

   private:
723 724
      int call(ContextMethodType method, const scope_name& scope, const name& table, account_name bta, array_ptr<const char> data, size_t data_len) {
         const auto& t_id = context.find_or_create_table(context.receiver, scope, table);
725 726 727 728 729
         FC_ASSERT(data_len >= KeyCount * sizeof(KeyType), "Data is not long enough to contain keys");
         const KeyType* keys = reinterpret_cast<const KeyType *>((const char *)data);

         const char* record_data =  ((const char*)data) + sizeof(KeyArrayType);
         size_t record_len = data_len - sizeof(KeyArrayType);
730
         return (context.*(method))(t_id, bta, keys, record_data, record_len) + sizeof(KeyArrayType);
731 732 733 734 735
      }

   public:
      using context_aware_api::context_aware_api;

736 737
      int store(const scope_name& scope, const name& table, const account_name& bta, array_ptr<const char> data, size_t data_len) {
         auto res = call(&apply_context::store_record<ObjectType>, scope, table, bta, data, data_len);
738 739
         //ilog("STORE [${scope},${code},${table}] => ${res} :: ${HEX}", ("scope",scope)("code",context.receiver)("table",table)("res",res)("HEX", fc::to_hex(data, data_len)));
         return res;
740 741
      }

742 743
      int update(const scope_name& scope, const name& table, const account_name& bta, array_ptr<const char> data, size_t data_len) {
         return call(&apply_context::update_record<ObjectType>, scope, table, bta, data, data_len);
744 745 746
      }

      int remove(const scope_name& scope, const name& table, const KeyArrayType &keys) {
747
         const auto& t_id = context.find_or_create_table(context.receiver, scope, table);
748 749 750 751 752 753 754 755 756 757 758 759
         return context.remove_record<ObjectType>(t_id, keys);
      }
};

template<typename IndexType, typename Scope>
class db_index_api : public context_aware_api {
   using KeyType = typename IndexType::value_type::key_type;
   static constexpr int KeyCount = IndexType::value_type::number_of_keys;
   using KeyArrayType = KeyType[KeyCount];
   using ContextMethodType = int(apply_context::*)(const table_id_object&, KeyType*, char*, size_t);


760
   int call(ContextMethodType method, const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
761
      auto maybe_t_id = context.find_table(code, scope, table);
762
      if (maybe_t_id == nullptr) {
763
         return -1;
764 765 766 767 768 769 770 771 772
      }

      const auto& t_id = *maybe_t_id;
      FC_ASSERT(data_len >= KeyCount * sizeof(KeyType), "Data is not long enough to contain keys");
      KeyType* keys = reinterpret_cast<KeyType *>((char *)data);

      char* record_data =  ((char*)data) + sizeof(KeyArrayType);
      size_t record_len = data_len - sizeof(KeyArrayType);

773 774 775 776 777
      auto res = (context.*(method))(t_id, keys, record_data, record_len);
      if (res != -1) {
         res += sizeof(KeyArrayType);
      }
      return res;
778 779 780 781 782
   }

   public:
      using context_aware_api::context_aware_api;

783 784
      int load(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         auto res = call(&apply_context::load_record<IndexType, Scope>, code, scope, table, data, data_len);
785 786
         //ilog("LOAD [${scope},${code},${table}] => ${res} :: ${HEX}", ("scope",scope)("code",code)("table",table)("res",res)("HEX", fc::to_hex(data, data_len)));
         return res;
787 788
      }

789 790
      int front(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         return call(&apply_context::front_record<IndexType, Scope>, code, scope, table, data, data_len);
791 792
      }

793 794
      int back(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         return call(&apply_context::back_record<IndexType, Scope>, code, scope, table, data, data_len);
795 796
      }

797 798
      int next(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         return call(&apply_context::next_record<IndexType, Scope>, code, scope, table, data, data_len);
799 800
      }

801 802
      int previous(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         return call(&apply_context::previous_record<IndexType, Scope>, code, scope, table, data, data_len);
803 804
      }

805 806
      int lower_bound(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         return call(&apply_context::lower_bound_record<IndexType, Scope>, code, scope, table, data, data_len);
807 808
      }

809 810
      int upper_bound(const account_name& code, const scope_name& scope, const name& table, array_ptr<char> data, size_t data_len) {
         return call(&apply_context::upper_bound_record<IndexType, Scope>, code, scope, table, data, data_len);
811 812 813 814
      }

};

815
class memory_api : public context_aware_api {
816
   public:
B
Bucky Kittinger 已提交
817
      using context_aware_api::context_aware_api;
818
     
819 820 821 822
      char* memcpy( array_ptr<char> dest, array_ptr<const char> src, size_t length) {
         return (char *)::memcpy(dest, src, length);
      }

823 824 825 826
      char* memmove( array_ptr<char> dest, array_ptr<const char> src, size_t length) {
         return (char *)::memmove(dest, src, length);
      }

827 828 829 830
      int memcmp( array_ptr<const char> dest, array_ptr<const char> src, size_t length) {
         return ::memcmp(dest, src, length);
      }

B
Bucky Kittinger 已提交
831
      char* memset( array_ptr<char> dest, int value, size_t length ) {
B
Bucky Kittinger 已提交
832
         return (char *)::memset( dest, value, length );
B
Bucky Kittinger 已提交
833 834 835
      }

      uint32_t sbrk(int num_bytes) {
836
         return code.wavm.sbrk(num_bytes);
837
      }
838 839
};

840 841 842 843
class transaction_api : public context_aware_api {
   public:
      using context_aware_api::context_aware_api;

844 845 846 847 848 849 850 851 852 853
      int read_transaction( array_ptr<char> data, size_t data_len ) {
         bytes trx = context.get_packed_transaction();
         if (data_len >= trx.size()) {
            memcpy(data, trx.data(), trx.size());
         }
         return trx.size();
      }

      int transaction_size() {
         return context.get_packed_transaction().size();
854 855 856
      }

      int expiration() {
857
        return context.trx_meta.trx().expiration.sec_since_epoch();
858 859 860
      }

      int tapos_block_num() {
861
        return context.trx_meta.trx().ref_block_num;
862
      }
863
      int tapos_block_prefix() {
864
        return context.trx_meta.trx().ref_block_prefix;
865 866
      }

867 868 869 870 871 872 873 874 875 876 877
      void send_inline( array_ptr<char> data, size_t data_len ) {
         // TODO: use global properties object for dynamic configuration of this default_max_gen_trx_size
         FC_ASSERT( data_len < config::default_max_inline_action_size, "inline action too big" );

         action act;
         fc::raw::unpack<action>(data, data_len, act);
         context.execute_inline(std::move(act));
      }


      void send_deferred( uint32_t sender_id, const fc::time_point_sec& execute_after, array_ptr<char> data, size_t data_len ) {
878 879 880
         try {
            // TODO: use global properties object for dynamic configuration of this default_max_gen_trx_size
            FC_ASSERT(data_len < config::default_max_gen_trx_size, "generated transaction too big");
881

882 883 884 885 886 887 888
            deferred_transaction dtrx;
            fc::raw::unpack<transaction>(data, data_len, dtrx);
            dtrx.sender = context.receiver;
            dtrx.sender_id = sender_id;
            dtrx.execute_after = execute_after;
            context.execute_deferred(std::move(dtrx));
         } FC_CAPTURE_AND_RETHROW((fc::to_hex(data, data_len)));
889 890 891 892
      }

};

893 894 895 896 897 898 899 900 901 902 903

REGISTER_INTRINSICS(privileged_api,
   (activate_feature,          void(int64_t))
   (is_feature_active,         int(int64_t))
   (set_resource_limits,       void(int64_t,int64_t,int64_t,int64_t,int64_t))
   (set_active_producers,      void(int,int))
   (is_privileged,             int(int64_t))
   (set_privileged,            void(int64_t, int))
   (freeze_account,            void(int64_t, int))
   (is_frozen,                 int(int64_t))
);
904 905

REGISTER_INTRINSICS(checktime_api,
906
   (checktime,      void(int))
907 908
);

909
REGISTER_INTRINSICS(producer_api,
910
   (get_active_producers,      int(int, int))
911 912
);

D
Daniel Larimer 已提交
913 914 915 916
REGISTER_INTRINSICS( database_api,
   (db_store_i64,        int(int64_t,int64_t,int64_t,int64_t,int,int))
   (db_update_i64,       void(int,int64_t,int,int))
   (db_remove_i64,       void(int))
917
   (db_get_i64,          int(int, int, int))
918 919
   (db_next_i64,         int(int, int))
   (db_previous_i64,     int(int, int))
D
Daniel Larimer 已提交
920 921
   (db_find_i64,         int(int64_t,int64_t,int64_t,int64_t))
   (db_lowerbound_i64,   int(int64_t,int64_t,int64_t,int64_t))
922
   (db_upperbound_i64,   int(int64_t,int64_t,int64_t,int64_t))
923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943
                             
   (db_idx64_store,          int(int64_t,int64_t,int64_t,int64_t,int))
   (db_idx64_remove,         void(int))
   (db_idx64_update,         void(int,int64_t,int))
   (db_idx64_find_primary,   int(int64_t,int64_t,int64_t,int,int64_t))
   (db_idx64_find_secondary, int(int64_t,int64_t,int64_t,int,int))
   (db_idx64_lowerbound,     int(int64_t,int64_t,int64_t,int,int))
   (db_idx64_upperbound,     int(int64_t,int64_t,int64_t,int,int))
   (db_idx64_next,           int(int, int))
   (db_idx64_previous,       int(int, int))

   (db_idx128_store,          int(int64_t,int64_t,int64_t,int64_t,int))
   (db_idx128_remove,         void(int))
   (db_idx128_update,         void(int,int64_t,int))
   (db_idx128_find_primary,   int(int64_t,int64_t,int64_t,int,int64_t))
   (db_idx128_find_secondary, int(int64_t,int64_t,int64_t,int,int))
   (db_idx128_lowerbound,     int(int64_t,int64_t,int64_t,int,int))
   (db_idx128_upperbound,     int(int64_t,int64_t,int64_t,int,int))
   (db_idx128_next,           int(int, int))
   (db_idx128_previous,       int(int, int))
);
D
Daniel Larimer 已提交
944

945
REGISTER_INTRINSICS(crypto_api,
946 947
   (assert_recover_key,  void(int, int, int, int, int))
   (recover_key,    int(int, int, int, int, int))
948
   (assert_sha256,  void(int, int, int))
949
   (sha1,           void(int, int, int))
950
   (sha256,         void(int, int, int))
951 952
   (sha512,         void(int, int, int))
   (ripemd160,      void(int, int, int))
953 954 955 956 957 958
);

REGISTER_INTRINSICS(string_api,
   (assert_is_utf8,  void(int, int, int))
);

B
Bart Wyatt 已提交
959
REGISTER_INTRINSICS(system_api,
960 961
   (abort,        void())
   (eosio_assert, void(int, int))
962
   (now,          int())
B
Bart Wyatt 已提交
963 964 965 966 967 968
);

REGISTER_INTRINSICS(action_api,
   (read_action,            int(int, int)  )
   (action_size,            int()          )
   (current_receiver,   int64_t()          )
969 970
   (publication_time,   int32_t()          )
   (current_sender,     int64_t()          )
B
Bart Wyatt 已提交
971 972 973
);

REGISTER_INTRINSICS(apply_context,
974
   (require_write_lock,    void(int64_t)            )
975
   (require_read_lock,     void(int64_t, int64_t)   )
976
   (require_recipient,     void(int64_t)            )
977
   (require_authorization, void(int64_t), "require_auth", void(apply_context::*)(const account_name&)const)
978
   (is_account,            int(int64_t)             )
B
Bart Wyatt 已提交
979 980 981 982 983 984 985 986 987 988 989 990
);

REGISTER_INTRINSICS(console_api,
   (prints,                void(int)       )
   (prints_l,              void(int, int)  )
   (printi,                void(int64_t)   )
   (printi128,             void(int)       )
   (printd,                void(int64_t)   )
   (printn,                void(int64_t)   )
   (printhex,              void(int, int)  )
);

991
REGISTER_INTRINSICS(transaction_api,
992 993 994 995 996 997
   (read_transaction,       int(int, int)            )
   (transaction_size,       int()                    )
   (expiration,             int()                    )
   (tapos_block_prefix,     int()                    )
   (tapos_block_num,        int()                    )
   (send_inline,           void(int, int)            )
998 999 1000
   (send_deferred,         void(int, int, int, int)  )
);

1001 1002
REGISTER_INTRINSICS(memory_api,
   (memcpy,                 int(int, int, int)   )
1003
   (memmove,                int(int, int, int)   )
1004 1005
   (memcmp,                 int(int, int, int)   )
   (memset,                 int(int, int, int)   )
B
Bucky Kittinger 已提交
1006
   (sbrk,                   int(int)             )
1007 1008
);

1009

1010
#define DB_METHOD_SEQ(SUFFIX) \
K
Khaled Al-Hassanieh 已提交
1011 1012
   (store,        int32_t(int64_t, int64_t, int64_t, int, int),   "store_"#SUFFIX ) \
   (update,       int32_t(int64_t, int64_t, int64_t, int, int),   "update_"#SUFFIX ) \
1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025
   (remove,       int32_t(int64_t, int64_t, int),                 "remove_"#SUFFIX )

#define DB_INDEX_METHOD_SEQ(SUFFIX)\
   (load,         int32_t(int64_t, int64_t, int64_t, int, int),   "load_"#SUFFIX )\
   (front,        int32_t(int64_t, int64_t, int64_t, int, int),   "front_"#SUFFIX )\
   (back,         int32_t(int64_t, int64_t, int64_t, int, int),   "back_"#SUFFIX )\
   (previous,     int32_t(int64_t, int64_t, int64_t, int, int),   "previous_"#SUFFIX )\
   (lower_bound,  int32_t(int64_t, int64_t, int64_t, int, int),   "lower_bound_"#SUFFIX )\
   (upper_bound,  int32_t(int64_t, int64_t, int64_t, int, int),   "upper_bound_"#SUFFIX )\

using db_api_key_value_object                                 = db_api<key_value_object>;
using db_api_keystr_value_object                              = db_api<keystr_value_object>;
using db_api_key128x128_value_object                          = db_api<key128x128_value_object>;
1026
using db_api_key64x64_value_object                            = db_api<key64x64_value_object>;
1027 1028 1029 1030 1031
using db_api_key64x64x64_value_object                         = db_api<key64x64x64_value_object>;
using db_index_api_key_value_index_by_scope_primary           = db_index_api<key_value_index,by_scope_primary>;
using db_index_api_keystr_value_index_by_scope_primary        = db_index_api<keystr_value_index,by_scope_primary>;
using db_index_api_key128x128_value_index_by_scope_primary    = db_index_api<key128x128_value_index,by_scope_primary>;
using db_index_api_key128x128_value_index_by_scope_secondary  = db_index_api<key128x128_value_index,by_scope_secondary>;
1032 1033
using db_index_api_key64x64_value_index_by_scope_primary      = db_index_api<key64x64_value_index,by_scope_primary>;
using db_index_api_key64x64_value_index_by_scope_secondary    = db_index_api<key64x64_value_index,by_scope_secondary>;
1034 1035 1036 1037 1038 1039 1040
using db_index_api_key64x64x64_value_index_by_scope_primary   = db_index_api<key64x64x64_value_index,by_scope_primary>;
using db_index_api_key64x64x64_value_index_by_scope_secondary = db_index_api<key64x64x64_value_index,by_scope_secondary>;
using db_index_api_key64x64x64_value_index_by_scope_tertiary  = db_index_api<key64x64x64_value_index,by_scope_tertiary>;

REGISTER_INTRINSICS(db_api_key_value_object,         DB_METHOD_SEQ(i64));
REGISTER_INTRINSICS(db_api_keystr_value_object,      DB_METHOD_SEQ(str));
REGISTER_INTRINSICS(db_api_key128x128_value_object,  DB_METHOD_SEQ(i128i128));
1041
REGISTER_INTRINSICS(db_api_key64x64_value_object,    DB_METHOD_SEQ(i64i64));
1042 1043 1044 1045 1046 1047
REGISTER_INTRINSICS(db_api_key64x64x64_value_object, DB_METHOD_SEQ(i64i64i64));

REGISTER_INTRINSICS(db_index_api_key_value_index_by_scope_primary,           DB_INDEX_METHOD_SEQ(i64));
REGISTER_INTRINSICS(db_index_api_keystr_value_index_by_scope_primary,        DB_INDEX_METHOD_SEQ(str));
REGISTER_INTRINSICS(db_index_api_key128x128_value_index_by_scope_primary,    DB_INDEX_METHOD_SEQ(primary_i128i128));
REGISTER_INTRINSICS(db_index_api_key128x128_value_index_by_scope_secondary,  DB_INDEX_METHOD_SEQ(secondary_i128i128));
1048 1049
REGISTER_INTRINSICS(db_index_api_key64x64_value_index_by_scope_primary,      DB_INDEX_METHOD_SEQ(primary_i64i64));
REGISTER_INTRINSICS(db_index_api_key64x64_value_index_by_scope_secondary,    DB_INDEX_METHOD_SEQ(secondary_i64i64));
1050
REGISTER_INTRINSICS(db_index_api_key64x64x64_value_index_by_scope_primary,   DB_INDEX_METHOD_SEQ(primary_i64i64i64));
1051
REGISTER_INTRINSICS(db_index_api_key64x64x64_value_index_by_scope_secondary, DB_INDEX_METHOD_SEQ(secondary_i64i64i64));
1052 1053
REGISTER_INTRINSICS(db_index_api_key64x64x64_value_index_by_scope_tertiary,  DB_INDEX_METHOD_SEQ(tertiary_i64i64i64));

B
Brian Johnson 已提交
1054

D
Daniel Larimer 已提交
1055
} } /// eosio::chain