From c5dba14c09bb1b039165ded55e1481b1a9c8e0dc Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 11 Jul 2017 18:47:14 -0400 Subject: [PATCH] Refine EOS standard C++ library - implement a large part of example exchange contract --- CMakeLists.txt | 2 +- contracts/currency/Makefile | 5 +- contracts/currency/currency.cpp | 4 +- contracts/currency/currency.hpp | 2 +- contracts/currency/currency.wast.hpp | 893 +++--------------- contracts/currency/eos.hpp | 222 ----- contracts/eoslib/eos.hpp | 375 ++++++++ contracts/exchange/Makefile | 19 + contracts/exchange/exchange.cpp | 259 ++++- .../include/eos/chain/key_value_object.hpp | 42 + .../eos/chain/message_handling_contexts.hpp | 12 + libraries/chain/include/eos/chain/types.hpp | 3 + libraries/chain/message_handling_contexts.cpp | 74 ++ libraries/types/include/eos/types/native.hpp | 1 + tests/tests/block_tests.cpp | 85 ++ 15 files changed, 974 insertions(+), 1024 deletions(-) delete mode 100644 contracts/currency/eos.hpp create mode 100644 contracts/eoslib/eos.hpp create mode 100644 contracts/exchange/Makefile diff --git a/CMakeLists.txt b/CMakeLists.txt index 1445b5605..fc154f9ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ INCLUDE( VersionMacros ) INCLUDE( SetupTargetMacros ) set( BLOCKCHAIN_NAME "Eos" ) -set( CMAKE_CXX_STANDARD 14 ) +set( CMAKE_CXX_STANDARD 17 ) set( CLI_CLIENT_EXECUTABLE_NAME eos_client ) set( GUI_CLIENT_EXECUTABLE_NAME eos ) diff --git a/contracts/currency/Makefile b/contracts/currency/Makefile index 17262c503..665fac647 100644 --- a/contracts/currency/Makefile +++ b/contracts/currency/Makefile @@ -1,5 +1,5 @@ -currency.wast: currency.cpp Makefile eos.hpp currency.hpp - /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm --std=c++14 --target=wasm32 -nostdinc -c currency.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .currency.bc +currency.wast: currency.cpp Makefile ../eoslib/eos.hpp currency.hpp + /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm -O3 --std=c++14 --target=wasm32 -c currency.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .currency.bc -I /usr/local/Cellar/llvm/4.0.0_1/include/c++/v1/ /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 @@ -9,6 +9,7 @@ currency.wast: currency.cpp Makefile eos.hpp currency.hpp echo ')=====";' >> currency.wast.hpp # /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm -O3 --std=c++14 --target=wasm32 -nostdinc -c currency.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .currency.bc +# /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm --std=c++14 --target=wasm32 -nostdinc -c currency.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .currency.bc test.wast: test.cpp Makefile eos.hpp /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm -O3 --std=c++14 --target=wasm32 -nostdinc -c test.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions diff --git a/contracts/currency/currency.cpp b/contracts/currency/currency.cpp index f04bdaef9..31af03dee 100644 --- a/contracts/currency/currency.cpp +++ b/contracts/currency/currency.cpp @@ -25,8 +25,8 @@ void apply_currency_transfer() { requireNotice( transfer.to, transfer.from ); requireAuth( transfer.from ); - static CurrencyAccount from_account; - static CurrencyAccount to_account; + CurrencyAccount from_account; + CurrencyAccount to_account; Db::get( transfer.from, NAME("account"), from_account ); Db::get( transfer.to, NAME("account"), to_account ); diff --git a/contracts/currency/currency.hpp b/contracts/currency/currency.hpp index efb132881..a7fb23616 100644 --- a/contracts/currency/currency.hpp +++ b/contracts/currency/currency.hpp @@ -1,4 +1,4 @@ -#include "eos.hpp" +#include /** * Transfer requires that the sender and receiver be the first two diff --git a/contracts/currency/currency.wast.hpp b/contracts/currency/currency.wast.hpp index e1c82bbac..130f944c4 100644 --- a/contracts/currency/currency.wast.hpp +++ b/contracts/currency/currency.wast.hpp @@ -18,325 +18,199 @@ const char* currency_wast = R"=====( (import "env" "store_i64" (func $store_i64 (param i64 i64 i64 i32 i32) (result i32))) (table 0 anyfunc) (memory $0 1) - (data (i32.const 4) "P\04\00\00") - (data (i32.const 48) "insufficient funds\00") + (data (i32.const 4) "0\04\00\00") + (data (i32.const 16) "insufficient funds\00") (export "memory" (memory $0)) (export "_Z23apply_currency_transferv" (func $_Z23apply_currency_transferv)) (export "init" (func $init)) (export "apply" (func $apply)) (func $_Z23apply_currency_transferv - (local $0 i32) - (local $1 i32) - (local $2 i64) - (local $3 i64) - (local $4 i32) + (local $0 i64) + (local $1 i64) + (local $2 i32) (i32.store offset=4 (i32.const 0) - (tee_local $4 + (tee_local $2 (i32.sub (i32.load offset=4 (i32.const 0) ) - (i32.const 112) + (i32.const 48) ) ) ) - (call $_Z14currentMessageI8TransferET_v - (i32.add - (get_local $4) - (i32.const 80) - ) + (i64.store offset=40 + (get_local $2) + (i64.const 0) ) - (i32.store offset=108 - (get_local $4) - (i32.add - (get_local $4) - (i32.const 80) + (drop + (call $readMessage + (i32.add + (get_local $2) + (i32.const 24) + ) + (i32.const 24) ) ) - (call $_Z13requireNoticeIJyEEvyDpT_ - (i64.load offset=88 - (get_local $4) - ) - (i64.load offset=80 - (get_local $4) + (set_local $0 + (i64.load offset=24 + (get_local $2) ) ) - (call $requireAuth - (i64.load - (i32.load offset=108 - (get_local $4) - ) + (call $requireNotice + (i64.load offset=32 + (get_local $2) ) ) - (block $label$0 - (br_if $label$0 - (i32.load8_u offset=24 - (i32.const 0) - ) - ) - (drop - (call $_ZN15CurrencyAccountC2Ey - (i32.const 16) - (i64.const 0) - ) - ) - (i32.store8 offset=24 - (i32.const 0) - (i32.const 1) - ) + (call $requireNotice + (get_local $0) ) - (block $label$1 - (br_if $label$1 - (i32.load8_u offset=40 - (i32.const 0) - ) - ) - (drop - (call $_ZN15CurrencyAccountC2Ey - (i32.const 32) - (i64.const 0) - ) - ) - (i32.store8 offset=40 - (i32.const 0) - (i32.const 1) + (call $requireAuth + (i64.load offset=24 + (get_local $2) ) ) - (set_local $0 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 72) - ) - (i64.load - (i32.load offset=108 - (get_local $4) - ) - ) - ) + (i64.store offset=16 + (get_local $2) + (i64.const 0) ) - (set_local $1 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 64) - ) - (call $_ZN9ConstNameILy21967113313EE5valueEv) - ) + (i64.store offset=8 + (get_local $2) + (i64.const 0) ) (drop - (call $_ZN2Db3getI15CurrencyAccountEEb4NameS2_RT_ - (i64.load - (get_local $0) - ) - (i64.load - (get_local $1) - ) - (i32.const 16) - ) - ) - (set_local $0 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 56) - ) - (i64.load offset=8 - (i32.load offset=108 - (get_local $4) - ) + (call $load_i64 + (i64.load offset=24 + (get_local $2) ) - ) - ) - (set_local $1 - (call $_ZN4NameC2Ey + (call $currentCode) + (i64.const 497826380083) + (i64.const 21967113313) (i32.add - (get_local $4) - (i32.const 48) + (get_local $2) + (i32.const 16) ) - (call $_ZN9ConstNameILy21967113313EE5valueEv) + (i32.const 8) ) ) (drop - (call $_ZN2Db3getI15CurrencyAccountEEb4NameS2_RT_ - (i64.load - (get_local $0) + (call $load_i64 + (i64.load offset=32 + (get_local $2) ) - (i64.load - (get_local $1) + (call $currentCode) + (i64.const 497826380083) + (i64.const 21967113313) + (i32.add + (get_local $2) + (i32.const 8) ) - (i32.const 32) + (i32.const 8) ) ) (call $assert (i64.ge_u (i64.load offset=16 - (i32.const 0) + (get_local $2) ) - (i64.load offset=16 - (i32.load offset=108 - (get_local $4) - ) + (i64.load offset=40 + (get_local $2) ) ) - (i32.const 48) + (i32.const 16) ) (i64.store offset=16 - (i32.const 0) - (tee_local $3 + (get_local $2) + (tee_local $1 (i64.sub (i64.load offset=16 - (i32.const 0) + (get_local $2) ) - (tee_local $2 - (i64.load offset=16 - (i32.load offset=108 - (get_local $4) - ) + (tee_local $0 + (i64.load offset=40 + (get_local $2) ) ) ) ) ) - (i64.store offset=32 - (i32.const 0) + (i64.store offset=8 + (get_local $2) (i64.add - (get_local $2) - (i64.load offset=32 - (i32.const 0) + (get_local $0) + (i64.load offset=8 + (get_local $2) ) ) ) - (block $label$2 - (block $label$3 - (br_if $label$3 + (set_local $0 + (i64.load offset=24 + (get_local $2) + ) + ) + (block $label$0 + (block $label$1 + (br_if $label$1 (i64.eq - (get_local $3) + (get_local $1) (i64.const 0) ) ) - (set_local $0 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 24) - ) - (i64.load - (i32.load offset=108 - (get_local $4) - ) - ) - ) - ) - (set_local $1 - (call $_ZN4NameC2Ey + (drop + (call $store_i64 + (get_local $0) + (i64.const 497826380083) + (i64.const 21967113313) (i32.add - (get_local $4) + (get_local $2) (i32.const 16) ) - (call $_ZN9ConstNameILy21967113313EE5valueEv) - ) - ) - (drop - (call $_ZN2Db5storeI15CurrencyAccountEEl4NameS2_RKT_ - (i64.load - (get_local $0) - ) - (i64.load - (get_local $1) - ) - (i32.const 16) - ) - ) - (br $label$2) - ) - (set_local $0 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 40) - ) - (i64.load - (i32.load offset=108 - (get_local $4) - ) - ) - ) - ) - (set_local $1 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 32) + (i32.const 8) ) - (call $_ZN9ConstNameILy21967113313EE5valueEv) ) + (br $label$0) ) (drop - (call $_ZN2Db6removeI15CurrencyAccountEEb4NameS2_ - (i64.load - (get_local $0) - ) - (i64.load - (get_local $1) - ) - ) - ) - ) - (set_local $0 - (call $_ZN4NameC2Ey - (i32.add - (get_local $4) - (i32.const 8) - ) - (i64.load offset=8 - (i32.load offset=108 - (get_local $4) - ) + (call $remove_i64 + (get_local $0) + (i64.const 497826380083) + (i64.const 21967113313) ) ) ) - (set_local $1 - (call $_ZN4NameC2Ey - (get_local $4) - (call $_ZN9ConstNameILy21967113313EE5valueEv) - ) - ) (drop - (call $_ZN2Db5storeI15CurrencyAccountEEl4NameS2_RKT_ + (call $store_i64 (i64.load - (get_local $0) + (i32.add + (i32.add + (get_local $2) + (i32.const 24) + ) + (i32.const 8) + ) ) - (i64.load - (get_local $1) + (i64.const 497826380083) + (i64.const 21967113313) + (i32.add + (get_local $2) + (i32.const 8) ) - (i32.const 32) + (i32.const 8) ) ) (i32.store offset=4 (i32.const 0) (i32.add - (get_local $4) - (i32.const 112) - ) - ) - ) - (func $_Z14currentMessageI8TransferET_v (param $0 i32) - (drop - (call $readMessage - (call $_ZN8TransferC2Ev - (get_local $0) - ) - (i32.const 24) + (get_local $2) + (i32.const 48) ) ) ) - (func $_Z13requireNoticeIJyEEvyDpT_ (param $0 i64) (param $1 i64) - (local $2 i32) + (func $init + (local $0 i32) (i32.store offset=4 (i32.const 0) - (tee_local $2 + (tee_local $0 (i32.sub (i32.load offset=4 (i32.const 0) @@ -346,552 +220,45 @@ const char* currency_wast = R"=====( ) ) (i64.store offset=8 - (get_local $2) (get_local $0) + (i64.const 1000000000) ) - (i64.store - (get_local $2) - (get_local $1) - ) - (call $requireNotice - (i64.load offset=8 - (get_local $2) - ) - ) - (call $requireNotice - (i64.load - (get_local $2) + (drop + (call $store_i64 + (i64.const 862690298531) + (i64.const 497826380083) + (i64.const 21967113313) + (i32.add + (get_local $0) + (i32.const 8) + ) + (i32.const 8) ) ) (i32.store offset=4 (i32.const 0) (i32.add - (get_local $2) + (get_local $0) (i32.const 16) ) ) ) - (func $_ZN15CurrencyAccountC2Ey (param $0 i32) (param $1 i64) (result i32) - (local $2 i32) - (i32.store offset=12 - (tee_local $2 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 16) - ) - ) - (get_local $0) - ) - (i64.store - (get_local $2) - (get_local $1) - ) - (i64.store - (tee_local $2 - (i32.load offset=12 - (get_local $2) - ) - ) - (get_local $1) - ) - (get_local $2) - ) - (func $_ZN2Db3getI15CurrencyAccountEEb4NameS2_RT_ (param $0 i64) (param $1 i64) (param $2 i32) (result i32) - (local $3 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $3 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 48) - ) - ) - ) - (i64.store offset=40 - (get_local $3) - (get_local $0) - ) - (i64.store offset=32 - (get_local $3) - (get_local $1) - ) - (i32.store offset=28 - (get_local $3) - (get_local $2) - ) - (i32.store offset=20 - (get_local $3) - (i32.load offset=44 - (get_local $3) - ) - ) - (i32.store offset=16 - (get_local $3) - (i32.load offset=40 - (get_local $3) - ) - ) - (set_local $2 - (call $_ZN4NameC2Ey - (i32.add - (get_local $3) - (i32.const 8) + (func $apply (param $0 i64) (param $1 i64) + (block $label$0 + (br_if $label$0 + (i64.ne + (get_local $0) + (i64.const 862690298531) ) - (call $currentCode) ) - ) - (i64.store - (get_local $3) - (tee_local $1 - (i64.load offset=32 - (get_local $3) - ) - ) - ) - (set_local $2 - (call $_ZN2Db3getI15CurrencyAccountEEb4NameS2_S2_RT_ - (i64.load offset=16 - (get_local $3) - ) - (i64.load - (get_local $2) - ) - (get_local $1) - (i32.load offset=28 - (get_local $3) - ) - ) - ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $3) - (i32.const 48) - ) - ) - (get_local $2) - ) - (func $_ZN4NameC2Ey (param $0 i32) (param $1 i64) (result i32) - (local $2 i32) - (i32.store offset=12 - (tee_local $2 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 16) - ) - ) - (get_local $0) - ) - (i64.store - (get_local $2) - (get_local $1) - ) - (i64.store - (tee_local $2 - (i32.load offset=12 - (get_local $2) - ) - ) - (get_local $1) - ) - (get_local $2) - ) - (func $_ZN9ConstNameILy21967113313EE5valueEv (result i64) - (i64.const 21967113313) - ) - (func $_ZN2Db6removeI15CurrencyAccountEEb4NameS2_ (param $0 i64) (param $1 i64) (result i32) - (local $2 i32) - (local $3 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $3 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 32) - ) - ) - ) - (i64.store offset=24 - (get_local $3) - (get_local $0) - ) - (i64.store offset=16 - (get_local $3) - (get_local $1) - ) - (set_local $1 - (call $_ZNK4NamecvyEv - (i32.add - (get_local $3) - (i32.const 24) - ) - ) - ) - (i64.store offset=8 - (get_local $3) - (call $_ZN15CurrencyAccount7tableIdEv) - ) - (set_local $2 - (call $remove_i64 - (get_local $1) - (call $_ZNK4NamecvyEv - (i32.add - (get_local $3) - (i32.const 8) - ) - ) - (call $_ZNK4NamecvyEv - (i32.add - (get_local $3) - (i32.const 16) - ) - ) - ) - ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $3) - (i32.const 32) - ) - ) - (i32.ne - (get_local $2) - (i32.const 0) - ) - ) - (func $_ZN2Db5storeI15CurrencyAccountEEl4NameS2_RKT_ (param $0 i64) (param $1 i64) (param $2 i32) (result i32) - (local $3 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $3 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 32) - ) - ) - ) - (i64.store offset=24 - (get_local $3) - (get_local $0) - ) - (i64.store offset=16 - (get_local $3) - (get_local $1) - ) - (i32.store offset=12 - (get_local $3) - (get_local $2) - ) - (set_local $1 - (call $_ZNK4NamecvyEv - (i32.add - (get_local $3) - (i32.const 24) - ) - ) - ) - (i64.store - (get_local $3) - (call $_ZN15CurrencyAccount7tableIdEv) - ) - (set_local $2 - (call $store_i64 - (get_local $1) - (call $_ZNK4NamecvyEv - (get_local $3) - ) - (call $_ZNK4NamecvyEv - (i32.add - (get_local $3) - (i32.const 16) - ) - ) - (i32.load offset=12 - (get_local $3) - ) - (i32.const 8) - ) - ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $3) - (i32.const 32) - ) - ) - (get_local $2) - ) - (func $init - (local $0 i32) - (local $1 i32) - (local $2 i32) - (local $3 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $3 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 32) - ) - ) - ) - (set_local $0 - (call $_ZN4NameC2Ey - (i32.add - (get_local $3) - (i32.const 24) - ) - (call $_ZN9ConstNameILy862690298531EE5valueEv) - ) - ) - (set_local $1 - (call $_ZN4NameC2Ey - (i32.add - (get_local $3) - (i32.const 16) - ) - (call $_ZN9ConstNameILy21967113313EE5valueEv) - ) - ) - (set_local $2 - (call $_ZN15CurrencyAccountC2Ey - (i32.add - (get_local $3) - (i32.const 8) - ) - (i64.const 1000000000) - ) - ) - (drop - (call $_ZN2Db5storeI15CurrencyAccountEEl4NameS2_RKT_ - (i64.load - (get_local $0) - ) - (i64.load - (get_local $1) - ) - (get_local $2) - ) - ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $3) - (i32.const 32) - ) - ) - ) - (func $_ZN9ConstNameILy862690298531EE5valueEv (result i64) - (i64.const 862690298531) - ) - (func $apply (param $0 i64) (param $1 i64) - (local $2 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $2 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 16) - ) - ) - ) - (i64.store offset=8 - (get_local $2) - (get_local $0) - ) - (i64.store - (get_local $2) - (get_local $1) - ) - (block $label$0 - (br_if $label$0 - (i64.ne - (i64.load offset=8 - (get_local $2) - ) - (call $_ZN9ConstNameILy862690298531EE5valueEv) - ) - ) - (br_if $label$0 - (i64.ne - (i64.load - (get_local $2) - ) - (call $_ZN9ConstNameILy624065709652EE5valueEv) + (br_if $label$0 + (i64.ne + (get_local $1) + (i64.const 624065709652) ) ) (call $_Z23apply_currency_transferv) ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $2) - (i32.const 16) - ) - ) - ) - (func $_ZN9ConstNameILy624065709652EE5valueEv (result i64) - (i64.const 624065709652) - ) - (func $_ZN8TransferC2Ev (param $0 i32) (result i32) - (local $1 i32) - (set_local $1 - (i32.load offset=4 - (i32.const 0) - ) - ) - (i64.store offset=16 - (get_local $0) - (i64.const 0) - ) - (i32.store offset=12 - (i32.sub - (get_local $1) - (i32.const 16) - ) - (get_local $0) - ) - (get_local $0) - ) - (func $_ZN2Db3getI15CurrencyAccountEEb4NameS2_S2_RT_ (param $0 i64) (param $1 i64) (param $2 i64) (param $3 i32) (result i32) - (local $4 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $4 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 48) - ) - ) - ) - (i64.store offset=40 - (get_local $4) - (get_local $0) - ) - (i64.store offset=32 - (get_local $4) - (get_local $1) - ) - (i64.store offset=24 - (get_local $4) - (get_local $2) - ) - (i32.store offset=20 - (get_local $4) - (get_local $3) - ) - (set_local $2 - (i64.load offset=32 - (get_local $4) - ) - ) - (set_local $1 - (i64.load offset=40 - (get_local $4) - ) - ) - (i64.store offset=8 - (get_local $4) - (tee_local $0 - (call $_ZN15CurrencyAccount7tableIdEv) - ) - ) - (i32.store offset=16 - (get_local $4) - (tee_local $3 - (call $load_i64 - (get_local $1) - (get_local $2) - (get_local $0) - (i64.load offset=24 - (get_local $4) - ) - (i32.load offset=20 - (get_local $4) - ) - (i32.const 8) - ) - ) - ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $4) - (i32.const 48) - ) - ) - (i32.gt_s - (get_local $3) - (i32.const 0) - ) - ) - (func $_ZN15CurrencyAccount7tableIdEv (result i64) - (local $0 i64) - (local $1 i32) - (i32.store offset=4 - (i32.const 0) - (tee_local $1 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 16) - ) - ) - ) - (set_local $0 - (i64.load - (call $_ZN4NameC2Ey - (i32.add - (get_local $1) - (i32.const 8) - ) - (call $_ZN9ConstNameILy497826380083EE5valueEv) - ) - ) - ) - (i32.store offset=4 - (i32.const 0) - (i32.add - (get_local $1) - (i32.const 16) - ) - ) - (get_local $0) - ) - (func $_ZN9ConstNameILy497826380083EE5valueEv (result i64) - (i64.const 497826380083) - ) - (func $_ZNK4NamecvyEv (param $0 i32) (result i64) - (i32.store offset=12 - (i32.sub - (i32.load offset=4 - (i32.const 0) - ) - (i32.const 16) - ) - (get_local $0) - ) - (i64.load - (get_local $0) - ) ) ) )====="; diff --git a/contracts/currency/eos.hpp b/contracts/currency/eos.hpp deleted file mode 100644 index 8bd5d3d1f..000000000 --- a/contracts/currency/eos.hpp +++ /dev/null @@ -1,222 +0,0 @@ -extern "C" { - -typedef long long int64_t; -typedef unsigned long long uint64_t; -typedef unsigned long uint32_t; -typedef long int32_t; - -typedef uint64_t AccountName; -typedef uint64_t TableName; - -/** - * @param msg - a pointer where up to @ref len bytes of the current message will be coppied - * @return the number of bytes copied to msg - */ -extern uint32_t readMessage( void* msg, uint32_t len ); -/** - * This method is useful for dynamicly sized messages - * - * @return the length of the current message - */ -extern uint32_t messageSize(); - -/** - * @param scope - the account scope that will be read, must exist in the transaction scopes list - * @param table - the ID/name of the table within the current scope/code context to modify - * @param key - an key that can be used to lookup data in the table - * - * @return a unique ID assigned to this table record separate from the key, used for iterator access - */ -int32_t store_i64( AccountName scope, TableName table, uint64_t key, const void* data, uint32_t datalen ); - -/** - * @param scope - the account scope that will be read, must exist in the transaction scopes list - * @param code - identifies the code that controls write-access to the data - * @param table - the ID/name of the table within the scope/code context to query - * @param key - an key that can be used to lookup data in the table - * @param data - location to copy the data stored at key - * @param datalen - the maximum length of data to read - * - * @return the number of bytes read or -1 if key was not found - */ -int32_t load_i64( AccountName scope, AccountName code, TableName table, uint64_t key, void* data, uint32_t datalen ); -int32_t remove_i64( AccountName scope, TableName table, uint64_t key ); - -void print( const char* cstr ); -void printi( uint64_t value ); - -/** - * @return the account which specifes the code that is being run - */ -AccountName currentCode(); -void assert( uint32_t test, const char* cstr ); - -/** - * Verifies that @ref name exists in the set of notified accounts on a message. Throws if not found - */ -void requireNotice( AccountName ); -/** - * Verifies that @ref name exists in the set of provided auths on a message. Throws if not found - */ -void requireAuth( AccountName name ); - -/** - * Gets the notified account at index N - */ -AccountName getNotify( int32_t index ); - -/** - * Gets the required auth at index N - */ -AccountName getAuth( int32_t index ); - -/** - * Returns the time of the last block (not the block including this message) - */ -uint32_t now(); - -/** - * Any message handler can generate an "output" to be returned if it calls the callResult() API, if - * callResult() is not called then result_length will be set to 0. - * - * @param code - the account whose code should execute within the notify account contexts - * @param notify - any account in the notify list must be within the current read scope - * @param message - the message that should be delivered - * @param result - a place to store the result of the call - * - * @param canfail - if true then a new undo context is started and undon on error, else this method throws on error - * @return 0 on success and 1 if the call aborted - */ -uint32_t syncCall( AccountName code, AccountName* authorities, uint32_t numauths, - AccountName* notify, uint32_t numnotice, - const void* message, uint32_t message_length, - void* result, uint32_t* result_length, - bool canfail ); - -/** - * Used to specify the return value for syncCall - */ -void callResult( const void* data, uint32_t datalen ); - -/** - * Given the message with CODE.ACTION notifying CONTEXT we normally also allow - * CONTEXT to define its own method handler that can be called: eg context::apply_code_action() - * - * In some cases the code::apply_code_action() may want to prevent context::apply_code_action() - * from being invoked. This is necessary if the context may have incentive to block a particular - * contract from modifying data stored in the context/code section. - * - * For example a social media website that stores votes on accounts and an account owner interested - * in blocking negative votes. - */ -void disableContextCode( uint64_t AccountName ); - -} /// extern C - -static constexpr char char_to_symbol( char c ) { - if( c >= 'a' && c <= 'z' ) - return (c - 'a') + 1; - if( c >= '1' && c <= '5' ) - return (c - '1') + 26; - return 0; -} - -static constexpr uint64_t string_to_name( const char* str ) { - uint32_t len = 0; - while( str[len] ) ++len; - - uint64_t value = 0; - - for( uint32_t i = 0; i <= 12 && i < len; ++i ) { - value <<= 5; - value |= char_to_symbol( str[ len -1 - i ] ); - } - - if( len == 13 ) { - value <<= 4; - value |= 0x0f & char_to_symbol( str[ 12 ] ); - } - return value; -} -template -struct ConstName { - static uint64_t value() { return I; } - operator uint64_t()const { return I; } -}; -#define NAME(X) (ConstName::value()) - -struct Name { - Name(){} -// Name( const char* c ) : value( string_to_name(c) ){} - Name( uint64_t v ): value(v) {} - - operator uint64_t()const { return value; } - - friend bool operator==( const Name& a, const Name& b ) { return a.value == b.value; } - AccountName value = 0; -}; - -struct Db -{ - template - static bool test( Name key, const T& value ) { - return T::tableId(); - } - - template - static bool get( Name key, T& value ){ - return get( currentCode(), key, value ); - } - - template - static bool get( Name scope, Name key, T& value ){ - return get( scope, currentCode(), key, value ); - } - - template - static bool get( Name scope, Name code, Name key, T& result ) { - auto read = load_i64( scope.value, code.value, T::tableId().value, key.value, &result, sizeof(result) ); - return read > 0; - } - - template - static int32_t store( Name key, const T& value ) { - return store( currentCode(), key, value ); - } - - template - static int32_t store( Name scope, Name key, const T& value ) { - return store_i64( scope, T::tableId(), key, &value, sizeof(value) ); - } - - template - static bool remove( Name scope, Name key ) { - return remove_i64( scope, T::tableId(), key ); - } - template - static bool remove( Name key ) { - return remove( currentCode(), key ); - } -}; - -template -T currentMessage() { - T value; - readMessage( &value, sizeof(value) ); - return value; -} - -template -void requireNotice( AccountName name, Accounts... accounts ){ - requireNotice( name ); - requireNotice( accounts... ); -} - - -struct Ratio { - uint64_t base = 1; - uint64_t quote = 1; -}; -static_assert( sizeof(Ratio) == 2*sizeof(uint64_t), "unexpected padding" ); - - diff --git a/contracts/eoslib/eos.hpp b/contracts/eoslib/eos.hpp new file mode 100644 index 000000000..2db2e88cc --- /dev/null +++ b/contracts/eoslib/eos.hpp @@ -0,0 +1,375 @@ +extern "C" { + +typedef long long int64_t; +typedef unsigned long long uint64_t; +typedef unsigned long uint32_t; +typedef long int32_t; +typedef unsigned __int128 uint128_t; +typedef __int128 int128_t; +typedef unsigned char uint8_t; + +typedef uint64_t AccountName; +typedef uint64_t TableName; +typedef uint32_t Time; + +/** + * @param msg - a pointer where up to @ref len bytes of the current message will be coppied + * @return the number of bytes copied to msg + */ +extern uint32_t readMessage( void* msg, uint32_t len ); +/** + * This method is useful for dynamicly sized messages + * + * @return the length of the current message + */ +extern uint32_t messageSize(); + +/** + * @param scope - the account scope that will be read, must exist in the transaction scopes list + * @param table - the ID/name of the table within the current scope/code context to modify + * @param key - an key that can be used to lookup data in the table + * + * @return a unique ID assigned to this table record separate from the key, used for iterator access + */ +int32_t store_i64( AccountName scope, TableName table, uint64_t key, const void* data, uint32_t datalen ); + +/** + * @param scope - the account scope that will be read, must exist in the transaction scopes list + * @param code - identifies the code that controls write-access to the data + * @param table - the ID/name of the table within the scope/code context to query + * @param key - an key that can be used to lookup data in the table + * @param data - location to copy the data stored at key + * @param datalen - the maximum length of data to read + * + * @return the number of bytes read or -1 if key was not found + */ +int32_t load_i64( AccountName scope, AccountName code, TableName table, uint64_t key, void* data, uint32_t datalen ); +int32_t remove_i64( AccountName scope, TableName table, uint64_t key ); + + +/** + * These methods expect data to point to a record that is at least 2*sizeof(uint128_t) where the leading + * 32 bytes are the primary and secondary keys. These keys will be interpreted and sorted as unsigned + * 128 bit integers. + * + * @return the total number of bytes read or -1 for "not found" or "end". + */ +///@{ +int32_t front_primary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ); +int32_t back_primary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ); +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 front_secondary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ); +int32_t back_secondary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ); +int32_t next_secondary_i128i128( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ); +int32_t previous_secondary_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 uint128_t* key, void* data, uint32_t len ); +int32_t lower_bound_primary_i128i128( AccountName scope, AccountName code, TableName table, + const uint128_t* key, void* data, uint32_t len ); + +int32_t upper_bound_secondary_i128i128( AccountName scope, AccountName code, TableName table, + const uint128_t* key, void* data, uint32_t len ); +int32_t lower_bound_secondary_i128i128( AccountName scope, AccountName code, TableName table, + const uint128_t* key, void* data, uint32_t len ); + + +/// data must point to 2*sizeof(uint128) containing primary and secondary key +bool remove_i128i128( AccountName scope, TableName table, void* data ); +/// data must point to at least 2*sizeof(uint128) containing primary and secondary key +bool store_i128i128( AccountName scope, 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_secondary_i128i128( AccountName scope, AccountName code, TableName table, const void* secondary, void* data, uint32_t len ); + + +void print( const char* cstr ); +void printi( uint64_t value ); + +/** + * @return the account which specifes the code that is being run + */ +AccountName currentCode(); +void assert( uint32_t test, const char* cstr ); + +/** + * Verifies that @ref name exists in the set of notified accounts on a message. Throws if not found + */ +void requireNotice( AccountName ); +/** + * Verifies that @ref name exists in the set of provided auths on a message. Throws if not found + */ +void requireAuth( AccountName name ); + +/** + * Gets the notified account at index N + */ +AccountName getNotify( int32_t index ); + +/** + * Gets the required auth at index N + */ +AccountName getAuth( int32_t index ); + +/** + * Returns the time of the last block (not the block including this message) + */ +uint32_t now(); + +/** + * Any message handler can generate an "output" to be returned if it calls the callResult() API, if + * callResult() is not called then result_length will be set to 0. + * + * @param code - the account whose code should execute within the notify account contexts + * @param notify - any account in the notify list must be within the current read scope + * @param message - the message that should be delivered + * @param result - a place to store the result of the call + * + * @param canfail - if true then a new undo context is started and undon on error, else this method throws on error + * @return 0 on success and 1 if the call aborted + */ +uint32_t syncCall( AccountName code, AccountName* authorities, uint32_t numauths, + AccountName* notify, uint32_t numnotice, + const void* message, uint32_t message_length, + void* result, uint32_t* result_length, + bool canfail ); + +/** + * Used to specify the return value for syncCall + */ +void callResult( const void* data, uint32_t datalen ); + +/** + * Given the message with CODE.ACTION notifying CONTEXT we normally also allow + * CONTEXT to define its own method handler that can be called: eg context::apply_code_action() + * + * In some cases the code::apply_code_action() may want to prevent context::apply_code_action() + * from being invoked. This is necessary if the context may have incentive to block a particular + * contract from modifying data stored in the context/code section. + * + * For example a social media website that stores votes on accounts and an account owner interested + * in blocking negative votes. + */ +void disableContextCode( uint64_t AccountName ); + +} /// extern C + +template +T min( const T& a, const T&b ) { + return a < b ? a : b; +} + +static constexpr char char_to_symbol( char c ) { + if( c >= 'a' && c <= 'z' ) + return (c - 'a') + 1; + if( c >= '1' && c <= '5' ) + return (c - '1') + 26; + return 0; +} + +static constexpr uint64_t string_to_name( const char* str ) { + uint32_t len = 0; + while( str[len] ) ++len; + + uint64_t value = 0; + + for( uint32_t i = 0; i <= 12 && i < len; ++i ) { + value <<= 5; + value |= char_to_symbol( str[ len -1 - i ] ); + } + + if( len == 13 ) { + value <<= 4; + value |= 0x0f & char_to_symbol( str[ 12 ] ); + } + return value; +} +template +struct ConstName { + static uint64_t value() { return I; } + operator uint64_t()const { return I; } +}; +#define NAME(X) (ConstName::value()) +#define N(X) string_to_name(#X) + +struct Name { + Name(){} +// Name( const char* c ) : value( string_to_name(c) ){} + Name( uint64_t v ): value(v) {} + + operator uint64_t()const { return value; } + + friend bool operator==( const Name& a, const Name& b ) { return a.value == b.value; } + AccountName value = 0; +}; + +struct Db +{ + + template + static bool get( Name key, T& value ){ + return get( currentCode(), key, value ); + } + + template + static bool get( Name scope, Name key, T& value ){ + return get( scope, currentCode(), key, value ); + } + + template + static bool get( Name scope, Name code, Name key, T& result ) { + auto read = load_i64( scope.value, code.value, T::tableId().value, key.value, &result, sizeof(result) ); + return read > 0; + } + + template + static int32_t store( Name key, const T& value ) { + return store( currentCode(), key, value ); + } + + template + static int32_t store( Name scope, Name key, const T& value ) { + return store_i64( scope, T::tableId(), key, &value, sizeof(value) ); + } + + template + static bool remove( Name scope, Name key ) { + return remove_i64( scope, T::tableId(), key ); + } + template + static bool remove( Name key ) { + return remove( currentCode(), key ); + } +}; + +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; +template struct remove_reference { typedef T type; }; + +template +T currentMessage() { + T value; + readMessage( &value, sizeof(value) ); + return value; +} + +template +void requireNotice( AccountName name, Accounts... accounts ){ + requireNotice( name ); + requireNotice( accounts... ); +} + + +struct Ratio { + uint64_t base = 1; + uint64_t quote = 1; +}; +static_assert( sizeof(Ratio) == 2*sizeof(uint64_t), "unexpected padding" ); + + +template +struct table_impl{}; + +template<> +struct table_impl<16,16> { + static int32_t front_primary( uint64_t scope, uint64_t code, uint64_t table, void* data, uint32_t len ) { + return front_primary_i128i128( scope, code, table, data, len ); + } + static int32_t back_primary( uint64_t scope, uint64_t code, uint64_t table, void* data, uint32_t len ) { + return back_primary_i128i128( scope, code, table, data, len ); + } + static bool remove( uint64_t scope, uint64_t table, 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 front_secondary( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ) { + return front_secondary_i128i128( scope, code, table, data, len ); + } + static int32_t back_secondary( AccountName scope, AccountName code, TableName table, void* data, uint32_t len ) { + return back_secondary_i128i128( scope, code, table, data, len ); + } + static bool store( AccountName scope, TableName table, void* data, uint32_t len ) { + return store_i128i128( scope, table, data, len ); + } +}; + +template +struct Table { + private: + typedef typename remove_referenceprimary() )>::type Primary; + typedef typename remove_referencesecondary() )>::type Secondary; + + typedef table_impl impl; + + public: + + struct PrimaryIndex { + static bool front( Record& r ) { + return impl::front_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool back( Record& r ) { + return impl::back_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool next( Record& r ) { + return impl::next_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool previous( Record& r ) { + return impl::previous_primary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool get( const Primary& p, Record& r ) { + return impl::load_primary( scope, code, table, &p, &r, sizeof(Record) ) == sizeof(Record); + } + static bool lower_bound( const Primary& p, Record& r ) { + return impl::lower_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record); + } + static bool upper_bound( const Primary& p, Record& r ) { + return impl::upper_bound_primary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record); + } + static bool remove( Record& r ) { + impl::remove( scope, table, &r ); + } + }; + struct SecondaryIndex { + static bool front( Record& r ) { + return impl::front_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool back( Record& r ) { + return impl::back_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool next( Record& r ) { + return impl::next_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool previous( Record& r ) { + return impl::previous_secondary( scope, code, table, &r, sizeof(Record) ) == sizeof(Record); + } + static bool get( const Primary& p, Record& r ) { + return impl::load_secondary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record); + } + static bool lower_bound( const Primary& p, Record& r ) { + return impl::lower_bound_secondary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record); + } + static bool upper_bound( const Primary& p, Record& r ) { + return impl::upper_bound_secondary( scope, code, table, &p &r, sizeof(Record) ) == sizeof(Record); + } + static bool remove( Record& r ) { + impl::remove( scope, table, &r ); + } + }; + + static bool store( Record& r ) { + return impl::store( scope, table, &r, sizeof(r) ); + } + static bool remove( Record& r ) { + return impl::remove( scope, table, &r ); + } + +}; + +#define TABLE(SCOPE, CODE, TABLE, TYPE) Table + + + diff --git a/contracts/exchange/Makefile b/contracts/exchange/Makefile new file mode 100644 index 000000000..aec4c8930 --- /dev/null +++ b/contracts/exchange/Makefile @@ -0,0 +1,19 @@ +exchange.wast: exchange.cpp Makefile ../eoslib/eos.hpp 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 + cat exchange.wast >> exchange.wast.hpp + echo ')=====";' >> exchange.wast.hpp + +# /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm -O3 --std=c++14 --target=wasm32 -nostdinc -c currency.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .currency.bc +# /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm --std=c++14 --target=wasm32 -nostdinc -c currency.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions -o .currency.bc + +test.wast: test.cpp Makefile eos.hpp + /usr/local/Cellar/llvm/4.0.0_1/bin/clang-4.0 -emit-llvm -O3 --std=c++14 --target=wasm32 -nostdinc -c test.cpp -I.. -fno-threadsafe-statics -fno-rtti -fno-exceptions + /Users/dlarimer/Downloads/llvm/build/bin/llc -asm-verbose=false test.bc + /Users/dlarimer/eos/libraries/binaryen/bin/s2wasm test.s -s 1024 > test.wast + cat test.wast + diff --git a/contracts/exchange/exchange.cpp b/contracts/exchange/exchange.cpp index c7e6f6d74..3413dc677 100644 --- a/contracts/exchange/exchange.cpp +++ b/contracts/exchange/exchange.cpp @@ -33,8 +33,40 @@ * creating, canceling, and filling orders do not require blocking either currency * contract. Users can only deposit or withdraw to their own currency account. */ -#include /// defines transfer struct +#include /// defines transfer struct +typedef uint128_t Price; + +struct OrderID { + uint64_t owner = 0; + uint64_t id = 0; + + operator const uint128_t& ()const { + return *reinterpret_cast(this); + } + + operator uint128_t& (){ + return *reinterpret_cast(this); + } +}; +static_assert( sizeof(OrderID) == sizeof(uint128_t), "unexpected packing" ); + +struct Order { + OrderID& primary() { return id; } + Price& secondary(){ return price; } + + OrderID id; + Price price; + uint64_t quantity; + Time expiration; +}; + +using Bids = TABLE(exchange,exchange,bids,Order); +using Asks = TABLE(exchange,exchange,asks,Order); +typedef Asks::SecondaryIndex AsksByPrice; +typedef Bids::SecondaryIndex BidsByPrice; +typedef Asks::PrimaryIndex AsksById; +typedef Bids::PrimaryIndex BidsById; struct Account { uint64_t a = 0; @@ -42,11 +74,8 @@ struct Account { int open_orders = 0; bool isEmpty()const { return !(a|b|open_orders); } - /** - * Balance records for all exchange users are stored here - * exchange/exchange/balance/username -> Balance - */ - static Name tableId() { return Name("balance"); } + + static Name tableId() { return NAME("account"); } }; @@ -58,21 +87,21 @@ struct Account { void apply_currencya_transfer() { const auto& transfer = currentMessage(); - if( transfer.to == "exchange" ) { - static Balance to_balance; + if( transfer.to == N(exchange) ) { + Account to_balance; Db::get( transfer.from, to_balance ); to_balance.a += transfer.amount; Db::store( transfer.from, to_balance ); - } else if( transfer.from == "exchange" ) { + } else if( transfer.from == N(exchange) ) { requireAuth( transfer.to ); /// require the reciever of funds (account owner) to authorize this transfer - static Balance to_balance; - auto balance = Db::get( transfer.to, to_balance ); - assert( balance.a >= transfer.amount, "insufficient funds to withdraw" ); - balance.a -= transfer.amount; + Account to_balance; + Db::get( transfer.to, to_balance ); + assert( to_balance.a >= transfer.amount, "insufficient funds to withdraw" ); + to_balance.a -= transfer.amount; - if( balance.isEmpty() ) - Db::remove( transfer.to ); + if( to_balance.isEmpty() ) + Db::remove( transfer.to ); else Db::store( transfer.to, to_balance ); } else { @@ -80,28 +109,24 @@ void apply_currencya_transfer() { } } -/** - * This method is called after the "transfer" action of code - * "currencya" is called and "exchange" is listed in the notifiers. - */ void apply_currencyb_transfer() { const auto& transfer = currentMessage(); - if( transfer.to == "exchange" ) { - static Balance to_balance; + if( transfer.to == N(exchange) ) { + Account to_balance; Db::get( transfer.from, to_balance ); to_balance.b += transfer.amount; Db::store( transfer.from, to_balance ); - } else if( transfer.from == "exchange" ) { + } else if( transfer.from == N(exchange) ) { requireAuth( transfer.to ); /// require the reciever of funds (account owner) to authorize this transfer - static Balance to_balance; - auto balance = Db::get( transfer.to, to_balance ); - assert( balance.b >= transfer.amount, "insufficient funds to withdraw" ); - balance.b -= transfer.amount; + Account to_balance; + Db::get( transfer.to, to_balance ); + assert( to_balance.b >= transfer.amount, "insufficient funds to withdraw" ); + to_balance.b -= transfer.amount; - if( balance.isEmpty() ) - Db::remove( transfer.to ); + if( to_balance.isEmpty() ) + Db::remove( transfer.to ); else Db::store( transfer.to, to_balance ); } else { @@ -109,13 +134,181 @@ void apply_currencyb_transfer() { } } +struct Buy { + AccountName buyer; + Price price; + uint64_t quantity = 0; + uint32_t id = 0; + Time expiration; + uint8_t fill_or_kill = false; +}; + +struct Sell { + AccountName seller; + Price price; + uint64_t quantity = 0; + uint32_t id = 0; + Time expiration; + uint8_t fill_or_kill = false; +}; + +void fill( Order& bid, Order& ask, Account& buyer, Account& seller, uint64_t usd, uint64_t token ) { + bid.quantity -= usd; + seller.a += usd; + ask.quantity -= token; + buyer.b += token; +} + +/** + * + * + */ +void apply_exchange_buy() { + auto buy = currentMessage(); + assert( buy.expiration > now(), "order expired" ); + + Account buyer_account; + + Db::get( buy.buyer, buyer_account ); + assert( buyer_account.a >= buy.quantity, "insufficient funds" ); + assert( buy.quantity > 0, "invalid quantity" ); + buyer_account.a -= buy.quantity; + + Order buyer_bid; + assert( BidsById::get( OrderID{ buy.buyer, buy.id}, buyer_bid ), "order with this id already exists" ); + + buyer_bid.price = buy.price; + buyer_bid.id.owner = buy.buyer; + buyer_bid.id.id = buy.id; + buyer_bid.quantity = buy.quantity; + buyer_bid.expiration = buy.expiration; + + Order lowest_ask; + + if( !AsksByPrice::front( lowest_ask ) ) { + assert( !buy.fill_or_kill, "order not completely filled" ); + Bids::store( buyer_bid ); + Db::store( buy.buyer, buyer_account ); + return; + } + + Account seller_account; + Db::get( lowest_ask.id.owner, seller_account ); + + while( lowest_ask.price <= buyer_bid.price ) { + auto ask_usd = lowest_ask.price * lowest_ask.quantity; + auto fill_amount_usd = min( ask_usd, buyer_bid.quantity ); + uint64_t fill_amount_token = 0; + + lowest_ask.quantity -= fill_amount_token; + buy.quantity -= fill_amount_usd; + + if( fill_amount_usd == ask_usd ) { /// complete fill of ask + fill_amount_token = lowest_ask.quantity; + } else { /// complete fill of buy + fill_amount_token = fill_amount_usd / lowest_ask.price; + } + /// either fill_amount_token == seller.quantity or fill_amount_usd == buy.quantity + + fill( buyer_bid, lowest_ask, buyer_account, seller_account, fill_amount_usd, fill_amount_token ); + + if( lowest_ask.quantity == 0 ) { + Db::store( lowest_ask.id.owner, seller_account ); + Asks::remove( lowest_ask ); + if( !AsksByPrice::front( lowest_ask ) ) { + break; + } + Db::get( lowest_ask.id.owner, seller_account ); + } else { + break; // buyer's bid should be filled + } + } + + Db::store( buy.buyer, buyer_account ); + if( buyer_bid.quantity > 0 ) { + assert( !buy.fill_or_kill, "order not completely filled" ); + Bids::store( buyer_bid ); + } +} + +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 ); + } +} + -export "C" { +extern "C" { void init() { + /* setAuthority( "currencya", "transfer", "anyone" ); setAuthority( "currencyb", "transfer", "anyone" ); registerHandler( "apply", "currencya", "transfer" ); registerHandler( "apply", "currencyb", "transfer" ); + */ } // void validate( uint64_t code, uint64_t action ) { } @@ -124,10 +317,10 @@ export "C" { * The apply method implements the dispatch of events to this contract */ void apply( uint64_t code, uint64_t action ) { - if( code == "currencya" ) { - if( action == "transfer" ) apply_currencya_transfer(); - } else if( code == "currencyb" ) { - if( action == "transfer" ) apply_currencyb_transfer(); + if( code == N(currencya) ) { + if( action == N(transfer) ) apply_currencya_transfer(); + } else if( code == N(currencyb) ) { + if( action == N(transfer) ) apply_currencyb_transfer(); } else { } } diff --git a/libraries/chain/include/eos/chain/key_value_object.hpp b/libraries/chain/include/eos/chain/key_value_object.hpp index 48ad226bd..1d3235b42 100644 --- a/libraries/chain/include/eos/chain/key_value_object.hpp +++ b/libraries/chain/include/eos/chain/key_value_object.hpp @@ -57,8 +57,50 @@ namespace eos { namespace chain { > >; + struct key128x128_value_object : public chainbase::object { + OBJECT_CTOR(key128x128_value_object, (value)) + + id_type id; + AccountName scope; + AccountName code; + AccountName table; + uint128_t primary_key; + uint128_t secondary_key; + shared_string value; + }; + + struct by_scope_primary; + struct by_scope_secondary; + using key128x128_value_index = chainbase::shared_multi_index_container< + key128x128_value_object, + indexed_by< + ordered_unique, member>, + ordered_unique, + composite_key< key128x128_value_object, + member, + member, + member, + member, + member + >, + composite_key_compare< std::less,std::less,std::less,std::less,std::less > + >, + ordered_unique, + composite_key< key128x128_value_object, + member, + member, + member, + member, + member + >, + composite_key_compare< std::less,std::less,std::less,std::less, std::less > + > + > + >; + } } // eos::chain CHAINBASE_SET_INDEX_TYPE(eos::chain::key_value_object, eos::chain::key_value_index) +CHAINBASE_SET_INDEX_TYPE(eos::chain::key128x128_value_object, eos::chain::key128x128_value_index) FC_REFLECT(eos::chain::key_value_object, (id)(scope)(code)(table)(key)(value) ) diff --git a/libraries/chain/include/eos/chain/message_handling_contexts.hpp b/libraries/chain/include/eos/chain/message_handling_contexts.hpp index b51b07763..572929d4f 100644 --- a/libraries/chain/include/eos/chain/message_handling_contexts.hpp +++ b/libraries/chain/include/eos/chain/message_handling_contexts.hpp @@ -38,6 +38,18 @@ public: int32_t load_i64( Name scope, Name code, Name table, Name Key, char* data, uint32_t maxlen ); + + int32_t front_primary_i128i128( Name scope, Name code, Name table, + 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 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, + uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen ); + int32_t lowerbound_secondary_i128i128( Name scope, Name code, Name table, + uint128_t* primary, uint128_t* secondary, char* data, uint32_t maxlen ); + ///< Parallel to msg.authorization; tracks which permissions have been used while processing the message vector used_authorizations; }; diff --git a/libraries/chain/include/eos/chain/types.hpp b/libraries/chain/include/eos/chain/types.hpp index b880fefbb..62949e704 100644 --- a/libraries/chain/include/eos/chain/types.hpp +++ b/libraries/chain/include/eos/chain/types.hpp @@ -133,6 +133,7 @@ namespace eos { namespace chain { using eos::types::Int64; using eos::types::Int128; using eos::types::Int256; + using eos::types::uint128_t; using ProducerRound = std::array; using RoundChanges = std::map; @@ -162,6 +163,7 @@ namespace eos { namespace chain { permission_object_type, action_code_object_type, key_value_object_type, + key128x128_value_object_type, action_permission_object_type, global_property_object_type, dynamic_global_property_object_type, @@ -206,6 +208,7 @@ FC_REFLECT_ENUM(eos::chain::object_type, (permission_object_type) (action_code_object_type) (key_value_object_type) + (key128x128_value_object_type) (action_permission_object_type) (global_property_object_type) (dynamic_global_property_object_type) diff --git a/libraries/chain/message_handling_contexts.cpp b/libraries/chain/message_handling_contexts.cpp index be853caf7..20f0feb1e 100644 --- a/libraries/chain/message_handling_contexts.cpp +++ b/libraries/chain/message_handling_contexts.cpp @@ -58,6 +58,80 @@ int32_t message_validate_context::load_i64( Name scope, Name code, Name table, N 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 ) { + + 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() || + itr->scope != scope || + itr->code != code || + itr->table != table || + itr->primary_key != *primary ) return -1; + + *secondary = itr->secondary_key; + + auto copylen = std::min(itr->value.size(),valuelen); + if( copylen ) { + itr->value.copy(value, copylen); + } + return copylen; +} +int32_t message_validate_context::lowerbound_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() || + itr->scope != scope || + itr->code != code || + itr->table != table ) return -1; + + *primary = itr->primary_key; + *secondary = itr->secondary_key; + + auto copylen = std::min(itr->value.size(),valuelen); + if( copylen ) { + itr->value.copy(value, copylen); + } + return copylen; +} + +int32_t message_validate_context::lowerbound_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), + uint128_t(0), *secondary ) ); + + if( itr == idx.end() || + itr->scope != scope || + itr->code != code || + itr->table != table ) return -1; + + *primary = itr->primary_key; + *secondary = itr->secondary_key; + + auto copylen = std::min(itr->value.size(),valuelen); + if( copylen ) { + itr->value.copy(value, copylen); + } + return copylen; +} + int32_t apply_context::remove_i64( Name scope, Name table, Name key ) { require_scope( scope ); diff --git a/libraries/types/include/eos/types/native.hpp b/libraries/types/include/eos/types/native.hpp index 6668cabee..5853360c3 100644 --- a/libraries/types/include/eos/types/native.hpp +++ b/libraries/types/include/eos/types/native.hpp @@ -54,6 +54,7 @@ namespace eos { namespace types { using Int64 = int64_t; //Int<64>; using Int128 = boost::multiprecision::int128_t; using Int256 = boost::multiprecision::int256_t; + using uint128_t = unsigned __int128; /// native clang/gcc 128 intrinisic struct Name { uint64_t value = 0; diff --git a/tests/tests/block_tests.cpp b/tests/tests/block_tests.cpp index d3f3462a1..510c5d0c4 100644 --- a/tests/tests/block_tests.cpp +++ b/tests/tests/block_tests.cpp @@ -137,6 +137,91 @@ using namespace chain; BOOST_AUTO_TEST_SUITE(block_tests) + +/** + * The purpose of this test is to demonstrate that it is possible + * to schedule 3M transactions into cycles where each cycle contains + * a set of independent transactions. + * + * This simple single threaded algorithm sorts 3M transactions each of which + * requires scope of 2 accounts chosen with a normal probability distribution + * amoung a set of 2 million accounts. + * + * This algorithm executes in less than 0.5 seconds on a 4Ghz Core i7 and could + * potentially take just .05 seconds with 10+ CPU cores. Future improvements might + * use a more robust bloomfilter to identify collisions. + */ +///@{ +struct Location { + uint32_t thread = -1; + uint32_t cycle = -1; + Location& operator=( const Location& l ) { + thread = l.thread; + cycle = l.cycle; + return *this; + } +}; +struct ExTransaction : public Transaction { + Location location; +}; + +BOOST_AUTO_TEST_CASE(schedule_test) { + vector transactions(3*1000*1000); + auto rand_scope = []() { + return rand()%1000000 + rand()%1000000; + }; + + for( auto& t : transactions ) { + t = new ExTransaction(); + t->scope = sort_names({ rand_scope(), rand_scope() }); + } + + int cycle = 0; + std::vector thread_count(1024); + + vector postponed; + postponed.reserve(transactions.size()); + auto current = transactions; + + vector used(1024*1024); + auto start = fc::time_point::now(); + bool scheduled = true; + while( scheduled ) { + scheduled = false; + for( auto t : current ) { + bool u = false; + for( const auto& s : t->scope ) { + if( used[s.value%used.size()] ) { + u = true; + postponed.push_back(t); + break; + } + } + if( !u ) { + for( const auto& s : t->scope ) { + used[s.value%used.size()] = true; + } + t->location.cycle = cycle; + t->location.thread = thread_count[cycle]++; + scheduled = true; + } + } + current.resize(0); + used.resize(0); used.resize(1024*1024); + std::swap( current, postponed ); + ++cycle; + + } + auto end = fc::time_point::now(); + thread_count.resize(cycle+1); +// idump((cycle)); +// idump((thread_count)); + + auto sort_time = end-start; + edump((sort_time.count()/1000000.0)); +} +///@} end of schedule test + BOOST_AUTO_TEST_CASE(name_test) { using eos::types::Name; Name temp; -- GitLab