提交 0a301c4c 编写于 作者: D Daniel Larimer

progress toward testing exchange contract

上级 2890b469
......@@ -3,8 +3,7 @@ currency.wast: currency.cpp Makefile ../eoslib/eos.hpp currency.hpp
/Users/dlarimer/Downloads/llvm/build/bin/llc -asm-verbose=false .currency.bc
/Users/dlarimer/eos/libraries/binaryen/bin/s2wasm -s 1024 .currency.s > currency.wast
wc -l currency.wast
echo '#pragma once ' > currency.wast.hpp
echo 'const char* currency_wast = R"=====(' >> currency.wast.hpp
echo 'const char* currency_wast = R"=====(' > currency.wast.hpp
cat currency.wast >> currency.wast.hpp
echo ')=====";' >> currency.wast.hpp
......
#pragma once
const char* currency_wast = R"=====(
(module
(type $FUNCSIG$vj (func (param i64)))
......
......@@ -39,8 +39,7 @@ int32_t back_primary_i128i128( AccountName scope, AccountName code, TableName ta
int32_t next_primary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len );
int32_t previous_primary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len );
int32_t load_primary_i128i128( AccountName scope, AccountName code, TableName table,
const void* primary, void* data, uint32_t len );
int32_t load_primary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len );
int32_t upper_bound_primary_i128i128( AccountName scope, AccountName code, TableName table,
const void* key, void* data, uint32_t len );
......
......@@ -56,8 +56,8 @@ struct table_impl<sizeof(uint128_t),sizeof(uint128_t)> {
static bool remove( uint64_t scope, uint64_t table, const void* data ) {
return remove_i128i128( scope, table, data );
}
static int32_t load_primary( uint64_t scope, uint64_t code, uint64_t table, const void* primary, void* data, uint32_t len ) {
return load_primary_i128i128( scope, code, table, primary, data, len );
static int32_t load_primary( uint64_t scope, uint64_t code, uint64_t table, void* data, uint32_t len ) {
return load_primary_i128i128( scope, code, table, data, len );
}
static int32_t front_secondary( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ) {
return front_secondary_i128i128( scope, code, table, data, len );
......@@ -96,7 +96,8 @@ struct Table {
return impl::previous_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool get( const PrimaryType& p, Record& r ) {
return impl::load_primary( scope, code, table, &p, &r, sizeof(Record) ) == sizeof(Record);
*reinterpret_cast<PrimaryType*>(&r) = p;
return impl::load_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record);
}
static bool lower_bound( const PrimaryType& p, Record& r ) {
return impl::lower_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record);
......
......@@ -28,8 +28,10 @@ uint32_t readMessage( void* msg, uint32_t len );
*/
uint32_t messageSize();
void print( const char* cstr );
void prints( const char* cstr );
void printi( uint64_t value );
void printi128( const uint128_t* value );
void printn( uint64_t name );
/**
* @return the account which specifes the code that is being run
......
#pragma once
extern "C" {
void multeq_i128( uint128_t* self, const uint128_t* other );
void diveq_i128 ( uint128_t* self, const uint128_t* other );
} // extern "C"
#pragma once
#include <eoslib/math.h>
namespace eos {
inline void multeq( uint128_t& self, const uint128_t& other ) {
multeq_i128( &self, &other );
}
inline void diveq( uint128_t& self, const uint128_t& other ) {
diveq_i128( &self, &other );
}
struct uint128 {
public:
uint128( uint128_t i = 0 ):value(i){}
uint128( uint64_t i ):value(i){}
uint128( uint32_t i ):value(i){}
friend uint128 operator * ( uint128 a, const uint128& b ) {
return a *= b;
}
friend uint128 operator / ( uint128 a, const uint128& b ) {
return a /= b;
}
friend bool operator <= ( const uint128& a, const uint128& b ) {
return a.value <= b.value;
}
friend bool operator >= ( const uint128& a, const uint128& b ) {
return a.value >= b.value;
}
uint128& operator *= ( const uint128_t& other ) {
multeq( value, other );
return *this;
}
uint128& operator *= ( const uint128& other ) {
multeq( value, other.value );
return *this;
}
uint128& operator /= ( const uint128_t& other ) {
diveq( value, other );
return *this;
}
uint128& operator /= ( const uint128& other ) {
diveq( value, other.value );
return *this;
}
explicit operator uint64_t()const {
assert( !(value >> 64), "cast to 64 bit loss of precision" );
return uint64_t(value);
}
private:
uint128_t value;
};
}
#pragma once
#include <eoslib/eos.hpp>
namespace eos {
inline void print_native( const char* ptr ) {
prints(ptr);
}
inline void print_native( uint64_t num ) {
printi(num);
}
inline void print_native( uint32_t num ) {
printi(num);
}
inline void print_native( int num ) {
printi(num);
}
inline void print_native( uint128 num ) {
printi128((uint128_t*)&num);
}
inline void print_native( uint128_t num ) {
printi128((uint128_t*)&num);
}
inline void print_native( Name name ) {
printn(name.value);
}
template<typename Arg>
inline void print( Arg a ) {
print_native(a);
}
template<typename Arg, typename... Args>
void print( Arg a, Args... args ) {
print(a);
print(args...);
}
}
#pragma once
#include <eoslib/math.hpp>
namespace eos {
template<typename NumberType, uint64_t CurrencyType = N(eos) >
......@@ -55,11 +57,11 @@ namespace eos {
*/
static const uint64_t precision = 1000ll*1000ll*1000ll*1000ll*1000ll;
price():base_per_quote(1){}
price():base_per_quote(1ul){}
price( BaseToken base, QuoteToken quote ) {
assert( base >= BaseToken(1), "invalid price" );
assert( quote >= QuoteToken(1), "invalid price" );
assert( base >= BaseToken(1ul), "invalid price" );
assert( quote >= QuoteToken(1ul), "invalid price" );
base_per_quote = base.quantity;
base_per_quote *= precision;
......@@ -67,7 +69,7 @@ namespace eos {
}
friend QuoteToken operator / ( BaseToken b, const price& q ) {
return QuoteToken( uint64_t((uint128_t(b.quantity) * precision) / q.base_per_quote) );
return QuoteToken( uint64_t((uint128(b.quantity) * uint128(precision) ) / q.base_per_quote) );
}
friend BaseToken operator * ( QuoteToken b, const price& q ) {
......@@ -82,11 +84,11 @@ namespace eos {
friend bool operator == ( const price& a, const price& b ) { return a.base_per_quote == b.base_per_quote; }
friend bool operator != ( const price& a, const price& b ) { return a.base_per_quote != b.base_per_quote; }
private:
// private:
/**
* represented as number of base tokens to purchase 1 quote token
*/
uint128_t base_per_quote;
eos::uint128 base_per_quote;
};
typedef eos::token<uint64_t,N(eos)> Tokens;
......
exchange.wast: exchange.cpp Makefile ../eoslib/eos.hpp exchange.hpp
exchange.wast: exchange.cpp Makefile ../eoslib/* exchange.hpp
/usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm -O3 --std=c++14 --target=wasm32 -c exchange.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .exchange.bc -I /usr/local/Cellar/llvm/4.0.0_1/include/c++/v1/
/Users/dlarimer/Downloads/llvm/build/bin/llc -asm-verbose=false .exchange.bc
/Users/dlarimer/eos/libraries/binaryen/bin/s2wasm -s 1024 .exchange.s > exchange.wast
wc -l exchange.wast
echo '#pragma once ' > exchange.wast.hpp
echo 'const char* exchange_wast = R"=====(' >> exchange.wast.hpp
echo 'const char* exchange_wast = R"=====(' > exchange.wast.hpp
cat exchange.wast >> exchange.wast.hpp
echo ')=====";' >> exchange.wast.hpp
......
......@@ -34,18 +34,25 @@
* contract. Users can only deposit or withdraw to their own currency account.
*/
#include <exchange/exchange.hpp> /// defines transfer struct
#include <eoslib/print.hpp>
using namespace exchange;
using namespace eos;
namespace exchange {
void save( const Account& a ) {
if( a.isEmpty() )
inline void save( const Account& a ) {
if( a.isEmpty() ) {
print("remove");
Db::remove( N(exchange), N(account), a.owner );
else
}
else {
print("store");
Db::store( N(exchange), N(account), a.owner, a );
}
}
template<typename Lambda>
void modifyAccount( AccountName a, Lambda&& modify ) {
inline void modifyAccount( AccountName a, Lambda&& modify ) {
auto acnt = getAccount( a );
modify( acnt );
save( acnt );
......@@ -57,13 +64,21 @@ void modifyAccount( AccountName a, Lambda&& modify ) {
*/
void apply_currency_transfer( const currency::Transfer& transfer ) {
if( transfer.to == N(exchange) ) {
modifyAccount( transfer.to, [&]( Account& account ){
modifyAccount( transfer.from, [&]( Account& account ){
print( "balance: " );
printi( account.currency_balance.quantity );
print( "deposit: " );
printi( transfer.quantity.quantity );
account.currency_balance += transfer.quantity;
});
} else if( transfer.from == N(exchange) ) {
requireAuth( transfer.to ); /// require the reciever of funds (account owner) to authorize this transfer
modifyAccount( transfer.to, [&]( Account& account ){
print( "balance: " );
printi( account.currency_balance.quantity );
print( "withdraw: " );
printi( transfer.quantity.quantity );
account.currency_balance -= transfer.quantity;
});
} else {
......@@ -77,7 +92,7 @@ void apply_currency_transfer( const currency::Transfer& transfer ) {
*/
void apply_eos_transfer( const eos::Transfer& transfer ) {
if( transfer.to == N(exchange) ) {
modifyAccount( transfer.to, [&]( Account& account ){
modifyAccount( transfer.from, [&]( Account& account ){
account.eos_balance += transfer.quantity;
});
} else if( transfer.from == N(exchange) ) {
......@@ -91,16 +106,6 @@ void apply_eos_transfer( const eos::Transfer& transfer ) {
}
}
void fill( Bid& bid, Ask& ask, Account& buyer, Account& seller,
eos::Tokens e, currency::Tokens c) {
bid.quantity -= e;
seller.eos_balance += e;
ask.quantity -= c;
buyer.currency_balance += c;
}
void match( Bid& bid, Account& buyer, Ask& ask, Account& seller ) {
eos::Tokens ask_eos = ask.quantity * ask.price;
......@@ -113,7 +118,15 @@ void match( Bid& bid, Account& buyer, Ask& ask, Account& seller ) {
fill_amount_currency = fill_amount_eos / ask.price;
}
fill( bid, ask, buyer, seller, fill_amount_eos, fill_amount_currency );
print( "\n\nmatch bid: ", Name(bid.buyer.name), ":", bid.buyer.number,
"match ask: ", Name(ask.seller.name), ":", ask.seller.number, "\n\n" );
bid.quantity -= fill_amount_eos;
seller.eos_balance += fill_amount_eos;
ask.quantity -= fill_amount_currency;
buyer.currency_balance += fill_amount_currency;
}
/**
......@@ -127,23 +140,30 @@ void apply_exchange_buy( BuyOrder order ) {
assert( bid.quantity > eos::Tokens(0), "invalid quantity" );
assert( bid.expiration > now(), "order expired" );
static Bid existing_bid;
assert( BidsById::get( bid.buyer, existing_bid ), "order with this id already exists" );
print( Name(bid.buyer.name), " created bid for ", order.quantity.quantity, " currency at price: ", order.price.base_per_quote, "\n" );
Bid existing_bid;
assert( !BidsById::get( bid.buyer, existing_bid ), "order with this id already exists" );
print( __FILE__, __LINE__, "\n" );
auto buyer_account = getAccount( bid.buyer.name );
buyer_account.eos_balance -= bid.quantity;
static Ask lowest_ask;
Ask lowest_ask;
if( !AsksByPrice::front( lowest_ask ) ) {
print( "\n No asks found, saving buyer account and storing bid\n" );
assert( !order.fill_or_kill, "order not completely filled" );
Bids::store( bid );
save( buyer_account );
return;
}
print( "asks found, lets see what matches\n" );
auto seller_account = getAccount( lowest_ask.seller.name );
while( lowest_ask.price <= bid.price ) {
print( "lowest ask <= bid.price\n" );
match( bid, buyer_account, lowest_ask, seller_account );
if( lowest_ask.quantity == currency::Tokens(0) ) {
......@@ -158,12 +178,18 @@ void apply_exchange_buy( BuyOrder order ) {
break; // buyer's bid should be filled
}
}
print( "lowest_ask >= bid.price or buyer's bid has been filled\n" );
save( buyer_account );
print( "saving buyer's account\n" );
if( bid.quantity ) {
print( bid.quantity.quantity, " eos left over" );
assert( !order.fill_or_kill, "order not completely filled" );
Bids::store( bid );
return;
}
print( "bid filled\n" );
}
void apply_exchange_sell( SellOrder order ) {
......@@ -173,20 +199,26 @@ void apply_exchange_sell( SellOrder order ) {
assert( ask.quantity > currency::Tokens(0), "invalid quantity" );
assert( ask.expiration > now(), "order expired" );
static Ask existing_ask;
assert( AsksById::get( ask.seller, existing_ask ), "order with this id already exists" );
print( "\n\n", Name(ask.seller.name), " created sell for ", order.quantity.quantity,
" currency at price: ", order.price.base_per_quote, "\n");
Ask existing_ask;
assert( !AsksById::get( ask.seller, existing_ask ), "order with this id already exists" );
auto seller_account = getAccount( ask.seller.name );
seller_account.currency_balance -= ask.quantity;
static Bid highest_bid;
Bid highest_bid;
if( !BidsByPrice::back( highest_bid ) ) {
assert( !order.fill_or_kill, "order not completely filled" );
print( "\n No bids found, saving seller account and storing ask\n" );
Asks::store( ask );
save( seller_account );
return;
}
print( "\n bids found, lets see what matches\n" );
auto buyer_account = getAccount( highest_bid.buyer.name );
while( highest_bid.price >= ask.price ) {
......@@ -208,80 +240,11 @@ void apply_exchange_sell( SellOrder order ) {
save( seller_account );
if( ask.quantity ) {
assert( !order.fill_or_kill, "order not completely filled" );
print( "saving ask\n" );
Asks::store( ask );
}
}
/*
void apply_exchange_sell() {
auto sell = currentMessage<Sell>();
assert( sell.expiration > now(), "order expired" );
Account seller_account;
Db::get( sell.seller, seller_account );
assert( seller_account.b >= sell.quantity, "insufficient funds" );
assert( sell.quantity > 0, "invalid quantity" );
seller_account.b -= sell.quantity;
Order seller_ask;
assert( AsksById::get( OrderID{ sell.seller, sell.id}, seller_ask ), "order with this id already exists" );
seller_ask.price = sell.price;
seller_ask.id.owner = sell.seller;
seller_ask.id.id = sell.id;
seller_ask.quantity = sell.quantity;
seller_ask.expiration = sell.expiration;
Order highest_bid;
if( !BidsByPrice::back( highest_bid ) ) {
assert( !sell.fill_or_kill, "order not completely filled" );
Bids::store( seller_ask );
Db::store( sell.seller, seller_account );
return;
}
Account buyer_account;
Db::get( highest_bid.id.owner, buyer_account );
while( highest_bid.price >= seller_ask.price ) {
auto ask_usd = seller_ask.quantity;
auto bid_usd = seller_ask.price * seller_ask.quantity;
auto fill_amount_usd = min<uint64_t>( ask_usd, bid_usd );
uint64_t fill_amount_token = 0;
seller_ask.quantity -= fill_amount_token;
highest_bid.quantity -= fill_amount_usd;
if( fill_amount_usd == ask_usd ) { /// complete fill of ask
fill_amount_token = seller_ask.quantity;
} else { /// complete fill of buy
fill_amount_token = fill_amount_usd / seller_ask.price;
}
/// either fill_amount_token == seller.quantity or fill_amount_usd == buy.quantity
fill( highest_bid, seller_ask, buyer_account, seller_account, fill_amount_usd, fill_amount_token );
if( highest_bid.quantity == 0 ) {
Db::store( highest_bid.id.owner, buyer_account );
Asks::remove( highest_bid );
if( !BidsByPrice::back( highest_bid ) ) {
break;
}
Db::get( highest_bid.id.owner, buyer_account );
} else {
break; // buyer's bid should be filled
}
}
Db::store( sell.seller, seller_account );
if( seller_ask.quantity > 0 ) {
assert( !sell.fill_or_kill, "order not completely filled" );
Asks::store( seller_ask );
}
}
*/
} // namespace exchange
extern "C" {
......@@ -306,6 +269,7 @@ extern "C" {
apply_exchange_buy( currentMessage<exchange::BuyOrder>() );
break;
case N(sell):
apply_exchange_sell( currentMessage<exchange::SellOrder>() );
break;
default:
assert( false, "unknown action" );
......
......@@ -34,7 +34,7 @@ namespace exchange {
bool isEmpty()const { return ! ( bool(eos_balance) | bool(currency_balance) | open_orders); }
};
Account getAccount( AccountName owner ) {
inline Account getAccount( AccountName owner ) {
Account account(owner);
Db::get( N(exchange), N(exchange), N(account), owner, account );
return account;
......
......@@ -484,7 +484,7 @@ try {
#warning TODO: call validate handlers on all notified accounts, currently it only calls the recipient's validate
message_validate_context mvc(_db,trx,*m,a);
message_validate_context mvc(*this,_db,trx,*m,a);
auto contract_handlers_itr = message_validate_handlers.find(a); /// namespace is the notifier
if (contract_handlers_itr != message_validate_handlers.end()) {
auto message_handler_itr = contract_handlers_itr->second.find({m->code, m->type});
......@@ -597,7 +597,7 @@ void chain_controller::validate_message_precondition( precondition_validate_cont
* entire message.
*/
void chain_controller::process_message( const Transaction& trx, const Message& message) {
apply_context apply_ctx(_db, trx, message, message.code);
apply_context apply_ctx(*this, _db, trx, message, message.code);
/** TODO: pre condition validation and application can occur in parallel */
/** TODO: verify that message is fully authorized
......@@ -607,7 +607,7 @@ void chain_controller::process_message( const Transaction& trx, const Message& m
for (const auto& recipient : message.recipients) {
try {
apply_context recipient_ctx(_db, trx, message, recipient);
apply_context recipient_ctx(*this,_db, trx, message, recipient);
validate_message_precondition(recipient_ctx);
apply_message(recipient_ctx);
} FC_CAPTURE_AND_RETHROW((recipient)(message))
......@@ -629,6 +629,7 @@ void chain_controller::apply_message(apply_context& context)
}
const auto& recipient = _db.get<account_object,by_name>(context.code);
if (recipient.code.size()) {
//idump((context.code)(context.msg.type));
wasm_interface::get().apply(context);
}
......@@ -767,6 +768,7 @@ void chain_controller::initialize_indexes() {
_db.add_index<permission_index>();
_db.add_index<action_permission_index>();
_db.add_index<key_value_index>();
_db.add_index<key128x128_value_index>();
_db.add_index<global_property_multi_index>();
_db.add_index<dynamic_global_property_multi_index>();
......
......@@ -8,13 +8,15 @@ namespace chainbase { class database; }
namespace eos { namespace chain {
class chain_controller;
class message_validate_context {
public:
explicit message_validate_context(const chainbase::database& d,
explicit message_validate_context(const chain_controller& control,
const chainbase::database& d,
const chain::Transaction& t,
const chain::Message& m,
types::AccountName c )
:db(d),trx(t),msg(m),code(c),used_authorizations(msg.authorization.size(), false){}
:controller(control),db(d),trx(t),msg(m),code(c),used_authorizations(msg.authorization.size(), false){}
/**
* @brief Require @ref account to have approved of this message
......@@ -30,6 +32,7 @@ public:
void require_recipient(const types::AccountName& account)const;
bool all_authorizations_used() const;
const chain_controller& controller;
const chainbase::database& db; ///< database where state is stored
const chain::Transaction& trx; ///< used to gather the valid read/write scopes
const chain::Message& msg; ///< message being applied
......@@ -43,6 +46,10 @@ public:
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );
int32_t back_primary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );
int32_t front_secondary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );
int32_t back_secondary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );
int32_t load_primary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen );
int32_t lowerbound_primary_i128i128( Name scope, Name code, Name table,
......@@ -56,27 +63,32 @@ public:
class precondition_validate_context : public message_validate_context {
public:
precondition_validate_context(const chainbase::database& db,
precondition_validate_context(const chain_controller& con,
const chainbase::database& db,
const chain::Transaction& t,
const chain::Message& m,
const types::AccountName& code)
:message_validate_context(db, t, m, code){}
:message_validate_context(con, db, t, m, code){}
};
class apply_context : public precondition_validate_context {
public:
apply_context(chainbase::database& db,
apply_context(chain_controller& con,
chainbase::database& db,
const chain::Transaction& t,
const chain::Message& m,
const types::AccountName& code)
:precondition_validate_context(db,t,m,code),mutable_db(db){}
:precondition_validate_context(con,db,t,m,code),mutable_controller(con),mutable_db(db){}
int32_t store_i64( Name scope, Name table, Name key, const char* data, uint32_t len);
int32_t remove_i64( Name scope, Name table, Name key );
int32_t store_i128i128( Name scope, Name table, uint128_t primary, uint128_t secondary,
const char* data, uint32_t len );
std::deque<eos::chain::generated_transaction> applied; ///< sync calls made
std::deque<eos::chain::generated_transaction> generated; ///< async calls requested
chain_controller& mutable_controller;
chainbase::database& mutable_db;
};
......
......@@ -58,6 +58,127 @@ int32_t message_validate_context::load_i64( Name scope, Name code, Name table, N
return copylen;
}
int32_t message_validate_context::back_primary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) {
require_scope( scope );
const auto& idx = db.get_index<key128x128_value_index,by_scope_primary>();
auto itr = idx.lower_bound( boost::make_tuple( AccountName(scope),
AccountName(code),
table.value+1,
*primary, uint128_t(0) ) );
if( itr == idx.begin() && itr == idx.end() )
return 0;
--itr;
if( itr->scope != scope ||
itr->code != code ||
itr->table != table ||
itr->primary_key != *primary ) return -1;
*secondary = itr->secondary_key;
*primary = itr->primary_key;
auto copylen = std::min<size_t>(itr->value.size(),valuelen);
if( copylen ) {
itr->value.copy(value, copylen);
}
return copylen;
}
int32_t message_validate_context::back_secondary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) {
require_scope( scope );
const auto& idx = db.get_index<key128x128_value_index,by_scope_secondary>();
auto itr = idx.lower_bound( boost::make_tuple( AccountName(scope),
AccountName(code),
table.value+1,
*secondary, uint128_t(0) ) );
if( itr == idx.begin() && itr == idx.end() )
return 0;
--itr;
if( itr->scope != scope ||
itr->code != code ||
itr->table != table ||
itr->primary_key != *primary ) return -1;
*secondary = itr->secondary_key;
*primary = itr->primary_key;
auto copylen = std::min<size_t>(itr->value.size(),valuelen);
if( copylen ) {
itr->value.copy(value, copylen);
}
return copylen;
}
int32_t message_validate_context::front_primary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) {
require_scope( scope );
const auto& idx = db.get_index<key128x128_value_index,by_scope_primary>();
auto itr = idx.lower_bound( boost::make_tuple( AccountName(scope),
AccountName(code),
AccountName(table),
*primary, uint128_t(0) ) );
if( itr == idx.end() )
return -1;
--itr;
if( itr->scope != scope ||
itr->code != code ||
itr->table != table ||
itr->primary_key != *primary ) return -1;
*secondary = itr->secondary_key;
*primary = itr->primary_key;
auto copylen = std::min<size_t>(itr->value.size(),valuelen);
if( copylen ) {
itr->value.copy(value, copylen);
}
return copylen;
}
int32_t message_validate_context::front_secondary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) {
require_scope( scope );
const auto& idx = db.get_index<key128x128_value_index,by_scope_secondary>();
auto itr = idx.lower_bound( boost::make_tuple( AccountName(scope),
AccountName(code),
AccountName(table),
*secondary, uint128_t(0) ) );
if( itr == idx.end() )
return -1;
--itr;
if( itr->scope != scope ||
itr->code != code ||
itr->table != table ||
itr->primary_key != *primary ) return -1;
*secondary = itr->secondary_key;
*primary = itr->primary_key;
auto copylen = std::min<size_t>(itr->value.size(),valuelen);
if( copylen ) {
itr->value.copy(value, copylen);
}
return copylen;
}
int32_t message_validate_context::load_primary_i128i128( Name scope, Name code, Name table,
uint128_t* primary, uint128_t* secondary, char* value, uint32_t valuelen ) {
......@@ -168,7 +289,34 @@ int32_t apply_context::store_i64( Name scope, Name table, Name key, const char*
o.key = key;
o.value.insert( 0, value, valuelen );
});
return 1;
return valuelen;
}
}
int32_t apply_context::store_i128i128( Name scope, Name table, uint128_t primary, uint128_t secondary,
const char* value, uint32_t valuelen ) {
const auto* obj = db.find<key128x128_value_object,by_scope_primary>( boost::make_tuple(
AccountName(scope),
AccountName(code),
AccountName(table),
primary, secondary ) );
if( obj ) {
mutable_db.modify( *obj, [&]( auto& o ) {
o.primary_key = primary;
o.secondary_key = secondary;
o.value.assign(value, valuelen);
});
return 0;
} else {
mutable_db.create<key128x128_value_object>( [&](auto& o) {
o.scope = scope;
o.code = code;
o.table = table;
o.primary_key = primary;
o.secondary_key = secondary;
o.value.insert( 0, value, valuelen );
});
return valuelen;
}
}
......
#include <eos/chain/wasm_interface.hpp>
#include <eos/chain/chain_controller.hpp>
#include "Platform/Platform.h"
#include "WAST/WAST.h"
#include "Runtime/Runtime.h"
......@@ -17,6 +18,87 @@ namespace eos { namespace chain {
wasm_interface::wasm_interface() {
}
DEFINE_INTRINSIC_FUNCTION2(env,multeq_i128,multeq_i128,none,i32,self,i32,other) {
auto& wasm = wasm_interface::get();
auto mem = wasm.current_memory;
uint128_t& v = memoryRef<uint128_t>( mem, self );
const uint128_t& o= memoryRef<const uint128_t>( mem, other );
v *= o;
}
DEFINE_INTRINSIC_FUNCTION2(env,diveq_i128,diveq_i128,none,i32,self,i32,other) {
auto& wasm = wasm_interface::get();
auto mem = wasm.current_memory;
uint128_t& v = memoryRef<uint128_t>( mem, self );
const uint128_t& o = memoryRef<const uint128_t>( mem, other );
FC_ASSERT( o != 0, "divide by zero" );
v /= o;
}
DEFINE_INTRINSIC_FUNCTION0(env,now,now,i32) {
return wasm_interface::get().current_validate_context->controller.head_block_time().sec_since_epoch();
}
DEFINE_INTRINSIC_FUNCTION4(env,store_i128i128,store_i128i128,i32,i64,scope,i64,table,i32,data,i32,datalen) {
auto& wasm = wasm_interface::get();
FC_ASSERT( wasm.current_apply_context, "not a valid apply context" );
char* v = &memoryRef<char>( wasm.current_memory, data );
uint128_t& primary = *((uint128_t*)v);
uint128_t& secondary = *(((uint128_t*)v)+1);
char* value = v + 2*sizeof(uint128_t);
return wasm_interface::get().current_apply_context->store_i128i128(
Name(scope), Name(table),
primary, secondary, value, datalen - 2*sizeof(uint128_t) );
}
DEFINE_INTRINSIC_FUNCTION3(env,remove_i128i128,remove_i128i128,i32,i64,scope,i64,table,i32,data) {
FC_ASSERT( !"remove not implemented" );
return 0;
}
DEFINE_INTRINSIC_FUNCTION5(env,load_primary_i128i128,load_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) {
auto& wasm = wasm_interface::get();
char* v = &memoryRef<char>( wasm.current_memory, data );
return wasm_interface::get().current_validate_context->load_primary_i128i128( Name(scope), Name(code), Name(table),
(uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) );
}
DEFINE_INTRINSIC_FUNCTION5(env,load_secondary_i128i128,load_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) {
FC_ASSERT( !"load_secondary_i128i128 not implemented" );
return 0;
}
DEFINE_INTRINSIC_FUNCTION5(env,back_primary_i128i128,back_primary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) {
auto& wasm = wasm_interface::get();
char* v = &memoryRef<char>( wasm.current_memory, data );
return wasm_interface::get().current_validate_context->back_primary_i128i128( Name(scope), Name(code), Name(table),
(uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) );
}
DEFINE_INTRINSIC_FUNCTION5(env,front_primary_i128i128,front_primary,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) {
auto& wasm = wasm_interface::get();
char* v = &memoryRef<char>( wasm.current_memory, data );
return wasm_interface::get().current_validate_context->front_primary_i128i128( Name(scope), Name(code), Name(table),
(uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) );
}
DEFINE_INTRINSIC_FUNCTION5(env,back_secondary_i128i128,back_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) {
auto& wasm = wasm_interface::get();
char* v = &memoryRef<char>( wasm.current_memory, data );
return wasm_interface::get().current_validate_context->back_secondary_i128i128( Name(scope), Name(code), Name(table),
(uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) );
return 0;
}
DEFINE_INTRINSIC_FUNCTION5(env,front_secondary_i128i128,front_secondary_i128i128,i32,i64,scope,i64,code,i64,table,i32,data,i32,datalen) {
auto& wasm = wasm_interface::get();
char* v = &memoryRef<char>( wasm.current_memory, data );
return wasm_interface::get().current_validate_context->front_secondary_i128i128( Name(scope), Name(code), Name(table),
(uint128_t*)v, (uint128_t*)(v+sizeof(uint128_t)), v, datalen-(2*sizeof(uint128_t)) );
}
DEFINE_INTRINSIC_FUNCTION0(env,currentCode,currentCode,i64) {
auto& wasm = wasm_interface::get();
return wasm.current_validate_context->code.value;
......@@ -173,18 +255,26 @@ DEFINE_INTRINSIC_FUNCTION1(env,malloc,malloc,i32,i32,size) {
}
DEFINE_INTRINSIC_FUNCTION1(env,printi,printi,none,i64,val) {
std::cerr << uint64_t(val) << " " << Name(val).toString();
//idump((val)(Name(val)));
std::cerr << uint64_t(val);
}
DEFINE_INTRINSIC_FUNCTION1(env,printi128,printi128,none,i32,val) {
auto& wasm = wasm_interface::get();
auto mem = wasm.current_memory;
fc::uint128_t& value = memoryRef<fc::uint128_t>( mem, val );
std::cerr << fc::variant(value).get_string();
}
DEFINE_INTRINSIC_FUNCTION1(env,printn,printn,none,i64,val) {
std::cerr << Name(val).toString();
}
DEFINE_INTRINSIC_FUNCTION1(env,print,print,none,i32,charptr) {
DEFINE_INTRINSIC_FUNCTION1(env,prints,prints,none,i32,charptr) {
auto& wasm = wasm_interface::get();
auto mem = wasm.current_memory;
const char* str = &memoryRef<const char>( mem, charptr );
std::cerr << std::string( str, strlen(str) );
wdump( (std::string( str, strlen(str) )) );
}
DEFINE_INTRINSIC_FUNCTION1(env,free,free,none,i32,ptr) {
......@@ -261,6 +351,8 @@ DEFINE_INTRINSIC_FUNCTION1(env,toUpper,toUpper,none,i32,charptr) {
//FC_ASSERT( apply, "no entry point found for ${call}", ("call", std::string(name)) );
FC_ASSERT( getFunctionType(call)->parameters.size() == 2 );
idump((current_validate_context->msg.code)(current_validate_context->msg.type)(current_validate_context->code));
std::vector<Value> args = { Value(uint64_t(current_validate_context->msg.code)),
Value(uint64_t(current_validate_context->msg.type)) };
......@@ -351,6 +443,7 @@ DEFINE_INTRINSIC_FUNCTION1(env,toUpper,toUpper,none,i32,charptr) {
void wasm_interface::load( const AccountName& name, const chainbase::database& db ) {
const auto& recipient = db.get<account_object,by_name>( name );
// idump(("recipient")(Name(name))(recipient.code_version));
auto& state = instances[name];
if( state.code_version != recipient.code_version ) {
......@@ -392,6 +485,7 @@ DEFINE_INTRINSIC_FUNCTION1(env,toUpper,toUpper,none,i32,charptr) {
memcpy( state.init_memory.data(), memstart, state.mem_end ); //state.init_memory.size() );
std::cerr <<"\n";
state.code_version = recipient.code_version;
idump((state.code_version));
}
catch(Serialization::FatalSerializationException exception)
{
......
......@@ -39,7 +39,7 @@ void apply_system_setcode(apply_context& context) {
memcpy( a.code.data(), msg.code.data(), msg.code.size() );
});
apply_context init_context( context.mutable_db, context.trx, context.msg, msg.account );
apply_context init_context( context.mutable_controller, context.mutable_db, context.trx, context.msg, msg.account );
wasm_interface::get().init( init_context );
}
......
......@@ -46,6 +46,27 @@
using namespace eos;
using namespace chain;
struct OrderID {
AccountName name;
uint64_t number = 0;
};
struct Bid {
OrderID buyer;
fc::uint128_t price;
uint64_t quantity;
Time expiration;
};
struct Ask {
OrderID seller;
fc::uint128_t price;
uint64_t quantity;
Time expiration;
};
FC_REFLECT( OrderID, (name)(number) );
FC_REFLECT( Bid, (buyer)(price)(quantity)(expiration) );
FC_REFLECT( Ask, (seller)(price)(quantity)(expiration) );
BOOST_AUTO_TEST_SUITE(slow_tests)
// Test that TaPoS still works after block 65535 (See Issue #55)
......@@ -157,6 +178,51 @@ vector<uint8_t> assemble_wast( const std::string& wast ) {
}
}
void SetCode( testing_blockchain& chain, AccountName account, const char* wast ) {
try {
types::setcode handler;
handler.account = account;
auto wasm = assemble_wast( wast );
handler.code.resize(wasm.size());
memcpy( handler.code.data(), wasm.data(), wasm.size() );
{
eos::chain::SignedTransaction trx;
trx.scope = {account};
trx.messages.resize(1);
trx.messages[0].code = config::SystemContractName;
trx.messages[0].recipients = {account};
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);
}
} FC_LOG_AND_RETHROW( ) }
void TransferCurrency( testing_blockchain& chain, AccountName from, AccountName to, uint64_t amount ) {
eos::chain::SignedTransaction trx;
trx.scope = sort_names({from,to});
trx.emplaceMessage("currency", sort_names( {from,to} ),
vector<types::AccountPermission>{ {from,"active"} },
"transfer", types::transfer{from, to, amount, ""});
trx.expiration = chain.head_block_time() + 100;
trx.set_reference_block(chain.head_block_id());
chain.push_transaction(trx);
}
void WithdrawCurrency( testing_blockchain& chain, AccountName from, AccountName to, uint64_t amount ) {
eos::chain::SignedTransaction trx;
trx.scope = sort_names({from,to});
trx.emplaceMessage("currency", sort_names( {from,to} ),
vector<types::AccountPermission>{ {from,"active"},{to,"active"} },
"transfer", types::transfer{from, to, amount, ""});
trx.expiration = chain.head_block_time() + 100;
trx.set_reference_block(chain.head_block_id());
chain.push_transaction(trx);
}
//Test account script processing
BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture)
{ try {
......@@ -208,156 +274,123 @@ BOOST_FIXTURE_TEST_CASE(create_script, testing_fixture)
} FC_LOG_AND_RETHROW() }
//Test account script float rejection
BOOST_FIXTURE_TEST_CASE(create_script_w_float, testing_fixture)
{ try {
Make_Blockchain(chain);
chain.produce_blocks(10);
Make_Account(chain, simplecoin);
chain.produce_blocks(1);
static const uint64_t precision = 1000ll*1000ll*1000ll*1000ll*1000ll;
fc::uint128_t to_price( double p ) {
uint64_t pi(p);
fc::uint128_t result(pi);
result *= precision;
/*
auto c_apply = R"(
typedef long long uint64_t;
typedef unsigned int uint32_t;
void print( char* string, int length );
void printi( int );
void printi64( uint64_t );
void assert( int test, char* message );
void store( const char* key, int keylength, const char* value, int valuelen );
int load( const char* key, int keylength, char* value, int maxlen );
int remove( const char* key, int keyLength );
void* memcpy( void* dest, const void* src, uint32_t size );
int readMessage( char* dest, int destsize );
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];
double fract = p - pi;
result += uint64_t( fract * precision );
return result;
}
typedef struct {
uint64_t name[4];
} AccountName;
typedef struct {
uint32_t length;
char data[];
} String;
void SellCurrency( testing_blockchain& chain, AccountName seller, AccountName exchange, uint64_t ordernum, uint64_t cur_quantity, double price ) {
typedef struct {
char* start;
char* pos;
char* end;
} DataStream;
Ask b { OrderID{seller,ordernum},
to_price(price),
cur_quantity,
chain.head_block_time()+fc::days(3)
};
void DataStream_init( DataStream* ds, char* start, int length ) {
ds->start = start;
ds->end = start + length;
ds->pos = start;
}
void AccountName_initString( AccountName* a, String* s ) {
assert( s->length <= sizeof(AccountName), "String is longer than account name allows" );
memcpy( a, s->data, s->length );
}
void AccountName_initCString( AccountName* a, const char* s, uint32_t len ) {
assert( len <= sizeof(AccountName), "String is longer than account name allows" );
memcpy( a, s, len );
}
eos::chain::SignedTransaction trx;
trx.scope = sort_names({"exchange"});
trx.emplaceMessage("exchange", sort_names( {"exchange"} ),
vector<types::AccountPermission>{ {seller,"active"} },
"sell", b );
trx.expiration = chain.head_block_time() + 100;
trx.set_reference_block(chain.head_block_id());
chain.push_transaction(trx);
void AccountName_unpack( DataStream* ds, AccountName* account );
void uint64_unpack( DataStream* ds, uint64_t* value ) {
assert( ds->pos + sizeof(uint64_t) <= ds->end, "read past end of stream" );
memcpy( (char*)value, ds->pos, 8 );
ds->pos += sizeof(uint64_t);
}
void Varint_unpack( DataStream* ds, uint32_t* value );
void String_unpack( DataStream* ds, String** value ) {
static uint32_t size;
Varint_unpack( ds, &size );
assert( ds->pos + size <= ds->end, "read past end of stream");
String* str = (String*)malloc( size + sizeof(String) );
memcpy( str->data, ds->pos, size );
*value = str;
void BuyCurrency( testing_blockchain& chain, AccountName buyer, AccountName exchange, uint64_t ordernum, uint64_t cur_quantity, double price ) {
Bid b { OrderID{buyer,ordernum},
to_price(price),
cur_quantity,
chain.head_block_time()+fc::days(3)
};
eos::chain::SignedTransaction trx;
trx.scope = sort_names({"exchange"});
trx.emplaceMessage("exchange", sort_names( {"exchange"} ),
vector<types::AccountPermission>{ {buyer,"active"} },
"buy", b );
trx.expiration = chain.head_block_time() + 100;
trx.set_reference_block(chain.head_block_id());
chain.push_transaction(trx);
}
/// END BUILT IN LIBRARY.... everything below this is "user contract"
BOOST_FIXTURE_TEST_CASE(create_exchange, testing_fixture) {
#include <currency/currency.wast.hpp>
#include <exchange/exchange.wast.hpp>
try {
try {
Make_Blockchain(chain);
chain.produce_blocks(2);
Make_Account(chain, currency);
Make_Account(chain, exchange);
chain.produce_blocks(1);
SetCode(chain, "currency", currency_wast);
SetCode(chain, "exchange", exchange_wast);
typedef struct {
AccountName from;
AccountName to;
uint64_t amount;
String* memo;
} Transfer;
chain.produce_blocks(1);
void Transfer_unpack( DataStream* ds, Transfer* transfer )
{
AccountName_unpack( ds, &transfer->from );
AccountName_unpack( ds, &transfer->to );
uint64_unpack( ds, &transfer->amount );
String_unpack( ds, &transfer->memo );
}
ilog( "transfering currency to the users" );
TransferCurrency( chain, "currency", "inita", 1000 );
TransferCurrency( chain, "currency", "initb", 2000 );
typedef struct {
uint64_t balance;
} Balance;
Transfer_Asset(chain, system, inita, Asset(50));
Transfer_Asset(chain, system, initb, Asset(50));
chain.produce_blocks(1);
ilog( "transfering funds to the exchange" );
TransferCurrency( chain, "inita", "exchange", 1000 );
TransferCurrency( chain, "initb", "exchange", 2000 );
void onInit() {
static Balance initial;
static AccountName simplecoin;
AccountName_initCString( &simplecoin, "simplecoin", 10 );
initial.balance = 1000*1000;
Transfer_Asset(chain, inita, exchange, Asset(500));
Transfer_Asset(chain, initb, exchange, Asset(500));
store( (const char*)&simplecoin, sizeof(AccountName), (const char*)&initial, sizeof(Balance));
}
BOOST_REQUIRE_THROW( TransferCurrency( chain, "initb", "exchange", 2000 ), fc::exception ); // insufficient funds
void onApply_Transfer_simplecoin() {
static char buffer[100];
BOOST_REQUIRE_THROW( WithdrawCurrency( chain, "exchange", "initb", 2001 ), fc::exception ); // insufficient funds
int read = readMessage( buffer, 100 );
static Transfer message;
static DataStream ds;
DataStream_init( &ds, buffer, read );
Transfer_unpack( &ds, &message );
ilog( "withdrawing from exchange" );
static Balance from_balance;
static Balance to_balance;
to_balance.balance = 0;
WithdrawCurrency( chain, "exchange", "initb", 2000 );
chain.produce_blocks(1);
read = load( (const char*)&message.from, sizeof(message.from), (char*)&from_balance.balance,
sizeof(from_balance.balance) );
assert( read == sizeof(Balance), "no existing balance" );
assert( from_balance.balance >= message.amount, "insufficient funds" );
read = load( (const char*)&message.to, sizeof(message.to), (char*)&to_balance.balance, sizeof(to_balance.balance) );
ilog( "send back to exchange" );
TransferCurrency( chain, "initb", "exchange", 2000 );
chain.produce_blocks(1);
to_balance.balance += message.amount;
from_balance.balance -= message.amount;
SellCurrency( chain, "initb", "exchange", 1, 100, .5 );
//BOOST_REQUIRE_THROW( SellCurrency( chain, "initb", "exchange", 1, 100, .5 ), fc::exception ); // order id already exists
SellCurrency( chain, "initb", "exchange", 2, 100, .75 );
double bal = to_balance.balance;
if( bal + 0.5 < 50.5 )
return;
BuyCurrency( chain, "initb", "exchange", 1, 50, .25 );
//BOOST_REQUIRE_THROW( BuyCurrency( chain, "initb", "exchange", 1, 50, .25 ), fc::exception ); // order id already exists
if( from_balance.balance )
store( (const char*)&message.from, sizeof(AccountName), (const char*)&from_balance.balance,
sizeof(from_balance.balance) );
else
remove( (const char*)&message.from, sizeof(AccountName) );
/// this should buy 5 from initb order 2 at a price of .75
BuyCurrency( chain, "initb", "exchange", 2, 50, .8 );
store( (const char*)&message.to, sizeof(message.to), (const char*)&to_balance.balance, sizeof(to_balance.balance) );
} FC_LOG_AND_RETHROW()
}catch(...) {
elog( "unexpected exception" );
}
}
");
*/
//Test account script float rejection
BOOST_FIXTURE_TEST_CASE(create_script_w_float, testing_fixture)
{ try {
Make_Blockchain(chain);
chain.produce_blocks(10);
Make_Account(chain, simplecoin);
chain.produce_blocks(1);
std::string wast_apply =
R"(
(module
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册