提交 9a47bc60 编写于 作者: D Daniel Larimer 提交者: GitHub

Merge pull request #67 from brianjohnson5972/master

Limiting WASM exectuion time
add_subdirectory(currency)
add_subdirectory(exchange)
#add_subdirectory(social)
\ No newline at end of file
add_subdirectory(infinite)
#add_subdirectory(social)
file(GLOB SOURCE_FILES "*.cpp")
add_wast_target(infinite "${SOURCE_FILES}" "${CMAKE_SOURCE_DIR}/contracts" ${CMAKE_CURRENT_SOURCE_DIR})
#include <infinite/infinite.hpp> /// 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 >() );
}
}
}
#include <eoslib/eos.hpp>
#include <eoslib/token.hpp>
#include <eoslib/db.hpp>
namespace infinite {
typedef eos::token<uint64_t,N(currency)> 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<N(currency),N(currency),N(account),Account,uint64_t>;
/**
* 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
......@@ -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" )
......
#pragma once
#include <eos/chain/exceptions.hpp>
#include <eos/chain/message.hpp>
#include <eos/chain/message_handling_contexts.hpp>
#include <Runtime/Runtime.h>
......@@ -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<AccountName, ModuleState> instances;
fc::time_point checktimeStart;
wasm_interface();
};
......
......@@ -10,6 +10,7 @@
#include "IR/Validate.h"
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/account_object.hpp>
#include <chrono>
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<Value> invokeArgs(1);
invokeArgs[0] = U32(bytes);
checktimeStart = fc::time_point::now();
auto result = Runtime::invokeFunction(alloc_function,invokeArgs);
return &memoryRef<char>( 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);
......
......@@ -46,7 +46,7 @@ class ProducerVotesObject : public chainbase::object<chain::producer_votes_objec
void updateVotes(types::ShareType deltaVotes, types::UInt128 currentRaceTime);
/// @brief Get the number of votes this producer has received
types::ShareType getVotes() const { return race.speed; }
pair<types::ShareType,id_type> getVoteOrder()const { return std::tie( race.speed, id ); }
pair<types::ShareType,id_type> 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<chain::producer_votes_objec
types::UInt128 projectedRaceFinishTime() const { return race.projectedFinishTime; }
typedef std::pair<types::UInt128,id_type> rft_order_type;
rft_order_type projectedRaceFinishTimeOrder() const { return std::tie(race.projectedFinishTime,id); }
rft_order_type projectedRaceFinishTimeOrder() const { return {race.projectedFinishTime,id}; }
};
/**
......
......@@ -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);
}
......@@ -29,6 +29,7 @@
#include <eos/chain/account_object.hpp>
#include <eos/chain/key_value_object.hpp>
#include <eos/chain/block_summary_object.hpp>
#include <eos/chain/wasm_interface.hpp>
#include <eos/utilities/tempdir.hpp>
......@@ -45,6 +46,7 @@
#include <currency/currency.wast.hpp>
#include <exchange/exchange.wast.hpp>
#include <infinite/infinite.wast.hpp>
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<types::AccountPermission>{ {"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()
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<typename Key, typename Value>
int load( const Key& key, Value& v ) { return load( &key, sizeof(Key), &v, sizeof(Value) ); }
template<typename Key, typename Value>
void store( const Key& key, const Value& v ) { store( &key, sizeof(key), &v, sizeof(v) ); }
template<typename Key>
void remove( const Key& key ) { remove( &key, sizeof(key) ); }
template<typename Message>
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"
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)
)
)
))====";
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册