提交 0f6695cb 编写于 作者: B Bart Wyatt

Merge branch 'develop'

......@@ -96,7 +96,7 @@ steps:
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -LE long_running_tests --output-on-failure
ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -j8 -LE _tests --output-on-failure
retry:
automatic:
limit: 1
......@@ -108,6 +108,26 @@ steps:
- "build/genesis.json"
- "build/config.ini"
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":darwin: Build" && \
tar -zxf build.tar.gz && \
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
ln -s "$(pwd)" /data/job && cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure
retry:
automatic:
limit: 1
label: ":darwin: NP Tests"
agents:
- "role=macos-tester"
artifact_paths:
- "mongod.log"
- "build/genesis.json"
- "build/config.ini"
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
......@@ -116,7 +136,7 @@ steps:
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -LE long_running_tests --output-on-failure
cd /data/job/build && ctest -j8 -LE _tests --output-on-failure
retry:
automatic:
limit: 1
......@@ -132,6 +152,30 @@ steps:
image: "eosio/ci:ubuntu"
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: Build" && \
tar -zxf build.tar.gz && \
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure
retry:
automatic:
limit: 1
label: ":ubuntu: NP Tests"
agents:
- "role=linux-tester"
artifact_paths:
- "mongod.log"
- "build/genesis.json"
- "build/config.ini"
plugins:
docker#v1.4.0:
image: "eosio/ci:ubuntu"
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
......@@ -140,7 +184,7 @@ steps:
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -LE long_running_tests --output-on-failure
cd /data/job/build && ctest -j8 -LE _tests --output-on-failure
retry:
automatic:
limit: 1
......@@ -157,6 +201,30 @@ steps:
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":ubuntu: 18.04 Build" && \
tar -zxf build.tar.gz && \
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure
retry:
automatic:
limit: 1
label: ":ubuntu: 18.04 NP Tests"
agents:
- "role=linux-tester"
artifact_paths:
- "mongod.log"
- "build/genesis.json"
- "build/config.ini"
plugins:
docker#v1.4.0:
image: "eosio/ci:ubuntu18"
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":fedora: Build" && \
......@@ -164,7 +232,7 @@ steps:
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -LE long_running_tests --output-on-failure
cd /data/job/build && ctest -j8 -LE _tests --output-on-failure
retry:
automatic:
limit: 1
......@@ -181,6 +249,30 @@ steps:
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":fedora: Build" && \
tar -zxf build.tar.gz && \
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure
retry:
automatic:
limit: 1
label: ":fedora: NP Tests"
agents:
- "role=linux-tester"
artifact_paths:
- "mongod.log"
- "build/genesis.json"
- "build/config.ini"
plugins:
docker#v1.4.0:
image: "eosio/ci:fedora"
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":centos: Build" && \
......@@ -188,7 +280,7 @@ steps:
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -LE long_running_tests --output-on-failure
cd /data/job/build && ctest -j8 -LE _tests --output-on-failure
retry:
automatic:
limit: 1
......@@ -205,6 +297,30 @@ steps:
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":centos: Build" && \
tar -zxf build.tar.gz && \
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure
retry:
automatic:
limit: 1
label: ":centos: NP Tests"
agents:
- "role=linux-tester"
artifact_paths:
- "mongod.log"
- "build/genesis.json"
- "build/config.ini"
plugins:
docker#v1.4.0:
image: "eosio/ci:centos"
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":aws: Build" && \
......@@ -212,7 +328,7 @@ steps:
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -LE long_running_tests --output-on-failure
cd /data/job/build && ctest -j8 -LE _tests --output-on-failure
retry:
automatic:
limit: 1
......@@ -228,3 +344,27 @@ steps:
image: "eosio/ci:amazonlinux"
workdir: /data/job
timeout: 60
- command: |
echo "--- :arrow_down: Downloading build directory" && \
buildkite-agent artifact download "build.tar.gz" . --step ":aws: Build" && \
tar -zxf build.tar.gz && \
echo "--- :m: Starting MongoDB" && \
$(which mongod) --fork --logpath "$(pwd)"/mongod.log && \
echo "+++ :microscope: Running tests" && \
cd /data/job/build && ctest -L nonparallelizable_tests --output-on-failure
retry:
automatic:
limit: 1
label: ":aws: NP Tests"
agents:
- "role=linux-tester"
artifact_paths:
- "mongod.log"
- "build/genesis.json"
- "build/config.ini"
plugins:
docker#v1.4.0:
image: "eosio/ci:amazonlinux"
workdir: /data/job
timeout: 60
......@@ -27,3 +27,6 @@
[submodule "libraries/fc"]
path = libraries/fc
url = https://github.com/EOSIO/fc
[submodule "libraries/wabt"]
path = libraries/wabt
url = http://github.com/EOSIO/wabt
......@@ -26,8 +26,8 @@ set( CMAKE_CXX_EXTENSIONS ON )
set( CXX_STANDARD_REQUIRED ON)
set(VERSION_MAJOR 1)
set(VERSION_MINOR 2)
set(VERSION_PATCH 6)
set(VERSION_MINOR 3)
set(VERSION_PATCH 0)
set( CLI_CLIENT_EXECUTABLE_NAME cleos )
set( NODE_EXECUTABLE_NAME nodeos )
......@@ -74,8 +74,6 @@ if ("${OPENSSL_ROOT_DIR}" STREQUAL "")
endif()
endif()
find_package(Secp256k1 REQUIRED)
if(UNIX)
if(APPLE)
set(whole_archive_flag "-force_load")
......
......@@ -50,6 +50,7 @@ endif()
find_library(libbinaryen binaryen @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libwasm WASM @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libwast WAST @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libwabt wabt @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libir IR @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libplatform Platform @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(liblogging Logging @CMAKE_INSTALL_FULL_LIBDIR@)
......@@ -59,7 +60,13 @@ find_library(liboscrypto crypto @OPENSSL_ROOT_DIR@/lib)
find_library(libosssl ssl @OPENSSL_ROOT_DIR@/lib)
find_library(libchainbase chainbase @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libbuiltins builtins @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(libsecp256k1 secp256k1 @Secp256k1_ROOT_DIR@/lib)
find_library(libsecp256k1 secp256k1 @CMAKE_INSTALL_FULL_LIBDIR@)
find_library(GMP_LIBRARIES NAMES libgmp.a gmp.lib gmp libgmp-10 mpir
HINTS ENV GMP_LIB_DIR
ENV GMP_DIR
PATH_SUFFIXES lib
DOC "Path to the GMP library"
)
macro(add_eosio_test test_name)
add_executable( ${test_name} ${ARGN} )
......@@ -71,6 +78,7 @@ macro(add_eosio_test test_name)
${libbinaryen}
${libwast}
${libwasm}
${libwabt}
${libruntime}
${libplatform}
${libir}
......@@ -80,6 +88,7 @@ macro(add_eosio_test test_name)
${liblogging}
${libchainbase}
${libbuiltins}
${GMP_LIBRARIES}
${libsecp256k1}
LLVMX86Disassembler
......
......@@ -51,6 +51,7 @@ find_library(libbinaryen binaryen @CMAKE_BINARY_DIR@/externals/binaryen/lib)
find_library(libwasm WASM @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/WASM)
find_library(libwast WAST @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/WAST)
find_library(libir IR @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/IR)
find_library(libwabt wabt @CMAKE_BINARY_DIR@/libraries/wabt)
find_library(libplatform Platform @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/Platform)
find_library(liblogging Logging @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/Logging)
find_library(libruntime Runtime @CMAKE_BINARY_DIR@/libraries/wasm-jit/Source/Runtime)
......@@ -59,7 +60,13 @@ find_library(liboscrypto crypto @OPENSSL_ROOT_DIR@/lib)
find_library(libosssl ssl @OPENSSL_ROOT_DIR@/lib)
find_library(libchainbase chainbase @CMAKE_BINARY_DIR@/libraries/chainbase)
find_library(libbuiltins builtins @CMAKE_BINARY_DIR@/libraries/builtins)
find_library(libsecp256k1 secp256k1 @Secp256k1_ROOT_DIR@/lib)
find_library(libsecp256k1 secp256k1 @CMAKE_BINARY_DIR@/libraries/fc/secp256k1)
find_library(GMP_LIBRARIES NAMES libgmp.a gmp.lib gmp libgmp-10 mpir
HINTS ENV GMP_LIB_DIR
ENV GMP_DIR
PATH_SUFFIXES lib
DOC "Path to the GMP library"
)
macro(add_eosio_test test_name)
add_executable( ${test_name} ${ARGN} )
......@@ -71,6 +78,7 @@ macro(add_eosio_test test_name)
${libbinaryen}
${libwast}
${libwasm}
${libwabt}
${libruntime}
${libplatform}
${libir}
......@@ -80,6 +88,7 @@ macro(add_eosio_test test_name)
${liblogging}
${libchainbase}
${libbuiltins}
${GMP_LIBRARIES}
${libsecp256k1}
LLVMX86Disassembler
......
......@@ -5,7 +5,7 @@ ARG symbol=SYS
RUN git clone -b $branch https://github.com/EOSIO/eos.git --recursive \
&& cd eos && echo "$branch:$(git rev-parse HEAD)" > /etc/eosio-version \
&& cmake -H. -B"/tmp/build" -GNinja -DCMAKE_BUILD_TYPE=Release -DWASM_ROOT=/opt/wasm -DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=/tmp/build -DSecp256k1_ROOT_DIR=/usr/local -DBUILD_MONGO_DB_PLUGIN=true -DCORE_SYMBOL_NAME=$symbol \
-DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=/tmp/build -DBUILD_MONGO_DB_PLUGIN=true -DCORE_SYMBOL_NAME=$symbol \
&& cmake --build /tmp/build --target install && rm /tmp/build/bin/eosiocpp
......@@ -21,5 +21,4 @@ COPY --from=builder /eos/Docker/nodeosd.sh /opt/eosio/bin/nodeosd.sh
ENV EOSIO_ROOT=/opt/eosio
RUN chmod +x /opt/eosio/bin/nodeosd.sh
ENV LD_LIBRARY_PATH /usr/local/lib
VOLUME /opt/eosio/bin/data-dir
ENV PATH /opt/eosio/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
......@@ -20,10 +20,10 @@ cd eos/Docker
docker build . -t eosio/eos
```
The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v1.2.6 tag, you could do the following:
The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v1.3.0 tag, you could do the following:
```bash
docker build -t eosio/eos:v1.2.6 --build-arg branch=v1.2.6 .
docker build -t eosio/eos:v1.3.0 --build-arg branch=v1.3.0 .
```
By default, the symbol in eosio.system is set to SYS. You can override this using the symbol argument while building the docker image.
......
......@@ -50,13 +50,6 @@ RUN git clone --depth 1 --single-branch --branch release_40 https://github.com/l
&& cmake --build build --target install \
&& cd .. && rm -rf llvm
RUN git clone --depth 1 https://github.com/cryptonomex/secp256k1-zkp \
&& cd secp256k1-zkp \
&& ./autogen.sh \
&& ./configure --prefix=/usr/local \
&& make -j$(nproc) install \
&& cd .. && rm -rf secp256k1-zkp
RUN git clone --depth 1 -b releases/v3.3 https://github.com/mongodb/mongo-cxx-driver \
&& cd mongo-cxx-driver/build \
&& cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr/local .. \
......
......@@ -5,7 +5,7 @@ ARG symbol=SYS
RUN git clone -b $branch https://github.com/EOSIO/eos.git --recursive \
&& cd eos && echo "$branch:$(git rev-parse HEAD)" > /etc/eosio-version \
&& cmake -H. -B"/opt/eosio" -GNinja -DCMAKE_BUILD_TYPE=Release -DWASM_ROOT=/opt/wasm -DCMAKE_CXX_COMPILER=clang++ \
-DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=/opt/eosio -DSecp256k1_ROOT_DIR=/usr/local -DBUILD_MONGO_DB_PLUGIN=true -DCORE_SYMBOL_NAME=$symbol \
-DCMAKE_C_COMPILER=clang -DCMAKE_INSTALL_PREFIX=/opt/eosio -DBUILD_MONGO_DB_PLUGIN=true -DCORE_SYMBOL_NAME=$symbol \
&& cmake --build /opt/eosio --target install \
&& cp /eos/Docker/config.ini / && ln -s /opt/eosio/contracts /contracts && cp /eos/Docker/nodeosd.sh /opt/eosio/bin/nodeosd.sh && ln -s /eos/tutorials /tutorials
......@@ -14,5 +14,4 @@ RUN pip3 install numpy
ENV EOSIO_ROOT=/opt/eosio
RUN chmod +x /opt/eosio/bin/nodeosd.sh
ENV LD_LIBRARY_PATH /usr/local/lib
VOLUME /opt/eosio/bin/data-dir
ENV PATH /opt/eosio/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
#!/bin/sh
cd /opt/eosio/bin
if [ ! -d "/opt/eosio/bin/data-dir" ]; then
mkdir /opt/eosio/bin/data-dir
fi
if [ -f '/opt/eosio/bin/data-dir/config.ini' ]; then
echo
else
......
......@@ -15,7 +15,6 @@ add_subdirectory(multi_index_test)
add_subdirectory(eosio.system)
add_subdirectory(identity)
add_subdirectory(stltest)
add_subdirectory(exchange)
add_subdirectory(test.inline)
#add_subdirectory(bancor)
......
#include <exchange/exchange_state.hpp>
#include <eosio.system/exchange_state.hpp>
namespace eosiosystem {
asset exchange_state::convert_to_exchange( connector& c, asset in ) {
......
......@@ -189,7 +189,7 @@ extern "C" {
/**
* Add two long doubles split as two 64 bit unsigned integers and assign the value to the first parameter.
* @brief Add two long doubles (which are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param ret It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -201,7 +201,7 @@ extern "C" {
/**
* Subtract two long doubles split as two 64 bit unsigned integers and assign the value to the first parameter.
* @brief Subtract two long doubles (which are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param ret It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -213,7 +213,7 @@ extern "C" {
/**
* Multiply two long doubles split as two 64 bit unsigned integers and assign the value to the first parameter.
* @brief Multiply two long doubles (which are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param ret It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -225,7 +225,7 @@ extern "C" {
/**
* Divide two long doubles split as two 64 bit unsigned integers and assign the value to the first parameter.
* @brief Divide two long doubles (which are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param ret It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -237,7 +237,6 @@ extern "C" {
/**
* Check equality between two doubles split as two 64 bit unsigned integers
* @brief Check equality between two doubles (which are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -252,7 +251,6 @@ extern "C" {
/**
* Check inequality between two doubles split as two 64 bit unsigned integers
* @brief Check inequality between two doubles (which are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -268,7 +266,6 @@ extern "C" {
/**
* Check if the first double is greater or equal to the second double, the doubles are split as two 64 bit unsigned integers
* @brief Check if the first double is greater or equal to the second double, (the doubles are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -283,7 +280,6 @@ extern "C" {
/**
* Check if the first double is greater than the second double, the doubles are split as two 64 bit unsigned integers
* @brief Check if the first double is greater than the second double, (the doubles are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -298,7 +294,6 @@ extern "C" {
/**
* Check if the first double is less or equal to the second double, the doubles are split as two 64 bit unsigned integers
* @brief Check if the first double is less or equal to the second double, (the doubles are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -313,7 +308,6 @@ extern "C" {
/**
* Check if the first double is less than the second double, the doubles are split as two 64 bit unsigned integers
* @brief Check if the first double is less than the second double, (the doubles are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -328,7 +322,6 @@ extern "C" {
/**
* Compare two doubles which are split as two 64 bit unsigned integers
* @brief Compare two doubles (the doubles are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......@@ -343,7 +336,6 @@ extern "C" {
/**
* Check if either of the doubles is NaN, the doubles are split as two 64 bit unsigned integers
* @brief Check if either of the doubles is NaN, (the doubles are represented as two 64 bit unsigned integers)
* @param res It will be replaced with the result product.
* @param la Low 64 bits of the first 128 bit factor.
* @param ha High 64 bits of the first 128 bit factor.
* @param lb Low 64 bits of the second 128 bit factor.
......
......@@ -362,7 +362,7 @@ namespace eosio
* Assignment operator. Assign fixed_point32 to fixed_point64
*
* @brief Assignment operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return fixed_point64& - Reference to this object
*/
......@@ -372,7 +372,7 @@ namespace eosio
* Assignment operator. Assign fixed_point64 to fixed_point64
*
* @brief Assignment operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return fixed_point64& - Reference to this object
*/
......@@ -426,7 +426,7 @@ namespace eosio
* Equality operator
*
* @brief Equality operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return true - if equal
* @return false - otherwise
......@@ -437,7 +437,7 @@ namespace eosio
* Greater than operator
*
* @brief Greater than operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return true - if equal
* @return false - otherwise
......@@ -448,7 +448,7 @@ namespace eosio
* Less than operator
*
* @brief Less than operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return true - if equal
* @return false - otherwise
......@@ -506,7 +506,7 @@ namespace eosio
* Construct a new fixed point32 object from int32_t
*
* @brief Construct a new fixed point32 object
* @param v - int32_t representation of the fixed point value
* @param param - int32_t representation of the fixed point value
*/
fixed_point32(int32_t param=0) : val(param) {}
......@@ -553,7 +553,7 @@ namespace eosio
* Assignment operator. Assign fixed_point32 to fixed_point32
*
* @brief Assignment operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return fixed_point32& - Reference to this object
*/
......@@ -563,7 +563,7 @@ namespace eosio
* Assignment operator. Assign fixed_point64 to fixed_point32
*
* @brief Assignment operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return fixed_point32& - Reference to this object
*/
......@@ -615,7 +615,7 @@ namespace eosio
* Equality operator
*
* @brief Equality operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return true - if equal
* @return false - otherwise
......@@ -626,7 +626,7 @@ namespace eosio
* Greater than operator
*
* @brief Greater than operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return true - if equal
* @return false - otherwise
......@@ -637,7 +637,7 @@ namespace eosio
* Less than operator
*
* @brief Less than operator
* @tparam qr - Precision of the source
* @tparam QR - Precision of the source
* @param r - Source
* @return true - if equal
* @return false - otherwise
......
......@@ -13,7 +13,7 @@ extern "C" {
*
*
* Deferred transactions will not be processed until a future block. They
* can therefore have no effect on the success of failure of their parent
* can therefore have no effect on the success or failure of their parent
* transaction so long as they appear well formed. If any other condition
* causes the parent transaction to be marked as failing, then the deferred
* transaction will never be processed.
......@@ -27,7 +27,7 @@ extern "C" {
* ends such that the success or failure of the parent transaction is
* dependent on the success of the message. If an inline message fails in
* processing then the whole tree of transactions and actions rooted in the
* block will me marked as failing and none of effects on the database will
* block will be marked as failing and none of effects on the database will
* persist.
*
* Inline actions and Deferred transactions must adhere to the permissions
......@@ -68,7 +68,7 @@ extern "C" {
* @return 1 if transaction was canceled, 0 if transaction was not found
*
* Example:
*
*
* @code
* id = 0xffffffffffffffff
* cancel_deferred( id );
......
file(GLOB ABI_FILES "*.abi")
add_wast_executable(TARGET exchange
INCLUDE_FOLDERS "${STANDARD_INCLUDE_FOLDERS}"
LIBRARIES libc++ libc eosiolib
DESTINATION_FOLDER ${CMAKE_CURRENT_BINARY_DIR}
)
configure_file("${ABI_FILES}" "${CMAKE_CURRENT_BINARY_DIR}" COPYONLY)
add_executable(test_exchange test_exchange.cpp )
#bfp/lib/pack.c bfp/lib/posit.cpp bfp/lib/util.c bfp/lib/op2.c)
target_link_libraries( test_exchange fc )
target_include_directories( test_exchange PUBLIC fixed_point/include )
# Pegged Derivative Currency Design
A currency is designed to be a fungible and non-callable asset. A pegged Derivative currency, such as BitUSD, is backed by a cryptocurrency held as collateral. The "issuer" is "short" the dollar and extra-long the cryptocurrency. The buyer is simply long the dollar.
Background
----------
BitShares created the first working pegged asset system by allowing anyone to take out a short position by posting collateral and issuing BitUSD at a minimum 1.5:1 collateral:debt ratio. The **least collateralized position** was forced to provide liquidity for BitUSD holders
any time the market price fell more than a couple percent below the dollar (if the BitUSD holder opted to use forced liquidation).
To prevent abuse of the price feed, all forced liquidation was delayed.
In the event of a "black swan" all shorts have their positions liquidated at the price feed and all holders of BitUSD are only promised a fixed redemption rate.
There are several problems with this design:
1. There is very **poor liquidity** in the BitUSD / BitShares market creating **large spreads**
2. The shorts take all the risk and only profit when the price of BitShares rises
3. Blackswans are perpetual and very disruptive.
4. It is "every short for themselves"
5. Due to the risk/reward ratio the supply can be limited
6. The **collateral requirements** limit opportunity for leverage.
New Approach
------------
We present a new approach to pegged assets where the short-positions cooperate to provide the
service of a pegged asset with **high liquidity**. They make money by encouraging people to trade
their pegged asset and earning income **from the trading fees rather than seeking heavy leverage**
in a speculative market. They also generate money by earning interest on personal short positions.
The Setup Process
-----------------
An initial user deposits a Collateral Currency (C) into an smart contract and provides the initial
price feed. A new Debt token (D) is issued based upon the price feed and a 1.5:1 C:D ratio and the
issued tokens are deposited into the **Bancor market maker**. At this point in time there is 0 leverage by
the market maker because no D have been sold. The initial user is also issued exchange tokens (E) in the
market maker.
At this point people can buy E or D and the Bancor algorithm will provide liquidity between C, E, and D. Due to
the fees charged by the the market maker the value of E will increase in terms of C.
> Collateral currency = Smart Token/reserve of parent currency
>
> Issued tokens = Bounty Tokens (distributed to early holders / community supporters)
>
> Collateral Ratio (C:D) = reciprocal of Loan-to-Value Ratio (LTV)
Maintaining the Peg
-------------------
To maximize the utility of the D token, the market maker needs to maintain a **narrow trading range** of D vs the Dollar.
The more **consistant and reliable this trading range** is, the more people (arbitrageur) will be willing to hold and trade D. There are several
situations that can occur:
1. D is trading above a dollar +5%
a. Maker is fully collateralized `C:D>1.5`
- issue new D and deposit into maker such that collateral ratio is 1.5:1
b. Maker is not fully collateralized `C:D<1.5`
- adjust the maker weights to lower the redemption prices (defending capital of maker), arbitrageur will probably prevent this reality.
> Marker Weights = Connector Weights (in Bancor)
>
> Redemption Price: The price at which a bond may be repurchased by the issuer before maturity
2. D is selling for less than a dollar -5%
a. Maker is fully collateralized `C:D>1.5`
- adjust the maker weights to increase redemption prices
b. Maker is under collateralized `C:D<1.5`
```
- stop E -> C and E -> D trades.
- offer bonus on C->E and D->E trades.
- on D->E conversions take received D out of circulation rather than add to the market maker
- on C<->D conversion continue as normal
- stop attempting adjusting maker ratio to defend the price feed and let the price rise until above +1%
```
Value of E = C - D where D == all in circulation, so E->C conversions should always assume all outstanding D was **settled at current maker price**. The result of such a conversion will **raise the collateral ratio**, unless they are forced to buy and retire some D at the current ratio. The algorithm must ensure the individual selling E doesn't leave those holding E worse-off from a D/E perspective (doesnot reduce D to a large extent). An individual buying E will create new D to maintain the same D/E ratio.
This implies that when value of all outstanding D is greater than all C that E cannot be sold until the network
generates **enough in trading fees** to recaptialize the market. This is like a company with more debt than equity not allowing buybacks. In fact, **E should not be sellable any time the collateral ratio falls below 1.5:1**.
BitShares is typical **margin call** territory, but holders of E have a chance at future liquidity if the situation improves. While E is not sellable,
E can be purchased at a 10% discount to its theoretical value, this will dilute existing holders of E but will raise capital and hopefully move E holders closer to eventual liquidity.
Adjusting Bancor Ratios by Price Feed
-------------------------------------
The price feed informs the algorithm of significant deviations between the Bancor effective price and the target peg. The price feed is necessarily a lagging indicator and may also factor in natural spreads between different exchanges. Therefore, the price feed shall have no impact unless there is a significant deviation (5%). When such a deviation occurs, the ratio is automatically adjusted to 4%.
In other words, the price feed keeps the maker in the "channel" but does not attempt to set the real-time prices. If there is a sudden change and the price feed differs from maker by 50% then after the adjustment it will still differ by 4%.
> Effective Price = Connected Tokens exchanged / Smart Tokens exchanged
Summary
-------
Under this model holders of E are short the dollar and make money to recollateralize their positions via market activity.
Anyone selling E must **realize the losses as a result of being short**.
Anyone buying E can get in to take their place at the current collateral ratio.
The value of E is equal to the value of a **margin postion**.
Anyone can buy E for a combination C and D equal to the current collateral ratio.
Anyone may sell E for a personal margin position with equal ratio of C and D.
Anyone may buy E with a personal margin position.
If they only have C, then they must use some of C to buy D first (which will move the price).
If they only have D, then they must use some of D to buy C first (which will also move the price).
Anyone can buy and sell E based upon Bancor balances of C and (all D), they must sell their E for a combination of D and C at current ratio, then sell the C or D for the other.
Anytime collateral level falls below 1.5 selling E is blocked and buying of E is given a 10% bonus.
Anyone can convert D<->C using Bancor maker configured to maintain price within +/- 5% of the price feed.
{
"version": "eosio::abi/1.0",
"types": [{
"new_type_name": "account_name",
"type": "name"
}
],
"structs": [
{
"name": "extended_symbol",
"base": "",
"fields": [
{"name":"sym", "type":"symbol"},
{"name":"contract", "type":"account_name"}
]
},
{
"name": "extended_asset",
"base": "",
"fields": [
{"name":"quantity", "type":"asset"},
{"name":"contract", "type":"account_name"}
]
},
{
"name": "upmargin",
"base": "",
"fields": [
{"name":"borrower", "type":"account_name"},
{"name":"market", "type":"symbol"},
{"name":"delta_borrow", "type":"extended_asset"},
{"name":"delta_collateral", "type":"extended_asset"}
]
},
{
"name": "covermargin",
"base": "",
"fields": [
{"name":"borrower", "type":"account_name"},
{"name":"market", "type":"symbol"},
{"name":"cover_amount", "type":"extended_asset"}
]
},
{
"name": "lend",
"base": "",
"fields": [
{"name":"lender", "type":"account_name"},
{"name":"market", "type":"symbol"},
{"name":"quantity", "type":"extended_asset"}
]
},
{
"name": "unlend",
"base": "",
"fields": [
{"name":"lender", "type":"account_name"},
{"name":"market", "type":"symbol"},
{"name":"interest_shares", "type":"float64"},
{"name":"interest_symbol", "type":"extended_symbol"}
]
},
{
"name": "trade",
"base": "",
"fields": [
{"name":"seller", "type":"account_name"},
{"name":"market", "type":"symbol"},
{"name":"sell", "type":"extended_asset"},
{"name":"min_receive", "type":"extended_asset"},
{"name":"expire", "type":"uint32"},
{"name":"fill_or_kill", "type":"uint8"}
]
},
{
"name": "createx",
"base": "",
"fields": [
{"name":"creator", "type":"account_name"},
{"name":"initial_supply", "type":"asset"},
{"name":"fee", "type":"uint32"},
{"name":"base_deposit", "type":"extended_asset"},
{"name":"quote_deposit", "type":"extended_asset"}
]
},
{
"name": "transfer",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"to", "type":"account_name"},
{"name":"quantity", "type":"asset"},
{"name":"memo", "type":"string"}
]
},
{
"name": "deposit",
"base": "",
"fields": [
{"name":"from", "type":"account_name"},
{"name":"quantity", "type":"extended_asset"}
]
},
{
"name": "create",
"base": "",
"fields": [
{"name":"issuer", "type":"account_name"},
{"name":"maximum_supply", "type":"asset"},
{"name":"can_freeze", "type":"uint8"},
{"name":"can_recall", "type":"uint8"},
{"name":"can_whitelist", "type":"uint8"}
]
},{
"name": "issue",
"base": "",
"fields": [
{"name":"to", "type":"account_name"},
{"name":"quantity", "type":"asset"},
{"name":"memo", "type":"string"}
]
},{
"name": "account",
"base": "",
"fields": [
{"name":"currency", "type":"uint64"},
{"name":"balance", "type":"uint64"}
]
},{
"name": "currency_stats",
"base": "",
"fields": [
{"name":"currency", "type":"uint64"},
{"name":"supply", "type":"uint64"}
]
}
],
"actions": [
{ "name": "deposit", "type": "deposit", "ricardian_contract": "" },
{ "name": "transfer", "type": "transfer", "ricardian_contract": "" },
{ "name": "trade", "type": "trade", "ricardian_contract": "" },
{ "name": "createx", "type": "createx", "ricardian_contract": "" },
{ "name": "issue", "type": "issue", "ricardian_contract": "" },
{ "name": "lend", "type": "lend", "ricardian_contract": "" },
{ "name": "unlend", "type": "unlend", "ricardian_contract": "" },
{ "name": "upmargin", "type": "upmargin", "ricardian_contract": "" },
{ "name": "covermargin", "type": "covermargin", "ricardian_contract": "" },
{ "name": "create", "type": "create", "ricardian_contract": "" }
],
"tables": [{
"name": "account",
"type": "account",
"index_type": "i64",
"key_names" : ["currency"],
"key_types" : ["uint64"]
},{
"name": "stat",
"type": "currency_stats",
"index_type": "i64",
"key_names" : ["currency"],
"key_types" : ["uint64"]
}
],
"ricardian_clauses": [],
"abi_extensions": []
}
#include <math.h>
#include "exchange.hpp"
#include "exchange_state.cpp"
#include "exchange_accounts.cpp"
#include "market_state.cpp"
#include <eosiolib/dispatcher.hpp>
namespace eosio {
void exchange::deposit( account_name from, extended_asset quantity ) {
eosio_assert( quantity.is_valid(), "invalid quantity" );
currency::inline_transfer( from, _this_contract, quantity, "deposit" );
_accounts.adjust_balance( from, quantity, "deposit" );
}
void exchange::withdraw( account_name from, extended_asset quantity ) {
require_auth( from );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount >= 0, "cannot withdraw negative balance" ); // Redundant? inline_transfer will fail if quantity is not positive.
_accounts.adjust_balance( from, -quantity );
currency::inline_transfer( _this_contract, from, quantity, "withdraw" );
}
void exchange::on( const trade& t ) {
require_auth( t.seller );
eosio_assert( t.sell.is_valid(), "invalid sell amount" );
eosio_assert( t.sell.amount > 0, "sell amount must be positive" );
eosio_assert( t.min_receive.is_valid(), "invalid min receive amount" );
eosio_assert( t.min_receive.amount >= 0, "min receive amount cannot be negative" );
auto receive_symbol = t.min_receive.get_extended_symbol();
eosio_assert( t.sell.get_extended_symbol() != receive_symbol, "invalid conversion" );
market_state market( _this_contract, t.market, _accounts );
auto temp = market.exstate;
auto output = temp.convert( t.sell, receive_symbol );
while( temp.requires_margin_call() ) {
market.margin_call( receive_symbol );
temp = market.exstate;
output = temp.convert( t.sell, receive_symbol );
}
market.exstate = temp;
print( name{t.seller}, " ", t.sell, " => ", output, "\n" );
if( t.min_receive.amount != 0 ) {
eosio_assert( t.min_receive.amount <= output.amount, "unable to fill" );
}
_accounts.adjust_balance( t.seller, -t.sell, "sold" );
_accounts.adjust_balance( t.seller, output, "received" );
if( market.exstate.supply.amount != market.initial_state().supply.amount ) {
auto delta = market.exstate.supply - market.initial_state().supply;
_excurrencies.issue_currency( { .to = _this_contract,
.quantity = delta,
.memo = string("") } );
}
/// TODO: if pending order start deferred trx to fill it
market.save();
}
/**
* This action shall fail if it would result in a margin call
*/
void exchange::on( const upmargin& b ) {
require_auth( b.borrower );
eosio_assert( b.delta_borrow.is_valid(), "invalid borrow delta" );
eosio_assert( b.delta_collateral.is_valid(), "invalid collateral delta" );
market_state market( _this_contract, b.market, _accounts );
eosio_assert( b.delta_borrow.amount != 0 || b.delta_collateral.amount != 0, "no effect" );
eosio_assert( b.delta_borrow.get_extended_symbol() != b.delta_collateral.get_extended_symbol(), "invalid args" );
eosio_assert( market.exstate.base.balance.get_extended_symbol() == b.delta_borrow.get_extended_symbol() ||
market.exstate.quote.balance.get_extended_symbol() == b.delta_borrow.get_extended_symbol(),
"invalid asset for market" );
eosio_assert( market.exstate.base.balance.get_extended_symbol() == b.delta_collateral.get_extended_symbol() ||
market.exstate.quote.balance.get_extended_symbol() == b.delta_collateral.get_extended_symbol(),
"invalid asset for market" );
market.update_margin( b.borrower, b.delta_borrow, b.delta_collateral );
/// if this succeeds then the borrower will see their balances adjusted accordingly,
/// if they don't have sufficient balance to either fund the collateral or pay off the
/// debt then this will fail before we go further.
_accounts.adjust_balance( b.borrower, b.delta_borrow, "borrowed" );
_accounts.adjust_balance( b.borrower, -b.delta_collateral, "collateral" );
market.save();
}
void exchange::on( const covermargin& c ) {
require_auth( c.borrower );
eosio_assert( c.cover_amount.is_valid(), "invalid cover amount" );
eosio_assert( c.cover_amount.amount > 0, "cover amount must be positive" );
market_state market( _this_contract, c.market, _accounts );
market.cover_margin( c.borrower, c.cover_amount);
market.save();
}
void exchange::createx( account_name creator,
asset initial_supply,
uint32_t /* fee */,
extended_asset base_deposit,
extended_asset quote_deposit
) {
require_auth( creator );
eosio_assert( initial_supply.is_valid(), "invalid initial supply" );
eosio_assert( initial_supply.amount > 0, "initial supply must be positive" );
eosio_assert( base_deposit.is_valid(), "invalid base deposit" );
eosio_assert( base_deposit.amount > 0, "base deposit must be positive" );
eosio_assert( quote_deposit.is_valid(), "invalid quote deposit" );
eosio_assert( quote_deposit.amount > 0, "quote deposit must be positive" );
eosio_assert( base_deposit.get_extended_symbol() != quote_deposit.get_extended_symbol(),
"must exchange between two different currencies" );
print( "base: ", base_deposit.get_extended_symbol() );
print( "quote: ",quote_deposit.get_extended_symbol() );
auto exchange_symbol = initial_supply.symbol.name();
print( "marketid: ", exchange_symbol, " \n " );
markets exstates( _this_contract, exchange_symbol );
auto existing = exstates.find( exchange_symbol );
eosio_assert( existing == exstates.end(), "market already exists" );
exstates.emplace( creator, [&]( auto& s ) {
s.manager = creator;
s.supply = extended_asset(initial_supply, _this_contract);
s.base.balance = base_deposit;
s.quote.balance = quote_deposit;
s.base.peer_margin.total_lent.symbol = base_deposit.symbol;
s.base.peer_margin.total_lent.contract = base_deposit.contract;
s.base.peer_margin.total_lendable.symbol = base_deposit.symbol;
s.base.peer_margin.total_lendable.contract = base_deposit.contract;
s.quote.peer_margin.total_lent.symbol = quote_deposit.symbol;
s.quote.peer_margin.total_lent.contract = quote_deposit.contract;
s.quote.peer_margin.total_lendable.symbol = quote_deposit.symbol;
s.quote.peer_margin.total_lendable.contract = quote_deposit.contract;
});
_excurrencies.create_currency( { .issuer = _this_contract,
// TODO: After currency contract respects maximum supply limits, the maximum supply here needs to be set appropriately.
.maximum_supply = asset( 0, initial_supply.symbol ),
.issuer_can_freeze = false,
.issuer_can_whitelist = false,
.issuer_can_recall = false } );
_excurrencies.issue_currency( { .to = _this_contract,
.quantity = initial_supply,
.memo = string("initial exchange tokens") } );
_accounts.adjust_balance( creator, extended_asset( initial_supply, _this_contract ), "new exchange issue" );
_accounts.adjust_balance( creator, -base_deposit, "new exchange deposit" );
_accounts.adjust_balance( creator, -quote_deposit, "new exchange deposit" );
}
void exchange::lend( account_name lender, symbol_type market, extended_asset quantity ) {
require_auth( lender );
eosio_assert( quantity.is_valid(), "invalid quantity" );
eosio_assert( quantity.amount > 0, "must lend a positive amount" );
market_state m( _this_contract, market, _accounts );
m.lend( lender, quantity );
m.save();
}
void exchange::unlend( account_name lender, symbol_type market, double interest_shares, extended_symbol interest_symbol ) {
require_auth( lender );
eosio_assert( interest_shares > 0, "must unlend a positive amount" );
market_state m( _this_contract, market, _accounts );
m.unlend( lender, interest_shares, interest_symbol );
m.save();
}
void exchange::on( const currency::transfer& t, account_name code ) {
if( code == _this_contract )
_excurrencies.on( t );
if( t.to == _this_contract ) {
auto a = extended_asset(t.quantity, code);
eosio_assert( a.is_valid(), "invalid quantity in transfer" );
eosio_assert( a.amount != 0, "zero quantity is disallowed in transfer");
eosio_assert( a.amount > 0 || t.memo == "withdraw", "withdrew tokens without withdraw in memo");
eosio_assert( a.amount < 0 || t.memo == "deposit", "received tokens without deposit in memo" );
_accounts.adjust_balance( t.from, a, t.memo );
}
}
#define N(X) ::eosio::string_to_name(#X)
void exchange::apply( account_name contract, account_name act ) {
if( act == N(transfer) ) {
on( unpack_action_data<currency::transfer>(), contract );
return;
}
if( contract != _this_contract )
return;
auto& thiscontract = *this;
switch( act ) {
EOSIO_API( exchange, (createx)(deposit)(withdraw)(lend)(unlend) )
};
switch( act ) {
case N(trade):
on( unpack_action_data<trade>() );
return;
case N(upmargin):
on( unpack_action_data<upmargin>() );
return;
case N(covermargin):
on( unpack_action_data<covermargin>() );
return;
default:
_excurrencies.apply( contract, act );
return;
}
}
} /// namespace eosio
extern "C" {
[[noreturn]] void apply( uint64_t receiver, uint64_t code, uint64_t action ) {
eosio::exchange ex( receiver );
ex.apply( code, action );
eosio_exit(0);
}
}
#include <eosiolib/types.hpp>
#include <eosiolib/currency.hpp>
#include <boost/container/flat_map.hpp>
#include <cmath>
#include <exchange/market_state.hpp>
namespace eosio {
/**
* This contract enables users to create an exchange between any pair of
* standard currency types. A new exchange is created by funding it with
* an equal value of both sides of the order book and giving the issuer
* the initial shares in that orderbook.
*
* To prevent excessive rounding errors, the initial deposit should include
* a sizeable quantity of both the base and quote currencies and the exchange
* shares should have a quantity 100x the quantity of the largest initial
* deposit.
*
* Users must deposit funds into the exchange before they can trade on the
* exchange.
*
* Each time an exchange is created a new currency for that exchanges market
* maker is also created. This currencies supply and symbol must be unique and
* it uses the currency contract's tables to manage it.
*/
class exchange {
private:
account_name _this_contract;
currency _excurrencies;
exchange_accounts _accounts;
public:
exchange( account_name self )
:_this_contract(self),
_excurrencies(self),
_accounts(self)
{}
void createx( account_name creator,
asset initial_supply,
uint32_t fee,
extended_asset base_deposit,
extended_asset quote_deposit
);
void deposit( account_name from, extended_asset quantity );
void withdraw( account_name from, extended_asset quantity );
void lend( account_name lender, symbol_type market, extended_asset quantity );
void unlend(
account_name lender,
symbol_type market,
double interest_shares,
extended_symbol interest_symbol
);
struct covermargin {
account_name borrower;
symbol_type market;
extended_asset cover_amount;
};
struct upmargin {
account_name borrower;
symbol_type market;
extended_asset delta_borrow;
extended_asset delta_collateral;
};
struct trade {
account_name seller;
symbol_type market;
extended_asset sell;
extended_asset min_receive;
uint32_t expire = 0;
uint8_t fill_or_kill = true;
};
void on( const trade& t );
void on( const upmargin& b );
void on( const covermargin& b );
void on( const currency::transfer& t, account_name code );
void apply( account_name contract, account_name act );
};
} // namespace eosio
#include <exchange/exchange_accounts.hpp>
namespace eosio {
void exchange_accounts::adjust_balance( account_name owner, extended_asset delta, const string& reason ) {
(void)reason;
auto table = exaccounts_cache.find( owner );
if( table == exaccounts_cache.end() ) {
table = exaccounts_cache.emplace( owner, exaccounts(_this_contract, owner ) ).first;
}
auto useraccounts = table->second.find( owner );
if( useraccounts == table->second.end() ) {
table->second.emplace( owner, [&]( auto& exa ){
exa.owner = owner;
exa.balances[delta.get_extended_symbol()] = delta.amount;
eosio_assert( delta.amount >= 0, "overdrawn balance 1" );
});
} else {
table->second.modify( useraccounts, 0, [&]( auto& exa ) {
const auto& b = exa.balances[delta.get_extended_symbol()] += delta.amount;
eosio_assert( b >= 0, "overdrawn balance 2" );
});
}
}
} /// namespace eosio
#pragma once
#include <eosiolib/asset.hpp>
#include <eosiolib/multi_index.hpp>
namespace eosio {
using boost::container::flat_map;
/**
* Each user has their own account with the exchange contract that keeps track
* of how much a user has on deposit for each extended asset type. The assumption
* is that storing a single flat map of all balances for a particular user will
* be more practical than breaking this down into a multi-index table sorted by
* the extended_symbol.
*/
struct exaccount {
account_name owner;
flat_map<extended_symbol, int64_t> balances;
uint64_t primary_key() const { return owner; }
EOSLIB_SERIALIZE( exaccount, (owner)(balances) )
};
typedef eosio::multi_index<N(exaccounts), exaccount> exaccounts;
/**
* Provides an abstracted interface around storing balances for users. This class
* caches tables to make multiple accesses effecient.
*/
struct exchange_accounts {
exchange_accounts( account_name code ):_this_contract(code){}
void adjust_balance( account_name owner, extended_asset delta, const string& reason = string() );
private:
account_name _this_contract;
/**
* Keep a cache of all accounts tables we access
*/
flat_map<account_name, exaccounts> exaccounts_cache;
};
} /// namespace eosio
#include <exchange/exchange_state.hpp>
namespace eosio {
extended_asset exchange_state::convert_to_exchange( connector& c, extended_asset in ) {
real_type R(supply.amount);
real_type C(c.balance.amount+in.amount);
real_type F(c.weight/1000.0);
real_type T(in.amount);
real_type ONE(1.0);
real_type E = -R * (ONE - std::pow( ONE + T / C, F) );
int64_t issued = int64_t(E);
supply.amount += issued;
c.balance.amount += in.amount;
return extended_asset( issued, supply.get_extended_symbol() );
}
extended_asset exchange_state::convert_from_exchange( connector& c, extended_asset in ) {
eosio_assert( in.contract == supply.contract, "unexpected asset contract input" );
eosio_assert( in.symbol== supply.symbol, "unexpected asset symbol input" );
real_type R(supply.amount - in.amount);
real_type C(c.balance.amount);
real_type F(1000.0/c.weight);
real_type E(in.amount);
real_type ONE(1.0);
real_type T = C * (std::pow( ONE + E/R, F) - ONE);
int64_t out = int64_t(T);
supply.amount -= in.amount;
c.balance.amount -= out;
return extended_asset( out, c.balance.get_extended_symbol() );
}
extended_asset exchange_state::convert( extended_asset from, extended_symbol to ) {
auto sell_symbol = from.get_extended_symbol();
auto ex_symbol = supply.get_extended_symbol();
auto base_symbol = base.balance.get_extended_symbol();
auto quote_symbol = quote.balance.get_extended_symbol();
if( sell_symbol != ex_symbol ) {
if( sell_symbol == base_symbol ) {
from = convert_to_exchange( base, from );
} else if( sell_symbol == quote_symbol ) {
from = convert_to_exchange( quote, from );
} else {
eosio_assert( false, "invalid sell" );
}
} else {
if( to == base_symbol ) {
from = convert_from_exchange( base, from );
} else if( to == quote_symbol ) {
from = convert_from_exchange( quote, from );
} else {
eosio_assert( false, "invalid conversion" );
}
}
if( to != from.get_extended_symbol() )
return convert( from, to );
return from;
}
bool exchange_state::requires_margin_call( const exchange_state::connector& con )const {
if( con.peer_margin.total_lent.amount > 0 ) {
auto tmp = *this;
auto base_total_col = int64_t(con.peer_margin.total_lent.amount * con.peer_margin.least_collateralized);
auto covered = tmp.convert( extended_asset( base_total_col, con.balance.get_extended_symbol()), con.peer_margin.total_lent.get_extended_symbol() );
if( covered.amount <= con.peer_margin.total_lent.amount )
return true;
}
return false;
}
bool exchange_state::requires_margin_call()const {
return requires_margin_call( base ) || requires_margin_call( quote );
}
} /// namespace eosio
#pragma once
#include <eosiolib/asset.hpp>
namespace eosio {
typedef double real_type;
struct margin_state {
extended_asset total_lendable;
extended_asset total_lent;
real_type least_collateralized = std::numeric_limits<double>::max();
/**
* Total shares allocated to those who have lent, when someone unlends they get
* total_lendable * user_interest_shares / interest_shares and total_lendable is reduced.
*
* When interest is paid, it shows up in total_lendable
*/
real_type interest_shares = 0;
real_type lend( int64_t new_lendable ) {
if( total_lendable.amount > 0 ) {
real_type new_shares = (interest_shares * new_lendable) / total_lendable.amount;
interest_shares += new_shares;
total_lendable.amount += new_lendable;
} else {
interest_shares += new_lendable;
total_lendable.amount += new_lendable;
}
return new_lendable;
}
extended_asset unlend( double ishares ) {
extended_asset result = total_lent;
print( "unlend: ", ishares, " existing interest_shares: ", interest_shares, "\n" );
result.amount = int64_t( (ishares * total_lendable.amount) / interest_shares );
total_lendable.amount -= result.amount;
interest_shares -= ishares;
eosio_assert( interest_shares >= 0, "underflow" );
eosio_assert( total_lendable.amount >= 0, "underflow" );
return result;
}
EOSLIB_SERIALIZE( margin_state, (total_lendable)(total_lent)(least_collateralized)(interest_shares) )
};
/**
* Uses Bancor math to create a 50/50 relay between two asset types. The state of the
* bancor exchange is entirely contained within this struct. There are no external
* side effects associated with using this API.
*/
struct exchange_state {
account_name manager;
extended_asset supply;
uint32_t fee = 0;
struct connector {
extended_asset balance;
uint32_t weight = 500;
margin_state peer_margin; /// peer_connector collateral lending balance
EOSLIB_SERIALIZE( connector, (balance)(weight)(peer_margin) )
};
connector base;
connector quote;
uint64_t primary_key()const { return supply.symbol.name(); }
extended_asset convert_to_exchange( connector& c, extended_asset in );
extended_asset convert_from_exchange( connector& c, extended_asset in );
extended_asset convert( extended_asset from, extended_symbol to );
bool requires_margin_call( const exchange_state::connector& con )const;
bool requires_margin_call()const;
EOSLIB_SERIALIZE( exchange_state, (manager)(supply)(fee)(base)(quote) )
};
typedef eosio::multi_index<N(markets), exchange_state> markets;
} /// namespace eosio
#include <exchange/market_state.hpp>
#include <boost/math/special_functions/relative_difference.hpp>
namespace eosio {
market_state::market_state( account_name this_contract, symbol_type market_symbol, exchange_accounts& acnts )
:marketid( market_symbol.name() ),
market_table( this_contract, marketid ),
base_margins( this_contract, (marketid<<4) + 1),
quote_margins( this_contract, (marketid<<4) + 2),
base_loans( this_contract, (marketid<<4) + 1),
quote_loans( this_contract, (marketid<<4) + 2),
_accounts(acnts),
market_state_itr( market_table.find(marketid) )
{
eosio_assert( market_state_itr != market_table.end(), "unknown market" );
exstate = *market_state_itr;
}
void market_state::margin_call( extended_symbol debt_type ) {
if( debt_type == exstate.base.balance.get_extended_symbol() )
margin_call( exstate.base, base_margins );
else
margin_call( exstate.quote, quote_margins );
}
void market_state::margin_call( exchange_state::connector& c, margins& marginstable ) {
auto price_idx = marginstable.get_index<N(callprice)>();
auto pos = price_idx.begin();
if( pos == price_idx.end() )
return;
auto receipt = exstate.convert( pos->collateral, pos->borrowed.get_extended_symbol() );
eosio_assert( receipt.amount >= pos->borrowed.amount, "programmer error: insufficient collateral to cover" );/// VERY BAD, SHOULD NOT HAPPEN
auto change_debt = receipt - pos->borrowed;
auto change_collat = exstate.convert( change_debt, pos->collateral.get_extended_symbol() );
_accounts.adjust_balance( pos->owner, change_collat );
c.peer_margin.total_lent.amount -= pos->borrowed.amount;
price_idx.erase(pos);
pos = price_idx.begin();
if( pos != price_idx.end() )
c.peer_margin.least_collateralized = pos->call_price;
else
c.peer_margin.least_collateralized = double(uint64_t(-1));
}
const exchange_state& market_state::initial_state()const {
return *market_state_itr;
}
void market_state::lend( account_name lender, const extended_asset& quantity ) {
auto sym = quantity.get_extended_symbol();
_accounts.adjust_balance( lender, -quantity );
if( sym == exstate.base.balance.get_extended_symbol() ) {
double new_shares = exstate.base.peer_margin.lend( quantity.amount );
adjust_lend_shares( lender, base_loans, new_shares );
}
else if( sym == exstate.quote.balance.get_extended_symbol() ) {
double new_shares = exstate.quote.peer_margin.lend( quantity.amount );
adjust_lend_shares( lender, quote_loans, new_shares );
}
else eosio_assert( false, "unable to lend to this market" );
}
void market_state::unlend( account_name lender, double ishares, const extended_symbol& sym ) {
eosio_assert( ishares > 0, "cannot unlend negative balance" );
adjust_lend_shares( lender, base_loans, -ishares );
print( "sym: ", sym );
if( sym == exstate.base.balance.get_extended_symbol() ) {
extended_asset unlent = exstate.base.peer_margin.unlend( ishares );
_accounts.adjust_balance( lender, unlent );
}
else if( sym == exstate.quote.balance.get_extended_symbol() ) {
extended_asset unlent = exstate.quote.peer_margin.unlend( ishares );
_accounts.adjust_balance( lender, unlent );
}
else eosio_assert( false, "unable to lend to this market" );
}
void market_state::adjust_lend_shares( account_name lender, loans& l, double delta ) {
auto existing = l.find( lender );
if( existing == l.end() ) {
l.emplace( lender, [&]( auto& obj ) {
obj.owner = lender;
obj.interest_shares = delta;
eosio_assert( delta >= 0, "underflow" );
});
} else {
l.modify( existing, 0, [&]( auto& obj ) {
obj.interest_shares += delta;
eosio_assert( obj.interest_shares >= 0, "underflow" );
});
}
}
void market_state::cover_margin( account_name borrower, const extended_asset& cover_amount ) {
if( cover_amount.get_extended_symbol() == exstate.base.balance.get_extended_symbol() ) {
cover_margin( borrower, base_margins, exstate.base, cover_amount );
} else if( cover_amount.get_extended_symbol() == exstate.quote.balance.get_extended_symbol() ) {
cover_margin( borrower, quote_margins, exstate.quote, cover_amount );
} else {
eosio_assert( false, "invalid debt asset" );
}
}
/**
* This method will use the collateral to buy the borrowed asset from the market
* with collateral to cancel the debt.
*/
void market_state::cover_margin( account_name borrower, margins& m, exchange_state::connector& c,
const extended_asset& cover_amount )
{
auto existing = m.find( borrower );
eosio_assert( existing != m.end(), "no known margin position" );
eosio_assert( existing->borrowed.amount >= cover_amount.amount, "attempt to cover more than user has" );
auto tmp = exstate;
auto estcol = tmp.convert( cover_amount, existing->collateral.get_extended_symbol() );
auto debpaid = exstate.convert( estcol, cover_amount.get_extended_symbol() );
eosio_assert( debpaid.amount >= cover_amount.amount, "unable to cover debt" );
auto refundcover = debpaid - cover_amount;
auto refundcol = exstate.convert( refundcover, existing->collateral.get_extended_symbol() );
estcol.amount -= refundcol.amount;
if( existing->borrowed.amount == cover_amount.amount ) {
auto freedcollateral = existing->collateral - estcol;
m.erase( existing );
existing = m.begin();
_accounts.adjust_balance( borrower, freedcollateral );
}
else {
m.modify( existing, 0, [&]( auto& obj ) {
obj.collateral.amount -= estcol.amount;
obj.borrowed.amount -= cover_amount.amount;
obj.call_price = double(obj.borrowed.amount) / obj.collateral.amount;
});
}
c.peer_margin.total_lent.amount -= cover_amount.amount;
if( existing != m.end() ) {
if( existing->call_price < c.peer_margin.least_collateralized )
c.peer_margin.least_collateralized = existing->call_price;
} else {
c.peer_margin.least_collateralized = std::numeric_limits<double>::max();
}
}
void market_state::update_margin( account_name borrower, const extended_asset& delta_debt, const extended_asset& delta_col )
{
if( delta_debt.get_extended_symbol() == exstate.base.balance.get_extended_symbol() ) {
adjust_margin( borrower, base_margins, exstate.base, delta_debt, delta_col );
} else if( delta_debt.get_extended_symbol() == exstate.quote.balance.get_extended_symbol() ) {
adjust_margin( borrower, quote_margins, exstate.quote, delta_debt, delta_col );
} else {
eosio_assert( false, "invalid debt asset" );
}
}
void market_state::adjust_margin( account_name borrower, margins& m, exchange_state::connector& c,
const extended_asset& delta_debt, const extended_asset& delta_col )
{
auto existing = m.find( borrower );
if( existing == m.end() ) {
eosio_assert( delta_debt.amount > 0, "cannot borrow neg" );
eosio_assert( delta_col.amount > 0, "cannot have neg collat" );
existing = m.emplace( borrower, [&]( auto& obj ) {
obj.owner = borrower;
obj.borrowed = delta_debt;
obj.collateral = delta_col;
obj.call_price = double(obj.borrowed.amount) / obj.collateral.amount;
});
} else {
if( existing->borrowed.amount == -delta_debt.amount ) {
eosio_assert( existing->collateral.amount == -delta_col.amount, "user failed to claim all collateral" );
m.erase( existing );
existing = m.begin();
} else {
m.modify( existing, 0, [&]( auto& obj ) {
obj.borrowed += delta_debt;
obj.collateral += delta_col;
obj.call_price = double(obj.borrowed.amount) / obj.collateral.amount;
});
}
}
c.peer_margin.total_lent += delta_debt;
eosio_assert( c.peer_margin.total_lent.amount <= c.peer_margin.total_lendable.amount, "insufficient funds availalbe to borrow" );
if( existing != m.end() ) {
if( existing->call_price < c.peer_margin.least_collateralized )
c.peer_margin.least_collateralized = existing->call_price;
eosio_assert( !exstate.requires_margin_call( c ), "this update would trigger a margin call" );
} else {
c.peer_margin.least_collateralized = std::numeric_limits<double>::max();
}
}
void market_state::save() {
market_table.modify( market_state_itr, 0, [&]( auto& s ) {
s = exstate;
});
}
}
#pragma once
#include <exchange/exchange_state.hpp>
#include <exchange/exchange_accounts.hpp>
namespace eosio {
/**
* We calculate a unique scope for each market/borrowed_symbol/collateral_symbol and then
* instantiate a table of margin positions... with in this table each user has exactly
* one position and therefore the owner can serve as the primary key.
*/
struct margin_position {
account_name owner;
extended_asset borrowed;
extended_asset collateral;
double call_price = 0;
uint64_t get_call()const { return uint64_t(1000000*call_price); }
uint64_t primary_key()const { return owner; }
EOSLIB_SERIALIZE( margin_position, (owner)(borrowed)(collateral)(call_price) )
};
typedef eosio::multi_index<N(margins), margin_position,
indexed_by<N(callprice), eosio::const_mem_fun<margin_position, uint64_t, &margin_position::get_call> >
> margins;
struct loan_position {
account_name owner; /// the owner
double interest_shares; /// the number of shares in the total lent pool
uint64_t primary_key()const { return owner; }
EOSLIB_SERIALIZE( loan_position, (owner)(interest_shares) )
};
typedef eosio::multi_index<N(loans), loan_position> loans;
/**
* Maintains a state along with the cache of margin positions and/or limit orders.
*/
struct market_state {
market_state( account_name this_contract, symbol_type market_symbol, exchange_accounts& acnts );
const exchange_state& initial_state()const;
void margin_call( extended_symbol debt_type );
void lend( account_name lender, const extended_asset& debt );
void unlend( account_name lender, double ishares, const extended_symbol& sym );
void update_margin( account_name borrower, const extended_asset& delta_debt,
const extended_asset& delta_collateral );
void cover_margin( account_name borrower, const extended_asset& cover_amount );
void save();
symbol_name marketid;
exchange_state exstate;
markets market_table;
margins base_margins;
margins quote_margins;
loans base_loans;
loans quote_loans;
private:
exchange_accounts& _accounts;
markets::const_iterator market_state_itr;
void cover_margin( account_name borrower, margins& m, exchange_state::connector& c,
const extended_asset& cover_amount );
void adjust_margin( account_name borrower, margins& m, exchange_state::connector& c,
const extended_asset& delta_debt, const extended_asset& delta_col );
void adjust_lend_shares( account_name lender, loans& l, double delta );
void margin_call( exchange_state::connector& c, margins& m );
};
} /// namespace eosio
#include <map>
#include <exception>
#include <string>
#include <cstdint>
#include <iostream>
#include <math.h>
#include <fc/exception/exception.hpp>
/*
#include <boost/multiprecision/cpp_dec_float.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/multiprecision/cpp_bin_float.hpp>
#include <boost/rational.hpp>
#include <sg14/fixed_point>
#include "fixed.hpp"
*/
#include <fc/time.hpp>
#include <fc/log/logger.hpp>
//#include "bfp/lib/posit.h"
using namespace std;
typedef long double real_type;
typedef double token_type;
/*
struct margin_position {
account_name owner;
uint64_t exchange_id;
asset lent;
asset collateral;
uint64_t open_time;
uint64_t primary_key()const{ return owner; }
uint256_t by_owner_ex_lent_collateral()const {
}
real_type by_call_price()const {
return collateral.amount / real_type(lent.amount);
}
};
*/
template<typename Real>
Real Abs(Real Nbr)
{
if( Nbr >= 0 )
return Nbr;
else
return -Nbr;
}
template<typename Real>
Real sqrt_safe( const Real Nbr)
{
return sqrt(Nbr);
// cout << " " << Nbr << "\n";;
Real Number = Nbr / Real(2.0);
const Real Tolerance = Real(double(1.0e-12));
//cout << "tol: " << Tolerance << "\n";
Real Sq;
Real Er;
do {
auto tmp = Nbr / Number;
tmp += Number;
tmp /= real_type(2.0);
if( Number == tmp ) break;
Number = tmp;
Sq = Number * Number;
Er = Abs(Sq - Nbr);
// wdump((Er.getDouble())(1.0e-8)(Tolerance.getDouble()));
// wdump(((Er - Tolerance).getDouble()));
}while( Er >= Tolerance );
return Number;
}
typedef __uint128_t uint128_t;
typedef string account_name;
typedef string symbol_type;
static const symbol_type exchange_symbol = "EXC";
struct asset {
token_type amount;
symbol_type symbol;
};
struct margin_key {
symbol_type lent;
symbol_type collat;
};
struct margin {
asset lent;
symbol_type collateral_symbol;
real_type least_collateralized_rate;
};
struct user_margin {
asset lent;
asset collateral;
real_type call_price()const {
return collateral.amount / real_type(lent.amount);
}
};
struct exchange_state;
struct connector {
asset balance;
real_type weight = 0.5;
token_type total_lent; /// lent from maker to users
token_type total_borrowed; /// borrowed from users to maker
token_type total_available_to_lend; /// amount available to borrow
token_type interest_pool; /// total interest earned but not claimed,
/// each user can claim user_lent
void borrow( exchange_state& ex, const asset& amount_to_borrow );
asset convert_to_exchange( exchange_state& ex, const asset& input );
asset convert_from_exchange( exchange_state& ex, const asset& input );
};
struct balance_key {
account_name owner;
symbol_type symbol;
friend bool operator < ( const balance_key& a, const balance_key& b ) {
return std::tie( a.owner, a.symbol ) < std::tie( b.owner, b.symbol );
}
friend bool operator == ( const balance_key& a, const balance_key& b ) {
return std::tie( a.owner, a.symbol ) == std::tie( b.owner, b.symbol );
}
};
real_type fee = 1;//.9995;
int64_t maxtrade = 20000ll;
struct exchange_state {
token_type supply;
symbol_type symbol = exchange_symbol;
connector base;
connector quote;
void transfer( account_name user, asset q ) {
output[balance_key{user,q.symbol}] += q.amount;
}
map<balance_key, token_type> output;
vector<margin> margins;
};
/*
void connector::borrow( exchange_state& ex, account_name user,
asset amount_to_borrow,
asset collateral,
user_margin& marg ) {
FC_ASSERT( amount_to_borrow.amount < balance.amount, "attempt to borrow too much" );
lent.amount += amount_to_borrow.amount;
balance.amount -= amount_to_borrow.amount;
ex.transfer( user, amount_to_borrow );
marg.collateral.amount += collateral.amount;
marg.lent.amount += amount_to_borrow.amount;
auto p = marg.price();
if( collateral.symbol == ex.symbol ) {
if( p > ex_margin.least_collateralized_rate )
ex_margin.least_collateralized_rate = p;
}
else if( collateral.symbol == peer_margin.collateral.symbol ) {
if( p > peer_margin.least_collateralized_rate )
peer_margin.least_collateralized_rate = p;
}
}
*/
asset connector::convert_to_exchange( exchange_state& ex, const asset& input ) {
real_type R(ex.supply);
real_type S(balance.amount+input.amount);
real_type F(weight);
real_type T(input.amount);
real_type ONE(1.0);
auto E = R * (ONE - std::pow( ONE + T / S, F) );
//auto real_issued = real_type(ex.supply) * (sqrt_safe( 1.0 + (real_type(input.amount) / (balance.amount+input.amount))) - 1.0);
//auto real_issued = real_type(ex.supply) * (std::pow( 1.0 + (real_type(input.amount) / (balance.amount+input.amount)), weight) - real_type(1.0));
//auto real_issued = R * (std::pow( ONE + (T / S), F) - ONE);
//wdump((double(E))(double(real_issued)));
token_type issued = -E; //real_issued;
ex.supply += issued;
balance.amount += input.amount;
return asset{ issued, exchange_symbol };
}
asset connector::convert_from_exchange( exchange_state& ex, const asset& input ) {
real_type R(ex.supply - input.amount);
real_type S(balance.amount);
real_type F(weight);
real_type E(input.amount);
real_type ONE(1.0);
real_type T = S * (std::pow( ONE + E/R, ONE/F) - ONE);
/*
real_type base = real_type(1.0) + ( real_type(input.amount) / real_type(ex.supply-input.amount));
auto out = (balance.amount * ( std::pow(base,1.0/weight) - real_type(1.0) ));
*/
auto out = T;
// edump((double(out-T))(double(out))(double(T)));
ex.supply -= input.amount;
balance.amount -= token_type(out);
return asset{ token_type(out), balance.symbol };
}
void eosio_assert( bool test, const string& msg ) {
if( !test ) throw std::runtime_error( msg );
}
void print_state( const exchange_state& e );
/**
* Given the current state, calculate the new state
*/
exchange_state convert( const exchange_state& current,
account_name user,
asset input,
asset min_output,
asset* out = nullptr) {
eosio_assert( min_output.symbol != input.symbol, "cannot convert" );
exchange_state result(current);
asset initial_output = input;
if( input.symbol != exchange_symbol ) {
if( input.symbol == result.base.balance.symbol ) {
initial_output = result.base.convert_to_exchange( result, input );
}
else if( input.symbol == result.quote.balance.symbol ) {
initial_output = result.quote.convert_to_exchange( result, input );
}
else eosio_assert( false, "invalid symbol" );
} else {
if( min_output.symbol == result.base.balance.symbol ) {
initial_output = result.base.convert_from_exchange( result, initial_output );
}
else if( min_output.symbol == result.quote.balance.symbol ) {
initial_output= result.quote.convert_from_exchange( result, initial_output );
}
else eosio_assert( false, "invalid symbol" );
}
asset final_output = initial_output;
// std::cerr << "\n\nconvert " << input.amount << " "<< input.symbol << " => " << final_output.amount << " " << final_output.symbol << " final: " << min_output.symbol << " \n";
result.output[ balance_key{user,final_output.symbol} ] += final_output.amount;
result.output[ balance_key{user,input.symbol} ] -= input.amount;
if( min_output.symbol != final_output.symbol ) {
return convert( result, user, final_output, min_output, out );
}
if( out ) *out = final_output;
return result;
}
/* VALIDATE MARGIN ALGORITHM
*
* Given an initial condition, verify that all margin positions can be filled.
*
* Assume 3 assets, B, Q, and X and the notation LENT-COLLAT we get the following
* pairs:
*
* B-X
* B-A
* A-X
* A-B
* X-A
* X-B
*
* We assume that pairs of the same lent-type have to be simultainously filled,
* as filling one could make it impossible to fill the other.
*
*
void validate_margin( exchange_state& e ) {
for( const auto& pos : e.margins ) {
token_type min_collat = pos.lent.amount * pos.least_collateralized_rate;
asset received;
e = convert( e, "user", asset{ min_collat, pos.first.collat }, pos.lent, &received );
FC_ASSERT( received > pos.lent.amount, "insufficient collateral" );
received.amount -= pos.lent.amount;
e = convert( e, "user", received, asset{ token_type(0), pos.collateral_symbol} );
}
}
*/
/**
* A user has Collateral C and wishes to borrow B, so we give user B
* provided that C is enough to buy B back after removing it from market and
* that no margin calls would be triggered.
*/
exchange_state borrow( const exchange_state& current, account_name user,
asset amount_to_borrow,
asset collateral_provided ) {
FC_ASSERT( amount_to_borrow.symbol != collateral_provided.symbol );
/// lookup the margin position for user
/// update user's margin position
/// update least collateralized margin position on state
/// remove amount_to_borrow from exchange
/// lock collateral for user
/// simulate complete margin calls
return exchange_state();
}
exchange_state cover( const exchange_state& current, account_name user,
asset amount_to_cover, asset collateral_to_cover_with )
{
/// lookup existing position for user/debt/collat
/// verify collat > collateral_to_cover_with
/// sell collateral_to_cover_with for debt on market
/// reduce debt by proceeds
/// add proceeds to connector
// - if borrowed from user, reduce borrowed from user
/// calculate new call price and update least collateralized position
/// simulate complete margin calls
return exchange_state();
}
exchange_state lend( const exchange_state& current, account_name lender,
asset asset_to_lend ) {
/// add to pool of funds available for lending and buy SHARES in
/// interest pool at current rate.
return exchange_state();
}
exchange_state unlend( const exchange_state& current, account_name lender,
asset asset_to_lend ) {
/// sell shares in interest pool at current rate
/// this is permitable so long as total borrowed from users remains less than
/// total available to lend. Otherwise, margin is called on the least
/// collateralized position.
return exchange_state();
}
void print_state( const exchange_state& e ) {
std::cerr << "\n-----------------------------\n";
std::cerr << "supply: " << e.supply << "\n";
std::cerr << "base: " << e.base.balance.amount << " " << e.base.balance.symbol << "\n";
std::cerr << "quote: " << e.quote.balance.amount << " " << e.quote.balance.symbol << "\n";
for( const auto& item : e.output ) {
cerr << item.first.owner << " " << item.second << " " << item.first.symbol << "\n";
}
std::cerr << "\n-----------------------------\n";
}
int main( int argc, char** argv ) {
// std::cerr << "root: " << double(root.numerator())/root.denominator() << "\n";
exchange_state state;
state.supply = 100000000000ll;
//state.base.weight = state.total_weight / 2.;
state.base.balance.amount = 100000000;
state.base.balance.symbol = "USD";
state.base.weight = .49;
//state.quote.weight = state.total_weight / 2.;
state.quote.balance.amount = state.base.balance.amount;
state.quote.balance.symbol = "BTC";
state.quote.weight = .51;
print_state( state );
//state = convert( state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
auto start = fc::time_point::now();
for( uint32_t i = 0; i < 10000; ++i ) {
if( rand() % 2 == 0 )
state = convert( state, "dan", asset{ token_type(uint32_t(rand())%maxtrade), "USD"}, asset{ 0, "BTC" } );
else
state = convert( state, "dan", asset{ token_type(uint32_t(rand())%maxtrade), "BTC"}, asset{ 0, "USD" } );
}
for( const auto& item : state.output ) {
if( item.second > 0 ) {
if( item.first.symbol == "USD" )
state = convert( state, "dan", asset{ item.second, item.first.symbol}, asset{ 0, "BTC" } );
else
state = convert( state, "dan", asset{ item.second, item.first.symbol}, asset{ 0, "USD" } );
break;
}
}
print_state( state );
auto end = fc::time_point::now();
wdump((end-start));
/*
auto new_state = convert( state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 92.5-0.08-.53, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 100, "BTC"}, asset{ 0, "USD" } );
*/
//new_state = convert( new_state, "dan", asset{ 442+487-733+280+349+4.493+62.9, "BTC"}, asset{ 0, "USD" } );
/*
auto new_state = convert( state, "dan", asset{ 500, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 500, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 442+487, "BTC"}, asset{ 0, "USD" } );
*/
/*
new_state = convert( new_state, "dan", asset{ 487, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 442, "BTC"}, asset{ 0, "USD" } );
*/
//new_state = convert( new_state, "dan", asset{ 526, "BTC"}, asset{ 0, "USD" } );
//new_state = convert( new_state, "dan", asset{ 558, "BTC"}, asset{ 0, "USD" } );
//new_state = convert( new_state, "dan", asset{ 1746, "BTC"}, asset{ 0, "USD" } );
/*
new_state = convert( new_state, "dan", asset{ 526, "BTC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 500, "USD"}, asset{ 0, "EXC" } );
new_state = convert( new_state, "dan", asset{ 500, "BTC"}, asset{ 0, "EXC" } );
new_state = convert( new_state, "dan", asset{ 10, "EXC"}, asset{ 0, "USD" } );
new_state = convert( new_state, "dan", asset{ 10, "EXC"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 500, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 500, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 500, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 500, "USD"}, asset{ 0, "BTC" } );
new_state = convert( new_state, "dan", asset{ 2613, "BTC"}, asset{ 0, "USD" } );
*/
/*
auto new_state = convert( state, "dan", asset{ 10, "EXC"}, asset{ 0, "USD" } );
print_state( new_state );
new_state = convert( state, "dan", asset{ 10, "EXC"}, asset{ 0, "BTC" } );
print_state( new_state );
new_state = convert( new_state, "dan", asset{ 10, "EXC"}, asset{ 0, "USD" } );
print_state( new_state );
//new_state = convert( new_state, "dan", asset{ 52, "USD"}, asset{ 0, "EXC" } );
*/
return 0;
}
#if 0
0. if( margin_fault )
Convert Least Collateral
if( margin fault ))
defer
if( margin_fault ) assert( false, "busy calling" );
1. Fill Incoming Order
2. Check Counter Order
3. if( margin fault )
Defer Trx to finish margin call
#endif
{
"version": "eosio::abi/1.0",
"types": [{
"new_type_name": "account_name",
"type": "name"
......
{
"version": "eosio::abi/1.0",
"____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-04-19T09:07:16",
"types": [],
"structs": [{
......
......@@ -174,6 +174,17 @@ void test_action::require_notice(uint64_t receiver, uint64_t code, uint64_t acti
eosio_assert(false, "Should've failed");
}
void test_action::require_notice_tests(uint64_t receiver, uint64_t code, uint64_t action) {
eosio::print( "require_notice_tests" );
if( receiver == N( testapi ) ) {
eosio::print( "require_recipient( N(acc5) )" );
eosio::require_recipient( N( acc5 ) );
} else if( receiver == N( acc5 ) ) {
eosio::print( "require_recipient( N(testapi) )" );
eosio::require_recipient( N( testapi ) );
}
}
void test_action::require_auth() {
prints("require_auth");
eosio::require_auth( N(acc3) );
......
......@@ -71,6 +71,7 @@ extern "C" {
WASM_TEST_HANDLER(test_action, read_action_to_0);
WASM_TEST_HANDLER(test_action, read_action_to_64k);
WASM_TEST_HANDLER_EX(test_action, require_notice);
WASM_TEST_HANDLER_EX(test_action, require_notice_tests);
WASM_TEST_HANDLER(test_action, require_auth);
WASM_TEST_HANDLER(test_action, assert_false);
WASM_TEST_HANDLER(test_action, assert_true);
......
......@@ -63,6 +63,7 @@ struct test_action {
static void test_dummy_action();
static void test_cf_action();
static void require_notice(uint64_t receiver, uint64_t code, uint64_t action);
static void require_notice_tests(uint64_t receiver, uint64_t code, uint64_t action);
static void require_auth();
static void assert_false();
static void assert_true();
......
{
"____comment": "This file was generated by eosio-abigen. DO NOT EDIT - 2018-03-29T02:09:11",
"version": "eosio::abi/1.0",
"types": [{
"new_type_name": "account_name",
"type": "name"
......
......@@ -120,7 +120,7 @@
pushd "${SOURCE_DIR}" &> /dev/null
STALE_SUBMODS=$(( $(git submodule status | grep -c "^[+\-]") ))
STALE_SUBMODS=$(( $(git submodule status --recursive | grep -c "^[+\-]") ))
if [ $STALE_SUBMODS -gt 0 ]; then
printf "\\n\\tgit submodules are not up to date.\\n"
printf "\\tPlease run the command 'git submodule update --init --recursive'.\\n"
......
......@@ -8,3 +8,9 @@ add_subdirectory( appbase )
add_subdirectory( chain )
add_subdirectory( testing )
add_subdirectory( abi_generator )
#turn these off for now
set(BUILD_TESTS OFF CACHE BOOL "Build GTest-based tests")
set(BUILD_TOOLS OFF CACHE BOOL "Build wabt tools")
set(RUN_RE2C OFF CACHE BOOL "Run re2c")
add_subdirectory( wabt )
Subproject commit fa0e7fd9aa8be6ddc0c2f620cae63e58fefafab2
Subproject commit 6e440a7f3c51f3b8226860663b5eb6446087fed9
......@@ -38,6 +38,7 @@ add_library( eosio_chain
webassembly/wavm.cpp
webassembly/binaryen.cpp
webassembly/wabt.cpp
# get_config.cpp
# global_property_object.cpp
......@@ -50,12 +51,14 @@ add_library( eosio_chain
)
target_link_libraries( eosio_chain eos_utilities fc chainbase Logging IR WAST WASM Runtime
wasm asmjs passes cfg ast emscripten-optimizer support softfloat builtins
wasm asmjs passes cfg ast emscripten-optimizer support softfloat builtins wabt
)
target_include_directories( eosio_chain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../wasm-jit/Include"
"${CMAKE_CURRENT_SOURCE_DIR}/../../externals/binaryen/src"
"${CMAKE_SOURCE_DIR}/libraries/wabt"
"${CMAKE_BINARY_DIR}/libraries/wabt"
)
install( TARGETS eosio_chain
......
......@@ -105,11 +105,14 @@ namespace eosio { namespace chain {
void abi_serializer::set_abi(const abi_def& abi, const fc::microseconds& max_serialization_time) {
const fc::time_point deadline = fc::time_point::now() + max_serialization_time;
EOS_ASSERT(starts_with(abi.version, "eosio::abi/1."), unsupported_abi_version_exception, "ABI has an unsupported version");
typedefs.clear();
structs.clear();
actions.clear();
tables.clear();
error_messages.clear();
variants.clear();
for( const auto& st : abi.structs )
structs[st.name] = st;
......@@ -129,6 +132,9 @@ namespace eosio { namespace chain {
for( const auto& e : abi.error_messages )
error_messages[e.error_code] = e.error_msg;
for( const auto& v : abi.variants.value )
variants[v.name] = v;
/**
* The ABI vector may contain duplicates which would make it
* an invalid ABI
......@@ -138,6 +144,7 @@ namespace eosio { namespace chain {
EOS_ASSERT( actions.size() == abi.actions.size(), duplicate_abi_action_def_exception, "duplicate action definition detected" );
EOS_ASSERT( tables.size() == abi.tables.size(), duplicate_abi_table_def_exception, "duplicate table definition detected" );
EOS_ASSERT( error_messages.size() == abi.error_messages.size(), duplicate_abi_err_msg_def_exception, "duplicate error message definition detected" );
EOS_ASSERT( variants.size() == abi.variants.value.size(), duplicate_abi_variant_def_exception, "duplicate variant definition detected" );
validate(deadline, max_serialization_time);
}
......@@ -183,6 +190,13 @@ namespace eosio { namespace chain {
}
}
type_name abi_serializer::_remove_bin_extension(const type_name& type) {
if( ends_with(type, "$") )
return type.substr(0, type.size()-1);
else
return type;
}
bool abi_serializer::_is_type(const type_name& rtype, size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const {
EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
if( ++recursion_depth > max_recursion_depth) return false;
......@@ -190,6 +204,7 @@ namespace eosio { namespace chain {
if( built_in_types.find(type) != built_in_types.end() ) return true;
if( typedefs.find(type) != typedefs.end() ) return _is_type(typedefs.find(type)->second, recursion_depth, deadline, max_serialization_time);
if( structs.find(type) != structs.end() ) return true;
if( variants.find(type) != variants.end() ) return true;
return false;
}
......@@ -227,9 +242,15 @@ namespace eosio { namespace chain {
}
for( const auto& field : s.second.fields ) { try {
EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
EOS_ASSERT(_is_type(field.type, 0, deadline, max_serialization_time), invalid_type_inside_abi, "", ("type",field.type) );
EOS_ASSERT(_is_type(_remove_bin_extension(field.type), 0, deadline, max_serialization_time), invalid_type_inside_abi, "", ("type",field.type) );
} FC_CAPTURE_AND_RETHROW( (field) ) }
} FC_CAPTURE_AND_RETHROW( (s) ) }
for( const auto& s : variants ) { try {
for( const auto& type : s.second.types ) { try {
EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
EOS_ASSERT(_is_type(type, 0, deadline, max_serialization_time), invalid_type_inside_abi, "", ("type",type) );
} FC_CAPTURE_AND_RETHROW( (type) ) }
} FC_CAPTURE_AND_RETHROW( (s) ) }
for( const auto& a : actions ) { try {
EOS_ASSERT( fc::time_point::now() < deadline, abi_serialization_deadline_exception, "serialization time limit ${t}us exceeded", ("t", max_serialization_time) );
EOS_ASSERT(_is_type(a.second, 0, deadline, max_serialization_time), invalid_type_inside_abi, "", ("type",a.second) );
......@@ -264,7 +285,9 @@ namespace eosio { namespace chain {
_binary_to_variant(resolve_type(st.base), stream, obj, recursion_depth, deadline, max_serialization_time);
}
for( const auto& field : st.fields ) {
obj( field.name, _binary_to_variant(resolve_type(field.type), stream, recursion_depth, deadline, max_serialization_time) );
if( !stream.remaining() && ends_with(field.type, "$") )
continue;
obj( field.name, _binary_to_variant(resolve_type(_remove_bin_extension(field.type)), stream, recursion_depth, deadline, max_serialization_time) );
}
}
......@@ -289,7 +312,7 @@ namespace eosio { namespace chain {
vars.emplace_back(std::move(v));
}
EOS_ASSERT( vars.size() == size.value,
unpack_exception,
unpack_exception,
"packed size does not match unpacked array size, packed size ${p} actual size ${a}",
("p", size)("a", vars.size()) );
return fc::variant( std::move(vars) );
......@@ -297,6 +320,14 @@ namespace eosio { namespace chain {
char flag;
fc::raw::unpack(stream, flag);
return flag ? _binary_to_variant(ftype, stream, recursion_depth, deadline, max_serialization_time) : fc::variant();
} else {
auto v = variants.find(rtype);
if( v != variants.end() ) {
fc::unsigned_int select;
fc::raw::unpack(stream, select);
EOS_ASSERT( (size_t)select < v->second.types.size(), unpack_exception, "Invalid packed variant" );
return vector<fc::variant>{v->second.types[select], _binary_to_variant(v->second.types[select], stream, recursion_depth, deadline, max_serialization_time)};
}
}
fc::mutable_variant_object mvo;
......@@ -314,7 +345,7 @@ namespace eosio { namespace chain {
return _binary_to_variant(type, ds, recursion_depth, deadline, max_serialization_time);
}
void abi_serializer::_variant_to_binary( const type_name& type, const fc::variant& var, fc::datastream<char *>& ds,
void abi_serializer::_variant_to_binary( const type_name& type, const fc::variant& var, fc::datastream<char *>& ds, bool allow_extensions,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time )const
{ try {
EOS_ASSERT( ++recursion_depth < max_recursion_depth, abi_recursion_depth_exception, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
......@@ -328,8 +359,15 @@ namespace eosio { namespace chain {
vector<fc::variant> vars = var.get_array();
fc::raw::pack(ds, (fc::unsigned_int)vars.size());
for (const auto& var : vars) {
_variant_to_binary(fundamental_type(rtype), var, ds, recursion_depth, deadline, max_serialization_time);
_variant_to_binary(fundamental_type(rtype), var, ds, false, recursion_depth, deadline, max_serialization_time);
}
} else if ( variants.find(rtype) != variants.end() ) {
EOS_ASSERT( var.is_array() && var.size() == 2 && var[size_t(0)].is_string(), abi_exception, "expected array containing variant" );
auto& v = variants.find(rtype)->second;
auto it = find(v.types.begin(), v.types.end(), var[size_t(0)].get_string());
EOS_ASSERT( it != v.types.end(), abi_exception, "type is not valid within this variant" );
fc::raw::pack(ds, fc::unsigned_int(it - v.types.begin()));
_variant_to_binary( *it, var[size_t(1)], ds, allow_extensions, recursion_depth, deadline, max_serialization_time );
} else {
const auto& st = get_struct(rtype);
......@@ -337,15 +375,17 @@ namespace eosio { namespace chain {
const auto& vo = var.get_object();
if( st.base != type_name() ) {
_variant_to_binary(resolve_type(st.base), var, ds, recursion_depth, deadline, max_serialization_time);
_variant_to_binary(resolve_type(st.base), var, ds, false, recursion_depth, deadline, max_serialization_time);
}
bool missing_extension = false;
for( const auto& field : st.fields ) {
if( vo.contains( string(field.name).c_str() ) ) {
_variant_to_binary(field.type, vo[field.name], ds, recursion_depth, deadline, max_serialization_time);
}
else {
_variant_to_binary(field.type, fc::variant(), ds, recursion_depth, deadline, max_serialization_time);
/// TODO: default construct field and write it out
if( missing_extension )
EOS_THROW( pack_exception, "Unexpected '${f}' in variant object", ("f",field.name) );
_variant_to_binary(_remove_bin_extension(field.type), vo[field.name], ds, allow_extensions && &field == &st.fields.back(), recursion_depth, deadline, max_serialization_time);
} else if( ends_with(field.type, "$") && allow_extensions ) {
missing_extension = true;
} else {
EOS_THROW( pack_exception, "Missing '${f}' in variant object", ("f",field.name) );
}
}
......@@ -353,20 +393,23 @@ namespace eosio { namespace chain {
const auto& va = var.get_array();
EOS_ASSERT( st.base == type_name(), invalid_type_inside_abi, "support for base class as array not yet implemented" );
uint32_t i = 0;
if (va.size() > 0) {
for( const auto& field : st.fields ) {
if( va.size() > i )
_variant_to_binary(field.type, va[i], ds, recursion_depth, deadline, max_serialization_time);
else
_variant_to_binary(field.type, fc::variant(), ds, recursion_depth, deadline, max_serialization_time);
++i;
}
for( const auto& field : st.fields ) {
if( va.size() > i )
_variant_to_binary(_remove_bin_extension(field.type), va[i], ds, allow_extensions && &field == &st.fields.back(), recursion_depth, deadline, max_serialization_time);
else if( ends_with(field.type, "$") && allow_extensions )
break;
else
EOS_THROW( pack_exception, "Early end to array specifying the fields of struct '${t}'; require input for field '${f}'",
("t", st.name)("f", field.name) );
++i;
}
} else {
EOS_THROW( pack_exception, "Failed to serialize struct '${t}' in variant object", ("t", st.name));
}
}
} FC_CAPTURE_AND_RETHROW( (type)(var) ) }
bytes abi_serializer::_variant_to_binary( const type_name& type, const fc::variant& var,
bytes abi_serializer::_variant_to_binary( const type_name& type, const fc::variant& var, bool allow_extensions,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time )const
{ try {
EOS_ASSERT( ++recursion_depth < max_recursion_depth, abi_recursion_depth_exception, "recursive definition, max_recursion_depth ${r} ", ("r", max_recursion_depth) );
......@@ -377,7 +420,7 @@ namespace eosio { namespace chain {
bytes temp( 1024*1024 );
fc::datastream<char*> ds(temp.data(), temp.size() );
_variant_to_binary(type, var, ds, recursion_depth, deadline, max_serialization_time);
_variant_to_binary(type, var, ds, allow_extensions, recursion_depth, deadline, max_serialization_time);
temp.resize(ds.tellp());
return temp;
} FC_CAPTURE_AND_RETHROW( (type)(var) ) }
......
......@@ -76,7 +76,13 @@ action_trace apply_context::exec_one()
action_trace t(r);
t.trx_id = trx_context.id;
t.block_num = control.pending_block_state()->block_num;
t.block_time = control.pending_block_time();
t.producer_block_id = control.pending_producer_block_id();
t.account_ram_deltas = std::move( _account_ram_deltas );
_account_ram_deltas.clear();
t.act = act;
t.context_free = context_free;
t.console = _pending_console_output.str();
trx_context.executed.emplace_back( move(r) );
......@@ -102,7 +108,7 @@ void apply_context::exec()
if( _cfa_inline_actions.size() > 0 || _inline_actions.size() > 0 ) {
EOS_ASSERT( recurse_depth < control.get_global_properties().configuration.max_inline_action_depth,
transaction_exception, "inline action recursion depth reached" );
transaction_exception, "max inline action depth per transaction reached" );
}
for( const auto& inline_action : _cfa_inline_actions ) {
......@@ -275,7 +281,7 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
"Replacing a deferred transaction is temporarily disabled." );
// TODO: The logic of the next line needs to be incorporated into the next hard fork.
// trx_context.add_ram_usage( ptr->payer, -(config::billable_size_v<generated_transaction_object> + ptr->packed_trx.size()) );
// add_ram_usage( ptr->payer, -(config::billable_size_v<generated_transaction_object> + ptr->packed_trx.size()) );
d.modify<generated_transaction_object>( *ptr, [&]( auto& gtx ) {
gtx.sender = receiver;
......@@ -303,14 +309,14 @@ void apply_context::schedule_deferred_transaction( const uint128_t& sender_id, a
EOS_ASSERT( control.is_ram_billing_in_notify_allowed() || (receiver == act.account) || (receiver == payer) || privileged,
subjective_block_production_exception, "Cannot charge RAM to other accounts during notify." );
trx_context.add_ram_usage( payer, (config::billable_size_v<generated_transaction_object> + trx_size) );
add_ram_usage( payer, (config::billable_size_v<generated_transaction_object> + trx_size) );
}
bool apply_context::cancel_deferred_transaction( const uint128_t& sender_id, account_name sender ) {
auto& generated_transaction_idx = db.get_mutable_index<generated_transaction_multi_index>();
const auto* gto = db.find<generated_transaction_object,by_sender_id>(boost::make_tuple(sender, sender_id));
if ( gto ) {
trx_context.add_ram_usage( gto->payer, -(config::billable_size_v<generated_transaction_object> + gto->packed_trx.size()) );
add_ram_usage( gto->payer, -(config::billable_size_v<generated_transaction_object> + gto->packed_trx.size()) );
generated_transaction_idx.remove(*gto);
}
return gto;
......@@ -369,7 +375,7 @@ void apply_context::update_db_usage( const account_name& payer, int64_t delta )
require_authorization( payer );
}
}
trx_context.add_ram_usage(payer, delta);
add_ram_usage(payer, delta);
}
......@@ -634,5 +640,14 @@ uint64_t apply_context::next_auth_sequence( account_name actor ) {
return rs.auth_sequence;
}
void apply_context::add_ram_usage( account_name account, int64_t ram_delta ) {
trx_context.add_ram_usage( account, ram_delta );
auto p = _account_ram_deltas.emplace( account, ram_delta );
if( !p.second ) {
p.first->delta += ram_delta;
}
}
} } /// eosio::chain
......@@ -12,6 +12,7 @@
#include <eosio/chain/global_property_object.hpp>
#include <eosio/chain/contract_types.hpp>
#include <eosio/chain/generated_transaction_object.hpp>
#include <boost/tuple/tuple_io.hpp>
namespace eosio { namespace chain {
......@@ -402,7 +403,8 @@ namespace eosio { namespace chain {
EOS_ASSERT( checker.satisfied( p.first, p.second ), unsatisfied_authorization,
"transaction declares authority '${auth}', "
"but does not have signatures for it under a provided delay of ${provided_delay} ms, "
"provided permissions ${provided_permissions}, and provided keys ${provided_keys}",
"provided permissions ${provided_permissions}, provided keys ${provided_keys}, "
"and a delay max limit of ${delay_max_limit_ms} ms",
("auth", p.first)
("provided_delay", provided_delay.count()/1000)
("provided_permissions", provided_permissions)
......@@ -443,7 +445,8 @@ namespace eosio { namespace chain {
EOS_ASSERT( checker.satisfied({account, permission}), unsatisfied_authorization,
"permission '${auth}' was not satisfied under a provided delay of ${provided_delay} ms, "
"provided permissions ${provided_permissions}, and provided keys ${provided_keys}",
"provided permissions ${provided_permissions}, provided keys ${provided_keys}, "
"and a delay max limit of ${delay_max_limit_ms} ms",
("auth", permission_level{account, permission})
("provided_delay", provided_delay.count()/1000)
("provided_permissions", provided_permissions)
......
......@@ -83,6 +83,8 @@ struct pending_state {
controller::block_status _block_status = controller::block_status::incomplete;
optional<block_id_type> _producer_block_id;
void push() {
_db_session.push();
}
......@@ -106,6 +108,7 @@ struct controller_impl {
db_read_mode read_mode = db_read_mode::SPECULATIVE;
bool in_trx_requiring_checks = false; ///< if true, checks that are normally skipped on replay (e.g. auth checks) cannot be skipped
optional<fc::microseconds> subjective_cpu_leeway;
bool trusted_producer_light_validation = false;
typedef pair<scope_name,action_name> handler_key;
map< account_name, map<handler_key, apply_handler> > apply_handlers;
......@@ -629,6 +632,9 @@ struct controller_impl {
if( gtrx.expiration < self.pending_block_time() ) {
trace = std::make_shared<transaction_trace>();
trace->id = gtrx.trx_id;
trace->block_num = self.pending_block_state()->block_num;
trace->block_time = self.pending_block_time();
trace->producer_block_id = self.pending_producer_block_id();
trace->scheduled = true;
trace->receipt = push_receipt( gtrx.trx_id, transaction_receipt::expired, billed_cpu_time_us, 0 ); // expire the transaction
emit( self.accepted_transaction, trx );
......@@ -868,7 +874,9 @@ struct controller_impl {
} /// push_transaction
void start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s ) {
void start_block( block_timestamp_type when, uint16_t confirm_block_count, controller::block_status s,
const optional<block_id_type>& producer_block_id )
{
EOS_ASSERT( !pending, block_validate_exception, "pending block already exists" );
auto guard_pending = fc::make_scoped_exit([this](){
......@@ -885,6 +893,7 @@ struct controller_impl {
}
pending->_block_status = s;
pending->_producer_block_id = producer_block_id;
pending->_pending_block_state = std::make_shared<block_state>( *head, when ); // promotes pending schedule (if any) to active
pending->_pending_block_state->in_current_chain = true;
......@@ -953,7 +962,8 @@ struct controller_impl {
void apply_block( const signed_block_ptr& b, controller::block_status s ) { try {
try {
EOS_ASSERT( b->block_extensions.size() == 0, block_validate_exception, "no supported extensions" );
start_block( b->timestamp, b->confirmed, s );
auto producer_block_id = b->id();
start_block( b->timestamp, b->confirmed, s , producer_block_id);
transaction_trace_ptr trace;
......@@ -993,9 +1003,9 @@ struct controller_impl {
finalize_block();
// this implicitly asserts that all header fields (less the signature) are identical
EOS_ASSERT(b->id() == pending->_pending_block_state->header.id(),
EOS_ASSERT(producer_block_id == pending->_pending_block_state->header.id(),
block_validate_exception, "Block ID does not match",
("producer_block_id",b->id())("validator_block_id",pending->_pending_block_state->header.id()));
("producer_block_id",producer_block_id)("validator_block_id",pending->_pending_block_state->header.id()));
// We need to fill out the pending block state's block because that gets serialized in the reversible block log
// in the future we can optimize this by serializing the original and not the copy
......@@ -1018,14 +1028,20 @@ struct controller_impl {
void push_block( const signed_block_ptr& b, controller::block_status s ) {
// idump((fc::json::to_pretty_string(*b)));
EOS_ASSERT(!pending, block_validate_exception, "it is not valid to push a block when there is a pending block");
auto reset_prod_light_validation = fc::make_scoped_exit([old_value=trusted_producer_light_validation, this]() {
trusted_producer_light_validation = old_value;
});
try {
EOS_ASSERT( b, block_validate_exception, "trying to push empty block" );
EOS_ASSERT( s != controller::block_status::incomplete, block_validate_exception, "invalid block status for a completed block" );
emit( self.pre_accepted_block, b );
bool trust = !conf.force_all_checks && (s == controller::block_status::irreversible || s == controller::block_status::validated);
auto new_header_state = fork_db.add( b, trust );
if (conf.trusted_producers.count(b->producer)) {
trusted_producer_light_validation = true;
};
emit( self.accepted_block_header, new_header_state );
if ( read_mode != db_read_mode::IRREVERSIBLE ) {
......@@ -1391,7 +1407,7 @@ fork_database& controller::fork_db()const { return my->fork_db; }
void controller::start_block( block_timestamp_type when, uint16_t confirm_block_count) {
validate_db_available_size();
my->start_block(when, confirm_block_count, block_status::incomplete );
my->start_block(when, confirm_block_count, block_status::incomplete, optional<block_id_type>() );
}
void controller::finalize_block() {
......@@ -1523,6 +1539,11 @@ time_point controller::pending_block_time()const {
return my->pending->_pending_block_state->header.timestamp;
}
optional<block_id_type> controller::pending_producer_block_id()const {
EOS_ASSERT( my->pending, block_validate_exception, "no pending block" );
return my->pending->_producer_block_id;
}
uint32_t controller::last_irreversible_block_num() const {
return std::max(my->head->bft_irreversible_blocknum, my->head->dpos_irreversible_blocknum);
}
......@@ -1659,13 +1680,14 @@ bool controller::light_validation_allowed(bool replay_opts_disabled_by_policy) c
return false;
}
auto pb_status = my->pending->_block_status;
const auto pb_status = my->pending->_block_status;
// in a pending irreversible or previously validated block and we have forcing all checks
bool consider_skipping_on_replay = (pb_status == block_status::irreversible || pb_status == block_status::validated) && !replay_opts_disabled_by_policy;
const bool consider_skipping_on_replay = (pb_status == block_status::irreversible || pb_status == block_status::validated) && !replay_opts_disabled_by_policy;
// OR in a signed block and in light validation mode
bool consider_skipping_on_validate = (pb_status == block_status::complete && my->conf.block_validation_mode == validation_mode::LIGHT);
const bool consider_skipping_on_validate = (pb_status == block_status::complete &&
(my->conf.block_validation_mode == validation_mode::LIGHT || my->trusted_producer_light_validation));
return consider_skipping_on_replay || consider_skipping_on_validate;
}
......
......@@ -121,7 +121,7 @@ void apply_eosio_newaccount(apply_context& context) {
ram_delta += owner_permission.auth.get_billable_size();
ram_delta += active_permission.auth.get_billable_size();
context.trx_context.add_ram_usage(create.name, ram_delta);
context.add_ram_usage(create.name, ram_delta);
} FC_CAPTURE_AND_RETHROW( (create) ) }
......@@ -167,7 +167,7 @@ void apply_eosio_setcode(apply_context& context) {
});
if (new_size != old_size) {
context.trx_context.add_ram_usage( act.account, new_size - old_size );
context.add_ram_usage( act.account, new_size - old_size );
}
}
......@@ -196,7 +196,7 @@ void apply_eosio_setabi(apply_context& context) {
});
if (new_size != old_size) {
context.trx_context.add_ram_usage( act.account, new_size - old_size );
context.add_ram_usage( act.account, new_size - old_size );
}
}
......@@ -254,13 +254,13 @@ void apply_eosio_updateauth(apply_context& context) {
int64_t new_size = (int64_t)(config::billable_size_v<permission_object> + permission->auth.get_billable_size());
context.trx_context.add_ram_usage( permission->owner, new_size - old_size );
context.add_ram_usage( permission->owner, new_size - old_size );
} else {
const auto& p = authorization.create_permission( update.account, update.permission, parent_id, update.auth );
int64_t new_size = (int64_t)(config::billable_size_v<permission_object> + p.auth.get_billable_size());
context.trx_context.add_ram_usage( update.account, new_size );
context.add_ram_usage( update.account, new_size );
}
}
......@@ -291,7 +291,7 @@ void apply_eosio_deleteauth(apply_context& context) {
authorization.remove_permission( permission );
context.trx_context.add_ram_usage( remove.account, -old_size );
context.add_ram_usage( remove.account, -old_size );
}
......@@ -334,7 +334,7 @@ void apply_eosio_linkauth(apply_context& context) {
link.required_permission = requirement.requirement;
});
context.trx_context.add_ram_usage(
context.add_ram_usage(
l.account,
(int64_t)(config::billable_size_v<permission_link_object>)
);
......@@ -354,7 +354,7 @@ void apply_eosio_unlinkauth(apply_context& context) {
auto link_key = boost::make_tuple(unlink.account, unlink.code, unlink.type);
auto link = db.find<permission_link_object, by_action_name>(link_key);
EOS_ASSERT(link != nullptr, action_validate_exception, "Attempting to unlink authority, but no link found");
context.trx_context.add_ram_usage(
context.add_ram_usage(
link->account,
-(int64_t)(config::billable_size_v<permission_link_object>)
);
......
......@@ -94,11 +94,20 @@ struct error_message {
string error_msg;
};
struct variant_def {
type_name name;
vector<type_name> types;
};
template<typename T>
struct may_not_exist {
T value{};
};
struct abi_def {
abi_def() = default;
abi_def(const vector<type_def>& types, const vector<struct_def>& structs, const vector<action_def>& actions, const vector<table_def>& tables, const vector<clause_pair>& clauses, const vector<error_message>& error_msgs)
:version("eosio::abi/1.0")
,types(types)
:types(types)
,structs(structs)
,actions(actions)
,tables(tables)
......@@ -106,14 +115,15 @@ struct abi_def {
,error_messages(error_msgs)
{}
string version = "eosio::abi/1.0";
vector<type_def> types;
vector<struct_def> structs;
vector<action_def> actions;
vector<table_def> tables;
vector<clause_pair> ricardian_clauses;
vector<error_message> error_messages;
extensions_type abi_extensions;
string version = "";
vector<type_def> types;
vector<struct_def> structs;
vector<action_def> actions;
vector<table_def> tables;
vector<clause_pair> ricardian_clauses;
vector<error_message> error_messages;
extensions_type abi_extensions;
may_not_exist<vector<variant_def>> variants;
};
abi_def eosio_contract_abi(const abi_def& eosio_system_abi);
......@@ -121,6 +131,33 @@ vector<type_def> common_type_defs();
} } /// namespace eosio::chain
namespace fc {
template<typename ST, typename T>
datastream<ST>& operator << (datastream<ST>& s, const eosio::chain::may_not_exist<T>& v) {
raw::pack(s, v.value);
return s;
}
template<typename ST, typename T>
datastream<ST>& operator >> (datastream<ST>& s, eosio::chain::may_not_exist<T>& v) {
if (s.remaining())
raw::unpack(s, v.value);
return s;
}
template<typename T>
void to_variant(const eosio::chain::may_not_exist<T>& e, fc::variant& v) {
to_variant( e.value, v);
}
template<typename T>
void from_variant(const fc::variant& v, eosio::chain::may_not_exist<T>& e) {
from_variant( v, e.value );
}
} // namespace fc
FC_REFLECT( eosio::chain::type_def , (new_type_name)(type) )
FC_REFLECT( eosio::chain::field_def , (name)(type) )
FC_REFLECT( eosio::chain::struct_def , (name)(base)(fields) )
......@@ -128,5 +165,6 @@ FC_REFLECT( eosio::chain::action_def , (name)(type)(ricard
FC_REFLECT( eosio::chain::table_def , (name)(index_type)(key_names)(key_types)(type) )
FC_REFLECT( eosio::chain::clause_pair , (id)(body) )
FC_REFLECT( eosio::chain::error_message , (error_code)(error_msg) )
FC_REFLECT( eosio::chain::variant_def , (name)(types) )
FC_REFLECT( eosio::chain::abi_def , (version)(types)(structs)(actions)(tables)
(ricardian_clauses)(error_messages)(abi_extensions) )
(ricardian_clauses)(error_messages)(abi_extensions)(variants) )
......@@ -53,14 +53,14 @@ struct abi_serializer {
return _binary_to_variant(type, binary, 0, fc::time_point::now() + max_serialization_time, max_serialization_time);
}
bytes variant_to_binary(const type_name& type, const fc::variant& var, const fc::microseconds& max_serialization_time)const {
return _variant_to_binary(type, var, 0, fc::time_point::now() + max_serialization_time, max_serialization_time);
return _variant_to_binary(type, var, true, 0, fc::time_point::now() + max_serialization_time, max_serialization_time);
}
fc::variant binary_to_variant(const type_name& type, fc::datastream<const char*>& binary, const fc::microseconds& max_serialization_time)const {
return _binary_to_variant(type, binary, 0, fc::time_point::now() + max_serialization_time, max_serialization_time);
}
void variant_to_binary(const type_name& type, const fc::variant& var, fc::datastream<char*>& ds, const fc::microseconds& max_serialization_time)const {
_variant_to_binary(type, var, ds, 0, fc::time_point::now() + max_serialization_time, max_serialization_time);
_variant_to_binary(type, var, ds, true, 0, fc::time_point::now() + max_serialization_time, max_serialization_time);
}
template<typename T, typename Resolver>
......@@ -95,28 +95,30 @@ struct abi_serializer {
private:
map<type_name, type_name> typedefs;
map<type_name, struct_def> structs;
map<name,type_name> actions;
map<name,type_name> tables;
map<uint64_t, string> error_messages;
map<type_name, type_name> typedefs;
map<type_name, struct_def> structs;
map<name,type_name> actions;
map<name,type_name> tables;
map<uint64_t, string> error_messages;
map<type_name, variant_def> variants;
map<type_name, pair<unpack_function, pack_function>> built_in_types;
void configure_built_in_types();
fc::variant _binary_to_variant(const type_name& type, const bytes& binary,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
bytes _variant_to_binary(const type_name& type, const fc::variant& var,
bytes _variant_to_binary(const type_name& type, const fc::variant& var, bool allow_extensions,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
fc::variant _binary_to_variant(const type_name& type, fc::datastream<const char*>& binary,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
void _variant_to_binary(const type_name& type, const fc::variant& var, fc::datastream<char*>& ds,
void _variant_to_binary(const type_name& type, const fc::variant& var, fc::datastream<char*>& ds, bool allow_extensions,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
void _binary_to_variant(const type_name& type, fc::datastream<const char*>& stream, fc::mutable_variant_object& obj,
size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
static type_name _remove_bin_extension(const type_name& type);
bool _is_type(const type_name& type, size_t recursion_depth, const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
void validate(const fc::time_point& deadline, const fc::microseconds& max_serialization_time)const;
......@@ -470,7 +472,7 @@ namespace impl {
if (abi.valid()) {
auto type = abi->get_action_type(act.name);
if (!type.empty()) {
act.data = std::move( abi->_variant_to_binary( type, data, recursion_depth, deadline, max_serialization_time ));
act.data = std::move( abi->_variant_to_binary( type, data, true, recursion_depth, deadline, max_serialization_time ));
valid_empty_data = act.data.empty();
}
}
......
......@@ -572,6 +572,8 @@ class apply_context {
uint64_t next_recv_sequence( account_name receiver );
uint64_t next_auth_sequence( account_name actor );
void add_ram_usage( account_name account, int64_t ram_delta );
private:
void validate_referenced_accounts( const transaction& t )const;
......@@ -607,6 +609,7 @@ class apply_context {
vector<action> _inline_actions; ///< queued inline messages
vector<action> _cfa_inline_actions; ///< queued inline messages
std::ostringstream _pending_console_output;
flat_set<account_delta> _account_ram_deltas; ///< flat_set of account_delta so json is an array of objects
//bytes _cached_trx;
};
......
......@@ -74,6 +74,7 @@ namespace eosio { namespace chain {
validation_mode block_validation_mode = validation_mode::FULL;
flat_set<account_name> resource_greylist;
flat_set<account_name> trusted_producers;
};
enum class block_status {
......@@ -183,8 +184,9 @@ namespace eosio { namespace chain {
time_point fork_db_head_block_time()const;
account_name fork_db_head_block_producer()const;
time_point pending_block_time()const;
block_state_ptr pending_block_state()const;
time_point pending_block_time()const;
block_state_ptr pending_block_state()const;
optional<block_id_type> pending_producer_block_id()const;
const producer_schedule_type& active_producers()const;
const producer_schedule_type& pending_producers()const;
......@@ -306,4 +308,5 @@ FC_REFLECT( eosio::chain::controller::config,
(genesis)
(wasm_runtime)
(resource_greylist)
(trusted_producers)
)
......@@ -416,6 +416,10 @@ namespace eosio { namespace chain {
3015013, "Unpack data exception" )
FC_DECLARE_DERIVED_EXCEPTION( pack_exception, abi_exception,
3015014, "Pack data exception" )
FC_DECLARE_DERIVED_EXCEPTION( duplicate_abi_variant_def_exception, abi_exception,
3015015, "Duplicate variant definition in the ABI" )
FC_DECLARE_DERIVED_EXCEPTION( unsupported_abi_version_exception, abi_exception,
3015016, "ABI has an unsupported version" )
FC_DECLARE_DERIVED_EXCEPTION( contract_exception, chain_exception,
3160000, "Contract exception" )
......
......@@ -10,18 +10,33 @@
namespace eosio { namespace chain {
struct account_delta {
account_delta( const account_name& n, int64_t d):account(n),delta(d){}
account_delta(){}
account_name account;
int64_t delta = 0;
friend bool operator<( const account_delta& lhs, const account_delta& rhs ) { return lhs.account < rhs.account; }
};
struct base_action_trace {
base_action_trace( const action_receipt& r ):receipt(r){}
base_action_trace(){}
action_receipt receipt;
action act;
bool context_free = false;
fc::microseconds elapsed;
uint64_t cpu_usage = 0;
string console;
uint64_t total_cpu_usage = 0; /// total of inline_traces[x].cpu_usage + cpu_usage
transaction_id_type trx_id; ///< the transaction that generated this action
uint32_t block_num = 0;
block_timestamp_type block_time;
fc::optional<block_id_type> producer_block_id;
flat_set<account_delta> account_ram_deltas;
};
struct action_trace : public base_action_trace {
......@@ -35,6 +50,9 @@ namespace eosio { namespace chain {
struct transaction_trace {
transaction_id_type id;
uint32_t block_num = 0;
block_timestamp_type block_time;
fc::optional<block_id_type> producer_block_id;
fc::optional<transaction_receipt_header> receipt;
fc::microseconds elapsed;
uint64_t net_usage = 0;
......@@ -48,11 +66,16 @@ namespace eosio { namespace chain {
} } /// namespace eosio::chain
FC_REFLECT( eosio::chain::account_delta,
(account)(delta) )
FC_REFLECT( eosio::chain::base_action_trace,
(receipt)(act)(elapsed)(cpu_usage)(console)(total_cpu_usage)(trx_id) )
(receipt)(act)(context_free)(elapsed)(cpu_usage)(console)(total_cpu_usage)(trx_id)
(block_num)(block_time)(producer_block_id)(account_ram_deltas) )
FC_REFLECT_DERIVED( eosio::chain::action_trace,
(eosio::chain::base_action_trace), (inline_traces) )
FC_REFLECT( eosio::chain::transaction_trace, (id)(receipt)(elapsed)(net_usage)(scheduled)
FC_REFLECT( eosio::chain::transaction_trace, (id)(block_num)(block_time)(producer_block_id)
(receipt)(elapsed)(net_usage)(scheduled)
(action_traces)(failed_dtrx_trace)(except) )
......@@ -38,8 +38,6 @@ namespace eosio { namespace chain {
void pause_billing_timer();
void resume_billing_timer();
void add_ram_usage( account_name account, int64_t ram_delta );
uint32_t update_billed_cpu_time( fc::time_point now );
std::tuple<int64_t, int64_t, bool, bool> max_bandwidth_billed_accounts_can_pay( bool force_elastic_limits = false )const;
......@@ -49,6 +47,8 @@ namespace eosio { namespace chain {
friend struct controller_impl;
friend class apply_context;
void add_ram_usage( account_name account, int64_t ram_delta );
void dispatch_action( action_trace& trace, const action& a, account_name receiver, bool context_free = false, uint32_t recurse_depth = 0 );
inline void dispatch_action( action_trace& trace, const action& a, bool context_free = false ) {
dispatch_action(trace, a, a.account, context_free);
......
......@@ -54,6 +54,7 @@ namespace eosio { namespace chain {
enum class vm_type {
wavm,
binaryen,
wabt
};
wasm_interface(vm_type vm);
......@@ -76,4 +77,4 @@ namespace eosio{ namespace chain {
std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime);
}}
FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (wavm)(binaryen) )
FC_REFLECT_ENUM( eosio::chain::wasm_interface::vm_type, (wavm)(binaryen)(wabt) )
......@@ -3,6 +3,7 @@
#include <eosio/chain/wasm_interface.hpp>
#include <eosio/chain/webassembly/wavm.hpp>
#include <eosio/chain/webassembly/binaryen.hpp>
#include <eosio/chain/webassembly/wabt.hpp>
#include <eosio/chain/webassembly/runtime_interface.hpp>
#include <eosio/chain/wasm_eosio_injection.hpp>
#include <eosio/chain/transaction_context.hpp>
......@@ -28,6 +29,8 @@ namespace eosio { namespace chain {
runtime_interface = std::make_unique<webassembly::wavm::wavm_runtime>();
else if(vm == wasm_interface::vm_type::binaryen)
runtime_interface = std::make_unique<webassembly::binaryen::binaryen_runtime>();
else if(vm == wasm_interface::vm_type::wabt)
runtime_interface = std::make_unique<webassembly::wabt_runtime::wabt_runtime>();
else
EOS_THROW(wasm_exception, "wasm_interface_impl fall through");
}
......@@ -95,7 +98,8 @@ namespace eosio { namespace chain {
#define _REGISTER_INTRINSIC_EXPLICIT(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\
_REGISTER_WAVM_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\
_REGISTER_BINARYEN_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)
_REGISTER_BINARYEN_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\
_REGISTER_WABT_INTRINSIC(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)
#define _REGISTER_INTRINSIC4(CLS, MOD, METHOD, WASM_SIG, NAME, SIG)\
_REGISTER_INTRINSIC_EXPLICIT(CLS, MOD, METHOD, WASM_SIG, NAME, SIG )
......
......@@ -3,6 +3,7 @@
#include <eosio/chain/resource_limits_private.hpp>
#include <eosio/chain/transaction_metadata.hpp>
#include <eosio/chain/transaction.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <algorithm>
namespace eosio { namespace chain { namespace resource_limits {
......
......@@ -26,6 +26,9 @@ namespace eosio { namespace chain {
undo_session = c.db().start_undo_session(true);
}
trace->id = id;
trace->block_num = c.pending_block_state()->block_num;
trace->block_time = c.pending_block_time();
trace->producer_block_id = c.pending_producer_block_id();
executed.reserve( trx.total_actions() );
EOS_ASSERT( trx.transaction_extensions.size() == 0, unsupported_feature, "we don't support any extensions yet" );
}
......
......@@ -1913,6 +1913,8 @@ std::istream& operator>>(std::istream& in, wasm_interface::vm_type& runtime) {
runtime = eosio::chain::wasm_interface::vm_type::wavm;
else if (s == "binaryen")
runtime = eosio::chain::wasm_interface::vm_type::binaryen;
else if (s == "wabt")
runtime = eosio::chain::wasm_interface::vm_type::wabt;
else
in.setstate(std::ios_base::failbit);
return in;
......
#include <eosio/chain/webassembly/wabt.hpp>
#include <eosio/chain/apply_context.hpp>
#include <eosio/chain/wasm_eosio_constraints.hpp>
//wabt includes
#include <src/interp.h>
#include <src/binary-reader-interp.h>
#include <src/error-formatter.h>
namespace eosio { namespace chain { namespace webassembly { namespace wabt_runtime {
//yep 🤮
static wabt_apply_instance_vars* static_wabt_vars;
using namespace wabt;
using namespace wabt::interp;
namespace wasm_constraints = eosio::chain::wasm_constraints;
class wabt_instantiated_module : public wasm_instantiated_module_interface {
public:
wabt_instantiated_module(std::unique_ptr<interp::Environment> e, std::vector<uint8_t> initial_mem, interp::DefinedModule* mod) :
_env(move(e)), _instatiated_module(mod), _initial_memory(initial_mem),
_executor(_env.get(), nullptr, Thread::Options(64*1024,
wasm_constraints::maximum_call_depth+2))
{
for(Index i = 0; i < _env->GetGlobalCount(); ++i) {
if(_env->GetGlobal(i)->mutable_ == false)
continue;
_initial_globals.emplace_back(_env->GetGlobal(i), _env->GetGlobal(i)->typed_value);
}
if(_env->GetMemoryCount())
_initial_memory_configuration = _env->GetMemory(0)->page_limits;
}
void apply(apply_context& context) override {
//reset mutable globals
for(const auto& mg : _initial_globals)
mg.first->typed_value = mg.second;
wabt_apply_instance_vars this_run_vars{nullptr, context};
static_wabt_vars = &this_run_vars;
//reset memory to inital size & copy back in initial data
if(_env->GetMemoryCount()) {
Memory* memory = this_run_vars.memory = _env->GetMemory(0);
memory->page_limits = _initial_memory_configuration;
memory->data.resize(_initial_memory_configuration.initial * WABT_PAGE_SIZE);
memset(memory->data.data(), 0, memory->data.size());
memcpy(memory->data.data(), _initial_memory.data(), _initial_memory.size());
}
_params[0].set_i64(uint64_t(context.receiver));
_params[1].set_i64(uint64_t(context.act.account));
_params[2].set_i64(uint64_t(context.act.name));
ExecResult res = _executor.RunStartFunction(_instatiated_module);
EOS_ASSERT( res.result == interp::Result::Ok, wasm_execution_error, "wabt start function failure (${s})", ("s", ResultToString(res.result)) );
res = _executor.RunExportByName(_instatiated_module, "apply", _params);
EOS_ASSERT( res.result == interp::Result::Ok, wasm_execution_error, "wabt execution failure (${s})", ("s", ResultToString(res.result)) );
}
private:
std::unique_ptr<interp::Environment> _env;
DefinedModule* _instatiated_module; //this is owned by the Environment
std::vector<uint8_t> _initial_memory;
TypedValues _params{3, TypedValue(Type::I64)};
std::vector<std::pair<Global*, TypedValue>> _initial_globals;
Limits _initial_memory_configuration;
Executor _executor;
};
wabt_runtime::wabt_runtime() {}
std::unique_ptr<wasm_instantiated_module_interface> wabt_runtime::instantiate_module(const char* code_bytes, size_t code_size, std::vector<uint8_t> initial_memory) {
std::unique_ptr<interp::Environment> env = std::make_unique<interp::Environment>();
for(auto it = intrinsic_registrator::get_map().begin() ; it != intrinsic_registrator::get_map().end(); ++it) {
interp::HostModule* host_module = env->AppendHostModule(it->first);
for(auto itf = it->second.begin(); itf != it->second.end(); ++itf) {
host_module->AppendFuncExport(itf->first, itf->second.sig, [fn=itf->second.func](const auto* f, const auto* fs, const auto& args, auto& res) {
TypedValue ret = fn(*static_wabt_vars, args);
if(ret.type != Type::Void)
res[0] = ret;
return interp::Result::Ok;
});
}
}
interp::DefinedModule* instantiated_module = nullptr;
wabt::Errors errors;
wabt::Result res = ReadBinaryInterp(env.get(), code_bytes, code_size, read_binary_options, &errors, &instantiated_module);
EOS_ASSERT( Succeeded(res), wasm_execution_error, "Error building wabt interp: ${e}", ("e", wabt::FormatErrorsToString(errors, Location::Type::Binary)) );
return std::make_unique<wabt_instantiated_module>(std::move(env), initial_memory, instantiated_module);
}
}}}}
Subproject commit 959cb4ddffe4316b8f6d7c1f9290400a52ae40c4
Subproject commit 9bfe5043f5484e00f89387091e9b5beb90b88c62
Subproject commit 62a19a758682679e3de27d956986eaf8b016465d
Subproject commit 4dc8375d7d3e02ab1177ab5c22835f75b45c845a
......@@ -6,6 +6,7 @@
#include <eosio/chain/abi_serializer.hpp>
#include <fc/io/json.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/tuple/tuple_io.hpp>
#include <iosfwd>
......@@ -308,14 +309,17 @@ namespace eosio { namespace testing {
try {
if( num_blocks_to_producer_before_shutdown > 0 )
produce_blocks( num_blocks_to_producer_before_shutdown );
BOOST_REQUIRE_EQUAL( validate(), true );
if (!skip_validate)
BOOST_REQUIRE_EQUAL( validate(), true );
} catch( const fc::exception& e ) {
wdump((e.to_detail_string()));
}
}
controller::config vcfg;
validating_tester() {
static controller::config default_config() {
fc::temp_directory tempdir;
controller::config vcfg;
vcfg.blocks_dir = tempdir.path() / std::string("v_").append(config::default_blocks_dir_name);
vcfg.state_dir = tempdir.path() / std::string("v_").append(config::default_state_dir_name);
vcfg.state_size = 1024*1024*8;
......@@ -332,8 +336,16 @@ namespace eosio { namespace testing {
vcfg.wasm_runtime = chain::wasm_interface::vm_type::binaryen;
else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wavm"))
vcfg.wasm_runtime = chain::wasm_interface::vm_type::wavm;
else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wabt"))
vcfg.wasm_runtime = chain::wasm_interface::vm_type::wabt;
}
return vcfg;
}
validating_tester(const flat_set<account_name>& trusted_producers = flat_set<account_name>()) {
vcfg = default_config();
vcfg.trusted_producers = trusted_producers;
validating_node = std::make_unique<controller>(vcfg);
validating_node->startup();
......@@ -362,6 +374,14 @@ namespace eosio { namespace testing {
return sb;
}
signed_block_ptr produce_block_no_validation( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms), uint32_t skip_flag = 0 /*skip_missed_block_penalty*/ ) {
return _produce_block(skip_time, false, skip_flag | 2);
}
void validate_push_block(const signed_block_ptr& sb) {
validating_node->push_block( sb );
}
signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms), uint32_t skip_flag = 0 /*skip_missed_block_penalty*/ )override {
control->abort_block();
auto sb = _produce_block(skip_time, true, skip_flag | 2);
......@@ -393,6 +413,7 @@ namespace eosio { namespace testing {
unique_ptr<controller> validating_node;
uint32_t num_blocks_to_producer_before_shutdown = 0;
bool skip_validate = false;
};
/**
......
......@@ -100,6 +100,8 @@ namespace eosio { namespace testing {
cfg.wasm_runtime = chain::wasm_interface::vm_type::binaryen;
else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wavm"))
cfg.wasm_runtime = chain::wasm_interface::vm_type::wavm;
else if(boost::unit_test::framework::master_test_suite().argv[i] == std::string("--wabt"))
cfg.wasm_runtime = chain::wasm_interface::vm_type::wabt;
else
cfg.wasm_runtime = chain::wasm_interface::vm_type::binaryen;
}
......
Subproject commit 2f5382661f7bf77cf7a70dcf0543a44fd5025910
......@@ -17,6 +17,8 @@ add_subdirectory(db_size_api_plugin)
#add_subdirectory(faucet_testnet_plugin)
add_subdirectory(mongo_db_plugin)
add_subdirectory(login_plugin)
add_subdirectory(test_control_plugin)
add_subdirectory(test_control_api_plugin)
# Forward variables to top level so packaging picks them up
set(CPACK_DEBIAN_PACKAGE_DEPENDS ${CPACK_DEBIAN_PACKAGE_DEPENDS} PARENT_SCOPE)
# Community Plugin List
This file contains a list of community authored plugins for `nodeos`, acting as a directory of the plugins that are available.
Third parties are encouraged to make pull requests to this file (`develop` branch please) in order to list new plugins.
| Description | URL |
| ----------- | --- |
| Watch for specific actions and send them to an HTTP URL | https://github.com/eosauthority/eosio-watcher-plugin |
| Kafka | https://github.com/TP-Lab/kafka_plugin |
## DISCLAIMER:
The fact that a plugin is listed in this file does not mean the plugin has been reviewed by this repository's maintainers. No warranties are made, i.e. you are at your own risk if you choose to use them.
......@@ -85,9 +85,12 @@ void chain_api_plugin::plugin_startup() {
CHAIN_RO_CALL(get_block_header_state, 200),
CHAIN_RO_CALL(get_account, 200),
CHAIN_RO_CALL(get_code, 200),
CHAIN_RO_CALL(get_code_hash, 200),
CHAIN_RO_CALL(get_abi, 200),
CHAIN_RO_CALL(get_raw_code_and_abi, 200),
CHAIN_RO_CALL(get_raw_abi, 200),
CHAIN_RO_CALL(get_table_rows, 200),
CHAIN_RO_CALL(get_table_by_scope, 200),
CHAIN_RO_CALL(get_currency_balance, 200),
CHAIN_RO_CALL(get_currency_stats, 200),
CHAIN_RO_CALL(get_producers, 200),
......
......@@ -212,7 +212,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
("blocks-dir", bpo::value<bfs::path>()->default_value("blocks"),
"the location of the blocks directory (absolute path or relative to application data dir)")
("checkpoint", bpo::value<vector<string>>()->composing(), "Pairs of [BLOCK_NUM,BLOCK_ID] that should be enforced as checkpoints.")
("wasm-runtime", bpo::value<eosio::chain::wasm_interface::vm_type>()->value_name("wavm/binaryen"), "Override default WASM runtime")
("wasm-runtime", bpo::value<eosio::chain::wasm_interface::vm_type>()->value_name("wavm/binaryen/wabt"), "Override default WASM runtime")
("abi-serializer-max-time-ms", bpo::value<uint32_t>()->default_value(config::default_abi_serializer_max_time_ms),
"Override default maximum ABI serialization time allowed in ms")
("chain-state-db-size-mb", bpo::value<uint64_t>()->default_value(config::default_state_size / (1024 * 1024)), "Maximum size (in MiB) of the chain state database")
......@@ -283,6 +283,7 @@ void chain_plugin::set_program_options(options_description& cli, options_descrip
"replace reversible block database with blocks imported from specified file and then exit")
("export-reversible-blocks", bpo::value<bfs::path>(),
"export reversible block database in portable format into specified file and then exit")
("trusted-producer", bpo::value<vector<string>>()->composing(), "Indicate a producer whose blocks headers signed by it will be fully validated, but transactions in those validated blocks will be trusted.")
;
}
......@@ -313,6 +314,17 @@ fc::time_point calculate_genesis_timestamp( string tstr ) {
return genesis_timestamp;
}
void clear_directory_contents( const fc::path& p ) {
using boost::filesystem::directory_iterator;
if( !fc::is_directory( p ) )
return;
for( directory_iterator enditr, itr{p}; itr != enditr; ++itr ) {
fc::remove_all( itr->path() );
}
}
void chain_plugin::plugin_initialize(const variables_map& options) {
ilog("initializing chain plugin");
......@@ -332,6 +344,8 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
LOAD_VALUE_SET( options, "contract-whitelist", my->chain_config->contract_whitelist );
LOAD_VALUE_SET( options, "contract-blacklist", my->chain_config->contract_blacklist );
LOAD_VALUE_SET( options, "trusted-producer", my->chain_config->trusted_producers );
if( options.count( "action-blacklist" )) {
const std::vector<std::string>& acts = options["action-blacklist"].as<std::vector<std::string>>();
auto& list = my->chain_config->action_blacklist;
......@@ -456,11 +470,11 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
ilog( "Deleting state database and blocks" );
if( options.at( "truncate-at-block" ).as<uint32_t>() > 0 )
wlog( "The --truncate-at-block option does not make sense when deleting all blocks." );
fc::remove_all( my->chain_config->state_dir );
clear_directory_contents( my->chain_config->state_dir );
fc::remove_all( my->blocks_dir );
} else if( options.at( "hard-replay-blockchain" ).as<bool>()) {
ilog( "Hard replay requested: deleting state database" );
fc::remove_all( my->chain_config->state_dir );
clear_directory_contents( my->chain_config->state_dir );
auto backup_dir = block_log::repair_log( my->blocks_dir, options.at( "truncate-at-block" ).as<uint32_t>());
if( fc::exists( backup_dir / config::reversible_blocks_dir_name ) ||
options.at( "fix-reversible-blocks" ).as<bool>()) {
......@@ -482,7 +496,7 @@ void chain_plugin::plugin_initialize(const variables_map& options) {
ilog( "Replay requested: deleting state database" );
if( options.at( "truncate-at-block" ).as<uint32_t>() > 0 )
wlog( "The --truncate-at-block option does not work for a regular replay of the blockchain." );
fc::remove_all( my->chain_config->state_dir );
clear_directory_contents( my->chain_config->state_dir );
if( options.at( "fix-reversible-blocks" ).as<bool>()) {
if( !recover_reversible_blocks( my->chain_config->blocks_dir / config::reversible_blocks_dir_name,
my->chain_config->reversible_cache_size )) {
......@@ -1104,6 +1118,48 @@ read_only::get_table_rows_result read_only::get_table_rows( const read_only::get
}
}
read_only::get_table_by_scope_result read_only::get_table_by_scope( const read_only::get_table_by_scope_params& p )const {
const auto& d = db.db();
const auto& idx = d.get_index<chain::table_id_multi_index, chain::by_code_scope_table>();
decltype(idx.lower_bound(boost::make_tuple(0, 0, 0))) lower;
decltype(idx.upper_bound(boost::make_tuple(0, 0, 0))) upper;
if (p.lower_bound.size()) {
uint64_t scope = convert_to_type<uint64_t>(p.lower_bound, "lower_bound scope");
lower = idx.lower_bound( boost::make_tuple(p.code, scope, p.table));
} else {
lower = idx.lower_bound(boost::make_tuple(p.code, 0, p.table));
}
if (p.upper_bound.size()) {
uint64_t scope = convert_to_type<uint64_t>(p.upper_bound, "upper_bound scope");
upper = idx.lower_bound( boost::make_tuple(p.code, scope, 0));
} else {
upper = idx.lower_bound(boost::make_tuple((uint64_t)p.code + 1, 0, 0));
}
auto end = fc::time_point::now() + fc::microseconds(1000 * 10); /// 10ms max time
unsigned int count = 0;
auto itr = lower;
read_only::get_table_by_scope_result result;
for (; itr != upper; ++itr) {
if (p.table && itr->table != p.table) {
if (fc::time_point::now() > end) {
break;
}
continue;
}
result.rows.push_back({itr->code, itr->scope, itr->table, itr->payer, itr->count});
if (++count == p.limit || fc::time_point::now() > end) {
++itr;
break;
}
}
if (itr != upper) {
result.more = (string)itr->scope;
}
return result;
}
vector<asset> read_only::get_currency_balance( const read_only::get_currency_balance_params& p )const {
const abi_def abi = eosio::chain_apis::get_abi( db, p.code );
......@@ -1499,6 +1555,19 @@ read_only::get_code_results read_only::get_code( const get_code_params& params )
return result;
}
read_only::get_code_hash_results read_only::get_code_hash( const get_code_hash_params& params )const {
get_code_hash_results result;
result.account_name = params.account_name;
const auto& d = db.db();
const auto& accnt = d.get<account_object,by_name>( params.account_name );
if( accnt.code.size() ) {
result.code_hash = fc::sha256::hash( accnt.code.data(), accnt.code.size() );
}
return result;
}
read_only::get_raw_code_and_abi_results read_only::get_raw_code_and_abi( const get_raw_code_and_abi_params& params)const {
get_raw_code_and_abi_results result;
result.account_name = params.account_name;
......@@ -1511,6 +1580,20 @@ read_only::get_raw_code_and_abi_results read_only::get_raw_code_and_abi( const g
return result;
}
read_only::get_raw_abi_results read_only::get_raw_abi( const get_raw_abi_params& params )const {
get_raw_abi_results result;
result.account_name = params.account_name;
const auto& d = db.db();
const auto& accnt = d.get<account_object,by_name>(params.account_name);
result.abi_hash = fc::sha256::hash( accnt.abi.data(), accnt.abi.size() );
result.code_hash = fc::sha256::hash( accnt.code.data(), accnt.code.size() );
if( !params.abi_hash || *params.abi_hash != result.abi_hash )
result.abi = blob{{accnt.abi.begin(), accnt.abi.end()}};
return result;
}
read_only::get_account_results read_only::get_account( const get_account_params& params )const {
get_account_results result;
result.account_name = params.account_name;
......@@ -1561,16 +1644,18 @@ read_only::get_account_results read_only::get_account( const get_account_params&
const auto token_code = N(eosio.token);
auto core_symbol = extract_core_symbol();
const auto* t_id = d.find<chain::table_id_object, chain::by_code_scope_table>(boost::make_tuple( token_code, params.account_name, N(accounts) ));
if( t_id != nullptr ) {
const auto &idx = d.get_index<key_value_index, by_scope_primary>();
auto it = idx.find(boost::make_tuple( t_id->id, symbol().to_symbol_code() ));
auto it = idx.find(boost::make_tuple( t_id->id, core_symbol.to_symbol_code() ));
if( it != idx.end() && it->value.size() >= sizeof(asset) ) {
asset bal;
fc::datastream<const char *> ds(it->value.data(), it->value.size());
fc::raw::unpack(ds, bal);
if( bal.get_symbol().valid() && bal.get_symbol() == symbol() ) {
if( bal.get_symbol().valid() && bal.get_symbol() == core_symbol ) {
result.core_liquid_balance = bal;
}
}
......@@ -1683,6 +1768,46 @@ read_only::get_transaction_id_result read_only::get_transaction_id( const read_o
return params.id();
}
namespace detail {
struct ram_market_exchange_state_t {
asset ignore1;
asset ignore2;
double ignore3;
asset core_symbol;
double ignore4;
};
}
chain::symbol read_only::extract_core_symbol()const {
symbol core_symbol; // Default to CORE_SYMBOL if the appropriate data structure cannot be found in the system contract table data
// The following code makes assumptions about the contract deployed on eosio account (i.e. the system contract) and how it stores its data.
const auto& d = db.db();
const auto* t_id = d.find<chain::table_id_object, chain::by_code_scope_table>(boost::make_tuple( N(eosio), N(eosio), N(rammarket) ));
if( t_id != nullptr ) {
const auto &idx = d.get_index<key_value_index, by_scope_primary>();
auto it = idx.find(boost::make_tuple( t_id->id, eosio::chain::string_to_symbol_c(4,"RAMCORE") ));
if( it != idx.end() ) {
detail::ram_market_exchange_state_t ram_market_exchange_state;
fc::datastream<const char *> ds( it->value.data(), it->value.size() );
try {
fc::raw::unpack(ds, ram_market_exchange_state);
} catch( ... ) {
return core_symbol;
}
if( ram_market_exchange_state.core_symbol.get_symbol().valid() ) {
core_symbol = ram_market_exchange_state.core_symbol.get_symbol();
}
}
}
return core_symbol;
}
} // namespace chain_apis
} // namespace eosio
FC_REFLECT( eosio::chain_apis::detail::ram_market_exchange_state_t, (ignore1)(ignore2)(ignore3)(core_symbol)(ignore4) )
......@@ -152,6 +152,15 @@ public:
bool code_as_wasm = false;
};
struct get_code_hash_results {
name account_name;
fc::sha256 code_hash;
};
struct get_code_hash_params {
name account_name;
};
struct get_abi_results {
name account_name;
optional<abi_def> abi;
......@@ -171,10 +180,24 @@ public:
name account_name;
};
struct get_raw_abi_params {
name account_name;
optional<fc::sha256> abi_hash;
};
struct get_raw_abi_results {
name account_name;
fc::sha256 code_hash;
fc::sha256 abi_hash;
optional<chain::blob> abi;
};
get_code_results get_code( const get_code_params& params )const;
get_code_hash_results get_code_hash( const get_code_hash_params& params )const;
get_abi_results get_abi( const get_abi_params& params )const;
get_raw_code_and_abi_results get_raw_code_and_abi( const get_raw_code_and_abi_params& params)const;
get_raw_abi_results get_raw_abi( const get_raw_abi_params& params)const;
......@@ -250,6 +273,27 @@ public:
get_table_rows_result get_table_rows( const get_table_rows_params& params )const;
struct get_table_by_scope_params {
name code; // mandatory
name table = 0; // optional, act as filter
string lower_bound; // lower bound of scope, optional
string upper_bound; // upper bound of scope, optional
uint32_t limit = 10;
};
struct get_table_by_scope_result_row {
name code;
name scope;
name table;
name payer;
uint32_t count;
};
struct get_table_by_scope_result {
vector<get_table_by_scope_result_row> rows;
string more; ///< fill lower_bound with this value to fetch more rows
};
get_table_by_scope_result get_table_by_scope( const get_table_by_scope_params& params )const;
struct get_currency_balance_params {
name code;
name account;
......@@ -468,6 +512,8 @@ public:
return result;
}
chain::symbol extract_core_symbol()const;
friend struct resolver_factory<read_only>;
};
......@@ -624,6 +670,10 @@ FC_REFLECT( eosio::chain_apis::read_write::push_transaction_results, (transactio
FC_REFLECT( eosio::chain_apis::read_only::get_table_rows_params, (json)(code)(scope)(table)(table_key)(lower_bound)(upper_bound)(limit)(key_type)(index_position)(encode_type) )
FC_REFLECT( eosio::chain_apis::read_only::get_table_rows_result, (rows)(more) );
FC_REFLECT( eosio::chain_apis::read_only::get_table_by_scope_params, (code)(table)(lower_bound)(upper_bound)(limit) )
FC_REFLECT( eosio::chain_apis::read_only::get_table_by_scope_result_row, (code)(scope)(table)(payer)(count));
FC_REFLECT( eosio::chain_apis::read_only::get_table_by_scope_result, (rows)(more) );
FC_REFLECT( eosio::chain_apis::read_only::get_currency_balance_params, (code)(account)(symbol));
FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_params, (code)(symbol));
FC_REFLECT( eosio::chain_apis::read_only::get_currency_stats_result, (supply)(max_supply)(issuer));
......@@ -642,12 +692,16 @@ FC_REFLECT( eosio::chain_apis::read_only::get_account_results,
(core_liquid_balance)(ram_quota)(net_weight)(cpu_weight)(net_limit)(cpu_limit)(ram_usage)(permissions)
(total_resources)(self_delegated_bandwidth)(refund_request)(voter_info) )
FC_REFLECT( eosio::chain_apis::read_only::get_code_results, (account_name)(code_hash)(wast)(wasm)(abi) )
FC_REFLECT( eosio::chain_apis::read_only::get_code_hash_results, (account_name)(code_hash) )
FC_REFLECT( eosio::chain_apis::read_only::get_abi_results, (account_name)(abi) )
FC_REFLECT( eosio::chain_apis::read_only::get_account_params, (account_name) )
FC_REFLECT( eosio::chain_apis::read_only::get_code_params, (account_name)(code_as_wasm) )
FC_REFLECT( eosio::chain_apis::read_only::get_code_hash_params, (account_name) )
FC_REFLECT( eosio::chain_apis::read_only::get_abi_params, (account_name) )
FC_REFLECT( eosio::chain_apis::read_only::get_raw_code_and_abi_params, (account_name) )
FC_REFLECT( eosio::chain_apis::read_only::get_raw_code_and_abi_results, (account_name)(wasm)(abi) )
FC_REFLECT( eosio::chain_apis::read_only::get_raw_abi_params, (account_name)(abi_hash) )
FC_REFLECT( eosio::chain_apis::read_only::get_raw_abi_results, (account_name)(code_hash)(abi_hash)(abi) )
FC_REFLECT( eosio::chain_apis::read_only::producer_info, (producer_name) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_params, (code)(action)(args) )
FC_REFLECT( eosio::chain_apis::read_only::abi_json_to_bin_result, (binargs) )
......
......@@ -10,7 +10,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/signals2/connection.hpp>
namespace eosio {
namespace eosio {
using namespace chain;
using boost::signals2::scoped_connection;
......@@ -51,7 +51,7 @@ namespace eosio {
indexed_by<
ordered_unique<tag<by_id>, member<action_history_object, action_history_object::id_type, &action_history_object::id>>,
ordered_unique<tag<by_action_sequence_num>, member<action_history_object, uint64_t, &action_history_object::action_sequence_num>>,
ordered_unique<tag<by_trx_id>,
ordered_unique<tag<by_trx_id>,
composite_key< action_history_object,
member<action_history_object, transaction_id_type, &action_history_object::trx_id>,
member<action_history_object, uint64_t, &action_history_object::action_sequence_num >
......@@ -64,7 +64,7 @@ namespace eosio {
account_history_object,
indexed_by<
ordered_unique<tag<by_id>, member<account_history_object, account_history_object::id_type, &account_history_object::id>>,
ordered_unique<tag<by_account_action_seq>,
ordered_unique<tag<by_account_action_seq>,
composite_key< account_history_object,
member<account_history_object, account_name, &account_history_object::account >,
member<account_history_object, int32_t, &account_history_object::account_sequence_num >
......@@ -213,7 +213,7 @@ namespace eosio {
uint64_t asn = 0;
if( itr != idx.begin() ) --itr;
if( itr->account == n )
if( itr->account == n )
asn = itr->account_sequence_num + 1;
//idump((n)(act.receipt.global_sequence)(asn));
......@@ -268,7 +268,7 @@ namespace eosio {
aho.block_time = chain.pending_block_time();
aho.trx_id = at.trx_id;
});
auto aset = account_set( at );
for( auto a : aset ) {
record_account_action( a, at );
......@@ -313,7 +313,7 @@ namespace eosio {
if( options.count( "filter-on" )) {
auto fo = options.at( "filter-on" ).as<vector<string>>();
for( auto& s : fo ) {
if( s == "*" ) {
if( s == "*" || s == "\"*\"" ) {
my->bypass_filter = true;
wlog( "--filter-on * enabled. This can fill shared_mem, causing nodeos to stop." );
break;
......@@ -366,7 +366,7 @@ namespace eosio {
namespace history_apis {
namespace history_apis {
read_only::get_actions_result read_only::get_actions( const read_only::get_actions_params& params )const {
edump((params));
auto& chain = history->chain_plug->chain();
......@@ -388,7 +388,7 @@ namespace eosio {
pos = itr->account_sequence_num+1;
} else if( itr != idx.begin() ) --itr;
if( itr->account == n )
if( itr->account == n )
pos = itr->account_sequence_num + 1;
}
......@@ -440,13 +440,31 @@ namespace eosio {
read_only::get_transaction_result read_only::get_transaction( const read_only::get_transaction_params& p )const {
auto& chain = history->chain_plug->chain();
const auto abi_serializer_max_time = history->chain_plug->get_abi_serializer_max_time();
auto short_id = fc::variant(p.id).as_string().substr(0,8);
transaction_id_type input_id;
auto input_id_length = p.id.size();
try {
FC_ASSERT( input_id_length <= 64, "hex string is too long to represent an actual transaction id" );
FC_ASSERT( input_id_length >= 8, "hex string representing transaction id should be at least 8 characters long to avoid excessive collisions" );
input_id = transaction_id_type(p.id);
} EOS_RETHROW_EXCEPTIONS(transaction_id_type_exception, "Invalid transaction ID: ${transaction_id}", ("transaction_id", p.id))
auto txn_id_matched = [&input_id, input_id_size = input_id_length/2, no_half_byte_at_end = (input_id_length % 2 == 0)]
( const transaction_id_type &id ) -> bool // hex prefix comparison
{
bool whole_byte_prefix_matches = memcmp( input_id.data(), id.data(), input_id_size ) == 0;
if( !whole_byte_prefix_matches || no_half_byte_at_end )
return whole_byte_prefix_matches;
// check if half byte at end of specified part of input_id matches
return (*(input_id.data() + input_id_size) & 0xF0) == (*(id.data() + input_id_size) & 0xF0);
};
const auto& db = chain.db();
const auto& idx = db.get_index<action_history_index, by_trx_id>();
auto itr = idx.lower_bound( boost::make_tuple(p.id) );
auto itr = idx.lower_bound( boost::make_tuple( input_id ) );
bool in_history = (itr != idx.end() && fc::variant(itr->trx_id).as_string().substr(0,8) == short_id );
bool in_history = (itr != idx.end() && txn_id_matched(itr->trx_id) );
if( !in_history && !p.block_num_hint ) {
EOS_THROW(tx_not_found, "Transaction ${id} not found in history and no block hint was given", ("id",p.id));
......@@ -454,12 +472,9 @@ namespace eosio {
get_transaction_result result;
if (in_history) {
result.id = p.id;
result.last_irreversible_block = chain.last_irreversible_block_num();
if( in_history ) {
result.id = itr->trx_id;
result.last_irreversible_block = chain.last_irreversible_block_num();
result.block_num = itr->block_num;
result.block_time = itr->block_time;
......@@ -509,7 +524,7 @@ namespace eosio {
if (receipt.trx.contains<packed_transaction>()) {
auto& pt = receipt.trx.get<packed_transaction>();
auto mtrx = transaction_metadata(pt);
if (fc::variant(mtrx.id).as_string().substr(0, 8) == short_id) {
if( txn_id_matched(mtrx.id) ) {
result.id = mtrx.id;
result.last_irreversible_block = chain.last_irreversible_block_num();
result.block_num = *p.block_num_hint;
......@@ -522,7 +537,7 @@ namespace eosio {
}
} else {
auto& id = receipt.trx.get<transaction_id_type>();
if (fc::variant(id).as_string().substr(0, 8) == short_id) {
if( txn_id_matched(id) ) {
result.id = id;
result.last_irreversible_block = chain.last_irreversible_block_num();
result.block_num = *p.block_num_hint;
......
......@@ -30,18 +30,6 @@ class read_only {
: history(history) {}
/*
struct get_transaction_params {
chain::transaction_id_type transaction_id;
};
struct get_transaction_results {
chain::transaction_id_type transaction_id;
fc::variant transaction;
};
get_transaction_results get_transaction(const get_transaction_params& params) const;
*/
struct get_actions_params {
chain::account_name account_name;
optional<int32_t> pos; /// a absolute sequence positon -1 is the end/last action
......@@ -67,7 +55,7 @@ class read_only {
struct get_transaction_params {
transaction_id_type id;
string id;
optional<uint32_t> block_num_hint;
};
......@@ -81,7 +69,7 @@ class read_only {
};
get_transaction_result get_transaction( const get_transaction_params& )const;
......@@ -120,13 +108,13 @@ class read_only {
/**
* This plugin tracks all actions and keys associated with a set of configured accounts. It enables
* wallets to paginate queries for history.
* wallets to paginate queries for history.
*
* An action will be included in the account's history if any of the following:
* - receiver
* - any account named in auth list
*
* A key will be linked to an account if the key is referneced in authorities of updateauth or newaccount
* A key will be linked to an account if the key is referneced in authorities of updateauth or newaccount
*/
class history_plugin : public plugin<history_plugin> {
public:
......
......@@ -3,6 +3,7 @@
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/http_plugin/local_endpoint.hpp>
#include <eosio/chain/exceptions.hpp>
#include <fc/network/ip.hpp>
......@@ -43,6 +44,11 @@ namespace eosio {
using std::shared_ptr;
using websocketpp::connection_hdl;
static http_plugin_defaults current_http_plugin_defaults;
void http_plugin::set_defaults(const http_plugin_defaults config) {
current_http_plugin_defaults = config;
}
namespace detail {
......@@ -79,9 +85,42 @@ namespace eosio {
static const long timeout_open_handshake = 0;
};
struct asio_local_with_stub_log : public websocketpp::config::asio {
typedef asio_local_with_stub_log type;
typedef asio base;
typedef base::concurrency_type concurrency_type;
typedef base::request_type request_type;
typedef base::response_type response_type;
typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;
typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;
typedef base::rng_type rng_type;
struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::basic_socket::local_endpoint socket_type;
};
typedef websocketpp::transport::asio::local_endpoint<transport_config> transport_type;
static const long timeout_open_handshake = 0;
};
}
using websocket_server_type = websocketpp::server<detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint>>;
using websocket_local_server_type = websocketpp::server<detail::asio_local_with_stub_log>;
using websocket_server_tls_type = websocketpp::server<detail::asio_with_stub_log<websocketpp::transport::asio::tls_socket::endpoint>>;
using ssl_context_ptr = websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context>;
......@@ -105,9 +144,16 @@ namespace eosio {
websocket_server_tls_type https_server;
optional<asio::local::stream_protocol::endpoint> unix_endpoint;
websocket_local_server_type unix_server;
bool validate_host;
set<string> valid_hosts;
string unix_socket_path_option_name = "unix-socket-path";
string http_server_address_option_name = "http-server-address";
string https_server_address_option_name = "https-server-address";
bool host_port_is_valid( const std::string& header_host_port, const string& endpoint_local_host_port ) {
return !validate_host || header_host_port == endpoint_local_host_port || valid_hosts.find(header_host_port) != valid_hosts.end();
}
......@@ -163,7 +209,7 @@ namespace eosio {
}
template<class T>
static void handle_exception(typename websocketpp::server<detail::asio_with_stub_log<T>>::connection_ptr con) {
static void handle_exception(typename websocketpp::server<T>::connection_ptr con) {
string err = "Internal Service error, http: ";
try {
con->set_status( websocketpp::http::status_code::internal_server_error );
......@@ -195,18 +241,26 @@ namespace eosio {
}
template<class T>
void handle_http_request(typename websocketpp::server<detail::asio_with_stub_log<T>>::connection_ptr con) {
try {
bool is_secure = con->get_uri()->get_secure();
const auto& local_endpoint = con->get_socket().lowest_layer().local_endpoint();
auto local_socket_host_port = local_endpoint.address().to_string() + ":" + std::to_string(local_endpoint.port());
bool allow_host(const typename T::request_type& req, typename websocketpp::server<T>::connection_ptr con) {
bool is_secure = con->get_uri()->get_secure();
const auto& local_endpoint = con->get_socket().lowest_layer().local_endpoint();
auto local_socket_host_port = local_endpoint.address().to_string() + ":" + std::to_string(local_endpoint.port());
const auto& host_str = req.get_header("Host");
if (host_str.empty() || !host_is_valid(host_str, local_socket_host_port, is_secure)) {
con->set_status(websocketpp::http::status_code::bad_request);
return false;
}
return true;
}
template<class T>
void handle_http_request(typename websocketpp::server<T>::connection_ptr con) {
try {
auto& req = con->get_request();
const auto& host_str = req.get_header("Host");
if (host_str.empty() || !host_is_valid(host_str, local_socket_host_port, is_secure)) {
con->set_status(websocketpp::http::status_code::bad_request);
if(!allow_host<T>(req, con))
return;
}
if( !access_control_allow_origin.empty()) {
con->append_header( "Access-Control-Allow-Origin", access_control_allow_origin );
......@@ -258,7 +312,7 @@ namespace eosio {
ws.set_reuse_addr(true);
ws.set_max_http_body_size(max_body_size);
ws.set_http_handler([&](connection_hdl hdl) {
handle_http_request<T>(ws.get_con_from_hdl(hdl));
handle_http_request<detail::asio_with_stub_log<T>>(ws.get_con_from_hdl(hdl));
});
} catch ( const fc::exception& e ){
elog( "http: ${e}", ("e",e.to_detail_string()));
......@@ -275,17 +329,41 @@ namespace eosio {
valid_hosts.emplace(host + ":" + resolved_port_str);
}
void mangle_option_names() {
if(current_http_plugin_defaults.address_config_prefix.empty())
return;
unix_socket_path_option_name.insert(0, current_http_plugin_defaults.address_config_prefix+"-");
http_server_address_option_name.insert(0, current_http_plugin_defaults.address_config_prefix+"-");
https_server_address_option_name.insert(0, current_http_plugin_defaults.address_config_prefix+"-");
}
};
template<>
bool http_plugin_impl::allow_host<detail::asio_local_with_stub_log>(const detail::asio_local_with_stub_log::request_type& req, websocketpp::server<detail::asio_local_with_stub_log>::connection_ptr con) {
return true;
}
http_plugin::http_plugin():my(new http_plugin_impl()){}
http_plugin::~http_plugin(){}
void http_plugin::set_program_options(options_description&, options_description& cfg) {
cfg.add_options()
("http-server-address", bpo::value<string>()->default_value("127.0.0.1:8888"),
"The local IP and port to listen for incoming http connections; set blank to disable.")
my->mangle_option_names();
if(current_http_plugin_defaults.default_unix_socket_path.length())
cfg.add_options()
(my->unix_socket_path_option_name.c_str(), bpo::value<string>()->default_value(current_http_plugin_defaults.default_unix_socket_path),
"The filename (relative to data-dir) to create a unix socket for HTTP RPC; set blank to disable.");
if(current_http_plugin_defaults.default_http_port)
cfg.add_options()
(my->http_server_address_option_name.c_str(), bpo::value<string>()->default_value("127.0.0.1:" + std::to_string(current_http_plugin_defaults.default_http_port)),
"The local IP and port to listen for incoming http connections; set blank to disable.");
else
cfg.add_options()
(my->http_server_address_option_name.c_str(), bpo::value<string>(),
"The local IP and port to listen for incoming http connections; leave blank to disable.");
("https-server-address", bpo::value<string>(),
cfg.add_options()
(my->https_server_address_option_name.c_str(), bpo::value<string>(),
"The local IP and port to listen for incoming https connections; leave blank to disable.")
("https-certificate-chain-file", bpo::value<string>(),
......@@ -334,8 +412,8 @@ namespace eosio {
}
tcp::resolver resolver( app().get_io_service());
if( options.count( "http-server-address" ) && options.at( "http-server-address" ).as<string>().length()) {
string lipstr = options.at( "http-server-address" ).as<string>();
if( options.count( my->http_server_address_option_name ) && options.at( my->http_server_address_option_name ).as<string>().length()) {
string lipstr = options.at( my->http_server_address_option_name ).as<string>();
string host = lipstr.substr( 0, lipstr.find( ':' ));
string port = lipstr.substr( host.size() + 1, lipstr.size());
tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str());
......@@ -353,7 +431,14 @@ namespace eosio {
}
}
if( options.count( "https-server-address" ) && options.at( "https-server-address" ).as<string>().length()) {
if( options.count( my->unix_socket_path_option_name ) && !options.at( my->unix_socket_path_option_name ).as<string>().empty()) {
boost::filesystem::path sock_path = options.at(my->unix_socket_path_option_name).as<string>();
if (sock_path.is_relative())
sock_path = app().data_dir() / sock_path;
my->unix_endpoint = asio::local::stream_protocol::endpoint(sock_path.string());
}
if( options.count( my->https_server_address_option_name ) && options.at( my->https_server_address_option_name ).as<string>().length()) {
if( !options.count( "https-certificate-chain-file" ) ||
options.at( "https-certificate-chain-file" ).as<string>().empty()) {
elog( "https-certificate-chain-file is required for HTTPS" );
......@@ -365,7 +450,7 @@ namespace eosio {
return;
}
string lipstr = options.at( "https-server-address" ).as<string>();
string lipstr = options.at( my->https_server_address_option_name ).as<string>();
string host = lipstr.substr( 0, lipstr.find( ':' ));
string port = lipstr.substr( host.size() + 1, lipstr.size());
tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str());
......@@ -413,6 +498,28 @@ namespace eosio {
}
}
if(my->unix_endpoint) {
try {
my->unix_server.clear_access_channels(websocketpp::log::alevel::all);
my->unix_server.init_asio(&app().get_io_service());
my->unix_server.set_max_http_body_size(my->max_body_size);
my->unix_server.listen(*my->unix_endpoint);
my->unix_server.set_http_handler([&](connection_hdl hdl) {
my->handle_http_request<detail::asio_local_with_stub_log>( my->unix_server.get_con_from_hdl(hdl));
});
my->unix_server.start_accept();
} catch ( const fc::exception& e ){
elog( "unix socket service failed to start: ${e}", ("e",e.to_detail_string()));
throw;
} catch ( const std::exception& e ){
elog( "unix socket service failed to start: ${e}", ("e",e.what()));
throw;
} catch (...) {
elog("error thrown from unix socket io service");
throw;
}
}
if(my->https_listen_endpoint) {
try {
my->create_server_for_endpoint(*my->https_listen_endpoint, my->https_server);
......
......@@ -40,6 +40,19 @@ namespace eosio {
*/
using api_description = std::map<string, url_handler>;
struct http_plugin_defaults {
//If not empty, this string is prepended on to the various configuration
// items for setting listen addresses
string address_config_prefix;
//If empty, unix socket support will be completely disabled. If not empty,
// unix socket support is enabled with the given default path (treated relative
// to the datadir)
string default_unix_socket_path;
//If non 0, HTTP will be enabled by default on the given port number. If
// 0, HTTP will not be enabled by default
uint16_t default_http_port{0};
};
/**
* This plugin starts an HTTP server and dispatches queries to
* registered handles based upon URL. The handler is passed the
......@@ -60,6 +73,9 @@ namespace eosio {
http_plugin();
virtual ~http_plugin();
//must be called before initialize
static void set_defaults(const http_plugin_defaults config);
APPBASE_PLUGIN_REQUIRES()
virtual void set_program_options(options_description&, options_description& cfg) override;
......
file(GLOB HEADERS "include/eosio/test_control_api_plugin/*.hpp")
add_library( test_control_api_plugin
test_control_api_plugin.cpp
${HEADERS} )
target_link_libraries( test_control_api_plugin test_control_plugin chain_plugin http_plugin appbase )
target_include_directories( test_control_api_plugin PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#pragma once
#include <eosio/test_control_plugin/test_control_plugin.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <appbase/application.hpp>
#include <eosio/chain/controller.hpp>
namespace eosio {
using eosio::chain::controller;
using std::unique_ptr;
using namespace appbase;
class test_control_api_plugin : public plugin<test_control_api_plugin> {
public:
APPBASE_PLUGIN_REQUIRES((test_control_plugin)(chain_plugin)(http_plugin))
test_control_api_plugin();
virtual ~test_control_api_plugin();
virtual void set_program_options(options_description&, options_description&) override;
void plugin_initialize(const variables_map&);
void plugin_startup();
void plugin_shutdown();
private:
unique_ptr<class test_control_api_plugin_impl> my;
};
}
/**
* @file
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/test_control_api_plugin/test_control_api_plugin.hpp>
#include <eosio/chain/exceptions.hpp>
#include <fc/io/json.hpp>
namespace eosio {
static appbase::abstract_plugin& _test_control_api_plugin = app().register_plugin<test_control_api_plugin>();
using namespace eosio;
class test_control_api_plugin_impl {
public:
test_control_api_plugin_impl(controller& db)
: db(db) {}
controller& db;
};
test_control_api_plugin::test_control_api_plugin(){}
test_control_api_plugin::~test_control_api_plugin(){}
void test_control_api_plugin::set_program_options(options_description&, options_description&) {}
void test_control_api_plugin::plugin_initialize(const variables_map&) {}
struct async_result_visitor : public fc::visitor<std::string> {
template<typename T>
std::string operator()(const T& v) const {
return fc::json::to_string(v);
}
};
#define CALL(api_name, api_handle, api_namespace, call_name, http_response_code) \
{std::string("/v1/" #api_name "/" #call_name), \
[this, api_handle](string, string body, url_response_callback cb) mutable { \
try { \
if (body.empty()) body = "{}"; \
auto result = api_handle.call_name(fc::json::from_string(body).as<api_namespace::call_name ## _params>()); \
cb(http_response_code, fc::json::to_string(result)); \
} catch (...) { \
http_plugin::handle_exception(#api_name, #call_name, body, cb); \
} \
}}
#define TEST_CONTROL_RW_CALL(call_name, http_response_code) CALL(test_control, rw_api, test_control_apis::read_write, call_name, http_response_code)
void test_control_api_plugin::plugin_startup() {
my.reset(new test_control_api_plugin_impl(app().get_plugin<chain_plugin>().chain()));
auto rw_api = app().get_plugin<test_control_plugin>().get_read_write_api();
app().get_plugin<http_plugin>().add_api({
TEST_CONTROL_RW_CALL(kill_node_on_producer, 202)
});
}
void test_control_api_plugin::plugin_shutdown() {}
}
file(GLOB HEADERS "include/eosio/test_control_plugin/*.hpp")
add_library( test_control_plugin
test_control_plugin.cpp
${HEADERS} )
target_link_libraries( test_control_plugin producer_plugin chain_plugin http_client_plugin appbase eosio_chain eos_utilities )
target_include_directories( test_control_plugin
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
此差异已折叠。
......@@ -43,7 +43,7 @@ void wallet_manager::set_timeout(const std::chrono::seconds& t) {
timeout = t;
auto now = std::chrono::system_clock::now();
timeout_time = now + timeout;
EOS_ASSERT(timeout_time >= now, invalid_lock_timeout_exception, "Overflow on timeout_time, specified ${t}, now ${now}, timeout_time ${timeout_time}",
EOS_ASSERT(timeout_time >= now && timeout_time.time_since_epoch().count() > 0, invalid_lock_timeout_exception, "Overflow on timeout_time, specified ${t}, now ${now}, timeout_time ${timeout_time}",
("t", t.count())("now", now.time_since_epoch().count())("timeout_time", timeout_time.time_since_epoch().count()));
}
......
......@@ -104,6 +104,13 @@ namespace eosio { namespace client { namespace http {
parsed_url parse_url( const string& server_url ) {
parsed_url res;
//unix socket doesn't quite follow classical "URL" rules so deal with it manually
if(boost::algorithm::starts_with(server_url, "unix://")) {
res.scheme = "unix";
res.server = server_url.substr(strlen("unix://"));
return res;
}
//via rfc3986 and modified a bit to suck out the port number
//Sadly this doesn't work for ipv6 addresses
std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
......@@ -125,6 +132,9 @@ namespace eosio { namespace client { namespace http {
}
resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
if(url.scheme == "unix")
return resolved_url(url);
tcp::resolver resolver(context->ios);
boost::system::error_code ec;
auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
......@@ -207,7 +217,12 @@ namespace eosio { namespace client { namespace http {
std::string re;
try {
if(url.scheme == "http") {
if(url.scheme == "unix") {
boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
re = do_txrx(unix_socket, request, status_code);
}
else if(url.scheme == "http") {
tcp::socket socket(cp.context->ios);
do_connect(socket, url);
re = do_txrx(socket, request, status_code);
......
此差异已折叠。
此差异已折叠。
......@@ -86,6 +86,7 @@ int main(int argc, const char **argv) { abi_def output; try {
vector<string> actions;
int result = Tool.run(create_find_macro_factory(contract, actions, abi_context).get());
if(!result) {
output.version = "eosio::abi/1.0";
result = Tool.run(create_factory(abi_verbose, abi_opt_sfs, abi_context, output, contract, actions).get());
if(!result) {
abi_serializer abis(output, fc::seconds(1)); // No risk to client side serialization taking a long time
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册