Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #573 from EOSIO/331_abi_access_to_tables
Browse files Browse the repository at this point in the history
ABI Verification of Access to Tables
  • Loading branch information
heifner authored Oct 25, 2017
2 parents c3746c3 + 373eb6b commit 224e238
Show file tree
Hide file tree
Showing 14 changed files with 914 additions and 177 deletions.
36 changes: 33 additions & 3 deletions CMakeModules/wasm.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ macro(add_wast_target target INCLUDE_FOLDERS DESTINATION_FOLDER)
VERBATIM
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${target}.wast)

add_custom_command(OUTPUT ${DESTINATION_FOLDER}/${target}.wast.hpp
DEPENDS ${DESTINATION_FOLDER}/${target}.wast
COMMAND echo "const char* ${target}_wast = R\"=====(" > ${DESTINATION_FOLDER}/${target}.wast.hpp
Expand All @@ -138,9 +138,39 @@ macro(add_wast_target target INCLUDE_FOLDERS DESTINATION_FOLDER)
COMMENT "Generating ${target}.wast.hpp"
VERBATIM
)

add_custom_target(${target} ALL DEPENDS ${DESTINATION_FOLDER}/${target}.wast.hpp)

if (EXISTS ${DESTINATION_FOLDER}/${target}.abi )
add_custom_command(OUTPUT ${DESTINATION_FOLDER}/${target}.abi.hpp
DEPENDS ${DESTINATION_FOLDER}/${target}.abi
COMMAND echo "const char* ${target}_abi = R\"=====(" > ${DESTINATION_FOLDER}/${target}.abi.hpp
COMMAND cat ${DESTINATION_FOLDER}/${target}.abi >> ${DESTINATION_FOLDER}/${target}.abi.hpp
COMMAND echo ")=====\";" >> ${DESTINATION_FOLDER}/${target}.abi.hpp
COMMENT "Generating ${target}.abi.hpp"
VERBATIM
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${target}.abi.hpp)
else()
endif()


add_custom_target(${target} ALL DEPENDS ${DESTINATION_FOLDER}/${target}.wast.hpp ${extra_target_dependency})

set_property(TARGET ${target} PROPERTY INCLUDE_DIRECTORIES ${INCLUDE_FOLDERS})

set(extra_target_dependency)

endmacro(add_wast_target)

function(add_wast_abi_target target INCLUDE_FOLDERS SOURCE_FOLDER DESTINATION_FOLDER)
add_custom_command(OUTPUT ${DESTINATION_FOLDER}/${target}.abi.hpp
DEPENDS ${SOURCE_FOLDER}/${target}.abi
COMMAND echo "const char* ${target}_abi = R\"=====(" > ${DESTINATION_FOLDER}/${target}.abi.hpp
COMMAND cat ${SOURCE_FOLDER}/${target}.abi >> ${DESTINATION_FOLDER}/${target}.abi.hpp
COMMAND echo ")=====\";" >> ${DESTINATION_FOLDER}/${target}.abi.hpp
COMMENT "Generating ${target}.abi.hpp"
VERBATIM
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${target}.abi.hpp)
set(extra_target_dependency ${DESTINATION_FOLDER}/${target}.abi.hpp)
add_wast_target(${target} "${INCLUDE_FOLDERS}" ${CMAKE_CURRENT_BINARY_DIR})
endfunction(add_wast_abi_target)
196 changes: 98 additions & 98 deletions contracts/test_api/test_db.cpp

Large diffs are not rendered by default.

23 changes: 9 additions & 14 deletions libraries/chain/chain_controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,8 @@ void chain_controller::apply_message(apply_context& context)
: _skip_flags | created_block
? _create_block_trans_execution_time
: _trans_execution_time;
wasm_interface::get().apply(context, execution_time);
const bool is_received_block = _skip_flags & received_block;
wasm_interface::get().apply(context, execution_time, is_received_block);
}

} FC_CAPTURE_AND_RETHROW((context.msg)) }
Expand Down Expand Up @@ -1449,10 +1450,8 @@ ProcessedTransaction chain_controller::transaction_from_variant( const fc::varia
result.messages[i].data = message_to_binary( result.messages[i].code, result.messages[i].type, data );
/*
const auto& code_account = _db.get<account_object,by_name>( result.messages[i].code );
if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
fc::datastream<const char*> ds( code_account.abi.data(), code_account.abi.size() );
eos::types::Abi abi;
fc::raw::unpack( ds, abi );
eos::types::Abi abi;
if( AbiSerializer::to_abi(code_account.abi, abi) ) {
types::AbiSerializer abis( abi );
result.messages[i].data = abis.variantToBinary( abis.getActionType( result.messages[i].type ), data );
}
Expand All @@ -1471,21 +1470,17 @@ ProcessedTransaction chain_controller::transaction_from_variant( const fc::varia
vector<char> chain_controller::message_to_binary( Name code, Name type, const fc::variant& obj )const
{ try {
const auto& code_account = _db.get<account_object,by_name>( code );
if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
fc::datastream<const char*> ds( code_account.abi.data(), code_account.abi.size() );
eos::types::Abi abi;
fc::raw::unpack( ds, abi );
eos::types::Abi abi;
if( types::AbiSerializer::to_abi(code_account.abi, abi) ) {
types::AbiSerializer abis( abi );
return abis.variantToBinary( abis.getActionType( type ), obj );
}
return vector<char>();
} FC_CAPTURE_AND_RETHROW( (code)(type)(obj) ) }
fc::variant chain_controller::message_from_binary( Name code, Name type, const vector<char>& data )const {
const auto& code_account = _db.get<account_object,by_name>( code );
if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
fc::datastream<const char*> ds( code_account.abi.data(), code_account.abi.size() );
eos::types::Abi abi;
fc::raw::unpack( ds, abi );
eos::types::Abi abi;
if( types::AbiSerializer::to_abi(code_account.abi, abi) ) {
types::AbiSerializer abis( abi );
return abis.binaryToVariant( abis.getActionType( type ), data );
}
Expand Down Expand Up @@ -1513,7 +1508,7 @@ fc::variant chain_controller::transaction_to_variant( const ProcessedTransactio
SET_FIELD( msg_mvo, msg, authorization );

const auto& code_account = _db.get<account_object,by_name>( msg.code );
if( code_account.abi.size() > 4 ) { /// 4 == packsize of empty Abi
if( !types::AbiSerializer::is_empty_abi(code_account.abi) ) {
try {
msg_mvo( "data", message_from_binary( msg.code, msg.type, msg.data ) );
msg_mvo( "hex_data", msg.data );
Expand Down
17 changes: 16 additions & 1 deletion libraries/chain/include/eos/chain/wasm_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,37 @@ class wasm_memory;
*/
class wasm_interface {
public:
enum key_type {
str,
i64,
i128i128,
i64i64i64,
invalid_key_type
};
typedef map<Name, key_type> TableMap;
struct ModuleState {
Runtime::ModuleInstance* instance = nullptr;
IR::Module* module = nullptr;
int mem_start = 0;
int mem_end = 1<<16;
vector<char> init_memory;
fc::sha256 code_version;
TableMap table_key_types;
bool tables_fixed = false;
};

static wasm_interface& get();

void init( apply_context& c );
void apply( apply_context& c, uint32_t execution_time );
void apply( apply_context& c, uint32_t execution_time, bool received_block );
void validate( apply_context& c );
void precondition( apply_context& c );

int64_t current_execution_time();

static key_type to_key_type(const types::TypeName& type_name);
static std::string to_type_name(key_type key_type);

apply_context* current_apply_context = nullptr;
apply_context* current_validate_context = nullptr;
apply_context* current_precondition_context = nullptr;
Expand All @@ -50,6 +63,8 @@ class wasm_interface {
Runtime::ModuleInstance* current_module = nullptr;
ModuleState* current_state = nullptr;
wasm_memory* current_memory_management = nullptr;
TableMap* table_key_types = nullptr;
bool tables_fixed = false;

uint32_t checktime_limit = 0;

Expand Down
141 changes: 105 additions & 36 deletions libraries/chain/wasm_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include "IR/Validate.h"
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/account_object.hpp>
#include <eos/types/AbiSerializer.hpp>
#include <chrono>
#include <boost/lexical_cast.hpp>

namespace eos { namespace chain {
using namespace IR;
Expand Down Expand Up @@ -44,6 +46,37 @@ namespace eos { namespace chain {
wasm_interface::wasm_interface() {
}

wasm_interface::key_type wasm_interface::to_key_type(const types::TypeName& type_name)
{
if ("str" == type_name)
return str;
if ("i64" == type_name)
return i64;
if ("i128i128" == type_name)
return i128i128;
if ("i64i64i64" == type_name)
return i64i64i64;

return invalid_key_type;
}

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

#ifdef NDEBUG
const int CHECKTIME_LIMIT = 3000;
#else
Expand Down Expand Up @@ -96,54 +129,66 @@ DEFINE_INTRINSIC_FUNCTION0(env,checktime,checktime,none) {
}


#define VERIFY_TABLE(TYPE) \
const auto table_name = Name(table); \
auto& wasm = wasm_interface::get(); \
if (wasm.table_key_types) \
{ \
auto table_key = wasm.table_key_types->find(table_name); \
if (table_key == wasm.table_key_types->end()) \
{ \
FC_ASSERT(!wasm.tables_fixed, "abi did not define table ${t}", ("t", table_name)); \
wasm.table_key_types->emplace(std::make_pair(table_name,wasm_interface::TYPE)); \
} \
else \
{ \
FC_ASSERT(wasm_interface::TYPE == table_key->second, "abi definition for ${table} expects \"${type}\", but code is requesting \"" #TYPE "\"", ("table",table_name)("type",wasm_interface::to_type_name(table_key->second))); \
} \
}

#define READ_RECORD(READFUNC, INDEX, SCOPE) \
auto lambda = [&](apply_context* ctx, INDEX::value_type::key_type* keys, char *data, uint32_t datalen) -> int32_t { \
auto res = ctx->READFUNC<INDEX, SCOPE>( Name(scope), Name(code), Name(table), keys, data, datalen); \
auto res = ctx->READFUNC<INDEX, SCOPE>( Name(scope), Name(code), table_name, keys, data, datalen); \
if (res >= 0) res += INDEX::value_type::number_of_keys*sizeof(INDEX::value_type::key_type); \
return res; \
}; \
return validate<decltype(lambda), INDEX::value_type::key_type, INDEX::value_type::number_of_keys>(valueptr, valuelen, lambda);

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

#define DEFINE_RECORD_UPDATE_FUNCTIONS(OBJTYPE, INDEX) \
DEFINE_INTRINSIC_FUNCTION4(env,store_##OBJTYPE,store_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \
VERIFY_TABLE(OBJTYPE) \
UPDATE_RECORD(store_record, INDEX, valuelen); \
} \
DEFINE_INTRINSIC_FUNCTION4(env,update_##OBJTYPE,update_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr,i32,valuelen) { \
VERIFY_TABLE(OBJTYPE) \
UPDATE_RECORD(update_record, INDEX, valuelen); \
} \
DEFINE_INTRINSIC_FUNCTION3(env,remove_##OBJTYPE,remove_##OBJTYPE,i32,i64,scope,i64,table,i32,valueptr) { \
VERIFY_TABLE(OBJTYPE) \
UPDATE_RECORD(remove_record, INDEX, sizeof(typename INDEX::value_type::key_type)*INDEX::value_type::number_of_keys); \
}

#define DEFINE_RECORD_READ_FUNCTIONS(OBJTYPE, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_INTRINSIC_FUNCTION5(env,load_##FUNCPREFIX##OBJTYPE,load_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(load_record, INDEX, SCOPE); \
} \
DEFINE_INTRINSIC_FUNCTION5(env,front_##FUNCPREFIX##OBJTYPE,front_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(front_record, INDEX, SCOPE); \
} \
DEFINE_INTRINSIC_FUNCTION5(env,back_##FUNCPREFIX##OBJTYPE,back_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(back_record, INDEX, SCOPE); \
} \
DEFINE_INTRINSIC_FUNCTION5(env,next_##FUNCPREFIX##OBJTYPE,next_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(next_record, INDEX, SCOPE); \
} \
DEFINE_INTRINSIC_FUNCTION5(env,previous_##FUNCPREFIX##OBJTYPE,previous_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(previous_record, INDEX, SCOPE); \
} \
DEFINE_INTRINSIC_FUNCTION5(env,lower_bound_##FUNCPREFIX##OBJTYPE,lower_bound_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(lower_bound_record, INDEX, SCOPE); \
} \
DEFINE_INTRINSIC_FUNCTION5(env,upper_bound_##FUNCPREFIX##OBJTYPE,upper_bound_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
READ_RECORD(upper_bound_record, INDEX, SCOPE); \
#define DEFINE_RECORD_READ_FUNCTION(OBJTYPE, ACTION, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_INTRINSIC_FUNCTION5(env,ACTION##_##FUNCPREFIX##OBJTYPE,ACTION##_##FUNCPREFIX##OBJTYPE,i32,i64,scope,i64,code,i64,table,i32,valueptr,i32,valuelen) { \
VERIFY_TABLE(OBJTYPE) \
READ_RECORD(ACTION##_record, INDEX, SCOPE); \
}

#define DEFINE_RECORD_READ_FUNCTIONS(OBJTYPE, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, load, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, front, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, back, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, next, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, previous, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, lower_bound, FUNCPREFIX, INDEX, SCOPE) \
DEFINE_RECORD_READ_FUNCTION(OBJTYPE, upper_bound, FUNCPREFIX, INDEX, SCOPE)

DEFINE_RECORD_UPDATE_FUNCTIONS(i64, key_value_index);
DEFINE_RECORD_READ_FUNCTIONS(i64,,key_value_index, by_scope_primary);

Expand All @@ -158,14 +203,16 @@ DEFINE_RECORD_READ_FUNCTIONS(i64i64i64, tertiary_, key64x64x64_value_index, by_


#define UPDATE_RECORD_STR(FUNCTION) \
VERIFY_TABLE(str) \
auto lambda = [&](apply_context* ctx, std::string* keys, char *data, uint32_t datalen) -> int32_t { \
return ctx->FUNCTION<keystr_value_object>( Name(scope), Name(ctx->code.value), Name(table), keys, data, datalen); \
return ctx->FUNCTION<keystr_value_object>( Name(scope), Name(ctx->code.value), table_name, keys, data, datalen); \
}; \
return validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda);

#define READ_RECORD_STR(FUNCTION) \
VERIFY_TABLE(str) \
auto lambda = [&](apply_context* ctx, std::string* keys, char *data, uint32_t datalen) -> int32_t { \
auto res = ctx->FUNCTION<keystr_value_index, by_scope_primary>( Name(scope), Name(code), Name(table), keys, data, datalen); \
auto res = ctx->FUNCTION<keystr_value_index, by_scope_primary>( Name(scope), Name(code), table_name, keys, data, datalen); \
return res; \
}; \
return validate_str<decltype(lambda)>(keyptr, keylen, valueptr, valuelen, lambda);
Expand Down Expand Up @@ -697,14 +744,18 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
} FC_CAPTURE_AND_RETHROW() }


void wasm_interface::apply( apply_context& c, uint32_t execution_time ) {
void wasm_interface::apply( apply_context& c, uint32_t execution_time, bool received_block ) {
try {
current_validate_context = &c;
current_precondition_context = &c;
current_apply_context = &c;
checktime_limit = execution_time;

load( c.code, c.db );
// if this is a received_block, then ignore the table_key_types
if (received_block)
table_key_types = nullptr;

vm_apply();

} FC_CAPTURE_AND_RETHROW() }
Expand All @@ -729,14 +780,15 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {

auto& state = instances[name];
if( state.code_version != recipient.code_version ) {
if( state.instance ) {
/// TODO: free existing instance and module
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;
state.code_version = fc::sha256();
}
state.module = new IR::Module();
state.instance = nullptr;
state.module = nullptr;
state.code_version = fc::sha256();
}
state.module = new IR::Module();
state.table_key_types.clear();

try
{
Expand Down Expand Up @@ -771,6 +823,21 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
//std::cerr <<"\n";
state.code_version = recipient.code_version;
// idump((state.code_version));

types::Abi abi;
if( types::AbiSerializer::to_abi(recipient.abi, abi) )
{
state.tables_fixed = true;
for(auto& table : abi.tables)
{
const auto key_type = to_key_type(table.indextype);
if (key_type == invalid_key_type)
throw Serialization::FatalSerializationException("For code \"" + (std::string)name + "\" indextype of \"" +
table.indextype + "\" referenced but not supported");

state.table_key_types.emplace(std::make_pair(table.table, key_type));
}
}
}
catch(Serialization::FatalSerializationException exception)
{
Expand All @@ -790,9 +857,11 @@ DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
throw;
}
}
current_module = state.instance;
current_memory = getDefaultMemory( current_module );
current_state = &state;
current_module = state.instance;
current_memory = getDefaultMemory( current_module );
current_state = &state;
table_key_types = &state.table_key_types;
tables_fixed = state.tables_fixed;
}

wasm_memory::wasm_memory(wasm_interface& interface)
Expand Down
Loading

0 comments on commit 224e238

Please sign in to comment.