diff --git a/contracts/currency/Makefile b/contracts/currency/Makefile index 672b35c0152dab1cf4d85c5b1a7a534bc8ccd006..493a7969edb6fc2b9dbe39940e54bccbd4346bfa 100644 --- a/contracts/currency/Makefile +++ b/contracts/currency/Makefile @@ -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 diff --git a/contracts/currency/currency.wast.hpp b/contracts/currency/currency.wast.hpp index 5126bc198ffa109ea9bd460fda7c5fccfff9081d..f9790a1e040c2bad0ddc5996b8420e50dc2bc893 100644 --- a/contracts/currency/currency.wast.hpp +++ b/contracts/currency/currency.wast.hpp @@ -1,4 +1,3 @@ -#pragma once const char* currency_wast = R"=====( (module (type $FUNCSIG$vj (func (param i64))) diff --git a/contracts/eoslib/db.h b/contracts/eoslib/db.h index d449a60fea25da48beedef43be27971f2e39ceca..2ea81f6be85b1ac305f413f0ab246f210caded68 100644 --- a/contracts/eoslib/db.h +++ b/contracts/eoslib/db.h @@ -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 ); diff --git a/contracts/eoslib/db.hpp b/contracts/eoslib/db.hpp index 3e981e577753d5a04b0ac647d93e1c870d8a0241..c9ed0f4dcc496c19110920357a74b2ba55f254e8 100644 --- a/contracts/eoslib/db.hpp +++ b/contracts/eoslib/db.hpp @@ -56,8 +56,8 @@ struct table_impl { 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(&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); diff --git a/contracts/eoslib/eos.hpp b/contracts/eoslib/eos.hpp index 403c96b36b197c7e50fb14ffbace9abd910368c7..f2cc086c7b0807b09526fe8e568a632e391b19ed 100644 --- a/contracts/eoslib/eos.hpp +++ b/contracts/eoslib/eos.hpp @@ -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 diff --git a/contracts/eoslib/math.h b/contracts/eoslib/math.h index db4560753c5c41be019b447097f1314a8a4d7e0c..f4967ffbec6c0dca7bb70a3f934cfd60583f65d5 100644 --- a/contracts/eoslib/math.h +++ b/contracts/eoslib/math.h @@ -1,5 +1,6 @@ #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" diff --git a/contracts/eoslib/math.hpp b/contracts/eoslib/math.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2495c0ca49094dde12b1a1d33df061fa1b803de0 --- /dev/null +++ b/contracts/eoslib/math.hpp @@ -0,0 +1,58 @@ +#pragma once +#include + +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; + }; +} diff --git a/contracts/eoslib/print.hpp b/contracts/eoslib/print.hpp new file mode 100644 index 0000000000000000000000000000000000000000..08b47274fe4e258780e588be550bb9657184c784 --- /dev/null +++ b/contracts/eoslib/print.hpp @@ -0,0 +1,41 @@ +#pragma once +#include + +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 + inline void print( Arg a ) { + print_native(a); + } + template + void print( Arg a, Args... args ) { + print(a); + print(args...); + } + +} diff --git a/contracts/eoslib/token.hpp b/contracts/eoslib/token.hpp index 95f62fe4a53876fde897a61acbd5eb61b39de89c..073c3a7cc50c923b88df4391f27d6368b82fe518 100644 --- a/contracts/eoslib/token.hpp +++ b/contracts/eoslib/token.hpp @@ -1,4 +1,6 @@ #pragma once +#include + namespace eos { template @@ -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 Tokens; diff --git a/contracts/exchange/Makefile b/contracts/exchange/Makefile index aec4c8930b07aee48b9fc5b873a635388e64f0f9..813b3993dcbc07727b5cc5ce316dea08cb6d1122 100644 --- a/contracts/exchange/Makefile +++ b/contracts/exchange/Makefile @@ -1,10 +1,9 @@ -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 diff --git a/contracts/exchange/exchange.cpp b/contracts/exchange/exchange.cpp index 70e490bb56d432a3424dc0b8b81dd06600bbf848..e719b25fa8805b3f82fd5ce98d10b88de566134c 100644 --- a/contracts/exchange/exchange.cpp +++ b/contracts/exchange/exchange.cpp @@ -34,18 +34,25 @@ * contract. Users can only deposit or withdraw to their own currency account. */ #include /// defines transfer struct +#include + 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 -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(); - 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( 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() ); break; case N(sell): + apply_exchange_sell( currentMessage() ); break; default: assert( false, "unknown action" ); diff --git a/contracts/exchange/exchange.hpp b/contracts/exchange/exchange.hpp index c0271f57ff806b84ee7d936338bcf94b4b43dfcd..c6d63fe1f9f96a185e53e3a1556832eec162e81d 100644 --- a/contracts/exchange/exchange.hpp +++ b/contracts/exchange/exchange.hpp @@ -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; diff --git a/libraries/chain/chain_controller.cpp b/libraries/chain/chain_controller.cpp index d63f3e118e466f6193085b2fdf8c896005dacd55..b605d4549c4b2c65875af72c35fc75af867dc388 100644 --- a/libraries/chain/chain_controller.cpp +++ b/libraries/chain/chain_controller.cpp @@ -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(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(); _db.add_index(); _db.add_index(); + _db.add_index(); _db.add_index(); _db.add_index(); diff --git a/libraries/chain/include/eos/chain/message_handling_contexts.hpp b/libraries/chain/include/eos/chain/message_handling_contexts.hpp index 572929d4fa586a08532a2ebeed2034c2bc960957..1f9239ff6846256c9096714c69713844acbada89 100644 --- a/libraries/chain/include/eos/chain/message_handling_contexts.hpp +++ b/libraries/chain/include/eos/chain/message_handling_contexts.hpp @@ -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 applied; ///< sync calls made std::deque generated; ///< async calls requested + chain_controller& mutable_controller; chainbase::database& mutable_db; }; diff --git a/libraries/chain/message_handling_contexts.cpp b/libraries/chain/message_handling_contexts.cpp index 20f0feb1e9462dec355adb22b99bc81e27c35e2d..beb0da99142c9d8a7be978cab854c1176aa5748f 100644 --- a/libraries/chain/message_handling_contexts.cpp +++ b/libraries/chain/message_handling_contexts.cpp @@ -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(); + 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(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(); + 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(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(); + 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(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(); + 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(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( 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( [&](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; } } diff --git a/libraries/chain/wasm_interface.cpp b/libraries/chain/wasm_interface.cpp index dabbb733171f2e870d770d60248f99f4cbce64ff..65d66caa6b0645d5826bbc04908e63c44d6e2683 100644 --- a/libraries/chain/wasm_interface.cpp +++ b/libraries/chain/wasm_interface.cpp @@ -1,4 +1,5 @@ #include +#include #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( mem, self ); + const uint128_t& o= memoryRef( 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( mem, self ); + const uint128_t& o = memoryRef( 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( 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( 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( 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( 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( 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( 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( 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( 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 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( 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) { diff --git a/libraries/native_contract/system_contract.cpp b/libraries/native_contract/system_contract.cpp index 5943183363a066a420db130e419df16c5e9d15cd..55d948d3a31dcdd91c975eef922094de84b36b84 100644 --- a/libraries/native_contract/system_contract.cpp +++ b/libraries/native_contract/system_contract.cpp @@ -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 ); } diff --git a/tests/slow_tests/slow_tests.cpp b/tests/slow_tests/slow_tests.cpp index df23f04e9064c074358bbaf7c04ab4b77442fece..e67ef160759342b188910a2b5e354695c53b1609 100644 --- a/tests/slow_tests/slow_tests.cpp +++ b/tests/slow_tests/slow_tests.cpp @@ -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 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{ {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{ {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{ {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{ {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 +#include + 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