提交 c5dba14c 编写于 作者: D Daniel Larimer

Refine EOS standard C++ library

- implement a large part of example exchange contract
上级 ba222004
......@@ -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 )
......
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
......
......@@ -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 );
......
#include "eos.hpp"
#include <eoslib/eos.hpp>
/**
* Transfer requires that the sender and receiver be the first two
......
......@@ -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)
)
)
)
)=====";
......@@ -4,9 +4,13 @@ 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
......@@ -42,6 +46,45 @@ int32_t store_i64( AccountName scope, TableName table, uint64_t key, const void*
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 );
......@@ -113,6 +156,11 @@ void disableContextCode( uint64_t AccountName );
} /// extern C
template<typename T>
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;
......@@ -144,6 +192,7 @@ struct ConstName {
operator uint64_t()const { return I; }
};
#define NAME(X) (ConstName<string_to_name(X)>::value())
#define N(X) string_to_name(#X)
struct Name {
Name(){}
......@@ -158,10 +207,6 @@ struct Name {
struct Db
{
template<typename T>
static bool test( Name key, const T& value ) {
return T::tableId();
}
template<typename T>
static bool get( Name key, T& value ){
......@@ -199,6 +244,10 @@ struct Db
}
};
template<typename T> struct remove_reference { typedef T type; };
template<typename T> struct remove_reference<T&> { typedef T type; };
template<typename T> struct remove_reference<const T&> { typedef T type; };
template<typename T>
T currentMessage() {
T value;
......@@ -220,3 +269,107 @@ struct Ratio {
static_assert( sizeof(Ratio) == 2*sizeof(uint64_t), "unexpected padding" );
template<int Primary, int Secondary>
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<uint64_t scope, uint64_t code, uint64_t table, typename Record>
struct Table {
private:
typedef typename remove_reference<decltype( ((Record*)(nullptr))->primary() )>::type Primary;
typedef typename remove_reference<decltype( ((Record*)(nullptr))->secondary() )>::type Secondary;
typedef table_impl<sizeof( Primary ), sizeof( Secondary )> 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<N(SCOPE),N(CODE),N(TABLE),TYPE>
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
......@@ -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 <contracts/currency.hpp> /// defines transfer struct
#include <currency/currency.hpp> /// 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<const uint128_t*>(this);
}
operator uint128_t& (){
return *reinterpret_cast<uint128_t*>(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<Transfer>();
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<Balance>( transfer.to );
if( to_balance.isEmpty() )
Db::remove<Account>( 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<Transfer>();
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<Balance>( transfer.to );
if( to_balance.isEmpty() )
Db::remove<Account>( 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<Buy>();
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<uint64_t>( 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<Sell>();
assert( sell.expiration > now(), "order expired" );
Account seller_account;
Db::get( sell.seller, seller_account );
assert( seller_account.b >= sell.quantity, "insufficient funds" );
assert( sell.quantity > 0, "invalid quantity" );
seller_account.b -= sell.quantity;
Order seller_ask;
assert( AsksById::get( OrderID{ sell.seller, sell.id}, seller_ask ), "order with this id already exists" );
seller_ask.price = sell.price;
seller_ask.id.owner = sell.seller;
seller_ask.id.id = sell.id;
seller_ask.quantity = sell.quantity;
seller_ask.expiration = sell.expiration;
Order highest_bid;
if( !BidsByPrice::back( highest_bid ) ) {
assert( !sell.fill_or_kill, "order not completely filled" );
Bids::store( seller_ask );
Db::store( sell.seller, seller_account );
return;
}
Account buyer_account;
Db::get( highest_bid.id.owner, buyer_account );
while( highest_bid.price >= seller_ask.price ) {
auto ask_usd = seller_ask.quantity;
auto bid_usd = seller_ask.price * seller_ask.quantity;
auto fill_amount_usd = min<uint64_t>( ask_usd, bid_usd );
uint64_t fill_amount_token = 0;
seller_ask.quantity -= fill_amount_token;
highest_bid.quantity -= fill_amount_usd;
if( fill_amount_usd == ask_usd ) { /// complete fill of ask
fill_amount_token = seller_ask.quantity;
} else { /// complete fill of buy
fill_amount_token = fill_amount_usd / seller_ask.price;
}
/// either fill_amount_token == seller.quantity or fill_amount_usd == buy.quantity
fill( highest_bid, seller_ask, buyer_account, seller_account, fill_amount_usd, fill_amount_token );
if( highest_bid.quantity == 0 ) {
Db::store( highest_bid.id.owner, buyer_account );
Asks::remove( highest_bid );
if( !BidsByPrice::back( highest_bid ) ) {
break;
}
Db::get( highest_bid.id.owner, buyer_account );
} else {
break; // buyer's bid should be filled
}
}
Db::store( sell.seller, seller_account );
if( seller_ask.quantity > 0 ) {
assert( !sell.fill_or_kill, "order not completely filled" );
Asks::store( seller_ask );
}
}
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 {
}
}
......
......@@ -57,8 +57,50 @@ namespace eos { namespace chain {
>
>;
struct key128x128_value_object : public chainbase::object<key128x128_value_object_type, key128x128_value_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<tag<by_id>, member<key128x128_value_object, key128x128_value_object::id_type, &key128x128_value_object::id>>,
ordered_unique<tag<by_scope_primary>,
composite_key< key128x128_value_object,
member<key128x128_value_object, AccountName, &key128x128_value_object::scope>,
member<key128x128_value_object, AccountName, &key128x128_value_object::code>,
member<key128x128_value_object, AccountName, &key128x128_value_object::table>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::primary_key>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::secondary_key>
>,
composite_key_compare< std::less<AccountName>,std::less<AccountName>,std::less<AccountName>,std::less<uint128_t>,std::less<uint128_t> >
>,
ordered_unique<tag<by_scope_secondary>,
composite_key< key128x128_value_object,
member<key128x128_value_object, AccountName, &key128x128_value_object::scope>,
member<key128x128_value_object, AccountName, &key128x128_value_object::code>,
member<key128x128_value_object, AccountName, &key128x128_value_object::table>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::secondary_key>,
member<key128x128_value_object, uint128_t, &key128x128_value_object::primary_key>
>,
composite_key_compare< std::less<AccountName>,std::less<AccountName>,std::less<AccountName>,std::less<uint128_t>, std::less<uint128_t> >
>
>
>;
} } // 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) )
......@@ -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<bool> used_authorizations;
};
......
......@@ -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<AccountName, config::BlocksPerRound>;
using RoundChanges = std::map<AccountName, AccountName>;
......@@ -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)
......
......@@ -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<key128x128_value_index,by_scope_primary>();
auto itr = idx.lower_bound( boost::make_tuple( AccountName(scope),
AccountName(code),
AccountName(table),
*primary, uint128_t(0) ) );
if( itr == idx.end() ||
itr->scope != scope ||
itr->code != code ||
itr->table != table ||
itr->primary_key != *primary ) return -1;
*secondary = itr->secondary_key;
auto copylen = std::min<size_t>(itr->value.size(),valuelen);
if( copylen ) {
itr->value.copy(value, copylen);
}
return copylen;
}
int32_t message_validate_context::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<key128x128_value_index,by_scope_primary>();
auto itr = idx.lower_bound( boost::make_tuple( AccountName(scope),
AccountName(code),
AccountName(table),
*primary, uint128_t(0) ) );
if( itr == idx.end() ||
itr->scope != scope ||
itr->code != code ||
itr->table != table ) return -1;
*primary = itr->primary_key;
*secondary = itr->secondary_key;
auto copylen = std::min<size_t>(itr->value.size(),valuelen);
if( copylen ) {
itr->value.copy(value, copylen);
}
return copylen;
}
int32_t message_validate_context::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<key128x128_value_index,by_scope_secondary>();
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<size_t>(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 );
......
......@@ -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;
......
......@@ -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<ExTransaction*> 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<int> thread_count(1024);
vector<ExTransaction*> postponed;
postponed.reserve(transactions.size());
auto current = transactions;
vector<bool> 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;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册