diff --git a/contracts/CMakeLists.txt b/contracts/CMakeLists.txt index 0cc39d18779..07a6de6edee 100644 --- a/contracts/CMakeLists.txt +++ b/contracts/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(currency) add_subdirectory(exchange) -#add_subdirectory(social) \ No newline at end of file +add_subdirectory(infinite) +#add_subdirectory(social) diff --git a/contracts/infinite/CMakeLists.txt b/contracts/infinite/CMakeLists.txt new file mode 100644 index 00000000000..16247cbb899 --- /dev/null +++ b/contracts/infinite/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB SOURCE_FILES "*.cpp") +add_wast_target(infinite "${SOURCE_FILES}" "${CMAKE_SOURCE_DIR}/contracts" ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/contracts/infinite/infinite.cpp b/contracts/infinite/infinite.cpp new file mode 100644 index 00000000000..f236962b702 --- /dev/null +++ b/contracts/infinite/infinite.cpp @@ -0,0 +1,50 @@ +#include /// defines transfer struct (abi) + +namespace infinite { + using namespace eos; + + /// When storing accounts, check for empty balance and remove account + void storeAccount( AccountName account, const Account& a ) { + if( a.isEmpty() ) { + /// value, scope + Accounts::remove( a, account ); + } else { + /// value, scope + Accounts::store( a, account ); + } + } + + void apply_currency_transfer( const infinite::Transfer& transfer ) { + requireNotice( transfer.to, transfer.from ); + requireAuth( transfer.from ); + + auto from = getAccount( transfer.from ); + auto to = getAccount( transfer.to ); + + while (from.balance > infinite::CurrencyTokens()) + { + from.balance -= transfer.quantity; /// token subtraction has underflow assertion + to.balance += transfer.quantity; /// token addition has overflow assertion + } + + storeAccount( transfer.from, from ); + storeAccount( transfer.to, to ); + } + +} // namespace infinite + +using namespace infinite; + +extern "C" { + void init() { + storeAccount( N(currency), Account( CurrencyTokens(1000ll*1000ll*1000ll) ) ); + } + + /// The apply method implements the dispatch of events to this contract + void apply( uint64_t code, uint64_t action ) { + if( code == N(currency) ) { + if( action == N(transfer) ) + infinite::apply_currency_transfer( currentMessage< infinite::Transfer >() ); + } + } +} diff --git a/contracts/infinite/infinite.hpp b/contracts/infinite/infinite.hpp new file mode 100644 index 00000000000..6816313829a --- /dev/null +++ b/contracts/infinite/infinite.hpp @@ -0,0 +1,54 @@ +#include +#include +#include + +namespace infinite { + + typedef eos::token CurrencyTokens; + + /** + * Transfer requires that the sender and receiver be the first two + * accounts notified and that the sender has provided authorization. + */ + struct Transfer { + AccountName from; + AccountName to; + CurrencyTokens quantity; + }; + + /** + * @brief row in Account table stored within each scope + */ + struct Account { + Account( CurrencyTokens b = CurrencyTokens() ):balance(b){} + + /** + * The key is constant because there is only one record per scope/currency/accounts + */ + const uint64_t key = N(account); + CurrencyTokens balance; + + bool isEmpty()const { return balance.quantity == 0; } + }; + static_assert( sizeof(Account) == sizeof(uint64_t)+sizeof(CurrencyTokens), "unexpected packing" ); + + using Accounts = Table; + + /** + * Accounts information for owner is stored: + * + * owner/infinite/account/account -> Account + * + * This API is made available for 3rd parties wanting read access to + * the users balance. If the account doesn't exist a default constructed + * account will be returned. + */ + inline Account getAccount( AccountName owner ) { + Account account; + /// scope, record + Accounts::get( account, owner ); + return account; + } + +} /// namespace infinite + diff --git a/libraries/chain/include/eos/chain/exceptions.hpp b/libraries/chain/include/eos/chain/exceptions.hpp index 96889e637ca..dc00e7ad725 100644 --- a/libraries/chain/include/eos/chain/exceptions.hpp +++ b/libraries/chain/include/eos/chain/exceptions.hpp @@ -49,6 +49,7 @@ namespace eos { namespace chain { FC_DECLARE_DERIVED_EXCEPTION( insufficient_fee, eos::chain::transaction_exception, 3030007, "insufficient fee" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_scope, eos::chain::transaction_exception, 3030008, "missing required scope" ) FC_DECLARE_DERIVED_EXCEPTION( tx_missing_recipient, eos::chain::transaction_exception, 3030009, "missing required recipient" ) + FC_DECLARE_DERIVED_EXCEPTION( checktime_exceeded, eos::chain::transaction_exception, 3030010, "allotted processing time was exceeded" ) FC_DECLARE_DERIVED_EXCEPTION( invalid_pts_address, eos::chain::utility_exception, 3060001, "invalid pts address" ) FC_DECLARE_DERIVED_EXCEPTION( insufficient_feeds, eos::chain::chain_exception, 37006, "insufficient feeds" ) diff --git a/libraries/chain/include/eos/chain/wasm_interface.hpp b/libraries/chain/include/eos/chain/wasm_interface.hpp index 7d152354b56..a5703baf2f3 100644 --- a/libraries/chain/include/eos/chain/wasm_interface.hpp +++ b/libraries/chain/include/eos/chain/wasm_interface.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -34,6 +35,8 @@ class wasm_interface { void validate( message_validate_context& c ); void precondition( precondition_validate_context& c ); + int64_t current_execution_time(); + apply_context* current_apply_context = nullptr; message_validate_context* current_validate_context = nullptr; precondition_validate_context* current_precondition_context = nullptr; @@ -56,7 +59,7 @@ class wasm_interface { map instances; - + fc::time_point checktimeStart; wasm_interface(); }; diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index 8793e4f1c7f..eb301ca69d2 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -10,6 +10,7 @@ #include "IR/Validate.h" #include #include +#include namespace eos { namespace chain { using namespace IR; @@ -18,6 +19,20 @@ namespace eos { namespace chain { wasm_interface::wasm_interface() { } +#ifdef NDEBUG + const int CHECKTIME_LIMIT = 2000; +#else + const int CHECKTIME_LIMIT = 12000; +#endif + +DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) { + auto dur = wasm_interface::get().current_execution_time(); + if (dur > CHECKTIME_LIMIT) { + wlog("checktime called ${d}", ("d", dur)); + throw checktime_exceeded(); + } +} + DEFINE_INTRINSIC_FUNCTION2(env,multeq_i128,multeq_i128,none,i32,self,i32,other) { auto& wasm = wasm_interface::get(); auto mem = wasm.current_memory; @@ -337,6 +352,11 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { } }; + int64_t wasm_interface::current_execution_time() + { + return (fc::time_point::now() - checktimeStart).count(); + } + char* wasm_interface::vm_allocate( int bytes ) { FunctionInstance* alloc_function = asFunctionNullable(getInstanceExport(current_module,"alloc")); @@ -345,6 +365,8 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { std::vector invokeArgs(1); invokeArgs[0] = U32(bytes); + checktimeStart = fc::time_point::now(); + auto result = Runtime::invokeFunction(alloc_function,invokeArgs); return &memoryRef( current_memory, result.i32 ); @@ -380,6 +402,8 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { memset( memstart + state.mem_end, 0, ((1<<16) - state.mem_end) ); memcpy( memstart, state.init_memory.data(), state.mem_end); + checktimeStart = fc::time_point::now(); + Runtime::invokeFunction(call,args); } catch( const Runtime::Exception& e ) { edump((std::string(describeExceptionCause(e.cause)))); @@ -400,6 +424,8 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { return; /// if not found then it is a no-op } + checktimeStart = fc::time_point::now(); + const FunctionType* functionType = getFunctionType(apply); FC_ASSERT( functionType->parameters.size() == 0 ); @@ -481,7 +507,7 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) { wlog( "LOADING CODE" ); auto start = fc::time_point::now(); Serialization::MemoryInputStream stream((const U8*)recipient.code.data(),recipient.code.size()); - WASM::serialize(stream,*state.module); + WASM::serializeWithInjection(stream,*state.module); RootResolver rootResolver; LinkResult linkResult = linkModule(*state.module,rootResolver); diff --git a/libraries/native_contract/include/eos/native_contract/producer_objects.hpp b/libraries/native_contract/include/eos/native_contract/producer_objects.hpp index e859730e0ed..5aedf6e0c02 100644 --- a/libraries/native_contract/include/eos/native_contract/producer_objects.hpp +++ b/libraries/native_contract/include/eos/native_contract/producer_objects.hpp @@ -46,7 +46,7 @@ class ProducerVotesObject : public chainbase::object getVoteOrder()const { return std::tie( race.speed, id ); } + pair getVoteOrder()const { return { race.speed, id }; } /** * These fields are used for the producer scheduling algorithm which uses a virtual race to ensure that runner-up @@ -103,7 +103,7 @@ class ProducerVotesObject : public chainbase::object rft_order_type; - rft_order_type projectedRaceFinishTimeOrder() const { return std::tie(race.projectedFinishTime,id); } + rft_order_type projectedRaceFinishTimeOrder() const { return {race.projectedFinishTime,id}; } }; /** diff --git a/libraries/wasm-jit/Include/WASM/WASM.h b/libraries/wasm-jit/Include/WASM/WASM.h index 458e0155f85..e56aaee09ba 100644 --- a/libraries/wasm-jit/Include/WASM/WASM.h +++ b/libraries/wasm-jit/Include/WASM/WASM.h @@ -11,6 +11,7 @@ namespace Serialization { struct InputStream; struct OutputStream; } namespace WASM { - WEBASSEMBLY_API void serialize(Serialization::InputStream& stream,IR::Module& module); - WEBASSEMBLY_API void serialize(Serialization::OutputStream& stream,const IR::Module& module); + WEBASSEMBLY_API void serialize(Serialization::InputStream& stream,IR::Module& module); + WEBASSEMBLY_API void serializeWithInjection(Serialization::InputStream& stream,IR::Module& module); + WEBASSEMBLY_API void serialize(Serialization::OutputStream& stream,const IR::Module& module); } diff --git a/libraries/wasm-jit/Source/WASM/WASMSerialization.cpp b/libraries/wasm-jit/Source/WASM/WASMSerialization.cpp index b576eacd881..4953550be10 100644 --- a/libraries/wasm-jit/Source/WASM/WASMSerialization.cpp +++ b/libraries/wasm-jit/Source/WASM/WASMSerialization.cpp @@ -7,6 +7,8 @@ #include "IR/Types.h" #include "IR/Validate.h" +#include + using namespace Serialization; static void throwIfNotValidUTF8(const std::string& string) @@ -17,7 +19,7 @@ static void throwIfNotValidUTF8(const std::string& string) throw FatalSerializationException("invalid UTF-8 encoding"); } } - + // These serialization functions need to be declared in the IR namespace for the array serializer in the Serialization namespace to find them. namespace IR { @@ -174,12 +176,112 @@ namespace IR } } +using namespace IR; + +class ChecktimeInjection +{ +public: + ChecktimeInjection() + : typeSlot(-1) + {} + + + void addCall(const Module& module, Serialization::OutputStream& inByteStream) + { + OpcodeAndImm* encodedOperator = (OpcodeAndImm*)inByteStream.advance(sizeof(OpcodeAndImm)); + encodedOperator->opcode = Opcode::call; + // checktime will be the last defined import + encodedOperator->imm.functionIndex = checktimeIndex(module); + } + + static U32 checktimeIndex(const Module& module) + { + return module.functions.imports.size() - 1; + } + + void setTypeSlot(const Module& module, ResultType returnType, const std::vector& parameterTypes) + { + if (returnType == ResultType::none && !parameterTypes.size() ) + typeSlot = module.types.size() - 1; + } + + void addTypeSlot(Module& module) + { + if (typeSlot < 0) + { + // add a type for void func(void) + typeSlot = module.types.size(); + module.types.push_back(FunctionType::get(ResultType::none)); + } + } + + void addImport(Module& module) + { + const U32 functionTypeIndex = typeSlot; + module.functions.imports.push_back({{functionTypeIndex},std::move(u8"env"),std::move(u8"checktime")}); + } + + void conditionallyAddCall(Opcode opcode, const ControlStructureImm& imm, const Module& module, Serialization::OutputStream& inByteStream) + { + switch(opcode) + { + case Opcode::loop: + case Opcode::block: + addCall(module, inByteStream); + default: + break; + }; + } + + template + void conditionallyAddCall(Opcode , const Imm& , const Module& , Serialization::OutputStream& ) + { + } + + void adjustExportIndex(Module& module) + { + // all function exports need to have their index increased to account for inserted definition + for (auto& exportDef : module.exports) + { + if (exportDef.kind == ObjectKind::function) + ++exportDef.index; + } + } + + void adjustCallIndex(const Module& module, CallImm& imm) + { + if (imm.functionIndex >= checktimeIndex(module)) + ++imm.functionIndex; + } + + template + void adjustCallIndex(const Module& , Imm& ) + { + } + +private: + int typeSlot; +}; + +struct NoOpInjection +{ + void addCall(const Module& , Serialization::OutputStream& ) {} + void setTypeSlot(const Module& , ResultType , const std::vector& ) {} + void addTypeSlot(Module& ) {} + void addImport(Module& ) {} + template + void conditionallyAddCall(Opcode , const Imm& , const Module& , Serialization::OutputStream& ) {} + void adjustExportIndex(Module& ) {} + template + void adjustCallIndex(const Module& , Imm& ) {} +}; + namespace WASM { - using namespace IR; - using namespace Serialization; - - enum + using namespace IR; + using namespace Serialization; + + enum { magicNumber=0x6d736100, // "\0asm" currentVersion=1 @@ -382,10 +484,10 @@ namespace WASM { serializeConstant(stream,"expected user section (section ID 0)",(U8)SectionType::user); ArrayOutputStream sectionStream; - serialize(sectionStream,userSection.name); + Serialization::serialize(sectionStream,userSection.name); serializeBytes(sectionStream,userSection.data.data(),userSection.data.size()); std::vector sectionBytes = sectionStream.getBytes(); - serialize(stream,sectionBytes); + Serialization::serialize(stream,sectionBytes); } void serialize(InputStream& stream,UserSection& userSection) @@ -395,10 +497,10 @@ namespace WASM serializeVarUInt32(stream,numSectionBytes); MemoryInputStream sectionStream(stream.advance(numSectionBytes),numSectionBytes); - serialize(sectionStream,userSection.name); + Serialization::serialize(sectionStream,userSection.name); throwIfNotValidUTF8(userSection.name); userSection.data.resize(sectionStream.capacity()); - serializeBytes(sectionStream,userSection.data.data(),userSection.data.size()); + Serialization::serializeBytes(sectionStream,userSection.data.data(),userSection.data.size()); assert(!sectionStream.capacity()); } @@ -441,390 +543,420 @@ namespace WASM FunctionDef& functionDef; }; - void serializeFunctionBody(OutputStream& sectionStream,Module& module,FunctionDef& functionDef) - { - ArrayOutputStream bodyStream; - - // Convert the function's local types into LocalSets: runs of locals of the same type. - LocalSet* localSets = (LocalSet*)alloca(sizeof(LocalSet)*functionDef.nonParameterLocalTypes.size()); - Uptr numLocalSets = 0; - if(functionDef.nonParameterLocalTypes.size()) - { - localSets[0].type = ValueType::any; - localSets[0].num = 0; - for(auto localType : functionDef.nonParameterLocalTypes) - { - if(localSets[numLocalSets].type != localType) - { - if(localSets[numLocalSets].type != ValueType::any) { ++numLocalSets; } - localSets[numLocalSets].type = localType; - localSets[numLocalSets].num = 0; - } - ++localSets[numLocalSets].num; - } - if(localSets[numLocalSets].type != ValueType::any) { ++numLocalSets; } - } - - // Serialize the local sets. - serializeVarUInt32(bodyStream,numLocalSets); - for(Uptr setIndex = 0;setIndex < numLocalSets;++setIndex) { serialize(bodyStream,localSets[setIndex]); } - - // Serialize the function code. - OperatorDecoderStream irDecoderStream(functionDef.code); - OperatorSerializerStream wasmOpEncoderStream(bodyStream,functionDef); - while(irDecoderStream) { irDecoderStream.decodeOp(wasmOpEncoderStream); }; - - std::vector bodyBytes = bodyStream.getBytes(); - serialize(sectionStream,bodyBytes); - } - - void serializeFunctionBody(InputStream& sectionStream,Module& module,FunctionDef& functionDef) - { - Uptr numBodyBytes = 0; - serializeVarUInt32(sectionStream,numBodyBytes); - - MemoryInputStream bodyStream(sectionStream.advance(numBodyBytes),numBodyBytes); - - // Deserialize local sets and unpack them into a linear array of local types. - Uptr numLocalSets = 0; - serializeVarUInt32(bodyStream,numLocalSets); - for(Uptr setIndex = 0;setIndex < numLocalSets;++setIndex) - { - LocalSet localSet; - serialize(bodyStream,localSet); - for(Uptr index = 0;index < localSet.num;++index) { functionDef.nonParameterLocalTypes.push_back(localSet.type); } - } - - // Deserialize the function code, validate it, and re-encode it in the IR format. - ArrayOutputStream irCodeByteStream; - OperatorEncoderStream irEncoderStream(irCodeByteStream); - CodeValidationStream codeValidationStream(module,functionDef); - while(bodyStream.capacity()) - { - Opcode opcode; - serialize(bodyStream,opcode); - switch(opcode) - { - #define VISIT_OPCODE(_,name,nameString,Imm,...) \ - case Opcode::name: \ - { \ - Imm imm; \ - serialize(bodyStream,imm,functionDef); \ - codeValidationStream.name(imm); \ - irEncoderStream.name(imm); \ - break; \ - } - ENUM_NONFLOAT_OPERATORS(VISIT_OPCODE) - #undef VISIT_OPCODE - #define VISIT_OPCODE(_,name,nameString,...) \ - case Opcode::name: \ - throw FatalSerializationException("float instructions not allowed"); - ENUM_FLOAT_NONCONTROL_NONPARAMETRIC_OPERATORS(VISIT_OPCODE) - #undef VISIT_OPCODE - default: throw FatalSerializationException("unknown opcode"); - }; - }; - codeValidationStream.finish(); - - functionDef.code = std::move(irCodeByteStream.getBytes()); - } - - template - void serializeTypeSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::type,[&module](Stream& sectionStream) - { - serializeArray(sectionStream,module.types,[](Stream& stream,const FunctionType*& functionType) - { - serializeConstant(stream,"function type tag",U8(0x60)); - if(Stream::isInput) - { - std::vector parameterTypes; - ResultType returnType; - serialize(stream,parameterTypes); - serialize(stream,returnType); - functionType = FunctionType::get(returnType,parameterTypes); - } - else - { - serialize(stream,const_cast&>(functionType->parameters)); - serialize(stream,const_cast(functionType->ret)); - } - }); - }); - } - template - void serializeImportSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::import,[&module](Stream& sectionStream) - { - Uptr size = module.functions.imports.size() - + module.tables.imports.size() - + module.memories.imports.size() - + module.globals.imports.size(); - serializeVarUInt32(sectionStream,size); - if(Stream::isInput) - { - for(Uptr index = 0;index < size;++index) - { - std::string moduleName; - std::string exportName; - ObjectKind kind = ObjectKind::invalid; - serialize(sectionStream,moduleName); - serialize(sectionStream,exportName); - throwIfNotValidUTF8(moduleName); - throwIfNotValidUTF8(exportName); - serialize(sectionStream,kind); - switch(kind) - { - case ObjectKind::function: - { - U32 functionTypeIndex = 0; - serializeVarUInt32(sectionStream,functionTypeIndex); - if(functionTypeIndex >= module.types.size()) - { - throw FatalSerializationException("invalid import function type index"); - } - module.functions.imports.push_back({{functionTypeIndex},std::move(moduleName),std::move(exportName)}); - break; - } - case ObjectKind::table: - { - TableType tableType; - serialize(sectionStream,tableType); - module.tables.imports.push_back({tableType,std::move(moduleName),std::move(exportName)}); - break; - } - case ObjectKind::memory: - { - MemoryType memoryType; - serialize(sectionStream,memoryType); - module.memories.imports.push_back({memoryType,std::move(moduleName),std::move(exportName)}); - break; - } - case ObjectKind::global: - { - GlobalType globalType; - serialize(sectionStream,globalType); - module.globals.imports.push_back({globalType,std::move(moduleName),std::move(exportName)}); - break; - } - default: throw FatalSerializationException("invalid ObjectKind"); - } - } - } - else - { - for(auto& functionImport : module.functions.imports) - { - serialize(sectionStream,functionImport.moduleName); - serialize(sectionStream,functionImport.exportName); - ObjectKind kind = ObjectKind::function; - serialize(sectionStream,kind); - serializeVarUInt32(sectionStream,functionImport.type.index); - } - for(auto& tableImport : module.tables.imports) - { - serialize(sectionStream,tableImport.moduleName); - serialize(sectionStream,tableImport.exportName); - ObjectKind kind = ObjectKind::table; - serialize(sectionStream,kind); - serialize(sectionStream,tableImport.type); - } - for(auto& memoryImport : module.memories.imports) - { - serialize(sectionStream,memoryImport.moduleName); - serialize(sectionStream,memoryImport.exportName); - ObjectKind kind = ObjectKind::memory; - serialize(sectionStream,kind); - serialize(sectionStream,memoryImport.type); - } - for(auto& globalImport : module.globals.imports) - { - serialize(sectionStream,globalImport.moduleName); - serialize(sectionStream,globalImport.exportName); - ObjectKind kind = ObjectKind::global; - serialize(sectionStream,kind); - serialize(sectionStream,globalImport.type); - } - } - }); - } - - template - void serializeFunctionSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::functionDeclarations,[&module](Stream& sectionStream) - { - Uptr numFunctions = module.functions.defs.size(); - serializeVarUInt32(sectionStream,numFunctions); - if(Stream::isInput) - { - // Grow the vector one element at a time: - // try to get a serialization exception before making a huge allocation for malformed input. - module.functions.defs.clear(); - for(Uptr functionIndex = 0;functionIndex < numFunctions;++functionIndex) - { - U32 functionTypeIndex = 0; - serializeVarUInt32(sectionStream,functionTypeIndex); - if(functionTypeIndex >= module.types.size()) { throw FatalSerializationException("invalid function type index"); } - module.functions.defs.push_back({{functionTypeIndex},{},{}}); - } - module.functions.defs.shrink_to_fit(); - } - else - { - for(FunctionDef& function : module.functions.defs) - { - serializeVarUInt32(sectionStream,function.type.index); - } - } - }); - } - - template - void serializeTableSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::table,[&module](Stream& sectionStream) - { - serialize(sectionStream,module.tables.defs); - }); - } - - template - void serializeMemorySection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::memory,[&module](Stream& sectionStream) - { - serialize(sectionStream,module.memories.defs); - }); - } - - template - void serializeGlobalSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::global,[&module](Stream& sectionStream) - { - serialize(sectionStream,module.globals.defs); - }); - } - - template - void serializeExportSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::export_,[&module](Stream& sectionStream) - { - serialize(sectionStream,module.exports); - }); - } - - template - void serializeStartSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::start,[&module](Stream& sectionStream) - { - serializeVarUInt32(sectionStream,module.startFunctionIndex); - }); - } - - template - void serializeElementSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::elem,[&module](Stream& sectionStream) - { - serialize(sectionStream,module.tableSegments); - }); - } - - template - void serializeCodeSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::functionDefinitions,[&module](Stream& sectionStream) - { - Uptr numFunctionBodies = module.functions.defs.size(); - serializeVarUInt32(sectionStream,numFunctionBodies); - if(Stream::isInput && numFunctionBodies != module.functions.defs.size()) - { throw FatalSerializationException("function and code sections have mismatched function counts"); } - for(FunctionDef& functionDef : module.functions.defs) { serializeFunctionBody(sectionStream,module,functionDef); } - }); - } - - template - void serializeDataSection(Stream& moduleStream,Module& module) - { - serializeSection(moduleStream,SectionType::data,[&module](Stream& sectionStream) - { - serialize(sectionStream,module.dataSegments); - }); - } - - void serializeModule(OutputStream& moduleStream,Module& module) - { - serializeConstant(moduleStream,"magic number",U32(magicNumber)); - serializeConstant(moduleStream,"version",U32(currentVersion)); - - if(module.types.size() > 0) { serializeTypeSection(moduleStream,module); } - if(module.functions.imports.size() > 0 - || module.tables.imports.size() > 0 - || module.memories.imports.size() > 0 - || module.globals.imports.size() > 0) { serializeImportSection(moduleStream,module); } - if(module.functions.defs.size() > 0) { serializeFunctionSection(moduleStream,module); } - if(module.tables.defs.size() > 0) { serializeTableSection(moduleStream,module); } - if(module.memories.defs.size() > 0) { serializeMemorySection(moduleStream,module); } - if(module.globals.defs.size() > 0) { serializeGlobalSection(moduleStream,module); } - if(module.exports.size() > 0) { serializeExportSection(moduleStream,module); } - if(module.startFunctionIndex != UINTPTR_MAX) { serializeStartSection(moduleStream,module); } - if(module.tableSegments.size() > 0) { serializeElementSection(moduleStream,module); } - if(module.functions.defs.size() > 0) { serializeCodeSection(moduleStream,module); } - if(module.dataSegments.size() > 0) { serializeDataSection(moduleStream,module); } - - for(auto& userSection : module.userSections) { serialize(moduleStream,userSection); } - } - void serializeModule(InputStream& moduleStream,Module& module) - { - serializeConstant(moduleStream,"magic number",U32(magicNumber)); - serializeConstant(moduleStream,"version",U32(currentVersion)); - - SectionType lastKnownSectionType = SectionType::unknown; - while(moduleStream.capacity()) - { - const SectionType sectionType = *(SectionType*)moduleStream.peek(sizeof(SectionType)); - if(sectionType != SectionType::user) - { - if(sectionType > lastKnownSectionType) { lastKnownSectionType = sectionType; } - else { throw FatalSerializationException("incorrect order for known section"); } - } - switch(sectionType) - { - case SectionType::type: serializeTypeSection(moduleStream,module); break; - case SectionType::import: serializeImportSection(moduleStream,module); break; - case SectionType::functionDeclarations: serializeFunctionSection(moduleStream,module); break; - case SectionType::table: serializeTableSection(moduleStream,module); break; - case SectionType::memory: serializeMemorySection(moduleStream,module); break; - case SectionType::global: serializeGlobalSection(moduleStream,module); break; - case SectionType::export_: serializeExportSection(moduleStream,module); break; - case SectionType::start: serializeStartSection(moduleStream,module); break; - case SectionType::elem: serializeElementSection(moduleStream,module); break; - case SectionType::functionDefinitions: serializeCodeSection(moduleStream,module); break; - case SectionType::data: serializeDataSection(moduleStream,module); break; - case SectionType::user: - { - UserSection& userSection = *module.userSections.insert(module.userSections.end(),UserSection()); - serialize(moduleStream,userSection); - break; - } - default: throw FatalSerializationException("unknown section ID"); - }; - }; - } - - void serialize(Serialization::InputStream& stream,Module& module) - { - serializeModule(stream,module); - IR::validateDefinitions(module); - } - void serialize(Serialization::OutputStream& stream,const Module& module) - { - serializeModule(stream,const_cast(module)); - } + template + struct WasmSerializationImpl + { + Injection injection; + + void serializeFunctionBody(OutputStream& sectionStream,Module& module,FunctionDef& functionDef) + { + ArrayOutputStream bodyStream; + + // Convert the function's local types into LocalSets: runs of locals of the same type. + LocalSet* localSets = (LocalSet*)alloca(sizeof(LocalSet)*functionDef.nonParameterLocalTypes.size()); + Uptr numLocalSets = 0; + if(functionDef.nonParameterLocalTypes.size()) + { + localSets[0].type = ValueType::any; + localSets[0].num = 0; + for(auto localType : functionDef.nonParameterLocalTypes) + { + if(localSets[numLocalSets].type != localType) + { + if(localSets[numLocalSets].type != ValueType::any) { ++numLocalSets; } + localSets[numLocalSets].type = localType; + localSets[numLocalSets].num = 0; + } + ++localSets[numLocalSets].num; + } + if(localSets[numLocalSets].type != ValueType::any) { ++numLocalSets; } + } + + // Serialize the local sets. + serializeVarUInt32(bodyStream,numLocalSets); + for(Uptr setIndex = 0;setIndex < numLocalSets;++setIndex) { serialize(bodyStream,localSets[setIndex]); } + + // Serialize the function code. + OperatorDecoderStream irDecoderStream(functionDef.code); + OperatorSerializerStream wasmOpEncoderStream(bodyStream,functionDef); + while(irDecoderStream) { irDecoderStream.decodeOp(wasmOpEncoderStream); }; + + std::vector bodyBytes = bodyStream.getBytes(); + serialize(sectionStream,bodyBytes); + } + + void serializeFunctionBody(InputStream& sectionStream,Module& module,FunctionDef& functionDef) + { + Uptr numBodyBytes = 0; + serializeVarUInt32(sectionStream,numBodyBytes); + + MemoryInputStream bodyStream(sectionStream.advance(numBodyBytes),numBodyBytes); + + // Deserialize local sets and unpack them into a linear array of local types. + Uptr numLocalSets = 0; + serializeVarUInt32(bodyStream,numLocalSets); + for(Uptr setIndex = 0;setIndex < numLocalSets;++setIndex) + { + LocalSet localSet; + serialize(bodyStream,localSet); + for(Uptr index = 0;index < localSet.num;++index) { functionDef.nonParameterLocalTypes.push_back(localSet.type); } + } + + // Deserialize the function code, validate it, and re-encode it in the IR format. + ArrayOutputStream irCodeByteStream; + + injection.addCall(module, irCodeByteStream); + + OperatorEncoderStream irEncoderStream(irCodeByteStream); + CodeValidationStream codeValidationStream(module,functionDef); + while(bodyStream.capacity()) + { + Opcode opcode; + serialize(bodyStream,opcode); + switch(opcode) + { + #define VISIT_OPCODE(_,name,nameString,Imm,...) \ + case Opcode::name: \ + { \ + Imm imm; \ + serialize(bodyStream,imm,functionDef); \ + injection.adjustCallIndex(module, imm); \ + codeValidationStream.name(imm); \ + irEncoderStream.name(imm); \ + injection.conditionallyAddCall(opcode, imm, module, irCodeByteStream); \ + break; \ + } + ENUM_NONFLOAT_OPERATORS(VISIT_OPCODE) + #undef VISIT_OPCODE + #define VISIT_OPCODE(_,name,nameString,...) \ + case Opcode::name: \ + throw FatalSerializationException("float instructions not allowed"); + ENUM_FLOAT_NONCONTROL_NONPARAMETRIC_OPERATORS(VISIT_OPCODE) + #undef VISIT_OPCODE + default: throw FatalSerializationException("unknown opcode"); + }; + }; + codeValidationStream.finish(); + + functionDef.code = std::move(irCodeByteStream.getBytes()); + } + + template + void serializeTypeSection(Stream& moduleStream,Module& module) + { + Injection& localInjection = injection; + serializeSection(moduleStream,SectionType::type,[&module,&localInjection](Stream& sectionStream) + { + serializeArray(sectionStream,module.types,[&module,&localInjection](Stream& stream,const FunctionType*& functionType) + { + serializeConstant(stream,"function type tag",U8(0x60)); + if(Stream::isInput) + { + std::vector parameterTypes; + ResultType returnType; + serialize(stream,parameterTypes); + serialize(stream,returnType); + functionType = FunctionType::get(returnType,parameterTypes); + localInjection.setTypeSlot(module, returnType, parameterTypes); + } + else + { + serialize(stream,const_cast&>(functionType->parameters)); + serialize(stream,const_cast(functionType->ret)); + } + }); + }); + + if(Stream::isInput) + { + injection.addTypeSlot(module); + } + } + template + void serializeImportSection(Stream& moduleStream,Module& module) + { + Injection& localInjection = injection; + serializeSection(moduleStream,SectionType::import,[&module,&localInjection](Stream& sectionStream) + { + Uptr size = module.functions.imports.size() + + module.tables.imports.size() + + module.memories.imports.size() + + module.globals.imports.size(); + serializeVarUInt32(sectionStream,size); + if(Stream::isInput) + { + for(Uptr index = 0;index < size;++index) + { + std::string moduleName; + std::string exportName; + ObjectKind kind = ObjectKind::invalid; + serialize(sectionStream,moduleName); + serialize(sectionStream,exportName); + throwIfNotValidUTF8(moduleName); + throwIfNotValidUTF8(exportName); + serialize(sectionStream,kind); + switch(kind) + { + case ObjectKind::function: + { + U32 functionTypeIndex = 0; + serializeVarUInt32(sectionStream,functionTypeIndex); + if(functionTypeIndex >= module.types.size()) + { + throw FatalSerializationException("invalid import function type index"); + } + module.functions.imports.push_back({{functionTypeIndex},std::move(moduleName),std::move(exportName)}); + break; + } + case ObjectKind::table: + { + TableType tableType; + serialize(sectionStream,tableType); + module.tables.imports.push_back({tableType,std::move(moduleName),std::move(exportName)}); + break; + } + case ObjectKind::memory: + { + MemoryType memoryType; + serialize(sectionStream,memoryType); + module.memories.imports.push_back({memoryType,std::move(moduleName),std::move(exportName)}); + break; + } + case ObjectKind::global: + { + GlobalType globalType; + serialize(sectionStream,globalType); + module.globals.imports.push_back({globalType,std::move(moduleName),std::move(exportName)}); + break; + } + default: throw FatalSerializationException("invalid ObjectKind"); + } + } + localInjection.addImport(module); + } + else + { + for(auto& functionImport : module.functions.imports) + { + serialize(sectionStream,functionImport.moduleName); + serialize(sectionStream,functionImport.exportName); + ObjectKind kind = ObjectKind::function; + serialize(sectionStream,kind); + serializeVarUInt32(sectionStream,functionImport.type.index); + } + for(auto& tableImport : module.tables.imports) + { + serialize(sectionStream,tableImport.moduleName); + serialize(sectionStream,tableImport.exportName); + ObjectKind kind = ObjectKind::table; + serialize(sectionStream,kind); + serialize(sectionStream,tableImport.type); + } + for(auto& memoryImport : module.memories.imports) + { + serialize(sectionStream,memoryImport.moduleName); + serialize(sectionStream,memoryImport.exportName); + ObjectKind kind = ObjectKind::memory; + serialize(sectionStream,kind); + serialize(sectionStream,memoryImport.type); + } + for(auto& globalImport : module.globals.imports) + { + serialize(sectionStream,globalImport.moduleName); + serialize(sectionStream,globalImport.exportName); + ObjectKind kind = ObjectKind::global; + serialize(sectionStream,kind); + serialize(sectionStream,globalImport.type); + } + } + }); + } + + template + void serializeFunctionSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::functionDeclarations,[&module](Stream& sectionStream) + { + Uptr numFunctions = module.functions.defs.size(); + serializeVarUInt32(sectionStream,numFunctions); + if(Stream::isInput) + { + // Grow the vector one element at a time: + // try to get a serialization exception before making a huge allocation for malformed input. + module.functions.defs.clear(); + for(Uptr functionIndex = 0;functionIndex < numFunctions;++functionIndex) + { + U32 functionTypeIndex = 0; + serializeVarUInt32(sectionStream,functionTypeIndex); + if(functionTypeIndex >= module.types.size()) { throw FatalSerializationException("invalid function type index"); } + module.functions.defs.push_back({{functionTypeIndex},{},{}}); + } + module.functions.defs.shrink_to_fit(); + } + else + { + for(FunctionDef& function : module.functions.defs) + { + serializeVarUInt32(sectionStream,function.type.index); + } + } + }); + } + + template + void serializeTableSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::table,[&module](Stream& sectionStream) + { + serialize(sectionStream,module.tables.defs); + }); + } + + template + void serializeMemorySection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::memory,[&module](Stream& sectionStream) + { + serialize(sectionStream,module.memories.defs); + }); + } + + template + void serializeGlobalSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::global,[&module](Stream& sectionStream) + { + serialize(sectionStream,module.globals.defs); + }); + } + + template + void serializeExportSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::export_,[&module](Stream& sectionStream) + { + serialize(sectionStream,module.exports); + }); + + injection.adjustExportIndex(module); + } + + template + void serializeStartSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::start,[&module](Stream& sectionStream) + { + serializeVarUInt32(sectionStream,module.startFunctionIndex); + }); + } + + template + void serializeElementSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::elem,[&module](Stream& sectionStream) + { + serialize(sectionStream,module.tableSegments); + }); + } + + template + void serializeCodeSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::functionDefinitions,[&module,this](Stream& sectionStream) + { + Uptr numFunctionBodies = module.functions.defs.size(); + serializeVarUInt32(sectionStream,numFunctionBodies); + if(Stream::isInput && numFunctionBodies != module.functions.defs.size()) + { throw FatalSerializationException("function and code sections have mismatched function counts"); } + for(FunctionDef& functionDef : module.functions.defs) { serializeFunctionBody(sectionStream,module,functionDef); } + }); + } + + template + void serializeDataSection(Stream& moduleStream,Module& module) + { + serializeSection(moduleStream,SectionType::data,[&module](Stream& sectionStream) + { + serialize(sectionStream,module.dataSegments); + }); + } + + void serializeModule(OutputStream& moduleStream,Module& module) + { + serializeConstant(moduleStream,"magic number",U32(magicNumber)); + serializeConstant(moduleStream,"version",U32(currentVersion)); + + if(module.types.size() > 0) { serializeTypeSection(moduleStream,module); } + if(module.functions.imports.size() > 0 + || module.tables.imports.size() > 0 + || module.memories.imports.size() > 0 + || module.globals.imports.size() > 0) { serializeImportSection(moduleStream,module); } + if(module.functions.defs.size() > 0) { serializeFunctionSection(moduleStream,module); } + if(module.tables.defs.size() > 0) { serializeTableSection(moduleStream,module); } + if(module.memories.defs.size() > 0) { serializeMemorySection(moduleStream,module); } + if(module.globals.defs.size() > 0) { serializeGlobalSection(moduleStream,module); } + if(module.exports.size() > 0) { serializeExportSection(moduleStream,module); } + if(module.startFunctionIndex != UINTPTR_MAX) { serializeStartSection(moduleStream,module); } + if(module.tableSegments.size() > 0) { serializeElementSection(moduleStream,module); } + if(module.functions.defs.size() > 0) { serializeCodeSection(moduleStream,module); } + if(module.dataSegments.size() > 0) { serializeDataSection(moduleStream,module); } + + for(auto& userSection : module.userSections) { serialize(moduleStream,userSection); } + } + void serializeModule(InputStream& moduleStream,Module& module) + { + serializeConstant(moduleStream,"magic number",U32(magicNumber)); + serializeConstant(moduleStream,"version",U32(currentVersion)); + + SectionType lastKnownSectionType = SectionType::unknown; + while(moduleStream.capacity()) + { + const SectionType sectionType = *(SectionType*)moduleStream.peek(sizeof(SectionType)); + if(sectionType != SectionType::user) + { + if(sectionType > lastKnownSectionType) { lastKnownSectionType = sectionType; } + else { throw FatalSerializationException("incorrect order for known section"); } + } + switch(sectionType) + { + case SectionType::type: serializeTypeSection(moduleStream,module); break; + case SectionType::import: serializeImportSection(moduleStream,module); break; + case SectionType::functionDeclarations: serializeFunctionSection(moduleStream,module); break; + case SectionType::table: serializeTableSection(moduleStream,module); break; + case SectionType::memory: serializeMemorySection(moduleStream,module); break; + case SectionType::global: serializeGlobalSection(moduleStream,module); break; + case SectionType::export_: serializeExportSection(moduleStream,module); break; + case SectionType::start: serializeStartSection(moduleStream,module); break; + case SectionType::elem: serializeElementSection(moduleStream,module); break; + case SectionType::functionDefinitions: serializeCodeSection(moduleStream,module); break; + case SectionType::data: serializeDataSection(moduleStream,module); break; + case SectionType::user: + { + UserSection& userSection = *module.userSections.insert(module.userSections.end(),UserSection()); + serialize(moduleStream,userSection); + break; + } + default: throw FatalSerializationException("unknown section ID"); + }; + }; + } + }; + + void serialize(Serialization::InputStream& stream,Module& module) + { + WasmSerializationImpl impl; + impl.serializeModule(stream,module); + IR::validateDefinitions(module); + } + void serializeWithInjection(Serialization::InputStream& stream,Module& module) + { + WasmSerializationImpl impl; + impl.serializeModule(stream,module); + IR::validateDefinitions(module); + } + void serialize(Serialization::OutputStream& stream,const Module& module) + { + WasmSerializationImpl impl; + impl.serializeModule(stream,const_cast(module)); + } } diff --git a/tests/slow_tests/slow_tests.cpp b/tests/slow_tests/slow_tests.cpp index 5bb1d19ea40..f64a26e25ff 100644 --- a/tests/slow_tests/slow_tests.cpp +++ b/tests/slow_tests/slow_tests.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include @@ -45,6 +46,7 @@ #include #include +#include using namespace eos; using namespace chain; @@ -522,8 +524,8 @@ R"( (export "uint64_unpack" (func $uint64_unpack)) (export "String_unpack" (func $String_unpack)) (export "Transfer_unpack" (func $Transfer_unpack)) - (export "onInit" (func $onInit)) - (export "onApply_Transfer_simplecoin" (func $onApply_Transfer_simplecoin)) + (export "init" (func $init)) + (export "apply_simplecoin_transfer" (func $apply_simplecoin_transfer)) (func $malloc (param $0 i32) (result i32) (local $1 i32) (i32.store offset=8208 @@ -834,7 +836,7 @@ R"( ) ) ) - (func $onInit + (func $init (call $assert (i32.const 1) (i32.const 8240) @@ -862,7 +864,7 @@ R"( (i32.const 8) ) ) - (func $onApply_Transfer_simplecoin + (func $apply_simplecoin_transfer (local $0 i32) (local $1 i32) (local $2 i64) @@ -1124,4 +1126,54 @@ R"( } } FC_LOG_AND_RETHROW() } +//Test account script float rejection +BOOST_FIXTURE_TEST_CASE(create_script_w_loop, testing_fixture) +{ try { + Make_Blockchain(chain); + chain.produce_blocks(10); + Make_Account(chain, currency); + chain.produce_blocks(1); + + + types::setcode handler; + handler.account = "currency"; + + auto wasm = assemble_wast( infinite_wast ); + handler.code.resize(wasm.size()); + memcpy( handler.code.data(), wasm.data(), wasm.size() ); + + { + eos::chain::SignedTransaction trx; + trx.scope = {"currency"}; + trx.messages.resize(1); + trx.messages[0].code = config::EosContractName; + trx.setMessage(0, "setcode", handler); + trx.expiration = chain.head_block_time() + 100; + trx.set_reference_block(chain.head_block_id()); + chain.push_transaction(trx); + chain.produce_blocks(1); + } + + + { + eos::chain::SignedTransaction trx; + trx.scope = sort_names({"currency","inita"}); + trx.emplaceMessage("currency", + vector{ {"currency","active"} }, + "transfer", types::transfer{"currency", "inita", 1}); + trx.expiration = chain.head_block_time() + 100; + trx.set_reference_block(chain.head_block_id()); + try + { + wlog("starting long transaction"); + chain.push_transaction(trx); + BOOST_FAIL("transaction should have failed with checktime_exceeded"); + } + catch (const eos::chain::checktime_exceeded& check) + { + wlog("checktime_exceeded caught"); + } + } +} FC_LOG_AND_RETHROW() } + BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/slow_tests/wast/loop.cpp b/tests/slow_tests/wast/loop.cpp new file mode 100644 index 00000000000..03a04dadd7c --- /dev/null +++ b/tests/slow_tests/wast/loop.cpp @@ -0,0 +1,90 @@ +extern "C" { +typedef long long uint64_t; +typedef unsigned int uint32_t; +typedef uint64_t AccountName; +int load( const void* keyptr, int keylen, void* valueptr, int valuelen ); +void store( const void* keyptr, int keylen, const void* valueptr, int valuelen ); +int readMessage( void* dest, int destsize ); +int remove( const void* key, int keyLength ); +void printi( uint64_t ); +void print( const char* str ); +void assert( int test, const char* message ); +void* memcpy( void* dest, const void* src, uint32_t size ); +uint64_t name_to_int64( const char* name ); +bool loopControl(int i); +/* +void* malloc( unsigned int size ) { + static char dynamic_memory[1024*8]; + static int start = 0; + int old_start = start; + start += 8*((size+7)/8); + assert( start < sizeof(dynamic_memory), "out of memory" ); + return &dynamic_memory[old_start]; +} +*/ +} + + +template +int load( const Key& key, Value& v ) { return load( &key, sizeof(Key), &v, sizeof(Value) ); } +template +void store( const Key& key, const Value& v ) { store( &key, sizeof(key), &v, sizeof(v) ); } +template +void remove( const Key& key ) { remove( &key, sizeof(key) ); } +template +void readMessage( Message& m ) { readMessage( &m, sizeof(Message) ); } +/// END BUILT IN LIBRARY.... everything below this is "user contract" + + +extern "C" { +struct Transfer { + uint64_t from; + uint64_t to; + uint64_t amount; + char memo[]; +}; + +static_assert( sizeof(Transfer) == 3*sizeof(uint64_t), "unexpected padding" ); + +struct Balance { + uint64_t balance; +}; + +void init() { + static Balance initial = { uint64_t(10)*1000*1000ll*1000ll }; + static AccountName simplecoin; + simplecoin = name_to_int64( "simplecoin" ); + print( "on_init called with "); printi( initial.balance ); print( "\n"); + store( simplecoin, initial ); +} + +void apply_simplecoin_transfer() { + static Transfer message; + static Balance from_balance; + static Balance to_balance; + to_balance.balance = 0; + + readMessage( message ); + load( message.from, from_balance ); + load( message.to, to_balance ); + + assert( from_balance.balance >= message.amount, "insufficient funds" ); + + int i = 0; + bool cont = true; + + while (cont) { + cont = loopControl(i++); + } + + from_balance.balance -= message.amount; + to_balance.balance += message.amount; + + if( from_balance.balance ) + store( message.from, from_balance ); + else + remove( message.from ); + + store( message.to, to_balance ); +} +} // extern "C" diff --git a/tests/slow_tests/wast/loop.wast b/tests/slow_tests/wast/loop.wast new file mode 100644 index 00000000000..ab4e2c2c10e --- /dev/null +++ b/tests/slow_tests/wast/loop.wast @@ -0,0 +1,168 @@ +const char* wast_apply = R"====((module + (type $FUNCSIG$ji (func (param i32) (result i64))) + (type $FUNCSIG$vi (func (param i32))) + (type $FUNCSIG$vj (func (param i64))) + (type $FUNCSIG$vii (func (param i32 i32))) + (type $FUNCSIG$ii (func (param i32) (result i32))) + (type $FUNCSIG$viiii (func (param i32 i32 i32 i32))) + (type $FUNCSIG$iii (func (param i32 i32) (result i32))) + (type $FUNCSIG$iiiii (func (param i32 i32 i32 i32) (result i32))) + (import "env" "assert" (func $assert (param i32 i32))) + (import "env" "load" (func $load (param i32 i32 i32 i32) (result i32))) + (import "env" "loopControl" (func $loopControl (param i32) (result i32))) + (import "env" "name_to_int64" (func $name_to_int64 (param i32) (result i64))) + (import "env" "print" (func $print (param i32))) + (import "env" "printi" (func $printi (param i64))) + (import "env" "readMessage" (func $readMessage (param i32 i32) (result i32))) + (import "env" "remove" (func $remove (param i32 i32) (result i32))) + (import "env" "store" (func $store (param i32 i32 i32 i32))) + (table 0 anyfunc) + (memory $0 1) + (data (i32.const 16) "\00\e4\0bT\02\00\00\00") + (data (i32.const 32) "simplecoin\00") + (data (i32.const 48) "on_init called with \00") + (data (i32.const 80) "\n\00") + (data (i32.const 128) "insufficient funds\00") + (export "memory" (memory $0)) + (export "init" (func $init)) + (export "apply_simplecoin_transfer" (func $apply_simplecoin_transfer)) + (func $init + (i64.store offset=24 + (i32.const 0) + (call $name_to_int64 + (i32.const 32) + ) + ) + (call $print + (i32.const 48) + ) + (call $printi + (i64.load offset=16 + (i32.const 0) + ) + ) + (call $print + (i32.const 80) + ) + (call $store + (i32.const 24) + (i32.const 8) + (i32.const 16) + (i32.const 8) + ) + ) + (func $apply_simplecoin_transfer + (local $0 i32) + (local $1 i64) + (local $2 i64) + (local $3 i32) + (set_local $3 + (i32.const 0) + ) + (i64.store offset=120 + (i32.const 0) + (i64.const 0) + ) + (drop + (call $readMessage + (i32.const 88) + (i32.const 24) + ) + ) + (drop + (call $load + (i32.const 88) + (i32.const 8) + (i32.const 112) + (i32.const 8) + ) + ) + (drop + (call $load + (i32.const 96) + (i32.const 8) + (i32.const 120) + (i32.const 8) + ) + ) + (call $assert + (i64.ge_s + (i64.load offset=112 + (i32.const 0) + ) + (i64.load offset=104 + (i32.const 0) + ) + ) + (i32.const 128) + ) + (loop $label$0 + (set_local $0 + (call $loopControl + (get_local $3) + ) + ) + (set_local $3 + (i32.add + (get_local $3) + (i32.const 1) + ) + ) + (br_if $label$0 + (get_local $0) + ) + ) + (i64.store offset=112 + (i32.const 0) + (tee_local $2 + (i64.sub + (i64.load offset=112 + (i32.const 0) + ) + (tee_local $1 + (i64.load offset=104 + (i32.const 0) + ) + ) + ) + ) + ) + (i64.store offset=120 + (i32.const 0) + (i64.add + (get_local $1) + (i64.load offset=120 + (i32.const 0) + ) + ) + ) + (block $label$1 + (block $label$2 + (br_if $label$2 + (i64.eqz + (get_local $2) + ) + ) + (call $store + (i32.const 88) + (i32.const 8) + (i32.const 112) + (i32.const 8) + ) + (br $label$1) + ) + (drop + (call $remove + (i32.const 88) + (i32.const 8) + ) + ) + ) + (call $store + (i32.const 96) + (i32.const 8) + (i32.const 120) + (i32.const 8) + ) + ) +))===="; diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index 498da07288a..44ab8fe0957 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -143,7 +143,7 @@ BOOST_FIXTURE_TEST_CASE(trx_variant, testing_fixture) { Name from("from"), to("to"); uint64_t amount = 10; - eos::chain::SignedTransaction trx; + eos::chain::ProcessedTransaction trx; trx.scope = sort_names({from,to}); trx.emplaceMessage("eos", vector{ {from,"active"} },