wasm_interface.cpp 17.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
#include <eos/chain/wasm_interface.hpp>
#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>
11
#include <eos/chain/account_object.hpp>
12 13 14 15

namespace eos { namespace chain {
   using namespace IR;
   using namespace Runtime;
16

17 18 19
   wasm_interface::wasm_interface() {
   }

20 21 22 23
DEFINE_INTRINSIC_FUNCTION0(env,currentCode,currentCode,i64) {
   auto& wasm  = wasm_interface::get();
   return wasm.current_validate_context->code.value;
}
24

25
DEFINE_INTRINSIC_FUNCTION1(env,requireAuth,requireAuth,none,i64,account) {
26 27 28 29 30
   auto& wasm  = wasm_interface::get();
   const auto& msg   = wasm.current_validate_context->msg;
   for( const auto& perm : msg.authorization )
      if( perm.account == Name(account) ) return;
   FC_ASSERT( !"missing authorization", "expected authorization ${auth}", ("auth",Name(account)) );
31
}
D
Daniel Larimer 已提交
32

33
DEFINE_INTRINSIC_FUNCTION1(env,requireNotice,requireNotice,none,i64,account) {
34
   wasm_interface::get().current_validate_context->require_recipient( account );
35 36 37
}

DEFINE_INTRINSIC_FUNCTION1(env,requireScope,requireScope,none,i64,scope) {
38
   wasm_interface::get().current_validate_context->require_scope( scope );
39 40 41 42 43 44 45 46 47
}

DEFINE_INTRINSIC_FUNCTION3(env,remove_i64,remove_i64,i32,i64,scope,i64,table,i64,key) 
{
   return 0;
}

DEFINE_INTRINSIC_FUNCTION5(env,store_i64,store_i64,i32,i64,scope,i64,table,i64,key,i32,valueptr,i32,valuelen) 
{
D
Daniel Larimer 已提交
48 49 50
   FC_ASSERT( valuelen >= 0 );

   auto& wasm  = wasm_interface::get();
51
   wasm.current_validate_context->require_scope( scope );
D
Daniel Larimer 已提交
52 53 54 55

   FC_ASSERT( wasm.current_apply_context, "no apply context found" );

   auto& db    = wasm.current_apply_context->mutable_db;
56
   auto& code = wasm.current_apply_context->code;
57

D
Daniel Larimer 已提交
58
   auto  mem   = wasm.current_memory;
59
   char* value = memoryArrayPtr<char>( mem, valueptr, valuelen);
D
Daniel Larimer 已提交
60

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
   //idump((Name(scope))(Name(code))(Name(table))(Name(key))(valuelen) );
   const auto* obj = db.find<key_value_object,by_scope_key>( boost::make_tuple(
                                                            AccountName(scope), 
                                                            AccountName(code), 
                                                            AccountName(table), 
                                                            AccountName(key) ) );
   if( obj ) {
      db.modify( *obj, [&]( auto& o ) {
         o.value.assign(value, valuelen);
      });
   } else {
      db.create<key_value_object>( [&](auto& o) {
         o.scope = scope;
         o.code  = code;
         o.table = table;
         o.key   = key;
         o.value.insert( 0, value, valuelen );
      });
   }

   return 0;
}
DEFINE_INTRINSIC_FUNCTION6(env,load_i64,load_i64,i32,i64,scope,i64,code,i64,table,i64,key,i32,valueptr,i32,valuelen) 
{
   //idump((Name(scope))(Name(code))(Name(table))(Name(key))(valuelen) );

   FC_ASSERT( valuelen >= 0 );

   auto& wasm  = wasm_interface::get();

   FC_ASSERT( wasm.current_validate_context, "no apply context found" );

   auto& db    = wasm.current_apply_context->mutable_db;
   auto  mem   = wasm.current_memory;
   char* value = memoryArrayPtr<char>( mem, valueptr, valuelen);


   const auto* obj = db.find<key_value_object,by_scope_key>( boost::make_tuple(
                                                            AccountName(scope), 
                                                            AccountName(code), 
                                                            AccountName(table), 
                                                            AccountName(key) ) );
   if( obj == nullptr ) {
      wlog( "not found" );
      return -1;
   }
D
Daniel Larimer 已提交
107 108 109 110 111 112 113 114
   auto copylen =  std::min<size_t>(obj->value.size(),valuelen);
   if( copylen ) {
      obj->value.copy(value, copylen);
   }
   return copylen;
}


115 116 117 118 119 120
DEFINE_INTRINSIC_FUNCTION1(env,name_to_int64,name_to_int64,i64,i32,cstr) {
   auto& wasm  = wasm_interface::get();
   auto  mem   = wasm.current_memory;
   const char* str   = memoryArrayPtr<const char>( mem, cstr, 13);
   return Name(str).value;
}
121

122
DEFINE_INTRINSIC_FUNCTION2(env,remove,remove,i32,i32,keyptr,i32,keylen) {
D
Daniel Larimer 已提交
123
   /*
124 125 126 127 128 129 130 131 132
   FC_ASSERT( keylen > 0 );

   auto& wasm  = wasm_interface::get();

   FC_ASSERT( wasm.current_apply_context, "no apply context found" );

   auto& db    = wasm.current_apply_context->mutable_db;
   auto& scope = wasm.current_apply_context->scope;
   auto  mem   = wasm.current_memory;
133
   char* key   = memoryArrayPtr<char>( mem, keyptr, keylen);
134 135 136 137
   string keystr( key, key+keylen);

   const auto* obj = db.find<key_value_object,by_scope_key>( boost::make_tuple(scope, keystr) );
   if( obj ) {
138
      db.remove( *obj );
139 140
      return true;
   }
D
Daniel Larimer 已提交
141
   */
142 143 144 145 146 147 148 149
   return false;
}

DEFINE_INTRINSIC_FUNCTION3(env,memcpy,memcpy,i32,i32,dstp,i32,srcp,i32,len) {
   auto& wasm          = wasm_interface::get();
   auto  mem           = wasm.current_memory;
   char* dst           = &memoryRef<char>( mem, dstp);
   const char* src     = &memoryRef<const char>( mem, srcp );
150
   //char* dst_end       = &memoryRef<char>( mem, dstp+uint32_t(len));
151 152 153 154 155
   const char* src_end = &memoryRef<const char>( mem, srcp+uint32_t(len) );

#warning TODO: wasm memcpy has undefined behavior if memory ranges overlap
/*
   if( dst > src )
156
      FC_ASSERT( dst < src_end && src < dst_end, "overlap of memory range is undefined", ("d",dstp)("s",srcp)("l",len) );
157
   else
158
      FC_ASSERT( src < dst_end && dst < src_end, "overlap of memory range is undefined", ("d",dstp)("s",srcp)("l",len) );
159 160 161 162 163 164 165 166 167 168
*/
   memcpy( dst, src, uint32_t(len) );
   return dstp;
}


DEFINE_INTRINSIC_FUNCTION2(env,Varint_unpack,Varint_unpack,none,i32,streamptr,i32,valueptr) {
   auto& wasm  = wasm_interface::get();
   auto  mem   = wasm.current_memory;

169
   uint32_t* stream = memoryArrayPtr<uint32_t>( mem, streamptr, 3 );
170 171 172 173 174 175 176 177 178 179 180 181 182
   const char* pos = &memoryRef<const char>( mem, stream[1] );
   const char* end = &memoryRef<const char>( mem, stream[2] );
   uint32_t& value = memoryRef<uint32_t>( mem, valueptr );

   fc::unsigned_int vi;
   fc::datastream<const char*> ds(pos,end-pos);
   fc::raw::unpack( ds, vi );
   value = vi.value;

   stream[1] += ds.pos() - pos;
}


183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
DEFINE_INTRINSIC_FUNCTION2(env,send,send,i32,i32,trx_buffer, i32,trx_buffer_size ) {
   auto& wasm  = wasm_interface::get();
   auto  mem   = wasm.current_memory;
   const char* buffer = &memoryRef<const char>( mem, trx_buffer );

   FC_ASSERT( trx_buffer_size > 0 );
   FC_ASSERT( wasm.current_apply_context, "not in apply context" );

   fc::datastream<const char*> ds(buffer, trx_buffer_size );
   eos::chain::generated_transaction gtrx;
   eos::chain::Transaction& trx = gtrx;
   fc::raw::unpack( ds, trx );

/**
 *  The code below this section provides sanity checks that the generated message is well formed
 *  before being accepted. These checks do not need to be applied during reindex.
 */
#warning TODO: reserve per-thread static memory for MAX TRX SIZE 
/** make sure that packing what we just unpacked produces expected output */
   auto test = fc::raw::pack( trx );
   FC_ASSERT( 0 == memcmp( buffer, test.data(), test.size() ) );

/** TODO: make sure that we can call validate() on the message and it passes, this is thread safe and
 *   ensures the type is properly registered and can be deserialized... one issue is that this could
 *   construct a RECURSIVE virtual machine state which means the wasm_interface state needs to be a STACK vs
 *   a per-thread global.
 **/

   wasm.current_apply_context->generated.emplace_back( std::move(gtrx) );
212

D
Daniel Larimer 已提交
213 214
   return 0;
}
215

216 217 218



219 220 221 222
DEFINE_INTRINSIC_FUNCTION2(env,readMessage,readMessage,i32,i32,destptr,i32,destsize) {
   FC_ASSERT( destsize > 0 );
   wasm_interface& wasm = wasm_interface::get();
   auto  mem   = wasm.current_memory;
223
   char* begin = memoryArrayPtr<char>( mem, destptr, destsize );
224 225 226 227 228 229

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

230 231 232
DEFINE_INTRINSIC_FUNCTION2(env,assert,assert,none,i32,test,i32,msg) {
  std::string message = &Runtime::memoryRef<char>( wasm_interface::get().current_memory, msg );
  if( !test ) edump((message));
233
  FC_ASSERT( test, "assertion failed: ${s}", ("s",message)("ptr",msg) );
234
}
235

236 237 238 239
DEFINE_INTRINSIC_FUNCTION0(env,messageSize,messageSize,i32) {
   return wasm_interface::get().current_validate_context->msg.data.size();
}

240
DEFINE_INTRINSIC_FUNCTION1(env,malloc,malloc,i32,i32,size) {
241 242 243 244 245 246 247 248
   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;
}

249
DEFINE_INTRINSIC_FUNCTION1(env,printi,printi,none,i64,val) {
250 251
  std::cerr << uint64_t(val) << " " << Name(val).toString();
  //idump((val)(Name(val)));
252
}
253

254
DEFINE_INTRINSIC_FUNCTION1(env,print,print,none,i32,charptr) {
255 256 257
  auto& wasm  = wasm_interface::get();
  auto  mem   = wasm.current_memory;

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

260
  std::cerr << std::string( str, strlen(str) );
261
 	wdump( (std::string( str, strlen(str) )) );
262 263
}

264
DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
265 266
}

267
DEFINE_INTRINSIC_FUNCTION1(env,toUpper,toUpper,none,i32,charptr) {
268 269 270 271 272 273 274 275 276 277 278 279 280
   std::cerr << "TO UPPER CALLED\n";// << charptr << "\n";
//   std::cerr << "moduleInstance: " << moduleInstance << "\n";
  // /*U8* base = */Runtime::getMemoryBaseAddress( Runtime::getDefaultMemory(moduleInstance) );
   //std::cerr << "Base Address: " << (int64_t)base;
   //char* c = (char*)(base + charptr);
   char& c = Runtime::memoryRef<char>( Runtime::getDefaultMemory(wasm_interface::get().current_module), charptr );
//   std::cerr << "char: " << c <<"\n";
//   if( c > 'Z' ) c -= 32;
//   return 0;
}

   wasm_interface& wasm_interface::get() {
      static wasm_interface*  wasm = nullptr;
281
      if( !wasm )
282 283
      {
         wlog( "Runtime::init" );
284
         Runtime::init();
285 286 287 288 289 290 291 292 293 294 295 296 297
         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
     {
298 299 300 301
         // 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;
302 303 304 305 306
     }
   };


   char* wasm_interface::vm_allocate( int bytes ) {
307 308
      FunctionInstance* alloc_function = asFunctionNullable(getInstanceExport(current_module,"alloc"));
      const FunctionType* functionType = getFunctionType(alloc_function);
309
      FC_ASSERT( functionType->parameters.size() == 1 );
310
      std::vector<Value> invokeArgs(1);
311 312 313 314 315 316 317 318 319 320 321
      invokeArgs[0] = U32(bytes);

      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 已提交
322
   void  wasm_interface::vm_call( const char* name ) {
323
   try {
324
      try {
D
Daniel Larimer 已提交
325
         /*
326
         name += "_" + std::string( current_validate_context->msg.code ) + "_";
327
         name += std::string( current_validate_context->msg.type );
D
Daniel Larimer 已提交
328 329 330
         */
         /// TODO: cache this somehow
         FunctionInstance* call = asFunctionNullable(getInstanceExport(current_module,name) );
331 332 333 334
         if( !call ) {
            //wlog( "unable to find call ${name}", ("name",name));
            return;
         }
D
Daniel Larimer 已提交
335
         //FC_ASSERT( apply, "no entry point found for ${call}", ("call", std::string(name))  );
336

D
Daniel Larimer 已提交
337 338 339
         FC_ASSERT( getFunctionType(call)->parameters.size() == 2 );
         std::vector<Value> args = { Value(uint64_t(current_validate_context->msg.code)),
                                     Value(uint64_t(current_validate_context->msg.type)) };
340 341 342 343 344 345

         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);

D
Daniel Larimer 已提交
346
         Runtime::invokeFunction(call,args);
347 348
      } catch( const Runtime::Exception& e ) {
          edump((std::string(describeExceptionCause(e.cause))));
349 350
          edump((e.callStack));
          throw;
351
      }
352 353
   } FC_CAPTURE_AND_RETHROW( (name)(current_validate_context->msg.type) ) }

D
Daniel Larimer 已提交
354 355 356
   void  wasm_interface::vm_precondition() { vm_call("precondition" ); } 
   void  wasm_interface::vm_apply()        { vm_call("apply" );        }
   void  wasm_interface::vm_validate()     { vm_call("validate");       }
357

358
   void  wasm_interface::vm_onInit()
359 360
   { try {
      try {
361
          wlog( "on_init" );
362 363
            FunctionInstance* apply = asFunctionNullable(getInstanceExport(current_module,"init"));
            if( !apply ) {
364
               elog( "no onInit method found" );
365
               return; /// if not found then it is a no-op
366 367
         }

368 369
            const FunctionType* functionType = getFunctionType(apply);
            FC_ASSERT( functionType->parameters.size() == 0 );
370

371
            std::vector<Value> args(0);
372

373
            Runtime::invokeFunction(apply,args);
374 375
      } catch( const Runtime::Exception& e ) {
          edump((std::string(describeExceptionCause(e.cause))));
376 377
          edump((e.callStack));
          throw;
378
      }
379
   } FC_CAPTURE_AND_RETHROW() }
380

381 382 383 384 385 386
   void wasm_interface::validate( message_validate_context& c ) {

      current_validate_context       = &c;
      current_precondition_context   = nullptr;
      current_apply_context          = nullptr;

D
Daniel Larimer 已提交
387
      load( c.code, c.db );
388 389
      vm_validate();
   }
390 391 392 393 394 395
   void wasm_interface::precondition( precondition_validate_context& c ) {
   try {

      current_validate_context       = &c;
      current_precondition_context   = &c;

D
Daniel Larimer 已提交
396
      load( c.code, c.db );
397 398 399
      vm_precondition();

   } FC_CAPTURE_AND_RETHROW() }
400 401


402 403
   void wasm_interface::apply( apply_context& c ) {
    try {
404 405 406 407
      current_validate_context       = &c;
      current_precondition_context   = &c;
      current_apply_context          = &c;

D
Daniel Larimer 已提交
408
      load( c.code, c.db );
409 410 411 412 413 414
      vm_apply();

   } FC_CAPTURE_AND_RETHROW() }

   void wasm_interface::init( apply_context& c ) {
    try {
415 416 417 418
      current_validate_context       = &c;
      current_precondition_context   = &c;
      current_apply_context          = &c;

D
Daniel Larimer 已提交
419
      load( c.code, c.db );
420
      vm_onInit();
421 422 423

   } FC_CAPTURE_AND_RETHROW() }

424

425

426 427 428 429 430 431 432 433 434 435
   void wasm_interface::load( const AccountName& name, const chainbase::database& db ) {
      const auto& recipient = db.get<account_object,by_name>( name );

      auto& state = instances[name];
      if( state.code_version != recipient.code_version ) {
         if( state.instance ) {
            /// TODO: free existing instance and module
#warning TODO: free existing module if the code has been updated, currently leak memory
            state.instance     = nullptr;
            state.module       = nullptr;
436
            state.code_version = fc::sha256();
437 438 439 440 441
         }
         state.module = new IR::Module();

        try
        {
442
          wlog( "LOADING CODE" );
443
          auto start = fc::time_point::now();
444 445 446 447 448 449 450
          Serialization::MemoryInputStream stream((const U8*)recipient.code.data(),recipient.code.size());
          WASM::serialize(stream,*state.module);

          RootResolver rootResolver;
          LinkResult linkResult = linkModule(*state.module,rootResolver);
          state.instance = instantiateModule( *state.module, std::move(linkResult.resolvedImports) );
          FC_ASSERT( state.instance );
451 452 453
          auto end = fc::time_point::now();
          idump(( (end-start).count()/1000000.0) );

454 455 456
          current_memory = Runtime::getDefaultMemory(state.instance);

          char* memstart = &memoryRef<char>( current_memory, 0 );
457
         // state.init_memory.resize(1<<16); /// TODO: actually get memory size
458
          std::cerr <<"INIT MEMORY: \n";
459
          for( uint32_t i = 0; i < 10000; ++i )
460 461
              if( memstart[i] ) {
                   state.mem_end = i;
462
                   //std::cerr << (char)memstart[i];
463 464 465 466
              }

          state.init_memory.resize(state.mem_end);
          memcpy( state.init_memory.data(), memstart, state.mem_end ); //state.init_memory.size() );
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489
          std::cerr <<"\n";
          state.code_version = recipient.code_version;
        }
        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;
        }
      }
      current_module = state.instance;
      current_memory = getDefaultMemory( current_module );
490
      current_state  = &state;
491
   }
492 493

} }