diff --git a/.gitignore b/.gitignore index 6a12fcf7d..8a32b0f8e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ coverage_report test/bindings/wasm/node_modules/ **/node_modules/ **/package-lock.json +c/src/init/autoregister.h Testing* wasm/test/in3 .in3 @@ -18,6 +19,7 @@ cmake-build-debug/ .idea/ .settings/ # Vim +swift/.swiftpm/ python/htmlcov wasm/test/.nyc_output wasm/test/coverage @@ -31,6 +33,7 @@ bin/ .classpath .project *.class +python/**/*.pyc target/ .cproject .overcommit.yml @@ -49,3 +52,8 @@ c/src/third-party/hidapi/compile c/src/third-party/hidapi/aclocal.m4 c/examples/CMakeLists.txt c/.clang-format +swift/**/.DS_Store +swift/**/.build +swift/**/Packages +swift/**/*.xcodeproj +swift/**/xcuserdata/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f0441a1a9..50c52ceaa 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,6 +30,7 @@ stages: - python - dotnet - java + - swift - test - analysis - deploy @@ -37,7 +38,7 @@ stages: # all the CI files to include and run, they are imported before the CI is started include: - - local: "code-quality.gitlab-ci.yml" + - local: "/.gitlab/code-quality.gitlab-ci.yml" - local: "/c/ci.yml" - local: "/c/ci-analyse.yml" - local: "/c/ci-deploy.yml" diff --git a/code-quality.gitlab-ci.yml b/.gitlab/code-quality.gitlab-ci.yml similarity index 100% rename from code-quality.gitlab-ci.yml rename to .gitlab/code-quality.gitlab-ci.yml diff --git a/.vscode/settings.json b/.vscode/settings.json index 4a4e9ce9b..575572393 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -91,7 +91,12 @@ "sha2.h": "c", "pb_decode.h": "c", "ios": "c", - "filesystem": "c" + "filesystem": "c", + "plugin.h": "c", + "request.h": "c", + "nodeselect_def.h": "c", + "in3_winhttp.h": "c", + "ed25519-hash-custom.h": "c" }, "C_Cpp.errorSquiggles": "Disabled", "C_Cpp.clang_format_fallbackStyle": "{BasedOnStyle: LLVM, AlignConsecutiveAssignments: true, AlignConsecutiveDeclarations: true, AlignTrailingComments: true, AllowShortBlocksOnASingleLine: true, AllowShortCaseLabelsOnASingleLine: true, AllowShortIfStatementsOnASingleLine: true }", diff --git a/CMakeLists.txt b/CMakeLists.txt index 572604744..0362c7516 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,253 +38,15 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/scripts/cmake_mo # project name project(in3) -# options -option(BUILD_DOC "generates the documenation with doxygen." OFF) -option(TAG_VERSION "the tagged version, which should be used" 3.0.0) -option(ETH_NANO "build minimal eth verification.(eth_getTransactionReceipt)" ON) -option(ETH_BASIC "build basic eth verification.(all rpc-calls except eth_call)" ON) -option(ETH_FULL "build full eth verification.(including eth_call)" ON) -option(IPFS "build IPFS verification" ON) -option(COLOR "Enable color codes for debug" ON) -option(BTC "if true, the bitcoin verifiers will be build" ON) -option(IN3API "build the USN-API which offer better interfaces and additional functions on top of the pure verification" ON) -option(USE_PRECOMPUTED_EC "if true the secp256k1 curve uses precompiled tables to boost performance. turning this off makes ecrecover slower, but saves about 37kb." ON) -option(LOGGING "if set logging and human readable error messages will be inculded in th executable, otherwise only the error code is used. (saves about 19kB)" ON) -option(EVM_GAS "if true the gas costs are verified when validating a eth_call. This is a optimization since most calls are only interessted in the result. EVM_GAS would be required if the contract uses gas-dependend op-codes." true) -option(IN3_LIB "if true a shared anmd static library with all in3-modules will be build." ON) -option(TEST "builds the tests and also adds special memory-management, which detects memory leaks, but will cause slower performance" OFF) -option(FAST_MATH "Math optimizations used in the EVM. This will also increase the filesize." OFF) -option(SEGGER_RTT "Use the segger real time transfer terminal as the logging mechanism" OFF) -option(CURL_BLOCKING "if true the curl-request will block until the response is received" OFF) -option(JAVA "build the java-binding (shared-lib and jar-file)" OFF) -option(JAVA_MULTI_LIBS "embedds multiple shared libs in the jar" OFF) -option(WASM "Includes the WASM-Build. In order to build it you need emscripten as toolchain. Usually you also want to turn off other builds in this case." OFF) -option(ASMJS "compiles the code as asm.js." OFF) -option(WASM_EMBED "embedds the wasm as base64-encoded into the js-file" ON) -option(WASM_EMMALLOC "use ther smaller EMSCRIPTEN Malloc, which reduces the size about 10k, but may be a bit slower" ON) -option(WASM_SYNC "intiaializes the WASM synchronisly, which allows to require and use it the same function, but this will not be supported by chrome (4k limit)" OFF) -option(CODE_COVERAGE "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" OFF) -option(GCC_ANALYZER "GCC10 static code analyses" OFF) -option(PAY_ETH "support for direct Eth-Payment" OFF) -option(USE_SCRYPT "integrate scrypt into the build in order to allow decrypt_key for scrypt encoded keys." ON) -option(USE_CURL "if true the curl transport will be built (with a dependency to libcurl)" ON) -option(USE_WINHTTP "if true the winhttp transport will be built (with a dependency to winhttp)" OFF) -option(LEDGER_NANO "include support for nano ledger" OFF) -option(ESP_IDF "include support for ESP-IDF microcontroller framework" OFF) -option(ASSERTIONS "includes assertions into the code, which help track errors but may cost time during runtime" OFF) -OPTION(TRANSPORTS "builds transports, which may require extra libraries." ON) -OPTION(IN3_SERVER "support for proxy server as part of the cmd-tool, which allows to start the cmd-tool with the -p option and listens to the given port for rpc-requests" OFF) -OPTION(CMD "build the comandline utils" ON) -OPTION(RECORDER "enable recording option for reproduce executions" ON) -OPTION(POA "support POA verification including validatorlist updates" OFF) -OPTION(MULTISIG "add capapbility to sign with a multig. Currrently only gnosis safe is supported" ON) -OPTION(ZKSYNC "add RPC-function to handle zksync-payments" ON) -OPTION(ZKCRYPTO_LIB "Path to the static zkcrypto-lib" OFF) -OPTION(SENTRY "Enable Sentry" OFF) -OPTION(BTC_PRE_BPI34 "Enable BTC-Verfification for blocks before BIP34 was activated" ON) -OPTION(PK_SIGNER "Enable Signing with private keys" ON) -OPTION(NODESELECT_DEF "Enable default nodeselect implementation" ON) -OPTION(NODESELECT_DEF_WL "Enable default nodeselect whitelist implementation" ON) -OPTION(PLGN_CLIENT_DATA "Enable client-data plugin" OFF) -OPTION(THREADSAFE "uses mutex to protect shared nodelist access" ON) - - -IF (DEFINED ANDROID_ABI) - set(TRANSPORTS,false) - set(IN3_LIB,false) - set(USE_CURL,false) - set(CMD,false) - set(JAVA,true) - set(RECORDER,false) -ENDIF() - -IF (BTC_PRE_BPI34) - ADD_DEFINITIONS(-DBTC_PRE_BPI34) -ENDIF (BTC_PRE_BPI34) - -IF (POA) - ADD_DEFINITIONS(-DPOA) -ENDIF (POA) - -IF (PK_SIGNER) - ADD_DEFINITIONS(-DPK_SIGNER) - set(IN3_API ${IN3_API} pk_signer) -ENDIF (PK_SIGNER) - -if (USE_PRECOMPUTED_EC) - ADD_DEFINITIONS(-DUSE_PRECOMPUTED_CP=1) -else() - ADD_DEFINITIONS(-DUSE_PRECOMPUTED_CP=0) -endif() - -if (LOGGING) - ADD_DEFINITIONS(-DLOGGING) -endif() - -if (MULTISIG) - ADD_DEFINITIONS(-DMULTISIG) -endif() - -if (ZKSYNC) - ADD_DEFINITIONS(-DZKSYNC) - set(WASM_MODULES ${WASM_MODULES} zksync) - set(IN3_API ${IN3_API} zksync) -endif() - - -if(ETH_FULL) - ADD_DEFINITIONS(-DETH_FULL) - set(IN3_VERIFIER eth_full) - set(ETH_BASIC true) - set(ETH_NANO true) -elseif(ETH_BASIC) - ADD_DEFINITIONS(-DETH_BASIC) - set(IN3_VERIFIER eth_basic) - set(ETH_NANO true) -elseif(ETH_NANO) - ADD_DEFINITIONS(-DETH_NANO) - set(IN3_VERIFIER eth_nano) -endif() - -if (ETH_NANO) - set(WASM_MODULES ${WASM_MODULES} eth) -endif() - -if(IN3API) - ADD_DEFINITIONS(-DETH_API) - set(IN3_API ${IN3_API} eth_api) -endif() - -if (ESP_IDF) - ADD_DEFINITIONS(-DESP_IDF) -endif() - -if(PAY_ETH) - ADD_DEFINITIONS(-DPAY_ETH -DPAY) - set(IN3_API ${IN3_API} pay_eth) -endif() - -if(IPFS) - ADD_DEFINITIONS(-DIPFS) - set(IN3_VERIFIER ${IN3_VERIFIER} ipfs) - set(WASM_MODULES ${WASM_MODULES} ipfs) - - if(IN3API) - set(IN3_API ${IN3_API} ipfs_api) - endif() -endif() - -if(BTC) - ADD_DEFINITIONS(-DBTC) - set(IN3_VERIFIER ${IN3_VERIFIER} btc) - set(WASM_MODULES ${WASM_MODULES} btc) - if(IN3API) - set(IN3_API ${IN3_API} btc_api) - endif() -endif() - -if(LEDGER_NANO) - add_definitions(-DLEDGER_NANO) -endif() - -if(COLOR AND NOT (MSVC OR MSYS OR MINGW)) - ADD_DEFINITIONS(-DLOG_USE_COLOR) -endif() - - -if(CMAKE_BUILD_TYPE MATCHES Debug) - ADD_DEFINITIONS(-DDEBUG) -endif(CMAKE_BUILD_TYPE MATCHES Debug) - -if(EVM_GAS) - ADD_DEFINITIONS(-DEVM_GAS) -endif(EVM_GAS) - -if(FAST_MATH) - ADD_DEFINITIONS(-DIN3_MATH_FAST) -else() - ADD_DEFINITIONS(-DIN3_MATH_LITE) -endif(FAST_MATH) - -if(SEGGER_RTT) - ADD_DEFINITIONS(-DSEGGER_RTT) -endif(SEGGER_RTT) - -if(RECORDER) - ADD_DEFINITIONS(-DRECORDER) -endif(RECORDER) - -if (SENTRY) - ADD_DEFINITIONS(-DSENTRY) - set(IN3_API ${IN3_API} in3_sentry) -endif() - -if (NODESELECT_DEF) - ADD_DEFINITIONS(-DNODESELECT_DEF) - if (NODESELECT_DEF_WL) - ADD_DEFINITIONS(-DNODESELECT_DEF_WL) - endif() - set(IN3_NODESELECT ${IN3_NODESELECT} nodeselect_def) -endif() - -# handle version -if (TAG_VERSION) - set(PROJECT_VERSION "${TAG_VERSION}") -else(TAG_VERSION) - set(PROJECT_VERSION "3.0.0-local") -endif(TAG_VERSION) - -MESSAGE(STATUS "Building version ${PROJECT_VERSION}") - -string(REPLACE "." ";" VERSION_LIST ${PROJECT_VERSION}) -list(GET VERSION_LIST 0 PROJECT_VERSION_MAJOR) -list(GET VERSION_LIST 1 PROJECT_VERSION_MINOR) -list(GET VERSION_LIST 2 PROJECT_VERSION_PATCH) - -ADD_DEFINITIONS("-DIN3_VERSION=\"${PROJECT_VERSION}\"") -ADD_DEFINITIONS(-DIN3_VERSION_MAJOR=${PROJECT_VERSION_MINOR}) -ADD_DEFINITIONS(-DIN3_VERSION_MINOR=${PROJECT_VERSION_MINOR}) -ADD_DEFINITIONS(-DIN3_VERSION_PATCH=${PROJECT_VERSION_PATCH}) - - -# define output dir structure -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - +include("c/options.cmake") IF (WASM) - set(TEST false) - set(RECORDER false) - set(TRANSPORTS false) - set(BUILD_DOC false) - set(IN3_LIB false) - set(CMD false) - set(USE_CURL false) - set(USE_WINHTTP false) - set(THREADSAFE false) - ADD_DEFINITIONS(-DWASM) add_subdirectory(wasm/src) ENDIF (WASM) -if (THREADSAFE) - ADD_DEFINITIONS(-DTHREADSAFE) -ENDIF() - # build tests if(TEST) - ADD_DEFINITIONS(-DTEST) - ADD_DEFINITIONS(-DIN3_EXPORT_TEST=) - ADD_DEFINITIONS(-DIN3_IMPORT_TEST=extern) - ADD_DEFINITIONS(-DDEBUG) - SET(CMAKE_BUILD_TYPE Debug) - enable_testing() add_subdirectory(c/test) - add_custom_target(ptest COMMAND ${CMAKE_CTEST_COMMAND} -j 8) - add_custom_target(rtest COMMAND ${CMAKE_CTEST_COMMAND} -V ) -else(TEST) - ADD_DEFINITIONS(-DIN3_EXPORT_TEST=static) - ADD_DEFINITIONS(-DIN3_IMPORT_TEST=) endif(TEST) add_subdirectory(c) diff --git a/Dockerfile b/Dockerfile index dd0261c47..9065f4aab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,7 @@ # This file is part of the Incubed project. # Sources: https://github.com/blockchainsllc/in3 # -# Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC +# Copyright (C) 2018-2021 slock.it GmbH, Blockchains LLC # # # COMMERCIAL LICENSE USAGE @@ -38,7 +38,7 @@ COPY c /in3/c/ COPY scripts /in3/scripts/ WORKDIR /in3/ USER root -RUN apt-get update && apt-get install -y libcurl4-openssl-dev curl cmake build-essential +RUN apt-get clean && apt-get update && apt-get install -y libcurl4-openssl-dev curl cmake build-essential RUN curl https://sh.rustup.rs -sSf | bash -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" RUN cd /in3/ && mkdir build && cd build && cmake -DZKCRYPTO_LIB=true -DCMAKE_BUILD_TYPE=MinSizeRel -DIN3_SERVER=true .. && make in3 @@ -46,7 +46,7 @@ RUN cd /in3/ && mkdir build && cd build && cmake -DZKCRYPTO_LIB=true -DCMAKE_B FROM debian:buster-slim COPY --from=build /in3/build/bin/in3 /bin/in3 -RUN apt-get update && apt-get install -y curl +RUN apt-get clean && apt-get update && apt-get install -y curl EXPOSE 8545 ENTRYPOINT ["/bin/in3"] CMD ["--help"] diff --git a/c/CMakeLists.txt b/c/CMakeLists.txt index 8bd5899dc..6ccf7e9d8 100644 --- a/c/CMakeLists.txt +++ b/c/CMakeLists.txt @@ -46,7 +46,7 @@ if (GCC_ANALYZER) set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fanalyzer -Werror") endif() -IF (TRANSPORTS) +IF (TRANSPORTS AND NOT SWIFT) ADD_DEFINITIONS(-DTRANSPORTS) if (USE_CURL) ADD_DEFINITIONS(-DUSE_CURL) @@ -61,7 +61,9 @@ IF (TRANSPORTS) set(IN3_TRANSPORT ${IN3_TRANSPORT} transport_http) endif (USE_CURL) add_subdirectory(src/transport) -ENDIF (TRANSPORTS) +ENDIF (TRANSPORTS AND NOT SWIFT) + +ADD_DEFINITIONS("-DIN3_AUTOINIT_PATH=\"${CMAKE_BINARY_DIR}/autoregister.h\"") add_subdirectory(src/core) add_subdirectory(src/init) @@ -74,41 +76,60 @@ add_subdirectory(src/tools) add_subdirectory(src/nodeselect) add_subdirectory(docs) +# generate the init-module +get_property(modules GLOBAL PROPERTY IN3_REGISTERS) +foreach(m IN LISTS modules) + set(REGISTER_DEF "${REGISTER_DEF}in3_ret_t ${m} (in3_t*);\n") + set(REGISTER_CALL "${REGISTER_CALL} in3_register_default(${m});\n") +endforeach() +file(WRITE ${CMAKE_BINARY_DIR}/autoregister.h "${REGISTER_DEF}\n\nstatic void auto_init() {\n${REGISTER_CALL}}\n") + +# define lib dir link_directories(${CMAKE_BINARY_DIR}/lib/) # create the library if (IN3_LIB) get_property(IN3_LIBS GLOBAL PROPERTY IN3_OBJECTS) - # create the libraries + # create static libraries add_library(in3_bundle STATIC ${IN3_LIBS} ) - add_library(in3_lib SHARED ${IN3_LIBS} ) set_target_properties(in3_bundle PROPERTIES OUTPUT_NAME "in3") - set_target_properties(in3_lib PROPERTIES OUTPUT_NAME "in3") - target_link_libraries(in3_lib ${IN3_TRANSPORT}) - if( LEDGER_NANO) - target_link_libraries(in3_lib ledger_signer) - endif() - if(SENTRY) - target_link_libraries(in3_lib sentry) - endif() - if (ZKSYNC AND ZKCRYPTO_LIB ) target_link_libraries(in3_bundle zk_crypto_rs ) - target_link_libraries(in3_lib zk_crypto_rs ) endif() - - - # install INSTALL(TARGETS in3_bundle - DESTINATION "lib" + DESTINATION "lib" ) - INSTALL(TARGETS in3_lib - DESTINATION lib - PERMISSIONS - OWNER_READ OWNER_WRITE OWNER_EXECUTE - GROUP_READ GROUP_EXECUTE - WORLD_READ WORLD_EXECUTE) + + IF (NOT SWIFT) + + # create shared libraries + add_library(in3_lib SHARED ${IN3_LIBS} ) + set_target_properties(in3_lib PROPERTIES OUTPUT_NAME "in3") + target_link_libraries(in3_lib ${IN3_TRANSPORT}) + + if( LEDGER_NANO) + target_link_libraries(in3_lib ledger_signer) + endif() + if(SENTRY) + target_link_libraries(in3_lib sentry) + endif() + + if (ZKSYNC AND ZKCRYPTO_LIB ) + target_link_libraries(in3_lib zk_crypto_rs ) + endif() + + + # install + INSTALL(TARGETS in3_lib + DESTINATION lib + PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE + WORLD_READ WORLD_EXECUTE) + ENDIF(NOT SWIFT) + + INSTALL ( DIRECTORY ${CMAKE_SOURCE_DIR}/c/include/in3 DESTINATION include diff --git a/c/ci-analyse.yml b/c/ci-analyse.yml index 553a71da6..9ce174630 100644 --- a/c/ci-analyse.yml +++ b/c/ci-analyse.yml @@ -18,7 +18,7 @@ coverage: script: - mkdir cov_build; cd cov_build - cmake -DIN3API=true -DIN3_LIB=false -DUSE_CURL=false -DTEST=true -DZKSYNC=true -DCODE_COVERAGE=true -DUSE_SEGGER_RTT=false -DTRANSPORTS=false -DCMAKE_BUILD_TYPE=Debug .. - - make -j8 && make ptest + - make -j8 && make CTEST_OUTPUT_ON_FAILURE=1 test - ../scripts/lcov_report.sh | xargs llvm-cov report - ../scripts/lcov_report.sh | xargs llvm-cov show -show-line-counts-or-regions -output-dir=ccov/all-merged -format=html - ../scripts/lcov_report.sh | xargs llvm-cov export -format=lcov > ccov/all-merged/lcov.info @@ -127,7 +127,7 @@ cpd: tags: - short-jobs script: - - cpd --minimum-tokens 180 --language cpp --exclude c/src/third-party --exclude c/src/signer/iamo-zksync/iamo_deploy.h --files c/src + - cpd --minimum-tokens 180 --language cpp --exclude c/src/third-party --exclude c/src/signer/zk_wallet/iamo_deploy.h --files c/src - cpd --minimum-tokens 150 --language java --files java/src - cpd --minimum-tokens 150 --language python --files python diff --git a/c/ci-deploy.yml b/c/ci-deploy.yml index e818bad61..a172d596e 100644 --- a/c/ci-deploy.yml +++ b/c/ci-deploy.yml @@ -63,10 +63,9 @@ readthedocs: - chmod 777 bin/* - cd ../wasm/src - cp ../../in3_wasm/index.d.ts . - - npm --cache .npm i typedoc - - node_modules/.bin/typedoc --includeDeclarations --ignoreCompilerErrors --readme none --target ES6 --mode 'modules' --excludeExternals --json doc.json index.d.ts + - typedoc --includeDeclarations --ignoreCompilerErrors --readme none --target ES6 --mode 'modules' --excludeExternals --json doc.json index.d.ts - cat doc.json | ../../generator/bin/slockit-docu index.d blockchainsllc/in3/blob/master/wasm/src "$PRE_DOC" > ../../doc/docs/api-wasm.md - - cd ../../doc/docs && make html && make latexpdf && make text + - cd ../../doc/docs && make html && make text artifacts: paths: - doc/build diff --git a/c/include/in3.swift.h b/c/include/in3.swift.h new file mode 100644 index 000000000..2dabd57d0 --- /dev/null +++ b/c/include/in3.swift.h @@ -0,0 +1,17 @@ +// AUTO-GENERATED FILE +// See scripts/build_includeh.sh +#include "../src/core/client/request_internal.h" +#include "../src/signer/pk-signer/signer.h" +#include "../src/tools/clientdata/client_data.h" +#include "../src/api/btc/btc_api.h" +#include "in3/bytes.h" +#include "in3/client.h" +#include "in3/request.h" +#include "in3/plugin.h" +#include "in3/error.h" +#include "in3/eth_api.h" +#include "in3/in3_curl.h" +#include "in3/in3_init.h" +#include "in3/log.h" +#include "../src/tools/swift/swift.h" +#include "../src/third-party/tommath/tommath.h" diff --git a/c/include/in3/bytes.h b/c/include/in3/bytes.h index de07d151f..0aa9fe3a1 100644 --- a/c/include/in3/bytes.h +++ b/c/include/in3/bytes.h @@ -123,6 +123,12 @@ NONULL static inline void b_optimize_len(bytes_t* b) { } } +static inline int b_compare(bytes_t a, bytes_t b) { + return (a.len == b.len) + ? memcmp(a.data, b.data, a.len) + : ((int) a.len) - ((int) b.len); +} + #define b_to_stack(d) \ { \ bytes_t o = d; \ diff --git a/c/include/in3/pay_eth.h b/c/include/in3/core_api.h similarity index 66% rename from c/include/in3/pay_eth.h rename to c/include/in3/core_api.h index e5eb3b5a8..90a084ac0 100644 --- a/c/include/in3/pay_eth.h +++ b/c/include/in3/core_api.h @@ -2,7 +2,7 @@ * This file is part of the Incubed project. * Sources: https://github.com/blockchainsllc/in3 * - * Copyright (C) 2018-2019 slock.it GmbH, Blockchains LLC + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC * * * COMMERCIAL LICENSE USAGE @@ -34,51 +34,27 @@ // @PUBLIC_HEADER /** @file - * USN API. + * Ethereum API. * - * This header-file defines easy to use function, which are verifying USN-Messages. + * This header-file defines easy to use function, which are preparing the JSON-RPC-Request, which is then executed and verified by the incubed-client. * */ -#ifndef PAY_ETH_H -#define PAY_ETH_H +#ifndef CORE_API_H +#define CORE_API_H #ifdef __cplusplus extern "C" { #endif -#include "client.h" -#include "plugin.h" - -typedef struct in3_pay_eth_node { - address_t address; - uint32_t price; - uint64_t payed; - struct in3_pay_eth_node* next; -} in3_pay_eth_node_t; -typedef struct { - uint64_t bulk_size; - uint64_t max_price; - uint64_t nonce; - uint64_t gas_price; - in3_pay_eth_node_t* nodes; -} in3_pay_eth_t; +#include "client.h" +#include "api_utils.h" /** - * Eth payment implementation + * register core-api */ -in3_ret_t in3_pay_eth(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx); +in3_ret_t in3_register_core_api(in3_t* c); -/** - * get access to internal plugin data if registered - */ -static inline in3_pay_eth_t* in3_pay_eth_data(in3_t* c) { - return in3_plugin_get_data(c, in3_pay_eth); -} - -/** - * registers the Eth payment plugin - */ -in3_ret_t in3_register_pay_eth(in3_t* c); #ifdef __cplusplus } #endif -#endif \ No newline at end of file + +#endif diff --git a/c/include/in3/data.h b/c/include/in3/data.h index 016a41daa..1f9126a4e 100644 --- a/c/include/in3/data.h +++ b/c/include/in3/data.h @@ -151,10 +151,10 @@ NONULL d_token_t* json_create_bool(json_ctx_t* jp, bool value); NONULL d_token_t* json_create_int(json_ctx_t* jp, uint64_t value); NONULL d_token_t* json_create_string(json_ctx_t* jp, char* value, int len); NONULL d_token_t* json_create_bytes(json_ctx_t* jp, bytes_t value); -NONULL d_token_t* json_create_object(json_ctx_t* jp); -NONULL d_token_t* json_create_array(json_ctx_t* jp); -NONULL d_token_t* json_object_add_prop(d_token_t* object, d_key_t key, d_token_t* value); -NONULL d_token_t* json_array_add_value(d_token_t* object, d_token_t* value); +NONULL int json_create_object(json_ctx_t* jp); +NONULL int json_create_array(json_ctx_t* jp); +NONULL void json_object_add_prop(json_ctx_t* jp, int ob_index, d_key_t key, d_token_t* value); +NONULL void json_array_add_value(json_ctx_t* jp, int parent_index, d_token_t* value); // Helper function to map string to 2byte keys (only for tests or debugging) char* d_get_keystr(json_ctx_t* json, d_key_t k); /**< returns the string for a key. This only works for index keys or known keys! */ diff --git a/c/include/in3/eth_api.h b/c/include/in3/eth_api.h index f9a70d6e6..aa07000ab 100644 --- a/c/include/in3/eth_api.h +++ b/c/include/in3/eth_api.h @@ -183,6 +183,7 @@ char* eth_wait_for_receipt(in3_t* in3, bytes32_t tx_hash); void eth_log_free(eth_log_t* log); /**< Frees a eth_log_t object */ void eth_tx_receipt_free(eth_tx_receipt_t* txr); /**< Frees a eth_tx_receipt_t object */ int string_val_to_bytes(char* val, char* unit, bytes32_t target); /**< reades the string as hex or decimal and converts it into bytes. the value may also contains a suffix as unit like '1.5eth` which will convert it into wei. the target-pointer must be at least as big as the strlen. The length of the bytes will be returned or a negative value in case of an error.*/ +char* bytes_to_string_val(bytes_t wei, int exp, int digits); /**< converts the bytes value to a decimal string */ in3_ret_t in3_register_eth_api(in3_t* c); /**< this function should only be called once and will register the eth-API verifier.*/ #ifdef __cplusplus } diff --git a/c/include/in3/eth_basic.h b/c/include/in3/eth_basic.h new file mode 100644 index 000000000..1cfb76949 --- /dev/null +++ b/c/include/in3/eth_basic.h @@ -0,0 +1,148 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ +// @PUBLIC_HEADER +/** @file + * Ethereum Nanon verification. + * */ + +#ifndef in3_eth_basic_h__ +#define in3_eth_basic_h__ + +#include "plugin.h" + +/** + * Filter type used internally when managing filters. + */ +typedef enum { + FILTER_EVENT = 0, /**< Event filter */ + FILTER_BLOCK = 1, /**< Block filter */ + FILTER_PENDING = 2, /**< Pending filter (Unsupported) */ +} in3_filter_type_t; + +typedef struct in3_filter_t_ { + bool is_first_usage; /**< if true the filter was not used previously */ + in3_filter_type_t type; /**< filter type: (event, block or pending) */ + uint64_t last_block; /**< block no. when filter was created OR eth_getFilterChanges was called */ + char* options; /**< associated filter options */ + void (*release)(struct in3_filter_t_* f); /**< method to release owned resources */ +} in3_filter_t; + +/** + * Handler which is added to client config in order to handle filter. + */ +typedef struct in3_filter_handler_t_ { + in3_filter_t** array; /** array of filters */ + size_t count; /** counter for filters */ +} in3_filter_handler_t; + +/** + * returns the filters + */ +in3_filter_handler_t* eth_basic_get_filters(in3_t* c); + +/** + * verifies internal tx-values. + */ +in3_ret_t eth_verify_tx_values(in3_vctx_t* vc, d_token_t* tx, bytes_t* raw); + +/** + * verifies a transaction. + */ +in3_ret_t eth_verify_eth_getTransaction(in3_vctx_t* vc, bytes_t* tx_hash); + +/** + * verifies a transaction by block hash/number and id. + */ +in3_ret_t eth_verify_eth_getTransactionByBlock(in3_vctx_t* vc, d_token_t* blk, uint32_t tx_idx); + +/** + * verify account-proofs + */ +in3_ret_t eth_verify_account_proof(in3_vctx_t* vc); + +/** + * verifies a block + */ +in3_ret_t eth_verify_eth_getBlock(in3_vctx_t* vc, bytes_t* block_hash, uint64_t blockNumber); + +/** + * verifies block transaction count by number or hash + */ +in3_ret_t eth_verify_eth_getBlockTransactionCount(in3_vctx_t* vc, bytes_t* block_hash, uint64_t blockNumber); + +/** + * this function should only be called once and will register the eth-nano verifier. + */ +in3_ret_t in3_register_eth_basic(in3_t* c); + +/** + * verify logs + */ +in3_ret_t eth_verify_eth_getLog(in3_vctx_t* vc, int l_logs); + +/** + * prepares a transaction and writes the data to the dst-bytes. In case of success, you MUST free only the data-pointer of the dst. + */ +in3_ret_t eth_prepare_unsigned_tx(d_token_t* tx, /**< a json-token desribing the transaction */ + in3_req_t* req, /**< the current context */ + bytes_t* dst /**< the bytes to write the result to. */ +); + +/** + * signs a unsigned raw transaction and writes the raw data to the dst-bytes. In case of success, you MUST free only the data-pointer of the dst. + */ +in3_ret_t eth_sign_raw_tx(bytes_t raw_tx, /**< the unsigned raw transaction to sign */ + in3_req_t* req, /**< the current context */ + address_t from, /**< the address of the account to sign with */ + bytes_t* dst /**< the bytes to write the result to. */ +); + +/** + * expects a req-object for a transaction and converts it into a sendRawTransaction after signing. + */ +in3_ret_t handle_eth_sendTransaction(in3_req_t* req, /**< the current context */ + d_token_t* req_data /**< the request */ +); + +/** + * returns a pointer to 32 bytes marking a empty hash (keccakc(0x)) + */ +const uint8_t* empty_hash(); + +/** + * minimum signer for the wallet, returns the signed message which needs to be freed + */ +RETURNS_NONULL NONULL char* eth_wallet_sign(const char* key, const char* data); + +#endif // in3_eth_basic_h__ \ No newline at end of file diff --git a/c/include/in3/eth_full.h b/c/include/in3/eth_full.h new file mode 100644 index 000000000..4c075107f --- /dev/null +++ b/c/include/in3/eth_full.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ +// @PUBLIC_HEADER +/** @file + * Ethereum Nanon verification. + * */ + +#ifndef in3_eth_full_h__ +#define in3_eth_full_h__ + +#include "plugin.h" + +/** + * this function should only be called once and will register the eth-full verifier. + */ +in3_ret_t in3_register_eth_full(in3_t* c); + +#endif // in3_eth_full_h__ \ No newline at end of file diff --git a/c/include/in3/eth_nano.h b/c/include/in3/eth_nano.h new file mode 100644 index 000000000..a777f60e4 --- /dev/null +++ b/c/include/in3/eth_nano.h @@ -0,0 +1,82 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ +// @PUBLIC_HEADER +/** @file + * Ethereum Nanon verification. + * */ + +#ifndef in3_eth_nano_h__ +#define in3_eth_nano_h__ + +#include "plugin.h" + +/** entry-function to execute the verification context. */ +NONULL in3_ret_t in3_verify_eth_nano(void* p_data, in3_plugin_act_t action, void* pctx); + +/** verifies a blockheader. */ +NONULL_FOR((1, 2)) +in3_ret_t eth_verify_blockheader(in3_vctx_t* vc, bytes_t* header, bytes_t* expected_blockhash); + +/** + * verifies a single signature blockheader. + * + * This function will return a positive integer with a bitmask holding the bit set according to the address that signed it. + * This is based on the signatiures in the request-config. + * + */ +NONULL unsigned int eth_verify_signature(in3_vctx_t* vc, bytes_t* msg_hash, d_token_t* sig); + +/** + * returns the address of the signature if the msg_hash is correct + */ +NONULL bytes_t* ecrecover_signature(bytes_t* msg_hash, d_token_t* sig); + +/** + * verifies a transaction receipt. + */ +NONULL in3_ret_t eth_verify_eth_getTransactionReceipt(in3_vctx_t* vc, bytes_t* tx_hash); + +/** + * this function should only be called once and will register the eth-nano verifier. + */ +NONULL in3_ret_t in3_register_eth_nano(in3_t* c); + +/** + * helper function to rlp-encode the transaction_index. + * + * The result must be freed after use! + */ +bytes_t* create_tx_path(uint32_t index); + +#endif // in3_eth_nano_h__ \ No newline at end of file diff --git a/c/include/in3/nodelist.h b/c/include/in3/nodelist.h new file mode 100644 index 000000000..1db00b740 --- /dev/null +++ b/c/include/in3/nodelist.h @@ -0,0 +1,322 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ + +/** + * handles nodelists. + * + * */ + +#include "client.h" +#include "request.h" +#include "log.h" +#include "mem.h" +#include + +#ifndef NODELIST_H +#define NODELIST_H + +#ifdef THREADSAFE +#if defined(_MSC_VER) || defined(__MINGW32__) +#include +typedef HANDLE in3_mutex_t; +#define MUTEX_INIT(mutex) mutex = CreateMutex(NULL, FALSE, NULL); +#define MUTEX_LOCK(mutex) WaitForSingleObject(mutex, INFINITE); +#define MUTEX_UNLOCK(mutex) ReleaseMutex(mutex); +#define MUTEX_FREE(mutex) CloseHandle(mutex); +#else +#include +typedef pthread_mutex_t in3_mutex_t; +#define MUTEX_INIT(mutex) \ + { \ + pthread_mutexattr_t attr; \ + pthread_mutexattr_init(&attr); \ + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ + pthread_mutex_init(&(mutex), &attr); \ + } +#define MUTEX_LOCK(mutex) pthread_mutex_lock(&(mutex)); +#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&(mutex)); +#define MUTEX_FREE(mutex) pthread_mutex_destroy(&(mutex)); +#endif +#endif + +/** + * a list of node attributes (mostly used internally) + */ +typedef enum { + ATTR_WHITELISTED = 1, /**< indicates if node exists in whiteList */ + ATTR_BOOT_NODE = 2, /**< used to avoid filtering manually added nodes before first nodeList update */ +} in3_node_attr_type_t; + +/** incubed node-configuration. + * + * These information are read from the Registry contract and stored in this struct representing a server or node. + */ +typedef struct in3_node { + address_t address; /**< address of the server */ + bool blocked; /**< if true this node has been blocked for sending wrong responses */ + uint_fast16_t index; /**< index within the nodelist, also used in the contract as key */ + uint_fast16_t capacity; /**< the maximal capacity able to handle */ + uint64_t deposit; /**< the deposit stored in the registry contract, which this would lose if it sends a wrong blockhash */ + in3_node_props_t props; /**< used to identify the capabilities of the node. See in3_node_props_type_t in nodelist.h */ + char* url; /**< the url of the node */ + uint_fast8_t attrs; /**< bitmask of internal attributes */ +} in3_node_t; + +/** + * Weight or reputation of a node. + * + * Based on the past performance of the node a weight is calculated given faster nodes a higher weight + * and chance when selecting the next node from the nodelist. + * These weights will also be stored in the cache (if available) + */ +typedef struct in3_node_weight { + uint32_t response_count; /**< counter for responses */ + uint32_t total_response_time; /**< total of all response times */ + uint64_t blacklisted_until; /**< if >0 this node is blacklisted until k. k is a unix timestamp */ +} in3_node_weight_t; + +/** + * defines a whitelist structure used for the nodelist. + */ +typedef struct in3_whitelist { + bool needs_update; /**< if true the nodelist should be updated and will trigger a `in3_nodeList`-request before the next request is send. */ + uint64_t last_block; /**< last blocknumber the whiteList was updated, which is used to detect changed in the whitelist */ + address_t contract; /**< address of whiteList contract. If specified, whiteList is always auto-updated and manual whiteList is overridden */ + bytes_t addresses; /**< serialized list of node addresses that constitute the whiteList */ +} in3_whitelist_t; + +typedef enum { + NODE_PROP_PROOF = 0x1, /**< filter out nodes which are providing no proof */ + NODE_PROP_MULTICHAIN = 0x2, /**< filter out nodes other then which have capability of the same RPC endpoint may also accept requests for different chains */ + NODE_PROP_ARCHIVE = 0x4, /**< filter out non-archive supporting nodes */ + NODE_PROP_HTTP = 0x8, /**< filter out non-http nodes */ + NODE_PROP_BINARY = 0x10, /**< filter out nodes that don't support binary encoding */ + NODE_PROP_ONION = 0x20, /**< filter out non-onion nodes */ + NODE_PROP_SIGNER = 0x40, /**< filter out non-signer nodes */ + NODE_PROP_DATA = 0x80, /**< filter out non-data provider nodes */ + NODE_PROP_STATS = 0x100, /**< filter out nodes that do not provide stats */ + NODE_PROP_MIN_BLOCK_HEIGHT = 0x400, /**< filter out nodes that will sign blocks with lower min block height than specified */ +} in3_node_props_type_t; + +typedef struct { + in3_node_props_t props; + d_token_t* nodes; + node_match_t* exclusions; +} in3_node_filter_t; + +typedef struct node_offline_ { + in3_node_t* offline; + address_t reporter; + struct node_offline_* next; +} node_offline_t; + +typedef struct in3_nodeselect_def { + bool dirty; /**< indicates whether the nodelist has been modified after last read from cache */ + uint16_t avg_block_time; /**< average block time (seconds) for this data (calculated internally) */ + unsigned int nodelist_length; /**< number of nodes in the nodeList */ + uint64_t last_block; /**< last blocknumber the nodeList was updated, which is used to detect changed in the nodelist*/ + address_t contract; /**< the address of the registry contract */ + bytes32_t registry_id; /**< the identifier of the registry */ + in3_node_t* nodelist; /**< array of nodes */ + in3_node_weight_t* weights; /**< stats and weights recorded for each node */ + bytes_t** init_addresses; /**< array of addresses of nodes that should always part of the nodeList */ + node_offline_t* offlines; /**< linked-list of offline nodes */ + +#ifdef NODESELECT_DEF_WL + in3_whitelist_t* whitelist; /**< if set the whitelist of the addresses. */ +#endif + + struct { + uint64_t exp_last_block; /**< the last_block when the nodelist last changed reported by this node */ + uint64_t timestamp; /**< approx. time when nodelist must be updated (i.e. when reported last_block will be considered final) */ + address_t node; /**< node that reported the last_block which necessitated a nodeList update */ + } * nodelist_upd8_params; + + chain_id_t chain_id; /**< the chain_id of the data */ + struct in3_nodeselect_def* next; /**< the next in the linked list */ + uint32_t ref_counter; /**< number of client using this nodelist */ + bytes_t* pre_address_filter; /**< addresses of allowed list (usually because those nodes where paid for) */ + +#ifdef THREADSAFE + in3_mutex_t mutex; /**< mutex to lock this nodelist */ +#endif +} in3_nodeselect_def_t; + +/** config for each client pointing to the global data*/ +typedef struct in3_nodeselect_config { + in3_nodeselect_def_t* data; /**< points to the global nodelist data*/ + in3_node_props_t node_props; /**< used to identify the capabilities of the node. */ + uint64_t min_deposit; /**< min stake of the server. Only nodes owning at least this amount will be chosen. */ + uint16_t node_limit; /**< the limit of nodes to store in the client. */ + uint8_t request_count; /**< the number of request send when getting a first answer */ +} in3_nodeselect_config_t; + +/** returns the nodelistwrapper.*/ +NONULL in3_nodeselect_config_t* in3_get_nodelist(in3_t* c); + +/** removes all nodes and their weights from the nodelist */ +NONULL void in3_nodelist_clear(in3_nodeselect_def_t* data); + +#ifdef NODESELECT_DEF_WL +/** removes all nodes and their weights from the nodelist */ +NONULL void in3_whitelist_clear(in3_whitelist_t* data); + +/** updates all whitelisted flags in the nodelist */ +NONULL void in3_client_run_chain_whitelisting(in3_nodeselect_def_t* data); +#endif + +/** check if the nodelist is up to date. + * + * if not it will fetch a new version first (if the needs_update-flag is set). + */ +NONULL in3_ret_t in3_node_list_get(in3_req_t* req, in3_nodeselect_def_t* data, bool update, in3_node_t** nodelist, unsigned int* nodelist_length, in3_node_weight_t** weights); + +/** + * filters and fills the weights on a returned linked list. + */ +NONULL_FOR((1, 2, 3, 4, 7, 8)) +node_match_t* in3_node_list_fill_weight(in3_t* c, in3_nodeselect_config_t* w, in3_node_t* all_nodes, in3_node_weight_t* weights, unsigned int len, uint64_t now, uint32_t* total_weight, unsigned int* total_found, const in3_node_filter_t* filter, bytes_t* pre_filter); + +/** + * calculates the weight for a node. + */ +NONULL uint32_t in3_node_calculate_weight(in3_node_weight_t* n, uint32_t capa, uint64_t now); +/** + * picks (based on the config) a random number of nodes and returns them as weightslist. + */ +NONULL_FOR((1, 2, 3)) +in3_ret_t in3_node_list_pick_nodes(in3_req_t* req, in3_nodeselect_config_t* w, node_match_t** nodes, unsigned int request_count, const in3_node_filter_t* filter); + +/** + * forces the client to update the nodelist + */ +in3_ret_t update_nodes(in3_t* c, in3_nodeselect_def_t* data); + +#define NODE_FILTER_INIT \ + (in3_node_filter_t) { .props = 0, .nodes = NULL } + +/** + * Initializer for in3_node_props_t + */ +#define in3_node_props_init(np) *(np) = 0 + +/** + * setter method for interacting with in3_node_props_t. + */ +NONULL void in3_node_props_set(in3_node_props_t* node_props, /**< pointer to the properties to change */ + in3_node_props_type_t type, /**< key or type of the property */ + uint8_t value /**< value to set */ +); + +/** + * returns the value of the specified property-type. + * @return value as a number + */ +static inline uint32_t in3_node_props_get(in3_node_props_t np, /**< property to read from */ + in3_node_props_type_t t) { /**< the value to extract */ + return ((t == NODE_PROP_MIN_BLOCK_HEIGHT) ? ((np >> 32U) & 0xFFU) : !!(np & t)); +} + +/** + * checks if the given type is set in the properties + * @return true if set + */ +static inline bool in3_node_props_matches(in3_node_props_t np, /**< property to read from */ + in3_node_props_type_t t) { /**< the value to extract */ + return !!(np & t); +} + +NONULL static inline bool nodelist_first_upd8(const in3_nodeselect_def_t* data) { + return (data->nodelist_upd8_params != NULL && data->nodelist_upd8_params->exp_last_block == 0); +} + +NONULL static inline bool nodelist_not_first_upd8(const in3_nodeselect_def_t* data) { + return (data->nodelist_upd8_params != NULL && data->nodelist_upd8_params->exp_last_block != 0); +} + +NONULL static inline in3_node_t* get_node_idx(const in3_nodeselect_def_t* data, unsigned int index) { + return index < data->nodelist_length ? data->nodelist + index : NULL; +} + +NONULL static inline in3_node_weight_t* get_node_weight_idx(const in3_nodeselect_def_t* data, unsigned int index) { + return index < data->nodelist_length ? data->weights + index : NULL; +} + +static inline in3_node_t* get_node(const in3_nodeselect_def_t* data, const node_match_t* node) { + return node ? get_node_idx(data, node->index) : NULL; +} + +NONULL static inline in3_node_weight_t* get_node_weight(const in3_nodeselect_def_t* data, const node_match_t* node) { + return node ? get_node_weight_idx(data, node->index) : NULL; +} + +static inline bool is_blacklisted(const in3_node_t* node) { return node && node->blocked; } + +NONULL static in3_ret_t blacklist_node(in3_nodeselect_def_t* data, unsigned int index, uint64_t secs_from_now) { + in3_node_t* node = get_node_idx(data, index); + if (is_blacklisted(node)) return IN3_ERPC; // already handled + + if (node && !node->blocked) { + in3_node_weight_t* w = get_node_weight_idx(data, index); + if (!w) { + in3_log_debug("failed to blacklist node: %s\n", node->url); + return IN3_EFIND; + } + + // blacklist the node + uint64_t blacklisted_until_ = in3_time(NULL) + secs_from_now; + if (w->blacklisted_until != blacklisted_until_) + data->dirty = true; + w->blacklisted_until = blacklisted_until_; + node->blocked = true; + in3_log_debug("Blacklisting node for unverifiable response: %s\n", node ? node->url : ""); + } + return IN3_OK; +} + +NONULL static inline in3_ret_t blacklist_node_addr(in3_nodeselect_def_t* data, const address_t node_addr, uint64_t secs_from_now) { + for (unsigned int i = 0; i < data->nodelist_length; ++i) + if (!memcmp(data->nodelist[i].address, node_addr, 20)) + return blacklist_node(data, data->nodelist[i].index, secs_from_now); + return IN3_OK; +} + +NONULL static inline in3_ret_t blacklist_node_url(in3_nodeselect_def_t* data, const char* node_url, uint64_t secs_from_now) { + for (unsigned int i = 0; i < data->nodelist_length; ++i) + if (!strcmp(data->nodelist[i].url, node_url)) + return blacklist_node(data, data->nodelist[i].index, secs_from_now); + return IN3_OK; +} + +#endif diff --git a/c/include/in3/nodeselect_def.h b/c/include/in3/nodeselect_def.h new file mode 100644 index 000000000..5b1214e59 --- /dev/null +++ b/c/include/in3/nodeselect_def.h @@ -0,0 +1,34 @@ +/** + * + */ +// @PUBLIC_HEADER +#ifndef IN3_NODE_SELECT_DEF_H +#define IN3_NODE_SELECT_DEF_H + +#include "plugin.h" +#include "request.h" +#include "nodelist.h" + +#ifdef NODESELECT_DEF + +/** + * default nodeselect implementation + */ +in3_ret_t in3_nodeselect_handle_action(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx); + +/** + * get access to internal plugin data if registered + */ +static inline in3_nodeselect_def_t* in3_nodeselect_def_data(in3_t* c) { + in3_nodeselect_config_t* w = in3_plugin_get_data(c, in3_nodeselect_handle_action); + return w ? w->data : NULL; +} + +/** + * registers the default nodeselect implementation + */ +in3_ret_t in3_register_nodeselect_def(in3_t* c); + +#endif //NODESELECT_DEF + +#endif //IN3_NODE_SELECT_DEF_H diff --git a/c/include/in3/plugin.h b/c/include/in3/plugin.h index 5d8601723..43dceb092 100644 --- a/c/include/in3/plugin.h +++ b/c/include/in3/plugin.h @@ -281,6 +281,7 @@ typedef struct sign_prepare_ctx { typedef enum { SIGN_EC_RAW = 0, /**< sign the data directly */ SIGN_EC_HASH = 1, /**< hash and sign the data */ + SIGN_EC_PREFIX = 2, /**< add Ethereum Signed Message-Proefix, hash and sign the data */ } d_signature_type_t; /** @@ -374,9 +375,9 @@ typedef void (*in3_storage_clear)( * context used during get config */ typedef struct in3_cache_ctx { - in3_req_t* req; /**< the request context */ - char* key; /**< the key to fetch */ - bytes_t* content; /**< the content to set */ + in3_req_t* req; /**< the request context */ + const char* key; /**< the key to fetch */ + bytes_t* content; /**< the content to set */ } in3_cache_ctx_t; /** diff --git a/c/include/in3/request.h b/c/include/in3/request.h index 114de40b9..6242f5661 100644 --- a/c/include/in3/request.h +++ b/c/include/in3/request.h @@ -133,6 +133,17 @@ NONULL in3_req_t* req_new( in3_t* client, /**< [in] the client-config. */ const char* req_data /**< [in] the rpc-request as json string. */ ); +/** + * creates a new request but clones the request-data. + * + * the request data will be parsed and represented in the context. + * calling this function will only parse the request data, but not send anything yet. + * + */ +NONULL in3_req_t* req_new_clone( + in3_t* client, /**< [in] the client-config. */ + const char* req_data /**< [in] the rpc-request as json string. */ +); /** * sends a previously created request to nodes and verifies it. * @@ -312,6 +323,11 @@ char* req_get_response_data( in3_req_t* req /**< [in] the request context. */ ); +/** + * returns the result or NULL in case of an error for that context. The result must be freed! + */ +char* req_get_result_json(in3_req_t* ctx, int index); + /** * returns the type of the request */ @@ -339,7 +355,7 @@ NONULL void req_free( * ```c in3_ret_t get_from_nodes(in3_req_t* parent, char* method, char* params, bytes_t* dst) { // check if the method is already existing - in3_req_t* req = req_find_required(parent, method); + in3_req_t* req = req_find_required(parent, method, NULL); if (ctx) { // found one - so we check if it is useable. switch (in3_req_state(ctx)) { @@ -386,9 +402,11 @@ NONULL in3_ret_t req_add_required( * * This method is used internaly to find a previously added context. */ -NONULL in3_req_t* req_find_required( - const in3_req_t* parent, /**< [in] the current request context. */ - const char* method /**< [in] the method of the rpc-request. */ +NONULL_FOR((1, 2)) +in3_req_t* req_find_required( + const in3_req_t* parent, /**< [in] the current request context. */ + const char* method, /**< [in] the method of the rpc-request. */ + const char* param_query /**< [in] a optional string within thew params. */ ); /** * removes a required context after usage. diff --git a/c/include/in3/scache.h b/c/include/in3/scache.h index 4f3970b64..6df98fa4b 100644 --- a/c/include/in3/scache.h +++ b/c/include/in3/scache.h @@ -53,6 +53,7 @@ typedef enum cache_props { CACHE_PROP_MUST_FREE = 0x1, /**< indicates the content must be freed*/ CACHE_PROP_SRC_REQ = 0x2, /**< the value holds the src-request */ CACHE_PROP_ONLY_EXTERNAL = 0x4, /**< should only be freed if the context is external */ + CACHE_PROP_JSON = 0x8, /**< indicates the content is a json_ctxt and must be freed as such*/ CACHE_PROP_PAYMENT = 0x80 /**< This cache-entry is a payment.data */ } cache_props_t; /** diff --git a/c/include/in3/stringbuilder.h b/c/include/in3/stringbuilder.h index 8a652de4e..ad75dd236 100644 --- a/c/include/in3/stringbuilder.h +++ b/c/include/in3/stringbuilder.h @@ -82,10 +82,10 @@ NONULL_FOR((1, 3)) sb_t* sb_add_bytes(sb_t* sb, const char* prefix, const bytes_t* bytes, int len, bool as_array); /**< add bytes as 0x-prefixed hexcoded string (including an optional prefix), if len>1 is passed bytes maybe an array ( if as_array==true) */ NONULL sb_t* sb_add_hexuint_l(sb_t* sb, uintmax_t uint, size_t l); /**< add a integer value as hexcoded, 0x-prefixed string*/ NONULL sb_t* sb_add_escaped_chars(sb_t* sb, const char* chars); /**< add chars but escapes all quotes */ -NONULL sb_t* sb_add_int(sb_t* sb, uint64_t val); /**< adds a numeric value to the stringbuilder */ +NONULL sb_t* sb_add_int(sb_t* sb, int64_t val); /**< adds a numeric value to the stringbuilder */ NONULL char* format_json(const char* json); /**< format a json string and returns a new string, which needs to be freed */ NONULL_FOR((1)) -sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, unsigned int fix_size); +sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, int fix_size); sb_t* sb_print(sb_t* sb, const char* fmt, ...); sb_t* sb_vprint(sb_t* sb, const char* fmt, va_list args); diff --git a/c/include/in3/utils.h b/c/include/in3/utils.h index 64acd9d1c..328134038 100644 --- a/c/include/in3/utils.h +++ b/c/include/in3/utils.h @@ -224,6 +224,11 @@ uint64_t current_ms(); return _r; \ } \ } +#define TRY_RPC(name, fn) \ + if (strcmp(ctx->method, name) == 0) return fn; +/** used in if-conditions and returns true if the vc->method mathes the name. It is also used as marker.*/ +#define VERIFY_RPC(name) (strcmp(vc->method, name) == 0) +#define CONFIG_KEY(name) key(name) /** * executes the expression and expects value to equal val. @@ -319,6 +324,11 @@ int64_t parse_float_val(const char* data, /**< the data string*/ int32_t expo /**< the exponent */ ); +/** + * simple add function, which adds the bytes (b) to a + */ +void b256_add(bytes32_t a, uint8_t* b, wlen_t len_b); + #ifdef THREADSAFE #define _NAME(x, y) x##y #if defined(_MSC_VER) || defined(__MINGW32__) diff --git a/c/include/in3/zksync.h b/c/include/in3/zksync.h index 7b77f1be6..660b1a342 100644 --- a/c/include/in3/zksync.h +++ b/c/include/in3/zksync.h @@ -128,6 +128,11 @@ typedef struct zksync_config { char* proof_create_method; /**< the rpc-method used to create the proof before creating a signature */ } zksync_config_t; +typedef struct valid { + uint64_t from; + uint64_t to; +} zksync_valid_t; + typedef struct pay_criteria { uint_fast32_t payed_nodes; /**< max number of nodes payed at the same time*/ uint64_t max_price_per_hundred_igas; /**< the max price per 100 gas units to accept a payment offer */ @@ -146,6 +151,7 @@ typedef struct { zk_msg_type_t type; /**< message type */ zk_fee_t amount; /**< amount to send */ zk_fee_t fee; /**< ransaction fees */ + zksync_valid_t valid; /**< validity */ } zksync_tx_data_t; /** registers the zksync-plugin in the client */ @@ -167,7 +173,7 @@ NONULL in3_ret_t zksync_emergency_withdraw(zksync_config_t* conf, in3_rpc_handle NONULL in3_ret_t zksync_sign_transfer(sb_t* sb, zksync_tx_data_t* data, in3_req_t* req, zksync_config_t* conf); /** creates message data and signs a change_pub_key-message */ -NONULL in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* req, uint8_t* sync_pub_key, uint32_t nonce, zksync_config_t* conf, zk_fee_t fee, zksync_token_t* token); +NONULL in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* req, uint8_t* sync_pub_key, uint32_t nonce, zksync_config_t* conf, zk_fee_t fee, zksync_token_t* token, zksync_valid_t valid); in3_ret_t zksync_musig_sign(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx); zk_musig_session_t* zk_musig_session_free(zk_musig_session_t* s); diff --git a/c/macro.cmake b/c/macro.cmake index aea814037..7278d8989 100644 --- a/c/macro.cmake +++ b/c/macro.cmake @@ -1,15 +1,28 @@ macro(add_static_library ) - cmake_parse_arguments(_LIB "" NAME "SOURCES;DEPENDS" ${ARGN} ) + cmake_parse_arguments(_LIB "" "NAME;REGISTER;OPTION;DESCR" "SOURCES;DEPENDS" ${ARGN} ) + if (_LIB_OPTION) + option(${_LIB_OPTION} ${_LIB_DESCR} ON) + else() + string(TOUPPER "MOD_${_LIB_NAME}" _LIB_OPTION) + set(${_LIB_OPTION} ON) + endif() + if (${${_LIB_OPTION}}) + # create objects + add_library(${_LIB_NAME}_o OBJECT ${_LIB_SOURCES}) - # create objects - add_library(${_LIB_NAME}_o OBJECT ${_LIB_SOURCES}) + # add dependency + add_library(${_LIB_NAME} STATIC $) - # add dependency - add_library(${_LIB_NAME} STATIC $) - - target_compile_definitions(${_LIB_NAME}_o PRIVATE) - target_link_libraries(${_LIB_NAME} ${_LIB_DEPENDS}) - get_property(tmp GLOBAL PROPERTY IN3_OBJECTS) - set_property(GLOBAL PROPERTY IN3_OBJECTS ${tmp} $) + target_compile_definitions(${_LIB_NAME}_o PRIVATE) + target_link_libraries(${_LIB_NAME} ${_LIB_DEPENDS}) + get_property(tmp GLOBAL PROPERTY IN3_OBJECTS) + set_property(GLOBAL PROPERTY IN3_OBJECTS ${tmp} $) + get_property(tmp GLOBAL PROPERTY IN3_API_NAMES) + set_property(GLOBAL PROPERTY IN3_API_NAMES ${tmp} ${_LIB_NAME}) + if(_LIB_REGISTER) + get_property(tmp GLOBAL PROPERTY IN3_REGISTERS) + set_property(GLOBAL PROPERTY IN3_REGISTERS ${tmp} ${_LIB_REGISTER}) + endif(_LIB_REGISTER) + endif() endmacro() diff --git a/c/options.cmake b/c/options.cmake new file mode 100644 index 000000000..58fc2cc1b --- /dev/null +++ b/c/options.cmake @@ -0,0 +1,263 @@ + +# options +option(BUILD_DOC "generates the documenation with doxygen." OFF) +option(TAG_VERSION "the tagged version, which should be used" 3.0.0) +option(ETH_NANO "build minimal eth verification.(eth_getTransactionReceipt)" ON) +option(ETH_BASIC "build basic eth verification.(all rpc-calls except eth_call)" ON) +option(ETH_FULL "build full eth verification.(including eth_call)" ON) +option(IPFS "build IPFS verification" ON) +option(COLOR "Enable color codes for debug" ON) +option(BTC "if true, the bitcoin verifiers will be build" ON) +option(IN3API "build the USN-API which offer better interfaces and additional functions on top of the pure verification" ON) +option(USE_PRECOMPUTED_EC "if true the secp256k1 curve uses precompiled tables to boost performance. turning this off makes ecrecover slower, but saves about 37kb." ON) +option(LOGGING "if set logging and human readable error messages will be inculded in th executable, otherwise only the error code is used. (saves about 19kB)" ON) +option(EVM_GAS "if true the gas costs are verified when validating a eth_call. This is a optimization since most calls are only interessted in the result. EVM_GAS would be required if the contract uses gas-dependend op-codes." true) +option(IN3_LIB "if true a shared anmd static library with all in3-modules will be build." ON) +option(TEST "builds the tests and also adds special memory-management, which detects memory leaks, but will cause slower performance" OFF) +option(FAST_MATH "Math optimizations used in the EVM. This will also increase the filesize." OFF) +option(SEGGER_RTT "Use the segger real time transfer terminal as the logging mechanism" OFF) +option(CURL_BLOCKING "if true the curl-request will block until the response is received" OFF) +option(JAVA "build the java-binding (shared-lib and jar-file)" OFF) +option(JAVA_MULTI_LIBS "embedds multiple shared libs in the jar" OFF) +option(WASM "Includes the WASM-Build. In order to build it you need emscripten as toolchain. Usually you also want to turn off other builds in this case." OFF) +option(ASMJS "compiles the code as asm.js." OFF) +option(WASM_EMBED "embedds the wasm as base64-encoded into the js-file" ON) +option(WASM_EMMALLOC "use ther smaller EMSCRIPTEN Malloc, which reduces the size about 10k, but may be a bit slower" ON) +option(WASM_SYNC "intiaializes the WASM synchronisly, which allows to require and use it the same function, but this will not be supported by chrome (4k limit)" OFF) +option(CODE_COVERAGE "Builds targets with code coverage instrumentation. (Requires GCC or Clang)" OFF) +option(GCC_ANALYZER "GCC10 static code analyses" OFF) +option(PAY_ETH "support for direct Eth-Payment" OFF) +option(USE_SCRYPT "integrate scrypt into the build in order to allow decrypt_key for scrypt encoded keys." ON) +option(USE_CURL "if true the curl transport will be built (with a dependency to libcurl)" ON) +option(USE_WINHTTP "if true the winhttp transport will be built (with a dependency to winhttp)" OFF) +option(LEDGER_NANO "include support for nano ledger" OFF) +option(ESP_IDF "include support for ESP-IDF microcontroller framework" OFF) +option(ASSERTIONS "includes assertions into the code, which help track errors but may cost time during runtime" OFF) +OPTION(TRANSPORTS "builds transports, which may require extra libraries." ON) +OPTION(IN3_SERVER "support for proxy server as part of the cmd-tool, which allows to start the cmd-tool with the -p option and listens to the given port for rpc-requests" OFF) +OPTION(CMD "build the comandline utils" ON) +OPTION(RECORDER "enable recording option for reproduce executions" ON) +OPTION(POA "support POA verification including validatorlist updates" OFF) +OPTION(MULTISIG "add capapbility to sign with a multig. Currrently only gnosis safe is supported" ON) +OPTION(ZKSYNC "add RPC-function to handle zksync-payments" ON) +OPTION(ZKCRYPTO_LIB "Path to the static zkcrypto-lib" OFF) +OPTION(SENTRY "Enable Sentry" OFF) +OPTION(BTC_PRE_BPI34 "Enable BTC-Verfification for blocks before BIP34 was activated" ON) +OPTION(PK_SIGNER "Enable Signing with private keys" ON) +OPTION(NODESELECT_DEF "Enable default nodeselect implementation" ON) +OPTION(NODESELECT_DEF_WL "Enable default nodeselect whitelist implementation" ON) +OPTION(PLGN_CLIENT_DATA "Enable client-data plugin" OFF) +OPTION(THREADSAFE "uses mutex to protect shared nodelist access" ON) +OPTION(SWIFT "swift API for swift bindings" OFF) +OPTION(CORE_API "include basic core-utils" ON) + + +IF (DEFINED ANDROID_ABI) + set(TRANSPORTS,false) + set(IN3_LIB,false) + set(USE_CURL,off) + set(CMD,false) + set(JAVA,true) + set(RECORDER,false) +ENDIF() +IF (SWIFT) + ADD_DEFINITIONS(-DSWIFT) + set(TRANSPORTS,false) + set(IN3_LIB,true) + set(USE_CURL, false) + set(CMD,false) + set(JAVA,false) + set(RECORDER,false) +ENDIF() + +IF (BTC_PRE_BPI34) + ADD_DEFINITIONS(-DBTC_PRE_BPI34) +ENDIF (BTC_PRE_BPI34) + +IF (POA) + ADD_DEFINITIONS(-DPOA) +ENDIF (POA) + +IF (PK_SIGNER) + ADD_DEFINITIONS(-DPK_SIGNER) + set(IN3_API ${IN3_API} pk_signer) +ENDIF (PK_SIGNER) + +IF (CORE_API) + set(IN3_API ${IN3_API} core_api) +ENDIF (CORE_API) + +if (USE_PRECOMPUTED_EC) + ADD_DEFINITIONS(-DUSE_PRECOMPUTED_CP=1) +else() + ADD_DEFINITIONS(-DUSE_PRECOMPUTED_CP=0) +endif() + +if (LOGGING) + ADD_DEFINITIONS(-DLOGGING) +endif() + +if (MULTISIG) + ADD_DEFINITIONS(-DMULTISIG) +endif() + +if (ZKSYNC) + ADD_DEFINITIONS(-DZKSYNC) + set(WASM_MODULES ${WASM_MODULES} zksync) + set(IN3_API ${IN3_API} zksync) +endif() + + +if(ETH_FULL) + ADD_DEFINITIONS(-DETH_FULL) + set(IN3_VERIFIER eth_full) + set(ETH_BASIC true) + set(ETH_NANO true) +elseif(ETH_BASIC) + ADD_DEFINITIONS(-DETH_BASIC) + set(IN3_VERIFIER eth_basic) + set(ETH_NANO true) +elseif(ETH_NANO) + ADD_DEFINITIONS(-DETH_NANO) + set(IN3_VERIFIER eth_nano) +endif() + +if (ETH_NANO) + set(WASM_MODULES ${WASM_MODULES} eth) +endif() + +if(IN3API) + ADD_DEFINITIONS(-DETH_API) + set(IN3_API ${IN3_API} eth_api) +endif() + +if (ESP_IDF) + ADD_DEFINITIONS(-DESP_IDF) +endif() + +if(PAY_ETH) + ADD_DEFINITIONS(-DPAY_ETH -DPAY) + set(IN3_API ${IN3_API} pay_eth) +endif() + +if(IPFS) + ADD_DEFINITIONS(-DIPFS) + set(IN3_VERIFIER ${IN3_VERIFIER} ipfs) + set(WASM_MODULES ${WASM_MODULES} ipfs) + + if(IN3API) + set(IN3_API ${IN3_API} ipfs_api) + endif() +endif() + +if(BTC) + ADD_DEFINITIONS(-DBTC) + set(IN3_VERIFIER ${IN3_VERIFIER} btc) + set(WASM_MODULES ${WASM_MODULES} btc) + if(IN3API) + set(IN3_API ${IN3_API} btc_api) + endif() +endif() + +if(LEDGER_NANO) + add_definitions(-DLEDGER_NANO) +endif() + +if(COLOR AND NOT (MSVC OR MSYS OR MINGW)) + ADD_DEFINITIONS(-DLOG_USE_COLOR) +endif() + + +if(CMAKE_BUILD_TYPE MATCHES Debug) + ADD_DEFINITIONS(-DDEBUG) +endif(CMAKE_BUILD_TYPE MATCHES Debug) + +if(EVM_GAS) + ADD_DEFINITIONS(-DEVM_GAS) +endif(EVM_GAS) + +if(FAST_MATH) + ADD_DEFINITIONS(-DIN3_MATH_FAST) +else() + ADD_DEFINITIONS(-DIN3_MATH_LITE) +endif(FAST_MATH) + +if(SEGGER_RTT) + ADD_DEFINITIONS(-DSEGGER_RTT) +endif(SEGGER_RTT) + +if(RECORDER) + ADD_DEFINITIONS(-DRECORDER) +endif(RECORDER) + +if (SENTRY) + ADD_DEFINITIONS(-DSENTRY) + set(IN3_API ${IN3_API} in3_sentry) +endif() + +if (NODESELECT_DEF) + ADD_DEFINITIONS(-DNODESELECT_DEF) + if (NODESELECT_DEF_WL) + ADD_DEFINITIONS(-DNODESELECT_DEF_WL) + endif() + set(IN3_NODESELECT ${IN3_NODESELECT} nodeselect_def) +endif() + +# handle version +if (TAG_VERSION) + set(PROJECT_VERSION "${TAG_VERSION}") +else(TAG_VERSION) + set(PROJECT_VERSION "3.0.0-local") +endif(TAG_VERSION) + +MESSAGE(STATUS "Building version ${PROJECT_VERSION}") + +string(REPLACE "." ";" VERSION_LIST ${PROJECT_VERSION}) +list(GET VERSION_LIST 0 PROJECT_VERSION_MAJOR) +list(GET VERSION_LIST 1 PROJECT_VERSION_MINOR) +list(GET VERSION_LIST 2 PROJECT_VERSION_PATCH) + +ADD_DEFINITIONS("-DIN3_VERSION=\"${PROJECT_VERSION}\"") +ADD_DEFINITIONS(-DIN3_VERSION_MAJOR=${PROJECT_VERSION_MINOR}) +ADD_DEFINITIONS(-DIN3_VERSION_MINOR=${PROJECT_VERSION_MINOR}) +ADD_DEFINITIONS(-DIN3_VERSION_PATCH=${PROJECT_VERSION_PATCH}) + + +# define output dir structure +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) + + +IF (WASM) + set(TEST false) + set(RECORDER false) + set(TRANSPORTS false) + set(BUILD_DOC false) + set(IN3_LIB false) + set(CMD false) + set(USE_CURL false) + set(USE_WINHTTP false) + set(THREADSAFE false) + ADD_DEFINITIONS(-DWASM) +ENDIF (WASM) + +if (THREADSAFE) + ADD_DEFINITIONS(-DTHREADSAFE) +ENDIF() + + +# build tests +if(TEST) + ADD_DEFINITIONS(-DTEST) + ADD_DEFINITIONS(-DIN3_EXPORT_TEST=) + ADD_DEFINITIONS(-DIN3_IMPORT_TEST=extern) + ADD_DEFINITIONS(-DDEBUG) + SET(CMAKE_BUILD_TYPE Debug) + enable_testing() + add_custom_target(ptest COMMAND ${CMAKE_CTEST_COMMAND} -j 8) + add_custom_target(rtest COMMAND ${CMAKE_CTEST_COMMAND} -V ) +else(TEST) + ADD_DEFINITIONS(-DIN3_EXPORT_TEST=static) + ADD_DEFINITIONS(-DIN3_IMPORT_TEST=) +endif(TEST) diff --git a/c/src/api/CMakeLists.txt b/c/src/api/CMakeLists.txt index f69538abc..cd8eb20d6 100644 --- a/c/src/api/CMakeLists.txt +++ b/c/src/api/CMakeLists.txt @@ -32,9 +32,10 @@ # with this program. If not, see . ############################################################################### +add_subdirectory(utils) +add_subdirectory(core) IF (IN3API) - add_subdirectory(utils) add_subdirectory(eth1) if (NOT WASM) diff --git a/c/src/api/core/CMakeLists.txt b/c/src/api/core/CMakeLists.txt new file mode 100644 index 000000000..8a969b3fe --- /dev/null +++ b/c/src/api/core/CMakeLists.txt @@ -0,0 +1,46 @@ +############################################################################### +# This file is part of the Incubed project. +# Sources: https://github.com/blockchainsllc/in3 +# +# Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC +# +# +# COMMERCIAL LICENSE USAGE +# +# Licensees holding a valid commercial license may use this file in accordance +# with the commercial license agreement provided with the Software or, alternatively, +# in accordance with the terms contained in a written agreement between you and +# slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further +# information please contact slock.it at in3@slock.it. +# +# Alternatively, this file may be used under the AGPL license as follows: +# +# AGPL LICENSE USAGE +# +# This program is free software: you can redistribute it and/or modify it under the +# terms of the GNU Affero General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. +# [Permissions of this strong copyleft license are conditioned on making available +# complete source code of licensed works and modifications, which include larger +# works using a licensed work, under the same license. Copyright and license notices +# must be preserved. Contributors provide an express grant of patent rights.] +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +############################################################################### + + +add_static_library( + NAME core_api + REGISTER in3_register_core_api + OPTION CORE_API + DESCR "registers a chain independend rpc-methods util-functions" + SOURCES + core_api.c + + DEPENDS + api_utils +) diff --git a/c/src/api/core/core_api.c b/c/src/api/core/core_api.c new file mode 100644 index 000000000..55111d0a7 --- /dev/null +++ b/c/src/api/core/core_api.c @@ -0,0 +1,157 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ + +#include "../../core/client/keys.h" +#include "../../core/client/plugin.h" +#include "../../core/client/request_internal.h" +#include "../../core/client/version.h" +#include "../../core/util/debug.h" +#include "../../core/util/log.h" +#include "../../core/util/mem.h" +#include "../../third-party/crypto/rand.h" +#include "../../third-party/crypto/secp256k1.h" +#include +#include +#include +#include +#include + +static in3_ret_t in3_sha3(in3_rpc_handle_ctx_t* ctx) { + if (!ctx->params || d_len(ctx->params) != 1) return req_set_error(ctx->req, "no data", IN3_EINVAL); + bytes32_t hash; + keccak(d_to_bytes(ctx->params + 1), hash); + return in3_rpc_handle_with_bytes(ctx, bytes(hash, 32)); +} +static in3_ret_t in3_sha256(in3_rpc_handle_ctx_t* ctx) { + if (!ctx->params || d_len(ctx->params) != 1) return req_set_error(ctx->req, "no data", IN3_EINVAL); + bytes32_t hash; + bytes_t data = d_to_bytes(ctx->params + 1); + SHA256_CTX c; + sha256_Init(&c); + sha256_Update(&c, data.data, data.len); + sha256_Final(&c, hash); + return in3_rpc_handle_with_bytes(ctx, bytes(hash, 32)); +} +static in3_ret_t web3_clientVersion(in3_rpc_handle_ctx_t* ctx) { + // for local chains, we return the client version of rpc endpoint. + return ctx->req->client->chain.chain_id == CHAIN_ID_LOCAL + ? IN3_EIGNORE + : in3_rpc_handle_with_string(ctx, "\"Incubed/" IN3_VERSION "\""); +} + +static in3_ret_t in3_config(in3_rpc_handle_ctx_t* ctx) { + if (!ctx->params || d_len(ctx->params) != 1 || d_type(ctx->params + 1) != T_OBJECT) return req_set_error(ctx->req, "no valid config-object as argument", IN3_EINVAL); + + ctx->req->client->pending--; // we need to to temporarly decrees it in order to allow configuring + str_range_t r = d_to_json(ctx->params + 1); + char old = r.data[r.len]; + r.data[r.len] = 0; + char* ret = in3_configure(ctx->req->client, r.data); + r.data[r.len] = old; + ctx->req->client->pending++; + + if (ret) { + req_set_error(ctx->req, ret, IN3_ECONFIG); + free(ret); + return IN3_ECONFIG; + } + + return in3_rpc_handle_with_string(ctx, "true"); +} + +static in3_ret_t in3_getConfig(in3_rpc_handle_ctx_t* ctx) { + char* ret = in3_get_config(ctx->req->client); + in3_rpc_handle_with_string(ctx, ret); + _free(ret); + return IN3_OK; +} + +static in3_ret_t in3_cacheClear(in3_rpc_handle_ctx_t* ctx) { + TRY(in3_plugin_execute_first(ctx->req, PLGN_ACT_CACHE_CLEAR, NULL)); + return in3_rpc_handle_with_string(ctx, "true"); +} + +static in3_ret_t in3_createKey(in3_rpc_handle_ctx_t* ctx) { + bytes32_t hash; + FILE* r = NULL; + if (d_len(ctx->params) == 1) { + CHECK_PARAM_TYPE(ctx->req, ctx->params, 0, T_BYTES) + keccak(d_to_bytes(ctx->params + 1), hash); + srand(bytes_to_int(hash, 4)); + } + else { +#ifndef WASM + r = fopen("/dev/urandom", "r"); + if (r) { + for (int i = 0; i < 32; i++) hash[i] = (uint8_t) fgetc(r); + fclose(r); + } + else +#endif + srand(current_ms() % 0xFFFFFFFF); + } + + if (!r) { +#if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) + unsigned int number; + for (int i = 0; i < 32; i++) { + hash[i] = (rand_s(&number) ? rand() : (int) number) % 256; + } +#else + for (int i = 0; i < 32; i++) hash[i] = rand() % 256; +#endif + } + return in3_rpc_handle_with_bytes(ctx, bytes(hash, 32)); +} + +static in3_ret_t handle_intern(void* pdata, in3_plugin_act_t action, void* plugin_ctx) { + UNUSED_VAR(pdata); + UNUSED_VAR(action); + + in3_rpc_handle_ctx_t* ctx = plugin_ctx; + TRY_RPC("web3_sha3", in3_sha3(ctx)) + TRY_RPC("keccak", in3_sha3(ctx)) + TRY_RPC("sha256", in3_sha256(ctx)) + TRY_RPC("web3_clientVersion", web3_clientVersion(ctx)) + TRY_RPC("in3_config", in3_config(ctx)) + TRY_RPC("in3_getConfig", in3_getConfig(ctx)) + TRY_RPC("in3_cacheClear", in3_cacheClear(ctx)) + TRY_RPC("in3_createKey", in3_createKey(ctx)) + + return IN3_EIGNORE; +} + +in3_ret_t in3_register_core_api(in3_t* c) { + return in3_plugin_register(c, PLGN_ACT_RPC_HANDLE, handle_intern, NULL, false); +} diff --git a/c/src/api/core/core_api.h b/c/src/api/core/core_api.h new file mode 100644 index 000000000..a281b4598 --- /dev/null +++ b/c/src/api/core/core_api.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ + +// @PUBLIC_HEADER +/** @file + * Ethereum API. + * + * This header-file defines easy to use function, which are preparing the JSON-RPC-Request, which is then executed and verified by the incubed-client. + * */ + +#ifndef CORE_API_H +#define CORE_API_H +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../core/client/client.h" +#include "../utils/api_utils.h" + +/** + * register core-api + */ +in3_ret_t in3_register_core_api(in3_t* c); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/api/core/rpc.yml b/c/src/api/core/rpc.yml new file mode 100644 index 000000000..ee2a218db --- /dev/null +++ b/c/src/api/core/rpc.yml @@ -0,0 +1,74 @@ +utils: + in3_cacheClear: + sync: true + descr: clears the incubed cache (usually found in the .in3-folder) + result: + descr: true indicating the success + example: + request: [] + response: true + + web3_clientVersion: + descr: Returns the underlying client version. See [web3_clientversion](https://eth.wiki/json-rpc/API#web3_clientversion) for spec. + result: + descr: when connected to the incubed-network, `Incubed/` will be returned, but in case of a direct enpoint, its's version will be used. + + keccak: + sync: true + alias: web3_sha3 + + web3_sha3: + sync: true + descr: | + Returns Keccak-256 (not the standardized SHA3-256) of the given data. + + See [web3_sha3](https://eth.wiki/json-rpc/API#web3_sha3) for spec. + + No proof needed, since the client will execute this locally. + params: + data: + descr: data to hash + type: bytes + result: + descr: the 32byte hash of the data + example: + request: + - "0x1234567890" + response: "0x3a56b02b60d4990074262f496ac34733f870e1b7815719b46ce155beac5e1a41" + + sha256: + sync: true + descr: | + Returns sha-256 of the given data. + + No proof needed, since the client will execute this locally. + params: + data: + descr: data to hash + type: bytes + result: + descr: the 32byte hash of the data + example: + request: + - "0x1234567890" + response: "0x6c450e037e79b76f231a71a22ff40403f7d9b74b15e014e52fe1156d3666c3e6" + + +account: + + in3_createKey: + sync: true + descr: | + Generates 32 random bytes. + If /dev/urandom is available it will be used and should generate a secure random number. + If not the number should not be considered sceure or used in production. + params: + seed: + optional: true + descr: the seed. If given the result will be deterministic. + type: bytes + result: + descr: the 32byte random data + example: + request: [] + response: "0x6c450e037e79b76f231a71a22ff40403f7d9b74b15e014e52fe1156d3666c3e6" diff --git a/c/src/api/eth1/CMakeLists.txt b/c/src/api/eth1/CMakeLists.txt index ea3923163..12773519a 100644 --- a/c/src/api/eth1/CMakeLists.txt +++ b/c/src/api/eth1/CMakeLists.txt @@ -45,6 +45,7 @@ endif() add_static_library( NAME eth_api + REGISTER in3_register_eth_api SOURCES eth_api.c diff --git a/c/src/api/eth1/abi.h b/c/src/api/eth1/abi.h index 9cc7e22d6..114be8ad4 100644 --- a/c/src/api/eth1/abi.h +++ b/c/src/api/eth1/abi.h @@ -56,7 +56,8 @@ typedef enum { * Depending on the type the config is stored in the union-structs. */ typedef struct signature { - abi_coder_type_t type; /**< the type of the coder */ + abi_coder_type_t type; /**< the type of the coder */ + bool indexed; /**< marks a tuple as being indexed, which is relevant for event decoding */ union { struct { struct signature** components; /**< the pointer to an array of ponters to the types */ @@ -157,4 +158,17 @@ json_ctx_t* abi_decode( char** error /**< the a pointer to error, which will hold the error message in case of an error. This does not need to be freed, since those messages are constant strings. */ ); + +/** + * decodes bytes to a JSON-structure. + * The resulting json_ctx MUST be freed using `json_free` if not NULL. + */ +json_ctx_t* abi_decode_event( + abi_sig_t* s, /**< the signature to use */ + bytes_t topics, /**< the topics to decode */ + bytes_t data, /**< the data to decode */ + char** error /**< the a pointer to error, which will hold the error message in case of an error. This does not need to be freed, since those messages are constant strings. */ + +); + #endif // _ETH_API__ABI_H_ diff --git a/c/src/api/eth1/abi_decode.c b/c/src/api/eth1/abi_decode.c index 36d78ea17..b40edeb08 100644 --- a/c/src/api/eth1/abi_decode.c +++ b/c/src/api/eth1/abi_decode.c @@ -10,7 +10,7 @@ #include #include -static in3_ret_t decode_tuple(abi_coder_t* tuple, bytes_t data, json_ctx_t* res, int* data_read, bool as_array, char** error); +static in3_ret_t decode_tuple(abi_coder_t* tuple, bytes_t data, json_ctx_t* res, int* data_read, bool as_array, bytes_t* topics, char** error); static in3_ret_t next_word(int* offset, bytes_t* data, uint8_t** dst, char** error) { if (*offset + 32 > (int) data->len) { @@ -82,7 +82,7 @@ static in3_ret_t decode_value(abi_coder_t* c, bytes_t data, json_ctx_t* res, int break; } case ABI_TUPLE: - return decode_tuple(c, data, res, data_read, true, error); + return decode_tuple(c, data, res, data_read, true, NULL, error); case ABI_ARRAY: { int len = c->data.array.len; if (!len) { @@ -91,7 +91,8 @@ static in3_ret_t decode_value(abi_coder_t* c, bytes_t data, json_ctx_t* res, int } bool is_dynamic = abi_is_dynamic(c->data.array.component); int offset = pos; - json_create_array(res)->len |= len; + json_create_array(res); + res->result[res->len - 1].len |= len; for (int i = 0; i < len; i++) { int r = 0, start = pos; if (is_dynamic) { @@ -108,13 +109,25 @@ static in3_ret_t decode_value(abi_coder_t* c, bytes_t data, json_ctx_t* res, int return IN3_OK; } -static in3_ret_t decode_tuple(abi_coder_t* tuple, bytes_t data, json_ctx_t* res, int* data_read, bool add_array, char** error) { - if (add_array) json_create_array(res)->len |= tuple->data.tuple.len; - uint8_t* word = NULL; - int pos = 0; +static in3_ret_t decode_tuple(abi_coder_t* tuple, bytes_t data, json_ctx_t* res, int* data_read, bool add_array, bytes_t* topics, char** error) { + if (add_array) { + json_create_array(res); + res->result[res->len - 1].len |= tuple->data.tuple.len; + } + uint8_t* word = NULL; + int pos = 0; + unsigned topic_pos = 1; for (int i = 0; i < tuple->data.tuple.len; i++) { abi_coder_t* c = tuple->data.tuple.components[i]; - if (abi_is_dynamic(c)) { + if (c->indexed) { + if (!topics || topics->len < (topic_pos + 1) * 32) { + *error = "topics are too short"; + return IN3_EINVAL; + } + TRY(decode_value(c, bytes(topics->data + topic_pos * 32, 32), res, NULL, error)) + topic_pos++; + } + else if (abi_is_dynamic(c)) { TRY(next_word(&pos, &data, &word, error)) int offset = bytes_to_int(word + 28, 4); if (offset + 32 > (int) data.len) { @@ -141,7 +154,25 @@ static in3_ret_t decode_tuple(abi_coder_t* tuple, bytes_t data, json_ctx_t* res, json_ctx_t* abi_decode(abi_sig_t* s, bytes_t data, char** error) { json_ctx_t* res = json_create(); abi_coder_t* c = s->output ? s->output : s->input; - in3_ret_t failed = decode_tuple(c, data, res, NULL, s->return_tuple || c->data.tuple.len != 1, error); + in3_ret_t failed = decode_tuple(c, data, res, NULL, s->return_tuple || c->data.tuple.len != 1, NULL, error); if (failed && res) json_free(res); return *error ? NULL : res; } + +json_ctx_t* abi_decode_event( + abi_sig_t* s, /**< the signature to use */ + bytes_t topics, /**< the topics to decode */ + bytes_t data, /**< the data to decode */ + char** error /**< the a pointer to error, which will hold the error message in case of an error. This does not need to be freed, since those messages are constant strings. */ + +) { + if (topics.len < 32 || memcmp(topics.data, s->fn_hash, 4)) { + *error = "The Topic does not match the event signature"; + return NULL; + } + json_ctx_t* res = json_create(); + abi_coder_t* c = s->output ? s->output : s->input; + in3_ret_t failed = decode_tuple(c, data, res, NULL, s->return_tuple || c->data.tuple.len != 1, &topics, error); + if (failed && res) json_free(res); + return *error ? NULL : res; +} \ No newline at end of file diff --git a/c/src/api/eth1/abi_parse.c b/c/src/api/eth1/abi_parse.c index 5f1dbd0c3..d2822f683 100644 --- a/c/src/api/eth1/abi_parse.c +++ b/c/src/api/eth1/abi_parse.c @@ -126,13 +126,20 @@ static abi_coder_t* create_tuple(char* val, char** error, char** next) { int tl = 0; abi_coder_t* tuple = _calloc(1, sizeof(abi_coder_t)); tuple->type = ABI_TUPLE; + bool indexed = false; for (char c = *val; !*error; c = *(++val)) { if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { + if (tl == 40) return abi_error(error, "toke too long", tuple); token[tl++] = c; continue; } if (c == ' ' && !tl) continue; + if (c == ' ' && tl == 7 && strncmp(token, "indexed", 7) == 0) { + tl = 0; + indexed = true; + continue; + } abi_coder_t* coder = NULL; if (tl) { @@ -162,6 +169,8 @@ static abi_coder_t* create_tuple(char* val, char** error, char** next) { } if (coder) { + coder->indexed = indexed; + indexed = false; tuple->data.tuple.components = tuple->data.tuple.len ? _realloc(tuple->data.tuple.components, (tuple->data.tuple.len + 1) * sizeof(abi_coder_t*), tuple->data.tuple.len * sizeof(abi_coder_t*)) : _malloc(sizeof(abi_coder_t*)); diff --git a/c/src/api/eth1/eth_api.c b/c/src/api/eth1/eth_api.c index fd308d67f..576458e4c 100644 --- a/c/src/api/eth1/eth_api.c +++ b/c/src/api/eth1/eth_api.c @@ -336,29 +336,29 @@ static void* eth_call_fn_intern(in3_t* in3, address_t contract, eth_blknum_t blo bytes_t data = {0}; if (!error) { json_ctx_t* in_data = json_create(); - d_token_t* args = json_create_array(in_data); + int args = json_create_array(in_data); for (int i = 0; i < req->input->data.tuple.len && !error; i++) { abi_coder_t* p = req->input->data.tuple.components[i]; switch (p->type) { case ABI_BOOL: - json_array_add_value(args, json_create_bool(in_data, va_arg(ap, int))); + json_array_add_value(in_data, args, json_create_bool(in_data, va_arg(ap, int))); break; case ABI_ADDRESS: - json_array_add_value(args, json_create_bytes(in_data, bytes(va_arg(ap, uint8_t*), 20))); + json_array_add_value(in_data, args, json_create_bytes(in_data, bytes(va_arg(ap, uint8_t*), 20))); break; case ABI_BYTES: - json_array_add_value(args, json_create_bytes(in_data, va_arg(ap, bytes_t))); + json_array_add_value(in_data, args, json_create_bytes(in_data, va_arg(ap, bytes_t))); break; case ABI_STRING: - json_array_add_value(args, json_create_string(in_data, va_arg(ap, char*), -1)); + json_array_add_value(in_data, args, json_create_string(in_data, va_arg(ap, char*), -1)); break; case ABI_NUMBER: { if (p->data.number.size <= 32) - json_array_add_value(args, json_create_int(in_data, va_arg(ap, uint32_t))); + json_array_add_value(in_data, args, json_create_int(in_data, va_arg(ap, uint32_t))); else if (p->data.number.size <= 64) - json_array_add_value(args, json_create_int(in_data, va_arg(ap, uint64_t))); + json_array_add_value(in_data, args, json_create_int(in_data, va_arg(ap, uint64_t))); else - json_array_add_value(args, json_create_bytes(in_data, bytes(va_arg(ap, uint256_t).data, 32))); + json_array_add_value(in_data, args, json_create_bytes(in_data, bytes(va_arg(ap, uint256_t).data, 32))); break; } default: @@ -366,7 +366,7 @@ static void* eth_call_fn_intern(in3_t* in3, address_t contract, eth_blknum_t blo } } - if (!error) data = abi_encode(req, args, &error); + if (!error) data = abi_encode(req, in_data->result + args, &error); json_free(in_data); } @@ -464,7 +464,8 @@ in3_ret_t eth_newPendingTransactionFilter(in3_t* in3) { bool eth_uninstallFilter(in3_t* in3, size_t id) { return filter_remove(eth_basic_get_filters(in3), id); } - +// same as "eth_getFilterChanges" +// or "eth_getFilterLogs" in3_ret_t eth_getFilterChanges(in3_t* in3, size_t id, bytes32_t** block_hashes, eth_log_t** logs) { in3_filter_handler_t* filters = eth_basic_get_filters(in3); if (filters == NULL) return IN3_EFIND; diff --git a/c/src/api/eth1/eth_api.h b/c/src/api/eth1/eth_api.h index f5e03e51d..56855fbea 100644 --- a/c/src/api/eth1/eth_api.h +++ b/c/src/api/eth1/eth_api.h @@ -183,6 +183,7 @@ char* eth_wait_for_receipt(in3_t* in3, bytes32_t tx_hash); void eth_log_free(eth_log_t* log); /**< Frees a eth_log_t object */ void eth_tx_receipt_free(eth_tx_receipt_t* txr); /**< Frees a eth_tx_receipt_t object */ int string_val_to_bytes(char* val, char* unit, bytes32_t target); /**< reades the string as hex or decimal and converts it into bytes. the value may also contains a suffix as unit like '1.5eth` which will convert it into wei. the target-pointer must be at least as big as the strlen. The length of the bytes will be returned or a negative value in case of an error.*/ +char* bytes_to_string_val(bytes_t wei, int exp, int digits); /**< converts the bytes value to a decimal string */ in3_ret_t in3_register_eth_api(in3_t* c); /**< this function should only be called once and will register the eth-API verifier.*/ #ifdef __cplusplus } diff --git a/c/src/api/eth1/rpc.yml b/c/src/api/eth1/rpc.yml new file mode 100644 index 000000000..0919200a6 --- /dev/null +++ b/c/src/api/eth1/rpc.yml @@ -0,0 +1,379 @@ +utils: + + descr: | + a Collection of utility-function. + + + in3_abiEncode: + sync: true + descr: based on the [ABI-encoding](https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html) used by solidity, this function encodes the value given and returns it as hexstring. + params: + signature: + type: string + descr: the signature of the function. e.g. `getBalance(uint256)`. The format is the same as used by solidity to create the functionhash. optional you can also add the return type, which in this case is ignored. + params: + type: any + array: true + descr: a array of arguments. the number of arguments must match the arguments in the signature. + result: + type: hex + descr: the ABI-encoded data as hex including the 4 byte function-signature. These data can be used for `eth_call` or to send a transaction. + example: + request: + - "getBalance(address)" + - ["0x1234567890123456789012345678901234567890"] + response: "0xf8b2cb4f0000000000000000000000001234567890123456789012345678901234567890" + + in3_abiDecode: + sync: true + descr: based on the [ABI-encoding](https://solidity.readthedocs.io/en/v0.5.3/abi-spec.html) used by solidity, this function decodes the bytes given and returns it as array of values. + params: + signature: + type: string + descr: the signature of the function. e.g. `uint256`, `(address,string,uint256)` or `getBalance(address):uint256`. If the complete functionhash is given, only the return-part will be used. + data: + type: bytes + descr: the data to decode (usually the result of a eth_call) + topics: + optional: true + type: bytes + descr: in case of an even the topics (concatinated to max 4x32bytes). This is used if indexed.arguments are used. + result: + type: any + array: true + descr: a array with the values after decodeing. + example: + request: + - (address,uint256) + - "0x00000000000000000000000012345678901234567890123456789012345678900000000000000000000000000000000000000000000000000000000000000005" + response: + - "0x1234567890123456789012345678901234567890" + - "0x05" + + in3_checksumAddress: + sync: true + descr: Will convert an upper or lowercase Ethereum address to a checksum address. (See [EIP55](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md) ) + params: + address: + descr: the address to convert. + type: address + useChainId: + descr: if true, the chainId is integrated as well (See [EIP1191](https://github.com/ethereum/EIPs/issues/1121) ) + type: bool + optional: true + result: + descr: the address-string using the upper/lowercase hex characters. + example: + request: + - "0x1fe2e9bf29aa1938859af64c413361227d04059a" + - false + response: "0x1Fe2E9bf29aa1938859Af64C413361227d04059a" + + + in3_toWei: + sync : true + descr: converts the given value into wei. + params: + value: + descr: the value, which may be floating number as string + type: string | uint + example: "0.9" + unit: + descr: the unit of the value, which must be one of `wei`, `kwei`, `Kwei`, `babbage`, `femtoether`, `mwei`, `Mwei`, `lovelace`, `picoether`, `gwei`, `Gwei`, `shannon`, `nanoether`, `nano`, `szabo`, `microether`, `micro`, `finney`, `milliether`, `milli`, `ether`, `eth`, `kether`, `grand`, `mether`, `gether` or `tether` + type: string + optional: true + default: eth + result: + descr: the value in wei as hex. + example: + request: + - "20.0009123" + - eth + response: "0x01159183c4793db800" + + + in3_fromWei: + sync: true + descr: converts a given uint (also as hex) with a wei-value into a specified unit. + params: + value: + descr: the value in wei + type: uint256 + example: "0x234324abdef" + unit: + descr: the unit of the target value, which must be one of `wei`, `kwei`, `Kwei`, `babbage`, `femtoether`, `mwei`, `Mwei`, `lovelace`, `picoether`, `gwei`, `Gwei`, `shannon`, `nanoether`, `nano`, `szabo`, `microether`, `micro`, `finney`, `milliether`, `milli`, `ether`, `eth`, `kether`, `grand`, `mether`, `gether` or `tether` + type: string + digits: + descr: fix number of digits after the comma. If left out, only as many as needed will be included. + type: int + optional: true + result: + descr: the value as string. + type: string + example: + request: + - "0x234324abadefdef" + - eth + - 3 + response: "0.158" + + in3_calcDeployAddress: + descr: calculates the address of a contract about to deploy. The address depends on the senders nonce. + params: + sender: + descr: the sender of the transaction + type: address + nonce: + descr: the nonce of the sender during deployment + type: uint64 + optional: true + result: + type: address + descr: the address of the deployed contract + example: + request: + - '0x5a0b54d5dc17e0aadc383d2db43b0a0d3e029c4c' + - 6054986 + response: '0xba866e7bd2573be3eaf5077b557751bb6d58076e' + + + net_version: + descr: Returns the current network id. + apiName: getNetworkId + result: + descr: the network id + type: uint64 + +account: + descr: | + Account Handling includes handling signers and preparing and signing transacrtion and data. + + Signers are Plugins able to create signatures. Those functions will use the registered plugins. + + + in3_pk2address: + sync: true + descr: extracts the address from a private key. + params: + pk: + descr: the 32 bytes private key as hex. + type: bytes32 + result: + descr: the address + example: + request: + - "0x0fd65f7da55d811634495754f27ab318a3309e8b4b8a978a50c20a661117435a" + response: "0xdc5c4280d8a286f0f9c8f7f55a5a0c67125efcfd" + + in3_pk2public: + sync: true + descr: extracts the public key from a private key. + params: + pk: + descr: the 32 bytes private key as hex. + type: bytes32 + result: + descr: the public key as 64 bytes + example: + request: + - "0x0fd65f7da55d811634495754f27ab318a3309e8b4b8a978a50c20a661117435a" + response: "0x0903329708d9380aca47b02f3955800179e18bffbb29be3a644593c5f87e4c7fa960983f78186577eccc909cec71cb5763acd92ef4c74e5fa3c43f3a172c6de1" + + in3_ecrecover: + sync: true + descr: extracts the public key and address from signature. + params: + msg: + descr: the message the signature is based on. + type: hex + sig: + descr: the 65 bytes signature as hex. + type: bytes + sigtype: + descr: "the type of the signature data : `eth_sign` (use the prefix and hash it), `raw` (hash the raw data), `hash` (use the already hashed data). Default: `raw`" + type: string + default: raw + optional: true + result: + descr: the extracted public key and address + type: + publicKey: + descr: the public Key of the signer (64 bytes) + type: bytes + address: + descr: the address + type: address + example: + request: + - "0x487b2cbb7997e45b4e9771d14c336b47c87dc2424b11590e32b3a8b9ab327999" + - "0x0f804ff891e97e8a1c35a2ebafc5e7f129a630a70787fb86ad5aec0758d98c7b454dee5564310d497ddfe814839c8babd3a727692be40330b5b41e7693a445b71c" + - hash + response: + publicKey: "0x94b26bafa6406d7b636fbb4de4edd62a2654eeecda9505e9a478a66c4f42e504c4481bad171e5ba6f15a5f11c26acfc620f802c6768b603dbcbe5151355bbffb" + address: "0xf68a4703314e9a9cf65be688bd6d9b3b34594ab4" + + + in3_prepareTx: + descr: prepares a Transaction by filling the unspecified values and returens the unsigned raw Transaction. + params: + tx: + descr: the tx-object, which is the same as specified in [eth_sendTransaction](https://eth.wiki/json-rpc/API#eth_sendTransaction). + type: transaction + + result: + descr: the unsigned raw transaction as hex. + example: + request: + - to: "0x63f666a23cbd135a91187499b5cc51d589c302a0" + value: "0x100000000" + from: "0xc2b2f4ad0d234b8c135c39eea8409b448e5e496f" + response: "0xe980851a13b865b38252089463f666a23cbd135a91187499b5cc51d589c302a085010000000080018080" + + in3_signTx: + descr: signs the given raw Tx (as prepared by in3_prepareTx ). The resulting data can be used in `eth_sendRawTransaction` to publish and broadcast the transaction. + params: + tx: + descr: the raw unsigned transactiondata + type: hex + from: + descr: the account to sign + type: address + + result: + descr: the raw transaction with signature. + example: + request: + - "0xe980851a13b865b38252089463f666a23cbd135a91187499b5cc51d589c302a085010000000080018080" + - "0xc2b2f4ad0d234b8c135c39eea8409b448e5e496f" + response: "0xf86980851a13b865b38252089463f666a23cbd135a91187499b5cc51d589c302a08501000000008026a03c5b094078383f3da3f65773ab1314e89ee76bc41f827f2ef211b2d3449e4435a077755f8d9b32966e1ad8f6c0e8c9376a4387ed237bdbf2db6e6b94016407e276" + + in3_signData: + descr: signs the given data. + params: + msg: + descr: the message to sign. + type: hex + account: + descr: the account to sign if the account is a bytes32 it will be used as private key + type: address | bytes32 + msgType: + descr: "the type of the signature data : `eth_sign` (use the prefix and hash it), `raw` (hash the raw data), `hash` (use the already hashed data)" + type: string + default: raw + optional: true + + result: + descr: the signature + type: + message: + descr: original message used + type: bytes + messageHash: + descr: the hash the signature is based on + type: bytes32 + signature: + descr: the signature (65 bytes) + type: bytes + r: + descr: the x-value of the EC-Point + type: bytes32 + s: + descr: the y-value of the EC-Point + type: bytes32 + v: + descr: the recovery value (0|1) + 27 + type: byte + + example: + request: + - "0x0102030405060708090a0b0c0d0e0f" + - "0xa8b8759ec8b59d7c13ef3630e8530f47ddb47eba12f00f9024d3d48247b62852" + - raw + response: + message: "0x0102030405060708090a0b0c0d0e0f" + messageHash: "0x1d4f6fccf1e27711667605e29b6f15adfda262e5aedfc5db904feea2baa75e67" + signature: "0xa5dea9537d27e4e20b6dfc89fa4b3bc4babe9a2375d64fb32a2eab04559e95792264ad1fb83be70c145aec69045da7986b95ee957fb9c5b6d315daa5c0c3e1521b" + r: "0xa5dea9537d27e4e20b6dfc89fa4b3bc4babe9a2375d64fb32a2eab04559e9579" + s: "0x2264ad1fb83be70c145aec69045da7986b95ee957fb9c5b6d315daa5c0c3e152" + v: 27 + + in3_decryptKey: + sync: true + descr: decrypts a JSON Keystore file as defined in the [Web3 Secret Storage Definition](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition). The result is the raw private key. + params: + key: + descr: Keydata as object as defined in the keystorefile + type: string + passphrase: + descr: the password to decrypt it. + type: string + + result: + descr: a raw private key (32 bytes) + example: + request: + - version: 3, + id: "f6b5c0b1-ba7a-4b67-9086-a01ea54ec638" + address: "08aa30739030f362a8dd597fd3fcde283e36f4a1" + crypto: + ciphertext: "d5c5aafdee81d25bb5ac4048c8c6954dd50c595ee918f120f5a2066951ef992d" + cipherparams: + iv: "415440d2b1d6811d5c8a3f4c92c73f49" + cipher: "aes-128-ctr" + kdf: pbkdf2 + kdfparams: + dklen: 32 + salt: "691e9ad0da2b44404f65e0a60cf6aabe3e92d2c23b7410fd187eeeb2c1de4a0d" + c: 16384 + prf: hmac-sha256 + mac: "de651c04fc67fd552002b4235fa23ab2178d3a500caa7070b554168e73359610" + - test + response: "0x1ff25594a5e12c1e31ebd8112bdf107d217c1393da8dc7fc9d57696263457546" + + eth_sign: + descr: | + The sign method calculates an Ethereum specific signature with: + + ```js + sign(keccak256("\x19Ethereum Signed Message:\n" + len(message) + message))). + ``` + + By adding a prefix to the message makes the calculated signature recognisable as an Ethereum specific signature. This prevents misuse where a malicious DApp can sign arbitrary data (e.g. transaction) and use the signature to impersonate the victim. + + For the address to sign a signer must be registered. + params: + account: + descr: the account to sign with + type: address + message: + descr: the message to sign + type: bytes + result: + descr: the signature (65 bytes) for the given message. + example: + request: + - '0x9b2055d370f73ec7d8a03e965129118dc8f5bf83' + - '0xdeadbeaf' + response: '0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b' + + + + eth_signTransaction: + descr: Signs a transaction that can be submitted to the network at a later time using with eth_sendRawTransaction. + params: + tx: + descr: transaction to sign + type: transaction + result: + descr: the raw signed transaction + example: + request: + - data: "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675" + from: "0xb60e8dd61c5d32be8058bb8eb970870f07233155" + gas: "0x76c0" + gasPrice: "0x9184e72a000" + to: "0xd46e8dd67c5d32be8058bb8eb970870f07244567" + value: "0x9184e72a" + response: '0xa3f20717a250c2b0b729b7e5becbff67fdaef7e0699da4de7ca5895b02a170a12d887fd3b17bfdce3481f10bea41f45ba9f709d39ce8325427b57afcfc994cee1b' + + diff --git a/c/src/api/eth1/rpc_api.c b/c/src/api/eth1/rpc_api.c index bd0d1a162..7ca45855f 100644 --- a/c/src/api/eth1/rpc_api.c +++ b/c/src/api/eth1/rpc_api.c @@ -35,9 +35,11 @@ #include "../../core/client/keys.h" #include "../../core/client/plugin.h" #include "../../core/client/request_internal.h" +#include "../../core/client/version.h" #include "../../core/util/debug.h" #include "../../core/util/log.h" #include "../../core/util/mem.h" +#include "../../third-party/crypto/bignum.h" #include "../../third-party/crypto/ecdsa.h" #include "../../third-party/crypto/rand.h" #include "../../third-party/crypto/secp256k1.h" @@ -49,6 +51,7 @@ #include #include #include +#include #include #ifdef ETH_FULL #include "../../third-party/tommath/tommath.h" @@ -79,14 +82,15 @@ static in3_ret_t in3_abiDecode(in3_rpc_handle_ctx_t* ctx) { CHECK_PARAM_TYPE(ctx->req, ctx->params, 0, T_STRING) CHECK_PARAM_TYPE(ctx->req, ctx->params, 1, T_BYTES) CHECK_PARAM(ctx->req, ctx->params, 1, val->len % 32 == 0) - char* error = NULL; - json_ctx_t* res = NULL; - char* sig = d_get_string_at(ctx->params, 0); - bytes_t data = d_to_bytes(d_get_at(ctx->params, 1)); - if (d_len(ctx->params) > 2) return req_set_error(ctx->req, "too many arguments (only 2 alllowed)", IN3_EINVAL); + char* error = NULL; + json_ctx_t* res = NULL; + char* sig = d_get_string_at(ctx->params, 0); + bytes_t data = d_to_bytes(d_get_at(ctx->params, 1)); + bytes_t topics = d_to_bytes(d_get_at(ctx->params, 2)); + if (d_len(ctx->params) > 3) return req_set_error(ctx->req, "too many arguments (only 3 alllowed)", IN3_EINVAL); abi_sig_t* req = abi_sig_create(sig, &error); - if (!error) res = abi_decode(req, data, &error); + if (!error) res = topics.data ? abi_decode_event(req, topics, data, &error) : abi_decode(req, data, &error); if (req) abi_sig_free(req); if (error) return req_set_error(ctx->req, error, IN3_EINVAL); char* result = d_create_json(res, res->result); @@ -139,22 +143,6 @@ static in3_ret_t in3_ens(in3_rpc_handle_ctx_t* ctx) { return in3_rpc_handle_with_bytes(ctx, bytes(result, res_len)); } -static in3_ret_t in3_sha3(in3_rpc_handle_ctx_t* ctx) { - if (!ctx->params || d_len(ctx->params) != 1) return req_set_error(ctx->req, "no data", IN3_EINVAL); - bytes32_t hash; - keccak(d_to_bytes(ctx->params + 1), hash); - return in3_rpc_handle_with_bytes(ctx, bytes(hash, 32)); -} -static in3_ret_t in3_sha256(in3_rpc_handle_ctx_t* ctx) { - if (!ctx->params || d_len(ctx->params) != 1) return req_set_error(ctx->req, "no data", IN3_EINVAL); - bytes32_t hash; - bytes_t data = d_to_bytes(ctx->params + 1); - SHA256_CTX c; - sha256_Init(&c); - sha256_Update(&c, data.data, data.len); - sha256_Final(&c, hash); - return in3_rpc_handle_with_bytes(ctx, bytes(hash, 32)); -} static const char* UNITS[] = { "wei", "", "kwei", "\x03", @@ -219,7 +207,7 @@ int string_val_to_bytes(char* val, char* unit, bytes32_t target) { char* dst = alloca(l + exp + 10); char* dot = strchr(val, '.'); if (!dot) - memcpy(dst, val, (p = l) + 1); + memcpy(dst, val, (p = nl) + 1); else if (dot - val != 1 || *val != '0') memcpy(dst, val, (p = dot - val) + 1); dst[p + exp] = 0; @@ -275,31 +263,68 @@ static in3_ret_t in3_toWei(in3_rpc_handle_ctx_t* ctx) { : in3_rpc_handle_with_bytes(ctx, bytes(tmp, (uint32_t) s)); } -static in3_ret_t in3_config(in3_rpc_handle_ctx_t* ctx) { - if (!ctx->params || d_len(ctx->params) != 1 || d_type(ctx->params + 1) != T_OBJECT) return req_set_error(ctx->req, "no valid config-object as argument", IN3_EINVAL); - - ctx->req->client->pending--; // we need to to temporarly decrees it in order to allow configuring - str_range_t r = d_to_json(ctx->params + 1); - char old = r.data[r.len]; - r.data[r.len] = 0; - char* ret = in3_configure(ctx->req->client, r.data); - r.data[r.len] = old; - ctx->req->client->pending++; - - if (ret) { - req_set_error(ctx->req, ret, IN3_ECONFIG); - free(ret); - return IN3_ECONFIG; +char* bytes_to_string_val(bytes_t wei, int exp, int digits) { + char tmp[300]; + bytes32_t val = {0}; + memcpy(val + 32 - wei.len, wei.data, wei.len); + bignum256 bn; + bn_read_be(val, &bn); + size_t l = bn_format(&bn, "", "", 0, 0, false, tmp, 300); + if (exp) { + if (l <= (size_t) exp) { + memmove(tmp + exp - l + 1, tmp, l + 1); + memset(tmp, '0', exp - l + 1); + l += exp - l + 1; + } + memmove(tmp + l - exp + 1, tmp + l - exp, exp + 1); + tmp[l - exp] = '.'; + l++; } + if (digits == -1 && exp) + for (int i = l - 1;; i--) { + if (tmp[i] == '0') + tmp[i] = 0; + else if (tmp[i] == '.') { + tmp[i] = 0; + break; + } + else + break; + } + else if (digits == 0) + tmp[l - exp + digits - 1] = 0; + else if (digits < exp) + tmp[l - exp + digits] = 0; - return in3_rpc_handle_with_string(ctx, "true"); + return _strdupn(tmp, -1); } -static in3_ret_t in3_getConfig(in3_rpc_handle_ctx_t* ctx) { - char* ret = in3_get_config(ctx->req->client); - in3_rpc_handle_with_string(ctx, ret); - _free(ret); - return IN3_OK; +static in3_ret_t in3_fromWei(in3_rpc_handle_ctx_t* ctx) { + if (!ctx->params || d_len(ctx->params) < 1) return req_set_error(ctx->req, "must have 1 params as number or bytes", IN3_EINVAL); + bytes_t val = d_to_bytes(ctx->params + 1); + d_token_t* unit = d_get_at(ctx->params, 1); + int exp = 0; + if (d_type(unit) == T_STRING) { + char* u = d_string(unit); + for (int i = 0; UNITS[i]; i += 2) { + if (strcmp(UNITS[i], u) == 0) { + exp = *UNITS[i + 1]; + break; + } + else if (!UNITS[i + 2]) + return req_set_error(ctx->req, "the unit can not be found", IN3_EINVAL); + } + } + else if (d_type(unit) == T_INTEGER) + exp = d_int(unit); + else + return req_set_error(ctx->req, "the unit must be eth-unit or a exponent", IN3_EINVAL); + + int digits = (unit = d_get_at(ctx->params, 2)) ? d_int(unit) : -1; + char* s = bytes_to_string_val(val, exp, digits); + in3_ret_t r = in3_rpc_handle_with_string(ctx, s); + _free(s); + return r; } static in3_ret_t in3_pk2address(in3_rpc_handle_ctx_t* ctx) { @@ -317,6 +342,34 @@ static in3_ret_t in3_pk2address(in3_rpc_handle_ctx_t* ctx) { return in3_rpc_handle_with_bytes(ctx, bytes(public_key + 1, 64)); } +static in3_ret_t in3_calcDeployAddress(in3_rpc_handle_ctx_t* ctx) { + bytes_t sender = d_to_bytes(d_get_at(ctx->params, 0)); + bytes_t nonce = d_to_bytes(d_get_at(ctx->params, 1)); + if (sender.len != 20) return req_set_error(ctx->req, "Invalid sender address, must be 20 bytes", IN3_EINVAL); + if (!nonce.data) { + sb_t sb = sb_stack(alloca(100)); + sb_add_rawbytes(&sb, "\"0x", sender, 0); + sb_add_chars(&sb, "\",\"latest\""); + d_token_t* result; + TRY(req_send_sub_request(ctx->req, "eth_getTransactionCount", sb.data, NULL, &result, NULL)) + nonce = d_to_bytes(result); + } + + // handle nonce as number, which means no leading zeros and if 0 it should be an empty bytes-array + b_optimize_len(&nonce); + if (nonce.len == 1 && nonce.data[0] == 0) nonce.len = 0; + + bytes_builder_t* bb = bb_new(); + rlp_encode_item(bb, &sender); + rlp_encode_item(bb, &nonce); + rlp_encode_to_list(bb); + bytes32_t hash; + keccak(bb->b, hash); + bb_free(bb); + + return in3_rpc_handle_with_bytes(ctx, bytes(hash + 12, 20)); +} + static in3_ret_t in3_ecrecover(in3_rpc_handle_ctx_t* ctx) { bytes_t msg = d_to_bytes(d_get_at(ctx->params, 0)); bytes_t* sig = d_get_bytes_at(ctx->params, 1); @@ -359,10 +412,11 @@ static in3_ret_t in3_ecrecover(in3_rpc_handle_ctx_t* ctx) { } static in3_ret_t in3_sign_data(in3_rpc_handle_ctx_t* ctx) { - bytes_t data = d_to_bytes(d_get_at(ctx->params, 0)); - const bytes_t* pk = d_get_bytes_at(ctx->params, 1); - char* sig_type = d_get_string_at(ctx->params, 2); - if (!sig_type) sig_type = "raw"; + const bool is_eth_sign = strcmp(ctx->method, "eth_sign") == 0; + bytes_t data = d_to_bytes(d_get_at(ctx->params, is_eth_sign ? 1 : 0)); + const bytes_t* pk = d_get_bytes_at(ctx->params, is_eth_sign ? 0 : 1); + char* sig_type = d_get_string_at(ctx->params, 2); + if (!sig_type) sig_type = is_eth_sign ? "eth_sign" : "raw"; // if (!pk) return req_set_error(ctx, "Invalid sprivate key! must be 32 bytes long", IN3_EINVAL); if (!data.data) return req_set_error(ctx->req, "Missing message", IN3_EINVAL); @@ -404,53 +458,68 @@ static in3_ret_t in3_sign_data(in3_rpc_handle_ctx_t* ctx) { sc.signature.data[64] += 27; sb_t* sb = in3_rpc_handle_start(ctx); - sb_add_char(sb, '{'); - sb_add_bytes(sb, "\"message\":", &data, 1, false); - sb_add_char(sb, ','); - if (strcmp(sig_type, "raw") == 0) { - bytes32_t hash_val; - bytes_t hash_bytes = bytes(hash_val, 32); - keccak(data, hash_val); - sb_add_bytes(sb, "\"messageHash\":", &hash_bytes, 1, false); + if (is_eth_sign) { + sb_add_rawbytes(sb, "\"0x", sig_bytes, 0); + sb_add_char(sb, '"'); } - else - sb_add_bytes(sb, "\"messageHash\":", &data, 1, false); - sb_add_char(sb, ','); - sb_add_bytes(sb, "\"signature\":", &sig_bytes, 1, false); - sig_bytes = bytes(sc.signature.data, 32); - sb_add_char(sb, ','); - sb_add_bytes(sb, "\"r\":", &sig_bytes, 1, false); - sig_bytes = bytes(sc.signature.data + 32, 32); - sb_add_char(sb, ','); - sb_add_bytes(sb, "\"s\":", &sig_bytes, 1, false); - char v[15]; - sprintf(v, ",\"v\":%d}", (unsigned int) sc.signature.data[64]); - sb_add_chars(sb, v); + else { + sb_add_char(sb, '{'); + sb_add_bytes(sb, "\"message\":", &data, 1, false); + sb_add_char(sb, ','); + if (strcmp(sig_type, "raw") == 0) { + bytes32_t hash_val; + bytes_t hash_bytes = bytes(hash_val, 32); + keccak(data, hash_val); + sb_add_bytes(sb, "\"messageHash\":", &hash_bytes, 1, false); + } + else + sb_add_bytes(sb, "\"messageHash\":", &data, 1, false); + sb_add_char(sb, ','); + sb_add_bytes(sb, "\"signature\":", &sig_bytes, 1, false); + sig_bytes = bytes(sc.signature.data, 32); + sb_add_char(sb, ','); + sb_add_bytes(sb, "\"r\":", &sig_bytes, 1, false); + sig_bytes = bytes(sc.signature.data + 32, 32); + sb_add_char(sb, ','); + sb_add_bytes(sb, "\"s\":", &sig_bytes, 1, false); + char v[15]; + sprintf(v, ",\"v\":%d}", (unsigned int) sc.signature.data[64]); + sb_add_chars(sb, v); + } + _free(sc.signature.data); return in3_rpc_handle_finish(ctx); } -static in3_ret_t in3_cacheClear(in3_rpc_handle_ctx_t* ctx) { - TRY(in3_plugin_execute_first(ctx->req, PLGN_ACT_CACHE_CLEAR, NULL)); - return in3_rpc_handle_with_string(ctx, "true"); -} - static in3_ret_t in3_decryptKey(in3_rpc_handle_ctx_t* ctx) { - d_token_t* keyfile = d_get_at(ctx->params, 0); - bytes_t password_bytes = d_to_bytes(d_get_at(ctx->params, 1)); - bytes32_t dst; + d_token_t* keyfile = d_get_at(ctx->params, 0); + bytes_t password_bytes = d_to_bytes(d_get_at(ctx->params, 1)); + bytes32_t dst; + json_ctx_t* sctx = NULL; if (!password_bytes.data) return req_set_error(ctx->req, "you need to specify a passphrase", IN3_EINVAL); - if (!keyfile || d_type(keyfile) != T_OBJECT) return req_set_error(ctx->req, "no valid key given", IN3_EINVAL); + if (d_type(keyfile) == T_STRING) { + sctx = parse_json(d_string(keyfile)); + if (!sctx) return req_set_error(ctx->req, "invalid keystore-json", IN3_EINVAL); + keyfile = sctx->result; + } + + if (!keyfile || d_type(keyfile) != T_OBJECT) { + if (sctx) json_free(sctx); + return req_set_error(ctx->req, "no valid key given", IN3_EINVAL); + } char* passphrase = alloca(password_bytes.len + 1); memcpy(passphrase, password_bytes.data, password_bytes.len); passphrase[password_bytes.len] = 0; in3_ret_t res = decrypt_key(keyfile, passphrase, dst); + if (sctx) json_free(sctx); if (res) return req_set_error(ctx->req, "Invalid key", res); return in3_rpc_handle_with_bytes(ctx, bytes(dst, 32)); } static in3_ret_t in3_prepareTx(in3_rpc_handle_ctx_t* ctx) { + CHECK_PARAMS_LEN(ctx->req, ctx->params, 1); + CHECK_PARAM_TYPE(ctx->req, ctx->params, 0, T_OBJECT); d_token_t* tx = d_get_at(ctx->params, 0); bytes_t dst = {0}; #if defined(ETH_BASIC) || defined(ETH_FULL) @@ -464,16 +533,34 @@ static in3_ret_t in3_prepareTx(in3_rpc_handle_ctx_t* ctx) { } static in3_ret_t in3_signTx(in3_rpc_handle_ctx_t* ctx) { - bytes_t* data = d_get_bytes_at(ctx->params, 0); - bytes_t* from_b = d_get_bytes_at(ctx->params, 1); + CHECK_PARAMS_LEN(ctx->req, ctx->params, 1) + d_token_t* tx_data = ctx->params + 1; + bytes_t tx_raw = bytes(NULL, 0); + bytes_t* from_b = NULL; + bytes_t* data = NULL; + if (strcmp(ctx->method, "eth_signTransaction") == 0 || d_type(tx_data) == T_OBJECT) { +#if defined(ETH_BASIC) || defined(ETH_FULL) + TRY(eth_prepare_unsigned_tx(tx_data, ctx->req, &tx_raw)) + from_b = d_get_bytes(tx_data, K_FROM); + data = &tx_raw; +#else + return req_set_error(ctx->req, "eth_basic is needed in order to use eth_prepareTx", IN3_EINVAL); +#endif + } + else { + data = d_get_bytes_at(ctx->params, 0); + from_b = d_get_bytes_at(ctx->params, 1); + } + address_t from; memset(from, 0, 20); if (from_b && from_b->data && from_b->len == 20) memcpy(from, from_b->data, 20); bytes_t dst = {0}; #if defined(ETH_BASIC) || defined(ETH_FULL) - TRY(eth_sign_raw_tx(*data, ctx->req, from, &dst)) + TRY_FINAL(eth_sign_raw_tx(*data, ctx->req, from, &dst), _free(tx_raw.data)) #else - if (data || ctx || from[0] || ctx->params) return req_set_error(ctx->req, "eth_basic is needed in order to use eth_prepareTx", IN3_EINVAL); + _free(tx_raw.data); + if (data || ctx || from[0] || ctx->params) return req_set_error(ctx->req, "eth_basic is needed in order to use eth_signTx", IN3_EINVAL); #endif in3_rpc_handle_with_bytes(ctx, dst); _free(dst.data); @@ -485,25 +572,25 @@ static in3_ret_t handle_intern(void* pdata, in3_plugin_act_t action, void* plugi UNUSED_VAR(action); in3_rpc_handle_ctx_t* ctx = plugin_ctx; + TRY_RPC("eth_sign", in3_sign_data(ctx)) + TRY_RPC("eth_signTransaction", in3_signTx(ctx)) + + if (strncmp(ctx->method, "in3_", 4)) return IN3_EIGNORE; // shortcut TRY_RPC("in3_abiEncode", in3_abiEncode(ctx)) TRY_RPC("in3_abiDecode", in3_abiDecode(ctx)) TRY_RPC("in3_checksumAddress", in3_checkSumAddress(ctx)) TRY_RPC("in3_ens", in3_ens(ctx)) - TRY_RPC("web3_sha3", in3_sha3(ctx)) - TRY_RPC("keccak", in3_sha3(ctx)) - TRY_RPC("sha256", in3_sha256(ctx)) TRY_RPC("in3_toWei", in3_toWei(ctx)) - TRY_RPC("in3_config", in3_config(ctx)) - TRY_RPC("in3_getConfig", in3_getConfig(ctx)) + TRY_RPC("in3_fromWei", in3_fromWei(ctx)) TRY_RPC("in3_pk2address", in3_pk2address(ctx)) TRY_RPC("in3_pk2public", in3_pk2address(ctx)) TRY_RPC("in3_ecrecover", in3_ecrecover(ctx)) TRY_RPC("in3_signData", in3_sign_data(ctx)) - TRY_RPC("in3_cacheClear", in3_cacheClear(ctx)) TRY_RPC("in3_decryptKey", in3_decryptKey(ctx)) TRY_RPC("in3_prepareTx", in3_prepareTx(ctx)) TRY_RPC("in3_signTx", in3_signTx(ctx)) + TRY_RPC("in3_calcDeployAddress", in3_calcDeployAddress(ctx)) return IN3_EIGNORE; } diff --git a/c/src/cmd/http-server/http_server.c b/c/src/cmd/http-server/http_server.c index 9f17e89a4..671393550 100644 --- a/c/src/cmd/http-server/http_server.c +++ b/c/src/cmd/http-server/http_server.c @@ -100,12 +100,8 @@ typedef struct queue { req_t* r; } queue_t; -queue_t * q_head = NULL, *q_tail = NULL; -static void error_response(char* message, int error_code) { - char* payload = alloca(strlen(message) + 100); - sprintf(payload, "{\"id\":1,\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"%s\",\"code\":%i}}", message, error_code); - printf("HTTP/1.1 200\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: %lu\r\n\r\n%s\r\n", strlen(payload), payload); -} +queue_t *q_head = NULL, *q_tail = NULL; + static void queue_add(req_t* r) { pthread_mutex_lock(&queue_mutex); queue_t* q = _malloc(sizeof(queue_t)); @@ -148,6 +144,12 @@ static void* thread_run(void* p) { static int clients[MAX_CON]; #endif +static void error_response(char* message, int error_code) { + char* payload = alloca(strlen(message) + 100); + sprintf(payload, "{\"id\":1,\"jsonrpc\":\"2.0\",\"error\":{\"message\":\"%s\",\"code\":%i}}", message, error_code); + printf("HTTP/1.1 200\r\nContent-Type: application/json; charset=utf-8\r\nContent-Length: %lu\r\n\r\n%s\r\n", strlen(payload), payload); +} + //client connection void* respond(void* arg) { req_t* r = arg; diff --git a/c/src/cmd/in3/CMakeLists.txt b/c/src/cmd/in3/CMakeLists.txt index 816096a30..4edf499de 100644 --- a/c/src/cmd/in3/CMakeLists.txt +++ b/c/src/cmd/in3/CMakeLists.txt @@ -32,6 +32,12 @@ # with this program. If not, see . ############################################################################### +if ( NOT CMD_NAME ) + set(CMD_NAME in3) +endif () + +ADD_DEFINITIONS("-DCMD_NAME=\"${CMD_NAME}\"") + IF (IN3_SERVER) ADD_DEFINITIONS(-DIN3_SERVER) set(LIBS ${LIBS} http_server) @@ -45,12 +51,24 @@ if (MULTISIG) set(LIBS ${LIBS} multisig) endif() -add_executable(in3 main.c in3_storage.c ../../tools/recorder/recorder.c) -target_compile_definitions(in3 PRIVATE _XOPEN_SOURCE=600) -target_link_libraries(in3 init pk_signer ${LIBS} -lm) +add_executable(${CMD_NAME} + helper.c + main.c + option_handler.c + rpc_handler.c + req_exec.c + transport.c + tx.c + weights.c + in3_storage.c + ../../tools/recorder/recorder.c +) +target_compile_definitions(${CMD_NAME} PRIVATE _XOPEN_SOURCE=600) + +target_link_libraries(${CMD_NAME} init pk_signer ${LIBS} -lm) -install(TARGETS in3 +install(TARGETS ${CMD_NAME} DESTINATION /usr/local/bin/ PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE diff --git a/c/src/cmd/in3/args.h b/c/src/cmd/in3/args.h new file mode 100644 index 000000000..428acdba8 --- /dev/null +++ b/c/src/cmd/in3/args.h @@ -0,0 +1,196 @@ +// This is a generated file, please don't edit it manually! + +#include + +const char* bool_props[] = {"includeCode", "debug", "keepIn3", "stats", "useBinary", "experimental", "autoUpdateList", "bootWeights", "useHttp", "nodes.needsUpdate", "clearCache", "eth", "wait", "json", "hex", "debug", "quiet", "human", "test-request", "test-health-request", "response.in", "response.out", "onlysign", "noproof", "nostats", "version", "help", NULL}; + +const char* help_args = "\ +--chainId -c the chainId or the name of a known chain\n\ +--finality -f the number in percent needed in order reach finality (% of signature of the validators)\n\ +--includeCode if true, the request should include the codes of all accounts\n\ +--debug if true, debug messages will be written to stderr\n\ +--maxAttempts -a max number of attempts in case a response is rejected\n\ +--keepIn3 -kin3 if true, requests sent to the input sream of the comandline util will be send theor responses in the...\n\ +--stats if true, requests sent will be used for stats\n\ +--useBinary if true the client will use binary format\n\ +--experimental -x if true the client allows to use use experimental features, otherwise a exception is thrown if those...\n\ +--timeout specifies the number of milliseconds before the request times out\n\ +--proof -p if true the nodes should send a proof of the response\n\ +--replaceLatestBlock -l if specified, the blocknumber *latest* will be replaced by blockNumber- specified value\n\ +--autoUpdateList if true the nodelist will be automaticly updated if the lastBlock is newer\n\ +--signatureCount -s number of signatures requested in order to verify the blockhash\n\ +--bootWeights -bw if true, the first request (updating the nodelist) will also fetch the current health status and use...\n\ +--useHttp if true the client will try to use http instead of https\n\ +--minDeposit min stake of the server\n\ +--nodeProps used to identify the capabilities of the node\n\ +--requestCount -rc the number of request send in parallel when getting an answer\n\ +--rpc url of one or more direct rpc-endpoints to use\n\ +--nodes defining the nodelist\n\ +--nodes.contract address of the registry contract\n\ +--nodes.whiteListContract address of the whiteList contract\n\ +--nodes.whiteList manual whitelist\n\ +--nodes.registryId identifier of the registry\n\ +--nodes.needsUpdate if set, the nodeList will be updated before next request\n\ +--nodes.avgBlockTime average block time (seconds) for this chain\n\ +--nodes.verifiedHashes if the client sends an array of blockhashes the server will not deliver any signatures or blockheade...\n\ +--nodes.verifiedHashes.block block number\n\ +--nodes.verifiedHashes.hash verified hash corresponding to block number\n\ +--nodes.nodeList manual nodeList\n\ +--nodes.nodeList.url URL of the node\n\ +--nodes.nodeList.address address of the node\n\ +--nodes.nodeList.props used to identify the capabilities of the node (defaults to 0xFFFF)\n\ +--zksync configuration for zksync-api ( only available if build with `-DZKSYNC=true`, which is on per defaul...\n\ +--zksync.provider_url -zks url of the zksync-server (if not defined it will be choosen depending on the chain)\n\ +--zksync.rest_api -zkr url of the zksync rest api (if not defined it will be choosen depending on the chain)\n\ +--zksync.account -zka the account to be used\n\ +--zksync.sync_key -zsk the seed used to generate the sync_key\n\ +--zksync.main_contract address of the main contract- If not specified it will be taken from the server\n\ +--zksync.signer_type -zkat type of the account\n\ +--zksync.musig_pub_keys -zms concatenated packed public keys (32byte) of the musig signers\n\ +--zksync.musig_urls -zmu a array of strings with urls based on the `musig_pub_keys`\n\ +--zksync.create2 -zc2 create2-arguments for sign_type `create2`\n\ +--zksync.create2.creator The address of contract or EOA deploying the contract ( for example the GnosisSafeFactory )\n\ +--zksync.create2.saltarg a salt-argument, which will be added to the pubkeyhash and create the create2-salt\n\ +--zksync.create2.codehash the hash of the actual deploy-tx including the constructor-arguments\n\ +--zksync.verify_proof_method -zvpm rpc-method, which will be used to verify the incomming proof before cosigning\n\ +--zksync.create_proof_method -zcpm rpc-method, which will be used to create the proof needed for cosigning\n\ +--key -k the client key to sign requests\n\ +--pk -pk registers raw private keys as signers for transactions\n\ +--btc configure the Bitcoin verification\n\ +--btc.maxDAP max number of DAPs (Difficulty Adjustment Periods) allowed when accepting new targets\n\ +--btc.maxDiff max increase (in percent) of the difference between targets when accepting new targets\n\ +--clearCache -ccacheclears the cache before performing any operation\n\ +--eth -e converts the result (as wei) to ether\n\ +--port -port if specified it will run as http-server listening to the given port\n\ +--allowed-methods -am only works if port is specified and declares a comma-seperated list of rpc-methods which are allowed\n\ +--block -b the blocknumber to use when making calls\n\ +--to -to the target address of the call\n\ +--from -from the sender of a call or tx (only needed if no signer is registered)\n\ +--data -d the data for a transaction\n\ +--gas_price -gp the gas price to use when sending transactions\n\ +--gas -gas the gas limit to use when sending transactions\n\ +--nonce -nonce the nonce\n\ +--test -test creates a new json-test written to stdout with the name as specified\n\ +--path -path the HD wallet derivation path \n\ +--sigtype -st the type of the signature data\n\ +--password -pwd password to unlock the key\n\ +--value -value the value to send when sending a transaction\n\ +--wait -w if given, instead returning the transaction, it will wait until the transaction is mined and return ...\n\ +--json -json if given the result will be returned as json, which is especially important for eth_call results wit...\n\ +--hex -hex if given the result will be returned as hex\n\ +--debug -debug if given incubed will output debug information when executing\n\ +--quiet -q quiet\n\ +--human -h human readable, which removes the json -structure and oly displays the values\n\ +--test-request -tr runs test request when showing in3_weights\n\ +--test-health-request -thr runs test request including health-check when showing in3_weights\n\ +--multisig -ms adds a multisig as signer this needs to be done in the right order! (first the pk then the multisaig...\n\ +--ms.signatures -sigs add additional signatures, which will be useds when sending through a multisig!\n\ +--response.in -ri read response from stdin\n\ +--response.out -ro write raw response to stdout\n\ +--file.in -fi reads a prerecorded request from the filepath and executes it with the recorded data\n\ +--file.out -fo records a request and writes the reproducable data in a file (including all cache-data, timestamps \n\ +--nodelist -nl a coma seperated list of urls (or address:url) to be used as fixed nodelist\n\ +--bootnodes -bn a coma seperated list of urls (or address:url) to be used as boot nodes\n\ +--onlysign -os only sign, do not send the raw Transaction\n\ +--noproof -np alias for --proof=none\n\ +--nostats -ns alias for --stats=false, which will mark all requests as not counting in the stats\n\ +--version -v displays the version\n\ +--help -h displays this help message\n\ +\n\ +In addition to the documented rpc-methods, those methods are also supported:\n\ +\n\ +send ...args\n\ + based on the -to, -value and -pk a transaction is build, signed and send.\n\ + if there is another argument after send, this would be taken as a function-signature of the smart contract followed by optional argument of the function.\n\ + \n\ +call ...args\n\ + uses eth_call to call a function. Following the call argument the function-signature and its arguments must follow.\n\ + \n\ +in3_nodeList returns the nodeList of the Incubed NodeRegistry as json.\n\ +in3_sign \n\ + requests a node to sign. in order to specify the signer, you need to pass the url with -c\n\ + \n\ +abi_encode ...args\n\ + encodes the arguments as described in the method signature using ABI-Encoding\n\ + \n\ +abi_decode data\n\ + decodes the data based on the signature.\n\ + \n\ +pk2address \n\ + extracts the public address from a private key\n\ + \n\ +pk2public \n\ + extracts the public key from a private key\n\ + \n\ +ecrecover \n\ + extracts the address and public key from a signature\n\ + \n\ +createKey generates a new private key. See in3_createKey.\n\ + \n\ +key \n\ + reads the private key from JSON-Keystore file and returns the private key.\n\ + \n\ +in3_weights list all current weights and stats\n\ + \n"; + +const char* aliases[] = { + "c", "chainId", + "f", "finality", + "a", "maxAttempts", + "kin3", "keepIn3=true", + "x", "experimental=true", + "p", "proof", + "l", "replaceLatestBlock", + "s", "signatureCount", + "bw", "bootWeights=true", + "rc", "requestCount", + "zks", "zksync.provider_url", + "zkr", "zksync.rest_api", + "zka", "zksync.account", + "zsk", "zksync.sync_key", + "zkat", "zksync.signer_type", + "zms", "zksync.musig_pub_keys", + "zmu", "zksync.musig_urls", + "zc2", "zksync.create2", + "zvpm", "zksync.verify_proof_method", + "zcpm", "zksync.create_proof_method", + "k", "key", + "pk", "pk", + "ccache", "clearCache=true", + "e", "eth=true", + "port", "port", + "am", "allowed-methods", + "b", "block", + "to", "to", + "from", "from", + "d", "data", + "gp", "gas_price", + "gas", "gas", + "nonce", "nonce", + "test", "test", + "path", "path", + "st", "sigtype", + "pwd", "password", + "value", "value", + "w", "wait=true", + "json", "json=true", + "hex", "hex=true", + "debug", "debug=true", + "q", "quiet=true", + "h", "human=true", + "tr", "test-request=true", + "thr", "test-health-request=true", + "ms", "multisig", + "sigs", "ms.signatures", + "ri", "response.in=true", + "ro", "response.out=true", + "fi", "file.in", + "fo", "file.out", + "nl", "nodelist", + "bn", "bootnodes", + "os", "onlysign=true", + "np", "proof=none", + "ns", "stats=false", + "v", "version=true", + "h", "help=true", + NULL}; diff --git a/c/src/cmd/in3/handlers.h b/c/src/cmd/in3/handlers.h new file mode 100644 index 000000000..355c0ec6e --- /dev/null +++ b/c/src/cmd/in3/handlers.h @@ -0,0 +1,6 @@ +#include "../../core/client/plugin.h" + +bool handle_option(in3_t* c, char* key, char* value, sb_t* conf, int argc, char** argv); +bool handle_rpc(in3_t* c, char** method, sb_t* params, int argc, char** argv); +void init_env(in3_t* c, int argc, char* argv[]); +void init_recorder(int* argc, char*** argv); \ No newline at end of file diff --git a/c/src/cmd/in3/helper.c b/c/src/cmd/in3/helper.c new file mode 100644 index 000000000..c37fa4da2 --- /dev/null +++ b/c/src/cmd/in3/helper.c @@ -0,0 +1,404 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ + +/** @file + * simple commandline-util sending in3-requests. + * */ +#include "helper.h" +#include "../../api/eth1/abi.h" +#include "../../api/eth1/eth_api.h" +#include "../../signer/pk-signer/signer.h" +#include "../../tools/recorder/recorder.h" +#ifdef CMD_ARGS_FILE +#include CMD_ARGS_FILE +#else +#include "args.h" +#endif +#include "handlers.h" +#include "transport.h" +#include "tx.h" + +#ifndef IN3_VERSION +#define IN3_VERSION "local" +#endif + +void die(char* msg) { + recorder_print(1, COLORT_RED "Error: %s" COLORT_RESET "\n", msg); + recorder_exit(EXIT_FAILURE); +} +void print_hex(uint8_t* data, int len) { + recorder_print(0, "0x"); + for (int i = 0; i < len; i++) recorder_print(0, "%02x", data[i]); + recorder_print(0, "\n"); +} + +const char* get_help_args() { + return help_args; +} + +// helper to read the password from tty +void read_pass(char* pw, int pwsize) { + int i = 0, ch = 0; + recorder_print(1, COLORT_HIDDEN); //conceal typing and save position + while (true) { + ch = getchar(); + if (ch == '\r' || ch == '\n' || ch == EOF) break; //get characters until CR or NL + if (i < pwsize - 1) { //do not save pw longer than space in pw + pw[i] = ch; //longer pw can be entered but excess is ignored + pw[i + 1] = 0; + } + i++; + } + recorder_print(1, COLORT_RESETHIDDEN); //reveal typing +} + +static bool is_number(char* val) { + for (; *val; val++) { + if (*val > '9' || *val < '0') return false; + } + return true; +} + +void configure_opt(in3_t* c, char* name, char* value, int argc, char** argv) { + sb_t sb = {0}; + // handle options + if (handle_option(c, name, value, &sb, argc, argv)) return; + if (!sb.data) { + char* _name = alloca(strlen(name + 1)); + strcpy(_name, name); + char* p = strtok(_name, "."); + sb_add_char(&sb, '{'); + int b = 1; + while (p) { + char* next = strtok(NULL, "."); + if (!next) { + if (strcmp(value, "true") == 0 || strcmp(value, "false") == 0 || is_number(value)) + sb_print(&sb, "\"%s\":%s", p, value); + else + sb_print(&sb, "\"%s\":\"%s\"", p, value); + break; + } + b++; + sb_print(&sb, "\"%s\":{", p); + p = next; + continue; + } + for (; b; b--) sb_add_char(&sb, '}'); + } + char* error = in3_configure(c, sb.data); + _free(sb.data); + if (error) { + char* msg = _malloc(200 + (strlen(error) + strlen(name) + strlen(value))); + sprintf(msg, "Invalid option '--%s=%s' : %s", name, value, error); + die(msg); + } +} +void configure(in3_t* c, char* name, char* value) { + configure_opt(c, name, value, 0, NULL); +} +static bool is_bool_opt(char* name) { + for (int i = 0; bool_props[i]; i++) { + if (strcmp(bool_props[i], name) == 0) return true; + } + return false; +} +bool configure_arg(in3_t* c, char** args, int* index, int argc) { + const char* arg = args[*index]; + char* value = strchr(arg, '='); + char* name = NULL; + if (arg[0] != '-') return false; + if (arg[1] && arg[1] != '-') { + for (int i = 0; aliases[i]; i += 2) { + if (strcmp(aliases[i], arg + 1) == 0) { + name = alloca(strlen(aliases[i + 1]) + 1); + strcpy(name, aliases[i + 1]); + value = strchr(aliases[i + 1], '='); + if (value) { + *strchr(name, '=') = 0; + value++; + } + break; + } + } + if (!name) { + char* err = alloca(strlen(arg) + 200); + sprintf(err, "Unknown option '%s'!", arg); + die(err); + } + } + else if (arg[1] != '-') + return false; + + if (!name) { + if (value) { + value++; + name = alloca(value - arg - 2); + strncpy(name, arg + 2, value - arg - 3); + name[value - arg - 1] = 0; + } + else { + name = alloca(strlen(arg) - 1); + strcpy(name, arg + 2); + } + } + + if (!value) { + if (is_bool_opt(name)) + value = "true"; + else { + if (argc - 1 <= *index) die("missing value for option"); + *index += 1; + value = args[*index]; + } + } + + configure_opt(c, name, value, argc, args); + return true; +} + +// accepts a value as +// 0.1eth +// 2keth +// 2.3meth +char* get_wei(char* val) { + if (*val == '0' && val[1] == 'x') return val; + bytes32_t tmp; + int s = string_val_to_bytes(val, NULL, tmp); + if (s < 0) die("Invalid numeric value"); + char* res = _malloc(s * 2 + 3); + bytes_to_hex(tmp, s, res + 2); + if (res[2] == '0') res++; + res[0] = '0'; + res[1] = 'x'; + return res; +} + +char* resolve(in3_t* c, char* name) { + if (!name) return NULL; + if (name[0] == '0' && name[1] == 'x') return name; + if (strstr(name, ".eth")) { + char* params = alloca(strlen(name) + 10); + sprintf(params, "[\"%s\"]", name); + char *res = NULL, *err = NULL; + in3_client_rpc(c, "in3_ens", params, &res, &err); + if (err) { + res = alloca(strlen(err) + 100); + sprintf(res, "Could not resolve %s : %s", name, err); + die(res); + } + if (res[0] == '"') { + res[strlen(res) - 1] = 0; + res++; + } + return res; + } + return name; +} + +// read data from a file and return the bytes +bytes_t readFile(FILE* f) { + if (!f) die("Could not read the input file"); + size_t allocated = 1024, len = 0, r; + uint8_t* buffer = _malloc(1025); + while (1) { + r = fread(buffer + len, 1, allocated - len, f); + len += r; + if (feof(f)) break; + size_t new_alloc = allocated * 2 + 1; + buffer = _realloc(buffer, new_alloc, allocated); + allocated = new_alloc; + } + buffer[len] = 0; + return bytes(buffer, len); +} + +// read from stdin and try to optimize hexdata. +bytes_t* get_std_in() { + if (feof(stdin)) return NULL; + bytes_t content = readFile(stdin); + + // this check tries to discover a solidity compilation poutput + char* bin = strstr((char*) content.data, "\nBinary: \n"); + if (bin) { + bin += 10; + char* end = strstr(bin, "\n"); + if (end) + return hex_to_new_bytes(bin, end - bin); + } + + // is it content starting with 0x, we treat it as hex otherwisae as rwa string + bytes_t* res = (content.len > 1 && *content.data == '0' && content.data[1] == 'x') + ? hex_to_new_bytes((char*) content.data + 2, content.len - 2) + : hex_to_new_bytes((char*) content.data, content.len); + _free(content.data); + return res; +} + +void print_val(d_token_t* t) { + switch (d_type(t)) { + case T_ARRAY: + case T_OBJECT: { + char* level = d_get_string(t, key("level")); + if (level) { + char* msg = d_get_string(t, key("msg")); + if (strcmp(level, "main") == 0) recorder_print(0, COLOR_GREEN_STR "\n", msg); + if (strcmp(level, "info") == 0) recorder_print(0, "%s\n", msg); + if (strcmp(level, "warning") == 0) recorder_print(0, COLOR_YELLOW_STR "\n", msg); + if (strcmp(level, "error") == 0) recorder_print(0, COLOR_RED_STR "\n", msg); + } + else { + for (d_iterator_t it = d_iter(t); it.left; d_iter_next(&it)) + print_val(it.token); + } + } break; + case T_BOOLEAN: + recorder_print(0, "%s\n", d_int(t) ? "true" : "false"); + break; + case T_INTEGER: + recorder_print(0, "%i\n", d_int(t)); + break; + case T_BYTES: + if (t->len < 9) + recorder_print(0, "%" PRId64 "\n", d_long(t)); + else { + recorder_print(0, "0x"); + for (int i = 0; i < (int) t->len; i++) recorder_print(0, "%02x", t->data[i]); + recorder_print(0, "\n"); + } + break; + case T_NULL: + recorder_print(0, "NULL\n"); + break; + case T_STRING: + recorder_print(0, "%s\n", d_string(t)); + break; + } +} +// decode pk +void read_pk(char* pk_file, char* pwd, in3_t* c, char* method) { + if (pk_file) { + if (!pwd) { + recorder_print(1, "Passphrase:\n"); + pwd = malloc(500); + read_pass(pwd, 500); + } + char* content; + if (strcmp(pk_file, "-") == 0) + content = (char*) readFile(stdin).data; + else if (pk_file[0] == '{') + content = pk_file; + else + content = (char*) readFile(fopen(pk_file, "r")).data; + + json_ctx_t* key_json = parse_json(content); + if (!key_json) die("invalid json in pk file"); + + uint8_t* pk_seed = malloc(32); + if (decrypt_key(key_json->result, pwd, pk_seed)) die("Invalid key"); + + if (!c && method && (strcmp(method, "keystore") == 0 || strcmp(method, "key") == 0)) { + char tmp[64]; + bytes_to_hex(pk_seed, 32, tmp); + recorder_print(0, "0x%s\n", tmp); + recorder_exit(0); + } + else + eth_set_pk_signer(c, pk_seed); + } +} + +char* get_argument(int argc, char* argv[], char* alias, char* arg, bool has_value) { + int l = strlen(arg); + for (int i = 1; i < argc; i++) { + if (alias && strcmp(alias, argv[i]) == 0) + return has_value ? (i + 1 < argc ? argv[i + 1] : NULL) : argv[i]; + if (strncmp(arg, argv[i], l) == 0) { + if (argv[i][l] == 0) + return has_value ? (i + 1 < argc ? argv[i + 1] : NULL) : argv[i]; + else if (argv[i][l] == '=') + return argv[i] + l + 1; + } + } + return NULL; +} + +static uint32_t conf = 0; +uint32_t* get_output_conf() { + return &conf; +} + +void display_result(char* method, char* result) { + // if the result is a string, we remove the quotes + if ((conf & out_human) == 0 && result[0] == '"' && result[strlen(result) - 1] == '"') { + memmove(result, result + 1, strlen(result)); + result[strlen(result) - 1] = 0; + } + + abi_sig_t* req = get_txdata()->abi_sig; + + // if the request was a eth_call, we decode the result + if (req) { + int l = strlen(result) / 2 - 1; + if (l) { + char* error = NULL; + uint8_t* tmp = alloca(l + 1); + json_ctx_t* res = abi_decode(req, bytes(tmp, hex_to_bytes(result, -1, tmp, l + 1)), &error); + if (error) die(error); + if (conf & out_json) + recorder_print(0, "%s\n", d_create_json(res, res->result)); + else + print_val(res->result); + } + // if not we simply print the result + } + else if (conf & out_human) { + json_ctx_t* jctx = parse_json(result); + if (jctx) + print_val(jctx->result); + else + recorder_print(0, "%s\n", result); + } + else if (is_onlyshow_rawtx() && strcmp(method, "in3_prepareTx") == 0 && get_txdata()->from) + recorder_print(0, "%s %s\n", result, get_txdata()->from); + else { + if (conf & out_eth && result[0] == '0' && result[1] == 'x' && strlen(result) <= 18) { + double val = char_to_long(result, strlen(result)); + recorder_print(0, "%.3f\n", val / 1000000000000000000L); + } + else if ((conf & out_hex) == 0 && result[0] == '0' && result[1] == 'x' && strlen(result) <= 18) + recorder_print(0, "%" PRIu64 "\n", char_to_long(result, strlen(result))); + else + recorder_print(0, "%s\n", result); + } +} \ No newline at end of file diff --git a/c/src/cmd/in3/helper.h b/c/src/cmd/in3/helper.h new file mode 100644 index 000000000..ca41f23b1 --- /dev/null +++ b/c/src/cmd/in3/helper.h @@ -0,0 +1,64 @@ + +#ifndef MAIN_HELPER_H +#define MAIN_HELPER_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "../../core/client/keys.h" +#include "../../core/client/plugin.h" +#include "../../core/client/version.h" +#include "../../core/util/bitset.h" +#include "../../core/util/colors.h" +#include "../../core/util/data.h" +#include "../../core/util/debug.h" +#include "../../core/util/log.h" +#include "../../core/util/mem.h" +#include "../../tools/recorder/recorder.h" +#include +#include +#include +#include +#include +#include + +_Noreturn void die(char* msg); + +void print_hex(uint8_t* data, int len); + +void read_pass(char* pw, int pwsize); + +void configure(in3_t* c, char* name, char* value); +bool configure_arg(in3_t* c, char** args, int* index, int argc); + +char* get_wei(char* val); + +char* resolve(in3_t* c, char* name); + +bytes_t readFile(FILE* f); + +bytes_t* get_std_in(); + +void read_pk(char* pk_file, char* pwd, in3_t* c, char* method); + +void print_val(d_token_t* t); + +char* get_argument(int argc, char* argv[], char* alias, char* arg, bool has_value); + +uint32_t* get_output_conf(); +const char* get_help_args(); +typedef enum output { + out_human = 1, + out_hex = 2, + out_json = 4, + out_eth = 8, + out_debug = 16 +} output_t; + +void display_result(char* method, char* result); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/c/src/cmd/in3/in3.yml b/c/src/cmd/in3/in3.yml new file mode 100644 index 000000000..f964e569c --- /dev/null +++ b/c/src/cmd/in3/in3.yml @@ -0,0 +1,239 @@ +config: + clearCache : + descr: clears the cache before performing any operation. + cmd: ccache + type: bool + example: true + eth : + cmd: e + type: bool + descr: converts the result (as wei) to ether. + example: true + port : + cmd: port + type: uint + descr: if specified it will run as http-server listening to the given port. + example: 8545 + allowed-methods : + cmd: am + type: string + descr: only works if port is specified and declares a comma-seperated list of rpc-methods which are allowed. All other will be rejected. + example: eth_sign,eth_blockNumber + block: + cmd: b + type: uint + descr: the blocknumber to use when making calls. could be either latest (default),earliest or a hexnumbner + example: latest + to: + cmd: to + type: address + descr: the target address of the call + example: '0x7d1c10184fa178ebb5b10a9aa6230a255c5c59f6' + from : + cmd: from + type: address + descr: the sender of a call or tx (only needed if no signer is registered) + example: '0x7d1c10184fa178ebb5b10a9aa6230a255c5c59f6' + data : + cmd: d + type: bytes + descr: the data for a transaction. This can be a filepath, a 0x-hexvalue or - for stdin. + example: '0x7d1c101' + gas_price: + cmd: gp + type: uint + descr: 'the gas price to use when sending transactions. (default: use eth_gasPrice)' + example: 1000000000000 + gas : + cmd: gas + type: uint + descr: 'the gas limit to use when sending transactions. (default: 100000)' + example: 100000 + nonce : + cmd: nonce + type: uint + descr: 'the nonce. (default: will be fetched useing eth_getTransactionCount)' + example: 2 + test : + cmd: test + type: string + descr: creates a new json-test written to stdout with the name as specified. + example: test_blockNumber + path : + cmd: path + type: string + descr: 'the HD wallet derivation path . We can pass in simplified way as hex string i.e [44,60,00,00,00] => 0x2c3c000000' + example: '0x2c3c000000' + sigtype: + cmd: st + type: string + enum: + raw: hash the raw data + hash: use the already hashed data + eth_sign: use the prefix and hash it + default: raw + descr: the type of the signature data. + example: hash + password: + cmd: pwd + type: string + descr: password to unlock the key + example: MYPASSWORD + value : + cmd: value + type: uint + descr: 'the value to send when sending a transaction. can be hexvalue or a float/integer with the suffix eth or wei like 1.8eth (default: 0)' + example: '0.2eth' + wait: + cmd: w + type: bool + descr: if given, instead returning the transaction, it will wait until the transaction is mined and return the transactionreceipt. + example: true + json : + cmd: json + type: bool + descr: if given the result will be returned as json, which is especially important for eth_call results with complex structres. + example: true + hex : + cmd: hex + type: bool + descr: if given the result will be returned as hex. + example: true + debug : + cmd: debug + type: bool + descr: if given incubed will output debug information when executing. + example: true + quiet: + cmd: q + type: bool + descr: quiet. no additional output. + example: true + human : + cmd: h + type: bool + descr: human readable, which removes the json -structure and oly displays the values. + example: true + test-request: + cmd: tr + type: bool + descr: runs test request when showing in3_weights + example: true + test-health-request: + cmd: thr + type: bool + descr: runs test request including health-check when showing in3_weights + example: true + multisig : + cmd: ms + type: address + descr: adds a multisig as signer this needs to be done in the right order! (first the pk then the multisaig(s) ) + example: '0x7d1c10184fa178ebb5b10a9aa6230a255c5c59f6' + ms.signatures : + cmd: sigs + type: bytes + descr: add additional signatures, which will be useds when sending through a multisig! + example: 0x7d1c10184fa178ebb5b10a9aa6230a255c5c59f6ab00f111c32258f3a53bd1dead143dd5d7eae3737c7b0f21843afcdd27a1b8f0 + response.in: + cmd: ri + type: bool + descr: read response from stdin + example: true + response.out: + cmd: ro + type: bool + descr: write raw response to stdout + example: true + file.in: + cmd: fi + type: string + descr: reads a prerecorded request from the filepath and executes it with the recorded data. (great for debugging) + example: record.txt + file.out: + cmd: fo + type: string + descr: records a request and writes the reproducable data in a file (including all cache-data, timestamps ...) + example: record.txt + nodelist: + cmd: nl + type: string + descr: a coma seperated list of urls (or address:url) to be used as fixed nodelist + example: 'https://in3-v2.slock.it/mainnet/nd-1,https://mainnet.incubed.net' + bootnodes: + cmd: bn + type: string + descr: a coma seperated list of urls (or address:url) to be used as boot nodes + example: 'https://in3-v2.slock.it/mainnet/nd-1,https://mainnet.incubed.net' + onlysign: + cmd: os + type: bool + descr: only sign, do not send the raw Transaction + example: true + noproof: + alias: proof=none + cmd: np + type: bool + descr: alias for --proof=none + example: true + nostats: + alias: stats=false + cmd: ns + type: bool + descr: alias for --stats=false, which will mark all requests as not counting in the stats + example: true + version: + cmd: v + type: bool + descr: displays the version + example: true + help: + cmd: h + type: bool + descr: displays this help message + example: true + +rpc: + send: | + ...args + based on the -to, -value and -pk a transaction is build, signed and send. + if there is another argument after send, this would be taken as a function-signature of the smart contract followed by optional argument of the function. + + call: | + ...args + uses eth_call to call a function. Following the call argument the function-signature and its arguments must follow. + + in3_nodeList: returns the nodeList of the Incubed NodeRegistry as json. + + in3_sign: | + + requests a node to sign. in order to specify the signer, you need to pass the url with -c + + abi_encode: | + ...args + encodes the arguments as described in the method signature using ABI-Encoding + + abi_decode: | + data + decodes the data based on the signature. + + pk2address: | + + extracts the public address from a private key + + pk2public: | + + extracts the public key from a private key + + ecrecover: | + + extracts the address and public key from a signature + + createKey: | + generates a new private key. See in3_createKey. + + key: | + + reads the private key from JSON-Keystore file and returns the private key. + + in3_weights: | + list all current weights and stats diff --git a/c/src/cmd/in3/main.c b/c/src/cmd/in3/main.c index ea0784ce8..d10a7cb89 100644 --- a/c/src/cmd/in3/main.c +++ b/c/src/cmd/in3/main.c @@ -1,1444 +1,95 @@ -/******************************************************************************* - * This file is part of the Incubed project. - * Sources: https://github.com/blockchainsllc/in3 - * - * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC - * - * - * COMMERCIAL LICENSE USAGE - * - * Licensees holding a valid commercial license may use this file in accordance - * with the commercial license agreement provided with the Software or, alternatively, - * in accordance with the terms contained in a written agreement between you and - * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further - * information please contact slock.it at in3@slock.it. - * - * Alternatively, this file may be used under the AGPL license as follows: - * - * AGPL LICENSE USAGE - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Affero General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - * [Permissions of this strong copyleft license are conditioned on making available - * complete source code of licensed works and modifications, which include larger - * works using a licensed work, under the same license. Copyright and license notices - * must be preserved. Contributors provide an express grant of patent rights.] - * You should have received a copy of the GNU Affero General Public License along - * with this program. If not, see . - *******************************************************************************/ - -/** @file - * simple commandline-util sending in3-requests. - * */ -#include "../../api/eth1/abi.h" -#include "../../api/eth1/eth_api.h" -#include "../../api/ipfs/ipfs_api.h" -#include "../../core/util/bitset.h" -#include "../../core/util/data.h" -#include "../../core/util/debug.h" -#include "../../core/util/log.h" -#include "../../core/util/mem.h" -#include "../../third-party/crypto/ecdsa.h" -#include "../../third-party/crypto/rand.h" -#include "../../third-party/crypto/secp256k1.h" -#ifdef USE_CURL -#include "../../transport/curl/in3_curl.h" -#elif USE_WINHTTP -#include "../../transport/winhttp/in3_winhttp.h" -#else -#include "../../transport/http/in3_http.h" -#endif -#ifdef IN3_SERVER -#include "../http-server/http_server.h" -#endif -#include "../../core/client/keys.h" -#include "../../core/client/plugin.h" -#include "../../core/client/version.h" -#include "../../core/util/colors.h" -#include "../../nodeselect/full/cache.h" -#include "../../nodeselect/full/nodelist.h" - -#if defined(LEDGER_NANO) -#include "../../signer/ledger-nano/signer/ethereum_apdu_client.h" -#include "../../signer/ledger-nano/signer/ethereum_apdu_client_priv.h" -#include "../../signer/ledger-nano/signer/ledger_signer.h" -#endif - #include "../../init/in3_init.h" +#include "../../nodeselect/full/nodelist.h" #include "../../nodeselect/full/nodeselect_def.h" -#include "../../signer/multisig/multisig.h" -#include "../../signer/pk-signer/signer.h" -#include "../../tools/recorder/recorder.h" -#include "../../verifier/eth1/nano/chainspec.h" -#include "in3_storage.h" -#include -#include -#include -#include -#include -#include - -#ifndef IN3_VERSION -#define IN3_VERSION "local" -#endif - -// helpstring -void show_help(char* name) { - recorder_print(0, "Usage: %s method ... \n\ -\n\ --c, -chain the chain to use. (mainnet, goerli, ewc, btc, ipfs, local or any RPCURL)\n\ --a max number of attempts before giving up (default 5)\n\ --rc number of request per try (default 1)\n\ --ns no stats if set requests will not be part of the official metrics and considered a service request\n\ --p, -proof specifies the Verification level: (none, standard(default), full)\n\ --md specifies the minimum Deposit of a node in order to be selected as a signer\n\ --np short for -p none\n\ --eth converts the result (as wei) to ether.\n\ --l, -latest replaces \"latest\" with latest BlockNumber - the number of blocks given.\n\ --s, -signs number of signatures to use when verifying.\n\ --f finality : number of blocks on top of the current one.\n\ --port if specified it will run as http-server listening to the given port.\n\ --am only works if port is specified and declares a comma-seperated list of rpc-methods which are allowed. All other will be rejected.\n\ --b, -block the blocknumber to use when making calls. could be either latest (default),earliest or a hexnumbner\n\ --to the target address of the call\n\ --d, -data the data for a transaction. This can be a filepath, a 0x-hexvalue or - for stdin.\n\ --gp,-gas_price the gas price to use when sending transactions. (default: use eth_gasPrice) \n\ --gas the gas limit to use when sending transactions. (default: 100000) \n\ --pk the private key as raw as keystorefile \n\ --path the HD wallet derivation path . We can pass in simplified way as hex string i.e [44,60,00,00,00] => 0x2c3c000000 \n\ --st, -sigtype the type of the signature data : eth_sign (use the prefix and hash it), raw (hash the raw data), hash (use the already hashed data). Default: raw \n\ --pwd password to unlock the key \n\ --value the value to send when sending a transaction. can be hexvalue or a float/integer with the suffix eth or wei like 1.8eth (default: 0)\n\ --w, -wait if given, instead returning the transaction, it will wait until the transaction is mined and return the transactionreceipt.\n\ --json if given the result will be returned as json, which is especially important for eth_call results with complex structres.\n\ --hex if given the result will be returned as hex.\n\ --kin3 if kin3 is specified, the response including in3-section is returned\n\ --bw initialize with weights from boot nodes.\n\ --debug if given incubed will output debug information when executing. \n\ --k 32bytes raw private key to sign requests.\n\ --q quit. no additional output. \n\ --tr runs test request when showing in3_weights \n\ --thr runs test request including health-check when showing in3_weights \n\ --ms adds a multisig as signer this needs to be done in the right order! (first the pk then the multisaig(s) ) \n\ --sigs add additional signatures, which will be useds when sending through a multisig! \n\ --ri read response from stdin \n\ --ro write raw response to stdout \n\ --fi reads a prerecorded request from the filepath and executes it with the recorded data. (great for debugging) \n\ --fo records a request and writes the reproducable data in a file (including all cache-data, timestamps ...) \n\ --nl a coma seperated list of urls (or address:url) to be used as fixed nodelist\n\ --bn a coma seperated list of urls (or address:url) to be used as boot nodes\n\ --zks zksync server to use\n\ --zkss zksync signatures to pass along when signing\n\ --zka zksync account to use\n\ --zkat zksync account type could be one of 'pk'(default), 'contract' or 'create2'\n\ --zsk zksync signer seed (if not set this key will be derrived from account unless create2)\n\ --zc2 zksync create2 arguments in the form ::. if set the account type is also changed to create2\n\ --zms public keys of a musig schnorr signatures to sign with\n\ --zmu url for signing service matching the first remote public key\n\ --zvpm method for calling to verify the proof\n\ --zcpm method for calling to create the proof\n\ --os only sign, don't send the raw Transaction \n\ --x support experimental features \n\ --version displays the version \n\ --help displays this help message \n\ -\n\ -As method, the following can be used:\n\ -\n\ --method\n\ - all official supported JSON-RPC-Method may be used.\n\ -\n\ -send ...args\n\ - based on the -to, -value and -pk a transaction is build, signed and send. \n\ - if there is another argument after send, this would be taken as a function-signature of the smart contract followed by optional argument of the function.\n\ -\n\ -call ...args\n\ - uses eth_call to call a function. Following the call argument the function-signature and its arguments must follow. \n\ -\n\ -in3_nodeList\n\ - returns the nodeList of the Incubed NodeRegistry as json.\n\ -\n\ -in3_sign \n\ - requests a node to sign. in order to specify the signer, you need to pass the url with -c\n\ -\n\ -ipfs_get \n\ - requests and verifies the content for a given ipfs-hash and write the content to stdout\n\ -\n\ -ipfs_put\n\ - reads a content from stdin and pushes to the ipfs-network. it write the ipfs-hash to stdout.\n\ -\n\ -in3_stats\n\ - returns the stats of a node. unless you specify the node with -c it will pick a random node.\n\ -\n\ -abi_encode ...args\n\ - encodes the arguments as described in the method signature using ABI-Encoding\n\ -\n\ -abi_decode data\n\ - decodes the data based on the signature.\n\ -\n\ -pk2address \n\ - extracts the public address from a private key\n\ -\n\ -pk2public \n\ - extracts the public key from a private key\n\ -\n\ -ecrecover \n\ - extracts the address and public key from a signature\n\ -\n\ -key \n\ - reads the private key from JSON-Keystore file and returns the private key.\n\ -\n\ -in3_weights\n\ - list all current weights and stats\n\ -\n\ -in3_ens \n\ - resolves a ens-domain. field can be addr(deault), owner, resolver or hash\n\ -\n", - name); -} -_Noreturn static void die(char* msg) { - recorder_print(1, COLORT_RED "Error: %s" COLORT_RESET "\n", msg); - recorder_exit(EXIT_FAILURE); -} -static void _configure(in3_t* c, char* k, const char* p, char* val) { - char* pattern = alloca(strlen(val) + strlen(k) + strlen(p) + 8); - char* data = alloca(strlen(val) + strlen(k) + 8); - sprintf(pattern, "{\"%s\": %s}", "%s", p); - sprintf(data, pattern, k, val); - char* e = in3_configure(c, data); - if (e) { - char* tmp = alloca(strlen(data) + strlen(e) + 60); - sprintf(tmp, "Error configuring the client with config (%s): %s", data, e); - die(tmp); - } -} -static void _configure2(in3_t* c, char* k1, char* k2, const char* p, char* val) { - char* pattern = alloca(strlen(val) + strlen(k1) + strlen(k2) + 16); - char* data = alloca(strlen(val) + strlen(k1) + strlen(k2) + 16); - sprintf(pattern, "{\"%s\":{\"%s\" : %s}}", "%s", "%s", p); - sprintf(data, pattern, k1, k2, val); - char* e = in3_configure(c, data); - if (e) die(e); -} - -#define configure(s, p) _configure(c, s, "\"%s\"", p) -#define configure_2(s1, s2, p) _configure2(c, s1, s2, "\"%s\"", p) - -static bool debug_mode = false; -static void print_hex(uint8_t* data, int len) { - recorder_print(0, "0x"); - for (int i = 0; i < len; i++) recorder_print(0, "%02x", data[i]); - recorder_print(0, "\n"); -} -// helper to read the password from tty -void read_pass(char* pw, int pwsize) { - int i = 0, ch = 0; - recorder_print(1, COLORT_HIDDEN); //conceal typing and save position - while (true) { - ch = getchar(); - if (ch == '\r' || ch == '\n' || ch == EOF) break; //get characters until CR or NL - if (i < pwsize - 1) { //do not save pw longer than space in pw - pw[i] = ch; //longer pw can be entered but excess is ignored - pw[i + 1] = 0; - } - i++; - } - recorder_print(1, COLORT_RESETHIDDEN); //reveal typing -} - -// accepts a value as -// 0.1eth -// 2keth -// 2.3meth -char* get_wei(char* val) { - if (*val == '0' && val[1] == 'x') return val; - bytes32_t tmp; - int s = string_val_to_bytes(val, NULL, tmp); - if (s < 0) die("Invalid numeric value"); - char* res = _malloc(s * 2 + 3); - bytes_to_hex(tmp, s, res + 2); - if (res[2] == '0') res++; - res[0] = '0'; - res[1] = 'x'; - return res; -} -static void execute(in3_t* c, FILE* f) { - if (feof(f)) die("no data"); - sb_t* sb = sb_new(NULL); - char first = 0, stop = 0; - int level = 0, d = 0; - while (1) { - d = fgetc(f); - if (d == EOF) { - if (first) - die("Invalid json-data from stdin"); - else - recorder_exit(EXIT_SUCCESS); - } - if (first == 0) { - if (d == '{') - stop = '}'; - else if (d == '[') - stop = ']'; - else - continue; - first = d; - } - - sb_add_char(sb, (char) d); - if (d == first) level++; - if (d == stop) level--; - if (level == 0) { - // time to execute - in3_req_t* ctx = req_new(c, sb->data); - if (ctx->error) - recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"error\":{\"code\":%i,\"message\":\"%s\"}\n", 1, ctx->verification_state, ctx->error); - else { - in3_ret_t ret = in3_send_req(ctx); - uint32_t id = d_get_int(ctx->requests[0], K_ID); - if (ctx->error) { - for (char* x = ctx->error; *x; x++) { - if (*x == '\n') *x = ' '; - } - } - - if (ret == IN3_OK) { - if (c->flags & FLAGS_KEEP_IN3) { - str_range_t rr = d_to_json(ctx->responses[0]); - rr.data[rr.len] = 0; - recorder_print(0, "%s\n", rr.data); - } - else { - d_token_t* result = d_get(ctx->responses[0], K_RESULT); - d_token_t* error = d_get(ctx->responses[0], K_ERROR); - char* r = d_create_json(ctx->response_context, result ? result : error); - if (result) - recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"result\":%s}\n", id, r); - else - recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"error\":%s}\n", id, r); - _free(r); - } - } - else - recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"error\":{\"code\":%i,\"message\":\"%s\"}}\n", id, ctx->verification_state, ctx->error == NULL ? "Unknown error" : ctx->error); - } - req_free(ctx); - first = 0; - sb->len = 0; - } - } -} - -char* resolve(in3_t* c, char* name) { - if (!name) return NULL; - if (name[0] == '0' && name[1] == 'x') return name; - if (strstr(name, ".eth")) { - char* params = alloca(strlen(name) + 10); - sprintf(params, "[\"%s\"]", name); - char *res = NULL, *err = NULL; - in3_client_rpc(c, "in3_ens", params, &res, &err); - if (err) { - res = alloca(strlen(err) + 100); - sprintf(res, "Could not resolve %s : %s", name, err); - die(res); - } - if (res[0] == '"') { - res[strlen(res) - 1] = 0; - res++; - } - return res; - } - return name; -} - -// read data from a file and return the bytes -bytes_t readFile(FILE* f) { - if (!f) die("Could not read the input file"); - size_t allocated = 1024, len = 0, r; - uint8_t* buffer = _malloc(1025); - while (1) { - r = fread(buffer + len, 1, allocated - len, f); - len += r; - if (feof(f)) break; - size_t new_alloc = allocated * 2 + 1; - buffer = _realloc(buffer, new_alloc, allocated); - allocated = new_alloc; - } - buffer[len] = 0; - return bytes(buffer, len); -} - -// read from stdin and try to optimize hexdata. -bytes_t* get_std_in() { - if (feof(stdin)) return NULL; - bytes_t content = readFile(stdin); - - // this check tries to discover a solidity compilation poutput - char* bin = strstr((char*) content.data, "\nBinary: \n"); - if (bin) { - bin += 10; - char* end = strstr(bin, "\n"); - if (end) - return hex_to_new_bytes(bin, end - bin); - } - - // is it content starting with 0x, we treat it as hex otherwisae as rwa string - bytes_t* res = (content.len > 1 && *content.data == '0' && content.data[1] == 'x') - ? hex_to_new_bytes((char*) content.data + 2, content.len - 2) - : hex_to_new_bytes((char*) content.data, content.len); - _free(content.data); - return res; -} +#include "handlers.h" +#include "helper.h" +#include "req_exec.h" +#include "transport.h" +#include "tx.h" -// convert the name to a chain_id -uint64_t getchain_id(char* name) { - if (strcmp(name, "mainnet") == 0) return CHAIN_ID_MAINNET; - if (strcmp(name, "goerli") == 0) return CHAIN_ID_GOERLI; - if (strcmp(name, "ewc") == 0) return CHAIN_ID_EWC; - if (strcmp(name, "ipfs") == 0) return CHAIN_ID_IPFS; - if (strcmp(name, "btc") == 0) return CHAIN_ID_BTC; - if (strcmp(name, "local") == 0) return CHAIN_ID_LOCAL; - if (name[0] == '0' && name[1] == 'x') { - bytes32_t d; - return bytes_to_long(d, hex_to_bytes(name + 2, -1, d, 32)); - } - die("Unknown or unsupported chain"); - return 0; -} - -// set the chain_id in the client -void set_chain_id(in3_t* c, char* id) { - c->chain.chain_id = strstr(id, "://") ? CHAIN_ID_LOCAL : getchain_id(id); - if (c->chain.chain_id == CHAIN_ID_LOCAL) { - sb_t* sb = sb_new("{\"autoUpdateList\":false,\"proof\":\"none\""); - sb_add_chars(sb, ",\"nodeRegistry\":{\"needsUpdate\":false,\"nodeList\":["); - sb_add_chars(sb, "{\"address\":\"0x0000000000000000000000000000000000000000\""); - sb_add_chars(sb, ",\"url\":\""); - sb_add_chars(sb, strstr(id, "://") ? id : "http://localhost:8545"); - sb_add_chars(sb, "\",\"props\":\"0xffff\"}"); - sb_add_chars(sb, "]}}}"); - char* err = in3_configure(c, sb->data); - if (err) - die(err); - sb_free(sb); - } -} - -// prepare a eth_call or eth_sendTransaction -abi_sig_t* prepare_tx(char* fn_sig, char* to, sb_t* args, char* block_number, uint64_t gas, uint64_t gas_price, char* value, bytes_t* data) { - char* error = NULL; - bytes_t rdata = {0}; - abi_sig_t* req = fn_sig ? abi_sig_create(fn_sig, &error) : NULL; // only if we have a function signature, we will parse it and create a call_request. - if (error) die(error); // parse-error we stop here. - if (req) { // if type is a tuple, it means we have areuments we need to parse. - json_ctx_t* in_data = parse_json(args->data); // the args are passed as a "[]"- json-array string. - rdata = abi_encode(req, in_data->result, &error); //encode data - if (error) die(error); // we then set the data, which appends the arguments to the functionhash. - json_free(in_data); // of course we clean up ;-) - } // - sb_t* params = sb_new("[{"); // now we create the transactionobject as json-argument. - if (to) { // if this is a deployment we must not include the to-property - sb_add_chars(params, "\"to\":\""); - sb_add_chars(params, to); - sb_add_chars(params, "\" "); - } - if (req || data) { // if we have a request context or explicitly data we create the data-property - if (params->len > 2) sb_add_char(params, ','); // add comma if this is not the first argument - sb_add_chars(params, "\"data\":"); // we will have a data-property - if (req && data) { // if we have a both, we need to concat thewm (this is the case when depkloying a contract with constructorarguments) - uint8_t* full = _malloc(rdata.len - 4 + data->len); // in this case we skip the functionsignature. - memcpy(full, data->data, data->len); - memcpy(full + data->len, rdata.data + 4, rdata.len - 4); - bytes_t bb = bytes(full, rdata.len - 4 + data->len); - sb_add_bytes(params, "", &bb, 1, false); - _free(full); - } - else if (req) - sb_add_bytes(params, "", &rdata, 1, false); - else if (data) - sb_add_bytes(params, "", data, 1, false); - } - - if (block_number) { - sb_add_chars(params, "},\""); - sb_add_chars(params, block_number); - sb_add_chars(params, "\"]"); - } - else { - uint8_t gasdata[8]; - bytes_t g_bytes = bytes(gasdata, 8); - - if (value) { - sb_add_chars(params, ", \"value\":\""); - sb_add_chars(params, value); - sb_add_chars(params, "\""); - } - if (gas_price) { - long_to_bytes(gas_price, gasdata); - b_optimize_len(&g_bytes); - sb_add_bytes(params, ", \"gasPrice\":", &g_bytes, 1, false); - } - long_to_bytes(gas ? gas : 100000, gasdata); - g_bytes = bytes(gasdata, 8); - b_optimize_len(&g_bytes); - sb_add_bytes(params, ", \"gasLimit\":", &g_bytes, 1, false); - sb_add_chars(params, "}]"); - } - args->len = 0; - sb_add_chars(args, params->data); - sb_free(params); - return req; -} - -void print_val(d_token_t* t) { - switch (d_type(t)) { - case T_ARRAY: - case T_OBJECT: - for (d_iterator_t it = d_iter(t); it.left; d_iter_next(&it)) - print_val(it.token); - break; - case T_BOOLEAN: - recorder_print(0, "%s\n", d_int(t) ? "true" : "false"); - break; - case T_INTEGER: - recorder_print(0, "%i\n", d_int(t)); - break; - case T_BYTES: - if (t->len < 9) - recorder_print(0, "%" PRId64 "\n", d_long(t)); - else { - recorder_print(0, "0x"); - for (int i = 0; i < (int) t->len; i++) recorder_print(0, "%02x", t->data[i]); - recorder_print(0, "\n"); - } - break; - case T_NULL: - recorder_print(0, "NULL\n"); - break; - case T_STRING: - recorder_print(0, "%s\n", d_string(t)); - break; - } -} -// decode pk -void read_pk(char* pk_file, char* pwd, in3_t* c, char* method) { - if (pk_file) { - if (!pwd) { - recorder_print(1, "Passphrase:\n"); - pwd = malloc(500); - read_pass(pwd, 500); - } - char* content; - if (strcmp(pk_file, "-") == 0) - content = (char*) readFile(stdin).data; - else if (pk_file[0] == '{') - content = pk_file; - else - content = (char*) readFile(fopen(pk_file, "r")).data; - - json_ctx_t* key_json = parse_json(content); - if (!key_json) die("invalid json in pk file"); - - uint8_t* pk_seed = malloc(32); - if (decrypt_key(key_json->result, pwd, pk_seed)) die("Invalid key"); - - if (!method || strcmp(method, "keystore") == 0 || strcmp(method, "key") == 0) { - char tmp[64]; - bytes_to_hex(pk_seed, 32, tmp); - recorder_print(0, "0x%s\n", tmp); - recorder_exit(0); - } - else - eth_set_pk_signer(c, pk_seed); +static void send_request(in3_t* c, int argc, char** argv, char* method, sb_t* args, char** result, char** error) { + sb_t* sb = sb_new("{\"method\":\""); + sb_add_chars(sb, method); + sb_add_chars(sb, "\",\"params\":"); + sb_add_chars(sb, args->data); + char* ms_sigs = get_argument(argc, argv, "-sigs", "--ms.signatures", true); + if (ms_sigs) { + sb_add_chars(sb, ",\"in3\":{\"msSigs\":\""); + sb_add_chars(sb, ms_sigs); + sb_add_chars(sb, "\"}}"); } -} + else + sb_add_chars(sb, "}"); + in3_client_rpc_raw(c, sb->data, result, error); + check_last_output(); #ifdef NODESELECT_DEF -static void set_nodelist(in3_t* c, char* nodes, bool update) { - if (!update) c->flags = FLAGS_STATS | FLAGS_BOOT_WEIGHTS | (c->flags & FLAGS_ALLOW_EXPERIMENTAL); - char* cpy = alloca(strlen(nodes) + 1); - in3_nodeselect_def_t* nl = in3_nodeselect_def_data(c); - if (!update && nl->nodelist_upd8_params) { - _free(nl->nodelist_upd8_params); - nl->nodelist_upd8_params = NULL; - } - memcpy(cpy, nodes, strlen(nodes) + 1); - char* s = NULL; - sb_t* sb = sb_new("{\"nodeRegistry\":{\"needsUpdate\":false,\"nodeList\":["); - for (char* next = strtok(cpy, ","); next; next = strtok(NULL, ",")) { - if (next != cpy) sb_add_char(sb, ','); - str_range_t address, url; - - if (*next == '0' && next[1] == 'x' && (s = strchr(next, ':'))) { - address = (str_range_t){.data = next, .len = s - next}; - url = (str_range_t){.data = s + 1, .len = strlen(s + 1)}; - } - else { - address = (str_range_t){.data = "0x1234567890123456789012345678901234567890", .len = 42}; - url = (str_range_t){.data = next, .len = strlen(next)}; - } - sb_add_chars(sb, "{\"address\":\""); - sb_add_range(sb, address.data, 0, address.len); - sb_add_chars(sb, "\",\"url\":\""); - sb_add_range(sb, url.data, 0, url.len); - sb_add_chars(sb, "\",\"props\":\"0xffff\"}"); - } - sb_add_chars(sb, "]}}}"); - char* err = in3_configure(c, sb->data); - if (err) - die(err); - sb_free(sb); -} -#endif -static bytes_t* last_response; -static bytes_t in_response = {.data = NULL, .len = 0}; -static bool only_show_raw_tx = false; -static in3_ret_t debug_transport(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx) { - UNUSED_VAR(plugin_data); - - in3_http_request_t* req = plugin_ctx; - if (action == PLGN_ACT_TRANSPORT_SEND) { -#ifndef DEBUG - if (debug_mode) - fprintf(stderr, "send request to %s: \n" COLORT_RYELLOW "%s" COLORT_RESET "\n", req->urls_len ? req->urls[0] : "none", req->payload); -#endif - if (in_response.len) { - for (unsigned int i = 0; i < req->urls_len; i++) { - req->req->raw_response[i].state = IN3_OK; - sb_add_range(&req->req->raw_response[i].data, (char*) in_response.data, 0, in_response.len); - req->req->raw_response[i].state = IN3_OK; - } - return 0; - } - if (only_show_raw_tx && str_find(req->payload, "\"method\":\"eth_sendRawTransaction\"")) { - char* data = str_find(req->payload, "0x"); - *strchr(data, '"') = 0; - recorder_print(0, "%s\n", data); - recorder_exit(EXIT_SUCCESS); - } - } -#ifdef USE_CURL - in3_ret_t r = send_curl(NULL, action, plugin_ctx); -#elif USE_WINHTTP - in3_ret_t r = send_winhttp(NULL, action, plugin_ctx); -#elif TRANSPORTS - in3_ret_t r = send_http(NULL, action, plugin_ctx); -#else - in3_ret_t r = plugin_ctx != NULL ? IN3_OK : IN3_ECONFIG; -#endif - if (action != PLGN_ACT_TRANSPORT_CLEAN) { - last_response = b_new((uint8_t*) req->req->raw_response[0].data.data, req->req->raw_response[0].data.len); -#ifndef DEBUG - if (debug_mode) { - if (req->req->raw_response[0].state == IN3_OK) - fprintf(stderr, "success response \n" COLORT_RGREEN "%s" COLORT_RESET "\n", req->req->raw_response[0].data.data); - else - fprintf(stderr, "error response \n" COLORT_RRED "%s" COLORT_RESET "\n", req->req->raw_response[0].data.data); - } -#endif + in3_chain_t* chain = &c->chain; + in3_nodeselect_def_t* nl = in3_nodeselect_def_data(c); + // Update nodelist if a newer latest block was reported + if (chain && nl && nl->nodelist_upd8_params && nl->nodelist_upd8_params->exp_last_block) { + char *r = NULL, *e = NULL; + if (chain->type == CHAIN_ETH) + in3_client_rpc(c, "eth_blockNumber", "[]", &r, &e); } - return r; -} -static char* test_name = NULL; -static in3_ret_t test_transport(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx) { - UNUSED_VAR(plugin_data); - in3_http_request_t* req = plugin_ctx; -#ifdef USE_CURL - in3_ret_t r = send_curl(NULL, action, plugin_ctx); -#elif USE_WINHTTP - in3_ret_t r = send_winhttp(NULL, action, plugin_ctx); -#elif TRANSPORTS - in3_ret_t r = send_http(NULL, action, plugin_ctx); -#else - in3_ret_t r = plugin_ctx != NULL ? IN3_OK : IN3_ECONFIG; #endif - if (r == IN3_OK) { - req->payload[strlen(req->payload) - 1] = 0; - recorder_print(0, "[{ \"descr\": \"%s\",\"chainId\": \"0x1\", \"verification\": \"proof\",\"binaryFormat\": false, \"request\": %s, \"response\": %s }]", test_name, req->payload + 1, req->req->raw_response->data.data); - recorder_exit(0); - } - - return r; } int main(int argc, char* argv[]) { - // check for usage - bool use_pk = false; - if (argc >= 2 && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-help") == 0)) { - show_help(argv[0]); - recorder_exit(0); - } - - if (argc >= 2 && (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-version") == 0)) { - recorder_print(0, "in3 " IN3_VERSION "\nbuild " __DATE__ " with"); -#ifdef TEST - recorder_print(0, " -DTEST=true"); -#endif -#ifdef EVM_GAS - recorder_print(0, " -DEVM_GAS=true"); -#endif -#ifdef CMD - recorder_print(0, " -DCMD=true"); -#endif -#ifdef IN3_MATH_FAST - recorder_print(0, " -DFAST_MATH=true"); -#endif -#ifdef IN3_SERVER - recorder_print(0, " -DIN3_SERVER=true"); -#endif -#ifdef USE_CURL - recorder_print(0, " -DUSE_CURL=true"); -#else - recorder_print(0, " -DUSE_CURL=false"); -#endif - recorder_print(0, "\n(c) " IN3_COPYRIGHT "\n"); - recorder_exit(0); - } - - // define vars - char* method = NULL; - sb_t* args = sb_new("["); - int i; -#ifdef LEDGER_NANO - uint8_t path[5]; -#endif - // we want to verify all in3_log_set_level(LOG_INFO); - // create the client - in3_t* c = in3_for_chain(CHAIN_ID_MAINNET); - bool out_response = false; - int run_test_request = 0; - bool force_hex = false; - char* sig = NULL; - char* to = NULL; - char* block_number = "latest"; - char* name = NULL; - abi_sig_t* req = NULL; - bool json = false; - char* ms_sigs = NULL; - char* allowed_methods = NULL; - uint64_t gas_limit = 100000; - uint64_t gas_price = 0; - char* value = NULL; - bool wait = false; - char* pwd = NULL; - char* pk_file = NULL; - char* validators = NULL; - bytes_t* data = NULL; - char* port = NULL; - char* sig_type = "raw"; - bool to_eth = false; - char* rc = "2"; - - in3_plugin_register(c, PLGN_ACT_TRANSPORT, debug_transport, NULL, true); - -#ifndef USE_WINHTTP - rc = "1"; -#endif - -#ifdef NODESELECT_DEF - _configure(c, "requestCount", "%s", rc); -#endif - - // handle clear cache opt before initializing cache - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-fi") == 0) { - recorder_update_cmd(argv[i + 1], &argc, &argv); - break; - } - if (strcmp(argv[i], "-pk") == 0) use_pk = true; - } - - // handle clear cache opt before initializing cache - for (i = 1; i < argc; i++) - if (strcmp(argv[i], "-ccache") == 0) - storage_clear(NULL); - - // use the storagehandler to cache data in .in3 - in3_register_file_storage(c); - - // check env - if (getenv("IN3_PK") && !use_pk) { - char* pks = _strdupn(getenv("IN3_PK"), -1); - bytes32_t pk; - for (char* cc = strtok(pks, ","); cc; cc = strtok(NULL, ",")) { - hex_to_bytes(cc, -1, pk, 32); - eth_set_pk_signer(c, pk); - } - } + // define vars + char* method = NULL; + sb_t* args = sb_new("["); + in3_t* c = in3_for_chain(CHAIN_ID_MAINNET); -#ifdef ZKSYNC - if (getenv("IN3_ZKS")) configure_2("zksync", "provider_url", getenv("IN3_ZKS")); -#endif + init_transport(c); + init_recorder(&argc, &argv); + init_env(c, argc, argv); - if (getenv("IN3_CHAIN")) - set_chain_id(c, getenv("IN3_CHAIN")); + // parse arguments + for (int i = 1; i < argc; i++) { + // is it a argument? + if (configure_arg(c, argv, &i, argc)) continue; - // fill from args - for (i = 1; i < argc; i++) { - if (strcmp(argv[i], "-pk") == 0) { // private key? - if (argv[i + 1][0] == '0' && argv[i + 1][1] == 'x') { - bytes32_t pk; - hex_to_bytes(argv[++i], -1, pk, 32); - eth_set_pk_signer(c, pk); - } - else - pk_file = argv[++i]; - } - else if (strcmp(argv[i], "-path") == 0) { -#if defined(LEDGER_NANO) - if (argv[i + 1][0] == '0' && argv[i + 1][1] == 'x') { - hex_to_bytes(argv[++i], -1, path, 5); - eth_ledger_set_signer_txn(c, path); - } -#else - die("path option not supported currently "); -#endif - } - else if (strcmp(argv[i], "-chain") == 0 || strcmp(argv[i], "-c") == 0) // chain_id - set_chain_id(c, argv[++i]); - else if (strcmp(argv[i], "-ccache") == 0) // NOOP - should have been handled earlier - ; - else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "-data") == 0) { // data - char* d = argv[++i]; - if (strcmp(d, "-") == 0) - data = get_std_in(); - else if (*d == '0' && d[1] == 'x') - data = hex_to_new_bytes(d + 2, strlen(d) - 2); - else { - FILE* f = fopen(d, "r"); - bytes_t content = readFile(f); - data = hex_to_new_bytes((char*) content.data + 2, content.len - 2); - fclose(f); - } - } - else if (strcmp(argv[i], "-block") == 0 || strcmp(argv[i], "-b") == 0) - block_number = argv[++i]; - else if (strcmp(argv[i], "-latest") == 0 || strcmp(argv[i], "-l") == 0) - c->replace_latest_block = atoll(argv[++i]); -#ifdef ZKSYNC - else if (strcmp(argv[i], "-zks") == 0) - configure_2("zksync", "provider_url", argv[++i]); - else if (strcmp(argv[i], "-zka") == 0) - configure_2("zksync", "account", argv[++i]); - else if (strcmp(argv[i], "-zcpm") == 0) - configure_2("zksync", "create_proof_method", argv[++i]); - else if (strcmp(argv[i], "-zvpm") == 0) - configure_2("zksync", "verify_proof_method", argv[++i]); - else if (strcmp(argv[i], "-zkat") == 0) - configure_2("zksync", "signer_type", argv[++i]); - else if (strcmp(argv[i], "-zms") == 0) - configure_2("zksync", "musig_pub_keys", argv[++i]); - else if (strcmp(argv[i], "-zmu") == 0) - _configure2(c, "zksync", "musig_urls", "[null,\"%s\"]", argv[++i]); - else if (strcmp(argv[i], "-zsk") == 0) - configure_2("zksync", "sync_key", argv[++i]); - else if (strcmp(argv[i], "-zc2") == 0) { - char* c2val = argv[++i]; - if (strlen(c2val) != 176) die("create2-arguments must have the form -zc2 ::"); - char tmp[177], t2[500]; - memcpy(tmp, c2val, 177); - tmp[42] = tmp[109] = 0; - sprintf(t2, "{\"zksync\":{\"signer_type\":\"create2\",\"create2\":{\"creator\":\"%s\",\"codehash\":\"%s\",\"saltarg\":\"%s\"}}}", tmp, tmp + 43, tmp + 110); - char* err = in3_configure(c, t2); - if (err) die(err); - } -#endif - else if (strcmp(argv[i], "-tr") == 0) - run_test_request = 1; - else if (strcmp(argv[i], "-thr") == 0) - run_test_request = 2; - else if (strcmp(argv[i], "-x") == 0) - c->flags |= FLAGS_ALLOW_EXPERIMENTAL; - else if (strcmp(argv[i], "-fo") == 0) - recorder_write_start(c, argv[++i], argc, argv); - else if (strcmp(argv[i], "-fi") == 0) - recorder_read_start(c, argv[++i]); -#ifdef NODESELECT_DEF - else if (strcmp(argv[i], "-nl") == 0) - set_nodelist(c, argv[++i], false); - else if (strcmp(argv[i], "-bn") == 0) - set_nodelist(c, argv[++i], true); -#endif - else if (strcmp(argv[i], "-mss") == 0 || strcmp(argv[i], "-sigs") == 0) - ms_sigs = argv[++i]; - else if (strcmp(argv[i], "-ms") == 0) { -#ifdef MULTISIG - address_t adr; - if (hex_to_bytes(argv[++i], -1, adr, 20) != 20) die("-ms must be exactly 20 bytes"); - add_gnosis_safe(c, adr); -#else - die("-ms is not supported. Compile with -DMULTISIG=true"); -#endif - } - else if (strcmp(argv[i], "-eth") == 0) - to_eth = true; - else if (strcmp(argv[i], "-md") == 0) - _configure(c, "minDeposit", "%s", argv[++i]); - else if (strcmp(argv[i], "-kin3") == 0) - c->flags |= FLAGS_KEEP_IN3; - else if (strcmp(argv[i], "-bw") == 0) - c->flags |= FLAGS_BOOT_WEIGHTS; - else if (strcmp(argv[i], "-to") == 0) - to = argv[++i]; - else if (strcmp(argv[i], "-gas") == 0 || strcmp(argv[i], "-gas_limit") == 0) - gas_limit = atoll(argv[++i]); - else if (strcmp(argv[i], "-gp") == 0 || strcmp(argv[i], "-gas_price") == 0) - gas_price = atoll(argv[++i]); - else if (strcmp(argv[i], "-test") == 0) { - test_name = argv[++i]; - in3_plugin_register(c, PLGN_ACT_TRANSPORT, test_transport, NULL, true); - } - else if (strcmp(argv[i], "-pwd") == 0) - pwd = argv[++i]; - else if (strcmp(argv[i], "-q") == 0) - in3_log_set_level(LOG_FATAL); - else if (strcmp(argv[i], "-value") == 0) - value = get_wei(argv[++i]); - else if (strcmp(argv[i], "-port") == 0) - port = argv[++i]; - else if (strcmp(argv[i], "-am") == 0) - allowed_methods = argv[++i]; - else if (strcmp(argv[i], "-os") == 0) - only_show_raw_tx = true; - else if (strcmp(argv[i], "-rc") == 0) - _configure(c, "requestCount", "%s", argv[++i]); - else if (strcmp(argv[i], "-a") == 0) - c->max_attempts = atoi(argv[++i]); - else if (strcmp(argv[i], "-name") == 0) - name = argv[++i]; - else if (strcmp(argv[i], "-validators") == 0) - validators = argv[++i]; - else if (strcmp(argv[i], "-hex") == 0) - force_hex = true; - else if (strcmp(argv[i], "-f") == 0 || strcmp(argv[i], "-finality") == 0) - c->finality = (uint16_t) atoi(argv[++i]); - else if (strcmp(argv[i], "-response-out") == 0 || strcmp(argv[i], "-ro") == 0) - out_response = true; - else if (strcmp(argv[i], "-response-in") == 0 || strcmp(argv[i], "-ri") == 0) - in_response = readFile(stdin); - else if (strcmp(argv[i], "-wait") == 0 || strcmp(argv[i], "-w") == 0) - wait = true; - else if (strcmp(argv[i], "-json") == 0) - json = true; - else if (strcmp(argv[i], "-k") == 0) { - if (argc <= i + 1 || strlen(argv[i + 1]) > 66) die("Invalid signer key"); - bytes32_t k; - hex_to_bytes(argv[++i], -1, k, 32); - eth_set_request_signer(c, k); - } - else if (strcmp(argv[i], "-np") == 0) - c->proof = PROOF_NONE; - else if (strcmp(argv[i], "-ns") == 0) - c->flags ^= FLAGS_STATS; - else if (strcmp(argv[i], "-sigtype") == 0 || strcmp(argv[i], "-st") == 0) - sig_type = argv[++i]; - else if (strcmp(argv[i], "-debug") == 0) { - in3_log_set_quiet(false); - in3_log_set_level(LOG_TRACE); - debug_mode = true; - } - else if (strcmp(argv[i], "-signs") == 0 || strcmp(argv[i], "-s") == 0) - c->signature_count = atoi(argv[++i]); - else if (strcmp(argv[i], "-proof") == 0 || strcmp(argv[i], "-p") == 0) { - if (strcmp(argv[i + 1], "none") == 0) - c->proof = PROOF_NONE; - else if (strcmp(argv[i + 1], "standard") == 0) - c->proof = PROOF_STANDARD; - else if (strcmp(argv[i + 1], "full") == 0) - c->proof = PROOF_FULL; - else - die("Invalid Argument for proof, must be none, standard or full"); - i++; - } // now handle arguments for special methods + if (method == NULL) + method = argv[i]; + else if (strcmp(method, "sign") == 0 && !get_txdata()->data) + get_txdata()->data = b_new((uint8_t*) argv[i], strlen(argv[i])); + else if (get_txdata()->sig == NULL && (strcmp(method, "call") == 0 || strcmp(method, "send") == 0 || strcmp(method, "abi_encode") == 0 || strcmp(method, "abi_decode") == 0)) + get_txdata()->sig = argv[i]; else { - if (method == NULL) - method = argv[i]; - else if (strcmp(method, "keystore") == 0 || strcmp(method, "key") == 0) - pk_file = argv[i]; - else if (strcmp(method, "sign") == 0 && !data) { - - data = b_new((uint8_t*) argv[i], strlen(argv[i])); - } - else if (sig == NULL && (strcmp(method, "call") == 0 || strcmp(method, "send") == 0 || strcmp(method, "abi_encode") == 0 || strcmp(method, "abi_decode") == 0)) - sig = argv[i]; - else { - // otherwise we add it to the params - if (args->len > 1) sb_add_char(args, ','); - if (*argv[i] >= '0' && *argv[i] <= '9' && *(argv[i] + 1) != 'x' && strcmp(method, "in3_toWei") && c->chain.chain_id != CHAIN_ID_BTC) - sb_print(args, "\"%s\"", get_wei(argv[i])); - else - sb_print(args, - (argv[i][0] == '{' || argv[i][0] == '[' || strcmp(argv[i], "true") == 0 || strcmp(argv[i], "false") == 0 || (*argv[i] >= '0' && *argv[i] <= '9' && strlen(argv[i]) < 16 && *(argv[i] + 1) != 'x')) - ? "%s" - : "\"%s\"", - strcmp(method, "in3_ens") ? resolve(c, argv[i]) : argv[i]); - } - } - } - sb_add_char(args, ']'); - char *result = NULL, *error = NULL; - -#ifdef IN3_SERVER - // start server - if (!method && port) { - http_run_server(port, c, allowed_methods); - recorder_exit(0); - } -#else - (void) (port); - (void) (allowed_methods); -#endif - - // handle private key - if (pk_file) read_pk(pk_file, pwd, c, method); - - // no proof for rpc-chain - if (c->chain.chain_id == 0xFFFF) c->proof = PROOF_NONE; - - // make sure boot nodes are initialized - char buf[15 + 11 /* UINT32_MAX */]; - sprintf(buf, "{\"chainId\":%" PRIu32 "}", c->chain.chain_id); - in3_configure(c, buf); - - // execute the method - if (sig && *sig == '-') die("unknown option"); - if (!method) { - in3_log_info("in3 " IN3_VERSION " - reading json-rpc from stdin. (exit with ctrl C)\n________________________________________________\n"); - execute(c, stdin); - recorder_exit(0); - } - if (*method == '-') die("unknown option"); - - // call -> eth_call - if (strcmp(method, "call") == 0) { - req = prepare_tx(sig, resolve(c, to), args, block_number, 0, 0, NULL, data); - method = "eth_call"; - } - else if (strcmp(method, "abi_encode") == 0) { - char* error = NULL; - json_ctx_t* in_data = parse_json(args->data); - if (!in_data) die("iinvalid params"); - if (!sig) die("missing signature"); - abi_sig_t* s = abi_sig_create(sig, &error); - if (s && !error) { - bytes_t data = abi_encode(s, in_data->result, &error); - if (data.data) - print_hex(data.data, data.len); - } - if (error) die(error); - recorder_exit(0); - } - else if (strcmp(method, "abi_decode") == 0) { - char* error = NULL; - json_ctx_t* in_data = parse_json(args->data); - if (!in_data) die("invalid params"); - if (!sig) die("missing signature"); - abi_sig_t* s = abi_sig_create(sig, &error); - if (s && !error) { - bytes_t data = d_to_bytes(d_get_at(parse_json(args->data)->result, 0)); - json_ctx_t* res = abi_decode(s, data, &error); - if (error) die(error); - if (json) - recorder_print(0, "%s\n", d_create_json(res, res->result)); - else - print_val(res->result); - } - if (error) die(error); - recorder_exit(0); -#ifdef IPFS - } - else if (strcmp(method, "ipfs_get") == 0) { - c->chain.chain_id = CHAIN_ID_IPFS; - int size = args->len; - if (size == 2 || args->data[1] != '"' || size < 20 || strstr(args->data + 2, "\"") == NULL) die("missing ipfs hash"); - args->data[size - 2] = 0; - bytes_t* content = ipfs_get(c, args->data + 2); - if (!content) die("IPFS hash not found!"); - fwrite(content->data, content->len, 1, stdout); - fflush(stdout); - recorder_exit(0); - } - else if (strcmp(method, "ipfs_put") == 0) { - c->chain.chain_id = CHAIN_ID_IPFS; - bytes_t data = readFile(stdin); - data.data[data.len] = 0; - recorder_print(0, "%s\n", ipfs_put(c, &data)); - recorder_exit(0); - -#endif - } -#ifdef NODESELECT_DEF - else if (strcmp(method, "in3_weights") == 0) { - c->max_attempts = 1; - uint32_t block = 0, b = 0; - BIT_CLEAR(c->flags, FLAGS_AUTO_UPDATE_LIST); - uint64_t now = in3_time(NULL); - char* more = "WEIGHT"; - in3_plugin_execute_all(c, PLGN_ACT_CHAIN_CHANGE, c); - in3_nodeselect_def_t* nl = in3_nodeselect_def_data(c); - if (run_test_request == 1) more = "WEIGHT : LAST_BLOCK"; - if (run_test_request == 2) more = "WEIGHT : NAME VERSION : RUNNING : HEALTH : LAST_BLOCK"; - recorder_print(0, " : %-45s : %7s : %5s : %5s: %s\n------------------------------------------------------------------------------------------------\n", "URL", "BL", "CNT", "AVG", more); - for (unsigned int i = 0; i < nl->nodelist_length; i++) { - in3_req_t* ctx = NULL; - char* health_s = NULL; - if (run_test_request) { - char req[300]; - char adr[41]; - bytes_to_hex((nl->nodelist + i)->address, 20, adr); - sprintf(req, "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"in3\":{\"dataNodes\":[\"0x%s\"]}}", adr); - ctx = req_new(c, req); - if (ctx) in3_send_req(ctx); - if (run_test_request == 2) { - int health = 1; - char* version = ""; - char* node_name = ""; - uint32_t running = 0; - json_ctx_t* health_res = NULL; - char health_url[500]; - char* urls[1]; - urls[0] = health_url; - sprintf(health_url, "%s/health", nl->nodelist[i].url); - in3_http_request_t r = {0}; - in3_req_t ctx = {0}; - ctx.raw_response = _calloc(sizeof(in3_response_t), 1); - ctx.raw_response->state = IN3_WAITING; - ctx.client = c; - r.req = &ctx; - r.urls = urls; - r.urls_len = 1; - r.payload = ""; -#ifdef USE_CURL - send_curl(NULL, PLGN_ACT_TRANSPORT_SEND, &r); -#elif USE_WINHTTP - send_winhttp(NULL, PLGN_ACT_TRANSPORT_SEND, &r); -#elif TRANSPORTS - send_http(NULL, PLGN_ACT_TRANSPORT_SEND, &r); -#endif - if (ctx.raw_response->state) - health = 0; - else { - health_res = parse_json(ctx.raw_response->data.data); - if (!health_res) - health = 0; - else { - node_name = d_get_string(health_res->result, key("name")); - version = d_get_string(health_res->result, key("version")); - running = d_get_int(health_res->result, key("running")); - char* status = d_get_string(health_res->result, key("status")); - if (!status || strcmp(status, "healthy")) health = 0; - } - } - if (version) { - char* l = strrchr(version, ':'); - if (l) version = l + 1; - } - health_s = _malloc(3000); - sprintf(health_s, "%-22s %-7s %7d %-9s ", node_name ? node_name : "-", version ? version : "-", running, health ? "OK" : "unhealthy"); - - if (ctx.raw_response->data.data) - _free(ctx.raw_response->data.data); - _free(ctx.raw_response); - if (health_res) json_free(health_res); - } - } - in3_node_t* node = nl->nodelist + i; - in3_node_weight_t* weight = nl->weights + i; - uint64_t blacklisted = weight->blacklisted_until > now ? weight->blacklisted_until : 0; - uint32_t calc_weight = in3_node_calculate_weight(weight, node->capacity, now); - char * tr = NULL, *warning = NULL; - if (ctx) { - tr = _malloc(1000); - if (!ctx->error && d_get(ctx->responses[0], K_ERROR)) { - d_token_t* msg = d_get(ctx->responses[0], K_ERROR); - if (d_type(msg) == T_OBJECT) msg = d_get(msg, K_MESSAGE); - sprintf((warning = tr), "%s", msg ? d_string(msg) : "Error-Response!"); - } - else if (!ctx->error) { - b = d_get_int(ctx->responses[0], K_RESULT); - if (block < b) block = b; - - if (b < block - 1) - sprintf((warning = tr), "#%i ( out of sync : %i blocks behind latest )", b, block - b); - else if (strncmp(node->url, "https://", 8)) - sprintf((warning = tr), "#%i (missing https, which is required in a browser )", b); - else if (!IS_APPROX(d_get_int(ctx->responses[0], K_RESULT), d_get_int(d_get(ctx->responses[0], K_IN3), K_CURRENT_BLOCK), 1)) - sprintf((warning = tr), "#%i ( current block mismatch: %i blocks apart )", b, - d_get_int(ctx->responses[0], K_RESULT) - d_get_int(d_get(ctx->responses[0], K_IN3), K_CURRENT_BLOCK)); - else - sprintf(tr, "#%i", b); - } - else if (!strlen(node->url) || !node->props) - sprintf((warning = tr), "No URL spcified anymore props = %i ", (int) (node->props & 0xFFFFFF)); - else if ((node->props & NODE_PROP_DATA) == 0) - sprintf((warning = tr), "The node is marked as not supporting Data-Providing"); - else if (c->proof != PROOF_NONE && (node->props & NODE_PROP_PROOF) == 0) - sprintf((warning = tr), "The node is marked as able to provide proof"); - else if ((c->flags & FLAGS_HTTP) && (node->props & NODE_PROP_HTTP) == 0) - sprintf((warning = tr), "The node is marked as able to support http-requests"); - else - tr = ctx->error; - if (strlen(tr) > 100) tr[100] = 0; - } - if (blacklisted) - recorder_print(0, COLORT_RED); - else if (warning) - recorder_print(0, COLORT_YELLOW); - else if (!weight->response_count) - recorder_print(0, COLORT_DARKGRAY); + // otherwise we add it to the params + if (args->len > 1) sb_add_char(args, ','); + if (*argv[i] >= '0' && *argv[i] <= '9' && *(argv[i] + 1) != 'x' && strcmp(method, "in3_toWei") && c->chain.chain_id != CHAIN_ID_BTC) + sb_print(args, "\"%s\"", get_wei(argv[i])); else - recorder_print(0, COLORT_GREEN); - recorder_print(0, "%2i %-45s %7i %5i %5i %5i %s%s", i, node->url, (int) (blacklisted ? blacklisted - now : 0), weight->response_count, weight->response_count ? (weight->total_response_time / weight->response_count) : 0, calc_weight, health_s ? health_s : "", tr ? tr : ""); - recorder_print(0, COLORT_RESET "\n"); - if (tr && tr != ctx->error) _free(tr); - if (health_s) _free(health_s); - if (ctx) req_free(ctx); - } - - recorder_exit(0); - } -#endif - else if (strcmp(method, "send") == 0) { - prepare_tx(sig, resolve(c, to), args, NULL, gas_limit, gas_price, value, data); - method = wait ? "eth_sendTransactionAndWait" : "eth_sendTransaction"; - } - else if (strcmp(method, "sign") == 0) { - if (!data) die("no data given"); - if (data->len > 2 && data->data[0] == '0' && data->data[1] == 'x') - data = hex_to_new_bytes((char*) data->data + 2, data->len - 2); - if (strcmp(sig_type, "eth_sign") == 0) { - char* tmp = alloca(data->len + 30); - int l = sprintf(tmp, "\x19" - "Ethereum Signed Message:\n%u", - data->len); - memcpy(tmp + l, data->data, data->len); - data = b_new((uint8_t*) tmp, l + data->len); - sig_type = "raw"; - } - - if (!in3_plugin_is_registered(c, PLGN_ACT_SIGN)) die("No private key/path given"); - in3_req_t ctx; - ctx.client = c; - in3_sign_ctx_t sc = {0}; - sc.req = &ctx; - sc.account = bytes(NULL, 0); - sc.message = *data; - sc.type = strcmp(sig_type, "hash") == 0 ? SIGN_EC_RAW : SIGN_EC_HASH; -#if defined(LEDGER_NANO) - if (c->signer->sign == eth_ledger_sign_txn) { // handling specific case when ledger nano signer is ethereum firmware app - char prefix[] = "msg"; - bytes_t* tmp_data = b_new((uint8_t*) NULL, data->len + strlen(prefix)); - uint8_t hash[32]; - - hasher_Raw(HASHER_SHA2, data->data, data->len, hash); - recorder_print(0, "Match the following hash with the message hash on ledger device\n"); - print_hex(hash, 32); - - memcpy(tmp_data->data, prefix, strlen(prefix)); - memcpy(tmp_data->data + strlen(prefix), data->data, data->len); - - sc.message = *tmp_data; - in3_plugin_execute_first(&ctx, PLGN_ACT_SIGN, &sc); - - b_free(tmp_data); - } - else { - in3_plugin_execute_first(&ctx, PLGN_ACT_SIGN, &sc); - } -#else - in3_plugin_execute_first(&ctx, PLGN_ACT_SIGN, &sc); -#endif - - if (sc.signature.len == 65) sc.signature.data[64] += 27; - print_hex(sc.signature.data, sc.signature.len); - recorder_exit(0); - } - else if (strcmp(method, "chainspec") == 0) { - char* json; - if (args->len > 2) { - args->data[args->len - 2] = 0; - json = (char*) readFile(fopen(args->data + 2, "r")).data; - } - else - json = (char*) readFile(stdin).data; - json_ctx_t* j = parse_json_indexed(json); - chainspec_t* spec = chainspec_create_from_json(j); - if (validators) { - // first PoA without validators-list - for (uint32_t i = 0; i < spec->consensus_transitions_len; i++) { - if (spec->consensus_transitions[i].validators.len == 0) { - spec->consensus_transitions[i].validators = *hex_to_new_bytes(validators + 2, strlen(validators) - 2); - break; - } - } + sb_print(args, + (argv[i][0] == '{' || argv[i][0] == '[' || strcmp(argv[i], "true") == 0 || strcmp(argv[i], "false") == 0 || (*argv[i] >= '0' && *argv[i] <= '9' && strlen(argv[i]) < 16 && *(argv[i] + 1) != 'x')) + ? "%s" + : "\"%s\"", + strcmp(method, "in3_ens") ? resolve(c, argv[i]) : argv[i]); } - bytes_builder_t* bb = bb_new(); - chainspec_to_bin(spec, bb); - - if (force_hex) - print_hex(bb->b.data, bb->b.len); - else { - bool is_hex = false; - recorder_print(0, "#define CHAINSPEC_%s \"", name); - for (i = 0; i < (int) bb->b.len; i++) { - uint8_t c = bb->b.data[i]; - if (is_hex && ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) recorder_print(0, "\" \""); - is_hex = c < ' ' || c > 0x7E || c == 0x5C || c == '"'; - recorder_print(0, is_hex ? "\\x%02x" : "%c", c); - } - recorder_print(0, "\"\n"); - } - - recorder_exit(0); } - else if (strcmp(method, "autocompletelist") == 0) { - recorder_print(0, "send call abi_encode abi_decode ipfs_get ipfs_put ecrecover key -sigtype -st eth_sign raw hash sign createkey -ri -ro keystore unlock pk2address pk2public mainnet goerli ewc btc ipfs local true false latest -np -debug -c -chain -p -version -proof -s -signs -b -block -to -d -data -gas_limit -value -w -wait -hex -json in3_nodeList in3_stats in3_sign web3_clientVersion web3_sha3 net_version net_peerCount net_listening eth_protocolVersion eth_syncing eth_coinbase eth_mining eth_hashrate eth_gasPrice eth_accounts eth_blockNumber eth_getBalance eth_getStorageAt eth_getTransactionCount eth_getBlockTransactionCountByHash eth_getBlockTransactionCountByNumber eth_getUncleCountByBlockHash eth_getUncleCountByBlockNumber eth_getCode eth_sign eth_sendTransaction eth_sendRawTransaction eth_call eth_estimateGas eth_getBlockByHash eth_getBlockByNumber eth_getTransactionByHash eth_getTransactionByBlockHashAndIndex eth_getTransactionByBlockNumberAndIndex eth_getTransactionReceipt eth_pendingTransactions eth_getUncleByBlockHashAndIndex eth_getUncleByBlockNumberAndIndex eth_getCompilers eth_compileLLL eth_compileSolidity eth_compileSerpent eth_newFilter eth_newBlockFilter eth_newPendingTransactionFilter eth_uninstallFilter eth_getFilterChanges eth_getFilterLogs eth_getLogs eth_getWork eth_submitWork eth_submitHashrate in3_cacheClear\n"); - recorder_exit(0); - } - else if (strcmp(method, "createkey") == 0) { - srand(current_ms() % 0xFFFFFFFF); - recorder_print(0, "0x"); - for (i = 0; i < 32; i++) recorder_print(0, "%02x", rand() % 256); - recorder_print(0, "\n"); - recorder_exit(0); - } - else if (strcmp(method, "pk2address") == 0) { - bytes32_t prv_key; - uint8_t public_key[65], sdata[32]; - hex_to_bytes(argv[argc - 1], -1, prv_key, 32); - ecdsa_get_public_key65(&secp256k1, prv_key, public_key); - keccak(bytes(public_key + 1, 64), sdata); - recorder_print(0, "0x"); - for (i = 0; i < 20; i++) recorder_print(0, "%02x", sdata[i + 12]); - recorder_print(0, "\n"); - recorder_exit(0); - } - else if (strcmp(method, "pk2public") == 0) { - bytes32_t prv_key; - uint8_t public_key[65]; - hex_to_bytes(argv[argc - 1], -1, prv_key, 32); - ecdsa_get_public_key65(&secp256k1, prv_key, public_key); - print_hex(public_key + 1, 64); - recorder_exit(0); - } - else if (strcmp(method, "ecrecover") == 0) { - json_ctx_t* rargs = parse_json(args->data); - if (!rargs || d_len(rargs->result) < 2) die("Invalid arguments for recovery args must be : "); - bytes_t msg = d_to_bytes(d_get_at(rargs->result, 0)); - bytes_t sig = d_to_bytes(d_get_at(rargs->result, 1)); - bytes32_t hash; - uint8_t pub[65]; - if (strcmp(sig_type, "eth_sign") == 0) { - char* tmp = alloca(msg.len + 30); - int l = sprintf(tmp, "\x19" - "Ethereum Signed Message:\n%u", - msg.len); - memcpy(tmp + l, msg.data, msg.len); - msg = *b_new((uint8_t*) tmp, l + msg.len); - } - if (strcmp(sig_type, "hash") == 0) { - if (msg.len != 32) die("The message hash must be 32 byte"); - memcpy(hash, msg.data, 32); - } - else - keccak(msg, hash); - if (sig.len != 65) die("The signature must be 65 bytes"); + sb_add_char(args, ']'); - if (ecdsa_recover_pub_from_sig(&secp256k1, pub, sig.data, hash, sig.data[64] >= 27 ? sig.data[64] - 27 : sig.data[64])) - die("Invalid Signature"); + // start the server? + if (!method) check_server(c); - keccak(bytes(pub + 1, 64), hash); - print_hex(hash + 12, 20); - print_hex(pub + 1, 64); - recorder_exit(0); - } + // handle special cmd-methods + if (handle_rpc(c, &method, args, argc, argv)) recorder_exit(0); + // execute in3_log_debug("..sending request %s %s\n", method, args->data); -#ifdef NODESELECT_DEF - in3_chain_t* chain = &c->chain; -#endif - - if (wait && strcmp(method, "eth_sendTransaction") == 0) method = "eth_sendTransactionAndWait"; - - // send the request - sb_t* sb = sb_new("{\"method\":\""); - sb_add_chars(sb, method); - sb_add_chars(sb, "\",\"params\":"); - sb_add_chars(sb, args->data); - if (ms_sigs) { - sb_add_chars(sb, ",\"in3\":{\"msSigs\":\""); - sb_add_chars(sb, ms_sigs); - sb_add_chars(sb, "\"}}"); - } - else - sb_add_chars(sb, "}"); - - in3_client_rpc_raw(c, sb->data, &result, &error); - -#ifdef NODESELECT_DEF - in3_nodeselect_def_t* nl = in3_nodeselect_def_data(c); - // Update nodelist if a newer latest block was reported - if (chain && nl->nodelist_upd8_params && nl->nodelist_upd8_params->exp_last_block) { - char *r = NULL, *e = NULL; - if (chain->type == CHAIN_ETH) - in3_client_rpc(c, "eth_blockNumber", "[]", &r, &e); - // else if (chain->type == CHAIN_BTC) - // in3_client_rpc(c, "getblockcount", "[]", &r, &e); - } -#endif + char *result = NULL, *error = NULL; + send_request(c, argc, argv, method, args, &result, &error); if (error) die(error); else if (!result) die("No result"); - else { - - if (out_response && last_response) { - char* r = alloca(last_response->len + 1); - memcpy(r, last_response->data, last_response->len); - r[last_response->len] = 0; - recorder_print(0, "%s\n", r); - recorder_exit(0); - } - - // if the result is a string, we remove the quotes - if (result[0] == '"' && result[strlen(result) - 1] == '"') { - memmove(result, result + 1, strlen(result)); - result[strlen(result) - 1] = 0; - } - - // if the request was a eth_call, we decode the result - if (req) { - int l = strlen(result) / 2 - 1; - if (l) { - char* error = NULL; - uint8_t* tmp = alloca(l + 1); - json_ctx_t* res = abi_decode(req, bytes(tmp, hex_to_bytes(result, -1, tmp, l + 1)), &error); - if (error) die(error); - if (json) - recorder_print(0, "%s\n", d_create_json(res, res->result)); - else - print_val(res->result); - } - // if not we simply print the result - } - else { - if (to_eth && result[0] == '0' && result[1] == 'x' && strlen(result) <= 18) { - double val = char_to_long(result, strlen(result)); - recorder_print(0, "%.3f\n", val / 1000000000000000000L); - } - else if (!force_hex && result[0] == '0' && result[1] == 'x' && strlen(result) <= 18) - recorder_print(0, "%" PRIu64 "\n", char_to_long(result, strlen(result))); - else - recorder_print(0, "%s\n", result); - } - } + else + display_result(method, result); recorder_exit(0); -} +} \ No newline at end of file diff --git a/c/src/cmd/in3/option_handler.c b/c/src/cmd/in3/option_handler.c new file mode 100644 index 000000000..ab09e2234 --- /dev/null +++ b/c/src/cmd/in3/option_handler.c @@ -0,0 +1,263 @@ +#include "../../nodeselect/full/nodelist.h" +#include "../../nodeselect/full/nodeselect_def.h" +#include "../../signer/multisig/multisig.h" +#include "../../signer/pk-signer/signer.h" + +#include "handlers.h" +#include "helper.h" +#include "in3_storage.h" +#include "req_exec.h" +#include "transport.h" +#include "tx.h" +#include "weights.h" + +#ifndef CMD_NAME +#define CMD_NAME "in3" +#endif + +#define CHECK_OPTION(name, fn) \ + if (strcmp(key, name) == 0) return fn; +#ifndef IN3_VERSION +#define IN3_VERSION "local" +#endif + +static bool set_chainId(char* value, sb_t* conf) { + if (strstr(value, "://") == NULL) return false; + sb_add_chars(conf, "{\"rpc\":\""); + sb_add_escaped_chars(conf, value); + sb_add_chars(conf, "\"}"); + return false; +} + +bool show_help() { + recorder_print(0, "Usage: " CMD_NAME " method ... \n\n%s", get_help_args()); + recorder_exit(0); + return true; +} +bool show_version() { + recorder_print(0, "in3 " IN3_VERSION "\nbuild " __DATE__ " with"); +#ifdef TEST + recorder_print(0, " -DTEST=true"); +#endif +#ifdef EVM_GAS + recorder_print(0, " -DEVM_GAS=true"); +#endif +#ifdef CMD + recorder_print(0, " -DCMD=true"); +#endif +#ifdef IN3_MATH_FAST + recorder_print(0, " -DFAST_MATH=true"); +#endif +#ifdef IN3_SERVER + recorder_print(0, " -DIN3_SERVER=true"); +#endif +#ifdef USE_CURL + recorder_print(0, " -DUSE_CURL=true"); +#else + recorder_print(0, " -DUSE_CURL=false"); +#endif + recorder_print(0, "\n(c) " IN3_COPYRIGHT "\n"); + recorder_exit(0); + return true; +} + +#ifdef NODESELECT_DEF +static bool set_nodelist(in3_t* c, char* nodes, sb_t* sb, bool update) { + if (!update) c->flags = FLAGS_STATS | FLAGS_BOOT_WEIGHTS | (c->flags & FLAGS_ALLOW_EXPERIMENTAL); + char* cpy = alloca(strlen(nodes) + 1); + in3_nodeselect_def_t* nl = in3_nodeselect_def_data(c); + if (!update && nl && nl->nodelist_upd8_params) { + _free(nl->nodelist_upd8_params); + nl->nodelist_upd8_params = NULL; + } + memcpy(cpy, nodes, strlen(nodes) + 1); + char* s = NULL; + sb_add_chars(sb, "{\"nodeRegistry\":{\"needsUpdate\":false,\"nodeList\":["); + for (char* next = strtok(cpy, ","); next; next = strtok(NULL, ",")) { + if (next != cpy) sb_add_char(sb, ','); + str_range_t address, url; + + if (*next == '0' && next[1] == 'x' && (s = strchr(next, ':'))) { + address = (str_range_t){.data = next, .len = s - next}; + url = (str_range_t){.data = s + 1, .len = strlen(s + 1)}; + } + else { + address = (str_range_t){.data = "0x1234567890123456789012345678901234567890", .len = 42}; + url = (str_range_t){.data = next, .len = strlen(next)}; + } + sb_add_chars(sb, "{\"address\":\""); + sb_add_range(sb, address.data, 0, address.len); + sb_add_chars(sb, "\",\"url\":\""); + sb_add_range(sb, url.data, 0, url.len); + sb_add_chars(sb, "\",\"props\":\"0xffff\"}"); + } + sb_add_chars(sb, "]}}}"); + return false; +} +#endif + +static bool set_data(char* value) { + if (strcmp(value, "-") == 0) + get_txdata()->data = get_std_in(); + else if (*value == '0' && value[1] == 'x') + get_txdata()->data = hex_to_new_bytes(value + 2, strlen(value) - 2); + else { + FILE* f = fopen(value, "r"); + bytes_t content = readFile(f); + get_txdata()->data = hex_to_new_bytes((char*) content.data + 2, content.len - 2); + fclose(f); + } + return true; +} +static bool set_string(char** dst, char* value) { + *dst = value; + return true; +} +static bool set_uint64(uint64_t* dst, char* value) { + // TODO support gwei or hex + *dst = (uint64_t) atoll(value); + return true; +} +static bool set_uint32(uint32_t* dst, char* value) { + // TODO support gwei or hex + *dst = (uint32_t) atoi(value); + return true; +} +static bool set_create2(char* value, sb_t* sb) { + if (strlen(value) != 176) die("create2-arguments must have the form -zc2 ::"); + char tmp[177], t2[500]; + memcpy(tmp, value, 177); + tmp[42] = tmp[109] = 0; + sprintf(t2, "{\"zksync\":{\"signer_type\":\"create2\",\"create2\":{\"creator\":\"%s\",\"codehash\":\"%s\",\"saltarg\":\"%s\"}}}", tmp, tmp + 43, tmp + 110); + sb_add_chars(sb, t2); + return false; +} +static bool set_recorder(in3_t* c, char* value, int argc, char** argv, bool write) { + if (write) + recorder_write_start(c, value, argc, argv); + else + recorder_read_start(c, value); + return true; +} +static bool set_pk(in3_t* c, char* value, int argc, char** argv) { + if (value[0] != '0' || value[1] != 'x') { + read_pk(value, get_argument(argc, argv, "-pwd", "--password", true), c, NULL); + return true; + } + else + return false; +} +static bool set_flag(uint32_t* dst, uint32_t val, char* value) { + if (strcmp(value, "true") == 0) + *dst |= val; + else + *dst ^= val; + return true; +} +static bool set_quiet() { + in3_log_set_level(LOG_FATAL); + return true; +} +static bool set_debug() { + in3_log_set_quiet(false); + in3_log_set_level(LOG_TRACE); + *get_output_conf() |= out_debug; + return true; +} + +#ifdef LEDGER_NANO +static bool set_path(in3_t* c, char* value) { + if (value[0] == '0' && value[1] == 'x') { + bytes32_t path; + hex_to_bytes(value, -1, path, 5); + eth_ledger_set_signer_txn(c, path); + return true; + } + else + die("Invalid path for nano ledger"); +} +#endif +#ifdef MULTISIG +static bool set_ms(in3_t* c, char* value) { + address_t adr; + if (hex_to_bytes(value, -1, adr, 20) != 20) die("-ms must be exactly 20 bytes"); + add_gnosis_safe(c, adr); + return true; +} +#endif +bool handle_option(in3_t* c, char* key, char* value, sb_t* conf, int argc, char** argv) { + CHECK_OPTION("test", set_test_transport(c, value)) + CHECK_OPTION("clearCache", true) + CHECK_OPTION("password", true) + CHECK_OPTION("help", show_help()) + CHECK_OPTION("version", show_version()) + CHECK_OPTION("chainId", set_chainId(value, conf)) + CHECK_OPTION("from", set_string(&get_txdata()->from, value)) + CHECK_OPTION("to", set_string(&get_txdata()->to, value)) + CHECK_OPTION("gas", set_uint64(&get_txdata()->gas, value)) + CHECK_OPTION("gas_price", set_uint64(&get_txdata()->gas_price, value)) + CHECK_OPTION("nonce", set_uint64(&get_txdata()->nonce, value)) + CHECK_OPTION("wait", set_uint32(&get_txdata()->wait, "1")) + CHECK_OPTION("block", set_string(&get_txdata()->block, value)) + CHECK_OPTION("data", set_data(value)) + CHECK_OPTION("value", set_string(&get_txdata()->value, get_wei(value))) + CHECK_OPTION("zksync.create2", set_create2(value, conf)) + CHECK_OPTION("test-request", set_flag(get_weightsdata(), weight_test_request, value)) + CHECK_OPTION("test-health-request", set_flag(get_weightsdata(), weight_health, value)) + CHECK_OPTION("response.in", set_response_file(true)) + CHECK_OPTION("response.out", set_response_file(false)) + CHECK_OPTION("file.in", set_recorder(c, value, argc, argv, false)) + CHECK_OPTION("file.out", set_recorder(c, value, argc, argv, true)) + CHECK_OPTION("ms.signatures", true) + CHECK_OPTION("multisig", set_ms(c, value)) + CHECK_OPTION("human", set_flag(get_output_conf(), out_human, value)) + CHECK_OPTION("eth", set_flag(get_output_conf(), out_eth, value)) + CHECK_OPTION("hex", set_flag(get_output_conf(), out_hex, value)) + CHECK_OPTION("json", set_flag(get_output_conf(), out_json, value)) + CHECK_OPTION("quiet", set_quiet()) + CHECK_OPTION("port", set_string(&get_req_exec()->port, value)) + CHECK_OPTION("allowed-methods", set_string(&get_req_exec()->allowed_methods, value)) + CHECK_OPTION("onlysign", set_onlyshow_rawtx()) + CHECK_OPTION("sigtype", set_string(&get_txdata()->signtype, value)) + CHECK_OPTION("debug", set_debug()) +#ifdef NODESELECT_DEF + CHECK_OPTION("nodelist", set_nodelist(c, value, conf, false)) + CHECK_OPTION("bootnodes", set_nodelist(c, value, conf, true)) + CHECK_OPTION("pk", set_pk(c, value, argc, argv)) +#endif +#ifdef LEDGER_NANO + CHECK_OPTION("path", set_path(c, valuev)) +#endif + return false; +} + +void init_recorder(int* argc, char*** argv) { + char* file = get_argument(*argc, *argv, "-fi", "--file.in", true); + if (file) + recorder_update_cmd(file, argc, argv); +} + +void init_env(in3_t* c, int argc, char* argv[]) { + // handle clear cache opt before initializing cache + if (get_argument(argc, argv, "-ccache", "--clearCache", false)) + storage_clear(NULL); + // use the storagehandler to cache data in .in3 + in3_register_file_storage(c); + + // PK + if (getenv("IN3_PK") && !get_argument(argc, argv, "-pk", "--pk", true)) { + char* pks = _strdupn(getenv("IN3_PK"), -1); + bytes32_t pk; + for (char* cc = strtok(pks, ","); cc; cc = strtok(NULL, ",")) { + hex_to_bytes(cc, -1, pk, 32); + eth_set_pk_signer(c, pk); + } + } + + // handle chainId + if (getenv("IN3_CHAIN")) configure(c, "chainId", getenv("IN3_CHAIN")); + +#ifdef ZKSYNC + if (getenv("IN3_ZKS")) configure(c, "zksync.provider_url", getenv("IN3_ZKS")); +#endif +} \ No newline at end of file diff --git a/c/src/cmd/in3/req_exec.c b/c/src/cmd/in3/req_exec.c new file mode 100644 index 000000000..9294f8f19 --- /dev/null +++ b/c/src/cmd/in3/req_exec.c @@ -0,0 +1,93 @@ +#include "req_exec.h" +#include "../../tools/recorder/recorder.h" +#include "../http-server/http_server.h" +#include "helper.h" +req_exec_t* get_req_exec() { + static req_exec_t val = {0}; + return &val; +} + +static void execute(in3_t* c, FILE* f) { + if (feof(f)) die("no data"); + sb_t* sb = sb_new(NULL); + char first = 0, stop = 0; + int level = 0, d = 0; + while (1) { + d = fgetc(f); + if (d == EOF) { + if (first) + die("Invalid json-data from stdin"); + else + recorder_exit(EXIT_SUCCESS); + } + if (first == 0) { + if (d == '{') + stop = '}'; + else if (d == '[') + stop = ']'; + else + continue; + first = d; + } + + sb_add_char(sb, (char) d); + if (d == first) level++; + if (d == stop) level--; + if (level == 0) { + // time to execute + in3_req_t* ctx = req_new(c, sb->data); + if (ctx->error) + recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"error\":{\"code\":%i,\"message\":\"%s\"}\n", 1, ctx->verification_state, ctx->error); + else { + in3_ret_t ret = in3_send_req(ctx); + uint32_t id = d_get_int(ctx->requests[0], K_ID); + if (ctx->error) { + for (char* x = ctx->error; *x; x++) { + if (*x == '\n') *x = ' '; + } + } + + if (ret == IN3_OK) { + if (c->flags & FLAGS_KEEP_IN3) { + str_range_t rr = d_to_json(ctx->responses[0]); + rr.data[rr.len] = 0; + recorder_print(0, "%s\n", rr.data); + } + else { + d_token_t* result = d_get(ctx->responses[0], K_RESULT); + d_token_t* error = d_get(ctx->responses[0], K_ERROR); + char* r = d_create_json(ctx->response_context, result ? result : error); + if (result) + recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"result\":%s}\n", id, r); + else + recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"error\":%s}\n", id, r); + _free(r); + } + } + else + recorder_print(0, "{\"jsonrpc\":\"2.0\",\"id\":%i,\"error\":{\"code\":%i,\"message\":\"%s\"}}\n", id, ctx->verification_state, ctx->error == NULL ? "Unknown error" : ctx->error); + } + fflush(stdout); + req_free(ctx); + first = 0; + sb->len = 0; + } + } +} + +void check_server(in3_t* c) { + if (get_req_exec()->port) { +#ifdef IN3_SERVER + // start server + http_run_server(get_req_exec()->port, c, get_req_exec()->allowed_methods); + recorder_exit(0); +#else + die("You need to compile in3 with -DIN3_SERVER=true to start the server."); +#endif + } + else { + in3_log_info("in3 " IN3_VERSION " - reading json-rpc from stdin. (exit with ctrl C)\n________________________________________________\n"); + execute(c, stdin); + recorder_exit(0); + } +} \ No newline at end of file diff --git a/c/src/cmd/in3/req_exec.h b/c/src/cmd/in3/req_exec.h new file mode 100644 index 000000000..2de336573 --- /dev/null +++ b/c/src/cmd/in3/req_exec.h @@ -0,0 +1,9 @@ +#include "helper.h" + +typedef struct req_exec { + char* port; + char* allowed_methods; +} req_exec_t; + +req_exec_t* get_req_exec(); +void check_server(in3_t* c); \ No newline at end of file diff --git a/c/src/cmd/in3/rpc_handler.c b/c/src/cmd/in3/rpc_handler.c new file mode 100644 index 000000000..7308c7a48 --- /dev/null +++ b/c/src/cmd/in3/rpc_handler.c @@ -0,0 +1,129 @@ +#include "../../api/eth1/abi.h" +#include "../../api/eth1/eth_api.h" +#include "../../api/ipfs/ipfs_api.h" +#include "helper.h" +#include "transport.h" +#include "tx.h" +#include "weights.h" +#define CHECK_RPC(name, fn) \ + if (strcmp(*method, name) == 0) return fn; + +static bool decode_keystore(char* args, int argc, char** argv) { + json_ctx_t* ctx = parse_json(args); + if (d_len(ctx->result) != 1 || d_type(ctx->result + 1) != T_STRING) die("decoding a key expects one argument with the filename of the keystorefile."); + read_pk(d_get_string_at(ctx->result, 0), get_argument(argc, argv, "-pwd", "--password", true), NULL, "key"); + json_free(ctx); + return true; +} +static bool _call(in3_t* c, char** method, sb_t* params) { + encode_abi(c, params, true); + *method = "eth_call"; + return false; +} +static bool _send(in3_t* c, char** method, sb_t* params) { + encode_abi(c, params, false); + if (is_onlyshow_rawtx() && (c->plugin_acts & (PLGN_ACT_SIGN | PLGN_ACT_SIGN_ACCOUNT)) == 0) + *method = "in3_prepareTx"; + else + *method = get_txdata()->wait ? "eth_sendTransactionAndWait" : "eth_sendTransaction"; + return false; +} + +static bool _sign(sb_t* params) { + params->len = 0; + bytes_t* data = get_txdata()->data; + if (data->len > 2 && data->data[0] == '0' && data->data[1] == 'x') + data = hex_to_new_bytes((char*) data->data + 2, data->len - 2); + sb_add_rawbytes(params, "[\"0x", *data, 0); + sb_add_chars(params, "\",NULL,\""); + sb_add_chars(params, get_txdata()->signtype ? get_txdata()->signtype : "raw"); + sb_add_chars(params, "\"]"); + if (data != get_txdata()->data) b_free(data); + return false; +} + +static bool _autocompletelist() { + recorder_print(0, "send call abi_encode abi_decode ipfs_get ipfs_put ecrecover key -sigtype -st eth_sign raw hash sign createkey -ri -ro keystore unlock pk2address pk2public mainnet goerli ewc btc ipfs local true false latest -np -debug -c -chain -p -version -proof -s -signs -b -block -to -d -data -gas_limit -value -w -wait -hex -json in3_nodeList in3_stats in3_sign web3_clientVersion web3_sha3 net_version net_peerCount net_listening eth_protocolVersion eth_syncing eth_coinbase eth_mining eth_hashrate eth_gasPrice eth_accounts eth_blockNumber eth_getBalance eth_getStorageAt eth_getTransactionCount eth_getBlockTransactionCountByHash eth_getBlockTransactionCountByNumber eth_getUncleCountByBlockHash eth_getUncleCountByBlockNumber eth_getCode eth_sign eth_sendTransaction eth_sendRawTransaction eth_call eth_estimateGas eth_getBlockByHash eth_getBlockByNumber eth_getTransactionByHash eth_getTransactionByBlockHashAndIndex eth_getTransactionByBlockNumberAndIndex eth_getTransactionReceipt eth_pendingTransactions eth_getUncleByBlockHashAndIndex eth_getUncleByBlockNumberAndIndex eth_getCompilers eth_compileLLL eth_compileSolidity eth_compileSerpent eth_newFilter eth_newBlockFilter eth_newPendingTransactionFilter eth_uninstallFilter eth_getFilterChanges eth_getFilterLogs eth_getLogs eth_getWork eth_submitWork eth_submitHashrate in3_cacheClear\n"); + return true; +} + +static bool _abi_encode(sb_t* args) { + char* error = NULL; + json_ctx_t* in_data = parse_json(args->data); + if (!in_data) die("invalid params"); + if (!get_txdata()->sig) die("missing signature"); + abi_sig_t* s = abi_sig_create(get_txdata()->sig, &error); + if (s && !error) { + bytes_t data = abi_encode(s, in_data->result, &error); + if (data.data) + print_hex(data.data, data.len); + abi_sig_free(s); + } + if (error) die(error); + return true; +} + +static bool _abi_decode(sb_t* args) { + char* error = NULL; + json_ctx_t* in_data = parse_json(args->data); + if (!in_data) die("invalid params"); + if (!get_txdata()->sig) die("missing signature"); + abi_sig_t* s = abi_sig_create(get_txdata()->sig, &error); + if (s && !error) { + bytes_t data = d_to_bytes(d_get_at(parse_json(args->data)->result, 0)); + json_ctx_t* res = abi_decode(s, data, &error); + if (error) die(error); + if (*get_output_conf() & out_json) + recorder_print(0, "%s\n", d_create_json(res, res->result)); + else + print_val(res->result); + abi_sig_free(s); + } + if (error) die(error); + return true; +} + +static bool _ipfs_get(in3_t* c, sb_t* args) { + if (c->chain.type != CHAIN_IPFS) configure(c, "chainId", "ipfs"); + int size = args->len; + if (size == 2 || args->data[1] != '"' || size < 20 || strstr(args->data + 2, "\"") == NULL) die("missing ipfs hash"); + args->data[size - 2] = 0; +#ifdef IPFS + bytes_t* content = ipfs_get(c, args->data + 2); +#else + die("ipfs is not supported. Please compile with -DIPFS=true"); +#endif + if (!content) die("IPFS hash not found!"); + fwrite(content->data, content->len, 1, stdout); + fflush(stdout); + return true; +} + +static bool _ipfs_put(in3_t* c, sb_t* args) { + if (c->chain.type != CHAIN_IPFS) configure(c, "chainId", "ipfs"); + args->len = 0; + sb_add_rawbytes(args, "[\"0x", readFile(stdin), 0); + sb_add_chars(args, "\",\"hex\"]"); + return false; +} + +bool handle_rpc(in3_t* c, char** method, sb_t* params, int argc, char** argv) { + CHECK_RPC("key", decode_keystore(params->data, argc, argv)) + CHECK_RPC("call", _call(c, method, params)) + CHECK_RPC("abi_encode", _abi_encode(params)) + CHECK_RPC("abi_decode", _abi_decode(params)) + CHECK_RPC("ipfs_get", _ipfs_get(c, params)) + CHECK_RPC("ipfs_put", _ipfs_put(c, params)) + CHECK_RPC("in3_weights", exec_weights(c)) + CHECK_RPC("send", _send(c, method, params)) + CHECK_RPC("sign", _sign(params)) + CHECK_RPC("autocompletelist", _autocompletelist()) + CHECK_RPC("createKey", (*method = "in3_createKey") == NULL) + CHECK_RPC("pk2address", (*method = "in3_pk2address") == NULL) + CHECK_RPC("pk2public", (*method = "in3_pk2public") == NULL) + CHECK_RPC("ecrecover", (*method = "in3_ecrecover") == NULL) + if (get_txdata()->wait) { + CHECK_RPC("eth_sendTransaction", (*method = "eth_sendTransactionAndWait") == NULL) + } + return false; +} \ No newline at end of file diff --git a/c/src/cmd/in3/transport.c b/c/src/cmd/in3/transport.c new file mode 100644 index 000000000..4518314d4 --- /dev/null +++ b/c/src/cmd/in3/transport.c @@ -0,0 +1,123 @@ +#include "../../core/client/request_internal.h" +#include "helper.h" +#ifdef USE_CURL +#include "../../transport/curl/in3_curl.h" +#elif USE_WINHTTP +#include "../../transport/winhttp/in3_winhttp.h" +#else +#include "../../transport/http/in3_http.h" +#endif + +static bool out_response = false; +static bytes_t* last_response; +static bytes_t in_response = {.data = NULL, .len = 0}; +static bool only_show_raw_tx = false; +in3_ret_t debug_transport(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx) { + UNUSED_VAR(plugin_data); + + in3_http_request_t* req = plugin_ctx; + if (action == PLGN_ACT_TRANSPORT_SEND) { +#ifndef DEBUG + if (*get_output_conf() & out_debug) + fprintf(stderr, "send request to %s: \n" COLORT_RYELLOW "%s" COLORT_RESET "\n", req->urls_len ? req->urls[0] : "none", req->payload); +#endif + if (in_response.len) { + for (unsigned int i = 0; i < req->urls_len; i++) { + req->req->raw_response[i].state = IN3_OK; + sb_add_range(&req->req->raw_response[i].data, (char*) in_response.data, 0, in_response.len); + req->req->raw_response[i].state = IN3_OK; + } + return 0; + } + if (only_show_raw_tx && str_find(req->payload, "\"method\":\"eth_sendRawTransaction\"")) { + char* data = str_find(req->payload, "0x"); + *strchr(data, '"') = 0; + recorder_print(0, "%s\n", data); + recorder_exit(EXIT_SUCCESS); + } + } +#ifdef USE_CURL + in3_ret_t r = send_curl(NULL, action, plugin_ctx); +#elif USE_WINHTTP + in3_ret_t r = send_winhttp(NULL, action, plugin_ctx); +#elif TRANSPORTS + in3_ret_t r = send_http(NULL, action, plugin_ctx); +#else + in3_ret_t r = req_set_error(req->req, "No transport supported in the client", IN3_ECONFIG); +#endif + if (action != PLGN_ACT_TRANSPORT_CLEAN) { + last_response = b_new((uint8_t*) req->req->raw_response[0].data.data, req->req->raw_response[0].data.len); +#ifndef DEBUG + if (*get_output_conf() & out_debug) { + if (req->req->raw_response[0].state == IN3_OK) + fprintf(stderr, "success response \n" COLORT_RGREEN "%s" COLORT_RESET "\n", req->req->raw_response[0].data.data); + else + fprintf(stderr, "error response \n" COLORT_RRED "%s" COLORT_RESET "\n", req->req->raw_response[0].data.data); + } +#endif + } + return r; +} +static char* test_name = NULL; +static in3_ret_t test_transport(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx) { + UNUSED_VAR(plugin_data); + in3_http_request_t* req = plugin_ctx; +#ifdef USE_CURL + in3_ret_t r = send_curl(NULL, action, plugin_ctx); +#elif USE_WINHTTP + in3_ret_t r = send_winhttp(NULL, action, plugin_ctx); +#elif TRANSPORTS + in3_ret_t r = send_http(NULL, action, plugin_ctx); +#else + in3_ret_t r = action && plugin_ctx != NULL ? IN3_OK : IN3_ECONFIG; +#endif + if (r == IN3_OK) { + req->payload[strlen(req->payload) - 1] = 0; + recorder_print(0, "[{ \"descr\": \"%s\",\"chainId\": \"0x1\", \"verification\": \"proof\",\"binaryFormat\": false, \"request\": %s, \"response\": %s }]", test_name, req->payload + 1, req->req->raw_response->data.data); + recorder_exit(0); + } + + return r; +} + +void init_transport(in3_t* c) { + in3_plugin_register(c, PLGN_ACT_TRANSPORT, debug_transport, NULL, true); +#ifdef USE_WINHTTP + configure(c, "requestCount", "1"); +#else + configure(c, "requestCount", "2"); +#endif +} + +bool set_test_transport(in3_t* c, char* name) { + test_name = name; + in3_plugin_register(c, PLGN_ACT_TRANSPORT, test_transport, NULL, true); + return true; +} + +bool set_onlyshow_rawtx() { + only_show_raw_tx = true; + return true; +} + +bool is_onlyshow_rawtx() { + return only_show_raw_tx; +} + +bool set_response_file(bool is_in) { + if (is_in) + in_response = readFile(stdin); + else + out_response = true; + return true; +} + +void check_last_output() { + if (out_response && last_response) { + char* r = alloca(last_response->len + 1); + memcpy(r, last_response->data, last_response->len); + r[last_response->len] = 0; + recorder_print(0, "%s\n", r); + recorder_exit(0); + } +} \ No newline at end of file diff --git a/c/src/cmd/in3/transport.h b/c/src/cmd/in3/transport.h new file mode 100644 index 000000000..e80b5e432 --- /dev/null +++ b/c/src/cmd/in3/transport.h @@ -0,0 +1,10 @@ +#include "../../core/client/plugin.h" + +in3_ret_t debug_transport(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx); + +void init_transport(in3_t* c); +bool set_test_transport(in3_t* c, char* name); +bool set_onlyshow_rawtx(); +bool is_onlyshow_rawtx(); +bool set_response_file(bool is_in); +void check_last_output(); \ No newline at end of file diff --git a/c/src/cmd/in3/tx.c b/c/src/cmd/in3/tx.c new file mode 100644 index 000000000..4220264f0 --- /dev/null +++ b/c/src/cmd/in3/tx.c @@ -0,0 +1,83 @@ +#include "tx.h" +#include "helper.h" +static tx_t _tx = {0}; + +tx_t* get_txdata() { + return &_tx; +} + +// prepare a eth_call or eth_sendTransaction +static abi_sig_t* prepare_tx(char* fn_sig, char* to, sb_t* args, char* block_number, uint64_t gas, uint64_t gas_price, char* value, bytes_t* data, char* from) { + char* error = NULL; + bytes_t rdata = {0}; + abi_sig_t* req = fn_sig ? abi_sig_create(fn_sig, &error) : NULL; // only if we have a function signature, we will parse it and create a call_request. + if (error) die(error); // parse-error we stop here. + if (req) { // if type is a tuple, it means we have areuments we need to parse. + json_ctx_t* in_data = parse_json(args->data); // the args are passed as a "[]"- json-array string. + rdata = abi_encode(req, in_data->result, &error); //encode data + if (error) die(error); // we then set the data, which appends the arguments to the functionhash. + json_free(in_data); // of course we clean up ;-) + } // + sb_t* params = sb_new("[{"); // now we create the transactionobject as json-argument. + if (to) { // if this is a deployment we must not include the to-property + sb_add_chars(params, "\"to\":\""); + sb_add_chars(params, to); + sb_add_chars(params, "\" "); + } + if (req || data) { // if we have a request context or explicitly data we create the data-property + if (params->len > 2) sb_add_char(params, ','); // add comma if this is not the first argument + sb_add_chars(params, "\"data\":"); // we will have a data-property + if (req && data) { // if we have a both, we need to concat thewm (this is the case when depkloying a contract with constructorarguments) + uint8_t* full = _malloc(rdata.len - 4 + data->len); // in this case we skip the functionsignature. + memcpy(full, data->data, data->len); + memcpy(full + data->len, rdata.data + 4, rdata.len - 4); + bytes_t bb = bytes(full, rdata.len - 4 + data->len); + sb_add_bytes(params, "", &bb, 1, false); + _free(full); + } + else if (req) + sb_add_bytes(params, "", &rdata, 1, false); + else if (data) + sb_add_bytes(params, "", data, 1, false); + } + + if (block_number) { + sb_add_chars(params, "},\""); + sb_add_chars(params, block_number); + sb_add_chars(params, "\"]"); + } + else { + uint8_t gasdata[8]; + bytes_t g_bytes = bytes(gasdata, 8); + + if (value) { + sb_add_chars(params, ", \"value\":\""); + sb_add_chars(params, value); + sb_add_chars(params, "\""); + } + if (from) { + sb_add_chars(params, ", \"from\":\""); + sb_add_chars(params, from); + sb_add_chars(params, "\""); + } + + if (gas_price) { + long_to_bytes(gas_price, gasdata); + b_optimize_len(&g_bytes); + sb_add_bytes(params, ", \"gasPrice\":", &g_bytes, 1, false); + } + long_to_bytes(gas ? gas : 100000, gasdata); + g_bytes = bytes(gasdata, 8); + b_optimize_len(&g_bytes); + sb_add_bytes(params, ", \"gasLimit\":", &g_bytes, 1, false); + sb_add_chars(params, "}]"); + } + args->len = 0; + sb_add_chars(args, params->data); + sb_free(params); + return req; +} + +void encode_abi(in3_t* c, sb_t* args, bool with_block) { + _tx.abi_sig = prepare_tx(_tx.sig, resolve(c, _tx.to), args, _tx.block == NULL && with_block ? "latest" : _tx.block, _tx.gas, _tx.gas_price, _tx.value, _tx.data, _tx.from); +} \ No newline at end of file diff --git a/c/src/cmd/in3/tx.h b/c/src/cmd/in3/tx.h new file mode 100644 index 000000000..0c7f7d9d8 --- /dev/null +++ b/c/src/cmd/in3/tx.h @@ -0,0 +1,20 @@ +#include "../../api/eth1/abi.h" +#include "helper.h" +typedef struct tx { + bytes_t* data; + char* block; + char* from; + char* to; + uint64_t gas; + uint64_t gas_price; + uint64_t nonce; + char* value; + uint32_t wait; + char* signtype; + char* sig; + abi_sig_t* abi_sig; + +} tx_t; + +tx_t* get_txdata(); +void encode_abi(in3_t* c, sb_t* args, bool with_block); \ No newline at end of file diff --git a/c/src/cmd/in3/weights.c b/c/src/cmd/in3/weights.c new file mode 100644 index 000000000..7a6d367dd --- /dev/null +++ b/c/src/cmd/in3/weights.c @@ -0,0 +1,145 @@ +#include "weights.h" +#include "../../nodeselect/full/nodelist.h" +#include "../../nodeselect/full/nodeselect_def.h" +#include "helper.h" +#ifdef USE_CURL +#include "../../transport/curl/in3_curl.h" +#elif USE_WINHTTP +#include "../../transport/winhttp/in3_winhttp.h" +#else +#include "../../transport/http/in3_http.h" +#endif +static uint32_t weightdata = 0; +uint32_t* get_weightsdata() { + return &weightdata; +} + +bool exec_weights(in3_t* c) { + int run_test_request = weightdata ? ((weightdata & weight_health) ? 2 : 1) : 0; + c->max_attempts = 1; + uint32_t block = 0, b = 0; + BIT_CLEAR(c->flags, FLAGS_AUTO_UPDATE_LIST); + uint64_t now = in3_time(NULL); + char* more = "WEIGHT"; + in3_plugin_execute_all(c, PLGN_ACT_CHAIN_CHANGE, c); + in3_nodeselect_def_t* nl = in3_nodeselect_def_data(c); + if (run_test_request == 1) more = "WEIGHT : LAST_BLOCK"; + if (run_test_request == 2) more = "WEIGHT : NAME VERSION : RUNNING : HEALTH : LAST_BLOCK"; + recorder_print(0, " : %-45s : %7s : %5s : %5s: %s\n------------------------------------------------------------------------------------------------\n", "URL", "BL", "CNT", "AVG", more); + for (unsigned int i = 0; i < nl->nodelist_length; i++) { + in3_req_t* ctx = NULL; + char* health_s = NULL; + if (run_test_request) { + char req[300]; + char adr[41]; + bytes_to_hex((nl->nodelist + i)->address, 20, adr); + sprintf(req, "{\"id\":1,\"jsonrpc\":\"2.0\",\"method\":\"eth_blockNumber\",\"params\":[],\"in3\":{\"dataNodes\":[\"0x%s\"]}}", adr); + ctx = req_new(c, req); + if (ctx) in3_send_req(ctx); + if (run_test_request == 2) { + int health = 1; + char* version = ""; + char* node_name = ""; + uint32_t running = 0; + json_ctx_t* health_res = NULL; + char health_url[500]; + char* urls[1]; + urls[0] = health_url; + sprintf(health_url, "%s/health", nl->nodelist[i].url); + in3_http_request_t r = {0}; + in3_req_t ctx = {0}; + ctx.raw_response = _calloc(sizeof(in3_response_t), 1); + ctx.raw_response->state = IN3_WAITING; + ctx.client = c; + r.req = &ctx; + r.urls = urls; + r.urls_len = 1; + r.payload = ""; +#ifdef USE_CURL + send_curl(NULL, PLGN_ACT_TRANSPORT_SEND, &r); +#elif USE_WINHTTP + send_winhttp(NULL, PLGN_ACT_TRANSPORT_SEND, &r); +#elif TRANSPORTS + send_http(NULL, PLGN_ACT_TRANSPORT_SEND, &r); +#endif + if (ctx.raw_response->state) + health = 0; + else { + health_res = parse_json(ctx.raw_response->data.data); + if (!health_res) + health = 0; + else { + node_name = d_get_string(health_res->result, key("name")); + version = d_get_string(health_res->result, key("version")); + running = d_get_int(health_res->result, key("running")); + char* status = d_get_string(health_res->result, key("status")); + if (!status || strcmp(status, "healthy")) health = 0; + } + } + if (version) { + char* l = strrchr(version, ':'); + if (l) version = l + 1; + } + health_s = _malloc(3000); + sprintf(health_s, "%-22s %-7s %7d %-9s ", node_name ? node_name : "-", version ? version : "-", running, health ? "OK" : "unhealthy"); + + if (ctx.raw_response->data.data) + _free(ctx.raw_response->data.data); + _free(ctx.raw_response); + if (health_res) json_free(health_res); + } + } + in3_node_t* node = nl->nodelist + i; + in3_node_weight_t* weight = nl->weights + i; + uint64_t blacklisted = weight->blacklisted_until > now ? weight->blacklisted_until : 0; + uint32_t calc_weight = in3_node_calculate_weight(weight, node->capacity, now); + char * tr = NULL, *warning = NULL; + if (ctx) { + tr = _malloc(1000); + if (!ctx->error && d_get(ctx->responses[0], K_ERROR)) { + d_token_t* msg = d_get(ctx->responses[0], K_ERROR); + if (d_type(msg) == T_OBJECT) msg = d_get(msg, K_MESSAGE); + sprintf((warning = tr), "%s", msg ? d_string(msg) : "Error-Response!"); + } + else if (!ctx->error) { + b = d_get_int(ctx->responses[0], K_RESULT); + if (block < b) block = b; + + if (b < block - 1) + sprintf((warning = tr), "#%i ( out of sync : %i blocks behind latest )", b, block - b); + else if (strncmp(node->url, "https://", 8)) + sprintf((warning = tr), "#%i (missing https, which is required in a browser )", b); + else if (!IS_APPROX(d_get_int(ctx->responses[0], K_RESULT), d_get_int(d_get(ctx->responses[0], K_IN3), K_CURRENT_BLOCK), 1)) + sprintf((warning = tr), "#%i ( current block mismatch: %i blocks apart )", b, + d_get_int(ctx->responses[0], K_RESULT) - d_get_int(d_get(ctx->responses[0], K_IN3), K_CURRENT_BLOCK)); + else + sprintf(tr, "#%i", b); + } + else if (!strlen(node->url) || !node->props) + sprintf((warning = tr), "No URL spcified anymore props = %i ", (int) (node->props & 0xFFFFFF)); + else if ((node->props & NODE_PROP_DATA) == 0) + sprintf((warning = tr), "The node is marked as not supporting Data-Providing"); + else if (c->proof != PROOF_NONE && (node->props & NODE_PROP_PROOF) == 0) + sprintf((warning = tr), "The node is marked as able to provide proof"); + else if ((c->flags & FLAGS_HTTP) && (node->props & NODE_PROP_HTTP) == 0) + sprintf((warning = tr), "The node is marked as able to support http-requests"); + else + tr = ctx->error; + if (strlen(tr) > 100) tr[100] = 0; + } + if (blacklisted) + recorder_print(0, COLORT_RED); + else if (warning) + recorder_print(0, COLORT_YELLOW); + else if (!weight->response_count) + recorder_print(0, COLORT_DARKGRAY); + else + recorder_print(0, COLORT_GREEN); + recorder_print(0, "%2i %-45s %7i %5i %5i %5i %s%s", i, node->url, (int) (blacklisted ? blacklisted - now : 0), weight->response_count, weight->response_count ? (weight->total_response_time / weight->response_count) : 0, calc_weight, health_s ? health_s : "", tr ? tr : ""); + recorder_print(0, COLORT_RESET "\n"); + if (tr && tr != ctx->error) _free(tr); + if (health_s) _free(health_s); + if (ctx) req_free(ctx); + } + return true; +} \ No newline at end of file diff --git a/c/src/cmd/in3/weights.h b/c/src/cmd/in3/weights.h new file mode 100644 index 000000000..0dd38d33c --- /dev/null +++ b/c/src/cmd/in3/weights.h @@ -0,0 +1,9 @@ +#include "helper.h" + +typedef enum weight_enum { + weight_test_request = 1, + weight_health = 2 +} weight_enum_t; + +uint32_t* get_weightsdata(); +bool exec_weights(in3_t* c); \ No newline at end of file diff --git a/c/src/core/client/client_init.c b/c/src/core/client/client_init.c index 6d7bc8329..bb91a883e 100644 --- a/c/src/core/client/client_init.c +++ b/c/src/core/client/client_init.c @@ -245,36 +245,35 @@ char* in3_configure(in3_t* c, const char* config) { for (d_iterator_t iter = d_iter(json->result); iter.left; d_iter_next(&iter)) { d_token_t* token = iter.token; - if (token->key == key("autoUpdateList")) { + if (token->key == CONFIG_KEY("autoUpdateList")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_AUTO_UPDATE_LIST, (d_int(token) ? true : false)); } - else if (token->key == key("chainType")) + else if (token->key == CONFIG_KEY("chainType")) ; // Ignore - handled within `chainId` case - else if (token->key == key("chainId")) { + else if (token->key == CONFIG_KEY("chainId")) { EXPECT_TOK(token, IS_D_UINT32(token) || (d_type(token) == T_STRING && chain_id(token) != 0), "expected uint32 or string value (mainnet/goerli)"); // check if chainType is set int ct_ = -1; - d_token_t* ct_token = d_get(json->result, key("chainType")); + d_token_t* ct_token = d_get(json->result, CONFIG_KEY("chainType")); if (ct_token) { ct_ = chain_type(ct_token); EXPECT_TOK(ct_token, ct_ != -1, "expected (btc|eth|ipfs|)"); } - else - ct_ = chain_type_from_id(c->chain.chain_id); bool changed = (c->chain.chain_id != chain_id(token)); c->chain.chain_id = chain_id(token); - c->chain.type = (uint8_t) ct_; + c->chain.type = ct_ == -1 ? chain_type_from_id(c->chain.chain_id) : ((in3_chain_type_t) ct_); + if (c->chain.chain_id == CHAIN_ID_BTC && !c->finality && !d_get(json->result, key("finality"))) c->finality = 7; in3_client_register_chain(c, c->chain.chain_id, c->chain.type, 2); if (changed) in3_plugin_execute_all(c, PLGN_ACT_CHAIN_CHANGE, c); } - else if (token->key == key("signatureCount")) { + else if (token->key == CONFIG_KEY("signatureCount")) { EXPECT_TOK_U8(token); c->signature_count = (uint8_t) d_int(token); } - else if (token->key == key("finality")) { + else if (token->key == CONFIG_KEY("finality")) { EXPECT_TOK_U16(token); #ifdef POA if (c->chain.chain_id == CHAIN_ID_GOERLI) @@ -282,24 +281,24 @@ char* in3_configure(in3_t* c, const char* config) { #endif c->finality = (uint16_t) d_int(token); } - else if (token->key == key("includeCode")) { + else if (token->key == CONFIG_KEY("includeCode")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_INCLUDE_CODE, (d_int(token) ? true : false)); } - else if (token->key == key("bootWeights")) { + else if (token->key == CONFIG_KEY("bootWeights")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_BOOT_WEIGHTS, (d_int(token) ? true : false)); } - else if (token->key == key("maxAttempts")) { + else if (token->key == CONFIG_KEY("maxAttempts")) { EXPECT_TOK_U16(token); EXPECT_CFG(d_int(token), "maxAttempts must be at least 1"); c->max_attempts = d_int(token); } - else if (token->key == key("keepIn3")) { + else if (token->key == CONFIG_KEY("keepIn3")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_KEEP_IN3, (d_int(token) ? true : false)); } - else if (token->key == key("debug")) { + else if (token->key == CONFIG_KEY("debug")) { if (d_int(token)) { in3_log_set_level(LOG_TRACE); in3_log_set_quiet(false); @@ -307,23 +306,23 @@ char* in3_configure(in3_t* c, const char* config) { else in3_log_set_quiet(true); } - else if (token->key == key("stats")) { + else if (token->key == CONFIG_KEY("stats")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_STATS, (d_int(token) ? true : false)); } - else if (token->key == key("useBinary")) { + else if (token->key == CONFIG_KEY("useBinary")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_BINARY, (d_int(token) ? true : false)); } - else if (token->key == key("experimental")) { + else if (token->key == CONFIG_KEY("experimental")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_ALLOW_EXPERIMENTAL, (d_int(token) ? true : false)); } - else if (token->key == key("useHttp")) { + else if (token->key == CONFIG_KEY("useHttp")) { EXPECT_TOK_BOOL(token); BITMASK_SET_BOOL(c->flags, FLAGS_HTTP, (d_int(token) ? true : false)); } - else if (token->key == key("maxVerifiedHashes")) { + else if (token->key == CONFIG_KEY("maxVerifiedHashes")) { EXPECT_TOK_U16(token); if (c->max_verified_hashes < d_long(token)) { c->chain.verified_hashes = _realloc(c->chain.verified_hashes, @@ -335,18 +334,18 @@ char* in3_configure(in3_t* c, const char* config) { c->max_verified_hashes = d_long(token); c->alloc_verified_hashes = c->max_verified_hashes; } - else if (token->key == key("timeout")) { + else if (token->key == CONFIG_KEY("timeout")) { EXPECT_TOK_U32(token); c->timeout = d_long(token); } - else if (token->key == key("proof")) { + else if (token->key == CONFIG_KEY("proof")) { EXPECT_TOK_STR(token); EXPECT_TOK(token, !strcmp(d_string(token), "full") || !strcmp(d_string(token), "standard") || !strcmp(d_string(token), "none"), "expected values - full/standard/none"); c->proof = strcmp(d_string(token), "full") == 0 ? PROOF_FULL : (strcmp(d_string(token), "standard") == 0 ? PROOF_STANDARD : PROOF_NONE); } - else if (token->key == key("verifiedHashes")) { + else if (token->key == CONFIG_KEY("verifiedHashes")) { EXPECT_TOK_ARR(token); EXPECT_TOK(token, (unsigned) d_len(token) <= c->max_verified_hashes, "expected array len <= maxVerifiedHashes"); if (!c->chain.verified_hashes) diff --git a/c/src/core/client/execute.c b/c/src/core/client/execute.c index 3b50eddde..39eac1b6f 100644 --- a/c/src/core/client/execute.c +++ b/c/src/core/client/execute.c @@ -297,7 +297,7 @@ static bool is_user_error(d_token_t* error, char** err_msg) { *err_msg = d_type(error) == T_STRING ? d_string(error) : d_get_string(error, K_MESSAGE); // here we need to find a better way to detect user errors // currently we assume a error-message starting with 'Error:' is a server error and not a user error. - return *err_msg && strncmp(*err_msg, "Error:", 6) != 0 && strncmp(*err_msg, "TypeError:", 10) != 0; + return *err_msg && strncmp(*err_msg, "Error:", 6) != 0 && strncmp(*err_msg, "TypeError:", 10) != 0 && strncmp(*err_msg, "Error connect", 13) != 0; } NONULL static void clear_response(in3_response_t* response) { @@ -683,7 +683,7 @@ typedef struct { static void transport_cleanup(in3_req_t* ctx, ctx_req_transports_t* transports, bool free_all) { for (int i = 0; i < transports->len; i++) { - if (free_all || transports->req[i].req == ctx) { + if ((free_all && transports->req[i].req) || transports->req[i].req == ctx) { in3_http_request_t req = {.req = ctx, .cptr = transports->req[i].ptr, .urls_len = 0, .urls = NULL, .payload = NULL}; in3_plugin_execute_first_or_none(ctx, PLGN_ACT_TRANSPORT_CLEAN, &req); if (!free_all) { @@ -722,6 +722,15 @@ static void in3_handle_rpc_next(in3_req_t* ctx, ctx_req_transports_t* transports } } + // looks like we were not able to send out the first request, so waiting for the second won't help. + node_match_t* w = ctx->nodes; + for (int j = 0; w; j++, w = w->next) { + if (ctx->raw_response[j].state == IN3_WAITING && !ctx->raw_response[j].data.data) { + in3_ctx_add_response(ctx, j, IN3_ERPC, "The request could not be send!", -1, 1); + return; + } + } + req_set_error(ctx, "waiting to fetch more responses, but no cptr was registered", IN3_ENOTSUP); } @@ -740,7 +749,7 @@ void in3_handle_rpc(in3_req_t* ctx, ctx_req_transports_t* transports) { for (unsigned int i = 0; i < request->urls_len; i++) in3_log_trace("... request to " COLOR_YELLOW_STR "\n... " COLOR_MAGENTA_STR "\n", request->urls[i], i == 0 ? request->payload : ""); - // handle it + // handle it (only if there is a transport) in3_plugin_execute_first(ctx, PLGN_ACT_TRANSPORT_SEND, request); // debug output @@ -822,12 +831,14 @@ void in3_sign_ctx_set_signature( _free(sign_ctx->signature.data); } -in3_req_t* req_find_required(const in3_req_t* parent, const char* search_method) { - in3_req_t* sub_ctx = parent->required; - while (sub_ctx) { - if (!sub_ctx->requests) continue; - if (req_is_method(sub_ctx, search_method)) return sub_ctx; - sub_ctx = sub_ctx->required; +in3_req_t* req_find_required(const in3_req_t* parent, const char* search_method, const char* param_query) { + for (in3_req_t* r = parent->required; r; r = r->required) { + if (!r->requests) continue; + if (req_is_method(r, search_method)) { + d_token_t* params = d_get(r->requests[0], K_PARAMS); + if (param_query && (!params || !params->data || !str_find((void*) params->data, param_query))) continue; + return r; + } } return NULL; } @@ -927,9 +938,6 @@ in3_ret_t in3_req_execute(in3_req_t* req) { // is it a valid request? if (!req->request_context || d_type(d_get(req->requests[0], K_METHOD)) != T_STRING) return req_set_error(req, "No Method defined", IN3_ECONFIG); - // if there is response we are done. - if (req->response_context && req->verification_state == IN3_OK) return IN3_OK; - // if we have required-contextes, we need to check them first if (req->required && (ret = in3_req_execute(req->required))) { if (ret == IN3_EIGNORE) @@ -938,6 +946,9 @@ in3_ret_t in3_req_execute(in3_req_t* req) { return req_set_error(req, get_error_message(req->required, ret), ret); } + // if there is response we are done. + if (req->response_context && req->verification_state == IN3_OK) return IN3_OK; + in3_log_debug("ctx_execute %s ... attempt %i\n", d_get_string(req->requests[0], K_METHOD), req->attempt + 1); switch (req->type) { diff --git a/c/src/core/client/plugin.h b/c/src/core/client/plugin.h index 5d8601723..4e6a7524a 100644 --- a/c/src/core/client/plugin.h +++ b/c/src/core/client/plugin.h @@ -279,8 +279,9 @@ typedef struct sign_prepare_ctx { /** type of the requested signature */ typedef enum { - SIGN_EC_RAW = 0, /**< sign the data directly */ - SIGN_EC_HASH = 1, /**< hash and sign the data */ + SIGN_EC_RAW = 0, /**< sign the data directly */ + SIGN_EC_HASH = 1, /**< hash and sign the data */ + SIGN_EC_PREFIX = 2, /**< add Ethereum Signed Message-Proefix, hash and sign the data */ } d_signature_type_t; /** @@ -374,9 +375,9 @@ typedef void (*in3_storage_clear)( * context used during get config */ typedef struct in3_cache_ctx { - in3_req_t* req; /**< the request context */ - char* key; /**< the key to fetch */ - bytes_t* content; /**< the content to set */ + in3_req_t* req; /**< the request context */ + const char* key; /**< the key to fetch */ + bytes_t* content; /**< the content to set */ } in3_cache_ctx_t; /** diff --git a/c/src/core/client/request.c b/c/src/core/client/request.c index c06ab8014..5b3398fa8 100644 --- a/c/src/core/client/request.c +++ b/c/src/core/client/request.c @@ -55,6 +55,16 @@ static in3_ret_t in3_plugin_init(in3_req_t* ctx) { return IN3_OK; } +in3_req_t* req_new_clone(in3_t* client, const char* req_data) { + char* data = _strdupn(req_data, -1); + in3_req_t* r = req_new(client, data); + if (r) + in3_cache_add_ptr(&r->cache, data); + else + _free(data); + return r; +} + in3_req_t* req_new(in3_t* client, const char* req_data) { assert_in3(client); assert(req_data); @@ -69,6 +79,7 @@ in3_req_t* req_new(in3_t* client, const char* req_data) { if (req_data != NULL) { ctx->request_context = parse_json(req_data); if (!ctx->request_context) { + in3_log_error("Invalid json-request: %s\n", req_data); req_set_error(ctx, "Error parsing the JSON-request!", IN3_EINVAL); return ctx; } @@ -109,6 +120,13 @@ char* req_get_error_data(in3_req_t* ctx) { return ctx ? ctx->error : "No request context"; } +char* req_get_result_json(in3_req_t* ctx, int index) { + assert_in3_req(ctx); + if (!ctx->responses) return NULL; + d_token_t* res = d_get(ctx->responses[index], K_RESULT); + return res ? d_create_json(ctx->response_context, res) : NULL; +} + char* req_get_response_data(in3_req_t* ctx) { assert_in3_req(ctx); @@ -312,7 +330,7 @@ in3_ret_t in3_rpc_handle_with_int(in3_rpc_handle_ctx_t* hctx, uint64_t value) { return in3_rpc_handle_with_string(hctx, s); } -in3_ret_t req_send_sub_request(in3_req_t* parent, char* method, char* params, char* in3, d_token_t** result) { +in3_ret_t req_send_sub_request(in3_req_t* parent, char* method, char* params, char* in3, d_token_t** result, in3_req_t** child) { bool use_cache = strcmp(method, "eth_sendTransaction") == 0; if (params == NULL) params = ""; char* req = NULL; @@ -343,6 +361,8 @@ in3_ret_t req_send_sub_request(in3_req_t* parent, char* method, char* params, ch if (strncmp(params, p.data + 1, p.len - 2) == 0) break; } + if (ctx && child) *child = ctx; + if (ctx) switch (in3_req_state(ctx)) { case REQ_ERROR: @@ -369,6 +389,7 @@ in3_ret_t req_send_sub_request(in3_req_t* parent, char* method, char* params, ch } ctx = req_new(parent->client, req); if (!ctx) return req_set_error(parent, "Invalid request!", IN3_ERPC); + if (child) *child = ctx; if (use_cache) in3_cache_add_ptr(&ctx->cache, req)->props = CACHE_PROP_SRC_REQ; in3_ret_t ret = req_add_required(parent, ctx); @@ -392,6 +413,8 @@ in3_ret_t req_require_signature(in3_req_t* ctx, d_signature_type_t type, bytes_t return IN3_OK; } + in3_log_debug("requesting signature type=%d from account %x\n", type, from.len > 2 ? bytes_to_int(from.data, 4) : 0); + // first try internal plugins for signing, before we create an context. if (in3_plugin_is_registered(ctx->client, PLGN_ACT_SIGN)) { in3_sign_ctx_t sc = {.account = from, .req = ctx, .message = raw_data, .signature = bytes(NULL, 0), .type = type}; @@ -404,10 +427,11 @@ in3_ret_t req_require_signature(in3_req_t* ctx, d_signature_type_t type, bytes_t else if (r != IN3_EIGNORE && r != IN3_OK) return r; } + in3_log_debug("nobody picked up the signature, sending req now \n"); // get the signature from required - const char* method = type == SIGN_EC_HASH ? "sign_ec_hash" : "sign_ec_raw"; - in3_req_t* c = req_find_required(ctx, method); + const char* method = type == SIGN_EC_HASH ? "sign_ec_hash" : (type == SIGN_EC_PREFIX ? "sign_ec_prefix" : "sign_ec_raw"); + in3_req_t* c = req_find_required(ctx, method, NULL); if (c) switch (in3_req_state(c)) { case REQ_ERROR: diff --git a/c/src/core/client/request.h b/c/src/core/client/request.h index 5250efaae..4436b4c06 100644 --- a/c/src/core/client/request.h +++ b/c/src/core/client/request.h @@ -133,6 +133,17 @@ NONULL in3_req_t* req_new( in3_t* client, /**< [in] the client-config. */ const char* req_data /**< [in] the rpc-request as json string. */ ); +/** + * creates a new request but clones the request-data. + * + * the request data will be parsed and represented in the context. + * calling this function will only parse the request data, but not send anything yet. + * + */ +NONULL in3_req_t* req_new_clone( + in3_t* client, /**< [in] the client-config. */ + const char* req_data /**< [in] the rpc-request as json string. */ +); /** * sends a previously created request to nodes and verifies it. * @@ -312,6 +323,11 @@ char* req_get_response_data( in3_req_t* req /**< [in] the request context. */ ); +/** + * returns the result or NULL in case of an error for that context. The result must be freed! + */ +char* req_get_result_json(in3_req_t* ctx, int index); + /** * returns the type of the request */ @@ -339,7 +355,7 @@ NONULL void req_free( * ```c in3_ret_t get_from_nodes(in3_req_t* parent, char* method, char* params, bytes_t* dst) { // check if the method is already existing - in3_req_t* req = req_find_required(parent, method); + in3_req_t* req = req_find_required(parent, method, NULL); if (ctx) { // found one - so we check if it is useable. switch (in3_req_state(ctx)) { @@ -386,9 +402,11 @@ NONULL in3_ret_t req_add_required( * * This method is used internaly to find a previously added context. */ -NONULL in3_req_t* req_find_required( - const in3_req_t* parent, /**< [in] the current request context. */ - const char* method /**< [in] the method of the rpc-request. */ +NONULL_FOR((1, 2)) +in3_req_t* req_find_required( + const in3_req_t* parent, /**< [in] the current request context. */ + const char* method, /**< [in] the method of the rpc-request. */ + const char* param_query /**< [in] a optional string within thew params. */ ); /** * removes a required context after usage. diff --git a/c/src/core/client/request_internal.h b/c/src/core/client/request_internal.h index 64e80f383..80d0f552f 100644 --- a/c/src/core/client/request_internal.h +++ b/c/src/core/client/request_internal.h @@ -93,7 +93,7 @@ in3_ret_t req_handle_failable( ); NONULL_FOR((1, 2, 3, 5)) -in3_ret_t req_send_sub_request(in3_req_t* parent, char* method, char* params, char* in3, d_token_t** result); +in3_ret_t req_send_sub_request(in3_req_t* parent, char* method, char* params, char* in3, d_token_t** result, in3_req_t** child); NONULL in3_ret_t req_require_signature(in3_req_t* req, d_signature_type_t type, bytes_t* sig, bytes_t raw_data, bytes_t from); NONULL in3_ret_t in3_retry_same_node(in3_req_t* req); diff --git a/c/src/core/client/rpc.yml b/c/src/core/client/rpc.yml new file mode 100644 index 000000000..cb7088e42 --- /dev/null +++ b/c/src/core/client/rpc.yml @@ -0,0 +1,139 @@ +config: + descr: | + There are also some Incubed specific rpc-methods, which will help the clients to bootstrap and update the nodeLists. + + + The incubed client itself offers special RPC-Methods, which are mostly handled directly inside the client: + + # config + config: + + chainId: + type: string | uint + descr: the chainId or the name of a known chain. It defines the nodelist to connect to. + example: goerli + optional: true + default: mainnet + enum: + mainnet: Mainnet Chain + goerli: Goerli Testnet + ewc: Energy WebFoundation + btc: Bitcoin + ipfs: ipfs + local: local-chain + cmd: c + + finality: + type: int + descr: the number in percent needed in order reach finality (% of signature of the validators). + example: 50 + optional: true + default: 0 + cmd: f + + includeCode: + type: bool + descr: if true, the request should include the codes of all accounts. otherwise only the the codeHash is returned. In this case the client may ask by calling eth_getCode() afterwards. + example: true + optional: true + default: false + + debug: + type: bool + descr: if true, debug messages will be written to stderr. + example: true + optional: true + default: false + + maxAttempts: + type: int + descr: max number of attempts in case a response is rejected. + example: 1 + optional: true + default: 7 + cmd: a + + keepIn3: + type: bool + descr: if true, requests sent to the input sream of the comandline util will be send theor responses in the same form as the server did. + example: true + optional: true + default: false + cmd: kin3 + + stats: + type: bool + descr: if true, requests sent will be used for stats. + example: false + optional: true + default: true + + + useBinary: + type: bool + descr: if true the client will use binary format. This will reduce the payload of the responses by about 60% but should only be used for embedded systems or when using the API, since this format does not include the propertynames anymore. + example: true + optional: true + default: false + + experimental: + type: bool + descr: if true the client allows to use use experimental features, otherwise a exception is thrown if those would be used. + example: true + optional: true + default: false + cmd: x + + timeout: + descr: specifies the number of milliseconds before the request times out. increasing may be helpful if the device uses a slow connection. + type: uint64 + optional: true + example: 100000 + default: 20000 + + proof: + descr: if true the nodes should send a proof of the response. If set to none, verification is turned off completly. + type: string + optional: true + enum: + none: no proof will be generated or verfiied. This also works with standard rpc-endpoints. + standard: Stanbdard Proof means all important properties are verfiied + full: In addition to standard, also some rarly needed properties are verfied, like uncles. But this causes a bigger payload. + example: none + default: standard + cmd: p + + replaceLatestBlock: + descr: if specified, the blocknumber *latest* will be replaced by blockNumber- specified value. + type: int + optional: true + example: 6 + cmd: l + + + # rpc-commands + + in3_config: + skipApi: true + descr: changes the configuration of a client. The configuration is passed as the first param and may contain only the values to change. + params: + config: + descr: a Object with config-params. + + result: + descr: an boolean confirming that the config has changed. + example: + request: + - chainId: "0x5" + maxAttempts: 4 + nodeLimit: 10 + nodes: + nodeList: + - address: "0x1234567890123456789012345678901234567890" + url: "https://mybootnode-A.com" + props: "0xFFFF" + - address: "0x1234567890123456789012345678901234567890" + url: "https://mybootnode-B.com" + props: "0xFFFF" + response: true + diff --git a/c/src/core/util/bytes.h b/c/src/core/util/bytes.h index de07d151f..0aa9fe3a1 100644 --- a/c/src/core/util/bytes.h +++ b/c/src/core/util/bytes.h @@ -123,6 +123,12 @@ NONULL static inline void b_optimize_len(bytes_t* b) { } } +static inline int b_compare(bytes_t a, bytes_t b) { + return (a.len == b.len) + ? memcmp(a.data, b.data, a.len) + : ((int) a.len) - ((int) b.len); +} + #define b_to_stack(d) \ { \ bytes_t o = d; \ diff --git a/c/src/core/util/data.c b/c/src/core/util/data.c index f062a4716..2bb60a447 100644 --- a/c/src/core/util/data.c +++ b/c/src/core/util/data.c @@ -372,7 +372,7 @@ NONULL int parse_number(json_ctx_t* jp, d_token_t* item) { uint64_t value = 0; // the resulting value (if it is a integer) jp->c--; // we also need to include hte previous character! - for (int i = 0; i < 20; i++) { // we are not accepting more than 20 characters, since a uint64 can hold up to 18446744073709552000 (which has 20 digits) + for (int i = 0; i < 21; i++) { // we are not accepting more than 20 characters, since a uint64 can hold up to 18446744073709552000 (which has 20 digits) if (jp->c[i] >= '0' && jp->c[i] <= '9') // as long as this is a digit value = value * 10 + (jp->c[i] - '0'); // we handle it and add it to the value. else { @@ -418,6 +418,8 @@ NONULL int parse_string(json_ctx_t* jp, d_token_t* item) { char* start = jp->c; size_t l, i; int n; + bool ishex = false; + int escape = 0; while (true) { switch (*(jp->c++)) { @@ -426,8 +428,17 @@ NONULL int parse_string(json_ctx_t* jp, d_token_t* item) { case '\'': case '"': if (start[-1] != jp->c[-1]) continue; - l = jp->c - start - 1; - if (l > 1 && *start == '0' && start[1] == 'x' && *(start - 1) != '\'') { + l = jp->c - start - 1; + ishex = l > 1 && *start == '0' && start[1] == 'x' && *(start - 1) != '\''; + if (ishex) + for (size_t n = 2; n < l; n++) { + char cc = start[n]; + if (!((cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'f') || (cc >= 'A' && cc <= 'F'))) { + ishex = false; + break; + } + } + if (ishex) { // this is a hex-value if (l == 2) { // empty byte array @@ -460,13 +471,25 @@ NONULL int parse_string(json_ctx_t* jp, d_token_t* item) { // here we do change or fix the input string because this would be an invalid string otherwise. *(jp->c - 1) = (*(start - 1) = '"'); } + l -= escape; item->len = l | T_STRING << 28; item->data = _malloc(l + 1); - memcpy(item->data, start, l); + if (escape) { + char* x = start; + for (size_t n = 0; n < l; n++, x++) { + if (*x == '\\') x++; + item->data[n] = *x; + } + } + else + memcpy(item->data, start, l); item->data[l] = 0; } return 0; - case '\\': jp->c++; break; + case '\\': + jp->c++; + escape++; + break; } } } @@ -880,24 +903,25 @@ d_token_t* json_create_bytes(json_ctx_t* jp, bytes_t value) { return r; } -d_token_t* json_create_object(json_ctx_t* jp) { - return next_item(jp, T_OBJECT, 0); +int json_create_object(json_ctx_t* jp) { + next_item(jp, T_OBJECT, 0); + return jp->len - 1; } -d_token_t* json_create_array(json_ctx_t* jp) { - return next_item(jp, T_ARRAY, 0); +int json_create_array(json_ctx_t* jp) { + next_item(jp, T_ARRAY, 0); + return jp->len - 1; } -d_token_t* json_object_add_prop(d_token_t* object, d_key_t key, d_token_t* value) { - object->len++; +void json_object_add_prop(json_ctx_t* jp, int ob_index, d_key_t key, d_token_t* value) { + jp->result[ob_index].len++; value->key = key; - return object; } -d_token_t* json_array_add_value(d_token_t* object, d_token_t* value) { - value->key = object->len; +void json_array_add_value(json_ctx_t* jp, int ob_index, d_token_t* value) { + d_token_t* object = jp->result + ob_index; + value->key = object->len; object->len++; - return object; } static void write_token_count(bytes_builder_t* bb, int len) { diff --git a/c/src/core/util/data.h b/c/src/core/util/data.h index 016a41daa..1f9126a4e 100644 --- a/c/src/core/util/data.h +++ b/c/src/core/util/data.h @@ -151,10 +151,10 @@ NONULL d_token_t* json_create_bool(json_ctx_t* jp, bool value); NONULL d_token_t* json_create_int(json_ctx_t* jp, uint64_t value); NONULL d_token_t* json_create_string(json_ctx_t* jp, char* value, int len); NONULL d_token_t* json_create_bytes(json_ctx_t* jp, bytes_t value); -NONULL d_token_t* json_create_object(json_ctx_t* jp); -NONULL d_token_t* json_create_array(json_ctx_t* jp); -NONULL d_token_t* json_object_add_prop(d_token_t* object, d_key_t key, d_token_t* value); -NONULL d_token_t* json_array_add_value(d_token_t* object, d_token_t* value); +NONULL int json_create_object(json_ctx_t* jp); +NONULL int json_create_array(json_ctx_t* jp); +NONULL void json_object_add_prop(json_ctx_t* jp, int ob_index, d_key_t key, d_token_t* value); +NONULL void json_array_add_value(json_ctx_t* jp, int parent_index, d_token_t* value); // Helper function to map string to 2byte keys (only for tests or debugging) char* d_get_keystr(json_ctx_t* json, d_key_t k); /**< returns the string for a key. This only works for index keys or known keys! */ diff --git a/c/src/core/util/debug.h b/c/src/core/util/debug.h index 72d0b5e09..6009055e1 100644 --- a/c/src/core/util/debug.h +++ b/c/src/core/util/debug.h @@ -174,6 +174,5 @@ static inline void add_hex(sb_t* sb, char prefix, const char* property, bytes_t } /** used for exeuting a function based on the name. This macro will return if the name matches. */ -#define TRY_RPC(name, fn) \ - if (strcmp(ctx->method, name) == 0) return fn; + #endif /* DEBUG_H */ \ No newline at end of file diff --git a/c/src/core/util/scache.c b/c/src/core/util/scache.c index 63795794e..54bbed85c 100644 --- a/c/src/core/util/scache.c +++ b/c/src/core/util/scache.c @@ -33,6 +33,7 @@ *******************************************************************************/ #include "scache.h" +#include "data.h" #include "mem.h" bytes_t* in3_cache_get_entry(cache_entry_t* cache, bytes_t* key) { @@ -44,7 +45,12 @@ bytes_t* in3_cache_get_entry(cache_entry_t* cache, bytes_t* key) { void in3_cache_free(cache_entry_t* cache, bool is_external) { cache_entry_t* p = NULL; while (cache) { - if (cache->key.data) _free(cache->key.data); + if (cache->key.data) { + if (cache->props & CACHE_PROP_JSON) + json_free((void*) cache->value.data); + else + _free(cache->key.data); + } if (cache->props & CACHE_PROP_MUST_FREE && ((cache->props & CACHE_PROP_ONLY_EXTERNAL) == 0 || is_external)) _free(cache->value.data); p = cache; diff --git a/c/src/core/util/scache.h b/c/src/core/util/scache.h index 4f3970b64..6df98fa4b 100644 --- a/c/src/core/util/scache.h +++ b/c/src/core/util/scache.h @@ -53,6 +53,7 @@ typedef enum cache_props { CACHE_PROP_MUST_FREE = 0x1, /**< indicates the content must be freed*/ CACHE_PROP_SRC_REQ = 0x2, /**< the value holds the src-request */ CACHE_PROP_ONLY_EXTERNAL = 0x4, /**< should only be freed if the context is external */ + CACHE_PROP_JSON = 0x8, /**< indicates the content is a json_ctxt and must be freed as such*/ CACHE_PROP_PAYMENT = 0x80 /**< This cache-entry is a payment.data */ } cache_props_t; /** diff --git a/c/src/core/util/stringbuilder.c b/c/src/core/util/stringbuilder.c index d0da8876a..62c0be58d 100644 --- a/c/src/core/util/stringbuilder.c +++ b/c/src/core/util/stringbuilder.c @@ -183,9 +183,9 @@ sb_t* sb_add_hexuint_l(sb_t* sb, uintmax_t uint, size_t l) { return sb; } -sb_t* sb_add_int(sb_t* sb, uint64_t val) { - char tmp[19]; // UINT64_MAX => 18446744073709551615 => 0xFFFFFFFFFFFFFFFF - int l = sprintf(tmp, "%" PRId64, val); +sb_t* sb_add_int(sb_t* sb, int64_t val) { + char tmp[30]; // UINT64_MAX => 18446744073709551615 => 0xFFFFFFFFFFFFFFFF + int l = sprintf(tmp, "%" PRIi64, val); check_size(sb, l); memcpy(sb->data + sb->len, tmp, l); sb->len += l; @@ -227,10 +227,17 @@ char* format_json(const char* json) { return _sb.data; } -sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, unsigned int fix_size) { +static const uint8_t zero = 0; + +sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, int fix_size) { + if (fix_size == -1) { + if (b.len == 0) b = bytes((uint8_t*) &zero, 1); + b_optimize_len(&b); + } int l = prefix ? strlen(prefix) : 0; int bl = b.len * 2; - if (fix_size > b.len) bl = fix_size * 2; + if (fix_size > (int) b.len) bl = fix_size * 2; + if (fix_size == -1 && b.len && *b.data < 16) bl--; l += bl; if (l == 0) return sb; check_size(sb, l); @@ -238,9 +245,16 @@ sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, unsigned int fix_size) sb->len += l; sb->data[sb->len] = 0; int p = sb->len - bl; - for (unsigned int i = b.len; i < fix_size; i++, p += 2) + for (int i = (int) b.len; i < fix_size; i++, p += 2) sb->data[p] = sb->data[p + 1] = '0'; - bytes_to_hex(b.data, b.len, sb->data + p); + if (fix_size == -1 && b.len && *b.data < 16) { + char tmp[3]; + bytes_to_hex(b.data, 1, tmp); + sb->data[p] = tmp[1]; + bytes_to_hex(b.data + 1, b.len - 1, sb->data + p + 1); + } + else + bytes_to_hex(b.data, b.len, sb->data + p); return sb; } diff --git a/c/src/core/util/stringbuilder.h b/c/src/core/util/stringbuilder.h index 8a652de4e..ad75dd236 100644 --- a/c/src/core/util/stringbuilder.h +++ b/c/src/core/util/stringbuilder.h @@ -82,10 +82,10 @@ NONULL_FOR((1, 3)) sb_t* sb_add_bytes(sb_t* sb, const char* prefix, const bytes_t* bytes, int len, bool as_array); /**< add bytes as 0x-prefixed hexcoded string (including an optional prefix), if len>1 is passed bytes maybe an array ( if as_array==true) */ NONULL sb_t* sb_add_hexuint_l(sb_t* sb, uintmax_t uint, size_t l); /**< add a integer value as hexcoded, 0x-prefixed string*/ NONULL sb_t* sb_add_escaped_chars(sb_t* sb, const char* chars); /**< add chars but escapes all quotes */ -NONULL sb_t* sb_add_int(sb_t* sb, uint64_t val); /**< adds a numeric value to the stringbuilder */ +NONULL sb_t* sb_add_int(sb_t* sb, int64_t val); /**< adds a numeric value to the stringbuilder */ NONULL char* format_json(const char* json); /**< format a json string and returns a new string, which needs to be freed */ NONULL_FOR((1)) -sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, unsigned int fix_size); +sb_t* sb_add_rawbytes(sb_t* sb, char* prefix, bytes_t b, int fix_size); sb_t* sb_print(sb_t* sb, const char* fmt, ...); sb_t* sb_vprint(sb_t* sb, const char* fmt, va_list args); diff --git a/c/src/core/util/utils.c b/c/src/core/util/utils.c index 09a4f2409..54d4ee67b 100644 --- a/c/src/core/util/utils.c +++ b/c/src/core/util/utils.c @@ -398,3 +398,22 @@ int64_t parse_float_val(const char* data, int32_t expo) { for (; expo > 0; expo--) val *= 10; return val; } + +void b256_add(bytes32_t a, uint8_t* b, wlen_t len_b) { + optimize_len(b, len_b); + uint8_t * pa = a + 31, *pb = b + len_b - 1; + uint_fast16_t carry = 0; + do { + carry += *pa + *pb; + *pa = carry & 0xFF; + carry >>= 8; + pb--, pa--; + } while (b == pb); + + while (carry && pa >= a) { + carry += *pa; + *pa = carry & 0xFF; + carry >>= 8; + pa--; + } +} diff --git a/c/src/core/util/utils.h b/c/src/core/util/utils.h index 64acd9d1c..328134038 100644 --- a/c/src/core/util/utils.h +++ b/c/src/core/util/utils.h @@ -224,6 +224,11 @@ uint64_t current_ms(); return _r; \ } \ } +#define TRY_RPC(name, fn) \ + if (strcmp(ctx->method, name) == 0) return fn; +/** used in if-conditions and returns true if the vc->method mathes the name. It is also used as marker.*/ +#define VERIFY_RPC(name) (strcmp(vc->method, name) == 0) +#define CONFIG_KEY(name) key(name) /** * executes the expression and expects value to equal val. @@ -319,6 +324,11 @@ int64_t parse_float_val(const char* data, /**< the data string*/ int32_t expo /**< the exponent */ ); +/** + * simple add function, which adds the bytes (b) to a + */ +void b256_add(bytes32_t a, uint8_t* b, wlen_t len_b); + #ifdef THREADSAFE #define _NAME(x, y) x##y #if defined(_MSC_VER) || defined(__MINGW32__) diff --git a/c/src/init/in3_init.c b/c/src/init/in3_init.c index b0ae5338d..d694a5412 100644 --- a/c/src/init/in3_init.c +++ b/c/src/init/in3_init.c @@ -1,88 +1,18 @@ #include "in3_init.h" -#include "../api/eth1/eth_api.h" #include "../core/client/plugin.h" -#include "../pay/eth/pay_eth.h" -#include "../pay/zksync/zksync.h" -#include "../signer/pk-signer/signer.h" -#include "../third-party/zkcrypto/lib.h" -#ifdef USE_CURL -#include "../transport/curl/in3_curl.h" -#elif USE_WINHTTP -#include "../transport/winhttp/in3_winhttp.h" -#else -#include "../transport/http/in3_http.h" -#endif -#ifdef NODESELECT_DEF -#include "../nodeselect/full/nodeselect_def.h" -#endif -#include "../verifier/btc/btc.h" -#include "../verifier/eth1/basic/eth_basic.h" -#include "../verifier/eth1/full/eth_full.h" -#include "../verifier/eth1/nano/eth_nano.h" -#include "../verifier/ipfs/ipfs.h" -#ifdef SENTRY -#include "../tools/sentry/sentry.h" +#ifndef IN3_AUTOINIT_PATH +#define IN3_AUTOINIT_PATH "autoregister.h" #endif +#include IN3_AUTOINIT_PATH -static bool initialized; +void zkcrypto_initialize(); -static void init_verifier() { -#ifdef ETH_FULL - in3_register_default(in3_register_eth_full); -#endif -#ifdef ETH_BASIC - in3_register_default(in3_register_eth_basic); -#endif -#ifdef ETH_NANO - in3_register_default(in3_register_eth_nano); -#endif -#ifdef ETH_API - in3_register_default(in3_register_eth_api); -#endif -#ifdef IPFS - in3_register_default(in3_register_ipfs); -#endif -#ifdef BTC - in3_register_default(in3_register_btc); -#endif -#ifdef PAY_ETH - in3_register_default(in3_register_pay_eth); -#endif -#ifdef ZKSYNC - in3_register_default(in3_register_zksync); -#endif -#ifdef SENTRY - in3_register_default(in3_register_sentry); -#endif -#ifdef PK_SIGNER - in3_register_default(eth_register_pk_signer); -#endif -} - -static void init_transport() { -#ifdef TRANSPORTS -#ifdef USE_CURL - in3_register_default(in3_register_curl); -#elif USE_WINHTTP - in3_register_default(in3_register_winhttp); -#else - in3_register_default(in3_register_http); -#endif /* USE_CURL */ -#endif /* TRANSPORTS */ -} - -static void init_nodeselect() { -#ifdef NODESELECT_DEF - in3_register_default(in3_register_nodeselect_def); -#endif -} +static bool initialized; void in3_init() { if (!initialized) { initialized = true; - init_transport(); - init_verifier(); - init_nodeselect(); + auto_init(); #ifdef ZKSYNC zkcrypto_initialize(); #endif diff --git a/c/src/nodeselect/full/CMakeLists.txt b/c/src/nodeselect/full/CMakeLists.txt index 9c3fea64d..1a42b85fc 100644 --- a/c/src/nodeselect/full/CMakeLists.txt +++ b/c/src/nodeselect/full/CMakeLists.txt @@ -35,6 +35,7 @@ add_static_library( NAME nodeselect_def + REGISTER in3_register_nodeselect_def SOURCES nodeselect_def.c diff --git a/c/src/nodeselect/full/nodelist.c b/c/src/nodeselect/full/nodelist.c index 8147f1187..a7bcec6cd 100644 --- a/c/src/nodeselect/full/nodelist.c +++ b/c/src/nodeselect/full/nodelist.c @@ -214,7 +214,7 @@ NONULL static in3_ret_t in3_client_fill_chain_whitelist(in3_nodeselect_def_t* da NONULL static in3_ret_t update_nodelist(in3_t* c, in3_nodeselect_def_t* data, in3_req_t* parent_ctx) { // is there a useable required ctx? - in3_req_t* ctx = req_find_required(parent_ctx, "in3_nodeList"); + in3_req_t* ctx = req_find_required(parent_ctx, "in3_nodeList", NULL); if (ctx) { if (in3_req_state(ctx) == REQ_ERROR || (in3_req_state(ctx) == REQ_SUCCESS && !d_get(ctx->responses[0], K_RESULT))) { @@ -289,7 +289,7 @@ NONULL static in3_ret_t update_nodelist(in3_t* c, in3_nodeselect_def_t* data, in #ifdef NODESELECT_DEF_WL NONULL static in3_ret_t update_whitelist(in3_t* c, in3_nodeselect_def_t* data, in3_req_t* parent_ctx) { // is there a useable required ctx? - in3_req_t* ctx = req_find_required(parent_ctx, "in3_whiteList"); + in3_req_t* ctx = req_find_required(parent_ctx, "in3_whiteList", NULL); if (ctx) switch (in3_req_state(ctx)) { @@ -473,7 +473,7 @@ in3_ret_t in3_node_list_get(in3_req_t* ctx, in3_nodeselect_def_t* data, bool upd in3_ret_t res; // do we need to update the nodelist? - if (data->nodelist_upd8_params || update || req_find_required(ctx, "in3_nodeList")) { + if (data->nodelist_upd8_params || update || req_find_required(ctx, "in3_nodeList", NULL)) { // skip update if update has been postponed or there's already one in progress if (postpone_update(data) || update_in_progress(ctx)) goto SKIP_UPDATE; @@ -487,9 +487,9 @@ in3_ret_t in3_node_list_get(in3_req_t* ctx, in3_nodeselect_def_t* data, bool upd #ifdef NODESELECT_DEF_WL // do we need to update the whitelist? - if (data->whitelist // only if we have a whitelist - && (data->whitelist->needs_update || update || req_find_required(ctx, "in3_whiteList")) // which has the needs_update-flag (or forced) or we have already sent the request and are now picking up the result - && !memiszero(data->whitelist->contract, 20)) { // and we need to have a contract set, zero-contract = manual whitelist, which will not be updated. + if (data->whitelist // only if we have a whitelist + && (data->whitelist->needs_update || update || req_find_required(ctx, "in3_whiteList", NULL)) // which has the needs_update-flag (or forced) or we have already sent the request and are now picking up the result + && !memiszero(data->whitelist->contract, 20)) { // and we need to have a contract set, zero-contract = manual whitelist, which will not be updated. data->whitelist->needs_update = false; // now update the whiteList res = update_whitelist(ctx->client, data, ctx); diff --git a/c/src/nodeselect/full/nodeselect_def.c b/c/src/nodeselect/full/nodeselect_def.c index 971b15f94..31e3c4a46 100644 --- a/c/src/nodeselect/full/nodeselect_def.c +++ b/c/src/nodeselect/full/nodeselect_def.c @@ -59,7 +59,7 @@ static in3_ret_t rpc_verify(in3_nodeselect_def_t* data, in3_vctx_t* vc) { // do we have a result? if not it is a valid error-response if (!vc->result) return IN3_OK; - if (strcmp(vc->method, "in3_nodeList") == 0) { + if (VERIFY_RPC("in3_nodeList")) { d_token_t* params = d_get(vc->request, K_PARAMS); return eth_verify_in3_nodelist(data, vc, d_get_int_at(params, 0), d_get_bytes_at(params, 1), d_get_at(params, 2)); } @@ -166,7 +166,7 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx json_ctx_t* json = ctx->json; d_token_t* token = ctx->token; - if (token->key == key("preselect_nodes")) { + if (token->key == CONFIG_KEY("preselect_nodes")) { if (data->pre_address_filter) b_free(data->pre_address_filter); if (d_type(token) == T_BYTES && d_len(token) % 20 == 0) data->pre_address_filter = b_dup(d_bytes(token)); @@ -176,32 +176,37 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx EXPECT_CFG(d_type(token) == T_NULL, "invalid preselect_nodes "); } } - else if (token->key == key("replaceLatestBlock")) { + else if (token->key == CONFIG_KEY("replaceLatestBlock")) { EXPECT_TOK_U8(token); ctx->client->replace_latest_block = (uint8_t) d_int(token); const uint64_t dp_ = ctx->client->replace_latest_block; w->node_props = (w->node_props & 0xFFFFFFFF) | (dp_ << 32U); } - else if (token->key == key("requestCount")) { + else if (token->key == CONFIG_KEY("requestCount")) { EXPECT_TOK_U8(token); EXPECT_CFG(d_int(token), "requestCount must be at least 1"); w->request_count = (uint8_t) d_int(token); } - else if (token->key == key("minDeposit")) { + else if (token->key == CONFIG_KEY("minDeposit")) { EXPECT_TOK_U64(token); w->min_deposit = d_long(token); } - else if (token->key == key("nodeProps")) { + else if (token->key == CONFIG_KEY("nodeProps")) { EXPECT_TOK_U64(token); w->node_props = d_long(token); } - else if (token->key == key("nodeLimit")) { + else if (token->key == CONFIG_KEY("nodeLimit")) { EXPECT_TOK_U16(token); w->node_limit = (uint16_t) d_int(token); } - else if (token->key == key("nodeRegistry")) { + else if (token->key == CONFIG_KEY("nodeRegistry") || token->key == CONFIG_KEY("servers") || token->key == CONFIG_KEY("nodes")) { EXPECT_TOK_OBJ(token); + // this is legacy-support, if the object has a key with the chain_id, we simply use the value. + char node_id[10]; + sprintf(node_id, "0x%x", ctx->client->chain.chain_id); + if (d_get(token, key(node_id))) token = d_get(token, key(node_id)); + // this is changing the nodelist config, so we need to make sure we have our own nodelist TRY(nodelist_seperate_from_registry(&data, w)) @@ -219,7 +224,7 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx memcpy(data->registry_id, reg_id.data, 32); } #ifdef NODESELECT_DEF_WL - else if (cp.token->key == key("whiteListContract")) { + else if (cp.token->key == CONFIG_KEY("whiteListContract")) { EXPECT_TOK_ADDR(cp.token); EXPECT_CFG(!has_man_wl, "cannot specify manual whiteList and whiteListContract together!"); has_wlc = true; @@ -228,7 +233,7 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx data->whitelist->needs_update = true; memcpy(data->whitelist->contract, cp.token->data, 20); } - else if (cp.token->key == key("whiteList")) { + else if (cp.token->key == CONFIG_KEY("whiteList")) { EXPECT_TOK_ARR(cp.token); EXPECT_CFG(!has_wlc, "cannot specify manual whiteList and whiteListContract together!"); has_man_wl = true; @@ -250,7 +255,7 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx } } #endif - else if (cp.token->key == key("needsUpdate")) { + else if (cp.token->key == CONFIG_KEY("needsUpdate")) { EXPECT_TOK_BOOL(cp.token); if (!d_int(cp.token)) { if (data->nodelist_upd8_params) { @@ -261,20 +266,20 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx else if (!data->nodelist_upd8_params) data->nodelist_upd8_params = _calloc(1, sizeof(*(data->nodelist_upd8_params))); } - else if (cp.token->key == key("avgBlockTime")) { + else if (cp.token->key == CONFIG_KEY("avgBlockTime")) { EXPECT_TOK_U16(cp.token); data->avg_block_time = (uint16_t) d_int(cp.token); } - else if (cp.token->key == key("nodeList")) { + else if (cp.token->key == CONFIG_KEY("nodeList")) { EXPECT_TOK_ARR(cp.token); if (clear_nodes(data) < 0) goto cleanup; for (d_iterator_t n = d_iter(cp.token); n.left; d_iter_next(&n)) { - EXPECT_CFG(d_get(n.token, key("url")) && d_get(n.token, key("address")), "expected URL & address"); - EXPECT_TOK_STR(d_get(n.token, key("url"))); - EXPECT_TOK_ADDR(d_get(n.token, key("address"))); - EXPECT_CFG(add_node(data, d_get_string(n.token, key("url")), - d_get_longd(n.token, key("props"), 65535), - d_get_byteskl(n.token, key("address"), 20)->data) == IN3_OK, + EXPECT_CFG(d_get(n.token, CONFIG_KEY("url")) && d_get(n.token, CONFIG_KEY("address")), "expected URL & address"); + EXPECT_TOK_STR(d_get(n.token, CONFIG_KEY("url"))); + EXPECT_TOK_ADDR(d_get(n.token, CONFIG_KEY("address"))); + EXPECT_CFG(add_node(data, d_get_string(n.token, CONFIG_KEY("url")), + d_get_longd(n.token, CONFIG_KEY("props"), 65535), + d_get_byteskl(n.token, CONFIG_KEY("address"), 20)->data) == IN3_OK, "add node failed"); assert(data->nodelist_length > 0); BIT_SET(data->nodelist[data->nodelist_length - 1].attrs, ATTR_BOOT_NODE); @@ -288,21 +293,23 @@ static in3_ret_t config_set(in3_nodeselect_def_t* data, in3_configure_ctx_t* ctx if (!nodeselect_def_cfg_data(ctx->client->chain.chain_id).data) EXPECT_CFG(!memiszero(data->registry_id, 32) && !memiszero(data->contract, 20), "missing registryId/contract!"); } - else if (token->key == key("rpc")) { + else if (token->key == CONFIG_KEY("rpc")) { EXPECT_TOK_STR(token); TRY(nodelist_seperate_from_registry(&data, w)) - in3_t* c = ctx->client; - c->proof = PROOF_NONE; - c->chain.chain_id = CHAIN_ID_LOCAL; - w->request_count = 1; + in3_t* c = ctx->client; + c->proof = PROOF_NONE; + c->signature_count = 0; + c->chain.chain_id = CHAIN_ID_LOCAL; + w->request_count = 1; clear_nodes(data); - data->nodelist = _calloc(1, sizeof(in3_node_t)); - data->nodelist_length++; - in3_node_t* n = &data->nodelist[0]; - if (n->url) _free(n->url); - n->url = _strdupn(d_string(token), -1); _free(data->nodelist_upd8_params); + data->nodelist_length++; + data->nodelist = _calloc(1, sizeof(in3_node_t)); + data->weights = _calloc(1, sizeof(in3_node_weight_t)); + data->nodelist[0].props = NODE_PROP_DATA; + in3_node_t* n = &data->nodelist[0]; + n->url = _strdupn(d_string(token), -1); data->nodelist_upd8_params = NULL; } else { diff --git a/c/src/nodeselect/full/nodeselect_def.h b/c/src/nodeselect/full/nodeselect_def.h index 8326ed346..390f7455f 100644 --- a/c/src/nodeselect/full/nodeselect_def.h +++ b/c/src/nodeselect/full/nodeselect_def.h @@ -1,3 +1,7 @@ +/** + * + */ +// @PUBLIC_HEADER #ifndef IN3_NODE_SELECT_DEF_H #define IN3_NODE_SELECT_DEF_H diff --git a/c/src/nodeselect/full/registry.c b/c/src/nodeselect/full/registry.c index 57abd6ab8..b869aa81b 100644 --- a/c/src/nodeselect/full/registry.c +++ b/c/src/nodeselect/full/registry.c @@ -36,6 +36,7 @@ #include "../../core/client/keys.h" #include "../../core/client/request.h" #include "../../core/util/mem.h" +#include "../../core/util/utils.h" #include "../../verifier/eth1/nano/eth_nano.h" #include "../../verifier/eth1/nano/merkle.h" #include "../../verifier/eth1/nano/rlp.h" @@ -44,25 +45,6 @@ #define SERVER_STRUCT_SIZE 6 -static void big_add(bytes32_t a, uint8_t* b, wlen_t len_b) { - optimize_len(b, len_b); - uint8_t * pa = a + 31, *pb = b + len_b - 1; - uint_fast16_t carry = 0; - do { - carry += *pa + *pb; - *pa = carry & 0xFF; - carry >>= 8; - pb--, pa--; - } while (b == pb); - - while (carry && pa >= a) { - carry += *pa; - *pa = carry & 0xFF; - carry >>= 8; - pa--; - } -} - static in3_ret_t get_storage_value(d_token_t* storage_proofs, uint8_t* skey, bytes32_t value) { uint_fast16_t key_len = 32; optimize_len(skey, key_len); @@ -137,7 +119,7 @@ static uint8_t* get_storage_array_key(uint32_t pos, uint32_t array_index, uint32 uint8_t tmp[4]; int_to_bytes(array_index * struct_size + struct_pos, tmp); - big_add(dst, tmp, 4); + b256_add(dst, tmp, 4); return dst; } diff --git a/c/src/nodeselect/full/rpc.yml b/c/src/nodeselect/full/rpc.yml new file mode 100644 index 000000000..2c137d18a --- /dev/null +++ b/c/src/nodeselect/full/rpc.yml @@ -0,0 +1,523 @@ +nodelist: + + descr: special Incubed nodelist-handling functions. Most of those are only used internally. + + # config + config: + autoUpdateList: + type: bool + optional: true + descr: if true the nodelist will be automaticly updated if the lastBlock is newer. + example: false + default: true + + signatureCount: + descr: number of signatures requested in order to verify the blockhash. + type: int + optional: true + example: 2 + default: 1 + cmd: s + + bootWeights: + descr: if true, the first request (updating the nodelist) will also fetch the current health status and use it for blacklisting unhealthy nodes. This is used only if no nodelist is availabkle from cache. + type: bool + optional: true + example: true + default: true + cmd: bw + + useHttp: + descr: if true the client will try to use http instead of https. + type: bool + optional: true + example: true + default: false + + minDeposit: + descr: min stake of the server. Only nodes owning at least this amount will be chosen. + type: uint256 + optional: true + example: 10000000 + + nodeProps: + descr: used to identify the capabilities of the node. + type: hex + optional: true + example: '0xffff' + + requestCount: + descr: the number of request send in parallel when getting an answer. More request will make it more expensive, but increase the chances to get a faster answer, since the client will continue once the first verifiable response was received. + type: int + optional: true + example: 3 + default: 2 + cmd: + - rc + + rpc: + descr: url of one or more direct rpc-endpoints to use. (list can be comma seperated). If this is used, proof will automaticly be turned off. + type: string + optional: true + example: http://loalhost:8545 + + nodes: + descr: defining the nodelist. collection of JSON objects with chain Id (hex string) as key. + optional: true + example: + contract: '0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f' + nodeList: + - address: "0x45d45e6ff99e6c34a235d263965910298985fcfe" + url: "https://in3-v2.slock.it/mainnet/nd-1" + props: "0xFFFF" + type: + contract: + descr: address of the registry contract. (This is the data-contract!) + optional: true + type: address + + whiteListContract: + descr: address of the whiteList contract. This cannot be combined with whiteList! + type: address + optional: true + + whiteList: + descr: manual whitelist. + type: address + array: true + optional: true + + registryId: + descr: identifier of the registry. + type: bytes32 + optional: true + + needsUpdate: + descr: if set, the nodeList will be updated before next request. + type: bool + optional: true + + avgBlockTime: + descr: average block time (seconds) for this chain. + type: int + optional: true + + verifiedHashes: + descr: if the client sends an array of blockhashes the server will not deliver any signatures or blockheaders for these blocks, but only return a string with a number. This is automaticly updated by the cache, but can be overriden per request. + optional: true + array : true + type: + block: + descr: block number + type: uint64 + hash: + descr: verified hash corresponding to block number. + type: bytes32 + + nodeList: + descr: manual nodeList. As Value a array of Node-Definitions is expected. + optional: true + array: true + type: + url: + descr: URL of the node. + type: string + address: + descr: address of the node + type: string + props: + descr: used to identify the capabilities of the node (defaults to 0xFFFF). + type: hex + + # Verified RPCs + in3_nodeList: + apiName: nodes + descr: fetches and verifies the nodeList from a node + params: + limit: + descr: if the number is defined and >0 this method will return a partial nodeList limited to the given number. + type: int + optional: true + seed: + descr: this 32byte hex integer is used to calculate the indexes of the partial nodeList. It is expected to be a random value choosen by the client in order to make the result deterministic. + type: bytes32 + optional: true + addresses: + descr: a optional array of addresses of signers the nodeList must include. + array: true + type: address + optional: true + + result: + typeName: NodeListDefinition + descr: the current nodelist + type: + nodes: + typeName: Node + array: true + descr: a array of node definitions. + type: + url: + descr: the url of the node. Currently only http/https is supported, but in the future this may even support onion-routing or any other protocols. + type: string + address: + descr: the address of the signer + type: address + index: + descr: the index within the nodeList of the contract + type: uint64 + deposit: + descr: the stored deposit + type: uint256 + props: + descr: the bitset of capabilities as described in the [Node Structure](spec.html#node-structure) + type: hex + timeout: + descr: the time in seconds describing how long the deposit would be locked when trying to unregister a node. + type: uint64 + registerTime: + descr: unix timestamp in seconds when the node has registered. + type: uint64 + weight: + descr: the weight of a node ( not used yet ) describing the amount of request-points it can handle per second. + type: uint64 + proofHash: + descr: | + a hash value containing the above values. + This hash is explicitly stored in the contract, which enables the client to have only one merkle proof + per node instead of verifying each property as its own storage value. + The proof hash is build `keccak256( abi.encodePacked( deposit, timeout, registerTime, props, signer, url ))` + type: bytes32 + contract: + descr: the address of the Incubed-storage-contract. The client may use this information to verify that we are talking about the same contract or throw an exception otherwise. + type: address + registryId: + descr: the registryId (32 bytes) of the contract, which is there to verify the correct contract. + type: bytes32 + lastBlockNumber: + descr: the blockNumber of the last change of the list (usually the last event). + type: uint64 + totalServer: + descr: the total numbers of nodes. + type: uint64 + + proof: + descr: | + if proof is requested, the proof will have the type `accountProof`. In the proof-section only the storage-keys of the `proofHash` will be included. + The required storage keys are calcualted : + + - `0x00` - the length of the nodeList or total numbers of nodes. + - `0x01` - the registryId + - per node : ` 0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 + index * 5 + 4` + + The blockNumber of the proof must be the latest final block (`latest`- minBlockHeight) and always greater or equal to the `lastBlockNumber` + + #### Partial NodeLists + + if the client requests a partial nodeList and the given limit is smaller then the total amount of nodes, the server needs to pick nodes in a deterministic way. This is done by using the given seed. + + 1. add all required addresses (if any) to the list. + 2. iterate over the indexes until the limit is reached: + + ```ts + function createIndexes(total: number, limit: number, seed: Buffer): number[] { + const result: number[] = [] // the result as a list of indexes + let step = seed.readUIntBE(0, 6) // first 6 bytes define the step size + let pos = seed.readUIntBE(6, 6) % total // next 6 bytes define the offset + while (result.length < limit) { + if (result.indexOf(pos) >= 0) { // if the index is already part of the result + seed = keccak256(seed) // we create a new seed by hashing the seed. + step = seed.readUIntBE(0, 6) // and change the step-size + } + else + result.push(pos) + pos = (pos + step) % total // use the modulo operator to calculate the next position. + } + return result + } + ```` + type: + type: + descr: the proofType + type: "accountProof" + block: + descr: the serialized blockheader of the latest final block + type: bytes + signatures: + descr: a array of signatures from the signers (if requested) of the above block. + array: true + type: bytes + accounts: + descr: a Object with the addresses of the db-contract as key and Proof as value. The Data Structure of the Proof is exactly the same as the result of - [`eth_getProof`](https://eth.wiki/json-rpc/API#eth_getproof), but it must contain the above described keys. + key: accountAdr + type: + address: + descr: the address of the account + type: address + balance: + descr: current Balance + type: uint256 + codeHash: + descr: hash of the contract code + type: bytes32 + nonce: + descr: nonce of the account + type: uint256 + storageHash: + descr: MerkleRoot of the Storage Trie + type: bytes32 + accountProof: + descr: MerkleProof of this account-node + array: true + type: bytes + storageProof: + descr: Array of Proofs for all required storage values + type: + key: + descr: the storage key (or hash) + type: bytes32 + value: + descr: the storage value + type: bytes32 + proof: + descr: the merkleProof of the value down to the storageHash as MerkleRoot + array: true + type: bytes + example: + request: + - 2 + - "0xe9c15c3b26342e3287bb069e433de48ac3fa4ddd32a31b48e426d19d761d7e9b" + - [] + response: + totalServers: 5 + contract: "0x64abe24afbba64cae47e3dc3ced0fcab95e4edd5" + registryId: "0x423dd84f33a44f60e5d58090dcdcc1c047f57be895415822f211b8cd1fd692e3" + lastBlockNumber: 8669495 + nodes: + - url: "https://in3-v2.slock.it/mainnet/nd-3" + address: "0x945F75c0408C0026a3CD204d36f5e47745182fd4" + index: 2 + deposit: "10000000000000000" + props: 29 + timeout: 3600 + registerTime: 1570109570 + weight: 2000 + proofHash: "0x27ffb9b7dc2c5f800c13731e7c1e43fb438928dd5d69aaa8159c21fb13180a4c" + - url: "https://in3-v2.slock.it/mainnet/nd-5" + address: "0xbcdF4E3e90cc7288b578329efd7bcC90655148d2" + index: 4 + deposit: "10000000000000000" + props: 29 + timeout: 3600 + registerTime: 1570109690 + weight: 2000 + proofHash: "0xd0dbb6f1e28a8b90761b973e678cf8ecd6b5b3a9d61fb9797d187be011ee9ec7" + in3: + proof: + type: accountProof + block: "0xf9021ca01...." + accounts: + "0x64abe24afbba64cae47e3dc3ced0fcab95e4edd5": + address: "0x64abe24afbba64cae47e3dc3ced0fcab95e4edd5" + balance: "0xb1a2bc2ec50000" + codeHash: "0x18e64869905158477a607a68e9c0074d78f56a9dd5665a5254f456f89d5be398" + nonce: "0x1" + storageHash: "0x4386ec93bd665ea07d7ed488e8b495b362a31dc4100cf762b22f4346ee925d1f" + accountProof: + - "0xf90211a0e822..." + - "0xf90211a0f6d0..." + - "0xf90211a04d7b..." + - "0xf90211a0e749..." + - "0xf90211a059cb..." + - "0xf90211a0568f..." + - "0xf8d1a0ac2433..." + - "0xf86d9d33b981..." + storageProof: + - key: "0x0" + proof: + - "0xf90211a0ccb6d2d5786..." + - "0xf871808080808080800..." + - "0xe2a0200decd9548b62a...05" + value: "0x5" + - key: "0x1" + proof: + - "0xf90211a0ccb6d2d5786..." + - "0xf871808080808080800..." + - "0xf843a0200e2d5276120...423dd84f33a44f60e5d58090dcdcc1c047f57be895415822f211b8cd1fd692e3" + value: "0x423dd84f33a44f60e5d58090dcdcc1c047f57be895415822f211b8cd1fd692e3" + + in3_sign: + apiName: signBlockHash + descr: | + requests a signed blockhash from the node. + In most cases these requests will come from other nodes, because the client simply adds the addresses of the requested signers + and the processising nodes will then aquire the signatures with this method from the other nodes. + + Since each node has a risk of signing a wrong blockhash and getting convicted and losing its deposit, + per default nodes will and should not sign blockHash of the last `minBlockHeight` (default: 6) blocks! + params: + blocks: + descr: array of requested blocks. + type: + blockNumber: + descr: the blockNumber to sign + type: uint64 + hash: + descr: the expected hash. This is optional and can be used to check if the expected hash is correct, but as a client you should not rely on it, but only on the hash in the signature. + type: bytes32 + optional: true + + result: + descr: the Array with signatures of all the requires blocks. + type: + blockHash: + descr: the blockhash which was signed. + type: bytes32 + block: + descr: the blocknumber + type: uint64 + r: + descr: r-value of the signature + type: bytes32 + s: + descr: s-value of the signature + type: bytes32 + v: + descr: v-value of the signature + type: byte + msgHash: + descr: the msgHash signed. This Hash is created with `keccak256( abi.encodePacked( _blockhash, _blockNumber, registryId ))` + type: bytes32 + example: + request: + - blockNumber: 8770580 + response: + - blockHash: "0xd8189793f64567992eaadefc51834f3d787b03e9a6850b8b9b8003d8d84a76c8" + block: 8770580 + r: "0x954ed45416e97387a55b2231bff5dd72e822e4a5d60fa43bc9f9e49402019337" + s: "0x277163f586585092d146d0d6885095c35c02b360e4125730c52332cf6b99e596" + v: 28 + msgHash: "0x40c23a32947f40a2560fcb633ab7fa4f3a96e33653096b17ec613fbf41f946ef" + + in3_whitelist: + descr: Returns whitelisted in3-nodes addresses. The whitelist addressed are accquired from whitelist contract that user can specify in request params. + params: + address: + descr: address of whitelist contract + type: address + result: + descr: the whitelisted addresses + type: + nodes: + descr: array of whitelisted nodes addresses. + type: address + lastWhiteList: + descr: the blockNumber of the last change of the in3 white list event. + type: uint64 + contract: + descr: whitelist contract address. + type: address + lastBlockNumber: + descr: the blockNumber of the last change of the list (usually the last event). + type: uint64 + totalServer: + descr: the total numbers of whitelist nodes. + type: uint64 + + proof: + descr: | + if proof is requested, the proof will have the type `accountProof`. In the proof-section only the storage-keys of the addresses and the length (`0x0`) will be included. + + The blockNumber of the proof must be the latest final block (`latest`- minBlockHeight) and always greater or equal to the `lastBlockNumber` + + type: + type: + descr: the proofType + type: "accountProof" + block: + descr: the serialized blockheader of the latest final block + type: bytes + signatures: + descr: a array of signatures from the signers (if requested) of the above block. + type: bytes[] + accounts: + descr: a Object with the addresses of the db-contract as key and Proof as value. The Data Structure of the Proof is exactly the same as the result of - [`eth_getProof`](https://eth.wiki/json-rpc/API#eth_getproof), but it must contain the above described keys. + key: the account Adress + type: + address: + descr: the address of the account + type: address + balance: + descr: current Balance + type: uint256 + codeHash: + descr: hash of the contract code + type: bytes32 + nonce: + descr: nonce of the account + type: uint256 + storageHash: + descr: MerkleRoot of the Storage Trie + type: bytes32 + accountProof: + descr: MerkleProof of this account-node + array: true + type: bytes + storageProof: + descr: Array of Proofs for all required storage values + type: + key: + descr: the storage key (or hash) + type: bytes32 + value: + descr: the storage value + type: bytes32 + proof: + array: true + descr: the merkleProof of the value down to the storageHash as MerkleRoot + type: bytes + + example: + request: + - "0x08e97ef0a92EB502a1D7574913E2a6636BeC557b" + response: + totalServers: 2 + contract: "0x08e97ef0a92EB502a1D7574913E2a6636BeC557b" + lastBlockNumber: 1546354 + nodes: + - "0x1fe2e9bf29aa1938859af64c413361227d04059a" + - "0x45d45e6ff99e6c34a235d263965910298985fcfe" + in3: + proof: + type: accountProof + block: "0xf9021ca01...." + accounts: + "0x08e97ef0a92EB502a1D7574913E2a6636BeC557b": + address: "0x08e97ef0a92EB502a1D7574913E2a6636BeC557b" + balance: "0xb1a2bc2ec50000" + codeHash: "0x18e64869905158477a607a68e9c0074d78f56a9dd5665a5254f456f89d5be398" + nonce: "0x1" + storageHash: "0x4386ec93bd665ea07d7ed488e8b495b362a31dc4100cf762b22f4346ee925d1f" + accountProof: + - "0xf90211a0e822..." + - "0xf90211a0f6d0..." + - "0xf90211a04d7b..." + - "0xf90211a0e749..." + - "0xf90211a059cb..." + - "0xf90211a0568f..." + - "0xf8d1a0ac2433..." + - "0xf86d9d33b981..." + storageProof: + - key: "0x0" + proof: + - "0xf90211a0ccb6d2d5786..." + - "0xf871808080808080800..." + - "0xe2a0200decd9548b62a...05" + value: "0x5" + - key: "0x1" + proof: + - "0xf90211a0ccb6d2d5786..." + - "0xf871808080808080800..." + - "0xf843a0200e2d5276120...423dd84f33a44f60e5d58090dcdcc1c047f57be895415822f211b8cd1fd692e3" + value: "0x6aa7bbfbb1778efa33da1ba032cc3a79b9ef57b428441b4de4f1c38c3f258874" + diff --git a/c/src/pay/eth/pay_eth.h b/c/src/pay/eth/pay_eth.h index c3cab063a..68db6ec54 100644 --- a/c/src/pay/eth/pay_eth.h +++ b/c/src/pay/eth/pay_eth.h @@ -32,7 +32,6 @@ * with this program. If not, see . *******************************************************************************/ -// @PUBLIC_HEADER /** @file * USN API. * diff --git a/c/src/pay/zksync/CMakeLists.txt b/c/src/pay/zksync/CMakeLists.txt index 95eb4efee..414ef88d4 100644 --- a/c/src/pay/zksync/CMakeLists.txt +++ b/c/src/pay/zksync/CMakeLists.txt @@ -35,7 +35,8 @@ add_static_library( NAME zksync - + REGISTER in3_register_zksync + SOURCES zksync.c zk_message.c @@ -45,6 +46,7 @@ add_static_library( zk_deposit.c zk_musig.c zk_incentive.c + zk_rest.c DEPENDS eth_basic diff --git a/c/src/pay/zksync/rpc.yml b/c/src/pay/zksync/rpc.yml new file mode 100644 index 000000000..5df6ed25f --- /dev/null +++ b/c/src/pay/zksync/rpc.yml @@ -0,0 +1,1100 @@ +types: + zk_receipt: + type: + descr: the Transaction-Type (`Withdraw` or `Transfer`) + type: string + accountId: + descr: the id of the sender account + type: uint64 + from: + descr: the address of the sender + type: address + to: + descr: the address of the receipient + type: address + token: + descr: the id of the token used + type: uint64 + amount: + descr: the amount sent + type: uint256 + fee: + descr: the fees paid + type: uint256 + nonce: + descr: the fees paid + type: uint64 + txHash: + descr: the transactionHash, which can be used to track the tx + type: string + + zk_tx: + accountId: + descr: the id of the sender account + type: uint64 + from: + descr: the address of the sender + type: address + to: + descr: the address of the receipient + type: address + token: + descr: the id of the token used + type: uint64 + amount: + descr: the amount sent + type: uint256 + fee: + descr: the fees paid + type: uint256 + nonce: + descr: the fees paid + type: uint64 + validFrom: + descr: timestamp set by the sender when the valid range starts + type: uint64 + validUntil: + descr: timestamp set by the sender when the valid range ends + type: uint64 + signature: + descr: the sync signature + type: + pubKey: + descr: the public key of the signer + type: bytes32 + signature: + descr: the signature + type: bytes + + zk_history: + tx_id: + descr: the transaction id based on the block-number and the index + type: string + hash: + descr: the transaction hash + type: string + eth_block: + descr: the blockNumber of a priority-operation like `Deposit` otherwise this is null + optional: true + type: uint64 + pq_id: + descr: the priority-operation id (for tx like `Deposit`) otherwise this is null + optional: true + type: uint64 + success: + descr: the result of the operation + type: bool + optional: true + fail_reason: + descr: the error message if failed, otherwise null + type: string + optional: true + commited: + descr: true if the tx was received and verified by the zksync-server + type: bool + verified: + descr: true if the tx was received and verified by the zksync-server + type: bool + created_at: + descr: UTC-Time when the transaction was created + type: string + tx: + descr: the transaction data + type: + type: + descr: Type of the transaction. `Transfer`, `ChangePubKey` or `Withdraw` + type: string + from: + descr: The sender of the address + optional: true + type: address + to: + descr: The recipient of the address + optional: true + type: address + token: + descr: The token id + type: string + optional: true + amount: + descr: the amount sent + type: uint256 + optional: true + account: + descr: the account sent from + type: address + optional: true + accountId: + descr: the account id used + type: uint64 + optional: true + newPkHash: + descr: the new public Key Hash (only used if the type is CHangePubKey) + type: string + optional: true + validFrom: + descr: timestamp set by the sender when the valid range starts + type: uint64 + optional: true + validUntil: + descr: timestamp set by the sender when the valid range ends + type: uint64 + optional: true + signature: + optional: true + descr: the sync signature + type: + pubKey: + descr: the public key of the signer + type: bytes32 + signature: + descr: the signature + type: bytes + fee: + optional: true + descr: the fee payed + type: uint256 + feeToken: + optional: true + descr: the token the fee was payed + type: uint64 + nonce: + optional: true + descr: the nonce of the account + type: uint64 + priority_op: + descr: the description of a priority operation like `Deposit` + optional: true + type: + from: + descr: The sender of the address + type: address + to: + descr: The recipient of the address + type: address + token: + descr: The token id + type: string + amount: + descr: the amount sent + type: uint256 + ethAuthData: + descr: the 2fa euth authorition + optional: true + type: + type: + descr: the type which should be CREATE2, ECDSA + type: string + saltArg: + descr: the hash component (only if type=CREATE2) + type: bytes32 + optional: true + codeHash: + descr: the hash of the deployment-data (only if type=CREATE2) + type: bytes32 + optional: true + creatorAddress: + descr: the address of the the deploying contract (only if type=CREATE2) + type: address + optional: true + +zksync: + descr: | + *Important: This feature is still experimental and not considered stable yet. In order to use it, you need to set the experimental-flag (-x on the comandline or `"experimental":true`!* + + the zksync-plugin is able to handle operations to use [zksync](https://zksync.io/) like deposit transfer or withdraw. Also see the #in3-config on how to configure the zksync-server or account. + + Also in order to sign messages you need to set a signer! + + All zksync-methods can be used with `zksync_` or `zk_` prefix. + + # config + config: + + zksync: + descr: configuration for zksync-api ( only available if build with `-DZKSYNC=true`, which is on per default). + example: + - account: '0x995628aa92d6a016da55e7de8b1727e1eb97d337' + sync_key: '0x9ad89ac0643ffdc32b2dab859ad0f9f7e4057ec23c2b17699c9b27eff331d816' + signer_type: contract + - account: '0x995628aa92d6a016da55e7de8b1727e1eb97d337' + sync_key: '0x9ad89ac0643ffdc32b2dab859ad0f9f7e4057ec23c2b17699c9b27eff331d816' + signer_type: create2 + create2: + creator: '0x6487c3ae644703c1f07527c18fe5569592654bcb' + saltarg: '0xb90306e2391fefe48aa89a8e91acbca502a94b2d734acc3335bb2ff5c266eb12' + codehash: '0xd6af3ee91c96e29ddab0d4cb9b5dd3025caf84baad13bef7f2b87038d38251e5' + - account: '0x995628aa92d6a016da55e7de8b1727e1eb97d337' + signer_type: pk + musig_pub_keys: '0x9ad89ac0643ffdc32b2dab859ad0f9f7e4057ec23c2b17699c9b27eff331d8160x9ad89ac0643ffdc32b2dab859ad0f9f7e4057ec23c2b17699c9b27eff331d816' + sync_key: '0xe8f2ee64be83c0ab9466b0490e4888dbf5a070fd1d82b567e33ebc90457a5734' + musig_urls: + - null + - 'https://approver.service.com' + + type: + provider_url: + descr: url of the zksync-server (if not defined it will be choosen depending on the chain) + type: string + optional: true + default: https://api.zksync.io/jsrpc + cmd: zks + + rest_api: + descr: url of the zksync rest api (if not defined it will be choosen depending on the chain) + type: string + optional: true + example: https://rinkeby-api.zksync.io/api/v0.1/ + cmd: zkr + + account: + descr: the account to be used. if not specified, the first signer will be used. + type: address + optional: true + cmd: zka + + sync_key: + descr: the seed used to generate the sync_key. This way you can explicitly set the pk instead of derriving it from a signer. + type: bytes32 + optional: true + cmd: zsk + + main_contract: + descr: address of the main contract- If not specified it will be taken from the server. + type: address + optional: true + + signer_type: + descr: type of the account. Must be either `pk`(default), `contract` (using contract signatures) or `create2` using the create2-section. + type: string + enum: + pk: Private matching the account is used ( for EOA) + contract: Contract Signature based EIP 1271 + create2: create2 optionas are used + + optional: true + default: pk + cmd: zkat + + musig_pub_keys: + descr: concatenated packed public keys (32byte) of the musig signers. if set the pubkey and pubkeyhash will based on the aggregated pubkey. Also the signing will use multiple keys. + type: bytes + optional: true + cmd: zms + + musig_urls: + descr: a array of strings with urls based on the `musig_pub_keys`. It is used so generate the combined signature by exchaing signature data (commitment and signatureshares) if the local client does not hold this key. + type: string + array: true + optional: true + cmd: zmu + + create2: + descr: create2-arguments for sign_type `create2`. This will allow to sign for contracts which are not deployed yet. + cmd: zc2 + optional: true + type: + creator: + descr: The address of contract or EOA deploying the contract ( for example the GnosisSafeFactory ) + type: address + saltarg: + descr: a salt-argument, which will be added to the pubkeyhash and create the create2-salt. + type: bytes32 + codehash: + descr: the hash of the actual deploy-tx including the constructor-arguments. + type: bytes32 + + verify_proof_method: + descr: rpc-method, which will be used to verify the incomming proof before cosigning. + type: string + cmd: zvpm + optional: true + + create_proof_method: + descr: rpc-method, which will be used to create the proof needed for cosigning. + type: string + cmd: zcpm + optional: true + + # rpc + zksync_contract_address: + descr: returns the contract address + result: + descr: fetches the contract addresses from the zksync server. This request also caches them and will return the results from cahe if available. + type: + govContract: + descr: the address of the govement contract + type: address + mainContract: + descr: the address of the main contract + type: address + example: + cmdParams: -x + response: + govContract: "0x34460C0EB5074C29A9F6FE13b8e7E23A0D08aF01" + mainContract: "0xaBEA9132b05A70803a4E85094fD0e1800777fBEF" + + + zksync_tokens: + descr: returns the list of all available tokens + result: + key: token symbol + descr: a array of tokens-definitions. This request also caches them and will return the results from cahe if available. + type: + address: + descr: the address of the ERC2-Contract or 0x00000..000 in case of the native token (eth) + type: address + decimals: + descr: decimals to be used when formating it for human readable representation. + type: int + id: + descr: id which will be used when encoding the token. + type: uint64 + symbol: + descr: symbol for the token + type: string + example: + cmdParams: -x + response: + BAT: + address: '0x0d8775f648430679a709e98d2b0cb6250d2887ef' + decimals: 18 + id: 8 + symbol: BAT + BUSD: + address: '0x4fabb145d64652a948d72533023f6e7a623c7c53' + decimals: 18 + id: 6 + symbol: BUSD + DAI: + address: '0x6b175474e89094c44da98b954eedeac495271d0f' + decimals: 18 + id: 1 + symbol: DAI + ETH: + address: '0x0000000000000000000000000000000000000000' + decimals: 18 + id: 0 + symbol: ETH + + zksync_account_info: + descr: returns account_info from the server + params: + address: + descr: the account-address. if not specified, the client will try to use its own address based on the signer config. + type: address + optional: true + result: + descr: the current state of the requested account. + type: + address: + descr: the address of the account + type: address + committed: + descr: the state of the zksync operator after executing transactions successfully, but not not verified on L1 yet. + type: + balances: + descr: the token-balance + key: the token + type: uint256 + nonce: + descr: the nonce or transaction count. + type: uint64 + pubKeyHash: + descr: the pubKeyHash set for the requested account or `0x0000...` if not set yet. + type: address + depositing: + descr: the state of all depositing-tx. + type: + balances: + descr: the token-values. + key: the token + type: uint256 + id: + descr: the assigned id of the account, which will be used when encoding it into the rollup. + type: uint64 + optional: true + verified: + descr: the state after the rollup was verified in L1. + type: + balances: + descr: the token-balances. + key: the token + type: uint256 + nonce: + descr: the nonce or transaction count. + type: uint64 + pubKeyHash: + descr: the pubKeyHash set for the requested account or `0x0000...` if not set yet. + type: address + example: + cmdParams: -x -pk 0xe41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000 + response: + address: '0x3b2a1bd631d9d7b17e87429a8e78dbbd9b4de292' + committed: + balances: {} + nonce: 0 + pubKeyHash: sync:0000000000000000000000000000000000000000 + depositing: + balances: {} + id: + verified: + balances: {} + nonce: 0 + pubKeyHash: sync:0000000000000000000000000000000000000000 + + + zksync_tx_info: + descr: returns the state or receipt of the the zksync-tx + params: + tx: + descr: the txHash of the send tx + type: bytes32 + result: + descr: the current state of the requested tx. + type: + block: + descr: the blockNumber containing the tx or `null` if still pending + type: uint64 + optional: true + executed: + descr: true, if the tx has been executed by the operator. If false it is still in the txpool of the operator. + type: bool + success: + descr: if executed, this property marks the success of the tx. + type: bool + optional: true + failReason: + descr: if executed and failed this will include an error message + type: string + optional: true + example: + cmdParams: -x + request: + - "sync-tx:e41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000" + response: + block: null + executed: false + failReason: null + success: null + + + zksync_tx_data: + descr: returns the full input data of a transaction. In order to use this, the `rest_api` needs to be set in the config. + params: + tx: + descr: the txHash of the send tx + type: bytes32 + result: + descr: the data and state of the requested tx. + type: + block_number: + descr: the blockNumber containing the tx or `null` if still pending + type: uint64 + optional: true + failReason: + descr: if executed and failed this will include an error message + type: string + optional: true + tx_type: + descr: Type of the transaction. `Transfer`, `ChangePubKey` or `Withdraw` + type: string + from: + descr: The sender of the address + type: address + to: + descr: The recipient of the address + type: address + token: + descr: The token id + type: uint64 + amount: + descr: the amount sent + type: uint256 + fee: + descr: the fee payed + type: uint256 + nonce: + descr: the nonce of the account + type: uint64 + created_at: + descr: the timestamp as UTC + type: string + tx: + descr: the tx input data + type: zk_tx + + example: + cmdParams: -x -zkr https://rinkeby-api.zksync.io/api/v0.1 + request: + - "0xc06ddc1c0914e8f9ca4d5bc98f609f7d758f6de2733fdcb8e3ec" + response: + tx_type: Transfer + from: '0x627d8e8c1a663cfea17432ec6dbbd3cc2c8a1f9a' + to: '0x03e2c10b74a260f46ab5cf881938c5888a6142df' + token: 1 + amount: '5000000' + fee: '2190' + block_number: 29588 + nonce: 20 + created_at: '2021-06-01T10:32:16.248564' + fail_reason: + tx: + to: '0x03e2c10b74a260f46ab5cf881938c5888a6142df' + fee: '2190' + from: '0x627d8e8c1a663cfea17432ec6dbbd3cc2c8a1f9a' + type: Transfer + nonce: 20 + token: 1 + amount: '5000000' + accountId: 161578 + signature: + pubKey: 91b533af2c430d7ad48db3ccc4ccb54befaff48307180c9a19a369099331d0a6 + signature: d17637db375a7a587474c8fee519fd7520f6ef98e1370e7a13d5de8176a6d0a22309e24a19dae50dad94ac9634ab3398427cf67abe8408e6c965c6b350b80c02 + validFrom: 0 + validUntil: 4294967295 + + + + zksync_account_history: + descr: returns the history of transaction for a given account. + params: + account: + descr: the address of the account + type: address + ref: + descr: the reference or start. this could be a tx_id prefixed with `<` or `>`for newer or older than the specified tx or `pending` returning all pending tx. + type: string + optional: true + limit: + descr: the max number of entries to return + type: int + optional: true + result: + array: true + descr: the data and state of the requested tx. + type: zk_history + + example: + cmdParams: -x -zkr https://rinkeby-api.zksync.io/api/v0.1 + request: + - "0x9df215737e137acddd0ad99e32f9a6b980ea526d" + response: + - tx_id: '29411,1' + hash: sync-tx:e83b1b982b4d8a08a21f87717e85a268e3b3a5305bdf5efc465e7fd8f0ad5335 + eth_block: + pq_id: + tx: + to: '0xb7b2af693a2362c5c7575841ca6eb72ad2aed77f' + fee: '11060000000000' + from: '0x9df215737e137acddd0ad99e32f9a6b980ea526d' + type: Transfer + nonce: 1 + token: ETH + amount: '1000000000000000' + accountId: 161418 + signature: + pubKey: 74835ee6dd9009b67fd4e4aef4a6f63ee2a597ced5e59f33b019905d1df70d91 + signature: 407314ebce8ce0217b41a6cf992c7359645215c35afbdf7e18e76c957a14ed20135b7e8e5ca24fb132640141c0b3168b3939571e2363e41639e18b1637f26d02 + validFrom: 0 + validUntil: 4294967295 + success: true + fail_reason: + commited: true + verified: true + created_at: '2021-05-31T11:54:56.248569Z' + - tx_id: '29376,10' + hash: sync-tx:5f92999f7bbc5d84fe0d34ebe8b7a0c38f977caece844686d3007bc48e5944e0 + eth_block: + pq_id: + tx: + to: '0xc98fc74a085cd7ecd91d9e8d860a18ef6769d873' + fee: '10450000000000' + from: '0xb7b2af693a2362c5c7575841ca6eb72ad2aed77f' + type: Transfer + nonce: 1 + token: ETH + amount: '10000000000000000' + accountId: 161391 + signature: + pubKey: 06cce677912252a9eb87090b795e5bd84a079cb398dfec7f6a6645ee456dc721 + signature: ef83b1519a737107798aa5740998a515c406510b61f176fbbac6f703231968a563551f74f37bf96c2220fd18a68aca128a155b5083333a13cfbbd348c0a75003 + validFrom: 0 + validUntil: 4294967295 + success: true + fail_reason: + commited: true + verified: true + created_at: '2021-05-31T08:11:17.250144Z' + - tx_id: '29376,5' + hash: sync-tx:78550bbcaefdfd4cc4275bd1a0168dd73efb1953bb17a9689381fea6729c924e + eth_block: + pq_id: + tx: + fee: '37500000000000' + type: ChangePubKey + nonce: 0 + account: '0xb7b2af693a2362c5c7575841ca6eb72ad2aed77f' + feeToken: 0 + accountId: 161391 + newPkHash: sync:1ae5a093f285ddd23b54bea2780ef4e9a4e348ea + signature: + pubKey: 06cce677912252a9eb87090b795e5bd84a079cb398dfec7f6a6645ee456dc721 + signature: 27f42a850de4dcc6527fea0a9baa5991dabf3c2ce30dae5a6112f03cf614da03bdc2ef7ac107337d17f9e4047e5b18b3e4c46acb6af41f8cfbb2fce43247d500 + validFrom: 0 + validUntil: 4294967295 + ethAuthData: + type: CREATE2 + saltArg: '0xd32a7ec6157d2433c9ae7f4fdc35dfac9bba6f92831d1ca20b09d04d039d8dd7' + codeHash: '0x96657bf6bdcbffce06518530907d2d729e4659ad3bc7b5cc1f5c5567d964272c' + creatorAddress: '0xaa8c54c65c14f132804f0809bdbef19970673709' + ethSignature: + success: true + fail_reason: + commited: true + verified: true + created_at: '2021-05-31T08:09:11.249472Z' + - tx_id: '29376,0' + hash: '0xc63566212c1569a0e64b255a07320483ed8476cd36b54aa37d3bd6f93b70f7f8' + eth_block: 8680840 + pq_id: 57181 + tx: + type: Deposit + account_id: 161391 + priority_op: + to: '0xb7b2af693a2362c5c7575841ca6eb72ad2aed77f' + from: '0x9d646b325787c6d7d612eb37915ca3023eea4dac' + token: ETH + amount: '500000000000000000' + success: true + fail_reason: + commited: true + verified: true + created_at: '2021-05-31T08:07:31.237817Z' + + + + zksync_set_key: + descr: | + sets the signerkey based on the current pk or as configured in the config. + You can specify the key by either + - setting a signer ( the sync key will be derrived through a signature ) + - setting the seed directly ( `sync_key` in the config) + - setting the `musig_pub_keys` to generate the pubKeyHash based on them + - setting the `create2` options and the sync-key will generate the account based on the pubKeyHash + + + we support 3 different signer types (`signer_type` in the `zksync` config) : + + 1. `pk` - Simple Private Key + If a signer is set (for example by setting the pk), incubed will derrive the sync-key through a signature and use it + 2. `contract` - Contract Signature + In this case a preAuth-tx will be send on L1 using the signer. If this contract is a mutisig, you should make sure, you have set the account explicitly in the config and also activate the multisig-plugin, so the transaction will be send through the multisig. + 3. `create2` - Create2 based Contract + + params: + token: + descr: the token to pay the gas (either the symbol or the address) + type: string + result: + descr: the pubKeyHash, if it was executed successfully + type: address + example: + cmdParams: -x -pk 0xe41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000 + request: + - eth + response: "sync:e41d2489571d322189246dafa5ebde1f4699f498" + + zksync_pubkeyhash: + sync: true + descr: returns the current PubKeyHash based on the configuration set. + params: + pubKey: + descr: the packed public key to hash ( if given the hash is build based on the given hash, otherwise the hash is based on the config) + type: bytes32 + optional: true + result: + descr: the pubKeyHash + type: address + example: + cmdParams: -x -pk 0xe41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000 + response: "sync:4dcd9bb4463121470c7232efb9ff23ec21398e58" + + + zksync_pubkey: + sync: true + descr: | + returns the current packed PubKey based on the config set. + + If the config contains public keys for musig-signatures, the keys will be aggregated, otherwise the pubkey will be derrived from the signing key set. + result: + descr: the pubKey + type: bytes32 + example: + cmdParams: -x -pk 0xe41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000 + response: "0xfca80a469dbb53f8002eb1e2569d66f156f0df24d71bd589432cc7bc647bfc04" + + + + zksync_account_address: + sync: true + descr: returns the address of the account used. + result: + descr: the account used. + type: address + example: + cmdParams: -x -pk 0xe41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000 + response: "0x3b2a1bd631d9d7b17e87429a8e78dbbd9b4de292" + + + + + zksync_sign: + descr: | + returns the schnorr musig signature based on the current config. + + This also supports signing with multiple keys. In this case the configuration needs to sets the urls of the other keys, so the client can then excange all data needed in order to create the combined signature. + when exchanging the data with other keys, all known data will be send using `zk_sign` as method, but instead of the raw message a object with those data will be passed. + + params: + message: + descr: the message to sign + type: bytes + result: + descr: | + The return value are 96 bytes of signature: + - `[0...32]` packed public key + - `[32..64]` r-value + - `[64..96]` s-value + type: bytes96 + example: + cmdParams: -x -pk 0xe41d2489571d322189246dafa5ebde1f4699f498000000000000000000000000 + request: + - '0xaabbccddeeff' + response: "0xfca80a469dbb53f8002eb1e2569d66f156f0df24d71bd589432cc7bc647bfc0493f69034c3980e7352741afa6c171b8e18355e41ed7427f6e706f8432e32e920c3e61e6c3aa00cfe0c202c29a31b69cd0910a432156a0977c3a5baa404547e01" + + zksync_verify: + sync: true + descr: | + returns 0 or 1 depending on the successfull verification of the signature. + + if the `musig_pubkeys` are set it will also verify against the given public keys list. + + params: + message: + descr: the message which was supposed to be signed + type: bytes + signature: + descr: the signature (96 bytes) + type: bytes96 + result: + descr: 1 if the signature(which contains the pubkey as the first 32bytes) matches the message. + type: int + example: + cmdParams: -x + request: + - '0xaabbccddeeff' + - '0xfca80a469dbb53f8002eb1e2569d66f156f0df24d71bd589432cc7bc647bfc0493f69034c3980e7352741afa6c171b8e18355e41ed7427f6e706f8432e32e920c3e61e6c3aa00cfe0c202c29a31b69cd0910a432156a0977c3a5baa404547e01' + response: 1 + + zksync_ethop_info: + descr: returns the state or receipt of the the PriorityOperation + params: + opId: + descr: the opId of a layer-operstion (like depositing) + type: uint64 + result: + descr: state of the PriorityOperation + type: + block: + descr: the block + type: + committed: + descr: state of the operation + type: bool + verified: + descr: if the opteration id has been included in the rollup block + type: bool + blockNumber: + descr: the blocknumber of the block that included the operation + type: uint64 + optional: true + optional: true + executed: + descr: if the operation was executed + type: bool + example: + cmdParams: -x + request: + - 1 + response: + block: + committed: true + blockNumber: 4 + verified: true + executed: true + + zksync_get_token_price: + descr: returns current token-price + params: + token: + descr: Symbol or address of the token + type: string + result: + descr: the token price + type: float + example: + cmdParams: -x + request: + - WBTC + response: 11320.002167 + + zksync_get_tx_fee: + descr: calculates the fees for a transaction. + params: + txType: + descr: The Type of the transaction "Withdraw" or "Transfer" + type: string + address: + descr: the address of the receipient + type: address + token: + descr: the symbol or address of the token to pay + type: string + result: + descr: the fees split up into single values + type: + feeType: + descr: Type of the transaaction + type: string + gasFee: + descr: the gas for the core-transaction + type: uint64 + gasPriceWei: + descr: current gasPrice + type: uint64 + gasTxAmount: + descr: gasTxAmount + type: uint64 + totalFee: + descr: total of all fees needed to pay in order to execute the transaction + type: uint64 + zkpFee: + descr: zkpFee + type: uint64 + example: + cmdParams: -x + request: + - Transfer + - '0xabea9132b05a70803a4e85094fd0e1800777fbef' + - BAT + response: + feeType: "TransferToNew" + gasFee: "47684047990828528" + gasPriceWei: "116000000000" + gasTxAmount: "350" + totalFee: "66000000000000000" + zkpFee: "18378682992117666" + + zksync_sync_key: + descr: returns private key used for signing zksync-transactions + result: + descr: the raw private key configured based on the signers seed + example: + cmdParams: -x -pk 0xb0f60e4783ccc1f6234deed9e21f16d460c4176fd7adbd4f31d17e283b8cfb1c + response: '0x019125314fda133d5bf62cb454ee8c60927d55b68eae8b8b8bd13db814389cd6' + + zksync_deposit: + descr: sends a deposit-transaction and returns the opId, which can be used to tradck progress. + params: + amount: + descr: the value to deposit in wei (or smallest token unit) + type: uint256 + token: + descr: the token as symbol or address + type: string + approveDepositAmountForERC20: + descr: if true and in case of an erc20-token, the client will send a approve transaction first, otherwise it is expected to be already approved. + type: bool + optional: true + account: + descr: address of the account to send the tx from. if not specified, the first available signer will be used. + type: address + optional: true + result: + descr: the receipt and the receipopId. You can use `zksync_ethop_info` to follow the state-changes. + type: + receipt: + descr: the transactionreceipt + type: transactionReceipt + priorityOpId: + descr: the operationId to rack to progress + type: uint64 + + example: + cmdParams: -x -pk 0xb0f60e4783ccc1f6234deed9e21f16d460c4176fd7adbd4f31d17e283b8cfb1c + request: + - 1000 + - WBTC + response: + receipt: + blockHash: '0xea6ee1e20d3408ad7f6981cfcc2625d80b4f4735a75ca5b20baeb328e41f0304' + blockNumber: '0x8c1e39' + contractAddress: + cumulativeGasUsed: '0x2466d' + gasUsed: '0x2466d' + logs: + - address: '0x85ec283a3ed4b66df4da23656d4bf8a507383bca' + blockHash: '0xea6ee1e20d3408ad7f6981cfcc2625d80b4f4735a75ca5b20baeb328e41f0304' + blockNumber: '0x8c1e39' + data: 0x00000000000... + logIndex: '0x0' + removed: false + topics: + - '0x9123e6a7c5d144bd06140643c88de8e01adcbb24350190c02218a4435c7041f8' + - '0xa2f7689fc12ea917d9029117d32b9fdef2a53462c853462ca86b71b97dd84af6' + - '0x55a6ef49ec5dcf6cd006d21f151f390692eedd839c813a150000000000000000' + transactionHash: '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + transactionIndex: '0x0' + transactionLogIndex: '0x0' + type: mined + logsBloom: 0x00000000000000000000200000... + root: + status: '0x1' + transactionHash: '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + transactionIndex: '0x0' + priorityOpId: 74 + + zksync_transfer: + descr: sends a zksync-transaction and returns data including the transactionHash. + params: + to: + descr: the receipient of the tokens + type: address + amount: + descr: the value to transfer in wei (or smallest token unit) + type: uint256 + token: + descr: the token as symbol or address + type: string + account: + descr: address of the account to send the tx from. if not specified, the first available signer will be used. + type: address + optional: true + result: + descr: the transactionReceipt. use `zksync_tx_info` to check the progress. + type: zk_receipt + example: + cmdParams: -x -pk 0xb0f60e4783ccc1f6234deed9e21f16d460c4176fd7adbd4f31d17e283b8cfb1c + request: + - 0xabea9132b05a70803a4e85094fd0e1800777fbef + - 100 + - WBTC + response: + type: Transfer + accountId: 1 + from: '0x8a91dc2d28b689474298d91899f0c1baf62cb85b' + to: '0x8a91dc2d28b689474298d91899f0c1baf62cb85b' + token: 0 + amount: 10 + fee: 3780000000000000 + nonce: 4 + txHash: 'sync-tx:40008d91ab92f7c539e45b06e708e186a4b906ad10c4b7a29f855fe02e7e7668' + + + zksync_withdraw: + descr: withdraws the amount to the given `ethAddress` for the given token. + params: + ethAddress: + descr: the receipient of the tokens in L1 + type: address + amount: + descr: the value to transfer in wei (or smallest token unit) + type: uint256 + token: + descr: the token as symbol or address + type: string + account: + descr: address of the account to send the tx from. if not specified, the first available signer will be used. + type: address + optional: true + result: + descr: the transactionReceipt. use `zksync_tx_info` to check the progress. + type: zk_receipt + example: + cmdParams: -x -pk 0xb0f60e4783ccc1f6234deed9e21f16d460c4176fd7adbd4f31d17e283b8cfb1c + request: + - 0xabea9132b05a70803a4e85094fd0e1800777fbef + - 100 + - WBTC + response: + type: Transfer + accountId: 1 + from: '0x8a91dc2d28b689474298d91899f0c1baf62cb85b' + to: '0x8a91dc2d28b689474298d91899f0c1baf62cb85b' + token: 0 + amount: 10 + fee: 3780000000000000 + nonce: 4 + txHash: 'sync-tx:40008d91ab92f7c539e45b06e708e186a4b906ad10c4b7a29f855fe02e7e7668' + + + zksync_emergency_withdraw: + descr: withdraws all tokens for the specified token as a onchain-transaction. This is useful in case the zksync-server is offline or tries to be malicious. + params: + token: + descr: the token as symbol or address + type: string + result: + descr: the transactionReceipt + type: transactionReceipt + example: + cmdParams: -x -pk 0xb0f60e4783ccc1f6234deed9e21f16d460c4176fd7adbd4f31d17e283b8cfb1c + request: + - WBTC + response: + blockHash: '0xea6ee1e20d3408ad7f6981cfcc2625d80b4f4735a75ca5b20baeb328e41f0304' + blockNumber: '0x8c1e39' + contractAddress: + cumulativeGasUsed: '0x2466d' + gasUsed: '0x2466d' + logs: + - address: '0x85ec283a3ed4b66df4da23656d4bf8a507383bca' + blockHash: '0xea6ee1e20d3408ad7f6981cfcc2625d80b4f4735a75ca5b20baeb328e41f0304' + blockNumber: '0x8c1e39' + data: 0x00000000000... + logIndex: '0x0' + removed: false + topics: + - '0x9123e6a7c5d144bd06140643c88de8e01adcbb24350190c02218a4435c7041f8' + - '0xa2f7689fc12ea917d9029117d32b9fdef2a53462c853462ca86b71b97dd84af6' + - '0x55a6ef49ec5dcf6cd006d21f151f390692eedd839c813a150000000000000000' + transactionHash: '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + transactionIndex: '0x0' + transactionLogIndex: '0x0' + type: mined + logsBloom: 0x00000000000000000000200000... + root: + status: '0x1' + transactionHash: '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + transactionIndex: '0x0' + + zksync_aggregate_pubkey: + sync: true + descr: calculate the public key based on multiple public keys signing together using schnorr musig signatures. + params: + pubkeys: + descr: concatinated packed publickeys of the signers. the length of the bytes must be `num_keys * 32` + type: bytes + result: + descr: the compact public Key + type: bytes32 + example: + cmdParams: -x + request: + - '0x0f61bfe164cc43b5a112bfbfb0583004e79dbfafc97a7daad14c5d511fea8e2435065ddd04329ec94be682bf004b03a5a4eeca9bf50a8b8b6023942adc0b3409' + response: '0x9ce5b6f8db3fbbe66a3bdbd3b4731f19ec27f80ee03ead3c0708798dd949882b' + + + + diff --git a/c/src/pay/zksync/zk_deposit.c b/c/src/pay/zksync/zk_deposit.c index a8f5d7931..5c2c92894 100644 --- a/c/src/pay/zksync/zk_deposit.c +++ b/c/src/pay/zksync/zk_deposit.c @@ -87,7 +87,7 @@ in3_ret_t zksync_deposit(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { sb_add_chars(sb, ",\"priorityOpId\":"); sb_add_int(sb, bytes_to_long(data->data + 64 - 8, 8)); sb_add_chars(sb, "}"); - req_remove_required(ctx->req, req_find_required(ctx->req, "eth_sendTransactionAndWait"), true); + req_remove_required(ctx->req, req_find_required(ctx->req, "eth_sendTransactionAndWait", NULL), true); return in3_rpc_handle_finish(ctx); } } diff --git a/c/src/pay/zksync/zk_helper.c b/c/src/pay/zksync/zk_helper.c index d4fa570dc..4bf69df49 100644 --- a/c/src/pay/zksync/zk_helper.c +++ b/c/src/pay/zksync/zk_helper.c @@ -79,15 +79,15 @@ in3_ret_t send_provider_request(in3_req_t* parent, zksync_config_t* conf, char* in3 = alloca(strlen(conf->provider_url) + 26); sprintf(in3, "{\"rpc\":\"%s\"}", conf->provider_url); } - return req_send_sub_request(parent, method, params, in3, result); + return req_send_sub_request(parent, method, params, in3, result, NULL); } void zksync_calculate_account(address_t creator, bytes32_t codehash, bytes32_t saltarg, address_t pub_key_hash, address_t dst) { uint8_t tmp[85]; memset(tmp, 0, 85); memcpy(tmp, saltarg, 32); - memcpy(tmp + 32 + 12, pub_key_hash, 20); - keccak(bytes(tmp, 64), tmp + 21); + memcpy(tmp + 32, pub_key_hash, 20); + keccak(bytes(tmp, 52), tmp + 21); *tmp = 0xff; memcpy(tmp + 1, creator, 20); memcpy(tmp + 53, codehash, 32); @@ -188,12 +188,17 @@ in3_ret_t zksync_get_sync_key(zksync_config_t* conf, in3_req_t* ctx, uint8_t* sy } uint8_t* account = NULL; bytes_t signature; - char* message = "\x19" - "Ethereum Signed Message:\n68" - "Access zkSync account.\n\nOnly sign this message for a trusted client!"; + char* message = "Access zkSync account.\n\nOnly sign this message for a trusted client!"; + if (ctx->client->chain.chain_id != CHAIN_ID_MAINNET) { + d_token_t* res = NULL; + TRY(req_send_sub_request(ctx, "eth_chainId", "", NULL, &res, NULL)) + char* tmp = alloca(strlen(message) + 30); + sprintf(tmp, "%s\nChain ID: %d.", message, (int) d_int(res)); + message = tmp; + } TRY(zksync_get_account(conf, ctx, &account)) assert(account); - TRY(req_require_signature(ctx, SIGN_EC_HASH, &signature, bytes((uint8_t*) message, strlen(message)), bytes(account, 20))) + TRY(req_require_signature(ctx, SIGN_EC_PREFIX, &signature, bytes((uint8_t*) message, strlen(message)), bytes(account, 20))) if (signature.len == 65 && signature.data[64] < 2) signature.data[64] += 27; zkcrypto_pk_from_seed(signature, conf->sync_key); @@ -269,7 +274,7 @@ in3_ret_t zksync_get_contracts(zksync_config_t* conf, in3_req_t* ctx, uint8_t** } // clean up - req_remove_required(ctx, req_find_required(ctx, "contract_address"), false); + req_remove_required(ctx, req_find_required(ctx, "contract_address", NULL), false); } if (main) *main = conf->main_contract; @@ -364,7 +369,7 @@ in3_ret_t resolve_tokens(zksync_config_t* conf, in3_req_t* ctx, d_token_t* token } // clean up - req_remove_required(ctx, req_find_required(ctx, "tokens"), false); + req_remove_required(ctx, req_find_required(ctx, "tokens", NULL), false); if (cache_name) { bytes_t data = bytes((void*) conf->tokens, conf->token_len * sizeof(zksync_token_t)); diff --git a/c/src/pay/zksync/zk_incentive.c b/c/src/pay/zksync/zk_incentive.c index 7a232aab3..563e62f45 100644 --- a/c/src/pay/zksync/zk_incentive.c +++ b/c/src/pay/zksync/zk_incentive.c @@ -105,8 +105,9 @@ static in3_ret_t set_amount(zk_fee_t* dst, in3_req_t* ctx, d_token_t* t) { } static in3_ret_t get_payed_addresses(in3_req_t* ctx, bytes_t* dst) { - in3_cache_ctx_t c = {.content = NULL, .req = ctx, .key = alloca(20)}; - sprintf(c.key, "payed_%d", (uint32_t) ctx->client->chain.chain_id); + char key[20]; + sprintf(key, "payed_%d", (uint32_t) ctx->client->chain.chain_id); + in3_cache_ctx_t c = {.content = NULL, .req = ctx, .key = key}; TRY(in3_plugin_execute_first_or_none(ctx, PLGN_ACT_CACHE_GET, &c)) if (c.content) { *dst = *c.content; @@ -117,8 +118,9 @@ static in3_ret_t get_payed_addresses(in3_req_t* ctx, bytes_t* dst) { static in3_ret_t update_payed_addresses(in3_req_t* ctx, unsigned int nodes, bytes_t payed, bool update_cache) { if (update_cache) { - in3_cache_ctx_t c = {.content = &payed, .req = ctx, .key = alloca(20)}; - sprintf(c.key, "payed_%d", (uint32_t) ctx->client->chain.chain_id); + char key[20]; + sprintf(key, "payed_%d", (uint32_t) ctx->client->chain.chain_id); + in3_cache_ctx_t c = {.content = &payed, .req = ctx, .key = key}; TRY(in3_plugin_execute_first_or_none(ctx, PLGN_ACT_CACHE_SET, &c)) } diff --git a/c/src/pay/zksync/zk_message.c b/c/src/pay/zksync/zk_message.c index a370d425c..eb2163823 100644 --- a/c/src/pay/zksync/zk_message.c +++ b/c/src/pay/zksync/zk_message.c @@ -111,9 +111,7 @@ static void create_human_readable_tx_info(sb_t* sb, zksync_tx_data_t* data, char add_amount(sb, data->token, data->amount); sb_add_chars(sb, " "); sb_add_chars(sb, data->token->symbol); - sb_add_rawbytes(sb, "\nTo: 0x", bytes(data->to, 20), 0); - sb_add_chars(sb, "\nNonce: "); - sb_add_int(sb, data->nonce); + sb_add_rawbytes(sb, " to: 0x", bytes(data->to, 20), 0); sb_add_chars(sb, "\nFee: "); add_amount(sb, data->token, data->fee); sb_add_chars(sb, " "); @@ -121,25 +119,13 @@ static void create_human_readable_tx_info(sb_t* sb, zksync_tx_data_t* data, char if (data->token->symbol) #endif sb_add_chars(sb, data->token->symbol); - sb_add_chars(sb, "\nAccount Id: "); - sb_add_int(sb, data->account_id); -} + sb_add_chars(sb, "\nNonce: "); + sb_add_int(sb, data->nonce); -static void create_signed_bytes(sb_t* sb) { - char* PREFIX = "\x19" - "Ethereum Signed Message:\n"; - char len_num[7]; - int l = strlen(PREFIX) + sprintf(len_num, "%d", (int) sb->len); - int len = sb->len; - sb_add_chars(sb, PREFIX); - sb_add_chars(sb, len_num); - memmove(sb->data + l, sb->data, len); - memcpy(sb->data, PREFIX, strlen(PREFIX)); - memcpy(sb->data + l - strlen(len_num), len_num, strlen(len_num)); + in3_log_debug("Human readable message : \n%s\n", sb->data); } -static in3_ret_t sign_sync_transfer(zksync_tx_data_t* data, in3_req_t* ctx, zksync_config_t* conf, uint8_t* raw, uint8_t* sig) { - uint32_t total; +static in3_ret_t sign_sync_transfer(zksync_tx_data_t* data, in3_req_t* ctx, zksync_config_t* conf, uint8_t* raw, uint8_t* sig, uint32_t* total) { char dec[80]; uint16_t tid = data->token ? data->token->id : 0; raw[0] = data->type; // 0: type(1) @@ -149,7 +135,7 @@ static in3_ret_t sign_sync_transfer(zksync_tx_data_t* data, in3_req_t* ctx, zksy raw[45] = (tid >> 8) & 0xff; // 45: token_id (2) raw[46] = tid & 0xff; // if (data->type == ZK_WITHDRAW) { - total = 69; + *total = 85; #ifdef ZKSYNC_256 memcpy(raw + 47, data->amount + 16, 16); #else @@ -161,7 +147,7 @@ static in3_ret_t sign_sync_transfer(zksync_tx_data_t* data, in3_req_t* ctx, zksy int_to_bytes(data->nonce, raw + 65); // 65: nonce(4) } else { - total = 58; + *total = 74; to_dec(dec, data->amount); // create a decimal represntation and pack it TRY(pack(dec, 35, 5, raw + 47, ctx)) // 47: amount packed (5) to_dec(dec, data->fee); // create a decimal represntation and pack it @@ -169,33 +155,38 @@ static in3_ret_t sign_sync_transfer(zksync_tx_data_t* data, in3_req_t* ctx, zksy int_to_bytes(data->nonce, raw + 54); // 54: nonce(4) } + long_to_bytes(data->valid.from, raw + (*total) - 16); + long_to_bytes(data->valid.to, raw + (*total) - 8); + // sign data - return zksync_sign(conf, bytes(raw, total), ctx, sig); + return zksync_sign(conf, bytes(raw, (*total)), ctx, sig); } in3_ret_t zksync_sign_transfer(sb_t* sb, zksync_tx_data_t* data, in3_req_t* ctx, zksync_config_t* conf) { - char msg_data[200]; - bytes_t signature; - sb_t msg = sb_stack(msg_data); - create_human_readable_tx_info(&msg, data, data->type == ZK_WITHDRAW ? "Withdraw " : "Transfer "); - create_signed_bytes(&msg); - if (data->conf->sign_type == ZK_SIGN_CREATE2) { - signature = bytes(alloca(65), 65); - memset(signature.data, 0, 65); + // fix valid.to first + if (!data->valid.to) data->valid.to = 0xffffffffl; + + bytes_t signature = bytes(NULL, 0); + + if (data->conf->sign_type != ZK_SIGN_CREATE2) { + char msg_data[216]; + sb_t msg = sb_stack(msg_data); + create_human_readable_tx_info(&msg, data, data->type == ZK_WITHDRAW ? "Withdraw " : "Transfer "); + TRY(req_require_signature(ctx, SIGN_EC_PREFIX, &signature, bytes((uint8_t*) msg_data, msg.len), bytes(data->from, 20))) + in3_log_debug("zksync_sign_transfer human readable :\n%s\n", msg_data); + + if (signature.len == 65 && signature.data[64] < 27) + signature.data[64] += 27; //because EIP155 chainID = 0 } - else - TRY(req_require_signature(ctx, SIGN_EC_HASH, &signature, bytes((uint8_t*) msg_data, msg.len), bytes(data->from, 20))) - in3_log_debug("zksync_sign_transfer human readable :\n%s\n", msg_data); - if (signature.len == 65 && signature.data[64] < 27) - signature.data[64] += 27; //because EIP155 chainID = 0 // now create the packed sync transfer - uint8_t raw[69], sig[96]; - TRY(sign_sync_transfer(data, ctx, conf, raw, sig)); + uint8_t raw[85], sig[96]; + uint32_t total = 0; + TRY(sign_sync_transfer(data, ctx, conf, raw, sig, &total)); if (in3_log_level_is(LOG_DEBUG) || in3_log_level_is(LOG_TRACE)) { char* hex = alloca(142); - bytes_to_hex(raw, data->type == ZK_WITHDRAW ? 69 : 58, hex); + bytes_to_hex(raw, total, hex); in3_log_debug("zksync_sign_transfer bin :\n%s\n", hex); } @@ -207,6 +198,8 @@ in3_ret_t zksync_sign_transfer(sb_t* sb, zksync_tx_data_t* data, in3_req_t* ctx, sb_add_rawbytes(sb, "\",\"to\":\"0x", bytes(data->to, 20), 0); sb_add_chars(sb, "\",\"token\":"); sb_add_int(sb, data->token->id); + sb_add_chars(sb, ",\"tokenId\":"); + sb_add_int(sb, data->token->id); sb_add_chars(sb, ",\"amount\":"); #ifdef ZKSYNC_256 char dec[80]; @@ -220,21 +213,29 @@ in3_ret_t zksync_sign_transfer(sb_t* sb, zksync_tx_data_t* data, in3_req_t* ctx, sb_add_chars(sb, ",\"fee\":"); sb_add_int(sb, data->fee); #endif + sb_add_chars(sb, ",\"validFrom\":"); + sb_add_int(sb, (int64_t) data->valid.from); + sb_add_chars(sb, ",\"validUntil\":"); + sb_add_int(sb, (int64_t) data->valid.to); sb_add_chars(sb, ",\"nonce\":"); sb_add_int(sb, data->nonce); sb_add_rawbytes(sb, ",\"signature\":{\"pubKey\":\"", bytes(sig, 32), 0); sb_add_rawbytes(sb, "\",\"signature\":\"", bytes(sig + 32, 64), 0); - sb_add_chars(sb, "\"}},{\"type\":\""); - if (data->conf->sign_type == ZK_SIGN_CONTRACT) - sb_add_chars(sb, "EIP1271Signature"); - else - sb_add_chars(sb, "EthereumSignature"); - sb_add_rawbytes(sb, "\",\"signature\":\"0x", signature, 0); - sb_add_chars(sb, "\"}"); + sb_add_chars(sb, "\"}},"); + if (data->conf->sign_type == ZK_SIGN_CREATE2) + sb_add_chars(sb, "null"); + else { + sb_add_chars(sb, "{\"type\":\""); + sb_add_chars(sb, data->conf->sign_type == ZK_SIGN_CONTRACT ? "EIP1271Signature" : "EthereumSignature"); + sb_add_rawbytes(sb, "\",\"signature\":\"0x", signature, 0); + sb_add_chars(sb, "\"}"); + } return IN3_OK; } in3_ret_t zksync_sign(zksync_config_t* conf, bytes_t msg, in3_req_t* ctx, uint8_t* sig) { + in3_log_debug("signing zksync data: \n"); + b_print(&msg); if (memiszero(conf->sync_key, 32)) return req_set_error(ctx, "no signing key set", IN3_ECONFIG); if (!conf->musig_pub_keys.data) return zkcrypto_sign_musig(conf->sync_key, msg, sig); char* p = alloca(msg.len * 2 + 5); @@ -245,17 +246,17 @@ in3_ret_t zksync_sign(zksync_config_t* conf, bytes_t msg, in3_req_t* ctx, uint8_ p[msg.len * 2 + 3] = '"'; p[msg.len * 2 + 4] = 0; d_token_t* result; - TRY(req_send_sub_request(ctx, "zk_sign", p, NULL, &result)) + TRY(req_send_sub_request(ctx, "zk_sign", p, NULL, &result, NULL)) if (d_type(result) != T_BYTES || d_len(result) != 96) return req_set_error(ctx, "invalid signature returned", IN3_ECONFIG); memcpy(sig, result->data, 96); return IN3_OK; } -in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* ctx, uint8_t* sync_pub_key, uint32_t nonce, zksync_config_t* conf, zk_fee_t fee, zksync_token_t* token) { +in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* ctx, uint8_t* sync_pub_key, uint32_t nonce, zksync_config_t* conf, zk_fee_t fee, zksync_token_t* token, zksync_valid_t valid) { // create sign_msg for the rollup char dec[80]; - uint8_t sign_msg_bytes[53], sig[96]; + uint8_t sign_msg_bytes[69], sig[96]; sign_msg_bytes[0] = 7; // tx type 7 (1 byte) int_to_bytes(conf->account_id, sign_msg_bytes + 1); // acount_id (4 bytes) memcpy(sign_msg_bytes + 5, conf->account, 20); // account address @@ -265,52 +266,55 @@ in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* ctx, uint8_t* sync_pub to_dec(dec, fee); // create a decimal represntation and pack it TRY(pack(dec, 11, 5, sign_msg_bytes + 47, ctx)) // 47: fee packed (2) int_to_bytes(nonce, sign_msg_bytes + 49); // nonce + long_to_bytes(valid.from, sign_msg_bytes + 53); // valid_from + long_to_bytes(valid.to, sign_msg_bytes + 61); // valid_to // now sign it with the new pk - TRY(zksync_sign(conf, bytes(sign_msg_bytes, 53), ctx, sig)) - // create human readable message - char msg_data[300]; - uint8_t tmp[8]; - bytes_t signature = bytes(NULL, 0); - sb_t msg = sb_stack(msg_data); + TRY(zksync_sign(conf, bytes(sign_msg_bytes, 69), ctx, sig)) - int_to_bytes(nonce, tmp); - int_to_bytes(conf->account_id, tmp + 4); - sb_add_rawbytes(&msg, "Register zkSync pubkey:\n\n", bytes(sync_pub_key, 20), 20); - sb_add_rawbytes(&msg, "\nnonce: 0x", bytes(tmp, 4), 4); - sb_add_rawbytes(&msg, "\naccount id: 0x", bytes(tmp + 4, 4), 4); - sb_add_chars(&msg, "\n\nOnly sign this message for a trusted client!"); - create_signed_bytes(&msg); - - if (conf->sign_type != ZK_SIGN_CONTRACT) - TRY(req_require_signature(ctx, SIGN_EC_HASH, &signature, bytes((uint8_t*) msg_data, msg.len), bytes(conf->account, 20))) + // create 2fa-message to be signed with the eth-signer + uint8_t ethmsg[60]; + bytes_t signature = bytes(NULL, 0); + memcpy(ethmsg, sync_pub_key, 20); // pubkeyhash (20) + int_to_bytes(nonce, ethmsg + 20); // nonce (4) + int_to_bytes(conf->account_id, ethmsg + 24); // acount_id (4 bytes) + memset(ethmsg + 28, 0, 32); // msgBatch hash - currently not supported, so 32x0 - if (signature.len == 65 && signature.data[64] < 27) - signature.data[64] += 27; //because EIP155 chainID = 0 + if (conf->sign_type != ZK_SIGN_CREATE2) { + TRY(req_require_signature(ctx, SIGN_EC_PREFIX, &signature, bytes((uint8_t*) ethmsg, 60), bytes(conf->account, 20))) + if (signature.len == 65 && signature.data[64] < 27) + signature.data[64] += 27; //because EIP155 chainID = 0 + } sb_add_chars(sb, "{\"type\":\"ChangePubKey\",\"accountId\":"); sb_add_int(sb, conf->account_id); sb_add_rawbytes(sb, ",\"account\":\"0x", bytes(conf->account, 20), 0); sb_add_rawbytes(sb, "\",\"newPkHash\":\"sync:", bytes(sync_pub_key, 20), 0); - sb_add_chars(sb, "\",\"feeToken\":"); + sb_add_chars(sb, "\",\"feeTokenId\":"); sb_add_int(sb, token->id); - sb_add_chars(sb, ",\"fee\":"); + sb_add_chars(sb, ",\"feeToken\":"); + sb_add_int(sb, token->id); + sb_add_chars(sb, ",\"validFrom\":"); + sb_add_int(sb, (int64_t) valid.from); + sb_add_chars(sb, ",\"validUntil\":"); + sb_add_int(sb, (int64_t) valid.to); + sb_add_chars(sb, ",\"fee\":\""); #ifdef ZKSYNC_256 to_dec(dec, fee); sb_add_chars(sb, dec); #else sb_add_int(sb, fee); #endif - sb_add_chars(sb, ",\"nonce\":"); + sb_add_chars(sb, "\",\"nonce\":"); sb_add_int(sb, nonce); if (conf->version > 0) { - sb_add_chars(sb, ",\"changePubkeyType\":{"); + sb_add_chars(sb, ",\"ethAuthData\":{"); if (conf->sign_type == ZK_SIGN_PK) - sb_add_rawbytes(sb, "\"type\":\"EthereumSignature\",\"ethSignature\":\"0x", signature, 0); + sb_add_rawbytes(sb, "\"type\":\"ECDSA\",\"ethSignature\":\"0x", signature, 0); else if (conf->sign_type == ZK_SIGN_CONTRACT) - sb_add_rawbytes(sb, "\"type\":\"OnchainTransaction", signature, 0); + sb_add_rawbytes(sb, "\"type\":\"Onchain", signature, 0); else if (conf->sign_type == ZK_SIGN_CREATE2 && conf->create2) { - sb_add_rawbytes(sb, "\"type\":\"Create2Contract\",\"creatorAddress\":\"0x", bytes(conf->create2->creator, 20), 0); + sb_add_rawbytes(sb, "\"type\":\"CREATE2\",\"creatorAddress\":\"0x", bytes(conf->create2->creator, 20), 0); sb_add_rawbytes(sb, "\",\"saltArg\":\"0x", bytes(conf->create2->salt_arg, 32), 0); sb_add_rawbytes(sb, "\",\"codeHash\":\"0x", bytes(conf->create2->codehash, 32), 0); } @@ -318,13 +322,7 @@ in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* ctx, uint8_t* sync_pub } sb_add_rawbytes(sb, ",\"signature\":{\"pubKey\":\"", bytes(sig, 32), 0); sb_add_rawbytes(sb, "\",\"signature\":\"", bytes(sig + 32, 64), 0); - if (signature.data) { - sb_add_rawbytes(sb, "\"},\"ethSignature\":\"0x", signature, 0); - sb_add_chars(sb, "\"},null,false"); - } - else { - sb_add_chars(sb, "\"},\"ethSignature\":null},null,false"); - } + sb_add_chars(sb, "\"}},null,false"); return IN3_OK; } diff --git a/c/src/pay/zksync/zk_musig.c b/c/src/pay/zksync/zk_musig.c index e24ead148..d871bca47 100644 --- a/c/src/pay/zksync/zk_musig.c +++ b/c/src/pay/zksync/zk_musig.c @@ -43,7 +43,7 @@ static in3_ret_t send_sign_request(in3_req_t* parent, int pos, zksync_config_t* if (!url) return req_set_error(parent, "missing url to fetch a signature", IN3_EINVAL); char* in3 = alloca(strlen(url) + 26); sprintf(in3, "{\"rpc\":\"%s\"}", url); - return req_send_sub_request(parent, method, params, in3, result); + return req_send_sub_request(parent, method, params, in3, result, NULL); } static in3_ret_t update_session(zk_musig_session_t* s, in3_req_t* ctx, d_token_t* data) { @@ -162,6 +162,7 @@ static in3_ret_t verify_proof(zksync_config_t* conf, in3_req_t* ctx, bytes_t* ac if (!signer_key) return req_set_error(ctx, "the signer key could not be found!", IN3_EINVAL); d_token_t* result = NULL; + in3_req_t* sub = NULL; char* proof_data = d_create_json(ctx->request_context, proof); sb_t sb = {0}; sb_add_rawbytes(&sb, "\"0x", *msg, 0); @@ -171,10 +172,10 @@ static in3_ret_t verify_proof(zksync_config_t* conf, in3_req_t* ctx, bytes_t* ac sb_add_chars(&sb, proof_data); _free(proof_data); - TRY_FINAL(req_send_sub_request(ctx, conf->proof_verify_method, sb.data, NULL, &result), _free(sb.data)) + TRY_FINAL(req_send_sub_request(ctx, conf->proof_verify_method, sb.data, NULL, &result, &sub), _free(sb.data)) in3_ret_t ret = (d_type(result) == T_BOOLEAN && d_int(result)) ? IN3_OK : req_set_error(ctx, "Proof could not be verified!", IN3_EINVAL); - req_remove_required(ctx, req_find_required(ctx, conf->proof_verify_method), false); + req_remove_required(ctx, sub, false); return ret; } @@ -184,6 +185,7 @@ static in3_ret_t create_proof(zksync_config_t* conf, in3_req_t* ctx, bytes_t* ms // prepare the arguments to create the proof d_token_t* result = NULL; uint8_t* account; + in3_req_t* sub = NULL; TRY(zksync_get_account(conf, ctx, &account)) sb_t sb = {0}; sb_add_rawbytes(&sb, "\"0x", *msg, 0); @@ -191,14 +193,11 @@ static in3_ret_t create_proof(zksync_config_t* conf, in3_req_t* ctx, bytes_t* ms sb_add_chars(&sb, "\""); // send the subrequest and wait for a response - TRY_FINAL(req_send_sub_request(ctx, conf->proof_create_method, sb.data, NULL, &result), _free(sb.data)) + TRY_FINAL(req_send_sub_request(ctx, conf->proof_create_method, sb.data, NULL, &result, &sub), _free(sb.data)) // handle error if (!result) req_set_error(ctx, "Proof could not be created!", IN3_EINVAL); - // all is well, so we find the subrequest - in3_req_t* sub = req_find_required(ctx, conf->proof_create_method); - // only copy the data as json, so we can store them without a json_ctx and can clean up. if (sub) { *proof_data = d_create_json(sub->response_context, result); @@ -333,8 +332,8 @@ in3_ret_t zksync_musig_sign(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { TRY_SIG(zkcrypto_compute_aggregated_pubkey(s->pub_keys, res)) TRY_SIG(zkcrypto_signer_receive_signature_shares(s->signer, s->signature_shares, res + 32)) cleanup_session(s, conf); - if (!zkcrypto_verify_signatures(message, conf->musig_pub_keys, bytes(res, 96))) - return req_set_error(ctx->req, "invalid signature", IN3_EINVAL); + // if (!zkcrypto_verify_signatures(message, conf->musig_pub_keys, bytes(res, 96))) + // return req_set_error(ctx->req, "invalid signature", IN3_EINVAL); return in3_rpc_handle_with_bytes(ctx, bytes(res, 96)); } diff --git a/c/src/pay/zksync/zk_rest.c b/c/src/pay/zksync/zk_rest.c new file mode 100644 index 000000000..3378b9d0c --- /dev/null +++ b/c/src/pay/zksync/zk_rest.c @@ -0,0 +1,87 @@ +#include "../../core/client/keys.h" +#include "../../core/client/plugin.h" +#include "../../core/client/request_internal.h" +#include "../../core/util/debug.h" +#include "../../core/util/mem.h" +#include "../../third-party/zkcrypto/lib.h" +#include "zk_helper.h" +#include "zksync.h" +#include +#include +#include +#include + +#define CHECK_REST_API(ctx, conf) \ + if (!conf->rest_api) return req_set_error(ctx->req, "No zksync Rest-Api set in config", IN3_ECONFIG); + +in3_ret_t zksync_tx_data(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { + CHECK_REST_API(ctx, conf) + CHECK_PARAMS_LEN(ctx->req, ctx->params, 1) + CHECK_PARAM_TYPE(ctx->req, ctx->params, 0, T_BYTES) + CHECK_PARAM_LEN(ctx->req, ctx->params, 0, 32) + + d_token_t* res = NULL; + in3_req_t* req = NULL; + sb_t sb = {0}; + sb_add_chars(&sb, "\"GET\",\""); + sb_add_escaped_chars(&sb, conf->rest_api); + sb_add_rawbytes(&sb, "/transactions_all/0x", d_to_bytes(ctx->params + 1), 32); + sb_add_chars(&sb, "\""); + + TRY_FINAL(req_send_sub_request(ctx->req, "in3_http", sb.data, NULL, &res, &req), _free(sb.data)) + + char* resp = d_create_json(req->response_context, res); + in3_rpc_handle_with_string(ctx, resp); + _free(resp); + return IN3_OK; +} + +in3_ret_t zksync_account_history(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { + CHECK_REST_API(ctx, conf) + CHECK_PARAMS_LEN(ctx->req, ctx->params, 1) + CHECK_PARAM_ADDRESS(ctx->req, ctx->params, 0) + + d_token_t* ref_tx = d_get_at(ctx->params, 1); + d_token_t* limit = d_get_at(ctx->params, 2); + if (!limit && d_type(ref_tx) == T_INTEGER) { + limit = ref_tx; + ref_tx = NULL; + } + if (d_type(ref_tx) == T_NULL) ref_tx = NULL; + if (d_type(limit) == T_NULL) limit = NULL; + if (ref_tx && d_type(ref_tx) != T_STRING) return req_set_error(ctx->req, "The 2nd argument in account History (base tx) must be a string starting with < or > and the transactionId", IN3_ECONFIG); + if (limit && d_type(limit) != T_INTEGER) return req_set_error(ctx->req, "The 3rd argument in account History (limit) must be a integer!", IN3_ECONFIG); + + if (limit && !d_int(limit)) limit = NULL; + d_token_t* res = NULL; + in3_req_t* req = NULL; + sb_t sb = {0}; + sb_add_chars(&sb, "\"GET\",\""); + sb_add_escaped_chars(&sb, conf->rest_api); + sb_add_rawbytes(&sb, "/account/0x", d_to_bytes(ctx->params + 1), 20); + sb_add_chars(&sb, "/history/"); + if (!ref_tx) { + sb_add_chars(&sb, "0/"); + sb_add_int(&sb, limit ? (int64_t) d_long(limit) : 100); + } + else if (strcmp(d_string(ref_tx), "pending") == 0) + sb_add_chars(&sb, "newer_than"); + else if (ref_tx->data[0] == '<' || ref_tx->data[0] == '>') { + sb_add_chars(&sb, ref_tx->data[0] == '<' ? "older_than?tx_id=" : "newer_than?tx_id="); + sb_add_chars(&sb, d_string(ref_tx)); + sb_add_chars(&sb, "&limit="); + sb_add_int(&sb, limit ? (int64_t) d_long(limit) : 100); + } + else { + _free(sb.data); + return req_set_error(ctx->req, "Invalid base_tx it must a tx_id with <,> or pending", IN3_ECONFIG); + } + sb_add_chars(&sb, "\""); + + TRY_FINAL(req_send_sub_request(ctx->req, "in3_http", sb.data, NULL, &res, &req), _free(sb.data)) + + char* resp = d_create_json(req->response_context, res); + in3_rpc_handle_with_string(ctx, resp); + _free(resp); + return IN3_OK; +} diff --git a/c/src/pay/zksync/zk_setkey.c b/c/src/pay/zksync/zk_setkey.c index 8587cc39b..7b6055ffa 100644 --- a/c/src/pay/zksync/zk_setkey.c +++ b/c/src/pay/zksync/zk_setkey.c @@ -58,10 +58,16 @@ static in3_ret_t auth_pub_key(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx, } in3_ret_t zksync_set_key(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { - address_t pub_hash; - uint32_t nonce; - d_token_t* token = d_len(ctx->params) == 1 ? ctx->params + 1 : NULL; - bytes_t* new_key = d_get_bytes_at(ctx->params, 1); + address_t pub_hash; + zksync_valid_t valid; + uint32_t nonce; + int plen = d_len(ctx->params); + d_token_t* token = plen == 1 ? ctx->params + 1 : NULL; + bytes_t* new_key = d_get_bytes_at(ctx->params, 1); + valid.from = plen > 2 ? d_get_long_at(ctx->params, 2) : 0; + valid.to = plen > 3 ? d_get_long_at(ctx->params, 3) : 0; + if (!valid.to) valid.to = 0xffffffffl; + zksync_token_t* token_data = NULL; if (!token) return req_set_error(ctx->req, "Missing fee token as first token", IN3_EINVAL); zk_fee_t fee; @@ -93,7 +99,7 @@ in3_ret_t zksync_set_key(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { } if (!cached) { sb_t sb = {0}; - in3_ret_t ret = zksync_sign_change_pub_key(&sb, ctx->req, pub_hash, nonce, conf, fee, token_data); + in3_ret_t ret = zksync_sign_change_pub_key(&sb, ctx->req, pub_hash, nonce, conf, fee, token_data, valid); if (ret && sb.data) _free(sb.data); TRY(ret) if (!sb.data) return IN3_EUNKNOWN; diff --git a/c/src/pay/zksync/zksync.c b/c/src/pay/zksync/zksync.c index 5498291a7..300086e0d 100644 --- a/c/src/pay/zksync/zksync.c +++ b/c/src/pay/zksync/zksync.c @@ -137,7 +137,7 @@ static in3_ret_t zksync_rpc(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { // check the prefix (zksync_ or zk_ is supported) if (strncmp(ctx->method, "zksync_", 7) == 0) ctx->method += 7; - else if (strncmp(ctx->method, "zk_", 3) == 0) + else if (strncmp(ctx->method, "zk_", 3) == 0 && strncmp(ctx->method, "zk_wallet_", 10)) ctx->method += 3; else return IN3_EIGNORE; @@ -162,6 +162,8 @@ static in3_ret_t zksync_rpc(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { TRY_RPC("verify", in3_rpc_handle_with_int(ctx, conf->musig_pub_keys.data ? zkcrypto_verify_signatures(d_to_bytes(ctx->params + 1), conf->musig_pub_keys, d_to_bytes(ctx->params + 2)) : zkcrypto_verify_musig(d_to_bytes(ctx->params + 1), d_to_bytes(ctx->params + 2)))) + TRY_RPC("tx_data", zksync_tx_data(conf, ctx)) + TRY_RPC("account_history", zksync_account_history(conf, ctx)) // prepare fallback to send to zksync-server str_range_t p = d_to_json(ctx->params); @@ -170,7 +172,7 @@ static in3_ret_t zksync_rpc(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx) { param_string[p.len - 2] = 0; if (strcmp(ctx->method, "account_info") == 0) { - if (*param_string == 0) { + if (*param_string == 0 || strcmp(param_string, "null") == 0) { TRY(zksync_get_account(conf, ctx->req, NULL)) param_string = alloca(45); set_quoted_address(param_string, conf->account); @@ -201,6 +203,7 @@ static in3_ret_t config_free(zksync_config_t* conf, bool free_conf) { } _free(conf->musig_urls); } + if (conf->rest_api) _free(conf->rest_api); if (conf->provider_url) _free(conf->provider_url); if (conf->main_contract) _free(conf->main_contract); if (conf->account) _free(conf->account); @@ -242,45 +245,50 @@ static in3_ret_t config_set(zksync_config_t* conf, in3_configure_ctx_t* ctx) { if (ctx->token->key != key("zksync")) return IN3_EIGNORE; // TODO error-reporting for invalid config - const char* provider = d_get_string(ctx->token, key("provider_url")); + const char* provider = d_get_string(ctx->token, CONFIG_KEY("provider_url")); if (provider) { if (conf->provider_url) _free(conf->provider_url); conf->provider_url = _strdupn(provider, -1); } - const char* pvm = d_get_string(ctx->token, key("verify_proof_method")); + const char* rest_api = d_get_string(ctx->token, CONFIG_KEY("rest_api")); + if (rest_api) { + if (conf->rest_api) _free(conf->rest_api); + conf->rest_api = _strdupn(rest_api, -1); + } + const char* pvm = d_get_string(ctx->token, CONFIG_KEY("verify_proof_method")); if (pvm) { if (conf->proof_verify_method) _free(conf->proof_verify_method); conf->proof_verify_method = _strdupn(pvm, -1); } - const char* pcm = d_get_string(ctx->token, key("create_proof_method")); + const char* pcm = d_get_string(ctx->token, CONFIG_KEY("create_proof_method")); if (pcm) { if (conf->proof_create_method) _free(conf->proof_create_method); conf->proof_create_method = _strdupn(pcm, -1); } - bytes_t* account = d_get_bytes(ctx->token, key("account")); + bytes_t* account = d_get_bytes(ctx->token, CONFIG_KEY("account")); if (account && account->len == 20) memcpy(conf->account = _malloc(20), account->data, 20); - bytes_t sync_key = d_to_bytes(d_get(ctx->token, key("sync_key"))); + bytes_t sync_key = d_to_bytes(d_get(ctx->token, CONFIG_KEY("sync_key"))); if (sync_key.len) { zkcrypto_pk_from_seed(sync_key, conf->sync_key); zkcrypto_pk_to_pubkey(conf->sync_key, conf->pub_key); zkcrypto_pubkey_hash(bytes(conf->pub_key, 32), conf->pub_key_hash_pk); } - bytes_t* main_contract = d_get_bytes(ctx->token, key("main_contract")); + bytes_t* main_contract = d_get_bytes(ctx->token, CONFIG_KEY("main_contract")); if (main_contract && main_contract->len == 20) memcpy(conf->main_contract = _malloc(20), main_contract->data, 20); - d_token_t* st = d_get(ctx->token, key("signer_type")); + d_token_t* st = d_get(ctx->token, CONFIG_KEY("signer_type")); if (st) conf->sign_type = get_sign_type(st); else if (conf->sign_type == 0) conf->sign_type = ZK_SIGN_PK; - conf->version = (uint32_t) d_intd(d_get(ctx->token, key("version")), conf->version); - d_token_t* musig = d_get(ctx->token, key("musig_pub_keys")); + conf->version = (uint32_t) d_intd(d_get(ctx->token, CONFIG_KEY("version")), conf->version); + d_token_t* musig = d_get(ctx->token, CONFIG_KEY("musig_pub_keys")); if (musig && d_type(musig) == T_BYTES && d_len(musig) % 32 == 0) { if (conf->musig_pub_keys.data) _free(conf->musig_pub_keys.data); conf->musig_pub_keys = bytes(_malloc(d_len(musig)), musig->len); memcpy(conf->musig_pub_keys.data, musig->data, musig->len); } - d_token_t* urls = d_get(ctx->token, key("musig_urls")); + d_token_t* urls = d_get(ctx->token, CONFIG_KEY("musig_urls")); if (urls) { if (conf->musig_urls) { for (unsigned int i = 0; i < conf->musig_pub_keys.len / 32; i++) { @@ -288,29 +296,35 @@ static in3_ret_t config_set(zksync_config_t* conf, in3_configure_ctx_t* ctx) { } _free(conf->musig_urls); } - conf->musig_urls = _calloc(d_len(urls), sizeof(char*)); - for (int i = 0; i < d_len(urls); i++) { - char* s = d_get_string_at(urls, i); - if (s) conf->musig_urls[i] = _strdupn(s, -1); + if (d_type(urls) == T_STRING) { + conf->musig_urls = _calloc(2, sizeof(char*)); + conf->musig_urls[1] = _strdupn(d_string(urls), -1); + } + else if (d_type(urls) == T_ARRAY) { + conf->musig_urls = _calloc(d_len(urls), sizeof(char*)); + for (int i = 0; i < d_len(urls); i++) { + char* s = d_get_string_at(urls, i); + if (s && strlen(s)) conf->musig_urls[i] = _strdupn(s, -1); + } } } - d_token_t* create2 = d_get(ctx->token, key("create2")); + d_token_t* create2 = d_get(ctx->token, CONFIG_KEY("create2")); if (create2) { conf->sign_type = ZK_SIGN_CREATE2; if (!conf->create2) conf->create2 = _calloc(1, sizeof(zk_create2_t)); - bytes_t* t = d_get_bytes(create2, key("creator")); + bytes_t* t = d_get_bytes(create2, CONFIG_KEY("creator")); if (t && t->len == 20) memcpy(conf->create2->creator, t->data, 20); - t = d_get_bytes(create2, key("saltarg")); + t = d_get_bytes(create2, CONFIG_KEY("saltarg")); if (t && t->len == 32) memcpy(conf->create2->salt_arg, t->data, 32); - t = d_get_bytes(create2, key("codehash")); + t = d_get_bytes(create2, CONFIG_KEY("codehash")); if (t && t->len == 32) memcpy(conf->create2->codehash, t->data, 32); } - d_token_t* incentive = d_get(ctx->token, key("incentive")); + d_token_t* incentive = d_get(ctx->token, CONFIG_KEY("incentive")); if (incentive) { if (!conf->incentive) conf->incentive = _calloc(1, sizeof(pay_criteria_t)); for (d_iterator_t iter = d_iter(incentive); iter.left; d_iter_next(&iter)) { - if (iter.token->key == key("nodes")) { + if (iter.token->key == CONFIG_KEY("nodes")) { conf->incentive->payed_nodes = d_int(iter.token); in3_req_t c = {0}; c.client = ctx->client; @@ -320,9 +334,9 @@ static in3_ret_t config_set(zksync_config_t* conf, in3_configure_ctx_t* ctx) { return ret; } } - else if (iter.token->key == key("max_price")) + else if (iter.token->key == CONFIG_KEY("max_price")) conf->incentive->max_price_per_hundred_igas = d_long(iter.token); - else if (iter.token->key == key("token")) { + else if (iter.token->key == CONFIG_KEY("token")) { _free(conf->incentive->token); conf->incentive->token = _strdupn(d_string(iter.token), -1); } @@ -362,4 +376,4 @@ in3_ret_t in3_register_zksync(in3_t* c) { return in3_plugin_register(c, PLGN_ACT_RPC_HANDLE | PLGN_ACT_INIT | PLGN_ACT_TERM | PLGN_ACT_CONFIG_GET | PLGN_ACT_CONFIG_SET | PLGN_ACT_ADD_PAYLOAD | PLGN_ACT_PAY_FOLLOWUP, handle_zksync, conf, false); -} \ No newline at end of file +} diff --git a/c/src/pay/zksync/zksync.h b/c/src/pay/zksync/zksync.h index 7d897bcb5..b93ec3616 100644 --- a/c/src/pay/zksync/zksync.h +++ b/c/src/pay/zksync/zksync.h @@ -106,6 +106,7 @@ struct pay_criteria; /** internal configuration-object */ typedef struct zksync_config { char* provider_url; /**< url of the zksync-server */ + char* rest_api; /**< url of the zksync-rest-api */ uint8_t* account; /**< address of the account */ uint8_t* main_contract; /**< address of the main zksync contract*/ uint8_t* gov_contract; /**< address of the government contract */ @@ -128,6 +129,11 @@ typedef struct zksync_config { char* proof_create_method; /**< the rpc-method used to create the proof before creating a signature */ } zksync_config_t; +typedef struct valid { + uint64_t from; + uint64_t to; +} zksync_valid_t; + typedef struct pay_criteria { uint_fast32_t payed_nodes; /**< max number of nodes payed at the same time*/ uint64_t max_price_per_hundred_igas; /**< the max price per 100 gas units to accept a payment offer */ @@ -146,6 +152,7 @@ typedef struct { zk_msg_type_t type; /**< message type */ zk_fee_t amount; /**< amount to send */ zk_fee_t fee; /**< ransaction fees */ + zksync_valid_t valid; /**< validity */ } zksync_tx_data_t; /** registers the zksync-plugin in the client */ @@ -167,7 +174,7 @@ NONULL in3_ret_t zksync_emergency_withdraw(zksync_config_t* conf, in3_rpc_handle NONULL in3_ret_t zksync_sign_transfer(sb_t* sb, zksync_tx_data_t* data, in3_req_t* req, zksync_config_t* conf); /** creates message data and signs a change_pub_key-message */ -NONULL in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* req, uint8_t* sync_pub_key, uint32_t nonce, zksync_config_t* conf, zk_fee_t fee, zksync_token_t* token); +NONULL in3_ret_t zksync_sign_change_pub_key(sb_t* sb, in3_req_t* req, uint8_t* sync_pub_key, uint32_t nonce, zksync_config_t* conf, zk_fee_t fee, zksync_token_t* token, zksync_valid_t valid); in3_ret_t zksync_musig_sign(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx); zk_musig_session_t* zk_musig_session_free(zk_musig_session_t* s); @@ -176,6 +183,8 @@ in3_ret_t zksync_check_payment(zksync_config_t* conf, in3_pay_followup in3_ret_t zksync_add_payload(in3_pay_payload_ctx_t* ctx); in3_ret_t update_nodelist_from_cache(in3_req_t* req, unsigned int nodelen); in3_ret_t handle_zksync(void* conf, in3_plugin_act_t action, void* arg); +in3_ret_t zksync_tx_data(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx); +in3_ret_t zksync_account_history(zksync_config_t* conf, in3_rpc_handle_ctx_t* ctx); #ifdef __cplusplus } #endif diff --git a/c/src/signer/ledger-nano/signer/CMakeLists.txt b/c/src/signer/ledger-nano/signer/CMakeLists.txt index 8d08d39c2..e2b04f2dc 100644 --- a/c/src/signer/ledger-nano/signer/CMakeLists.txt +++ b/c/src/signer/ledger-nano/signer/CMakeLists.txt @@ -40,7 +40,7 @@ endif() add_static_library( NAME ledger_signer - + SOURCES ledger_signer.c device_apdu_commands.c diff --git a/c/src/signer/ledger-nano/signer/ethereum_apdu_client.c b/c/src/signer/ledger-nano/signer/ethereum_apdu_client.c index 98f778c84..7ae73e74e 100644 --- a/c/src/signer/ledger-nano/signer/ethereum_apdu_client.c +++ b/c/src/signer/ledger-nano/signer/ethereum_apdu_client.c @@ -51,6 +51,7 @@ in3_ret_t eth_ledger_sign_txn(void* p_data, in3_plugin_act_t action, void* p_ctx memcpy(hash, sc->message.data, sc->message.len); is_hashed = true; case SIGN_EC_HASH: + case SIGN_EC_PREFIX: if (memcmp(prefix, sc->message.data, strlen(prefix)) == 0) { is_msg = true; } diff --git a/c/src/signer/ledger-nano/signer/ledger_signer.c b/c/src/signer/ledger-nano/signer/ledger_signer.c index efc6d666d..6ba77bebc 100644 --- a/c/src/signer/ledger-nano/signer/ledger_signer.c +++ b/c/src/signer/ledger-nano/signer/ledger_signer.c @@ -86,6 +86,7 @@ in3_ret_t eth_ledger_sign(void* p_data, in3_plugin_act_t action, void* p_ctx) { memcpy(hash, sc->message.data, sc->message.len); is_hashed = true; case SIGN_EC_HASH: + case SIGN_EC_PREFIX: if (!is_hashed) hasher_Raw(HASHER_SHA3K, sc->message.data, sc->message.len, hash); diff --git a/c/src/signer/multisig/multisig.c b/c/src/signer/multisig/multisig.c index 257aa4a47..d9af76aab 100644 --- a/c/src/signer/multisig/multisig.c +++ b/c/src/signer/multisig/multisig.c @@ -174,7 +174,7 @@ in3_ret_t get_tx_hash(in3_req_t* ctx, multisig_t* ms, tx_data_t* tx_data, bytes3 long_to_bytes(nonce, raw + 4 + 9 * 32 + 24); TRY(call(ctx, ms->address, bytes(raw, size), &rpc_result)) - if (rpc_result->len != 32) return req_set_error(ctx, "invalid getTransactionHash result!", IN3_EINVAL); + if (!rpc_result || rpc_result->len != 32) return req_set_error(ctx, "invalid getTransactionHash result!", IN3_EINVAL); memcpy(result, rpc_result->data, 32); return IN3_OK; } @@ -445,6 +445,7 @@ in3_ret_t gs_create_contract_signature(multisig_t* ms, in3_sign_ctx_t* ctx) { else memcpy(hash, ctx->message.data, 32); break; + case SIGN_EC_PREFIX: case SIGN_EC_HASH: { //do we know the domain_seperator? if (memiszero(ms->domain_sep, 32)) { @@ -459,7 +460,18 @@ in3_ret_t gs_create_contract_signature(multisig_t* ms, in3_sign_ctx_t* ctx) { // calculate the message hash according to the GnosisSafe getMessageHash - function. uint8_t tmp[66]; memcpy(tmp, "\x60\xb3\xcb\xf8\xb4\xa2\x23\xd6\x8d\x64\x1b\x3b\x6d\xdf\x9a\x29\x8e\x7f\x33\x71\x0c\xf3\xd3\xa9\xd1\x14\x6b\x5a\x61\x50\xfb\xca", 32); // SAFE_MSG_TYPEHASH - keccak(ctx->message, hash); + + struct SHA3_CTX kctx; + sha3_256_Init(&kctx); + if (ctx->type == SIGN_EC_PREFIX) { + const char* PREFIX = "\x19" + "Ethereum Signed Message:\n"; + sha3_Update(&kctx, (uint8_t*) PREFIX, strlen(PREFIX)); + sha3_Update(&kctx, hash, sprintf((char*) hash, "%d", (int) ctx->message.len)); + } + if (ctx->message.len) sha3_Update(&kctx, ctx->message.data, ctx->message.len); + keccak_Final(&kctx, hash); + if (ms->type == MS_IAMO_SAFE) keccak(bytes(hash, 32), tmp + 32); else diff --git a/c/src/signer/pk-signer/CMakeLists.txt b/c/src/signer/pk-signer/CMakeLists.txt index 73d80492b..2f3cb6b3a 100644 --- a/c/src/signer/pk-signer/CMakeLists.txt +++ b/c/src/signer/pk-signer/CMakeLists.txt @@ -35,7 +35,8 @@ add_static_library( NAME pk_signer - + REGISTER eth_register_pk_signer + SOURCES signer.c diff --git a/c/src/signer/pk-signer/rpc.yml b/c/src/signer/pk-signer/rpc.yml new file mode 100644 index 000000000..162a25c05 --- /dev/null +++ b/c/src/signer/pk-signer/rpc.yml @@ -0,0 +1,54 @@ +account: + + # config + config: + + key: + type: bytes32 + descr: the client key to sign requests. (only availble if build with `-DPK_SIGNER=true` , which is on per default) + example: "0xc9564409cbfca3f486a07996e8015124f30ff8331fc6dcbd610a050f1f983afe" + optional: true + cmd: k + + pk: + type: bytes32|bytes32[] + descr: registers raw private keys as signers for transactions. (only availble if build with `-DPK_SIGNER=true` , which is on per default) + example: ["0xc9564409cbfca3f486a07996e8015124f30ff8331fc6dcbd610a050f1f983afe"] + optional: true + cmd: pk + + # RPC + in3_addRawKey: + sync: true + descr: adds a raw private key as signer, which allows signing transactions. + params: + pk: + descr: the 32byte long private key as hex string. + type: bytes32 + result: + descr: the address of given key. + type: address + example: + request: + - "0x1234567890123456789012345678901234567890123456789012345678901234" + response: "0x2e988a386a799f506693793c6a5af6b54dfaabfb" + + eth_accounts: + sync: true + descr: | + returns a array of account-addresss the incubed client is able to sign with. + + In order to add keys, you can use [in3_addRawKey](#in3-addrawkey) or configure them in the config. The result also contains the addresses of any signer signer-supporting the `PLGN_ACT_SIGN_ACCOUNT` action. + result: + descr: the array of addresses of all registered signers. + array: true + type: address + example: + response: + - "0x2e988a386a799f506693793c6a5af6b54dfaabfb" + - "0x93793c6a5af6b54dfaabfb2e988a386a799f5066" + + + + + diff --git a/c/src/signer/pk-signer/signer.c b/c/src/signer/pk-signer/signer.c index e02383b6b..074772887 100644 --- a/c/src/signer/pk-signer/signer.c +++ b/c/src/signer/pk-signer/signer.c @@ -36,6 +36,7 @@ #include "../../core/client/keys.h" #include "../../core/client/plugin.h" #include "../../core/client/request_internal.h" +#include "../../core/util/debug.h" #include "../../core/util/mem.h" #include "../../core/util/utils.h" #include "../../third-party/crypto/ecdsa.h" @@ -73,9 +74,12 @@ static bool add_key(in3_t* c, bytes32_t pk) { address_t address; get_address(pk, address); in3_sign_account_ctx_t ctx = {0}; + in3_req_t r = {0}; + ctx.req = &r; + r.client = c; for (in3_plugin_t* p = c->plugins; p; p = p->next) { - if (p->acts & (PLGN_ACT_SIGN_ACCOUNT | PLGN_ACT_SIGN) && p->action_fn(p->data, PLGN_ACT_SIGN_ACCOUNT, &ctx) == IN3_OK && ctx.accounts_len) { + if ((p->acts & PLGN_ACT_SIGN_ACCOUNT) && (p->acts & PLGN_ACT_SIGN) && p->action_fn(p->data, PLGN_ACT_SIGN_ACCOUNT, &ctx) == IN3_OK && ctx.accounts_len) { bool is_same_address = memcmp(ctx.accounts, address, 20) == 0; _free(ctx.accounts); if (is_same_address) return false; @@ -96,6 +100,19 @@ static in3_ret_t eth_sign_pk(void* data, in3_plugin_act_t action, void* action_c switch (ctx->type) { case SIGN_EC_RAW: return ec_sign_pk_raw(ctx->message.data, k->pk, ctx->signature.data); + + case SIGN_EC_PREFIX: { + bytes32_t hash; + struct SHA3_CTX kctx; + sha3_256_Init(&kctx); + const char* PREFIX = "\x19" + "Ethereum Signed Message:\n"; + sha3_Update(&kctx, (uint8_t*) PREFIX, strlen(PREFIX)); + sha3_Update(&kctx, hash, sprintf((char*) hash, "%d", (int) ctx->message.len)); + if (ctx->message.len) sha3_Update(&kctx, ctx->message.data, ctx->message.len); + keccak_Final(&kctx, hash); + return ec_sign_pk_raw(hash, k->pk, ctx->signature.data); + } case SIGN_EC_HASH: return ec_sign_pk_hash(ctx->message.data, ctx->message.len, k->pk, hasher_sha3k, ctx->signature.data); default: @@ -132,13 +149,42 @@ in3_ret_t eth_set_pk_signer(in3_t* in3, bytes32_t pk) { return in3_plugin_register(in3, PLGN_ACT_SIGN_ACCOUNT | PLGN_ACT_SIGN | PLGN_ACT_TERM, eth_sign_pk, k, false); } +static in3_ret_t add_raw_key(in3_rpc_handle_ctx_t* ctx) { + if (d_len(ctx->params) != 1 || d_type(ctx->params + 1) != T_BYTES || d_len(ctx->params + 1) != 32) + return req_set_error(ctx->req, "one argument with 32 bytes is required!", IN3_EINVAL); + address_t adr; + get_address(d_bytes(ctx->params + 1)->data, adr); + add_key(ctx->req->client, d_bytes(ctx->params + 1)->data); + return in3_rpc_handle_with_bytes(ctx, bytes(adr, 20)); +} + +static in3_ret_t eth_accounts(in3_rpc_handle_ctx_t* ctx) { + sb_t* sb = in3_rpc_handle_start(ctx); + bool first = true; + in3_sign_account_ctx_t sc = {.req = ctx->req, .accounts = NULL, .accounts_len = 0, .signer_type = 0}; + for (in3_plugin_t* p = ctx->req->client->plugins; p; p = p->next) { + if (p->acts & PLGN_ACT_SIGN_ACCOUNT && p->action_fn(p->data, PLGN_ACT_SIGN_ACCOUNT, &sc) == IN3_OK) { + for (int i = 0; i < sc.accounts_len; i++) { + sb_add_rawbytes(sb, first ? "[\"0x" : "\",\"0x", bytes(sc.accounts + i * 20, 20), 20); + first = false; + } + if (sc.accounts) { + _free(sc.accounts); + sc.accounts_len = 0; + } + } + } + sb_add_chars(sb, first ? "[]" : "\"]"); + return in3_rpc_handle_finish(ctx); +} + // RPC-Handler static in3_ret_t pk_rpc(void* data, in3_plugin_act_t action, void* action_ctx) { UNUSED_VAR(data); switch (action) { case PLGN_ACT_CONFIG_SET: { in3_configure_ctx_t* ctx = action_ctx; - if (ctx->token->key == key("key")) { + if (ctx->token->key == CONFIG_KEY("key")) { if (d_type(ctx->token) != T_BYTES || d_len(ctx->token) != 32) { ctx->error_msg = _strdupn("invalid key-length, must be 32", -1); return IN3_EINVAL; @@ -146,7 +192,7 @@ static in3_ret_t pk_rpc(void* data, in3_plugin_act_t action, void* action_ctx) { eth_set_request_signer(ctx->client, ctx->token->data); return IN3_OK; } - if (ctx->token->key == key("pk")) { + if (ctx->token->key == CONFIG_KEY("pk")) { if (d_type(ctx->token) == T_BYTES) { if (d_len(ctx->token) != 32) { ctx->error_msg = _strdupn("invalid key-length, must be 32", -1); @@ -175,33 +221,8 @@ static in3_ret_t pk_rpc(void* data, in3_plugin_act_t action, void* action_ctx) { case PLGN_ACT_RPC_HANDLE: { in3_rpc_handle_ctx_t* ctx = action_ctx; - if (strcmp(ctx->method, "in3_addRawKey") == 0) { - if (d_len(ctx->params) != 1 || d_type(ctx->params + 1) != T_BYTES || d_len(ctx->params + 1) != 32) - return req_set_error(ctx->req, "one argument with 32 bytes is required!", IN3_EINVAL); - address_t adr; - get_address(d_bytes(ctx->params + 1)->data, adr); - add_key(ctx->req->client, d_bytes(ctx->params + 1)->data); - return in3_rpc_handle_with_bytes(ctx, bytes(adr, 20)); - } - if (strcmp(ctx->method, "eth_accounts") == 0) { - sb_t* sb = in3_rpc_handle_start(ctx); - bool first = true; - in3_sign_account_ctx_t sc = {.req = ctx->req, .accounts = NULL, .accounts_len = 0, .signer_type = 0}; - for (in3_plugin_t* p = ctx->req->client->plugins; p; p = p->next) { - if (p->acts & PLGN_ACT_SIGN_ACCOUNT && p->action_fn(p->data, PLGN_ACT_SIGN_ACCOUNT, &sc) == IN3_OK) { - for (int i = 0; i < sc.accounts_len; i++) { - sb_add_rawbytes(sb, first ? "[\"0x" : "\",\"0x", bytes(sc.accounts + i * 20, 20), 20); - first = false; - } - if (sc.accounts) { - _free(sc.accounts); - sc.accounts_len = 0; - } - } - } - sb_add_chars(sb, first ? "[]" : "\"]"); - return in3_rpc_handle_finish(ctx); - } + TRY_RPC("in3_addRawKey", add_raw_key(ctx)) + TRY_RPC("eth_accounts", eth_accounts(ctx)) return IN3_EIGNORE; } diff --git a/c/src/third-party/tommath/CMakeLists.txt b/c/src/third-party/tommath/CMakeLists.txt index 04dbcd004..458cf3985 100644 --- a/c/src/third-party/tommath/CMakeLists.txt +++ b/c/src/third-party/tommath/CMakeLists.txt @@ -262,6 +262,11 @@ if (IN3API) set(SRC ${SRC} bn_mp_read_radix.c bn_mp_add_d.c bn_mp_sub_d.c bn_mp_radix_smap.c ) endif() +if (SWIFT) + ADD_DEFINITIONS(-DBN_MP_TORADIX_C -DBN_MP_RADIX_SIZE_C -DBN_MP_GET_DOUBLE_C -DBN_MP_GET_INT_C -DBN_MP_GET_LONG_LONG_C -DBN_MP_INIT_SET_C) + set(SRC ${SRC} bn_mp_toradix.c bn_mp_radix_size.c bn_mp_div_d.c bn_mp_get_double.c bn_mp_get_int.c bn_mp_get_long_long.c bn_mp_get_long.c bn_mp_init_set.c) +endif(SWIFT) + add_static_library( NAME tommath diff --git a/c/src/third-party/zkcrypto/rust/Cargo.toml b/c/src/third-party/zkcrypto/rust/Cargo.toml index 487e09602..76ed13a46 100644 --- a/c/src/third-party/zkcrypto/rust/Cargo.toml +++ b/c/src/third-party/zkcrypto/rust/Cargo.toml @@ -6,6 +6,10 @@ version = "0.1.0" authors = ["Matter Labs Team "] edition = "2018" +[net] +retry = 2 # network retries +git-fetch-with-cli = true # use the `git` executable for git operations + [lib] crate-type = ["cdylib", "rlib", "staticlib"] @@ -40,5 +44,4 @@ wasm-bindgen-test = "0.3.10" byteorder = "1.3.4" [profile.release] -# Tell `rustc` to optimize for small code size. opt-level = "s" diff --git a/c/src/third-party/zkcrypto/wasm/lib.c b/c/src/third-party/zkcrypto/wasm/lib.c index 6325b5844..e7122ca34 100644 --- a/c/src/third-party/zkcrypto/wasm/lib.c +++ b/c/src/third-party/zkcrypto/wasm/lib.c @@ -20,8 +20,6 @@ void (*Z___wbindgen_placeholder__Z___wbg_error_4bb6c2a97407129aZ_vii)(u32, u32); /* import: './web.js' '__wbindgen_object_drop_ref' */ void (*Z___wbindgen_placeholder__Z___wbindgen_object_drop_refZ_vi)(u32); - - /* import: '__wbindgen_placeholder__' '__wbindgen_string_new' */ u32 (*Z___wbindgen_placeholder__Z___wbindgen_string_newZ_iii)(u32, u32); /* import: '__wbindgen_placeholder__' '__wbindgen_throw' */ @@ -32,8 +30,6 @@ void (*Z___wbindgen_placeholder__Z___wbindgen_rethrowZ_vi)(u32); /* import: '__wbindgen_placeholder__' '__wbindgen_debug_string' */ void (*Z___wbindgen_placeholder__Z___wbindgen_debug_stringZ_vii)(u32, u32); - - #define wmalloc(l) zkcrypto_Z___wbindgen_mallocZ_ii(l) #define wfree(p, l) zkcrypto_Z___wbindgen_freeZ_vii(p, l) #define mem_ptr(p) (zkcrypto_Z_memory->data + (p)) @@ -75,23 +71,21 @@ void zke_drop(u32 a) { /* import: '__wbindgen_placeholder__' '__wbindgen_string_new' */ u32 zke_string_new(u32 a, u32 b) { printf("# zke_string_new\n"); - u32 sp = wmalloc(b+1); - memcpy(mem_ptr(sp),mem_ptr(a),b+1); - char* s = (void*)mem_ptr(sp); - s[b]=0; + u32 sp = wmalloc(b + 1); + memcpy(mem_ptr(sp), mem_ptr(a), b + 1); + char* s = (void*) mem_ptr(sp); + s[b] = 0; return sp; } /* import: '__wbindgen_placeholder__' '__wbindgen_throw' */ void zke_throw(u32 a, u32 b) { if (a || b) - printf("# zke_throw\n"); - + printf("# zke_throw\n"); } /* import: '__wbindgen_placeholder__' '__wbindgen_rethrow' */ void zke_rethrow(u32 x) { if (x) - printf("# zke_rethrow\n"); - + printf("# zke_rethrow\n"); } void zkcrypto_initialize() { @@ -107,17 +101,15 @@ void zkcrypto_initialize() { Z___wbindgen_placeholder__Z___wbindgen_object_drop_refZ_vi = zke_drop; /* import: '__wbindgen_placeholder__' '__wbindgen_string_new' */ - Z___wbindgen_placeholder__Z___wbindgen_string_newZ_iii= zke_string_new; + Z___wbindgen_placeholder__Z___wbindgen_string_newZ_iii = zke_string_new; /* import: '__wbindgen_placeholder__' '__wbindgen_throw' */ Z___wbindgen_placeholder__Z___wbindgen_throwZ_vii = zke_throw; /* import: '__wbindgen_placeholder__' '__wbindgen_rethrow' */ Z___wbindgen_placeholder__Z___wbindgen_rethrowZ_vi = zke_rethrow; /* import: '__wbindgen_placeholder__' '__wbindgen_debug_string' */ Z___wbindgen_placeholder__Z___wbindgen_debug_stringZ_vii = zke_debug_string; - } - in3_ret_t zkcrypto_pk_from_seed(bytes_t seed, bytes32_t dst) { u32 sp = wmalloc(seed.len); memcpy(mem_ptr(sp), seed.data, seed.len); @@ -150,7 +142,6 @@ in3_ret_t zkcrypto_pk_to_pubkey_hash(bytes32_t pk, uint8_t* dst) { return r1 == 20 ? IN3_OK : IN3_EINVAL; } - in3_ret_t zkcrypto_sign_musig(bytes32_t pk, bytes_t msg, uint8_t* dst) { u32 pkp = wmalloc(32); u32 mp = wmalloc(msg.len); @@ -182,13 +173,13 @@ zkcrypto_signer_t zkcrypto_signer_new(bytes_t pub_keys, uint32_t pos) { } void zkcrypto_signer_free(zkcrypto_signer_t signer) { - return zkcrypto_Z___wbg_musigbn256wasmsigner_freeZ_vi((u32) signer); + return zkcrypto_Z___wbg_musigbn256wasmsigner_freeZ_vi((u32)(uint64_t) signer); } in3_ret_t zkcrypto_signer_compute_precommitment(zkcrypto_signer_t signer, bytes_t seed, uint8_t* dst) { u32 data = wmalloc(seed.len); memcpy(mem_ptr(data), seed.data, seed.len); - zkcrypto_Z_musigbn256wasmsigner_compute_precommitmentZ_viiii(8, (u32) signer, data, seed.len/4); + zkcrypto_Z_musigbn256wasmsigner_compute_precommitmentZ_viiii(8, (u32)(uint64_t) signer, data, seed.len / 4); u32 r0 = mem_u32(2); u32 r1 = mem_u32(3); if (r1 == 32) memcpy(dst, mem_ptr(r0), r1); @@ -196,11 +187,10 @@ in3_ret_t zkcrypto_signer_compute_precommitment(zkcrypto_signer_t signer, bytes_ return r1 == 32 ? IN3_OK : IN3_EINVAL; } - in3_ret_t zkcrypto_signer_receive_precommitment(zkcrypto_signer_t signer, bytes_t input, uint8_t* dst) { u32 data = wmalloc(input.len); memcpy(mem_ptr(data), input.data, input.len); - zkcrypto_Z_musigbn256wasmsigner_receive_precommitmentsZ_viiii(8, (u32) signer, data, input.len); + zkcrypto_Z_musigbn256wasmsigner_receive_precommitmentsZ_viiii(8, (u32)(uint64_t) signer, data, input.len); u32 r0 = mem_u32(2); u32 r1 = mem_u32(3); if (r1 == 32) memcpy(dst, mem_ptr(r0), r1); @@ -208,11 +198,10 @@ in3_ret_t zkcrypto_signer_receive_precommitment(zkcrypto_signer_t signer, bytes_ return r1 == 32 ? IN3_OK : IN3_EINVAL; } - in3_ret_t zkcrypto_signer_receive_commitment(zkcrypto_signer_t signer, bytes_t input, uint8_t* dst) { u32 data = wmalloc(input.len); memcpy(mem_ptr(data), input.data, input.len); - zkcrypto_Z_musigbn256wasmsigner_receive_commitmentsZ_viiii(8, (u32) signer, data, input.len); + zkcrypto_Z_musigbn256wasmsigner_receive_commitmentsZ_viiii(8, (u32)(uint64_t) signer, data, input.len); u32 r0 = mem_u32(2); u32 r1 = mem_u32(3); if (r1 == 32) memcpy(dst, mem_ptr(r0), r1); @@ -220,30 +209,28 @@ in3_ret_t zkcrypto_signer_receive_commitment(zkcrypto_signer_t signer, bytes_t i return r1 == 32 ? IN3_OK : IN3_EINVAL; } - - in3_ret_t zkcrypto_signer_sign(zkcrypto_signer_t signer, bytes32_t pk, bytes_t input, uint8_t* dst) { u32 data = wmalloc(input.len); memcpy(mem_ptr(data), input.data, input.len); u32 pkey = wmalloc(32); memcpy(mem_ptr(pkey), pk, 32); - zkcrypto_Z_musigbn256wasmsigner_signZ_viiiiii(8, (u32) signer, pkey, 32,data, input.len); + zkcrypto_Z_musigbn256wasmsigner_signZ_viiiiii(8, (u32)(uint64_t) signer, pkey, 32, data, input.len); u32 r0 = mem_u32(2); u32 r1 = mem_u32(3); - if (r1==32) memcpy(dst, mem_ptr(r0), r1); + if (r1 == 32) memcpy(dst, mem_ptr(r0), r1); wfree(r0, r1); - return r1 ==32 ? IN3_OK : IN3_EINVAL; + return r1 == 32 ? IN3_OK : IN3_EINVAL; } -in3_ret_t zkcrypto_signer_receive_signature_shares(zkcrypto_signer_t signer, bytes_t input, uint8_t* dst) { +in3_ret_t zkcrypto_signer_receive_signature_shares(zkcrypto_signer_t signer, bytes_t input, uint8_t* dst) { u32 data = wmalloc(input.len); memcpy(mem_ptr(data), input.data, input.len); - zkcrypto_Z_musigbn256wasmsigner_receive_signature_sharesZ_viiii(8, (u32) signer, data, input.len); + zkcrypto_Z_musigbn256wasmsigner_receive_signature_sharesZ_viiii(8, (u32)(uint64_t) signer, data, input.len); u32 r0 = mem_u32(2); u32 r1 = mem_u32(3); - if (r1==64) memcpy(dst, mem_ptr(r0), r1); + if (r1 == 64) memcpy(dst, mem_ptr(r0), r1); wfree(r0, r1); - return r1==64 ? IN3_OK : IN3_EINVAL; + return r1 == 64 ? IN3_OK : IN3_EINVAL; } bool zkcrypto_verify_signatures(bytes_t message, bytes_t pubkeys, bytes_t signature) { @@ -253,10 +240,9 @@ bool zkcrypto_verify_signatures(bytes_t message, bytes_t pubkeys, bytes_t signat memcpy(mem_ptr(_pubkeys), pubkeys.data, pubkeys.len); u32 _signature = wmalloc(signature.len); memcpy(mem_ptr(_signature), signature.data, signature.len); - return zkcrypto_Z_musigbn256wasmverifier_verifyZ_iiiiiii(_message, message.len, _pubkeys,pubkeys.len, _signature,signature.len)!=0; + return zkcrypto_Z_musigbn256wasmverifier_verifyZ_iiiiiii(_message, message.len, _pubkeys, pubkeys.len, _signature, signature.len) != 0; } - in3_ret_t zkcrypto_pubkey_hash(bytes_t pubkey, uint8_t* dst) { u32 pk = wmalloc(pubkey.len); memcpy(mem_ptr(pk), pubkey.data, pubkey.len); @@ -268,13 +254,10 @@ in3_ret_t zkcrypto_pubkey_hash(bytes_t pubkey, uint8_t* dst) { return r1 == 20 ? IN3_OK : IN3_EINVAL; } - - -bool zkcrypto_verify_musig(bytes_t message, bytes_t signature) { +bool zkcrypto_verify_musig(bytes_t message, bytes_t signature) { u32 _message = wmalloc(message.len); memcpy(mem_ptr(_message), message.data, message.len); u32 _signature = wmalloc(signature.len); memcpy(mem_ptr(_signature), signature.data, signature.len); - return zkcrypto_Z_verify_musigZ_iiiii(_message, message.len, _signature,signature.len)!=0; + return zkcrypto_Z_verify_musigZ_iiiii(_message, message.len, _signature, signature.len) != 0; } - diff --git a/c/src/third-party/zkcrypto/wasm/testzc.c b/c/src/third-party/zkcrypto/wasm/testzc.c deleted file mode 100644 index 4d041f25f..000000000 --- a/c/src/third-party/zkcrypto/wasm/testzc.c +++ /dev/null @@ -1,19 +0,0 @@ -#include -#include - -#include "lib.h" -void hex(bytes_t b) { - printf("0x"); - for (int i = 0; i < b.len; i++) printf("%02x", b.data[i]); - printf("\n"); -} - -int main(int argc, char** argv) { - char* msg = "abcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklmabcdefghijklm"; - bytes32_t p; - zkcrypto_initialize(); - zkcrypto_pk_from_seed(bytes((void*) msg, 32), p); - hex(bytes(p, 32)); - - return 0; -} \ No newline at end of file diff --git a/c/src/tools/CMakeLists.txt b/c/src/tools/CMakeLists.txt index 300dd8bdd..16a6f8319 100644 --- a/c/src/tools/CMakeLists.txt +++ b/c/src/tools/CMakeLists.txt @@ -40,6 +40,10 @@ if(RECORDER) add_subdirectory(recorder) endif() +if(SWIFT) + add_subdirectory(swift) +endif() + if(PLGN_CLIENT_DATA) add_subdirectory(clientdata) endif() diff --git a/c/src/tools/clientdata/CMakeLists.txt b/c/src/tools/clientdata/CMakeLists.txt index a17418a56..ff84ed2fe 100644 --- a/c/src/tools/clientdata/CMakeLists.txt +++ b/c/src/tools/clientdata/CMakeLists.txt @@ -33,8 +33,7 @@ ############################################################################### add_static_library( - NAME - plugin_client_data + NAME plugin_client_data SOURCES client_data.c diff --git a/c/src/tools/recorder/recorder.c b/c/src/tools/recorder/recorder.c index 4cf7ee0af..c83102352 100644 --- a/c/src/tools/recorder/recorder.c +++ b/c/src/tools/recorder/recorder.c @@ -282,7 +282,7 @@ void recorder_read_start(in3_t* c, char* file) { void recorder_update_cmd(char* file, int* argc, char** argv[]) { rec.f = fopen(file, "r"); if (!rec.f) { - fprintf(stderr, "Cannot open %s : %s\n", file, strerror((int) errno)); + fprintf(stderr, "Cannot open recordfile %s : %s\n", file, strerror((int) errno)); exit(EXIT_FAILURE); } recorder_entry_t* entry = next_entry("cmd", NULL); diff --git a/c/src/tools/sentry/CMakeLists.txt b/c/src/tools/sentry/CMakeLists.txt index d6ece783d..6fa78a6aa 100644 --- a/c/src/tools/sentry/CMakeLists.txt +++ b/c/src/tools/sentry/CMakeLists.txt @@ -35,6 +35,7 @@ add_static_library( NAME in3_sentry + REGISTER in3_register_sentry SOURCES sentry.c DEPENDS diff --git a/c/src/tools/swift/CMakeLists.txt b/c/src/tools/swift/CMakeLists.txt new file mode 100644 index 000000000..508c6cd4f --- /dev/null +++ b/c/src/tools/swift/CMakeLists.txt @@ -0,0 +1,43 @@ +############################################################################### +# This file is part of the Incubed project. +# Sources: https://github.com/slockit/in3-c +# +# Copyright (C) 2018-2019 slock.it GmbH, Blockchains LLC +# +# +# COMMERCIAL LICENSE USAGE +# +# Licensees holding a valid commercial license may use this file in accordance +# with the commercial license agreement provided with the Software or, alternatively, +# in accordance with the terms contained in a written agreement between you and +# slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further +# information please contact slock.it at in3@slock.it. +# +# Alternatively, this file may be used under the AGPL license as follows: +# +# AGPL LICENSE USAGE +# +# This program is free software: you can redistribute it and/or modify it under the +# terms of the GNU Affero General Public License as published by the Free Software +# Foundation, either version 3 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. +# [Permissions of this strong copyleft license are conditioned on making available +# complete source code of licensed works and modifications, which include larger +# works using a licensed work, under the same license. Copyright and license notices +# must be preserved. Contributors provide an express grant of patent rights.] +# You should have received a copy of the GNU Affero General Public License along +# with this program. If not, see . +############################################################################### + +add_static_library( + NAME in3_swift + + SOURCES + swift.c + + DEPENDS + core +) diff --git a/c/src/tools/swift/swift.c b/c/src/tools/swift/swift.c new file mode 100644 index 000000000..9ec093f0d --- /dev/null +++ b/c/src/tools/swift/swift.c @@ -0,0 +1,31 @@ + +#include "swift.h" + +static in3_ret_t handle(void* plugin_data, in3_plugin_act_t action, void* plugin_ctx) { + swift_cb_t* conf = plugin_data; + switch (action) { + case PLGN_ACT_TERM: + _free(plugin_data); + return IN3_OK; + case PLGN_ACT_CACHE_GET: { + in3_cache_ctx_t* _Nonnull ctx = plugin_ctx; + return conf->cache_get(ctx); + } + case PLGN_ACT_CACHE_SET: { + in3_cache_ctx_t* _Nonnull ctx = plugin_ctx; + return conf->cache_set(ctx); + } + case PLGN_ACT_CACHE_CLEAR: + return conf->cache_clear(); + + default: + return IN3_ENOTSUP; + } + return IN3_EIGNORE; +} + +in3_ret_t in3_register_swift(in3_t* c, swift_cb_t* cbs) { + swift_cb_t* ptr = _malloc(sizeof(swift_cb_t)); + memcpy(ptr, cbs, sizeof(swift_cb_t)); + return in3_plugin_register(c, PLGN_ACT_CACHE_GET | PLGN_ACT_CACHE_SET | PLGN_ACT_CACHE_CLEAR | PLGN_ACT_TERM, handle, ptr, true); +} \ No newline at end of file diff --git a/c/src/tools/swift/swift.h b/c/src/tools/swift/swift.h new file mode 100644 index 000000000..92a525b11 --- /dev/null +++ b/c/src/tools/swift/swift.h @@ -0,0 +1,51 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/slockit/in3-c + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ +#ifndef IN3_SWIFT_H +#define IN3_SWIFT_H +#ifdef __clang__ +#define _NONULL _Nonnull +#else +#define _NONULL +#endif +#include "../../core/client/plugin.h" + +typedef struct in3_swift_cb { + in3_ret_t (*_NONULL cache_get)(in3_cache_ctx_t* _Nonnull ctx); + in3_ret_t (*_NONULL cache_set)(in3_cache_ctx_t* _Nonnull ctx); + in3_ret_t (*_NONULL cache_clear)(); +} swift_cb_t; + +in3_ret_t in3_register_swift(in3_t* _NONULL c, swift_cb_t* _NONULL cbs); + +#endif //IN3_SWIFT_H diff --git a/c/src/transport/curl/CMakeLists.txt b/c/src/transport/curl/CMakeLists.txt index 60069bea5..8ddc9b1d8 100644 --- a/c/src/transport/curl/CMakeLists.txt +++ b/c/src/transport/curl/CMakeLists.txt @@ -100,7 +100,8 @@ endif () # add lib add_static_library( NAME transport_curl - + REGISTER in3_register_curl + SOURCES in3_curl.c diff --git a/c/src/transport/http/CMakeLists.txt b/c/src/transport/http/CMakeLists.txt index 9f0c32189..34b142fa9 100644 --- a/c/src/transport/http/CMakeLists.txt +++ b/c/src/transport/http/CMakeLists.txt @@ -35,7 +35,8 @@ # add lib add_static_library( NAME transport_http - + REGISTER in3_register_http + SOURCES in3_http.c diff --git a/c/src/transport/winhttp/CMakeLists.txt b/c/src/transport/winhttp/CMakeLists.txt index a166d33e3..680dafd9c 100644 --- a/c/src/transport/winhttp/CMakeLists.txt +++ b/c/src/transport/winhttp/CMakeLists.txt @@ -35,7 +35,8 @@ # add lib add_static_library( NAME transport_winhttp - + REGISTER in3_register_winhttp + SOURCES in3_winhttp.c diff --git a/c/src/verifier/btc/CMakeLists.txt b/c/src/verifier/btc/CMakeLists.txt index 5193471f8..381b53572 100644 --- a/c/src/verifier/btc/CMakeLists.txt +++ b/c/src/verifier/btc/CMakeLists.txt @@ -34,7 +34,8 @@ add_static_library( NAME btc - + REGISTER in3_register_btc + SOURCES btc_merkle.c btc_types.c diff --git a/c/src/verifier/btc/btc.c b/c/src/verifier/btc/btc.c index 1871b294e..c6fb1ab8d 100644 --- a/c/src/verifier/btc/btc.c +++ b/c/src/verifier/btc/btc.c @@ -2,6 +2,7 @@ #include "../../core/client/keys.h" #include "../../core/client/plugin.h" #include "../../core/client/request_internal.h" +#include "../../core/util/debug.h" #include "../../core/util/mem.h" #include "../../core/util/utils.h" #include "../../verifier/eth1/nano/eth_nano.h" @@ -444,7 +445,7 @@ static in3_ret_t in3_verify_btc(btc_target_conf_t* conf, in3_vctx_t* vc) { d_token_t* params = d_get(vc->request, K_PARAMS); bytes32_t hash; - if (strcmp(vc->method, "getblock") == 0) { + if (VERIFY_RPC("getblock")) { // mark zksync as experimental REQUIRE_EXPERIMENTAL(vc->req, "btc") @@ -453,22 +454,22 @@ static in3_ret_t in3_verify_btc(btc_target_conf_t* conf, in3_vctx_t* vc) { hex_to_bytes(d_string(block_hash), 64, hash, 32); return btc_verify_block(conf, vc, hash, d_len(params) > 1 ? d_get_int_at(params, 1) : 1, true); } - if (strcmp(vc->method, "getblockcount") == 0) { + if (VERIFY_RPC("getblockcount")) { REQUIRE_EXPERIMENTAL(vc->req, "btc") return btc_verify_blockcount(conf, vc); } - if (strcmp(vc->method, "getblockheader") == 0) { + if (VERIFY_RPC("getblockheader")) { REQUIRE_EXPERIMENTAL(vc->req, "btc") d_token_t* block_hash = d_get_at(params, 0); if (d_len(params) < 1 || d_type(params) != T_ARRAY || d_type(block_hash) != T_STRING || d_len(block_hash) != 64) return vc_err(vc, "Invalid blockhash"); hex_to_bytes(d_string(block_hash), 64, hash, 32); return btc_verify_block(conf, vc, hash, d_len(params) > 1 ? d_get_int_at(params, 1) : 1, false); } - if (strcmp(vc->method, "btc_proofTarget") == 0) { + if (VERIFY_RPC("btc_proofTarget")) { REQUIRE_EXPERIMENTAL(vc->req, "btc") return btc_verify_target_proof(conf, vc, params); } - if (strcmp(vc->method, "getrawtransaction") == 0) { + if (VERIFY_RPC("getrawtransaction")) { REQUIRE_EXPERIMENTAL(vc->req, "btc") d_token_t* tx_id = d_get_at(params, 0); bool json = d_len(params) < 2 ? d_type(vc->result) == T_OBJECT : d_get_int_at(params, 1); @@ -500,9 +501,9 @@ static in3_ret_t handle_btc(void* pdata, in3_plugin_act_t action, void* pctx) { } case PLGN_ACT_CONFIG_SET: { in3_configure_ctx_t* cctx = pctx; - if (cctx->token->key == key("maxDAP")) + if (cctx->token->key == CONFIG_KEY("maxDAP")) conf->max_daps = d_int(cctx->token); - else if (cctx->token->key == key("maxDiff")) + else if (cctx->token->key == CONFIG_KEY("maxDiff")) conf->max_diff = d_int(cctx->token); else return IN3_EIGNORE; diff --git a/c/src/verifier/btc/btc_target.c b/c/src/verifier/btc/btc_target.c index b91367ee3..076719990 100644 --- a/c/src/verifier/btc/btc_target.c +++ b/c/src/verifier/btc/btc_target.c @@ -173,7 +173,7 @@ in3_ret_t btc_check_target(btc_target_conf_t* tc, in3_vctx_t* vc, uint32_t block if (block_number < BIP34_START) return IN3_OK; // for pre bip34, this finalityheader already checked it // is there a required ctx, which we need to clean up? - in3_req_t* ctx = req_find_required(vc->req, "btc_proofTarget"); // do we have an existing required proofTarget-request? + in3_req_t* ctx = req_find_required(vc->req, "btc_proofTarget", NULL); // do we have an existing required proofTarget-request? if (ctx) // yes, we do! switch (in3_req_state(ctx)) { // but what is the state? case REQ_ERROR: // there was an error, diff --git a/c/src/verifier/btc/rpc.yml b/c/src/verifier/btc/rpc.yml new file mode 100644 index 000000000..74dd4cb02 --- /dev/null +++ b/c/src/verifier/btc/rpc.yml @@ -0,0 +1,917 @@ +types: + btcblockheader: + hash: + descr: the block hash (same as provided) + type: bytes32 + confirmations: + descr: The number of confirmations, or -1 if the block is not on the main chain + type: int + height: + descr: The block height or index + type: uint64 + version: + descr: The block version + type: int + versionHex: + descr: The block version formatted in hexadecimal + type: hex + merkleroot: + descr: The merkle root ( 32 bytes ) + type: bytes32 + time: + descr: The block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + mediantime: + descr: The median block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + nonce: + descr: The nonce + type: uint64 + bits: + descr: The bits ( 4 bytes as hex) representing the target + type: bytes4 + difficulty: + descr: The difficulty + type: double + chainwork: + descr: Expected number of hashes required to produce the current chain (in hex) + type: hex + nTx: + descr: The number of transactions in the block. + type: int + previousblockhash: + descr: The hash of the previous block + type: bytes32 + nextblockhash: + descr: The hash of the next block + type: bytes32 + + btctransaction: + txid: + descr: txid + type: bytes32 + in_active_chain: + descr: Whether specified block is in the active chain or not (only present with explicit "blockhash" argument) + type: bool + hex: + descr: The serialized, hex-encoded data for `txid` + type: bytes + hash: + descr: The transaction hash (differs from txid for witness transactions) + type: bytes32 + size: + descr: The serialized transaction size + type: uint64 + vsize: + descr: The virtual transaction size (differs from size for witness transactions) + type: uint64 + weight: + descr: The transaction's weight (between `vsize`\*4-3 and `vsize`\*4) + type: uint64 + version: + descr: The version + type: int + locktime: + descr: The lock time + type: uint64 + vin: + descr: array of json objects of incoming txs to be used + array: true + type: + txid: + descr: the transaction id + type: bytes32 + vout: + descr: the index of the transaction out to be used + type: uint64 + scriptSig: + descr: the script + type: + asm: + descr: the asm-codes + type: string + hex: + descr: hex representation + type: string + sequence: + descr: The script sequence number + type: uint64 + txinwitness: + array: true + descr: hex-encoded witness data (if any) + type: string + vout: + descr: array of json objects describing the tx outputs + array: true + type: + value: + descr: The Value in BTC + type: float + n: + descr: the index + type: int + scriptPubKey: + descr: the script pubkey + type: + asm: + descr: asm + type: string + hex: + descr: hex representation of the script + type: string + reqSigs: + descr: the required signatures + type: int + type: + descr: The type, eg 'pubkeyhash' + type: string + addresses: + descr: Array of address(each representing a bitcoin adress) + array: true + type: string + blockhash: + descr: the block hash + type: bytes32 + confirmations: + descr: The confirmations + type: int + blocktime: + descr: The block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + time: + descr: Same as "blocktime" + type: uint64 + + btcblockWithTx: + hash: + descr: the block hash (same as provided) + type: bytes32 + confirmations: + descr: The number of confirmations, or -1 if the block is not on the main chain + type: int + height: + descr: The block height or index + type: uint64 + version: + descr: The block version + type: int + versionHex: + descr: The block version formatted in hexadecimal + type: hex + merkleroot: + descr: The merkle root ( 32 bytes ) + type: bytes32 + time: + descr: The block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + mediantime: + descr: The median block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + nonce: + descr: The nonce + type: uint64 + bits: + descr: The bits ( 4 bytes as hex) representing the target + type: bytes4 + difficulty: + descr: The difficulty + type: double + chainwork: + descr: Expected number of hashes required to produce the current chain (in hex) + type: hex + nTx: + descr: The number of transactions in the block. + type: int + tx: + descr: the array of transactions either as ids (verbose=1) or full transaction (verbose=2) + array: true + type: btctransaction + previousblockhash: + descr: The hash of the previous block + type: bytes32 + nextblockhash: + descr: The hash of the next block + type: bytes32 + + btcblock: + hash: + descr: the block hash (same as provided) + type: bytes32 + confirmations: + descr: The number of confirmations, or -1 if the block is not on the main chain + type: int + height: + descr: The block height or index + type: uint256 + version: + descr: The block version + type: int + versionHex: + descr: The block version formatted in hexadecimal + type: hex + merkleroot: + descr: The merkle root ( 32 bytes ) + type: bytes32 + time: + descr: The block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + mediantime: + descr: The median block time in seconds since epoch (Jan 1 1970 GMT) + type: uint64 + nonce: + descr: The nonce + type: uint64 + bits: + descr: The bits ( 4 bytes as hex) representing the target + type: bytes4 + difficulty: + descr: The difficulty + type: double + chainwork: + descr: Expected number of hashes required to produce the current chain (in hex) + type: hex + nTx: + descr: The number of transactions in the block. + type: int + tx: + descr: the array of transactions either as ids (verbose=1) or full transaction (verbose=2) + array: true + type: bytes32 + previousblockhash: + descr: The hash of the previous block + type: bytes32 + nextblockhash: + descr: The hash of the next block + type: bytes32 + +btc: + descr: | + *Important: This feature is still experimental and not considered stable yet. In order to use it, you need to set the experimental-flag (-x on the comandline or `"experimental":true`!* + + For bitcoin incubed follows the specification as defined in [https://bitcoincore.org/en/doc/0.18.0/](https://bitcoincore.org/en/doc/0.18.0/). + Internally the in3-server will add proofs as part of the responses. The proof data differs between the methods. You will read which proof data will be provided and how the data can be used to prove the result for each method. + + Proofs will add a special `in3`-section to the response containing a `proof`- object. This object will contain parts or all of the following properties: + + * **block** + * **final** + * **txIndex** + * **merkleProof** + * **cbtx** + * **cbtxMerkleProof** + + # config + config: + + btc: + descr: configure the Bitcoin verification + example: + maxDAP: 30 + maxDiff: 5 + + type: + maxDAP: + descr: max number of DAPs (Difficulty Adjustment Periods) allowed when accepting new targets. + type: int + example: 10 + default: 20 + optional: true + + maxDiff: + descr: max increase (in percent) of the difference between targets when accepting new targets. + type: int + example: 5 + default: 10 + optional: true + + + getblockheader: + descr: Returns data of block header for given block hash. The returned level of details depends on the argument verbosity. + params: + hash: + descr: The block hash + type: bytes32 + verbosity: + descr: 0 or false for the hex-encoded data, 1 or true for a json object + type: bool + default: false + in3Params: + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + preBIP34: + descr: defines if the client wants to verify blocks before BIP34 (height < 227836) + type: bool + result: + options: + - params: + verbosity: false + name: getblockheaderAsHex + descr: returns a hex representation of the blockheader + example: '3045022100ae5bd019a63aed404b743c9ebcc77fbaa657e481f745e4...f3255d' + result: + optional: true + type: bytes + - params: + verbosity: true + name: getblockheader + descr: returns the blockheader + result: + optional: true + type: btcblockheader + descr: | + the blockheader. + - verbose `0` or `false`: a hex string with 80 bytes representing the blockheader + - verbose `1` or `true`: an object representing the blockheader. + type: btcblockheader + + proof: + descr: | + The `proof`-object contains the following properties: + + - for blocks before BIP34 (height < 227,836) and `in3.preBIP34` = false + + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + + - for blocks before BIP34 (height < 227,836) and `in3.preBIP34` = true + + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated up to the next checkpoint (maximum of 200 finality headers, since the distance between checkpoints = 200) + - **height** - the height of the block (block number) + + - for blocks after BIP34 (height >= 227,836), *the value of `in3.preBIP34` does not matter* + + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **cbtx** - the serialized coinbase transaction of the block (this is needed to get the verified block number) + - **cbtxMerkleProof** - the merkle proof of the coinbase transaction, proofing the correctness of the cbtx. + + Old blocks (height < 227,836) with `in3.preBIP34` disabled cannot be verified (proving the finality does not provide any security as explained in [preBIP34 proof](bitcoin.html#id1)). Old blocks with `in.preBIP34` enabled can be verified by performing a [preBIP34 proof](bitcoin.html#id1). Verifying newer blocks requires multiple proofs. The finality headers from the `final`-field will be used to perform a [finality proof](bitcoin.html#finality-proof). To verify the block number we are going to perform a [block number proof](bitcoin.html#block-number-proof) using the coinbase transaction (`cbtx`-field) and the [merkle proof](bitcoin.html#transaction-proof-merkle-proof) for the coinbase transaction (`cbtxMerkleProof`-field). + type: + final: + descr: the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + type: bytes + cbtx: + descr: the serialized coinbase transaction of the block (this is needed to get the verified block number). It will only be included if the blocknumber supports BIP34 and is higher 227,836) + type: bytes + cbtxMerkleProof: + descr: the merkle proof of the coinbase transaction, proofing the correctness of the cbtx. + type: bytes + height: + descr: the height of the block (block number) + type: uint64 + + + example: + in3Params: + finality: 8 + preBIP34: true + cmdParams: -x -c btc -f 8 + request: + - "000000000000000000103b2395f6cd94221b10d02eb9be5850303c0534307220" + - true + response: + hash: 000000000000000000103b2395f6cd94221b10d02eb9be5850303c0534307220 + confirmations: 8268 + height: 624958 + version: 536928256 + versionHex: 2000e000 + merkleroot: d786a334ea8c65f39272d5b9be505ac3170f3904842bd52525538a9377b359cb + time: 1586333924 + mediantime: 1586332639 + nonce: 1985217615 + bits: 17143b41 + difficulty: 13912524048945.91 + chainwork: 00000000000000000000000000000000000000000e4c88b66c5ee78deff0d494 + nTx: 33 + previousblockhash: 00000000000000000013cba040837778744ce66961cfcf2e7c34bb3d194c7f49 + nextblockhash: 0000000000000000000c799dc0e36302db7fbb471711f140dc308508ef19e343 + in3: + proof: + final: 0x00e0ff2720723034053c305058beb92ed010...276470 + cbtx: 0x0100000000010100000000000000000000000...39da2fc + cbtxMerkleProof: 0x6a8077bb4ce76b71d7742ddd368770279a64667b...52e688 + + + + + getblock: + descr: Returns data of block for given block hash. The returned level of details depends on the argument verbosity. + params: + hash: + descr: The block hash + type: bytes32 + verbosity: + descr: 0 or false for hex-encoded data, 1 or true for a json object, and 2 for json object **with** transaction data + type: int + default: 0 + in3Params: + finality: + descr: defines the amount of finality headers + type: int + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + preBIP34: + descr: defines if the client wants to verify blocks before BIP34 (height < 227836) + type: bool + result: + options: + - params: + verbosity: 0 + name: getBlockAsHex + example: '3045022100ae5bd019a63aed404b743c9ebcc77fbaa657e481f745e4...f3255d' + descr: returns a hex representation of the block + result: + optional: true + type: bytes + - params: + verbosity: 1 + name: getBlock + descr: returns the block with transactionhashes + result: + optional: true + type: btcblock + - params: + verbosity: 2 + name: getBlockWithTx + descr: returns the block with full transactions + result: + optional: true + type: btcblockWithTx + descr: | + the block. + - verbose `0` or `false`: a hex string with 80 bytes representing the blockheader + - verbose `1` or `true`: an object representing the blockheader. + type: btcblockWithTx + + + proof: + alias: getblockheader + + example: + in3Params: + finality: 8 + preBIP34: true + cmdParams: -x -c btc -f 8 + request: + - "000000000000000000103b2395f6cd94221b10d02eb9be5850303c0534307220" + - 1 + response: + hash: 000000000000000000103b2395f6cd94221b10d02eb9be5850303c0534307220 + confirmations: 8268 + height: 624958 + version: 536928256 + versionHex: 2000e000 + merkleroot: d786a334ea8c65f39272d5b9be505ac3170f3904842bd52525538a9377b359cb + time: 1586333924 + mediantime: 1586332639 + nonce: 1985217615 + bits: 17143b41 + difficulty: 13912524048945.91 + chainwork: 00000000000000000000000000000000000000000e4c88b66c5ee78deff0d494 + tx: + - d79ffc80e07fe9e0083319600c59d47afe69995b1357be6e5dba035675780290 + - ... + - 6456819bfa019ba30788620153ea9a361083cb888b3662e2ff39c0f7adf16919 + nTx: 33 + previousblockhash: 00000000000000000013cba040837778744ce66961cfcf2e7c34bb3d194c7f49 + nextblockhash: 0000000000000000000c799dc0e36302db7fbb471711f140dc308508ef19e343 + in3: + proof: + final: 0x00e0ff2720723034053c305058beb92ed010...276470 + cbtx: 0x0100000000010100000000000000000000000...39da2fc + cbtxMerkleProof: 0x6a8077bb4ce76b71d7742ddd368770279a64667b...52e688 + + getrawtransaction: + descr: Returns the raw transaction data. The returned level of details depends on the argument verbosity. + params: + txid: + descr: The transaction id + type: bytes32 + verbosity: + descr: 0 or false for the hex-encoded data for `txid`, 1 or true for a json object with information about `txid` + type: int + optional: true + default: 1 + blockhash: + descr: The block in which to look for the transaction + type: bytes32 + optional: true + in3Params: + finality: + descr: defines the amount of finality headers + type: int + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + preBIP34: + descr: defines if the client wants to verify blocks before BIP34 (height < 227836) + type: bool + result: + options: + - params: + verbosity: 0 + name: getRawTransactionAsHex + example: '3045022100ae5bd019a63aed404b743c9ebcc77fbaa657e481f745e4...f3255d' + descr: returns a hex representation of the tx + result: + optional: true + type: bytes + - params: + verbosity: 1 + name: getRawTransaction + descr: returns the raw transaction + result: + optional: true + type: btctransaction + descr: | + - verbose `0` or `false`: a string that is serialized, hex-encoded data for `txid` + - verbose `1` or `false`: an object representing the transaction. + type: btctransaction + + proof: + descr: | + - for blocks before BIP34 (height < 227836) and `in3.preBIP34` = false + + - **block** - a hex string with 80 bytes representing the blockheader + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **txIndex** - index of the transaction (`txIndex`=`0` for coinbase transaction, necessary to create/verify the merkle proof) + - **merkleProof** - the merkle proof of the requested transaction, proving the correctness of the transaction + + - for blocks before BIP34 (height < 227836) and `in3.preBIP34` = true + + - **block** - a hex string with 80 bytes representing the blockheader + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated up to the next checkpoint (maximum of 200 finality headers, since the distance between checkpoints = 200) + - **txIndex** - index of the transaction (`txIndex`=`0` for coinbase transaction, necessary to create/verify the merkle proof) + - **merkleProof** - the merkle proof of the requested transaction, proving the correctness of the transaction + - **height** - the height of the block (block number) + + - for blocks after BIP34 (height >= 227836), *the value of `in3.preBIP34` does not matter* + + - **block** - a hex string with 80 bytes representing the blockheader + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **txIndex** - index of the transaction (`txIndex`=`0` for coinbase transaction, necessary to create/verify the merkle proof) + - **merkleProof** - the merkle proof of the requested transaction, proving the correctness of the transaction + - **cbtx** - the serialized coinbase transaction of the block (this is needed to get the verified block number) + - **cbtxMerkleProof** - the merkle proof of the coinbase transaction, proving the correctness of the `cbtx` + + + Transactions of old blocks (height < 227836) with `in3.preBIP34` disabled cannot be verified (proving the finality does not provide any security as explained in [preBIP34 proof](bitcoin.html#id1) and relying on the merkle proof is only possible when the block is final). Transactions of old blocks with `in3.preBIP34` enabled can be verified by performing a [preBIP34 proof](bitcoin.html#id1) and a [merkle proof](bitcoin.html#transaction-proof-merkle-proof). Verifying newer blocks requires multiple proofs. The block header from the `block`-field and the finality headers from the `final`-field will be used to perform a [finality proof](bitcoin.html#finality-proof). By doing a [merkle proof](bitcoin.html#transaction-proof-merkle-proof) using the `txIndex`-field and the `merkleProof`-field the correctness of the requested transation can be proven. Furthermore we are going to perform a [block number proof](bitcoin.html#block-number-proof) using the coinbase transaction (`cbtx`-field) and the [merkle proof](bitcoin.html#transaction-proof-merkle-proof) for the coinbase transaction (`cbtxMerkleProof`-field). + example: + in3Params: + finality: 8 + cmdParams: -x -c btc -f 8 + request: + - 'f3c06e17b04ef748ce6604ad68e5b9f68ca96914b57c2118a1bb9a09a194ddaf' + # - true + # - '000000000000000000103b2395f6cd94221b10d02eb9be5850303c0534307220' + response: + in_active_chain: true + txid: f3c06e17b04ef748ce6604ad68e5b9f68ca96914b57c2118a1bb9a09a194ddaf + hash: f3c06e17b04ef748ce6604ad68e5b9f68ca96914b57c2118a1bb9a09a194ddaf + version: 1 + size: 518 + vsize: 518 + weight: 2072 + locktime: 0 + vin: + - txid: 0a74f6e5f99bc69af80da9f0d9878ea6afbfb5fbb2d43f1ff899bcdd641a098c + vout: 0 + scriptSig: + asm: 30440220481f2b3a49b202e26c73ac1b7bce022e4a74aff08473228cc...254874 + hex: 4730440220481f2b3a49b202e26c73ac1b7bce022e4a74aff08473228...254874 + sequence: 4294967295 + - txid: 869c5e82d4dfc3139c8a153d2ee126e30a467cf791718e6ea64120e5b19e5044 + vout: 0 + scriptSig: + asm: 3045022100ae5bd019a63aed404b743c9ebcc77fbaa657e481f745e4...f3255d + hex: 483045022100ae5bd019a63aed404b743c9ebcc77fbaa657e481f745...f3255d + sequence: 4294967295 + - txid: 8a03d29a1b8ae408c94a2ae15bef8329bc3d6b04c063d36b2e8c997273fa8eff + vout: 1 + scriptSig: + asm: 304402200bf7c5c7caec478bf6d7e9c5127c71505034302056d1284...0045da + hex: 47304402200bf7c5c7caec478bf6d7e9c5127c71505034302056d12...0045da + sequence: 4294967295 + vout: + - value: 0.00017571 + n: 0 + scriptPubKey: + asm: OP_DUP OP_HASH160 53196749b85367db9443ef9a5aec25cf0bdceedf OP_EQUALVERIFY + OP_CHECKSIG + hex: 76a91453196749b85367db9443ef9a5aec25cf0bdceedf88ac + reqSigs: 1 + type: pubkeyhash + addresses: + - 18aPWzBTq1nzs9o86oC9m3BQbxZWmV82UU + - value: 0.00915732 + n: 1 + scriptPubKey: + asm: OP_HASH160 8bb2b4b848d0b6336cc64ea57ae989630f447cba OP_EQUAL + hex: a9148bb2b4b848d0b6336cc64ea57ae989630f447cba87 + reqSigs: 1 + type: scripthash + addresses: + - 3ERfvuzAYPPpACivh1JnwYbBdrAjupTzbw + hex: '01000000038c091a64ddbc99f81f3fd4b2fbb5bfafa68e8...000000' + blockhash: 000000000000000000103b2395f6cd94221b10d02eb9be5850303c0534307220 + confirmations: 15307 + time: 1586333924 + blocktime: 1586333924 + in3: + proof: + block: 0x00e00020497f4c193dbb347c2ecfcf6169e64c747877...045476 + final: 0x00e0ff2720723034053c305058beb92ed0101b2294cd...276470 + txIndex: 7 + merkleProof: 0x348d4bb04943400a80f162c4ef64b746bc4af0...52e688 + cbtx: 0x010000000001010000000000000000000000000000000...9da2fc + cbtxMerkleProof: 0x6a8077bb4ce76b71d7742ddd368770279a...52e688 + + getblockcount: + descr: Returns the number of blocks in the longest blockchain. + in3Params: + finality: + descr: defines the amount of finality headers + type: int + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + result: + descr: the current blockheight + type: uint64 + proof: + descr: | + Since we can't prove the finality of the latest block we consider the `current block count` - `amount of finality` (set in `in3.finality`-field) as the latest block. The number of this block will be returned. Setting `in3.finality`=`0` will return the actual current block count. + + The `proof`-object contains the following properties: + + - **block** - a hex string with 80 bytes representing the blockheader + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **cbtx** - the serialized coinbase transaction of the block (this is needed to get the verified block number) + - vcbtxMerkleProof** - the merkle proof of the coinbase transaction, proving the correctness of the `cbtx` + + + The server is not able to prove the finality for the latest block (obviously there are no finality headers available yet). Instead the server will fetch the number of the latest block and subtracts the amount of finality headers (set in `in3.finality`-field) and returns the result to the client (the result is considered as the latest block number). By doing so the server is able to provide finality headers. \ + The block header from the `block`-field and the finality headers from the `final`-field will be used to perform a [finality proof](bitcoin.html#finality-proof). Having a verified block header (and therefore a verified merkle root) enables the possibility of a [block number proof](bitcoin.html#block-number-proof) using the coinbase transaction (`cbtx`-field) and the [merkle proof](bitcoin.html#transaction-proof-merkle-proof) for the coinbase transaction (`cbtxMerkleProof`-field). + + The client can set `in3.finality` equal to `0` to get the actual latest block number. **Caution**: This block is not final and could no longer be part of the blockchain later on due to the possibility of a fork. Additionally, there may already be a newer block that the server does not yet know about due to latency in the network. + example: + descr: The actual latest block is block `#640395` and `in3.finality` is set to `8`. The server is going to calculate `640395` - `8` and returns `640387` as the latest block number to the client. The headers of block `640388`..`640395` will be returned as finality headers. + in3Params: + finality: 8 + cmdParams: -x -c btc -f 8 + response: 640387 + in3: + proof: + block: "0x0000e020bd3eecbd741522e1aa78cd7b375744590502939aef9b...9c8b18" + final: "0x00008020f61dfcc47a6daed717b12221855196dee02d844ebb9c...774f4c" + cbtx: "0x02000000000101000000000000000000000000000000000000000...000000" + cbtxMerkleProof: "0xa3d607b274770911e53f06dbdb76440580ff968239...0ba297" + + + + getdifficulty: + descr: Returns the proof-of-work difficulty as a multiple of the minimum difficulty. + in3Params: + finality: + descr: defines the amount of finality headers + type: int + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + preBIP34: + descr: defines if the client wants to verify blocks before BIP34 (height < 227836) + type: bool + + result: + type: double + descr: | + - `blocknumber` is a certain number: the difficulty of this block + - `blocknumber` is `latest`, `earliest`, `pending` or empty: the difficulty of the latest block (`actual latest block` minus `in3.finality`) + + proof: + descr: | + The `proof`-object contains the following properties: + + - for blocks before BIP34 (height < 227,836) and `in3.preBIP34` = false + + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + + - for blocks before BIP34 (height < 227,836) and `in3.preBIP34` = true + + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated up to the next checkpoint (maximum of 200 finality headers, since the distance between checkpoints = 200) + - **height** - the height of the block (block number) + + - for blocks after BIP34 (height >= 227,836), *the value of `in3.preBIP34` does not matter* + + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **cbtx** - the serialized coinbase transaction of the block (this is needed to get the verified block number) + - **cbtxMerkleProof** - the merkle proof of the coinbase transaction, proofing the correctness of the cbtx. + + In case the client requests the diffictuly of a certain block (`blocknumber` is a certain number) the `block`-field will contain the block header of this block and the `final`-field the corresponding finality headers. For old blocks (height < 227,836) with `in3.preBIP34` disabled the result cannot be verified (proving the finality does not provide any security as explained in [preBIP34 proof](bitcoin.html#id1)). The result of old blocks with `in.preBIP34` enabled can be verified by performing a [preBIP34 proof](bitcoin.html#id1). In case the client requests the difficulty of the latest block the server is not able to prove the finality for this block (obviously there are no finality headers available yet). The server considers the latest block minus `in3.finality` as the latest block and returns its difficulty. The result can be verified by performing multiple proof. The block header from the `block`-field and the finality headers from the `final`-field will be used to perform a [finality proof](bitcoin.html#finality-proof). Having a verified block header (and therefore a verified merkle root) enables the possibility of a [block number proof](bitcoin.html#block-number-proof) using the coinbase transaction (`cbtx`-field) and the [merkle proof](bitcoin.html#transaction-proof-merkle-proof) for the coinbase transaction (`cbtxMerkleProof`-field). + + The result itself (the difficulty) can be verified in two ways: + - by converting the difficulty into a target and check whether the block hash is lower than the target (since we proved the finality we consider the block hash as verified) + - by converting the difficulty and the bits (part of the block header) into a target and check if both targets are similar (they will not be equal since the target of the bits is not getting saved with full precision - leading bytes are equal) + type: + final: + descr: the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + type: bytes + cbtx: + descr: the serialized coinbase transaction of the block (this is needed to get the verified block number). It will only be included if the blocknumber supports BIP34 and is higher 227,836) + type: bytes + cbtxMerkleProof: + descr: the merkle proof of the coinbase transaction, proofing the correctness of the cbtx. + type: bytes + height: + descr: the height of the block (block number) + type: uint64 + example: + in3Params: + finality: 8 + cmdParams: -x -c btc -f 8 + response: 15138043247082.88 + in3: + proof: + block: "0x0000e020bd3eecbd741522e1aa78cd7b375744590502939aef9b...9c8b18" + final: "0x00008020f61dfcc47a6daed717b12221855196dee02d844ebb9c...774f4c" + cbtx: "0x02000000000101000000000000000000000000000000000000000...000000" + cbtxMerkleProof: "0xa3d607b274770911e53f06dbdb76440580ff968239...0ba297" + + btc_proofTarget: + descr: Whenever the client is not able to trust the changes of the target (which is the case if a block can't be found in the verified target cache *and* the value of the target changed more than the client's limit `max_diff`) he will call this method. It will return additional proof data to verify the changes of the target on the side of the client. This is not a standard Bitcoin rpc-method like the other ones, but more like an internal method. + params: + target_dap: + descr: the number of the difficulty adjustment period (dap) we are looking for + type: uint64 + verified_dap: + descr: the number of the closest already verified dap + type: uint64 + max_diff: + descr: the maximum target difference between 2 verified daps + type: int + default: 5 + optional: true + max_dap: + descr: the maximum amount of daps between 2 verified daps + type: int + default: 5 + optional: true + limit: + descr: the maximum amount of daps to return (`0` = no limit) - this is important for embedded devices since returning all daps might be too much for limited memory + type: int + default: 0 + optional: true + in3Params: + finality: + descr: defines the amount of finality headers + type: int + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + preBIP34: + descr: defines if the client wants to verify blocks before BIP34 (height < 227836) + type: bool + validation: | + Hints: + + - difference between `target_dap` and `verified_dap` should be greater than `1` + - `target_dap` and `verified_dap` have to be greater than `0` + - `limit` will be set to `40` internaly when the parameter is equal to `0` or greater than `40` + - `max_dap` can't be equal to `0` + - `max_diff` equal to `0` means no tolerance regarding the change of the target - the path will contain every dap between `target_dap` and `verified_dap` (under consideration of `limit`) + - total possible amount of finality headers (`in3.finaliy` \* `limit`) can't be greater than `1000` + - changes of a target will always be accepted if it decreased from one dap to another (i.e. difficulty to mine a block increased) + - in case a dap that we want to verify next (i.e. add it to the path) is only 1 dap apart from a verified dap (i.e. `verified_dap` or latest dap of the path) *but* not within the given limit (`max_diff`) it will still be added to the path (since we can't do even smaller steps) + + This graph shows the usage of this method and visualizes the result from above. The client is not able to trust the changes of the target due to his limits (`max_diff` and `max_dap`). This method provides a path of daps in which the limits are fulfilled from dap to another. The client is going to trust the target of the target dap since he is able to perform a step by step verification of the target by using the path of daps. + + ![](proofTarget.png) + + result: + array: true + type: + dap: + descr: the difficulty adjustement period + type: uint64 + block: + descr: the first blockheader + type: bytes + final: + descr: the finality header + type: bytes + cbtx: + descr: the coinbase transaction as hex + type: bytes + cbtxMerkleProof: + descr: the coinbasetx merkle proof + type: bytes + + descr: A path of daps from the `verified_dap` to the `target_dap` which fulfils the conditions of `max_diff`, `max_dap` and `limit`. Each dap of the path is a `dap`-object with corresponding proof data. + proof: + descr: | + Each `dap`-object contains the following properties: + + - for blocks before BIP34 (height < 227836) and `in3.preBIP34` = false + + - **dap** - the numer of the difficulty adjustment period + - **block** - a hex string with 80 bytes representing the (always the first block of a dap) + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + + - for blocks before BIP34 (height < 227836) and `in3.preBIP34` = true + + - **dap** - the numer of the difficulty adjustment period + - **block** - a hex string with 80 bytes representing the blockheader + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated up to the next checkpoint (maximum of 200 finality headers, since the distance between checkpoints = 200) + - **height** - the height of the block (block number) + + - for blocks after BIP34 (height >= 227836), *the value of `in3.preBIP34` does not matter* + + - **dap** - the numer of the difficulty adjustment period + - **block** - a hex string with 80 bytes representing the (always the first block of a dap) + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **cbtx** - the serialized coinbase transaction of the block (this is needed to get the verified block number) + - **cbtxMerkleProof** - the merkle proof of the coinbase transaction, proving the correctness of the `cbtx` + + The goal is to verify the target of the `target_dap`. We will use the daps of the result to verify the target step by step starting with the `verified_dap`. For old blocks (height < 227,836) with `in3.preBIP34` disabled the target cannot be verified (proving the finality does not provide any security as explained in [preBIP34 proof](bitcoin.html#id1)). For old blocks with `in.preBIP34` enabled the block header can be verified by performing a [preBIP34 proof](bitcoin.html#id1). Verifying newer blocks requires multiple proofs. The block header from the `block`-field and the finality headers from the `final`-field will be used to perform a [finality proof](bitcoin.html#finality-proof). Having a verified block header allows us to consider the target of the block header as verified. Therefore, we have a verified target for the whole `dap`. Having a verified block header (and therefore a verified merkle root) enables the possibility of a [block number proof](bitcoin.html#block-number-proof) using the coinbase transaction (`cbtx`-field) and the [merkle proof](bitcoin.html#transaction-proof-merkle-proof) for the coinbase transaction (`cbtxMerkleProof`-field). This proof is needed to verify the dap number (`dap`). Having a verified dap number allows us to verify the mapping between the target and the dap number. + example: + cmdParams: -x -c btc -f 8 + request: + - 230 + - 200 + - 5 + - 5 + - 15 + response: + - dap: 205 + block: 0x04000000e62ef28cb9793f4f9cd2a67a58c1e7b593129b9b...0ab284 + final: 0x04000000cc69b68b702321adf4b0c485fdb1f3d6c1ddd140...090a5b + cbtx: 0x01000000...1485ce370573be63d7cc1b9efbad3489eb57c8...000000 + cbtxMerkleProof: 0xc72dffc1cb4cbeab960d0d2bdb80012acf7f9c...affcf4 + - dap: 210 + block: 0x0000003021622c26a4e62cafa8e434c7e083f540bccc8392...b374ce + final: 0x00000020858f8e5124cd516f4d5e6a078f7083c12c48e8cd...308c3d + cbtx: 0x01000000...c075061b4b6e434d696e657242332d50314861...000000 + cbtxMerkleProof: 0xf2885d0bac15fca7e1644c1162899ecd43d52b...93761d + - dap: 215 + block: 0x000000202509b3b8e4f98290c7c9551d180eb2a463f0b978...f97b64 + final: 0x0000002014c7c0ed7c33c59259b7b508bebfe3974e1c99a5...eb554e + cbtx: 0x01000000...90133cf94b1b1c40fae077a7833c0fe0ccc474...000000 + cbtxMerkleProof: 0x628c8d961adb157f800be7cfb03ffa1b53d3ad...ca5a61 + - dap: 220 + block: 0x00000020ff45c783d09706e359dcc76083e15e51839e4ed5...ddfe0e + final: 0x0000002039d2f8a1230dd0bee50034e8c63951ab812c0b89...5670c5 + cbtx: 0x01000000...b98e79fb3e4b88aefbc8ce59e82e99293e5b08...000000 + cbtxMerkleProof: 0x16adb7aeec2cf254db0bab0f4a5083fb0e0a3f...63a4f4 + - dap: 225 + block: 0x02000020170fad0b6b1ccbdc4401d7b1c8ee868c6977d6ce...1e7f8f + final: 0x0400000092945abbd7b9f0d407fcccbf418e4fc20570040c...a9b240 + cbtx: 0x01000000...cf6e8f930acb8f38b588d76cd8c3da3258d5a7...000000 + cbtxMerkleProof: 0x25575bcaf3e11970ccf835e88d6f97bedd6b85...bfdf46 + + + getbestblockhash: + descr: Returns the hash of the best (tip) block in the longest blockchain. + in3Params: + finality: + descr: defines the amount of finality headers + type: int + verification: + descr: defines the kind of proof the client is asking for (must be `never` or `proof`) + type: string + preBIP34: + descr: defines if the client wants to verify blocks before BIP34 (height < 227836) + type: bool + result: + descr: the hash of the best block + type: bytes32 + proof: + descr: | + Since we can't prove the finality of the latest block we consider the `current block count` - `amount of finality` (set in `in3.finality`-field) as the latest block. The hash of this block will be returned. Setting `in3.finality`=`0` will return will return the hash of the actual latest block. + + The `proof`-object contains the following properties: + + - **block** - a hex string with 80 bytes representing the blockheader + - **final** - the finality headers, which are hexcoded bytes of the following headers (80 bytes each) concatenated, the number depends on the requested finality (`finality`-property in the `in3`-section of the request) + - **cbtx** - the serialized coinbase transaction of the block (this is needed to get the verified block number) + - **cbtxMerkleProof* - the merkle proof of the coinbase transaction, proving the correctness of the `cbtx` + + The server is not able to prove the finality for the latest block (obviously there are no finality headers available yet). Instead the server will fetch the number of the latest block and subtracts the amount of finality headers (set in `in3.finality`-field) and returns the hash of this block to the client (the result is considered as the latest block hash). By doing so the server is able to provide finality headers. \ + The block header from the `block`-field and the finality headers from the `final`-field will be used to perform a [finality proof](bitcoin.html#finality-proof). Having a verified block header (and therefore a verified merkle root) enables the possibility of a [block number proof](bitcoin.html#block-number-proof) using the coinbase transaction (`cbtx`-field) and the [merkle proof](bitcoin.html#transaction-proof-merkle-proof) for the coinbase transaction (`cbtxMerkleProof`-field). + + The client can set `in3.finality` equal to `0` to get the actual latest block hash. **Caution**: This block is not final and could no longer be part of the blockchain later on due to the possibility of a fork. Additionally, there may already be a newer block that the server does not yet know about due to latency in the network. + example: + cmdParams: -x -c btc -f 8 + descr: The actual latest block is block `#640395` and `in3.finality` is set to `8`. The server is going to calculate `640395` - `8` and returns the hash of block `#640387` to the client. The headers of block `640388`..`640395` will be returned as finality headers. + in3Params: + finality: 8 + response: '000000000000000000039cbb4e842de0de9651852122b117d7ae6d7ac4fc1df6' + in3: + proof: + block: "0x0000e020bd3eecbd741522e1aa78cd7b375744590502939aef9b...9c8b18" + final: "0x00008020f61dfcc47a6daed717b12221855196dee02d844ebb9c...774f4c" + cbtx: "0x02000000000101000000000000000000000000000000000000000...000000" + cbtxMerkleProof: "0xa3d607b274770911e53f06dbdb76440580ff968239...0ba297" + + + + diff --git a/c/src/verifier/eth1/basic/CMakeLists.txt b/c/src/verifier/eth1/basic/CMakeLists.txt index 1e3ef01f5..46f284f05 100644 --- a/c/src/verifier/eth1/basic/CMakeLists.txt +++ b/c/src/verifier/eth1/basic/CMakeLists.txt @@ -34,6 +34,7 @@ add_static_library( NAME eth_basic + REGISTER in3_register_eth_basic SOURCES eth_basic.c diff --git a/c/src/verifier/eth1/basic/eth_account.c b/c/src/verifier/eth1/basic/eth_account.c index 3e690729c..e621b5d49 100644 --- a/c/src/verifier/eth1/basic/eth_account.c +++ b/c/src/verifier/eth1/basic/eth_account.c @@ -50,7 +50,7 @@ static int is_not_existened(d_token_t* account) { d_token_t* t = NULL; return ((t = d_get(account, K_BALANCE)) && d_type(t) == T_INTEGER && d_int(t) == 0 && (t = d_getl(account, K_CODE_HASH, 32)) && memcmp(t->data, EMPTY_HASH, 32) == 0 && d_get_long(account, K_NONCE) == 0) && (t = d_getl(account, K_STORAGE_HASH, 32)) && memcmp(t->data, EMPTY_ROOT_HASH, 32) == 0; } - +const uint8_t* empty_hash() { return EMPTY_HASH; } static in3_ret_t verify_proof(in3_vctx_t* vc, bytes_t* header, d_token_t* account) { d_token_t * t, *storage_proof, *p; int i; diff --git a/c/src/verifier/eth1/basic/eth_basic.c b/c/src/verifier/eth1/basic/eth_basic.c index cc140b018..9e6f6cf95 100644 --- a/c/src/verifier/eth1/basic/eth_basic.c +++ b/c/src/verifier/eth1/basic/eth_basic.c @@ -60,34 +60,32 @@ in3_ret_t in3_verify_eth_basic(in3_vctx_t* vc) { return IN3_OK; else if (d_type(vc->result) == T_NULL) { // check if there's a proof for non-existence - if (!strcmp(vc->method, "eth_getTransactionByBlockHashAndIndex") || !strcmp(vc->method, "eth_getTransactionByBlockNumberAndIndex")) { + if (VERIFY_RPC("eth_getTransactionByBlockHashAndIndex") || VERIFY_RPC("eth_getTransactionByBlockNumberAndIndex")) return eth_verify_eth_getTransactionByBlock(vc, d_get_at(d_get(vc->request, K_PARAMS), 0), d_get_int_at(d_get(vc->request, K_PARAMS), 1)); - } return IN3_OK; } - if (strcmp(vc->method, "eth_getTransactionByHash") == 0) + if (VERIFY_RPC("eth_getTransactionByHash")) return eth_verify_eth_getTransaction(vc, d_get_bytes_at(d_get(vc->request, K_PARAMS), 0)); - else if (!strcmp(vc->method, "eth_getTransactionByBlockHashAndIndex") || !strcmp(vc->method, "eth_getTransactionByBlockNumberAndIndex")) { + else if (VERIFY_RPC("eth_getTransactionByBlockHashAndIndex") || VERIFY_RPC("eth_getTransactionByBlockNumberAndIndex")) return eth_verify_eth_getTransactionByBlock(vc, d_get_at(d_get(vc->request, K_PARAMS), 0), d_get_int_at(d_get(vc->request, K_PARAMS), 1)); - } - else if (strcmp(vc->method, "eth_getBlockByNumber") == 0) + else if (VERIFY_RPC("eth_getBlockByNumber")) return eth_verify_eth_getBlock(vc, NULL, d_get_long_at(d_get(vc->request, K_PARAMS), 0)); - else if (strcmp(vc->method, "eth_getBlockTransactionCountByHash") == 0) + else if (VERIFY_RPC("eth_getBlockTransactionCountByHash")) return eth_verify_eth_getBlockTransactionCount(vc, d_get_bytes_at(d_get(vc->request, K_PARAMS), 0), 0); - else if (strcmp(vc->method, "eth_getBlockTransactionCountByNumber") == 0) + else if (VERIFY_RPC("eth_getBlockTransactionCountByNumber")) return eth_verify_eth_getBlockTransactionCount(vc, NULL, d_get_long_at(d_get(vc->request, K_PARAMS), 0)); - else if (strcmp(vc->method, "eth_getBlockByHash") == 0) + else if (VERIFY_RPC("eth_getBlockByHash")) return eth_verify_eth_getBlock(vc, d_get_bytes_at(d_get(vc->request, K_PARAMS), 0), 0); - else if (strcmp(vc->method, "eth_getBalance") == 0 || strcmp(vc->method, "eth_getCode") == 0 || strcmp(vc->method, "eth_getStorageAt") == 0 || strcmp(vc->method, "eth_getTransactionCount") == 0) + else if (VERIFY_RPC("eth_getBalance") || VERIFY_RPC("eth_getCode") || VERIFY_RPC("eth_getStorageAt") || VERIFY_RPC("eth_getTransactionCount")) return eth_verify_account_proof(vc); - else if (strcmp(vc->method, "eth_gasPrice") == 0) + else if (VERIFY_RPC("eth_gasPrice")) return IN3_OK; - else if (!strcmp(vc->method, "eth_newFilter") || !strcmp(vc->method, "eth_newBlockFilter") || !strcmp(vc->method, "eth_newPendingFilter") || !strcmp(vc->method, "eth_uninstallFilter") || !strcmp(vc->method, "eth_getFilterChanges")) + else if (VERIFY_RPC("eth_newFilter") || VERIFY_RPC("eth_newBlockFilter") || VERIFY_RPC("eth_newPendingFilter") || VERIFY_RPC("eth_uninstallFilter") || VERIFY_RPC("eth_getFilterChanges")) return IN3_OK; - else if (strcmp(vc->method, "eth_getLogs") == 0) // for txReceipt, we need the txhash + else if (VERIFY_RPC("eth_getLogs")) // for txReceipt, we need the txhash return eth_verify_eth_getLog(vc, d_len(vc->result)); - else if (strcmp(vc->method, "eth_sendRawTransaction") == 0) { + else if (VERIFY_RPC("eth_sendRawTransaction")) { bytes32_t hash; keccak(d_to_bytes(d_get_at(d_get(vc->request, K_PARAMS), 0)), hash); return bytes_cmp(*d_bytes(vc->result), bytes(hash, 32)) ? IN3_OK : vc_err(vc, "the transactionHash of the response does not match the raw transaction!"); @@ -101,8 +99,10 @@ static in3_ret_t eth_send_transaction_and_wait(in3_rpc_handle_ctx_t* ctx) { str_range_t r = d_to_json(ctx->params + 1); char* tx_data = alloca(r.len + 1); memcpy(tx_data, r.data, r.len); - tx_data[r.len] = 0; - TRY(req_send_sub_request(ctx->req, "eth_sendTransaction", tx_data, NULL, &tx_hash)) + tx_data[r.len] = 0; + in3_req_t* send_req = NULL; + in3_req_t* last_r = NULL; + TRY(req_send_sub_request(ctx->req, "eth_sendTransaction", tx_data, NULL, &tx_hash, &send_req)) // tx was sent, we have a tx_hash char tx_hash_hex[69]; bytes_to_hex(d_bytes(tx_hash)->data, 32, tx_hash_hex + 3); @@ -112,28 +112,27 @@ static in3_ret_t eth_send_transaction_and_wait(in3_rpc_handle_ctx_t* ctx) { tx_hash_hex[68] = 0; // get the tx_receipt - TRY(req_send_sub_request(ctx->req, "eth_getTransactionReceipt", tx_hash_hex, NULL, &tx_receipt)) + TRY(req_send_sub_request(ctx->req, "eth_getTransactionReceipt", tx_hash_hex, NULL, &tx_receipt, &last_r)) if (d_type(tx_receipt) == T_NULL || d_get_long(tx_receipt, K_BLOCK_NUMBER) == 0) { // no tx yet // we remove it and try again - in3_req_t* last_r = req_find_required(ctx->req, "eth_getTransactionReceipt"); - uint32_t wait = d_get_int(d_get(last_r->requests[0], K_IN3), K_WAIT); - wait = wait ? wait * 2 : 1000; + uint32_t wait = d_get_int(d_get(last_r->requests[0], K_IN3), K_WAIT); + wait = wait ? wait * 2 : 1000; req_remove_required(ctx->req, last_r, false); if (wait > 120000) // more than 2 minutes is too long, so we stop here return req_set_error(ctx->req, "Waited too long for the transaction to be minded", IN3_ELIMIT); char in3[20]; sprintf(in3, "{\"wait\":%d}", wait); - return req_send_sub_request(ctx->req, "eth_getTransactionReceipt", tx_hash_hex, in3, &tx_receipt); + return req_send_sub_request(ctx->req, "eth_getTransactionReceipt", tx_hash_hex, in3, &tx_receipt, &last_r); } else { // we have a result and we keep it str_range_t r = d_to_json(tx_receipt); sb_add_range(in3_rpc_handle_start(ctx), r.data, 0, r.len); - req_remove_required(ctx->req, req_find_required(ctx->req, "eth_getTransactionReceipt"), false); - req_remove_required(ctx->req, req_find_required(ctx->req, "eth_sendRawTransaction"), false); + req_remove_required(ctx->req, last_r, false); + req_remove_required(ctx->req, send_req, false); return in3_rpc_handle_finish(ctx); } } diff --git a/c/src/verifier/eth1/basic/eth_basic.h b/c/src/verifier/eth1/basic/eth_basic.h index 096b99184..1f3ba3cb9 100644 --- a/c/src/verifier/eth1/basic/eth_basic.h +++ b/c/src/verifier/eth1/basic/eth_basic.h @@ -31,7 +31,7 @@ * You should have received a copy of the GNU Affero General Public License along * with this program. If not, see . *******************************************************************************/ - +// @PUBLIC_HEADER /** @file * Ethereum Nanon verification. * */ @@ -135,6 +135,11 @@ in3_ret_t handle_eth_sendTransaction(in3_req_t* req, /**< the current contex d_token_t* req_data /**< the request */ ); +/** + * returns a pointer to 32 bytes marking a empty hash (keccakc(0x)) + */ +const uint8_t* empty_hash(); + /** * minimum signer for the wallet, returns the signed message which needs to be freed */ diff --git a/c/src/verifier/eth1/basic/filter.c b/c/src/verifier/eth1/basic/filter.c index 5b4514c23..12ef044c6 100644 --- a/c/src/verifier/eth1/basic/filter.c +++ b/c/src/verifier/eth1/basic/filter.c @@ -182,7 +182,7 @@ in3_ret_t filter_add(in3_filter_handler_t* filters, in3_req_t* ctx, in3_filter_t in3_ret_t res = IN3_OK; uint64_t current_block = 0; - in3_req_t* block_ctx = req_find_required(ctx, "eth_blockNumber"); + in3_req_t* block_ctx = req_find_required(ctx, "eth_blockNumber", NULL); if (!block_ctx) return req_add_required(ctx, req_new(ctx->client, _strdupn("{\"method\":\"eth_blockNumber\",\"params\":[]}", -1))); else { @@ -258,7 +258,7 @@ in3_ret_t filter_get_changes(in3_filter_handler_t* filters, in3_req_t* ctx, size return req_set_error(ctx, "filter with id does not exist", IN3_EUNKNOWN); // fetch the current block number - in3_req_t* block_ctx = req_find_required(ctx, "eth_blockNumber"); + in3_req_t* block_ctx = req_find_required(ctx, "eth_blockNumber", NULL); if (!block_ctx) return req_add_required(ctx, req_new(ctx->client, _strdupn("{\"method\":\"eth_blockNumber\",\"params\":[]}", -1))); else { @@ -288,7 +288,7 @@ in3_ret_t filter_get_changes(in3_filter_handler_t* filters, in3_req_t* ctx, size return IN3_OK; } - in3_req_t* logs_ctx = req_find_required(ctx, "eth_getLogs"); + in3_req_t* logs_ctx = req_find_required(ctx, "eth_getLogs", NULL); if (!logs_ctx) { // create request char* fopt_ = filter_opt_set_fromBlock(fopt, f->last_block, !f->is_first_usage); diff --git a/c/src/verifier/eth1/basic/rpc.yml b/c/src/verifier/eth1/basic/rpc.yml new file mode 100644 index 000000000..0e7efd251 --- /dev/null +++ b/c/src/verifier/eth1/basic/rpc.yml @@ -0,0 +1,1138 @@ +types: + + transaction: + to: + descr: receipient of the transaction. + type: address + optional: true + from: + descr: sender of the address (if not sepcified, the first signer will be the sender) + type: address + optional: true + value: + descr: value in wei to send + type: uint256 + optional: true + gas: + descr: the gas to be send along + type: uint64 + optional: true + default: 21000 + gasPrice: + descr: the price in wei for one gas-unit. If not specified it will be fetched using `eth_gasPrice` + type: uint64 + optional: true + nonce: + descr: the current nonce of the sender. If not specified it will be fetched using `eth_getTransactionCount` + type: uint64 + optional: true + data: + descr: the data-section of the transaction + type: bytes + optional: true + + + blockheader: + number: + descr: the block number. `null` when its pending block. + type: uint64 + hash: + descr: hash of the block. `null` when its pending block. + type: bytes32 + parentHash: + descr: hash of the parent block. + type: bytes32 + nonce: + descr: hash of the generated proof-of-work. `null` when its pending block. + type: uint256 + optional: true + sha3Uncles: + descr: SHA3 of the uncles Merkle root in the block. + type: bytes32 + logsBloom: + descr: the bloom filter for the logs of the block. `null` when its pending block. + type: bytes256 + transactionsRoot: + descr: the root of the transaction trie of the block. + type: bytes32 + stateRoot: + descr: the root of the final state trie of the block. + type: bytes32 + receiptsRoot: + descr: the root of the receipts trie of the block. + type: bytes32 + miner: + descr: the address of the beneficiary to whom the mining rewards were given. + type: address + difficulty: + descr: integer of the difficulty for this block. + type: uint256 + totalDifficulty: + descr: integer of the total difficulty of the chain until this block. + type: uint256 + extraData: + descr: the "extra data" field of this block. + type: bytes + size: + descr: integer the size of this block in bytes. + type: uint64 + gasLimit: + descr: the maximum gas allowed in this block. + type: uint64 + gasUsed: + descr: the total used gas by all transactions in this block. + type: uint64 + timestamp: + descr: the unix timestamp for when the block was collated. + type: uint64 + uncles: + descr: Array of uncle hashes. + array: true + type: bytes32 + + blockdataWithTxHashes: + _extends: blockheader + transactions: + descr: Array of transaction hashes + array: true + type: bytes32 + + blockdata: + _extends: blockheader + transactions: + descr: Array of transaction objects + array: true + type: transactiondata + + transactiondata: + to: + descr: receipient of the transaction. + type: address + from: + descr: sender or signer of the transaction + type: address + value: + descr: value in wei to send + type: uint256 + gas: + descr: the gas to be send along + type: uint64 + gasPrice: + descr: the price in wei for one gas-unit. If not specified it will be fetched using `eth_gasPrice` + type: uint64 + nonce: + descr: the current nonce of the sender. If not specified it will be fetched using `eth_getTransactionCount` + type: uint64 + blockHash: + descr: blockHash of the block holding this transaction or `null` if still pending. + type: bytes32 + blockNumber: + descr: blockNumber of the block holding this transaction or `null` if still pending. + type: uint64 + hash: + descr: transactionHash + type: bytes32 + input: + descr: data of the transaaction + type: bytes + transactionIndex: + descr: index of the transaaction in the block + type: uint64 + v: + descr: recovery-byte of the signature + type: byte + r: + descr: x-value of the EC-Point of the signature + type: bytes32 + s: + descr: y-value of the EC-Point of the signature + type: bytes32 + +eth: + + eth_blockNumber: + descr: | + returns the number of the most recent block. + + See [eth_blockNumber](https://eth.wiki/json-rpc/API#eth_blockNumber) for spec. + + No proof returned, since there is none, but the client should verify the result by comparing it to the current blocks returned from others. + With the `blockTime` from the chainspec, including a tolerance, the current blocknumber may be checked if in the proposed range. + result: + descr: the highest known blocknumber + type: uint64 + example: + response: "0xb8a2a5" + + eth_getBlockByNumber: + descr: | + returns information about a block by block number. + + See [eth_getBlockByNumber](https://eth.wiki/json-rpc/API#eth_getBlockByNumber) for spec. + params: + blockNumber: + descr: the blockNumber or one of `latest`, `earliest`or `pending` + type: uint64 + internalDefault : latest + optional: true + fullTx: + descr: if true the full transactions are contained in the result. + default: false + type: bool + result: + optional: true + options: + - params: + fullTx: false + name: getBlock + descr: returns the given Block by number with transactionHashes. if no blocknumber is specified the latest block will be returned. + result: + type: blockdataWithTxHashes + - params: + fullTx: true + name: getBlockWithTx + descr: returns the given Block by number with full transaction data. if no blocknumber is specified the latest block will be returned. + result: + type: blockdata + descr: the blockdata, or in case the block with that number does not exist, `null` will be returned. + type: blockdata + + + example: + request: + - latest + - false + response: + author: '0x0000000000000000000000000000000000000000' + difficulty: '0x2' + extraData: '0x696e667572612d696f0000000000000...31570f1e500' + gasLimit: '0x7a1200' + gasUsed: '0x20e145' + hash: '0x2baa54adcd8a105cdedfd9c6635d48d07b8f0e805af0a5853190c179e5a18585' + logsBloom: '0x000008000000000000...00400100000000080' + miner: '0x0000000000000000000000000000000000000000' + number: '0x449956' + parentHash: '0x2c2a4fcd11aa9aea6b9767651a10e7dbd2bcddbdaba703c74458ad6faf7c2694' + receiptsRoot: '0x0240b90272b5600bef7e25d0894868f85125174c2f387ef3236fc9ed9bfb3eff' + sealFields: + - '0xa00000000000000000000000000000000000000000000000000000000000000000' + - '0x880000000000000000' + sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' + size: '0x74b' + stateRoot: '0xf44699575afd2668060be5ba77e66e1e80edb77ad1b5070969ddfa63da6a4910' + timestamp: '0x605aec86' + totalDifficulty: '0x6564de' + transactions: + - '0xcb7edfdb3229c9beeb418ab1ef1a3c9210ecfb22f0157791c3287085d798da58' + - '0x0fb803696521ba109c40b3eecb773c93dc6ee891172af0f620c8d44c05198641' + - '0x3ef6725cab4470889c3c7d53609a5d4b263701f5891aa98c9ed48b73b6b2fb75' + - '0x4010c4c112514756dcdcf14f91117503826dcbe15b03a1636c07aa713da24b8d' + - '0xd9c14daa5e2e9cc955534865365ef6bde3045c70e3a984a74c298606c4d67bb5' + - '0xfa2326237ba5dcca2127241562be16b68c48fed93d29add8d62f79a00518c2d8' + transactionsRoot: '0xddbbd7bf723abdfe885539406540671c2c0eb97684972175ad199258c75416cc' + uncles: [] + in3: + proof: + type: blockProof + signatures: [] + transactions: + - '0xf8ac830331f78449504f80830186a094f74a...8a83ce8dc' + - '0xf8ac830331f88449504f80830186a094f74a...a81c2f1fee77' + - '0xf8a91e843b9aca008315a92594f0277caffea...c30d64dd139' + - '0xf8c601843b9aca008305573094309906d7b701...62f5e7a2319a' + - '0xf8c680843b9aca008305573094309906d7b701...78289116eac194e' + - '0xf9014b82020a843b9aca0083010f6894786f8d72...b649' + proof: + descr: | + The `eth_getBlockBy...` methods return the Block-Data. + In this case, all we need is somebody verifying the blockhash, which is done by requiring somebody who stored a deposit and would otherwise lose it, to sign this blockhash. + + The verification is then done by simply creating the blockhash and comparing this to the signed one. + + The blockhash is calculated by blockdata with [rlp](https://github.com/ethereum/wiki/wiki/RLP) and hashing it: + + + ```js + blockHeader = rlp.encode([ + bytes32( parentHash ), + bytes32( sha3Uncles ), + address( miner || coinbase ), + bytes32( stateRoot ), + bytes32( transactionsRoot ), + bytes32( receiptsRoot || receiptRoot ), + bytes256( logsBloom ), + uint( difficulty ), + uint( number ), + uint( gasLimit ), + uint( gasUsed ), + uint( timestamp ), + bytes( extraData ), + + ... sealFields + ? sealFields.map( rlp.decode ) + : [ + bytes32( b.mixHash ), + bytes8( b.nonce ) + ] + ]) + ``` + + For POA-chains, the blockheader will use the `sealFields` (instead of mixHash and nonce) which are already RLP-encoded and should be added as raw data when using rlp.encode. + + ```js + if (keccak256(blockHeader) !== singedBlockHash) + throw new Error('Invalid Block') + ``` + + In case of the `eth_getBlockTransactionCountBy...`, the proof contains the full blockHeader already serilalized plus all transactionHashes. + This is needed in order to verify them in a merkle tree and compare them with the `transactionRoot`. + + + Requests requiring proof for blocks will return a proof of type `blockProof`. Depending on the request, the proof will contain the following properties: + + - `type` : constant : `blockProof` + - `signatures` : a array of signatures from the signers (if requested) of the requested block. + - `transactions`: a array of raw transactions of the block. This is only needed the last parameter of the request (includeTransactions) is `false`, In this case the result only contains the transactionHashes, but in order to verify we need to be able to build the complete merkle-trie, where the raw transactions are needed. If the complete transactions are included the raw transactions can be build from those values. + - `finalityBlocks`: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + - `uncles`: only if `fullProof` is requested we add all blockheaders of the uncles to the proof in order to verify the uncleRoot. + + + eth_getBlockByHash: + descr: | + Returns information about a block by hash. + + See [eth_getBlockByHash](https://eth.wiki/json-rpc/API#eth_getBlockByHash) for spec. + + params: + blockHash: + descr: the blockHash of the block + type: bytes32 + fullTx: + descr: if true the full transactions are contained in the result. + type: bool + result: + optional: true + options: + - params: + fullTx: false + name: getBlockByHash + descr: returns the given Block by hash with transactionHashes + result: + type: blockdataWithTxHashes + - params: + fullTx: true + name: getBlockByHashWithTx + descr: returns the given Block by hash with full transaction data + result: + type: blockdata + descr: the blockdata, or in case the block with that number does not exist, `null` will be returned. + type: blockdata + + proof: + alias: eth_getBlockByNumber + example: + request: + - '0x2baa54adcd8a105cdedfd9c6635d48d07b8f0e805af0a5853190c179e5a18585' + - false + response: + author: '0x0000000000000000000000000000000000000000' + difficulty: '0x2' + extraData: '0x696e667572612d696f0000000000000...31570f1e500' + gasLimit: '0x7a1200' + gasUsed: '0x20e145' + hash: '0x2baa54adcd8a105cdedfd9c6635d48d07b8f0e805af0a5853190c179e5a18585' + logsBloom: '0x000008000000000000...00400100000000080' + miner: '0x0000000000000000000000000000000000000000' + number: '0x449956' + parentHash: '0x2c2a4fcd11aa9aea6b9767651a10e7dbd2bcddbdaba703c74458ad6faf7c2694' + receiptsRoot: '0x0240b90272b5600bef7e25d0894868f85125174c2f387ef3236fc9ed9bfb3eff' + sealFields: + - '0xa00000000000000000000000000000000000000000000000000000000000000000' + - '0x880000000000000000' + sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347' + size: '0x74b' + stateRoot: '0xf44699575afd2668060be5ba77e66e1e80edb77ad1b5070969ddfa63da6a4910' + timestamp: '0x605aec86' + totalDifficulty: '0x6564de' + transactions: + - '0xcb7edfdb3229c9beeb418ab1ef1a3c9210ecfb22f0157791c3287085d798da58' + - '0x0fb803696521ba109c40b3eecb773c93dc6ee891172af0f620c8d44c05198641' + - '0x3ef6725cab4470889c3c7d53609a5d4b263701f5891aa98c9ed48b73b6b2fb75' + - '0x4010c4c112514756dcdcf14f91117503826dcbe15b03a1636c07aa713da24b8d' + - '0xd9c14daa5e2e9cc955534865365ef6bde3045c70e3a984a74c298606c4d67bb5' + - '0xfa2326237ba5dcca2127241562be16b68c48fed93d29add8d62f79a00518c2d8' + transactionsRoot: '0xddbbd7bf723abdfe885539406540671c2c0eb97684972175ad199258c75416cc' + uncles: [] + in3: + proof: + type: blockProof + signatures: [] + transactions: + - '0xf8ac830331f78449504f80830186a094f74a...8a83ce8dc' + - '0xf8ac830331f88449504f80830186a094f74a...a81c2f1fee77' + - '0xf8a91e843b9aca008315a92594f0277caffea...c30d64dd139' + - '0xf8c601843b9aca008305573094309906d7b701...62f5e7a2319a' + - '0xf8c680843b9aca008305573094309906d7b701...78289116eac194e' + - '0xf9014b82020a843b9aca0083010f6894786f8d72...b649' + + + eth_getBlockTransactionCountByHash: + descr: returns the number of transactions. For Spec, see [eth_getBlockTransactionCountByHash](https://eth.wiki/json-rpc/API#eth_getBlockTransactionCountByHash). + params: + blockHash: + descr: the blockHash of the block + type: bytes32 + proof: + alias: eth_getUncleCountByBlockNumber + result: + optional: true + descr: the number of transactions in the block + type: int + + eth_getBlockTransactionCountByNumber: + descr: returns the number of transactions. For Spec, see [eth_getBlockTransactionCountByNumber](https://eth.wiki/json-rpc/API#eth_getBlockTransactionCountByNumber). + params: + blockNumber: + descr: the blockNumber of the block + type: uint64 + proof: + alias: eth_getUncleCountByBlockNumber + result: + optional: true + descr: the number of transactions in the block + type: int + + eth_getUncleCountByBlockHash: + descr: returns the number of uncles. For Spec, see [eth_getUncleCountByBlockHash](https://eth.wiki/json-rpc/API#eth_getUncleCountByBlockHash). + params: + blockHash: + descr: the blockHash of the block + type: bytes32 + proof: + alias: eth_getUncleCountByBlockNumber + result: + optional: true + descr: the number of uncles + type: int + + eth_getUncleCountByBlockNumber: + descr: returns the number of uncles. For Spec, see [eth_getUncleCountByBlockNumber](https://eth.wiki/json-rpc/API#eth_getUncleCountByBlockNumber). + params: + blockNumber: + descr: the blockNumber of the block + type: uint64 + proof: + descr: | + Requests requiring proof for blocks will return a proof of type `blockProof`. Depending on the request, the proof will contain the following properties: + + - `type` : constant : `blockProof` + - `signatures` : a array of signatures from the signers (if requested) of the requested block. + - `block` : the serialized blockheader + - `transactions`: a array of raw transactions of the block. This is only needed if the number of transactions are requested. + - `finalityBlocks`: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + - `uncles`: a array of blockheaders of the uncles of the block. This is only needed if the number of uncles are requested. + result: + optional: true + descr: the number of uncles + type: int + + eth_getTransactionByBlockHashAndIndex: + descr: | + returns the transaction data. + + See JSON-RPC-Spec for [eth_getTransactionByBlockHashAndIndex](https://eth.wiki/json-rpc/API#eth_getTransactionByBlockHashAndIndex) for more details. + params: + blockHash: + descr: the blockhash containing the transaction. + type: bytes32 + index: + descr: the transactionIndex + type: int + result: + optional: true + descr: the transactiondata or `null` if it does not exist + type: transactiondata + proof: + alias: eth_getTransactionByHash + + example: + request: + - '0x4fc08daf8d670a23eba7a1aca1f09591c19147305c64d25e1ddd3dd43ff658ee' + - '0xd5' + response: + blockHash: '0x4fc08daf8d670a23eba7a1aca1f09591c19147305c64d25e1ddd3dd43ff658ee' + blockNumber: '0xb8a4a9' + from: '0xcaa6cfc2ca92cabbdbce5a46901ee8b831e00a98' + gas: '0xac6b' + gasPrice: '0x1bf08eb000' + hash: '0xd635a97452d604f735116d9de29ac946e9987a20f99607fb03516ef267ea0eea' + input: '0x095ea7b300000000000000000000000...a7640000' + nonce: '0xa' + to: '0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce' + transactionIndex: '0xd5' + value: '0x0' + type: '0x0' + v: '0x25' + r: '0xb18e0928c988d898d3217b145d78439072db15ea7de1005a73cf5feaf01a57d4' + s: '0x6b530c2613f543f9e26ef9c27a7986c748fbc856aaeacd6000a8ff46d2a2dd78' + in3: + proof: + type: transactionProof + block: '0xf90212a033a7afd1b9...fa16cf2' + merkleProof: + - '0xf90131a0...91604f2f58080808080808080' + - '0xf851a06f...0808080808080808080' + - '0xf8d18080...8a2ac871c5808080' + - '0xf90111a05...0808080808080808080' + - '0xf8ae20b8...000a8ff46d2a2dd78' + txIndex: 213 + signatures: [] + + + eth_getTransactionByBlockNumberAndIndex: + descr: | + returns the transaction data. + + See JSON-RPC-Spec for [eth_getTransactionByBlockNumberAndIndex](https://eth.wiki/json-rpc/API#eth_getTransactionByBlockNumberAndIndex) for more details. + params: + blockNumber: + descr: the block number containing the transaction. + type: uint64 + index: + descr: the transactionIndex + type: int + result: + optional: true + descr: the transactiondata or `null` if it does not exist + type: transactiondata + proof: + alias: eth_getTransactionByHash + + example: + request: + - '0xb8a4a9' + - '0xd5' + response: + blockHash: '0x4fc08daf8d670a23eba7a1aca1f09591c19147305c64d25e1ddd3dd43ff658ee' + blockNumber: '0xb8a4a9' + from: '0xcaa6cfc2ca92cabbdbce5a46901ee8b831e00a98' + gas: '0xac6b' + gasPrice: '0x1bf08eb000' + hash: '0xd635a97452d604f735116d9de29ac946e9987a20f99607fb03516ef267ea0eea' + input: '0x095ea7b300000000000000000000000...a7640000' + nonce: '0xa' + to: '0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce' + transactionIndex: '0xd5' + value: '0x0' + type: '0x0' + v: '0x25' + r: '0xb18e0928c988d898d3217b145d78439072db15ea7de1005a73cf5feaf01a57d4' + s: '0x6b530c2613f543f9e26ef9c27a7986c748fbc856aaeacd6000a8ff46d2a2dd78' + in3: + proof: + type: transactionProof + block: '0xf90212a033a7afd1b9...fa16cf2' + merkleProof: + - '0xf90131a0...91604f2f58080808080808080' + - '0xf851a06f...0808080808080808080' + - '0xf8d18080...8a2ac871c5808080' + - '0xf90111a05...0808080808080808080' + - '0xf8ae20b8...000a8ff46d2a2dd78' + txIndex: 213 + signatures: [] + + + eth_getTransactionByHash: + descr: | + returns the transaction data. + + See JSON-RPC-Spec for [eth_getTransactionByHash](https://eth.wiki/json-rpc/API#eth_getTransactionByHash) for more details. + params: + txHash: + descr: the transactionHash of the transaction. + type: bytes32 + result: + optional: true + descr: the transactiondata or `null` if it does not exist + type: transactiondata + + + example: + request: + - '0xe9c15c3b26342e3287bb069e433de48ac3fa4ddd32a31b48e426d19d761d7e9b' + response: + blockHash: '0x4fc08daf8d670a23eba7a1aca1f09591c19147305c64d25e1ddd3dd43ff658ee' + blockNumber: '0xb8a4a9' + from: '0xcaa6cfc2ca92cabbdbce5a46901ee8b831e00a98' + gas: '0xac6b' + gasPrice: '0x1bf08eb000' + hash: '0xd635a97452d604f735116d9de29ac946e9987a20f99607fb03516ef267ea0eea' + input: '0x095ea7b300000000000000000000000...a7640000' + nonce: '0xa' + to: '0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce' + transactionIndex: '0xd5' + value: '0x0' + type: '0x0' + v: '0x25' + r: '0xb18e0928c988d898d3217b145d78439072db15ea7de1005a73cf5feaf01a57d4' + s: '0x6b530c2613f543f9e26ef9c27a7986c748fbc856aaeacd6000a8ff46d2a2dd78' + in3: + proof: + type: transactionProof + block: '0xf90212a033a7afd1b9...fa16cf2' + merkleProof: + - '0xf90131a0...91604f2f58080808080808080' + - '0xf851a06f...0808080808080808080' + - '0xf8d18080...8a2ac871c5808080' + - '0xf90111a05...0808080808080808080' + - '0xf8ae20b8...000a8ff46d2a2dd78' + txIndex: 213 + signatures: [] + + + proof: + descr: | + ```eval_rst + .. graphviz:: + + digraph minimal_nonplanar_graphs { + + fontname="Helvetica" + subgraph all { + + node [ fontsize = "12", style="", color=black fontname="Helvetica", shape=record ] + + subgraph block_header { + label="blockheader" style="" color=black + + bheader[ label="parentHash|...|transactionRoot|receiptRoot|stateRoot"] + troot:a -> bheader:tr + } + + subgraph cluster_client_registry { + label="Transaction Trie" color=lightblue style=filled + + troot[label="|0x123456|||||"] + ta[label="|0x123456||0xabcdef|||"] + tb[label="|0x98765||0xfcab34|||"] + tval[label="transaction data"] + + ta:a -> troot:a + tb:a -> troot:a + tval:a -> ta:a + } + + + } + } + + ``` + + In order to prove the transaction data, each transaction of the containing block must be serialized + + ```js + transaction = rlp.encode([ + uint( tx.nonce ), + uint( tx.gasPrice ), + uint( tx.gas || tx.gasLimit ), + address( tx.to ), + uint( tx.value ), + bytes( tx.input || tx.data ), + uint( tx.v ), + uint( tx.r ), + uint( tx.s ) + ]) + ``` + + and stored in a merkle tree with `rlp.encode(transactionIndex)` as key or path, since the blockheader only contains the `transactionRoot`, which is the root-hash of the resulting merkle tree. A merkle-proof with the transactionIndex of the target transaction will then be created from this tree. + + If the request requires proof (`verification`: `proof`) the node will provide an Transaction Proof as part of the in3-section of the response. + This proof section contains the following properties: + + - `type` : constant : `transactionProof` + - `block` : the serialized blockheader of the requested transaction. + - `signatures` : a array of signatures from the signers (if requested) of the above block. + - `txIndex` : The TransactionIndex as used in the MerkleProof ( not needed if the methode was `eth_getTransactionByBlock...`, since already given) + - `merkleProof`: the serialized nodes of the Transaction trie starting with the root node. + - `finalityBlocks`: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + + While there is no proof for a non existing transaction, if the request was a `eth_getTransactionByBlock...` the node must deliver a partial merkle-proof to verify that this node does not exist. + + + + eth_getLogs: + descr: searches for events matching the given criteria. See [eth_getLogs](https://eth.wiki/json-rpc/API#eth_getLogs) for the spec. + proof: + descr: | + Since logs or events are based on the TransactionReceipts, the only way to prove them is by proving the TransactionReceipt each event belongs to. + + That's why this proof needs to provide: + - all blockheaders where these events occured + - all TransactionReceipts plus their MerkleProof of the logs + - all MerkleProofs for the transactions in order to prove the transactionIndex + + The proof data structure will look like this: + + ```ts + Proof { + type: 'logProof', + logProof: { + [blockNr: string]: { // the blockNumber in hex as key + block : string // serialized blockheader + receipts: { + [txHash: string]: { // the transactionHash as key + txIndex: number // transactionIndex within the block + txProof: string[] // the merkle Proof-Array for the transaction + proof: string[] // the merkle Proof-Array for the receipts + } + } + } + } + } + ``` + + + In order to create the proof, we group all events into their blocks and transactions, so we only need to provide the blockheader once per block. + The merkle-proofs for receipts are created as described in the [Receipt Proof](#eth-gettransactionreceipt). + + + If the request requires proof (`verification`: `proof`) the node will provide an Transaction Proof as part of the in3-section of the response. + type: + type: + descr: proofType, which is `logProof` + type: string + logProof: + descr: The proof for all the receipts. This structure contains an object with the blockNumbers as keys. Each block contains the blockheader and the receipt proofs. + type: + block: + descr: serialized blockheader + type: bytes + receipts: + descr: array of proofs for the transayctionreceipts within the block + type: + txIndex: + descr: transactionIndex within the block + type: int + txProof: + descr: the merkle Proof-Array for the transaction + type: bytes[] + proof: + descr: the merkle Proof-Array for the receipt + type: bytes[] + signatures: + descr: the array of signatures for all used blocks in the result. + type: signature[] + finalityBlocks: + descr: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + type: bytes[] + params: + filter: + descr: The filter criteria for the events. + type: + fromBlock: + descr: Integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions. + type: uint64 + optional: true + default: latest + toBlock: + descr: Integer block number, or "latest" for the last mined block or "pending", "earliest" for not yet mined transactions. + type: uint64 + optional: true + default: latest + address: + descr: Contract address or a list of addresses from which logs should originate. + type: address + optional: true + topics: + descr: Array of 32 Bytes DATA topics. Topics are order-dependent. Each topic can also be an array of DATA with “or” options. + type: bytes32 + nullable: true + array: true + optional: true + blockhash: + descr: With the addition of EIP-234, blockHash will be a new filter option which restricts the logs returned to the single block with the 32-byte hash blockHash. Using blockHash is equivalent to fromBlock = toBlock = the block number with hash blockHash. If blockHash is present in in the filter criteria, then neither fromBlock nor toBlock are allowed. + type: bytes32 + optional: true + result: + array: true + type: ethlog + descr: array with all found event matching the specified filter + + + + eth_getBalance: + descr: gets the balance of an account for a given block + params: + account: + descr: address of the account + type: address + block: + internalDefault : latest + optionalAPI: true + descr: the blockNumber or `latest` + type: uint64 + result: + descr: the balance + type: uint256 + proof: + alias: eth_getStorageAt + example: + request: + - "0x2e333ec090f1028df0a3c39a918063443be82b2b" + - latest + response: '0x20599832af6ec00' + in3: + proof: + type: accountProof + block: '0xf90212a0af...5643f426d' + signatures: [] + accounts: + '0x2e333EC090f1028df0a3c39a918063443Be82B2b': + address: '0x2e333ec090f1028df0a3c39a918063443be82b2b' + accountProof: + - '0xf90211a099a5e...6d9f924480' + - '0xf90211a052b61...b19ff23445180' + - '0xf90211a0cc125...7e7afd9170280' + - '0xf90211a088c91...555f0b76fc6ec80' + - '0xf90211a0641a3...477d355d557a180' + - '0xf90211a0619e5...5977318c9487280' + - '0xf90111a0e25a1...641683d34adae808080' + - '0xf86e9d3f681a18...2273b7bfad8045d85a470' + balance: '0x20599832af6ec00' + codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' + nonce: '0x5' + storageHash: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' + storageProof: [] + + + + eth_getTransactionCount: + descr: gets the nonce or number of transaction sent from this account at a given block + params: + account: + descr: address of the account + type: address + block: + internalDefault : latest + optionalAPI: true + descr: the blockNumber or `latest` + type: uint64 + result: + descr: the nonce + type: uint64 + proof: + alias: eth_getStorageAt + example: + request: + - "0x2e333ec090f1028df0a3c39a918063443be82b2b" + - latest + response: '0x5' + in3: + proof: + type: accountProof + block: '0xf90212a0af...5643f426d' + signatures: [] + accounts: + '0x2e333EC090f1028df0a3c39a918063443Be82B2b': + address: '0x2e333ec090f1028df0a3c39a918063443be82b2b' + accountProof: + - '0xf90211a099a5e...6d9f924480' + - '0xf90211a052b61...b19ff23445180' + - '0xf90211a0cc125...7e7afd9170280' + - '0xf90211a088c91...555f0b76fc6ec80' + - '0xf90211a0641a3...477d355d557a180' + - '0xf90211a0619e5...5977318c9487280' + - '0xf90111a0e25a1...641683d34adae808080' + - '0xf86e9d3f681a18...2273b7bfad8045d85a470' + balance: '0x20599832af6ec00' + codeHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470' + nonce: '0x5' + storageHash: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421' + storageProof: [] + + + + + eth_getCode: + descr: gets the code of a given contract + params: + account: + descr: address of the account + type: address + block: + internalDefault : latest + optional: true + descr: the blockNumber or `latest` + type: uint64 + result: + descr: the code as hex + proof: + alias: eth_getStorageAt + example: + request: + - "0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f" + - latest + response: '0x6080604052348...6c634300050a0040' + in3: + proof: + type: accountProof + block: '0xf9020da02776...8ba1d5458be3b98' + signatures: [] + accounts: + '0xaC1b824795E1EB1F6e609FE0dA9b9af8bEaAb60F': + address: '0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f' + accountProof: + - '0xf90211a03...c41e862bd80' + - '0xf90211a02...c5766ac3ec9180' + - '0xf90211a0f...cdad27ecdfbc1c4c66e680' + - '0xf90211a08...84621739c3777ea1d5080' + - '0xf90211a00...02388c08615b82ef0320614380' + - '0xf90211a03...1b16a8c050f61d80' + - '0xf8f18080a...cafe05823be8080' + - '0xf8669d3ad8...903305697a1' + balance: '0x0' + codeHash: '0x29140efcd5358d1dd75badfaa179e3df0dd53f17a883a30152d82903305697a1' + nonce: '0x1' + storageHash: '0x4d6c5972bcc0c8229c8b041df4aa70879e37e9f7eb47530e4232b317438524ed' + storageProof: [] + + + + eth_getStorageAt: + descr: gets the storage value of a given key + params: + account: + descr: address of the account + type: address + key: + descr: key to look for + type: bytes32 + block: + internalDefault : latest + optional: true + descr: the blockNumber or`latest` + type: uint64 + result: + descr: the value of the storage slot. + proof: + type: + type: + descr: proof type, which is `accountProof` + type: string + block: + descr: serialized blockheader + type: bytes + accounts: + descr: object with all required accounts (using the address as keys) + type: + address: + descr: address of the account + type: address + balance: + descr: the balance + type: uint256 + nonce: + descr: nonce of the account + type: uint256 + codeHash: + descr: codehash of the account + type: bytes32 + storageHash: + descr: MerkleRoot of the Storage Trie + type: bytes32 + accountProof: + descr: MerkleProof of this account-node + type: bytes[] + storageProof: + descr: Array of Proofs for all required storage values + type: + key: + descr: the storage key (or hash) + type: bytes32 + value: + descr: the storage value + type: bytes32 + proof: + array: true + descr: the merkleProof of the value down to the storageHash as MerkleRoot + type: bytes + + signatures: + descr: the array of signatures for all used blocks in the result. + array: true + type: signature + finalityBlocks: + array: true + descr: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + type: bytes + + descr: | + Each of these account values are stored in the account-object: + + ```js + account = rlp.encode([ + uint( nonce), + uint( balance), + bytes32( storageHash || ethUtil.KECCAK256_RLP), + bytes32( codeHash || ethUtil.KECCAK256_NULL) + ]) + ``` + + The proof of an account is created by taking the state merkle tree and creating a MerkleProof. Since all of the above RPC-methods only provide a single value, the proof must contain all four values in order to encode them and verify the value of the MerkleProof. + + For verification, the `stateRoot` of the blockHeader is used and `keccak(accountProof.address)` as the path or key within the merkle tree. + + ```js + verifyMerkleProof( + block.stateRoot, // expected merkle root + keccak(accountProof.address), // path, which is the hashed address + accountProof.accountProof), // array of Buffer with the merkle-proof-data + isNotExistend(accountProof) ? null : serializeAccount(accountProof), // the expected serialized account + ) + ``` + + In case the account does not exist yet (which is the case if `none` == `startNonce` and `codeHash` == `'0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'`), the proof may end with one of these nodes: + + - The last node is a branch, where the child of the next step does not exist. + - The last node is a leaf with a different relative key. + + Both would prove that this key does not exist. + + For `eth_getStorageAt`, an additional storage proof is required. This is created by using the `storageHash` of the account and creating a MerkleProof using the hash of the storage key (`keccak(key)`) as path. + + + ```js + verifyMerkleProof( + bytes32( accountProof.storageHash ), // the storageRoot of the account + keccak(bytes32(s.key)), // the path, which is the hash of the key + s.proof.map(bytes), // array of Buffer with the merkle-proof-data + s.value === '0x0' ? null : util.rlp.encode(s.value) // the expected value or none to proof non-existence + )) + ``` + + + ```eval_rst + .. graphviz:: + + digraph minimal_nonplanar_graphs { + + fontname="Helvetica" + subgraph all { + + node [ fontsize = "12", style="", color=black fontname="Helvetica", shape=record ] + + subgraph cluster_block_header { + label="Blockheader" color=white style=filled + + bheader[ label="parentHash|...|stateRoot|transactionRoot|receiptRoot|..."] + } + + subgraph cluster_state_trie { + label="State Trie" color=lightblue style=filled + + troot[label="|0x123456|||||0xabcdef"] + ta[label="|0x123456||0xabcdef|||"] + tb[label="|0x98765||0xfcab34|||"] + tval[label="nonce|balance|storageHash|codeHash"] + + ta:a -> troot:a + tb:a -> troot:b + tval:a -> ta:a + } + + subgraph cluster_storage_trie { + label="Storage Trie" color=lightblue style=filled + + sroot[label="|0x123456|||||0xabcdef"] + sa[label="|0x123456||0xabcdef|||"] + sb[label="|0x98765||0xfcab34|||"] + sval[label="storage value"] + + sa:a -> sroot:a + sb:a -> sroot:b + sval:a -> sa:a + } + + sroot:a -> tval:sr + troot:a -> bheader:tr + + } + } + + ``` + + + + + If the request requires proof (`verification`: `proof`) the node will provide an Account Proof as part of the in3-section of the response. + + example: + request: + - "0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f" + - "0x0" + - latest + response: '0x19' + in3: + proof: + type: accountProof + block: '0xf90218a0e625bee...87a38707dbbc' + signatures: [] + accounts: + '0xaC1b824795E1EB1F6e609FE0dA9b9af8bEaAb60F': + address: '0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f' + accountProof: + - '0xf90211a0cef18...56e5cfcf3ef70de80' + - '0xf90211a0b67e01...2a13db20dcf291d533480' + - '0xf90211a05df65...be23ffb94580' + - '0xf90211a0825413...75d61184621739c3777ea1d5080' + - '0xf90211a000a403...82ef0320614380' + - '0xf90211a03b0114...a6e41b16a8c050f61d80' + - '0xf8f18080a01a27...96fcdfe84cafe05823be8080' + - '0xf8669d3ad8a871b...d82903305697a1' + balance: '0x0' + codeHash: '0x29140efcd5358d1dd75badfaa179e3df0dd53f17a883a30152d82903305697a1' + nonce: '0x1' + storageHash: '0x4d6c5972bcc0c8229c8b041df4aa70879e37e9f7eb47530e4232b317438524ed' + storageProof: + - key: '0x0' + value: '0x19' + proof: + - '0xf90211a084b9482...1ad85a4f2f1680' + - '0xf901b1a0625f8d3...6e0788855d2780' + - '0xf851a08a14eff77...08080808080808080' + - '0xe19f3decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56319' + + eth_sendTransaction: + descr: signs and sends a Transaction + params: + tx: + descr: the transactiondata to send + type: transaction + result: + descr: the transactionHash + proof: + descr: No proof from the nodes are required, because the client can generate the TransactionHash itself. This means to ensure the success of a transaction the receipt needs to be verified. + + eth_sendTransactionAndWait: + descr: signs and sends a Transaction, but then waits until the transaction receipt can be verified. Depending on the finality of the nodes, this may take a while, since only final blocks will be signed by the nodes. + params: + tx: + descr: the transactiondata to send + type: transaction + result: + descr: the transactionReceipt + type: transactionReceipt + + + eth_sendRawTransaction: + descr: sends or broadcasts a prviously signed raw transaction. See [eth_sendRawTransaction](https://eth.wiki/json-rpc/API#eth_sendRawTransaction) + params: + tx: + descr: the raw signed transactiondata to send + type: bytes + result: + descr: the transactionhash + type: bytes32 + + + diff --git a/c/src/verifier/eth1/basic/sign_tx.c b/c/src/verifier/eth1/basic/sign_tx.c index 1c3bb5c5c..413cb8d3f 100644 --- a/c/src/verifier/eth1/basic/sign_tx.c +++ b/c/src/verifier/eth1/basic/sign_tx.c @@ -62,7 +62,7 @@ static inline bytes_t getl(d_token_t* t, uint16_t key, size_t l) { /** return data from the client.*/ static in3_ret_t get_from_nodes(in3_req_t* parent, char* method, char* params, bytes_t* dst) { // check if the method is already existing - in3_req_t* ctx = req_find_required(parent, method); + in3_req_t* ctx = req_find_required(parent, method, NULL); if (ctx) { // found one - so we check if it is useable. switch (in3_req_state(ctx)) { @@ -168,7 +168,7 @@ in3_ret_t eth_prepare_unsigned_tx(d_token_t* tx, in3_req_t* ctx, bytes_t* dst) { chain_id_t chain_id = ctx->client->chain.chain_id; if (chain_id == CHAIN_ID_LOCAL) { d_token_t* r = NULL; - TRY(req_send_sub_request(ctx, "eth_chainId", "", NULL, &r)) + TRY(req_send_sub_request(ctx, "eth_chainId", "", NULL, &r, NULL)) chain_id = d_long(r); } TRY(get_from_address(tx, ctx, from)) @@ -197,8 +197,8 @@ in3_ret_t eth_prepare_unsigned_tx(d_token_t* tx, in3_req_t* ctx, bytes_t* dst) { } // cleanup subcontexts - TRY(req_remove_required(ctx, req_find_required(ctx, "eth_getTransactionCount"), false)) - TRY(req_remove_required(ctx, req_find_required(ctx, "eth_gasPrice"), false)) + TRY(req_remove_required(ctx, req_find_required(ctx, "eth_getTransactionCount", NULL), false)) + TRY(req_remove_required(ctx, req_find_required(ctx, "eth_gasPrice", NULL), false)) return IN3_OK; } @@ -213,7 +213,7 @@ in3_ret_t eth_sign_raw_tx(bytes_t raw_tx, in3_req_t* ctx, address_t from, bytes_ chain_id_t chain_id = ctx->client->chain.chain_id; if (chain_id == CHAIN_ID_LOCAL) { d_token_t* r = NULL; - TRY(req_send_sub_request(ctx, "eth_chainId", "", NULL, &r)) + TRY(req_send_sub_request(ctx, "eth_chainId", "", NULL, &r, NULL)) chain_id = d_long(r); } @@ -268,7 +268,7 @@ in3_ret_t handle_eth_sendTransaction(in3_req_t* ctx, d_token_t* req) { // is there a pending signature? // we get the raw transaction from this request - in3_req_t* sig_ctx = req_find_required(ctx, "sign_ec_hash"); + in3_req_t* sig_ctx = req_find_required(ctx, "sign_ec_hash", NULL); if (sig_ctx) { bytes_t raw = *d_get_bytes_at(d_get(sig_ctx->requests[0], K_PARAMS), 0); unsigned_tx = bytes(_malloc(raw.len), raw.len); diff --git a/c/src/verifier/eth1/evm/accounts.c b/c/src/verifier/eth1/evm/accounts.c index acddf18fa..818ff7f1d 100644 --- a/c/src/verifier/eth1/evm/accounts.c +++ b/c/src/verifier/eth1/evm/accounts.c @@ -38,23 +38,26 @@ #include "big.h" #include "gas.h" #ifdef EVM_GAS -/** - * sets a variable value to 32byte word. - */ -account_t* evm_get_account(evm_t* evm, address_t adr, wlen_t create) { - if (!adr) return NULL; +int evm_get_account(evm_t* evm, address_t adr, wlen_t create, account_t** dst) { + if (!adr) { + *dst = NULL; + return 0; + } account_t* ac = evm->accounts; // check if we already have the account. while (ac) { - if (memcmp(ac->address, adr, 20) == 0) return ac; + if (memcmp(ac->address, adr, 20) == 0) { + *dst = ac; + return 0; + } ac = ac->next; } // if this is a internal call take it from the parent if (evm->parent) { - ac = evm_get_account(evm->parent, adr, create); + TRY(evm_get_account(evm->parent, adr, create, &ac)) if (ac) { // clone and add account @@ -63,7 +66,8 @@ account_t* evm_get_account(evm_t* evm, address_t adr, wlen_t create) { a->storage = NULL; a->next = evm->accounts; evm->accounts = a; - return a; + *dst = a; + return 0; } } @@ -73,9 +77,12 @@ account_t* evm_get_account(evm_t* evm, address_t adr, wlen_t create) { int l_code_size = evm->env(evm, EVM_ENV_CODE_SIZE, adr, 20, &code_size, 0, 0); int l_nonce = evm->env(evm, EVM_ENV_NONCE, adr, 20, &nonce, 0, 0); - if (l_balance >= 0) optimize_len(balance, l_balance); - if (l_nonce >= 0) optimize_len(nonce, l_nonce); - if (l_code_size >= 0) optimize_len(code_size, l_code_size); + if (l_balance < 0) return l_balance; + if (l_code_size < 0) return l_code_size; + if (l_nonce < 0) return l_nonce; + optimize_len(balance, l_balance); + optimize_len(code_size, l_code_size); + optimize_len(nonce, l_nonce); // is this a non-empty account? (or do we have to create one) if (create || l_balance > 1 || l_nonce > 1 || l_code_size > 1 || (l_balance == 1 && *balance) || (l_nonce == 1 && *nonce) || (l_code_size == 1 && *code_size)) { @@ -97,13 +104,17 @@ account_t* evm_get_account(evm_t* evm, address_t adr, wlen_t create) { uint256_set(balance, l_balance, ac->balance); uint256_set(nonce, l_nonce, ac->nonce); } - return ac; + + *dst = ac; + return 0; } -account_t* evm_create_account(evm_t* evm, uint8_t* data, uint32_t l_data, address_t code_address, address_t caller) { +int evm_create_account(evm_t* evm, uint8_t* data, uint32_t l_data, address_t code_address, address_t caller, account_t** dst) { - account_t* new_account = NULL; - new_account = evm_get_account(evm, code_address, 1); + account_t* sender_account = NULL; + account_t* new_account = NULL; + int r = evm_get_account(evm, code_address, 1, &new_account); + if (r < 0) return r; // this is a create-call evm->code = bytes(data, l_data); evm->call_data.len = 0; @@ -111,15 +122,21 @@ account_t* evm_create_account(evm_t* evm, uint8_t* data, uint32_t l_data, addres new_account->nonce[31] = 1; // increment the nonce of the sender - account_t* sender_account = evm_get_account(evm, caller, 1); - bytes32_t new_nonce; + r = evm_get_account(evm, caller, 1, &sender_account); + if (r < 0) return r; + bytes32_t new_nonce; increment_nonce(sender_account, new_nonce); - return new_account; + *dst = new_account; + return 0; } -storage_t* evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_key_len, wlen_t create) { - account_t* ac = evm_get_account(evm, adr, create); - if (!ac) return NULL; +int evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_key_len, wlen_t create, storage_t** dst) { + account_t* ac = NULL; + TRY(evm_get_account(evm, adr, create, &ac)); + if (!ac) { + *dst = NULL; + return 0; + } storage_t* s = ac->storage; @@ -129,20 +146,25 @@ storage_t* evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_k // find existing entry while (s) { - if (memcmp(s->key, key_data, 32) == 0) return s; + if (memcmp(s->key, key_data, 32) == 0) { + *dst = s; + return 0; + } s = s->next; } // not found?, but if we have parents, we try to copy the entry from there first if (evm->parent) { - storage_t* parent_s = evm_get_storage(evm->parent, adr, s_key, s_key_len, create); + storage_t* parent_s = NULL; + TRY(evm_get_storage(evm->parent, adr, s_key, s_key_len, create, &parent_s)); if (parent_s) { // clone and add account s = _malloc(sizeof(storage_t)); memcpy(s, parent_s, sizeof(storage_t)); s->next = ac->storage; ac->storage = s; - return s; + *dst = s; + return 0; } } @@ -152,6 +174,8 @@ storage_t* evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_k ? 0 : evm->env(evm, EVM_ENV_STORAGE, s_key, s_key_len, &data, 0, 0); + // if (l < 0) return l; + // if it does not exist and we have a value, we set it if (create || l > 1 || (l == 1 && *data)) { // create with key @@ -165,7 +189,8 @@ storage_t* evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_k // set the value uint256_set(data, l, s->value); } - return s; + *dst = s; + return 0; } void copy_state(evm_t* dst, evm_t* src) { @@ -259,16 +284,18 @@ void copy_state(evm_t* dst, evm_t* src) { /** * transfer a value to a account. */ -int transfer_value(evm_t* current, address_t from_account, address_t to_account, uint8_t* value, wlen_t value_len, uint32_t base_gas) { +int transfer_value(evm_t* current, address_t from_account, address_t to_account, uint8_t* value, wlen_t value_len, uint32_t base_gas, bool is_call) { if (big_is_zero(value, value_len)) return 0; // while the gas is handled by the parent, the new state is handled in the current evm, so we can roll it back. - evm_t* evm = current->parent ? current->parent : current; - - account_t* ac_from = evm_get_account(current, from_account, true); - account_t* ac_to = evm_get_account(current, to_account, false); + evm_t* evm = current->parent ? current->parent : current; + account_t* ac_from = NULL; + account_t* ac_to = NULL; uint8_t tmp[32], val[32]; + TRY(evm_get_account(current, from_account, true, &ac_from)) + TRY(evm_get_account(current, to_account, false, &ac_to)) + // we clone it because the value may point to the value we want to change. memcpy(val, value, value_len); value = val; @@ -276,13 +303,13 @@ int transfer_value(evm_t* current, address_t from_account, address_t to_account, if (!ac_to) { // to account does exist, so we create it and manage gas for new account subgas(G_NEWACCOUNT); - ac_to = evm_get_account(current, to_account, true); + TRY(evm_get_account(current, to_account, true, &ac_to)) } subgas(base_gas); if (ac_from) { // check if the balance of the sender is high enough - if (big_cmp(ac_from->balance, 32, value, value_len) < 0) return EVM_ERROR_BALANCE_TOO_LOW; + if (big_cmp(ac_from->balance, 32, value, value_len) < 0) return is_call ? 0 : EVM_ERROR_BALANCE_TOO_LOW; // sub balance from sender uint256_set(tmp, big_sub(ac_from->balance, 32, value, value_len, tmp), ac_from->balance); diff --git a/c/src/verifier/eth1/evm/accounts.h b/c/src/verifier/eth1/evm/accounts.h index e8f9a330b..cf47cd084 100644 --- a/c/src/verifier/eth1/evm/accounts.h +++ b/c/src/verifier/eth1/evm/accounts.h @@ -36,16 +36,15 @@ #include /** reads a account from the enviroment. */ -account_t* evm_get_account(evm_t* evm, address_t adr, wlen_t create); +int evm_get_account(evm_t* evm, address_t adr, wlen_t create, account_t** dst); /** get account storage */ -storage_t* evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_key_len, wlen_t create); +int evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_key_len, wlen_t create, storage_t** dst); +int evm_create_account(evm_t* evm, uint8_t* data, uint32_t l_data, address_t code_address, address_t caller, account_t** dst); /** copy state. */ void copy_state(evm_t* dst, evm_t* src); -int transfer_value(evm_t* current, address_t from_account, address_t to_account, uint8_t* value, wlen_t value_len, uint32_t base_gas); - -account_t* evm_create_account(evm_t* evm, uint8_t* data, uint32_t l_data, address_t code_address, address_t caller); +int transfer_value(evm_t* current, address_t from_account, address_t to_account, uint8_t* value, wlen_t value_len, uint32_t base_gas, bool is_call); void increment_nonce(account_t* ac, bytes32_t nonce_output); \ No newline at end of file diff --git a/c/src/verifier/eth1/evm/call.c b/c/src/verifier/eth1/evm/call.c index 2a4dc6ace..4d6816de7 100644 --- a/c/src/verifier/eth1/evm/call.c +++ b/c/src/verifier/eth1/evm/call.c @@ -33,6 +33,7 @@ *******************************************************************************/ #include "../../../core/client/plugin.h" +#include "../../../core/util/log.h" #include "../../../core/util/mem.h" #include "big.h" #include "evm.h" @@ -174,8 +175,9 @@ int evm_sub_call(evm_t* parent, UNUSED_VAR(gas); // create a new evm - evm_t evm; - int res = evm_prepare_evm(&evm, address, code_address, origin, caller, parent->env, parent->env_ptr, mode), success = 0; + evm_t evm; + int res = evm_prepare_evm(&evm, address, code_address, origin, caller, parent->env, parent->env_ptr, mode), success = 0; + if (res < 0) goto cleanup; uint32_t c_xfer = 0, old_gas = 0; #ifdef EVM_GAS @@ -248,12 +250,42 @@ int evm_sub_call(evm_t* parent, } } FINALIZE_SUBCALL_GAS(&evm, success, parent); + +cleanup: // clean up evm_free(&evm); // we always return 0 since a failure simply means we write a 0 on the stack. return res; } +#ifdef EVM_GAS + +static void add_log(json_ctx_t* receipt, int logs, logs_t* log, uint32_t* index) { + if (!log) return; + add_log(receipt, logs, log->next, index); + int l = json_create_object(receipt); + json_array_add_value(receipt, logs, receipt->result + l); + json_object_add_prop(receipt, l, key("transactionLogIndex"), json_create_int(receipt, *index)); + *index = *index + 1; + json_object_add_prop(receipt, l, key("address"), json_create_bytes(receipt, bytes(log->address, 20))); + json_object_add_prop(receipt, l, key("data"), json_create_bytes(receipt, log->data)); + int topics = json_create_array(receipt); + json_object_add_prop(receipt, l, key("topics"), receipt->result + topics); + + for (unsigned int i = 0; i < log->topics.len; i += 32) + json_array_add_value(receipt, topics, json_create_bytes(receipt, bytes(log->topics.data + i, 32))); +} + +static void add_receipt(evm_t* evm, json_ctx_t* receipt, uint64_t gas_used) { + int r = json_create_object(receipt); + json_object_add_prop(receipt, r, key("gasUsed"), json_create_int(receipt, gas_used)); + int logs = json_create_array(receipt); + json_object_add_prop(receipt, r, key("logs"), receipt->result + logs); + uint32_t log_index = 0; + add_log(receipt, logs, evm->logs, &log_index); +} +#endif + /** * run a evm-call */ @@ -261,10 +293,11 @@ int evm_call(void* vc, address_t address, uint8_t* value, wlen_t l_value, uint8_t* data, uint32_t l_data, - address_t caller, - uint64_t gas, - uint64_t chain_id, - bytes_t** result) { + address_t caller, + uint64_t gas, + uint64_t chain_id, + bytes_t** result, + json_ctx_t* receipt) { evm_t evm; int res = evm_prepare_evm(&evm, address, address, caller, caller, in3_get_env, vc, 0); @@ -277,7 +310,7 @@ int evm_call(void* vc, #ifdef EVM_GAS // we only transfer initial value if the we have caller - if (res == 0 && l > 1) res = transfer_value(&evm, caller, address, value, l_value, 0); + if (res == 0 && l > 1) res = transfer_value(&evm, caller, address, value, l_value, 0, true); #else UNUSED_VAR(gas); UNUSED_VAR(value); @@ -288,11 +321,21 @@ int evm_call(void* vc, #ifdef EVM_GAS evm.gas = gas; #endif - evm.call_data.data = data; - evm.call_data.len = l_data; + evm.call_value.data = value; + evm.call_value.len = l_value; + evm.call_data.data = data; + evm.call_data.len = l_data; if (res == 0) res = evm_run(&evm, address); if (res == 0 && evm.return_data.data) *result = b_dup(&evm.return_data); + +#ifdef EVM_GAS + if (receipt) + add_receipt(&evm, receipt, gas - evm.gas); +#else + UNUSED_VAR(receipt); +#endif + evm_free(&evm); return res; diff --git a/c/src/verifier/eth1/evm/evm.c b/c/src/verifier/eth1/evm/evm.c index 21c1bd9c9..32e16fd15 100644 --- a/c/src/verifier/eth1/evm/evm.c +++ b/c/src/verifier/eth1/evm/evm.c @@ -37,6 +37,7 @@ #include "../../../core/util/data.h" #include "../../../core/util/log.h" #include "../../../core/util/mem.h" +#include "../../../core/util/utils.h" #include "../../../third-party/crypto/bignum.h" #include "../nano/merkle.h" #include "../nano/serialize.h" @@ -516,8 +517,9 @@ int evm_run(evm_t* evm, address_t code_address) { // subtract gas cost for ceation transactions subgas(evm->return_data.len * G_CODEDEPOSIT); // Modify state - account_t* acc_adr = evm_get_account(evm, evm->account, true); - acc_adr->code = evm->return_data; + account_t* acc_adr = NULL; + TRY(evm_get_account(evm, evm->account, true, &acc_adr)) + acc_adr->code = evm->return_data; } } // debug gas output diff --git a/c/src/verifier/eth1/evm/evm.h b/c/src/verifier/eth1/evm/evm.h index 1482dc33e..050a79d1b 100644 --- a/c/src/verifier/eth1/evm/evm.h +++ b/c/src/verifier/eth1/evm/evm.h @@ -37,6 +37,7 @@ * */ #include "../../../core/util/bytes.h" +#include "../../../core/util/data.h" #ifndef evm_h__ #define evm_h__ int exit_zero(void); @@ -125,31 +126,34 @@ typedef enum evm_state { #define OP_EXTCODECOPY_GAS(evm) \ do { \ - account_t* ac = evm_get_account(evm, address, 0); \ + account_t* ac = NULL; \ + TRY(evm_get_account(evm, address, 0, &ac)) \ if (ac && ac->code.len) \ return evm_mem_write(evm, mem_pos, bytes(ac->code.data + code_pos, ac->code.len > (uint32_t) code_pos ? ac->code.len - code_pos : 0), data_len); \ } while (0) -#define OP_SLOAD_GAS(evm) \ - do { \ - storage_t* s = evm_get_storage(evm, evm->account, key, l, 0); \ - if (s) { \ - value = s->value; \ - l = 32; \ - while (value[0] == 0 && l > 1) { \ - l--; \ - value++; \ - } \ - return evm_stack_push(evm, value, l); \ - } \ +#define OP_SLOAD_GAS(evm) \ + do { \ + storage_t* s = NULL; \ + TRY(evm_get_storage(evm, evm->account, key, l, 0, &s)) \ + if (s) { \ + value = s->value; \ + l = 32; \ + while (value[0] == 0 && l > 1) { \ + l--; \ + value++; \ + } \ + return evm_stack_push(evm, value, l); \ + } \ } while (0) #define OP_ACCOUNT_GAS(evm, key, address, data, l) \ do { \ if (key != EVM_ENV_BLOCKHASH) { \ - account_t* ac = evm_get_account(evm, address, 0); \ - uint8_t tmp[4]; \ - uint8_t hash[32]; \ + account_t* ac = NULL; \ + TRY(evm_get_account(evm, address, 0, &ac)) \ + uint8_t tmp[4]; \ + uint8_t hash[32]; \ if (ac) { \ data = NULL; \ if (key == EVM_ENV_BALANCE) { \ @@ -218,6 +222,7 @@ typedef struct account_storage { typedef struct logs { bytes_t topics; bytes_t data; + address_t address; struct logs* next; } logs_t; @@ -297,10 +302,11 @@ int evm_call(void* vc, uint8_t address[20], uint8_t* value, wlen_t l_value, uint8_t* data, uint32_t l_data, - uint8_t caller[20], - uint64_t gas, - uint64_t chain_id, - bytes_t** result); + uint8_t caller[20], + uint64_t gas, + uint64_t chain_id, + bytes_t** result, + json_ctx_t* receipt); void evm_print_stack(evm_t* evm, uint64_t last_gas, uint32_t pos); void evm_free(evm_t* evm); @@ -309,9 +315,9 @@ int evm_execute(evm_t* evm); int evm_run(evm_t* evm, address_t code_address); #ifdef EVM_GAS -account_t* evm_get_account(evm_t* evm, uint8_t adr[20], wlen_t create); -storage_t* evm_get_storage(evm_t* evm, uint8_t adr[20], uint8_t* key, wlen_t keylen, wlen_t create); -int transfer_value(evm_t* evm, uint8_t from_account[20], uint8_t to_account[20], uint8_t* value, wlen_t value_len, uint32_t base_gas); +int evm_get_account(evm_t* evm, address_t adr, wlen_t create, account_t** dst); +int evm_get_storage(evm_t* evm, address_t adr, uint8_t* s_key, wlen_t s_key_len, wlen_t create, storage_t** dst); +int transfer_value(evm_t* evm, uint8_t from_account[20], uint8_t to_account[20], uint8_t* value, wlen_t value_len, uint32_t base_gas, bool is_call); #endif #endif \ No newline at end of file diff --git a/c/src/verifier/eth1/evm/gas.h b/c/src/verifier/eth1/evm/gas.h index 101d54be2..e80675061 100644 --- a/c/src/verifier/eth1/evm/gas.h +++ b/c/src/verifier/eth1/evm/gas.h @@ -142,69 +142,70 @@ void update_account_code(evm_t* evm, account_t* new_account); #define SUBGAS(evm, g) subgas(evm, g) #define KEEP_TRACK_GAS(evm) evm->gas #define FINALIZE_SUBCALL_GAS(evm, success, parent) finalize_subcall_gas(evm, success, parent) -#define UPDATE_SUBCALL_GAS(evm, parent, address, code_address, caller, gas, mode, value, l_value) \ - do { \ - /**************************************************************/ \ - /* According to the Yellow Paper: C_call = C_gascap + C_extra */ \ - /* Where: */ \ - /* C_gascap = min(L(parent.gas - C_extra), gas) */ \ - /* L(x) = x - (x/64) */ \ - /* C_extra = G_CALL + C_xfer + C_new */ \ - /* C_xfer = (value == 0) ? 0 : G_CALLVALUE */ \ - /* C_new = to_account ? 0 : G_NEWACCOUNT */ \ - /**************************************************************/ \ - bool no_address = false; \ - if (!address) { \ - /* Flag EVM with "creating transaction", so the gas for modifying state will be deduced later */ \ - evm.properties |= EVM_PROP_TXCREATE; \ - new_account = evm_create_account(&evm, evm.call_data.data, evm.call_data.len, code_address, caller); \ - no_address = true; \ - } \ - else { /* if no code was already set for execution */ \ - account_t* ac = evm_get_account(parent, code_address, 0); \ - if (ac) evm.code = ac->code; \ - } \ - if (res == 0 && !big_is_zero(value, l_value)) { \ - if (mode == EVM_CALL_MODE_STATIC) \ - res = EVM_ERROR_UNSUPPORTED_CALL_OPCODE; \ - else { \ - if (mode == EVM_CALL_MODE_CALL || mode == EVM_CALL_MODE_CALLCODE) { \ - evm.gas += G_CALLSTIPEND; \ - c_xfer = G_CALLVALUE; \ - } \ - /* Transfer value from caller and deduce (C_xfer + C_new) from parent gas*/ \ - res = transfer_value(&evm, parent->address, evm.address, value, l_value, c_xfer); \ - } \ - } \ - /* Calculate L(parent.gas - C_extra) */ \ - /* As C_extra was already deduced in previous steps, we calculate only L(parent.gas) now */ \ - uint64_t max_gas_provided = parent->gas - (parent->gas >> 6); \ - /* Calculate C_gascap */ \ - if (no_address) { \ - gas = max_gas_provided; \ - } \ - else \ - gas = min(gas, max_gas_provided); \ - /* Assign values to child EVM */ \ - evm.gas += gas; \ - evm.init_gas = gas; \ - evm.gas_price.data = parent->gas_price.data; \ - evm.gas_price.len = parent->gas_price.len; \ - if (res == 0) { \ - if (parent->gas < gas) { \ - if (parent->gas + c_xfer < gas) { \ - /* transaction does not have enough gas to be completed */ \ - res = EVM_ERROR_OUT_OF_GAS; \ - } \ - else { \ - /* the call depends on how much gas will be refunded after execution to complete successfully */ \ - /* We take an 'optimistic' approach, set the flag and proceed with execution */ \ - evm.properties |= EVM_PROP_CALL_DEPEND_ON_REFUND; \ - } \ - } \ - else \ - parent->gas -= gas; \ - } \ +#define UPDATE_SUBCALL_GAS(evm, parent, address, code_address, caller, gas, mode, value, l_value) \ + do { \ + /**************************************************************/ \ + /* According to the Yellow Paper: C_call = C_gascap + C_extra */ \ + /* Where: */ \ + /* C_gascap = min(L(parent.gas - C_extra), gas) */ \ + /* L(x) = x - (x/64) */ \ + /* C_extra = G_CALL + C_xfer + C_new */ \ + /* C_xfer = (value == 0) ? 0 : G_CALLVALUE */ \ + /* C_new = to_account ? 0 : G_NEWACCOUNT */ \ + /**************************************************************/ \ + bool no_address = false; \ + if (!address) { \ + /* Flag EVM with "creating transaction", so the gas for modifying state will be deduced later */ \ + evm.properties |= EVM_PROP_TXCREATE; \ + res = evm_create_account(&evm, evm.call_data.data, evm.call_data.len, code_address, caller, &new_account); \ + no_address = true; \ + } \ + else { /* if no code was already set for execution */ \ + account_t* ac = NULL; \ + res = evm_get_account(parent, code_address, 0, &ac); \ + if (res == 0 && ac) evm.code = ac->code; \ + } \ + if (res == 0 && !big_is_zero(value, l_value)) { \ + if (mode == EVM_CALL_MODE_STATIC) \ + res = EVM_ERROR_UNSUPPORTED_CALL_OPCODE; \ + else { \ + if (mode == EVM_CALL_MODE_CALL || mode == EVM_CALL_MODE_CALLCODE) { \ + evm.gas += G_CALLSTIPEND; \ + c_xfer = G_CALLVALUE; \ + } \ + /* Transfer value from caller and deduce (C_xfer + C_new) from parent gas*/ \ + res = transfer_value(&evm, parent->address, evm.address, value, l_value, c_xfer, false); \ + } \ + } \ + /* Calculate L(parent.gas - C_extra) */ \ + /* As C_extra was already deduced in previous steps, we calculate only L(parent.gas) now */ \ + uint64_t max_gas_provided = parent->gas - (parent->gas >> 6); \ + /* Calculate C_gascap */ \ + if (no_address) { \ + gas = max_gas_provided; \ + } \ + else \ + gas = min(gas, max_gas_provided); \ + /* Assign values to child EVM */ \ + evm.gas += gas; \ + evm.init_gas = gas; \ + evm.gas_price.data = parent->gas_price.data; \ + evm.gas_price.len = parent->gas_price.len; \ + if (res == 0) { \ + if (parent->gas < gas) { \ + if (parent->gas + c_xfer < gas) { \ + /* transaction does not have enough gas to be completed */ \ + res = EVM_ERROR_OUT_OF_GAS; \ + } \ + else { \ + /* the call depends on how much gas will be refunded after execution to complete successfully */ \ + /* We take an 'optimistic' approach, set the flag and proceed with execution */ \ + evm.properties |= EVM_PROP_CALL_DEPEND_ON_REFUND; \ + } \ + } \ + else \ + parent->gas -= gas; \ + } \ } while (0) #define FINALIZE_AND_REFUND_GAS(evm) finalize_and_refund_gas(evm) diff --git a/c/src/verifier/eth1/evm/opcodes.c b/c/src/verifier/eth1/evm/opcodes.c index 8a9aa356a..550eaba67 100644 --- a/c/src/verifier/eth1/evm/opcodes.c +++ b/c/src/verifier/eth1/evm/opcodes.c @@ -599,7 +599,9 @@ int op_create(evm_t* evm, uint_fast8_t use_salt) { if (use_salt == 0) { // calculate the generated address - uint8_t* nonce = evm_get_account(evm, evm->address, true)->nonce; + account_t* ac = NULL; + TRY(evm_get_account(evm, evm->address, true, &ac)) + uint8_t* nonce = ac->nonce; bytes_builder_t* bb = bb_new(); tmp = bytes(evm->address, 20); rlp_encode_item(bb, &tmp); @@ -629,8 +631,9 @@ int op_create(evm_t* evm, uint_fast8_t use_salt) { } // get nonce before the call - account_t* ac = evm_get_account(evm, evm->address, 0); - uint8_t prev_nonce = ac->nonce[31]; + account_t* ac = NULL; + TRY(evm_get_account(evm, evm->address, 0, &ac)) + uint8_t prev_nonce = ac->nonce[31]; // now execute the call int res = evm_sub_call(evm, NULL, hash + 12, value, l_value, in_data.data, in_data.len, evm->address, evm->origin, 0, 0, 0, 0); @@ -645,7 +648,8 @@ int op_create(evm_t* evm, uint_fast8_t use_salt) { int op_selfdestruct(evm_t* evm) { uint8_t adr[20], l, *p; if (evm_stack_pop(evm, adr, 20) < 0) return EVM_ERROR_EMPTY_STACK; - account_t* self_account = evm_get_account(evm, evm->address, 1); + account_t *self_account = NULL, *tmp; + TRY(evm_get_account(evm, evm->address, 1, &self_account)) // TODO check if this account was selfsdesstructed before evm->refund += R_SELFDESTRUCT; @@ -653,11 +657,13 @@ int op_selfdestruct(evm_t* evm) { p = self_account->balance; optimize_len(p, l); if (l && (l > 1 || *p != 0)) { - if (evm_get_account(evm, adr, 0) == NULL) { + TRY(evm_get_account(evm, adr, 0, &tmp)) + + if (tmp == NULL) { if ((evm->properties & EVM_PROP_NO_FINALIZE) == 0) subgas(G_NEWACCOUNT); - evm_get_account(evm, adr, 1); + TRY(evm_get_account(evm, adr, 1, &tmp)) } - if (transfer_value(evm, evm->address, adr, self_account->balance, 32, 0) < 0) return EVM_ERROR_OUT_OF_GAS; + TRY(transfer_value(evm, evm->address, adr, self_account->balance, 32, 0, false)) } memset(self_account->balance, 0, 32); memset(self_account->nonce, 0, 32); @@ -680,19 +686,19 @@ int op_log(evm_t* evm, uint8_t len) { if (memlen) TRY(mem_check(evm, memoffset + memlen, true)); - logs_t* log = _malloc(sizeof(logs_t)); - - log->next = evm->logs; - evm->logs = log; - log->data.data = _malloc(memlen); - log->data.len = memlen; - - evm_mem_readi(evm, memoffset, log->data.data, memlen); - log->topics.data = _malloc(len * 32); - log->topics.len = len * 32; - uint8_t* t = NULL; int l; + logs_t* log = _malloc(sizeof(logs_t)); + evm_t* parent = evm; + while (parent->parent) parent = parent->parent; + log->next = parent->logs; + evm->logs = log; + log->data.data = _malloc(memlen); + log->data.len = memlen; + log->topics.data = _malloc(len * 32); + log->topics.len = len * 32; + memcpy(log->address, evm->address, 20); + evm_mem_readi(evm, memoffset, log->data.data, memlen); for (int i = 0; i < len; i++) { if ((l = evm_stack_pop_ref(evm, &t)) < 0) return l; @@ -707,11 +713,12 @@ int op_sstore(evm_t* evm) { if ((l_key = evm_stack_pop_ref(evm, &key)) < 0) return l_key; if ((l_val = evm_stack_pop_ref(evm, &value)) < 0) return l_val; - storage_t* s = evm_get_storage(evm, evm->account, key, l_key, 0); - uint8_t created = s == NULL, el = l_val; - uint8_t l_current = 0; + storage_t* s = NULL; + TRY(evm_get_storage(evm, evm->account, key, l_key, 0, &s)) + uint8_t created = s == NULL, el = l_val; + uint8_t l_current = 0; if (created) - s = evm_get_storage(evm, evm->account, key, l_key, 1); + TRY(evm_get_storage(evm, evm->account, key, l_key, 1, &s)) else { created = true; for (int i = 0; i < 32; i++) { diff --git a/c/src/verifier/eth1/full/CMakeLists.txt b/c/src/verifier/eth1/full/CMakeLists.txt index d20276eb8..e18f3aadb 100644 --- a/c/src/verifier/eth1/full/CMakeLists.txt +++ b/c/src/verifier/eth1/full/CMakeLists.txt @@ -35,7 +35,8 @@ add_static_library( NAME eth_full - + REGISTER in3_register_eth_full + SOURCES eth_full.c diff --git a/c/src/verifier/eth1/full/eth_full.c b/c/src/verifier/eth1/full/eth_full.c index 1e33787ef..2bfbbecfd 100644 --- a/c/src/verifier/eth1/full/eth_full.c +++ b/c/src/verifier/eth1/full/eth_full.c @@ -36,6 +36,7 @@ #include "../../../core/client/keys.h" #include "../../../core/client/request_internal.h" #include "../../../core/util/data.h" +#include "../../../core/util/debug.h" #include "../../../core/util/log.h" #include "../../../core/util/mem.h" #include "../../../third-party/crypto/ecdsa.h" @@ -55,7 +56,7 @@ in3_ret_t in3_verify_eth_full(void* pdata, in3_plugin_act_t action, void* pctx) // do we have a result? if not it is a vaslid error-response if (!vc->result) return IN3_OK; - if (strcmp(vc->method, "eth_call") == 0) { + if (VERIFY_RPC("eth_call")) { if (eth_verify_account_proof(vc) < 0) return vc_err(vc, "proof could not be validated"); d_token_t* tx = d_get_at(d_get(vc->request, K_PARAMS), 0); bytes_t* address = d_get_byteskl(tx, K_TO, 20); @@ -65,7 +66,7 @@ in3_ret_t in3_verify_eth_full(void* pdata, in3_plugin_act_t action, void* pctx) bytes_t* from = d_get_byteskl(tx, K_FROM, 20); bytes_t* value = d_get_bytes(tx, K_VALUE); bytes_t* data = d_get_bytes(tx, K_DATA); - bytes_t gas = d_to_bytes(d_get(tx, K_GAS_LIMIT)); + bytes_t gas = d_to_bytes(d_get_or(tx, K_GAS_LIMIT, K_GAS)); bytes_t* result = NULL; uint64_t gas_limit = bytes_to_long(gas.data, gas.len); if (!gas_limit) gas_limit = 0xFFFFFFFFFFFFFF; @@ -74,8 +75,16 @@ in3_ret_t in3_verify_eth_full(void* pdata, in3_plugin_act_t action, void* pctx) in3_log_disable_prefix(); in3_log_set_level(LOG_ERROR); #endif + // is there a receipt-context we need to pass? + json_ctx_t* receipt = NULL; + for (cache_entry_t* ce = vc->req->cache; ce; ce = ce->next) { + if (ce->props & CACHE_PROP_JSON) { + receipt = (json_ctx_t*) (void*) ce->value.data; + break; + } + } - int ret = evm_call(vc, address ? address->data : zeros, value ? value->data : zeros, value ? value->len : 1, data ? data->data : zeros, data ? data->len : 0, from ? from->data : zeros, gas_limit, vc->chain->chain_id, &result); + int ret = evm_call(vc, address ? address->data : zeros, value ? value->data : zeros, value ? value->len : 1, data ? data->data : zeros, data ? data->len : 0, from ? from->data : zeros, gas_limit, vc->chain->chain_id, &result, receipt); #if defined(DEBUG) && defined(LOGGING) in3_log_set_level(old); in3_log_enable_prefix(); @@ -102,8 +111,10 @@ in3_ret_t in3_verify_eth_full(void* pdata, in3_plugin_act_t action, void* pctx) return vc_err(vc, "This op code is not supported with eth_call!"); case EVM_ERROR_OUT_OF_GAS: return vc_err(vc, "Ran out of gas."); + case EVM_ERROR_BALANCE_TOO_LOW: + return vc_err(vc, "not enough funds to transfer the requested value."); case 0: - if (!result) return vc_err(vc, "no result"); + if (!result) return d_len(vc->result) == 0 ? 0 : vc_err(vc, "no result"); res = b_cmp(d_bytes(vc->result), result); b_free(result); diff --git a/c/src/verifier/eth1/full/eth_full.h b/c/src/verifier/eth1/full/eth_full.h index 418f71e93..d768cbd29 100644 --- a/c/src/verifier/eth1/full/eth_full.h +++ b/c/src/verifier/eth1/full/eth_full.h @@ -31,7 +31,7 @@ * You should have received a copy of the GNU Affero General Public License along * with this program. If not, see . *******************************************************************************/ - +// @PUBLIC_HEADER /** @file * Ethereum Nanon verification. * */ diff --git a/c/src/verifier/eth1/full/rpc.yml b/c/src/verifier/eth1/full/rpc.yml new file mode 100644 index 000000000..b83d1d2f8 --- /dev/null +++ b/c/src/verifier/eth1/full/rpc.yml @@ -0,0 +1,199 @@ +eth: + + eth_estimateGas: + descr: calculates the gas needed to execute a transaction. for spec see [eth_estimateGas](https://eth.wiki/json-rpc/API#eth_estimateGas) + params: + tx: + descr: the tx-object, which is the same as specified in [eth_sendTransaction](https://eth.wiki/json-rpc/API#eth_sendTransaction). + type: transaction + block: + internalDefault : latest + optionalAPI: true + descr: the blockNumber or `latest` + type: uint64 + + result: + descr: the amount of gass needed. + type: uint64 + proof: + alias: eth_call + + + eth_call: + descr: calls a function of a contract (or simply executes the evm opcodes) and returns the result. for spec see [eth_call](https://eth.wiki/json-rpc/API#eth_call) + params: + tx: + descr: the tx-object, which is the same as specified in [eth_sendTransaction](https://eth.wiki/json-rpc/API#eth_sendTransaction). + type: transaction + block: + internalDefault : latest + optionalAPI: true + descr: the blockNumber or `latest` + type: uint64 + result: + descr: the abi-encoded result of the function. + + example: + request: + - to: "0x2736D225f85740f42D17987100dc8d58e9e16252" + data: "0x5cf0f3570000000000000000000000000000000000000000000000000000000000000001" + - latest + response: 0x0000000000000000000000000... + in3: + proof: + type: callProof + block: 0xf90215a0c... + signatures: + - "..." + accounts: + '0x2736D225f85740f42D17987100dc8d58e9e16252': + accountProof: + - 0xf90211a095... + - 0xf90211a010... + - 0xf90211a062... + - 0xf90211a091... + - 0xf90211a03a... + - 0xf901f1a0d1... + - 0xf8b18080808... + address: '0x2736d225f85740f42d17987100dc8d58e9e16252' + balance: '0x4fffb' + codeHash: '0x2b8bdc59ce78fd8c248da7b5f82709e04f2149c39e899c4cdf4587063da8dc69' + nonce: '0x1' + storageHash: '0xbf904e79d4ebf851b2380d81aab081334d79e231295ae1b87f2dd600558f126e' + storageProof: + - key: '0x0' + proof: + - 0xf901f1a0db74... + - 0xf87180808080... + - 0xe2a0200decd9....05 + value: '0x5' + - key: '0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e569' + proof: + - 0xf901f1a0db74... + - 0xf891a0795a99... + - 0xe2a020ab8540...43 + value: '0x43' + - key: '0xaaab8540682e3a537d17674663ea013e92c83fdd69958f314b4521edb3b76f1a' + proof: + - 0xf901f1a0db747... + - 0xf891808080808... + - 0xf843a0207bd5ee... + value: '0x68747470733a2f2f696e332e736c6f636b2e69742f6d61696e6e65742f6e642d' + proof: + type: + type: + descr: proof type, which is `callProof` + type: string + block: + descr: serialized blockheader + type: bytes + accounts: + descr: Object with the addresses of all accounts required to run the call as keys. This includes also all storage values (SLOAD) including proof used. The DataStructure of the Proof for each account is exactly the same as the result of - [`eth_getProof`](https://eth.wiki/json-rpc/API#eth_getproof). + type: + address: + descr: address of the account + type: address + balance: + descr: the balance + type: uint256 + nonce: + descr: nonce of the account + type: uint64 + codeHash: + descr: codehash of the account + type: bytes32 + storageHash: + descr: MerkleRoot of the Storage Trie + type: bytes32 + accountProof: + array: true + descr: MerkleProof of this account-node + type: bytes + storageProof: + descr: Array of Proofs for all required storage values + array: true + type: + key: + descr: the storage key (or hash) + type: bytes32 + value: + descr: the storage value + type: bytes32 + proof: + array: true + descr: the merkleProof of the value down to the storageHash as MerkleRoot + type: bytes + + signatures: + array: true + descr: the array of signatures for all used blocks in the result. + type: signature + finalityBlocks: + descr: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + array: true + type: bytes + + descr: | + Verifying the result of an `eth_call` is a little bit more complex because the response is a result of executing opcodes in the vm. The only way to do so is to reproduce it and execute the same code. That's why a call proof needs to provide all data used within the call. This means: + + - All referred accounts including the code (if it is a contract), storageHash, nonce and balance. + - All storage keys that are used (this can be found by tracing the transaction and collecting data based on the `SLOAD`-opcode). + - All blockdata, which are referred at (besides the current one, also the `BLOCKHASH`-opcodes are referring to former blocks). + + For verifying, you need to follow these steps: + + 1. Serialize all used blockheaders and compare the blockhash with the signed hashes. (See [BlockProof](#blockproof)) + + 2. Verify all used accounts and their storage as showed in [Account Proof](#account-proof). + + 3. Create a new [VM](https://github.com/ethereumjs/ethereumjs-vm) with a MerkleTree as state and fill in all used value in the state: + + + ```js + // create new state for a vm + const state = new Trie() + const vm = new VM({ state }) + + // fill in values + for (const adr of Object.keys(accounts)) { + const ac = accounts[adr] + + // create an account-object + const account = new Account([ac.nonce, ac.balance, ac.stateRoot, ac.codeHash]) + + // if we have a code, we will set the code + if (ac.code) account.setCode( state, bytes( ac.code )) + + // set all storage-values + for (const s of ac.storageProof) + account.setStorage( state, bytes32( s.key ), rlp.encode( bytes32( s.value ))) + + // set the account data + state.put( address( adr ), account.serialize()) + } + + // add listener on each step to make sure it uses only values found in the proof + vm.on('step', ev => { + if (ev.opcode.name === 'SLOAD') { + const contract = toHex( ev.address ) // address of the current code + const storageKey = bytes32( ev.stack[ev.stack.length - 1] ) // last element on the stack is the key + if (!getStorageValue(contract, storageKey)) + throw new Error(`incomplete data: missing key ${storageKey}`) + } + /// ... check other opcodes as well + }) + + // create a transaction + const tx = new Transaction(txData) + + // run it + const result = await vm.runTx({ tx, block: new Block([block, [], []]) }) + + // use the return value + return result.vm.return + ``` + + In the future, we will be using the same approach to verify calls with ewasm. + + If the request requires proof (`verification`: `proof`) the node will provide an Call Proof as part of the in3-section of the response. Details on how create the proof can be found in the [CallProof-Chapter](#call-proof). + diff --git a/c/src/verifier/eth1/nano/CMakeLists.txt b/c/src/verifier/eth1/nano/CMakeLists.txt index 6e52a5c9e..67d9a3dcc 100644 --- a/c/src/verifier/eth1/nano/CMakeLists.txt +++ b/c/src/verifier/eth1/nano/CMakeLists.txt @@ -34,7 +34,8 @@ add_static_library( NAME eth_nano - + REGISTER in3_register_eth_nano + SOURCES eth_nano.c rlp.c diff --git a/c/src/verifier/eth1/nano/eth_nano.c b/c/src/verifier/eth1/nano/eth_nano.c index 1d6c80322..b1d297009 100644 --- a/c/src/verifier/eth1/nano/eth_nano.c +++ b/c/src/verifier/eth1/nano/eth_nano.c @@ -36,6 +36,7 @@ #include "../../../core/client/keys.h" #include "../../../core/client/plugin.h" #include "../../../core/client/request.h" +#include "../../../core/util/debug.h" #include "../../../core/util/mem.h" #include "../../../third-party/crypto/ecdsa.h" #include "merkle.h" @@ -43,8 +44,8 @@ #include // list of methods allowed withoput proof -#define MAX_METHODS 25 -char* ALLOWED_METHODS[MAX_METHODS] = {"eth_chainId", "in3_stats", "eth_blockNumber", "web3_clientVersion", "web3_sha3", "net_version", "net_peerCount", "net_listening", "eth_protocolVersion", "eth_syncing", "eth_coinbase", "eth_mining", "eth_hashrate", "eth_gasPrice", "eth_accounts", "eth_sign", "eth_sendRawTransaction", "eth_estimateGas", "eth_getCompilers", "eth_compileLLL", "eth_compileSolidity", "eth_compileSerpent", "eth_getWork", "eth_submitWork", "eth_submitHashrate"}; +#define MAX_METHODS 26 +const char* ALLOWED_METHODS[MAX_METHODS] = {"eth_chainId", "in3_stats", "eth_blockNumber", "web3_clientVersion", "web3_sha3", "net_version", "net_peerCount", "net_listening", "eth_protocolVersion", "eth_syncing", "eth_coinbase", "eth_mining", "eth_hashrate", "eth_gasPrice", "eth_accounts", "eth_sign", "eth_sendRawTransaction", "eth_estimateGas", "eth_getCompilers", "eth_compileLLL", "eth_compileSolidity", "eth_compileSerpent", "eth_getWork", "eth_submitWork", "eth_submitHashrate", "eth_getProof"}; in3_ret_t in3_verify_eth_nano(void* p_data, in3_plugin_act_t action, void* pctx) { UNUSED_VAR(p_data); @@ -61,7 +62,7 @@ in3_ret_t in3_verify_eth_nano(void* p_data, in3_plugin_act_t action, void* pctx) return IN3_OK; } - if (strcmp(vc->method, "eth_getTransactionReceipt") == 0) + if (VERIFY_RPC("eth_getTransactionReceipt")) // for txReceipt, we need the txhash return eth_verify_eth_getTransactionReceipt(vc, d_get_bytes_at(d_get(vc->request, K_PARAMS), 0)); else diff --git a/c/src/verifier/eth1/nano/eth_nano.h b/c/src/verifier/eth1/nano/eth_nano.h index ad64ffba9..cf242364e 100644 --- a/c/src/verifier/eth1/nano/eth_nano.h +++ b/c/src/verifier/eth1/nano/eth_nano.h @@ -31,7 +31,7 @@ * You should have received a copy of the GNU Affero General Public License along * with this program. If not, see . *******************************************************************************/ - +// @PUBLIC_HEADER /** @file * Ethereum Nanon verification. * */ diff --git a/c/src/verifier/eth1/nano/rlp.h b/c/src/verifier/eth1/nano/rlp.h index 02822d42f..a277b663b 100644 --- a/c/src/verifier/eth1/nano/rlp.h +++ b/c/src/verifier/eth1/nano/rlp.h @@ -135,7 +135,7 @@ bytes_builder_t* rlp_encode_to_list(bytes_builder_t* bb); /** * converts the data in the builder to a rlp-encoded item. * - * This function is optimized to not increase the memory more than needed and is fastet than + * This function is optimized to not increase the memory more than needed and is faster than * creating a second builder to encode the data. * * \param bb the builder containing the data. diff --git a/c/src/verifier/eth1/nano/rpc.yml b/c/src/verifier/eth1/nano/rpc.yml new file mode 100644 index 000000000..37a7ee990 --- /dev/null +++ b/c/src/verifier/eth1/nano/rpc.yml @@ -0,0 +1,228 @@ +types: + + ethlog: + address: + descr: the address triggering the event. + type: address + blockNumber: + descr: the blockNumber + type: uint64 + blockHash: + descr: blockhash if ther containing block + type: bytes32 + data: + descr: abi-encoded data of the event (all non indexed fields) + type: bytes + logIndex: + descr: the index of the even within the block. + type: int + removed: + descr: the reorg-status of the event. + type: bool + topics: + array: true + descr: array of 32byte-topics of the indexed fields. + type: bytes32 + transactionHash: + descr: requested transactionHash + type: bytes32 + transactionIndex: + descr: transactionIndex within the containing block. + type: int + transactionLogIndex: + descr: index of the event within the transaction. + type: int + optional: true + type: + descr: mining-status + type: string + optional: true + + transactionReceipt: + blockNumber: + descr: the blockNumber + type: uint64 + blockHash: + descr: blockhash if ther containing block + type: bytes32 + contractAddress: + descr: the deployed contract in case the tx did deploy a new contract + type: address + optional: true + cumulativeGasUsed: + descr: gas used for all transaction up to this one in the block + type: uint64 + gasUsed: + descr: gas used by this transaction. + type: uint64 + logs: + descr: array of events created during execution of the tx + array: true + type: ethlog + logsBloom: + descr: bloomfilter used to detect events for `eth_getLogs` + type: bytes128 + status: + descr: error-status of the tx. 0x1 = success 0x0 = failure + type: int + transactionHash: + descr: requested transactionHash + type: bytes32 + transactionIndex: + descr: transactionIndex within the containing block. + type: int + +eth: + descr: | + Standard JSON-RPC calls as described in https://eth.wiki/json-rpc/API. + + Whenever a request is made for a response with `verification`: `proof`, the node must provide the proof needed to validate the response result. The proof itself depends on the chain. + + For ethereum, all proofs are based on the correct block hash. That's why verification differentiates between [Verifying the blockhash](poa.html) (which depends on the user consensus) the actual result data. + + There is another reason why the BlockHash is so important. This is the only value you are able to access from within a SmartContract, because the evm supports a OpCode (`BLOCKHASH`), which allows you to read the last 256 blockhashes, which gives us the chance to verify even the blockhash onchain. + + Depending on the method, different proofs are needed, which are described in this document. + + Proofs will add a special in3-section to the response containing a `proof`- object. Each `in3`-section of the response containing proofs has a property with a proof-object with the following properties: + + * **type** `string` (required) - The type of the proof. + Must be one of the these values : `'transactionProof`', `'receiptProof`', `'blockProof`', `'accountProof`', `'callProof`', `'logProof`' + * **block** `string` - The serialized blockheader as hex, required in most proofs. + * **finalityBlocks** `array` - The serialized following blockheaders as hex, required in case of finality asked (only relevant for PoA-chains). The server must deliver enough blockheaders to cover more then 50% of the validators. In order to verify them, they must be linkable (with the parentHash). + * **transactions** `array` - The list of raw transactions of the block if needed to create a merkle trie for the transactions. + * **uncles** `array` - The list of uncle-headers of the block. This will only be set if full verification is required in order to create a merkle tree for the uncles and so prove the uncle_hash. + * **merkleProof** `string[]` - The serialized merkle-nodes beginning with the root-node (depending on the content to prove). + * **merkleProofPrev** `string[]` - The serialized merkle-nodes beginning with the root-node of the previous entry (only for full proof of receipts). + * **txProof** `string[]` - The serialized merkle-nodes beginning with the root-node in order to proof the transactionIndex (only needed for transaction receipts). + * **logProof** [LogProof](#logproof) - The Log Proof in case of a `eth_getLogs`-request. + * **accounts** `object` - A map of addresses and their AccountProof. + * **txIndex** `integer` - The transactionIndex within the block (for transaactions and receipts). + * **signatures** `Signature[]` - Requested signatures. + + eth_getTransactionReceipt: + descr: The Receipt of a Transaction. For Details, see [eth_getTransactionReceipt](https://eth.wiki/json-rpc/API#eth_gettransactionreceipt). + params: + txHash: + descr: the transactionHash + type: bytes32 + result: + optional: true + descr: the TransactionReceipt or `null` if it does not exist. + type: transactionReceipt + + example: + request: + - '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + response: + blockHash: '0xea6ee1e20d3408ad7f6981cfcc2625d80b4f4735a75ca5b20baeb328e41f0304' + blockNumber: '0x8c1e39' + contractAddress: + cumulativeGasUsed: '0x2466d' + gasUsed: '0x2466d' + logs: + - address: '0x85ec283a3ed4b66df4da23656d4bf8a507383bca' + blockHash: '0xea6ee1e20d3408ad7f6981cfcc2625d80b4f4735a75ca5b20baeb328e41f0304' + blockNumber: '0x8c1e39' + data: 0x00000000000... + logIndex: '0x0' + removed: false + topics: + - '0x9123e6a7c5d144bd06140643c88de8e01adcbb24350190c02218a4435c7041f8' + - '0xa2f7689fc12ea917d9029117d32b9fdef2a53462c853462ca86b71b97dd84af6' + - '0x55a6ef49ec5dcf6cd006d21f151f390692eedd839c813a150000000000000000' + transactionHash: '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + transactionIndex: '0x0' + transactionLogIndex: '0x0' + type: mined + logsBloom: 0x00000000000000000000200000... + root: + status: '0x1' + transactionHash: '0x5dc2a9ec73abfe0640f27975126bbaf14624967e2b0b7c2b3a0fb6111f0d3c5e' + transactionIndex: '0x0' + in3: + proof: + type: receiptProof + block: 0xf9023fa019e9d929ab... + txProof: + - 0xf851a083c8446ab932130... + merkleProof: + - 0xf851a0b0f5b7429a54b10... + txIndex: 0 + signatures: + - "..." + merkleProofPrev: + - 0xf851a0b0f5b7429a54b10... + + proof: + descr: | + ```eval_rst + .. graphviz:: + + digraph minimal_nonplanar_graphs { + + fontname="Helvetica" + subgraph all { + + node [ fontsize = "12", style="", color=black fontname="Helvetica", shape=record ] + + subgraph blockheader { + label="blocheader" style="" color=black + + bheader[ label="parentHash|...|transactionRoot|receiptRoot|stateRoot"] + troot:a -> bheader:tr + } + + subgraph cluster_client_registry { + label="Receipt Trie" color=lightblue style=filled + + troot[label="|0x123456|||||"] + ta[label="|0x123456||0xabcdef|||"] + tb[label="|0x98765||0xfcab34|||"] + tval[label="transaction receipt"] + + ta:a -> troot:a + tb:a -> troot:a + tval:a -> ta:a + } + + + } + } + + ``` + + The proof works similiar to the transaction proof. + + In order to create the proof we need to serialize all transaction receipts + + ```js + transactionReceipt = rlp.encode([ + uint( r.status || r.root ), + uint( r.cumulativeGasUsed ), + bytes256( r.logsBloom ), + r.logs.map(l => [ + address( l.address ), + l.topics.map( bytes32 ), + bytes( l.data ) + ]) + ].slice(r.status === null && r.root === null ? 1 : 0)) + ``` + + and store them in a merkle tree with `rlp.encode(transactionIndex)` as key or path, since the blockheader only contains the `receiptRoot`, which is the root-hash of the resulting merkle tree. A merkle proof with the transactionIndex of the target transaction receipt will then be created from this tree. + + Since the merkle proof is only proving the value for the given transactionIndex, we also need to prove that the transactionIndex matches the transactionHash requested. This is done by adding another MerkleProof for the transaction itself as described in the [Transaction Proof](#eth-gettransactionbyhash). + + + If the request requires proof (`verification`: `proof`) the node will provide an Transaction Proof as part of the in3-section of the response. + This proof section contains the following properties: + + - `type` : constant : `receiptProof` + - `block` : the serialized blockheader of the requested transaction. + - `signatures` : a array of signatures from the signers (if requested) of the above block. + - `txIndex` : The TransactionIndex as used in the MerkleProof + - `txProof` : the serialized nodes of the Transaction trie starting with the root node. This is needed in order to proof that the required transactionHash matches the receipt. + - `merkleProof`: the serialized nodes of the Transaction Receipt trie starting with the root node. + - `merkleProofPrev`: the serialized nodes of the previous Transaction Receipt (if txInxdex>0) trie starting with the root node. This is only needed if full-verification is requested. With a verified previous Receipt we can proof the `usedGas`. + - `finalityBlocks`: a array of blockHeaders which were mined after the requested block. The number of blocks depends on the request-property `finality`. If this is not specified, this property will not be defined. + diff --git a/c/src/verifier/ipfs/CMakeLists.txt b/c/src/verifier/ipfs/CMakeLists.txt index 7cf109ab9..e34a2a789 100644 --- a/c/src/verifier/ipfs/CMakeLists.txt +++ b/c/src/verifier/ipfs/CMakeLists.txt @@ -35,7 +35,8 @@ add_static_library( NAME ipfs - + REGISTER in3_register_ipfs + SOURCES ipfs.c ipfs.pb.c diff --git a/c/src/verifier/ipfs/ipfs.c b/c/src/verifier/ipfs/ipfs.c index 7c27a7ca4..0f267a2d6 100644 --- a/c/src/verifier/ipfs/ipfs.c +++ b/c/src/verifier/ipfs/ipfs.c @@ -2,6 +2,7 @@ #include "ipfs.h" #include "../../core/client/keys.h" #include "../../core/client/plugin.h" +#include "../../core/util/debug.h" #include "../../core/util/mem.h" #include "../../third-party/crypto/base58.h" #include "../../third-party/crypto/sha2.h" @@ -154,11 +155,11 @@ in3_ret_t in3_verify_ipfs(void* pdata, in3_plugin_act_t action, void* pctx) { if (strcmp(vc->method, "in3_nodeList") == 0) return true; - else if (strcmp(vc->method, "ipfs_get") == 0) + else if (VERIFY_RPC("ipfs_get")) return ipfs_verify_hash(d_string(vc->result), d_get_string_at(params, 1) ? d_get_string_at(params, 1) : "base64", d_get_string_at(params, 0)); - else if (strcmp(vc->method, "ipfs_put") == 0) + else if (VERIFY_RPC("ipfs_put")) return ipfs_verify_hash(d_get_string_at(params, 0), d_get_string_at(params, 1) ? d_get_string_at(params, 1) : "base64", d_string(vc->result)); diff --git a/c/src/verifier/ipfs/rpc.yml b/c/src/verifier/ipfs/rpc.yml new file mode 100644 index 000000000..c90c23a62 --- /dev/null +++ b/c/src/verifier/ipfs/rpc.yml @@ -0,0 +1,45 @@ +ipfs: + descr: | + A Node supporting IPFS must support these 2 RPC-Methods for uploading and downloading IPFS-Content. The node itself will run a ipfs-client to handle them. + + Fetching ipfs-content can be easily verified by creating the ipfs-hash based on the received data and comparing it to the requested ipfs-hash. Since there is no chance of manipulating the data, there is also no need to put a deposit or convict a node. That's why the registry-contract allows a zero-deposit fot ipfs-nodes. + + ipfs_get: + descr: Fetches the data for a requested ipfs-hash. If the node is not able to resolve the hash or find the data a error should be reported. + params: + ipfshash: + descr: the ipfs multi hash + type: string + encoding: + descr: the encoding used for the response. ( `hex` , `base64` or `utf8`) + type: string + result: + descr: the content matching the requested hash encoded in the defined encoding. + proof: + descr: No proof or verification needed on the server side. All the verification are done in the client by creating the ipfs multihash and comparing to the requested hash. + example: + request: + - QmSepGsypERjq71BSm4Cjq7j8tyAUnCw6ZDTeNdE8RUssD + - utf8 + response: "I love Incubed" + + ipfs_put: + descr: | + Stores ipfs-content to the ipfs network. + Important! As a client there is no garuantee that a node made this content available. ( just like `eth_sendRawTransaction` will only broadcast it). + Even if the node stores the content there is no gurantee it will do it forever. + params: + data: + descr: the content encoded with the specified encoding. + type: bytes | string + encoding: + descr: the encoding used for the request. ( `hex` , `base64` or `utf8`) + type: string + result: + descr: the ipfs multi hash + example: + request: + - I love Incubed + - utf8 + response: QmSepGsypERjq71BSm4Cjq7j8tyAUnCw6ZDTeNdE8RUssD + diff --git a/c/test/CMakeLists.txt b/c/test/CMakeLists.txt index f95461b9a..ad287dd15 100644 --- a/c/test/CMakeLists.txt +++ b/c/test/CMakeLists.txt @@ -41,6 +41,12 @@ else() endif() include_directories(. ../src) +get_filename_component(testdata_dir "${CMAKE_CURRENT_LIST_DIR}/testdata" ABSOLUTE) + +#file(RELATIVE_PATH testdata_dir "${CMAKE_BINARY_DIR}" "testdata") +add_definitions("-DTESTDATA_DIR=\"${testdata_dir}\"") +get_filename_component(buildDirRelFilePath "${myFile}" + REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") # handle codecoverage if (CODE_COVERAGE) @@ -108,13 +114,17 @@ foreach (file ${files}) endforeach () +if ( NOT CMD_NAME ) + set(CMD_NAME in3) +endif () + # add cmd--tests file(GLOB files "testdata/cmd/*.txt") foreach (file ${files}) get_filename_component(testname "${file}" NAME_WE) add_test( NAME "cmd_${testname}" - COMMAND ${CMAKE_BINARY_DIR}/bin/in3 -fi ${file} + COMMAND ${CMAKE_BINARY_DIR}/bin/${CMD_NAME} -fi ${file} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/.. ) set_tests_properties("cmd_${testname}" PROPERTIES diff --git a/c/test/qemu/esp32/main/CMakeLists.txt b/c/test/qemu/esp32/main/CMakeLists.txt index ca59e5e76..f15b3f30c 100644 --- a/c/test/qemu/esp32/main/CMakeLists.txt +++ b/c/test/qemu/esp32/main/CMakeLists.txt @@ -21,7 +21,6 @@ OPTION(ESP_IDF "Esp-idf framework" ON) ADD_DEFINITIONS(-DIN3_MATH_LITE) set(IN3_SRC ../../../..) add_subdirectory(${IN3_SRC}/src/core build_core) -add_subdirectory(${IN3_SRC}/src/init build_init) add_subdirectory(${IN3_SRC}/src/third-party/tommath build_tommath) add_subdirectory(${IN3_SRC}/src/third-party/crypto build_crypto) @@ -37,7 +36,6 @@ add_subdirectory(${IN3_SRC}/src/api/utils build_utils) set(IN3_LIBS $ $ - $ $ $ $ diff --git a/c/test/qemu/esp32/main/in3_main.c b/c/test/qemu/esp32/main/in3_main.c index 25157c917..cfb60a990 100644 --- a/c/test/qemu/esp32/main/in3_main.c +++ b/c/test/qemu/esp32/main/in3_main.c @@ -43,13 +43,14 @@ #include "sdkconfig.h" #include #include -#include // the core client -#include // functions for direct api-access -#include // if included the verifier will automaticly be initialized. -#include // logging functions -#include // the context -#include // default signer implementation -#include // stringbuilder tool for dynamic memory string handling +#include // the core client +#include // functions for direct api-access +#include // functions for nodeselection +#include // logging functions +#include // functions for direct api-access +#include // the context +#include // default signer implementation +#include // stringbuilder tool for dynamic memory string handling #include #include @@ -88,6 +89,9 @@ in3_ret_t transport_mock(void* plugin_data, in3_plugin_act_t action, void* plugi /* Setup and init in3 */ void init_in3(void) { c = in3_for_chain(CHAIN_ID_GOERLI); + in3_register_eth_full(c); + in3_register_nodeselect_def(c); + in3_log_set_quiet(false); in3_log_set_level(LOG_TRACE); in3_plugin_register(c, PLGN_ACT_TRANSPORT, transport_mock, NULL, true); diff --git a/c/test/qemu/zephyr-arm3/CMakeLists.txt b/c/test/qemu/zephyr-arm3/CMakeLists.txt index f1c7d46f5..1d640a4b8 100644 --- a/c/test/qemu/zephyr-arm3/CMakeLists.txt +++ b/c/test/qemu/zephyr-arm3/CMakeLists.txt @@ -26,7 +26,6 @@ ADD_DEFINITIONS(-DIN3_MATH_LITE) set(IN3_SRC ../../..) set(IN3_DEPS ${IN3_SRC}/src/core -${IN3_SRC}/src/init ${IN3_SRC}/src/verifier/eth1/basic ${IN3_SRC}/src/verifier/eth1/nano ${IN3_SRC}/src/signer/pk-signer diff --git a/c/test/qemu/zephyr-arm3/src/main.c b/c/test/qemu/zephyr-arm3/src/main.c index e0bec953b..5f3ada0be 100644 --- a/c/test/qemu/zephyr-arm3/src/main.c +++ b/c/test/qemu/zephyr-arm3/src/main.c @@ -71,9 +71,9 @@ in3_ret_t transport_mock(void* plugin_data, in3_plugin_act_t action, void* plugi in3_t* init_in3_goerli(in3_plugin_act_fn custom_transport) { in3_t* in3 = NULL; //int err; - in3_register_default(in3_register_eth_basic); - in3_register_default(in3_register_nodeselect_def); in3 = in3_for_chain(0x5); + in3_register_eth_basic(in3); + in3_register_nodeselect_def(in3); if (custom_transport) in3_plugin_register(in3, PLGN_ACT_TRANSPORT, custom_transport, NULL, true); in3->flags = FLAGS_STATS | FLAGS_INCLUDE_CODE | FLAGS_BINARY; diff --git a/c/test/runner.c b/c/test/runner.c index ab0dfacda..e1abedd7a 100644 --- a/c/test/runner.c +++ b/c/test/runner.c @@ -35,6 +35,7 @@ #ifndef TEST #define TEST #endif +#include "../src/api/core/core_api.h" #include "../src/api/eth1/eth_api.h" #include "../src/core/client/client.h" #include "../src/core/client/plugin.h" @@ -458,6 +459,7 @@ int main(int argc, char* argv[]) { in3_register_default(in3_register_eth_api); in3_register_default(in3_register_ipfs); in3_register_default(in3_register_btc); + in3_register_default(in3_register_core_api); in3_register_default(in3_register_nodeselect_def); int i = 0, size = 1; diff --git a/c/test/test_evm.c b/c/test/test_evm.c index a079012c8..38cff3391 100644 --- a/c/test/test_evm.c +++ b/c/test/test_evm.c @@ -219,7 +219,8 @@ int check_post_state(evm_t* evm, d_token_t* post) { uint8_t s_key[32]; int l_key = hex_to_bytes(s_str + 2, strlen(s_str) - 2, s_key, 32); bytes_t val_must = d_to_bytes(s); - storage_t* st = evm_get_storage(evm, address, s_key, l_key, 0); + storage_t* st = NULL; + TRY(evm_get_storage(evm, address, s_key, l_key, 0, &st)) if (!st) { print_error("Missing the storage key!"); return -1; @@ -340,6 +341,7 @@ int generate_state_root(evm_t* evm, uint8_t* dst) { uint8_t hash[32]; bytes_t hash_bytes = {.data = hash, .len = 32}; d_token_t* test = (d_token_t*) evm->env_ptr; + account_t* tmp; #ifdef EVM_GAS // make sure we have all accounts d_token_t *accounts = d_get(test, ikey(jc, "pre")), *t; @@ -347,7 +349,7 @@ int generate_state_root(evm_t* evm, uint8_t* dst) { for (i = 0, t = accounts + 1; i < d_len(accounts); i++, t = d_next(t)) { uint8_t adr[20]; hex_to_bytes(d_get_keystr(jc, t->key) + 2, 40, adr, 20); - evm_get_account(evm, adr, 1); + TRY(evm_get_account(evm, adr, 1, &tmp)) } EVM_DEBUG_BLOCK({ in3_log_trace("\n::: ================ "); @@ -397,19 +399,21 @@ static void uint256_setb(uint8_t* dst, uint8_t* data, int len) { #ifdef EVM_GAS static void read_accounts(evm_t* evm, d_token_t* accounts) { int i, j; + account_t* tmp = NULL; + storage_t* st = NULL; d_token_t *t, *storage, *s; for (i = 0, t = accounts + 1; i < d_len(accounts); i++, t = d_next(t)) { char* adr_str = d_get_keystr(jc, t->key); uint8_t address[20]; hex_to_bytes(adr_str + 2, strlen(adr_str) - 2, address, 20); - evm_get_account(evm, address, true); + evm_get_account(evm, address, true, &tmp); storage = d_get(t, ikey(jc, "storage")); if (storage) { for (j = 0, s = storage + 1; j < d_len(storage); j++, s = d_next(s)) { char* k = d_get_keystr(jc, s->key); uint8_t kk[32]; hex_to_bytes(k + 2, strlen(k) - 2, kk, 32); - evm_get_storage(evm, address, kk, (strlen(k) - 1) / 2, true); + evm_get_storage(evm, address, kk, (strlen(k) - 1) / 2, true, &st); } } } @@ -541,9 +545,11 @@ int run_evm(json_ctx_t* jctx, d_token_t* test, uint32_t props, uint64_t* ms, cha // we need to create an account since we don't have one if (big_is_zero(evm.address, 20)) { + account_t* atmp = NULL; // calculate the generated address - uint8_t* nonce = evm_get_account(&evm, caller, true)->nonce; + TRY(evm_get_account(&evm, caller, true, &atmp)) + uint8_t* nonce = atmp->nonce; bytes_builder_t* bb = bb_new(); bytes_t tmp = bytes(caller, 20); bytes32_t hash; @@ -561,11 +567,13 @@ int run_evm(json_ctx_t* jctx, d_token_t* test, uint32_t props, uint64_t* ms, cha bb_free(bb); memcpy(_to, hash + 12, 20); - evm_get_account(&evm, _to, true)->nonce[31]++; + TRY(evm_get_account(&evm, _to, true, &atmp)) + atmp->nonce[31]++; } // increase the nonce and pay for gas - account_t* c_adr = evm_get_account(&evm, evm.caller, true); + account_t* c_adr = NULL; + TRY(evm_get_account(&evm, evm.caller, true, &c_adr)) uint256_setn(c_adr->nonce, bytes_to_long(c_adr->nonce, 32) + 1); uint8_t tmp[32], txval[64]; int l; @@ -583,7 +591,8 @@ int run_evm(json_ctx_t* jctx, d_token_t* test, uint32_t props, uint64_t* ms, cha uint256_setb(c_adr->balance, txval, l); // handle balance for receiver - account_t* to_adr = evm_get_account(&evm, evm.address, true); + account_t* to_adr = NULL; + TRY(evm_get_account(&evm, evm.address, true, &to_adr)) uint256_setb(to_adr->balance, tmp, big_add(to_adr->balance, 32, evm.call_value.data, evm.call_value.len, tmp, 32)); total_gas = tx_intrinsic_gas; @@ -634,7 +643,8 @@ int run_evm(json_ctx_t* jctx, d_token_t* test, uint32_t props, uint64_t* ms, cha read_accounts(&evm, d_get(test, ikey(jc, "pre"))); // reduce the gasLimit*price from caller the - account_t* sender = evm_get_account(&evm, evm.caller, true); + account_t* sender = NULL; + TRY(evm_get_account(&evm, evm.caller, true, &sender)) long_to_bytes(total_gas, gas_tmp); int l = big_mul(evm.gas_price.data, evm.gas_price.len, gas_tmp, 8, gas_tmp2, 32); uint256_setb(sender->balance, gas_tmp, big_sub(sender->balance, 32, gas_tmp2, l, gas_tmp)); @@ -648,7 +658,8 @@ int run_evm(json_ctx_t* jctx, d_token_t* test, uint32_t props, uint64_t* ms, cha // if there is gas left we return it to the sender if (evm.gas > 0) { - account_t* c_adr = evm_get_account(&evm, evm.caller, true); + account_t* c_adr = NULL; + TRY(evm_get_account(&evm, evm.caller, true, &c_adr)) long_to_bytes(evm.gas, tmp); l = big_mul(evm.gas_price.data, evm.gas_price.len, tmp, 8, tmp2, 32); l = big_add(tmp2, l, c_adr->balance, 32, tmp, 32); @@ -656,7 +667,8 @@ int run_evm(json_ctx_t* jctx, d_token_t* test, uint32_t props, uint64_t* ms, cha } // pay the miner the total gas - account_t* miner = evm_get_account(&evm, d_get_bytes(d_get(test, ikey(jc, "env")), ikey(jc, "currentCoinbase"))->data, 1); + account_t* miner = NULL; + TRY(evm_get_account(&evm, d_get_bytes(d_get(test, ikey(jc, "env")), ikey(jc, "currentCoinbase"))->data, 1, &miner)) // increase balance of the miner long_to_bytes(total_gas, tmp); diff --git a/c/test/test_utils.h b/c/test/test_utils.h index cb7869ee6..40c013a47 100644 --- a/c/test/test_utils.h +++ b/c/test/test_utils.h @@ -47,16 +47,18 @@ #ifdef __cplusplus extern "C" { #endif - -#define TESTS_BEGIN() UNITY_BEGIN() -#define TESTS_END() UNITY_END() -#define TEST_LOG(fmt_, ...) printf("%s:%d:%s:LOG:" fmt_, __FILE__, __LINE__, __func__, __VA_ARGS__) +#ifndef TESTDATA_DIR +#define TESTDATA_DIR "../c/test/testdata" +#endif +#define TESTS_BEGIN() UNITY_BEGIN() +#define TESTS_END() UNITY_END() +#define TEST_LOG(fmt_, ...) printf("%s:%d:%s:LOG:" fmt_, __FILE__, __LINE__, __func__, __VA_ARGS__) #define TEST_LOG_INTERNAL(f_, fmt_, ...) printf("%s:%d:%s:LOG:" fmt_, __FILE__, __LINE__, f_, __VA_ARGS__) // Timing #define TIMING_START() gettimeofday(&begin, NULL) -#define TIMING_END() gettimeofday(&end, NULL) -#define TIMING_GET() ((double) (end.tv_usec - begin.tv_usec) / 1000000 + (double) (end.tv_sec - begin.tv_sec)) +#define TIMING_END() gettimeofday(&end, NULL) +#define TIMING_GET() ((double) (end.tv_usec - begin.tv_usec) / 1000000 + (double) (end.tv_sec - begin.tv_sec)) #define RUN_TIMED_TEST(t) \ do { \ diff --git a/c/test/testdata/cmd/btc_bip34.txt b/c/test/testdata/cmd/btc_bip34.txt index 57e7c3a41..1034ccba7 100644 --- a/c/test/testdata/cmd/btc_bip34.txt +++ b/c/test/testdata/cmd/btc_bip34.txt @@ -1,198 +1,20 @@ -:: cmd ./bin/in3 -x -fi ../c/test/testdata/cmd/btc_bip34.txt -c btc -nl http://localhost:8500 -f 5 getblockheader 000000002afcaf0e0d78e698e71c26fdd5f87b7703c18ed48e27ab133dc3d4bb true -debug -rc 1 -a 1 +:: cmd bin/in3 -x -fi ../c/test/testdata/cmd/btc_bip34.txt -c btc -nl http://localhost:8500 -f 5 getblockheader 000000002afcaf0e0d78e698e71c26fdd5f87b7703c18ed48e27ab133dc3d4bb true -debug -rc 1 -a 1 -:: time 1612516368 +:: time 1617222454 -:: rand 1138659513 - -:: rand 1223656574 - -:: rand 1692635546 - -:: rand 409749813 - -:: rand 1832534809 - -:: rand 202069589 - -:: rand 1011936416 - -:: rand 1692343119 - -:: request http://localhost:8500 - [{"id":2,"jsonrpc":"2.0","method":"in3_nodeList","params":[0,"0x43de90b948ef847e64e3919a186c49356d3a43190c0b56553c50eca064df1b4f",[],true],"in3":{"verification":"proof","version": "2.1.0","chainId":"0x99","finality":5,"preBIP34":true}}] - -:: response in3_nodeList 0 http://localhost:8500 0 6 -[{ - "id":2, - "result":{ - "nodes":[{ - "url":"https://in3-v2.slock.it/btc/nd-1", - "address":"0x45d45e6ff99e6c34a235d263965910298985fcfe", - "index":0, - "deposit":"0x2386f26fc10000", - "props":"0x2000001dd", - "timeout":3456000, - "registerTime":1593440588, - "weight":2000, - "performance":{ - "count":1, - "total":198, - "last_failed":0 - } - }, - { - "url":"https://in3-v2.slock.it/btc/nd-2", - "address":"0x1fe2e9bf29aa1938859af64c413361227d04059a", - "index":1, - "deposit":"0x2386f26fc10000", - "props":"0x2000001dd", - "timeout":3456000, - "registerTime":1593440633, - "weight":2000, - "performance":{ - "count":1, - "total":3201, - "last_failed":1612516223542 - } - }, - { - "url":"https://in3-v2.slock.it/btc/nd-3", - "address":"0x945f75c0408c0026a3cd204d36f5e47745182fd4", - "index":2, - "deposit":"0x2386f26fc10000", - "props":"0x2000001dd", - "timeout":3456000, - "registerTime":1593440648, - "weight":2000, - "performance":{ - "count":1, - "total":195, - "last_failed":0 - } - }, - { - "url":"https://in3-v2.slock.it/btc/nd-4", - "address":"0xc513a534de5a9d3f413152c41b09bd8116237fc8", - "index":3, - "deposit":"0x2386f26fc10000", - "props":"0x2000001dd", - "timeout":3456000, - "registerTime":1593440678, - "weight":2000, - "performance":{ - "count":1, - "total":3326, - "last_failed":1612516223542 - } - }, - { - "url":"https://in3-v2.slock.it/btc/nd-5", - "address":"0xbcdf4e3e90cc7288b578329efd7bcc90655148d2", - "index":4, - "deposit":"0x2386f26fc10000", - "props":"0x2000001dd", - "timeout":3456000, - "registerTime":1593440708, - "weight":2000, - "performance":{ - "count":1, - "total":3220, - "last_failed":1612516223542 - } - }], - "contract":"0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32", - "registryId":"0x53786c93e54c21d9852d093c394eee9df8d714d8f2534cdf92f9c9998c528d19", - "lastBlockNumber":4229185, - "totalServers":5 - }, - "jsonrpc":"2.0", - "in3":{ - "execTime":0, - "proof":{ - "type":"accountProof", - "block":"0xf9025ca076d3a4a758090c66b136b2b719a94d2823bc0be8d1172b23fbfda179bebcc96aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a0b5d5f066b66d451b5d5cf3395150122568c66c865766b17f3a83e2e4a211586fa04dbdc0f0e921e2e95988a8c725af395cc3d25314ade9ad412f809fd96af1be45a03fcc60c369a34f1fbf6611e71daa0d11f25e66a09c26237087d65c38a0719a79b90100008000000000000000000000008001000000800000000800008000000000000004000000000000040000008000004000000000000800000400000000000000000000000000001020000000090000008042014000000000000000200000000010000000000210000000000000000008040000000000000000000000100000104000000000000000000000400000000000000000000000000000000000010000001000020000000000000000000000000000000001000800000000000000000000000000020010000010000000200000000000200000000000000000000000200000000000000000100000000000000400000000000000000000000000000000000283408841837a1200831aae9884601d0b7cb861000000000000436f6e73656e5379732048797065726c65646765722042657375a93e60140d01945fb8a233a4eedab42b61e0e5c4e7ec25267ec0fd74b481351f5a397679d36ddbc97fd85abd7286ceb63b656bcdd83ebc7e7c8672faa53e541700a00000000000000000000000000000000000000000000000000000000000000000880000000000000000", - "accounts":{ - "0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32":{ - "accountProof":["0xf90211a02176c129dbc42e25c8d1162dc0edf92c2e65bee045d0c7560934438153406a6fa0e118fb12921b790d5a9ee1ad345c260ee3276cf850561658e1abe616baf40286a032517df96c207ceb6964b516f7cf7f54594ed433b0b5f032e362a550b38ad1ffa044b91c7c42e59fa635337b00d381c672bd2945c951e68cd1365ea485d4f37ec6a0e2d2af55418b8ef4f8d9c47f8a53f19b526294b9a97905c340b9cc6f620da301a00bceffcc6073b6cb383dca8d4f7f6abfe605b5e9f40838adc58105da6819319da01305e8d89582149cb08147b4a60bf98863b23c2708960782c29d986482faa388a024ba45083e5850966c3e760f95aec7d4004738f427c25a56c18439348dba9a23a0c40609f42e985bf7c1df75b26786528c4e4ea09a275f01f8704d720a7284e038a03989be34cb23145de7bbdfa095868575dd527202ce41a256b5c84e5582080be9a05f22ae2af2734025efc73c0b5c60329126733cefe03c9f57b0e9170da2264f43a0a86f752f84985ae46c715dd6de83cadeb8491a05442c1c2a490564e496adf8a1a09ba64dbea55976d707883e43c019cc6d94c49b8f6ae65099bda01c84c3888e4fa09296cf138f05ea04341e823d682a7cda889a3f80270725479b40733701db7f0ba0139fdc9eed3c2d025283d6dd4afaddde0919161f51a4ca7e0f2c8495017b7733a0543a55b212f31fa4009ae14c9008a142fd9629442a1dbb886a97c6a9e709b9fc80", - "0xf90211a0ed9c0ff9ae751d881c84a357918739f10525eed27b43de0f9296e5baaf9bb1fda0e0128203a2f634bd2bc3bedfd6bcf164f69f5fbe94c87c05d4908a37a1f81d46a08f3fd424a0c6719514dbef79e1e849c24f39f4239ecdf665c73116c152e09050a0979a57a7ecbc9018043f0d9edeacf3965f5cb9487aa243a016eaabff06740189a0b21b702577cd8579103422d551d85efb638172bc54fc55a75693b5a2fdfdc54fa09b6d19191d8d09916c9fbd3f819173caa43709c5b7041998bef417b2b7515ceda0b210d6ba8e1162dde71877ae2c8a5c9c16137efa662cec3e627d6a9645c0cd44a0acf80531cc4b674c58a4c9faf0fa63034e861b85c8613947da582fec5f1dee68a019cc0bc1c987814d96d15898ef1ec3dfff19b10fb41bbdc7f39eb39182833672a0926bf31e6b8575ca40a55b33717965ccee9500be55c525586d17138bc3d1e43fa0b35f96a10bf3126d6ed21b8cf400e9b1d94ee828dcfce3544462ab21d0c2d6aea00676b7047bfe5e74da9f04b0e58ac0624b60160496dd79f1530d2beb4a6e5350a09c5f723923cdc360a9f7e0e1200fe8f82967e6c1ecac06b94f2d9252204d6caea08799003a84ec35a486ec5d520bc160b57c62c6eb4f8117e0c69ca5cd1ac23440a0a3bfea660692e188e4cf88d78efc21868418c58b43aa51d15afbeaeb5a30ea90a0327f99800e8d4d5146898d0eb401ee4f967f0c62b98014a96efd248d71cb273f80", - "0xf90211a0c52b7d7c9faa8fc42e3843e59b0c7102188d918840b8b5cf5649197b13942a94a067f59108675f198f0b1f14273b4fbb3ba4c9ac61ecaa83900e37154881990941a08407f24415d4b1971faa2f1e53d2cfbb4fae100d135486236834036b94d129fca0cdf5918e749c6ed171d0e1db4a39df01763c7a2e7b0429b304f5de22d16195e8a01efdf7379329d0d371efabd4fa76401f1984fcbca64471ebe2c74475845f0a85a0a28daa09319d9b64e5967130cdd21a099b54869ddf92dff7cc080bbb25279364a0cea9a3f62b9e510d5508f3ca8323dd519cae9c320f99dc172193d07f0c6317b3a0369809678111f01bbcdcd5df6c6a2841e85077206e3b650db6eb2ec037e13a2da0e7511971b817dd5f0241d0309cd6f8e8e9ad35aba0807204884e840c16f4471ca06d6da5d6f453b1ea3dc911431e9a26abc197b409538aa40d46da259e6c08729aa0a5edc7f66811fa983d4e2d61c09fbc50bdefb4d77c487eb1ac533ceab3bcaf09a06e6f4a8927f13aadfb9c772b0715bcfe313f02b6034b0d6f2b59f56e0b9c9c3ba0763f63c10c5e3334c7b3af8f419f8add4e1f85f7cfcedea3f06912d66840c1fda05286d33d2797793fca9d99ece2f764da00716ac9536c635c83c695ed84a0e4d3a0079a7452891f1b97eccbb9bd876353da6732910c99194953c3e392265a6a0712a0d729c7014af2776b688d09d312623bb74cb092c92aafa099bdac3c6a1f53e39980", - "0xf90211a037a3337f0005db74a91f0f96ff9fbacbbc0521a8784b3ff8c9d8444d81e20456a075183b26afb188dcbbeee102e53d7ecf7f9b82c92c0d6efce4ff73c2fc9427eea0fe890d643ed692fa36be93853f94be950f7c57367fc9a3fd19fdfa76fbcd084ea06335914313fc71923c429c0a768c0584f2486976aaf5ae6e939cc0c1819c678fa0cc46707e8dab2140eabb4c2af2c8ae392674ac306f8608037e5afecd026b76b1a0910c8d52ee6bafc81d8033360f056cd845e15a87fbfd52d58daa576b58e9e39aa0db792420e982fc3088994c2fecc5271dae872a7743d8e9509b211c5d4e47bc8ca05eaa1847e192dda347bb7f272485e67f55925df2ad747e9faacc02ba12dd9d2da0a4437a739941a0a53d690f63cf5154030f1786944b7db70b224ad3bc3185725da0cd2436f554561a620f05eccc2cf101540161a3e3f5cd0b850602ece78b39e70da0ea6d47e623b880d04163a1976acd454e44ae57cfacda98a3757ae4350eead60ca0b696d8434f73dce704ab08de53ff772e86f50cfcc1a752002d1e0e5726308679a0a25b9334ba25fbd904b35fdc0063cca2be2817ce508108687a48c9020ff3aa5aa051ffa5e10005f382bbc6b388e200037bb41bbef652137761d8a317ffabaf2b3aa0abb3708f4efc71f63d4d3c45fdfb616da97646b412d79c134f58e64795cebde0a09b81a3cbbf7eeefcafae4f0163f51303c2708f20870dc23edf905da4aa8e522280", - "0xf90131808080a0615059ed3d0c135089a399e39e1108e5f58fc040d20bb1594d327d565f6530dfa0cca3258088afed6e4228125045d2813d736c800bc9551b38e99a228fb4985334a0b8609eb04093c8652ac25cf5d412d366616c55d78e769d0133a4e11baaf51c9480a071319005086e17af8200acfdcdaaac0d39b3a30daafb102cb1e81787a5671246a0d623ac3203b39d02c36962fae8246e75cfc6eb03d9ec358115794e0ef967b7d8a0005374980c9eb696e134af89e55bec93767d19fbc7eacdfb423865e28fb853d98080a0a7f8317e21a74ea5c5ade1f3cfe5b019caf1e5df49c0e6063fe477a7a3e7e79c80a05adcf071a5adcb1c99f67df461650b2ba62d3f48600f4b97e6b1201194288702a05db46c9db1409a03378debec1d3daf79a6365672e7ca0b1902e158d3b18355ea80", - "0xf8679e3dfa35139af4f993370c1dc5c729942db777d38a285fb227906c1bc93854b846f8440180a04d736bfa42e5592b521061863e73c096fb378444a82b1a057e240b863c90666ea029140efcd5358d1dd75badfaa179e3df0dd53f17a883a30152d82903305697a1"], - "address":"0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32", - "balance":"0x0", - "codeHash":"0x29140efcd5358d1dd75badfaa179e3df0dd53f17a883a30152d82903305697a1", - "nonce":"0x1", - "storageHash":"0x4d736bfa42e5592b521061863e73c096fb378444a82b1a057e240b863c90666e", - "storageProof":[{ - "key":"0x0", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf891a0f881becd1a54ec2ac129979fbb7cc851f4245d372d588056b040ccae152793e8808080808080a0f229214dba34c16ca65b1e0df175282a07b3ea9453d97f5b9e9e78762fe279fd80a081b7693d9ef9dea4b83b27e12fd4a86cfd861c266865545c3602ec6a1e43039da0aa7f3523263242a60b36be40eabda7dae87817cbb0cdbf6948564d446d43cae0808080808080", - "0xe2a0200decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56305"], - "value":"0x5" - }, - { - "key":"0x1", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf85180a083500cde7f6fd365be906592de84b1d7b3bbb1fc02e2129051371bf3adb79eed808080808080808080808080a008ce5962e734b12d09863c498b32a8f4b2078bf5b414ae7ee0fa3a57e2b6b3a48080", - "0xf843a0200e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6a1a053786c93e54c21d9852d093c394eee9df8d714d8f2534cdf92f9c9998c528d19"], - "value":"0x53786c93e54c21d9852d093c394eee9df8d714d8f2534cdf92f9c9998c528d19" - }, - { - "key":"0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e567", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf871a0a28c08659e018bd7c2c18c6275f97881c4d7c045943c5caecfea71e41474cc9e8080808080808080808080a0407e55e9a8f7681ee1a46fd778e03ffd2fa7507952c81308e60239bbaba3875a80a0a3e90ff33462c5a993a90a3387e5c9c2d7aad2f327ff5c69cbfab2e5ce6c3a798080", - "0xf843a020418048a637d1641c6d732dd38174732bbf7b47a1cf6d5f65895384518b07d9a1a013218ee8dd11494c58f119748f198ad3c24a0024746f3a5896f51dc1e008821f"], - "value":"0x13218ee8dd11494c58f119748f198ad3c24a0024746f3a5896f51dc1e008821f" - }, - { - "key":"0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e56c", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf8718080a004086d122b3bd8d29156e97aacb81408ae87858145b9749f8f6c7bbf653d6ce38080808080808080a0db8ab81a65a81b2ff2c25e14082cc52fa19e16339b5d616a4a7749e830bf136780a03f40fdc942ec4a9b8cf72f3599a83e4b40cf9f76905977c6e9cf1d93e3485a7b808080", - "0xf843a0205fcc8f73196524ea5f04c38888c2f09c6cbef411cb31e259d35b56e3d0047ba1a091605b26d5a69917787d9bcac74f808951039567c0db700da087a92a4e9a6072"], - "value":"0x91605b26d5a69917787d9bcac74f808951039567c0db700da087a92a4e9a6072" - }, - { - "key":"0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e571", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf871a0d3b9189299e575e9b50d9e6113d15c202e7dd53942477ff21ac5a62e5b5fcd018080a08a0a0e6297b603d3e5577d7e849031cfba639f44aa9f27a34b6e4b950c6c073a808080808080a04a5be21f4f0c0f5e1fcccb140e7cb5b1ff2ce45bce977d8e23cc7320e8c7bdd0808080808080", - "0xf843a0206695c256a4a4a1b8ed004dc824e330f1747032632c0e6d88c1d84c330c1c5ca1a0855e7bdf0b02683e5ab1cdc096c6101085b4c7fe69fe218bffa7dff912490a9b"], - "value":"0x855e7bdf0b02683e5ab1cdc096c6101085b4c7fe69fe218bffa7dff912490a9b" - }, - { - "key":"0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e576", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf85180a083500cde7f6fd365be906592de84b1d7b3bbb1fc02e2129051371bf3adb79eed808080808080808080808080a008ce5962e734b12d09863c498b32a8f4b2078bf5b414ae7ee0fa3a57e2b6b3a48080", - "0xf843a020257165ee8c7eae64faf81e97823d50dba1b6a2be88bccea1ac5d01256f0590a1a0b28786a66e4e84e2b7e236efb9fb0a82053e342cbf26d41af8c4cae4836d56e8"], - "value":"0xb28786a66e4e84e2b7e236efb9fb0a82053e342cbf26d41af8c4cae4836d56e8" - }, - { - "key":"0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e57b", - "proof":["0xf90211a0c1d22c4f4f1cc6ad83407a19b0bf3b129206880b89a580d09ec7151ad9886095a053147b12a82ecbbf958ea21eaaa2b4b5e676963a1753f0e1c817c7cfe8ea48ada0076fff6235c18f210b8221a89fed3f6722f69f20f36341bae5e8da877284000fa0551e4cfb868bba75b151be76c2da75e52824e15c501c0000ba044250a971176fa012a4969e387686fc86af25c07f72fbd658df39b133002d50a19ada8a8c005b9da0e2665b2ac7f21c0d77a88b8ffe7eb97fc247c768d77f8e49e518f7ef949d3989a00916ff25b14411517799d32a46128fd65ee9c90208563ffc6ca52e654673f1b6a07f67f5182dfcdcd333dbe5418dd1372f57ef648defa506aa4b8939d9e87b8fd8a0cee9a05be79629c249555cca89bead566bbf2a791de2729a3f37c4795b0ef3b2a0e07263f33741ea2779a238a95b43c60fe85c1f5c55ccfbe71202a5270e8d4b6ba05f03c75d89c4a70dfe72980c3c740155d12bc07edaca4300153d9f74b6b3624aa0be3f6a3c5587a2e321d2e6a766518c8966e311f921e1936906920fed463ec8b1a078dc7aa0d2de98f73391cda3066e6c4dec84c1ffa840b234b7081ca5ddebd4fea01d8b2a194aeacedef6268d6bf81d351fd0e2177ae007c5201ff7c10e9716d5c4a0f09b315bf02379572d599d47a7610f96feb1b72dd98123a1f47ac8e9750862c2a01d6cc819b88e2433c90c9990aed9dc37c67c80729c88d583c96832b368b76d9b80", - "0xf871a0a28c08659e018bd7c2c18c6275f97881c4d7c045943c5caecfea71e41474cc9e8080808080808080808080a0407e55e9a8f7681ee1a46fd778e03ffd2fa7507952c81308e60239bbaba3875a80a0a3e90ff33462c5a993a90a3387e5c9c2d7aad2f327ff5c69cbfab2e5ce6c3a798080", - "0xf85180808080a02ae042c128229c068ee987ed30164a035d10da3ba96f25b04b7e56e6fc2c12fb808080808080808080a04083c8127572e64ae53476470e7a632258565a37b7f2066cb22ef228e679c24e8080", - "0xf8429f3d807394a26a5623e844d859daa1940d13cb7bda091582294562d688f4de00a1a0986891b6cd760bac247ed5d7dbc574b1c07425bc49a44d54b9900dc485329218"], - "value":"0x986891b6cd760bac247ed5d7dbc574b1c07425bc49a44d54b9900dc485329218" - }] - } - } - } - } -}] - -:: cache btc_target_153 1 -000affff001d000fffff001d00145746651c0019a7bc201c001e64ba0e1c00235a0c011c0028ed66471b002d56720e1b0032cb04041b0037cd2d011b003cfa98001b00418521131a0046864a091a004bcaf00d1a0050d7690d1a005587320b1a005a5f8b0a1a005fc93c081a0064087e051a006962fa041a006e5c98041a007394de011a007815de001a007d328754190082cab01619008742120619008c2cf50119009199db0019009642286918009ba2ae3a1800a093b81f1800a5747b1b1800aa87bb181800aff017171800b48e41161800b9c14d131800be89b2101800c31bb3091800c8c3a4061800cd3684051800d22872051800d7c440041800dc858b031800e1937e021800e63e1b021800eb308d011800f00b31011800f54bce001800fa8c577e1700ff494a51170104495a411701097b4f2f17010e91c125170113f41e37170118505b2e17011d38ff291701229b0d1f170127f5ab1717012c3eb215170131ff3212170136bc2013170135413b1417 +:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 +07000000000043a3e5000000050000000000000000000000000000000001000000450100000000000000000000eb06000091e601008051010000000000eb060000883802008051010000000000eb0600005a3a020080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6274632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6274632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6274632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6274632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6274632f6e642d350000000000 :: request http://localhost:8500 [{"id":1,"jsonrpc":"2.0","method":"getblockheader","params":["000000002afcaf0e0d78e698e71c26fdd5f87b7703c18ed48e27ab133dc3d4bb",true],"in3":{"verification":"proof","version": "2.1.0","chainId":"0x99","finality":5,"preBIP34":true}}] -:: response getblockheader 0 http://localhost:8500 0 129 +:: response getblockheader 0 http://localhost:8500 0 489 [{ "id":1, "jsonrpc":"2.0", "result":{ "hash":"000000002afcaf0e0d78e698e71c26fdd5f87b7703c18ed48e27ab133dc3d4bb", - "confirmations":646406, + "confirmations":654404, "height":22790, "version":1, "versionHex":"00000001", @@ -212,14 +34,16 @@ "final":"0x01000000bbd4c33d13ab278ed48ec103777bf8d5fd261ce798e6780d0eaffc2a00000000d661462f180562c74d25efb0c007d6b4b20fe10b61adecad7484c3c53fde0e41c3dfac4affff001dba4db74d01000000fcdbcd016be8773a777ba924c6468a2c1cc4d020fdbdf13aec9f43c80000000014104745e906313add5762734b11a44e64ea7a35fce3b448365ae3e4e3161d1988e5ac4affff001d149df3dd01000000ceda3913875fad030d3ab752a2c95893e66958fa34c164cffe6e73da00000000b2d841452e2a7117753c423f049cbbe0d13dacdb99266880407e26282ea53d3d3aeaac4affff001d16343a420100000032b5d55ed9667e77bc11c7f3e16503ea184e146cdb3bd290cbd85e83000000003177ded1cce03952f1fc214b3930d6de33186d5a72a5c8c0c9e38ed608fac76f88f0ac4affff001d20e3ad500100000023c81d5fa8eb67ceccc8d42262c900ecb60b2c080865d3eb6e2bdf44000000007120278a4a79ccc46a29f6ec819485c6d04f20c721c0a4e0d1364f1e007eae505af2ac4affff001d0031144a01000000e8b8406cadca24f4be8feac6c2cd1ec21e0c134504e4874807092b6c00000000aa7fbe13f0d870c298a54dc0624375d728557b7f8f6a6c88f58b71a056dc90c542f8ac4affff001d08ffb3d001000000ffe8566bb6808828e251ec3dc6081c335f3a039f6ed4e7143d509ea500000000c2f7dfa0221a15512cb99ea2c193eb39c5c181c840204b623ffde6e17c4e5e47aefaac4affff001dc2bdcc11010000000a77bb0ff642c5c568a7f5440c11f0ddd04fd801d2a66a3325f5ce25000000003b846ebf084baa6a1dbd88ce761c793d21afb86588a1e285d050c3b6f1e8fc657bfcac4affff001d2080bb670100000030ff49d95072dcc9f78301ceeaf43e13c84d8250cb07229c61be5ef20000000005abf988b99ad4199b5122701452117f2328f3c1051678090fc452d8d319ec710404ad4affff001d1fec09660100000079ff834d61ea4c6264612c58d5853fc0392a5c4ded52c797e033a2d9000000008aea365a64ae3664861ed730c087d263d28273ae70c086fc55c211fed8e64d78310dad4affff001d176d900c", "height":22790 }, - "lastNodeList":4229185, - "execTime":125, + "lastNodeList":4539052, + "execTime":437, "rpcTime":0, "rpcCount":0, - "currentBlock":4229194, + "currentBlock":4539053, "version":"2.1.0" } }] +:: cache btc_target_153 0 + :: result -{"hash":"000000002afcaf0e0d78e698e71c26fdd5f87b7703c18ed48e27ab133dc3d4bb","confirmations":646406,"height":22790,"version":1,"versionHex":"00000001","merkleroot":"a2d99d38724cfb8bc7bf25b69278be0511dcc8229280b3d28144ed61c312a542","time":1252839569,"mediantime":1252834756,"nonce":231526776,"bits":"1d00ffff","difficulty":1,"chainwork":"0000000000000000000000000000000000000000000000000000590759075907","nTx":1,"previousblockhash":"0000000081a33fec1ece434279ddc7c59a00c7a4ec2f08db452bade37d50be75","nextblockhash":"00000000c8439fec3af1bdfd20d0c41c2c8a46c624a97b773a77e86b01cddbfc"} +{"hash":"000000002afcaf0e0d78e698e71c26fdd5f87b7703c18ed48e27ab133dc3d4bb","confirmations":654404,"height":22790,"version":1,"versionHex":"00000001","merkleroot":"a2d99d38724cfb8bc7bf25b69278be0511dcc8229280b3d28144ed61c312a542","time":1252839569,"mediantime":1252834756,"nonce":231526776,"bits":"1d00ffff","difficulty":1,"chainwork":"0000000000000000000000000000000000000000000000000000590759075907","nTx":1,"previousblockhash":"0000000081a33fec1ece434279ddc7c59a00c7a4ec2f08db452bade37d50be75","nextblockhash":"00000000c8439fec3af1bdfd20d0c41c2c8a46c624a97b773a77e86b01cddbfc"} diff --git a/c/test/testdata/cmd/eth_blockNumber.txt b/c/test/testdata/cmd/eth_blockNumber.txt index 36b20a370..120afbc57 100644 --- a/c/test/testdata/cmd/eth_blockNumber.txt +++ b/c/test/testdata/cmd/eth_blockNumber.txt @@ -1,4 +1,4 @@ -:: cmd ./bin/in3 -fi ../c/test/testdata/cmd/eth_blockNumber.txt eth_blockNumber +:: cmd ./bin/in3 -fi ../c/test/testdata/cmd/eth_blockNumber.txt eth_blockNumber -rc 1 :: time 1607332761 diff --git a/c/test/testdata/cmd/eth_getBlockByNumber.txt b/c/test/testdata/cmd/eth_getBlockByNumber.txt index d504e277a..3dca98c9b 100644 --- a/c/test/testdata/cmd/eth_getBlockByNumber.txt +++ b/c/test/testdata/cmd/eth_getBlockByNumber.txt @@ -1,4 +1,4 @@ -:: cmd ./bin/in3 -fi ../c/test/testdata/cmd/eth_getBlockByNumber.txt eth_getBlockByNumber latest false +:: cmd ./bin/in3 -fi ../c/test/testdata/cmd/eth_getBlockByNumber.txt eth_getBlockByNumber latest false -rc 1 :: time 1607332862 diff --git a/c/test/testdata/cmd/eth_getStorageAt_empty.txt b/c/test/testdata/cmd/eth_getStorageAt_empty.txt index 2ca004a3c..b6af389ab 100644 --- a/c/test/testdata/cmd/eth_getStorageAt_empty.txt +++ b/c/test/testdata/cmd/eth_getStorageAt_empty.txt @@ -1,4 +1,4 @@ -:: cmd ./bin/in3 -fi ../c/test/testdata/cmd/eth_getStorageAt_empty.txt eth_getStorageAt 0xe0060c57527ff36122342daebbeb19e910f3038b 0x0000000000000000000000000000000000000000000000000000000000000000 latest +:: cmd ./bin/in3 -fi ../c/test/testdata/cmd/eth_getStorageAt_empty.txt eth_getStorageAt 0xe0060c57527ff36122342daebbeb19e910f3038b 0x0000000000000000000000000000000000000000000000000000000000000000 latest -rc 1 :: time 1607333024 diff --git a/c/test/testdata/cmd/zksync_setKey.txt b/c/test/testdata/cmd/zksync_setKey.txt deleted file mode 100644 index 6100068f2..000000000 --- a/c/test/testdata/cmd/zksync_setKey.txt +++ /dev/null @@ -1,87 +0,0 @@ -:: cmd ./in3 -x -fi ../../c/test/testdata/cmd/zksync_set_key.txt -zks http://localhost:3030 -c http://localhost:8545 -pk 0xe20eb92b34a3c5bd2ef0802a4bc443a90e73fc4a0edc4781446d7b22a44cc5d8 zksync_set_key ETH -debug - -:: time 1608290665 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 0 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 0 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 0 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 0 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x8a91dc2d28b689474298d91899f0c1baf62cb85b"]}] - -:: response account_info 0 http://localhost:3030 0 82 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x8a91dc2d28b689474298d91899f0c1baf62cb85b", - "id":1, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"972447700001000001" - }, - "nonce":28, - "pubKeyHash":"sync:87b8682587abb487584801ca4546c4b8f8173c11" - }, - "verified":{ - "balances":{ - "ETH":"972447700001000001" - }, - "nonce":28, - "pubKeyHash":"sync:87b8682587abb487584801ca4546c4b8f8173c11" - } - }, - "id":2 -}] - - -:: cache zksync_tokens_69f5 1 -03004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f00000045544800000000001200000000000000000000000000000000000000000004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:3030 - [{"id":3,"jsonrpc":"2.0","method":"get_tx_fee","params":[{"ChangePubKey":{"onchainPubkeyAuth":false}},"0x8a91dc2d28b689474298d91899f0c1baf62cb85b","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 2 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":{ - "ChangePubKey":{ - "onchainPubkeyAuth":false - } - }, - "gasTxAmount":"10000", - "gasPriceWei":"9000000000", - "gasFee":"90000000000000", - "zkpFee":"31465100362113", - "totalFee":"121400000000000" - }, - "id":3 -}] - - -:: request http://localhost:3030 - [{"id":4,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"ChangePubKey","accountId":1,"account":"0x8a91dc2d28b689474298d91899f0c1baf62cb85b","newPkHash":"sync:20ae0f0ef7780f289ade282a466ad287969a1253","feeToken":0,"fee":121400000000000,"nonce":28,"signature":{"pubKey":"3746e7a4dbbb25ea36a3d20c2431775f69b737b1e0a173803e9cf74570b5dc86","signature":"3756ed60d8c323a81d070e1f8857fcb18a53eddbb9871d3c90a01599ddb470a63190fe165251c988338865049b97fafd9935be818a78fe9921453738f8536904"},"ethSignature":"0x836ae105a27a65deb4f688e6cdcbe71928bc6bb7599facce2281adcde8504052374a81ab3944c039ca56dfe98e7e9348c1f600f374c779c180bf30567405a9101b"},null,false]}] - -:: response tx_submit 0 http://localhost:3030 0 41 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:489f5f95f2dcaef4f5534d62d8646d2e9891a31b409f1a8ba9b581886e7177c6", - "id":4 -}] - - -:: result -sync:20ae0f0ef7780f289ade282a466ad287969a1253 \ No newline at end of file diff --git a/c/test/testdata/cmd/zksync_setkey_eip1271.txt b/c/test/testdata/cmd/zksync_setkey_eip1271.txt deleted file mode 100644 index e45d9fafb..000000000 --- a/c/test/testdata/cmd/zksync_setkey_eip1271.txt +++ /dev/null @@ -1,284 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xe20eb92b34a3c5bd2ef0802a4bc443a90e73fc4a0edc4781446d7b22a44cc5d8 -c http://localhost:8545 -fi zksync_setkey_eip1271.txt -ms 0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 -zka 0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 -zkat contract zksync_set_key ETH - -:: time 1610970472 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4"]}] - -:: response account_info 0 http://localhost:3030 0 153 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4", - "id":6, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"4000000000000000000" - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - }, - "verified":{ - "balances":{ - - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:8545 - [{"id":3,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xa3f4df7e"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 28 -[{ - "jsonrpc":"2.0", - "id":3, - "result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000949414d4f20536166650000000000000000000000000000000000000000000000" -}] - - -:: request http://localhost:8545 - [{"id":4,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xf698da25"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":4, - "result":"0x4846ae0437a0613c8742fb643c665ce1a680fbab2a296b40bc5d09e317c13acb" -}] - - -:: request http://localhost:8545 - [{"id":5,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xa0e67e2b"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":5, - "result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000072876ced28f246e1d1e44b38f7825c999a951ea70000000000000000000000008a91dc2d28b689474298d91899f0c1baf62cb85b" -}] - - -:: request http://localhost:8545 - [{"id":6,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xe75235b8"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 11 -[{ - "jsonrpc":"2.0", - "id":6, - "result":"0x0000000000000000000000000000000000000000000000000000000000000001" -}] - - -:: cache zksync_contracts_69f5 1 -9ab6772bd7789d2865a27da5a3f7f265bc3ea39d9534a57289b8f1b8dcb6b2722c803f2969a8c0d1 - -:: request http://localhost:8545 - [{"id":7,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x9ab6772bd7789d2865a27da5a3f7f265bc3ea39d","data":"0x8ae20dc90000000000000000000000007ff5c6fab254833c037ad23ca32e2b2b2c6083a40000000000000000000000000000000000000000000000000000000000000000"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 14 -[{ - "jsonrpc":"2.0", - "id":7, - "result":"0x0000000000000000000000000000000000000000000000000000000000000000" -}] - - -:: request http://localhost:8545 - [{"id":10,"jsonrpc":"2.0","method":"eth_chainId","params":[]}] - -:: response eth_chainId 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":10, - "result":"0x9" -}] - - -:: request http://localhost:8545 - [{"id":11,"jsonrpc":"2.0","method":"eth_getTransactionCount","params":["0x8a91dc2d28b689474298d91899f0c1baf62cb85b","latest"]}] - -:: response eth_getTransactionCount 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":11, - "result":"0x37" -}] - - -:: request http://localhost:8545 - [{"id":12,"jsonrpc":"2.0","method":"eth_gasPrice","params":[]}] - -:: response eth_gasPrice 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":12, - "result":"0x3b9aca00" -}] - - -:: request http://localhost:8545 - [{"id":13,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xaffed0e0"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 20 -[{ - "jsonrpc":"2.0", - "id":13, - "result":"0x0000000000000000000000000000000000000000000000000000000000000001" -}] - - -:: request http://localhost:8545 - [{"id":14,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xd8d11f780000000000000000000000009ab6772bd7789d2865a27da5a3f7f265bc3ea39d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d400000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000084595a5ebc0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000142634ea722a88f1d178fe4b1b63c2e91a7089cfac00000000000000000000000000000000000000000000000000000000000000000000000000000000"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 13 -[{ - "jsonrpc":"2.0", - "id":14, - "result":"0x3981ed8b93dd5af67e8c2d45f013bb70c9615665943629eba7fe99b7beb07d6d" -}] - - -:: request http://localhost:8545 - [{"id":9,"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0xf902ea37843b9aca008307a120947ff5c6fab254833c037ad23ca32e2b2b2c6083a480b902846a7612020000000000000000000000009ab6772bd7789d2865a27da5a3f7f265bc3ea39d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030d400000000000000000000000000000000000000000000000000000000000030d40000000000000000000000000000000000000000000000000000000003b9aca000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000084595a5ebc0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000142634ea722a88f1d178fe4b1b63c2e91a7089cfac0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000410000000000000000000000008a91dc2d28b689474298d91899f0c1baf62cb85b0000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000036a075df23ee769da112b0207b067fb560cd689d248ea9ee8d96a3ae55f1a1d21bc7a04d16290ada4c89ccd9cc56174706532a75e0c96988670e1d346a3214cf6cd535"]}] - -:: response eth_sendRawTransaction 0 http://localhost:8545 0 73 -[{ - "jsonrpc":"2.0", - "id":9, - "result":"0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef" -}] - - -:: request http://localhost:8545 - [{"id":15,"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef"]}] - -:: response eth_getTransactionReceipt 0 http://localhost:8545 0 52 -[{ - "jsonrpc":"2.0", - "id":15, - "result":null -}] - - -:: request http://localhost:8545 - [{"id":16,"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef"]}] - -:: response eth_getTransactionReceipt 0 http://localhost:8545 0 11 -[{ - "jsonrpc":"2.0", - "id":16, - "result":null -}] - - -:: request http://localhost:8545 - [{"id":17,"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef"]}] - -:: response eth_getTransactionReceipt 0 http://localhost:8545 0 13 -[{ - "jsonrpc":"2.0", - "id":17, - "result":{ - "blockHash":"0x5c088aa63127ea9800ec3942b26a06a3d456830b240e14ac5394204bbbf9d685", - "blockNumber":"0x27719", - "contractAddress":null, - "cumulativeGasUsed":"0x154d4", - "from":"0x8a91dc2d28b689474298d91899f0c1baf62cb85b", - "gasUsed":"0x154d4", - "logs":[{ - "address":"0x9ab6772bd7789d2865a27da5a3f7f265bc3ea39d", - "topics":["0x9ea39b45a0cc96a2139996ec8dd30326216111249750781e563ae27c31ae8766", - "0x0000000000000000000000007ff5c6fab254833c037ad23ca32e2b2b2c6083a4"], - "data":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000142634ea722a88f1d178fe4b1b63c2e91a7089cfac000000000000000000000000", - "blockNumber":"0x27719", - "transactionHash":"0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef", - "transactionIndex":"0x0", - "blockHash":"0x5c088aa63127ea9800ec3942b26a06a3d456830b240e14ac5394204bbbf9d685", - "logIndex":"0x0", - "removed":false - }, - { - "address":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4", - "topics":["0x442e715f626346e8c54381002da614f62bee8d27386535b2521ec8540898556e", - "0x3981ed8b93dd5af67e8c2d45f013bb70c9615665943629eba7fe99b7beb07d6d"], - "data":"0x0000000000000000000000000000000000000000000000000000d48b9710c000", - "blockNumber":"0x27719", - "transactionHash":"0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef", - "transactionIndex":"0x0", - "blockHash":"0x5c088aa63127ea9800ec3942b26a06a3d456830b240e14ac5394204bbbf9d685", - "logIndex":"0x1", - "removed":false - }], - "logsBloom":"0x00000000400000000000000000000000000000000000000000000020040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002100400000000000000000000080000000000000000000000000000000000000000100400000000000000000000000000000000000000000000000000040000000000000000004000000000000000002000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000002000000000000000000000000000000004080000000200000000", - "status":"0x1", - "to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4", - "transactionHash":"0x537799a241cdb5eea7e3d373cb0c7924291db063f748bbea1eaaff46ddacc7ef", - "transactionIndex":"0x0" - } -}] - - -:: request http://localhost:3030 - [{"id":18,"jsonrpc":"2.0","method":"get_tx_fee","params":[{"ChangePubKey":{"onchainPubkeyAuth":true}},"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 201 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":{ - "ChangePubKey":{ - "onchainPubkeyAuth":true - } - }, - "gasTxAmount":"9929", - "gasPriceWei":"1000000000", - "gasFee":"9929000000000", - "zkpFee":"30801359919309", - "totalFee":"40700000000000" - }, - "id":18 -}] - - -:: request http://localhost:3030 - [{"id":19,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"ChangePubKey","accountId":6,"account":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","newPkHash":"sync:2634ea722a88f1d178fe4b1b63c2e91a7089cfac","feeToken":0,"fee":40700000000000,"nonce":0,"signature":{"pubKey":"9210334638e3c73a45accc0f791339fab57bd0714a2c7295c252f270c5a24805","signature":"7d9d091313274db6d625c8128916b4a3625ad361d7499ec8689f79d80eaf1da7c0e4c9982e01b09fab74478db461e187ab8ac624b8ad1bb254c5d37042802601"},"ethSignature":null},null,false]}] - -:: response tx_submit 0 http://localhost:3030 0 130 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:10bcbaf339f299a74beab3c82b513fce633153adc5dd5acdafe8b8163ca521e7", - "id":19 -}] - - -:: result -sync:2634ea722a88f1d178fe4b1b63c2e91a7089cfac diff --git a/c/test/testdata/cmd/zksync_setkey_pk.txt b/c/test/testdata/cmd/zksync_setkey_pk.txt deleted file mode 100644 index cee40c904..000000000 --- a/c/test/testdata/cmd/zksync_setkey_pk.txt +++ /dev/null @@ -1,91 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xc073624ce79d35f60581134500771cecf21c34e07f5f599033cabaceeb187fef -c http://localhost:8545 -fi zksync_setkey_pk.txt zksync_set_key ETH -debug - -:: time 1610972402 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x3b98be46d348d45ddcd3343f7fdb21373e7f197c"]}] - -:: response account_info 0 http://localhost:3030 0 414 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c", - "id":8, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"1990493664000000000" - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - }, - "verified":{ - "balances":{ - - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:3030 - [{"id":3,"jsonrpc":"2.0","method":"get_tx_fee","params":[{"ChangePubKey":{"onchainPubkeyAuth":false}},"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 116 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":{ - "ChangePubKey":{ - "onchainPubkeyAuth":false - } - }, - "gasTxAmount":"10000", - "gasPriceWei":"1000000000", - "gasFee":"10000000000000", - "zkpFee":"27514315249288", - "totalFee":"37500000000000" - }, - "id":3 -}] - - -:: request http://localhost:3030 - [{"id":4,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"ChangePubKey","accountId":8,"account":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c","newPkHash":"sync:9d9625572548e7fe4ba248ac60c0c7fdaca432cc","feeToken":0,"fee":37500000000000,"nonce":0,"signature":{"pubKey":"33624d532da9ec053a183287a59f0af93750fa98f4c6c43f1beec1381bf03d12","signature":"58e7802d1b4781952f3f29c8d6474c7946a812bef54154f80636141164b7c79329dd1bdccc03b556ac0dd2fca4d9df3dc7c1a72a974f37af349d248fa9fa7002"},"ethSignature":"0x21a21b5d0942ba1e255c3ca9ae6f7c268504cac25a966ed279765c3be10e1a2e5427ee1cdb8100f46b8fb522789012e0d494e585aadf682e71ff9acdab0a37e31c"},null,false]}] - -:: response tx_submit 0 http://localhost:3030 0 316 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:d0a01ac86c19ccccbf1d7b8abe4dc50cd5c21ec337932635a5d62cfb56636f0d", - "id":4 -}] - - -:: result -sync:9d9625572548e7fe4ba248ac60c0c7fdaca432cc diff --git a/c/test/testdata/cmd/zksync_setkey_pk_again.txt b/c/test/testdata/cmd/zksync_setkey_pk_again.txt deleted file mode 100644 index 50757415c..000000000 --- a/c/test/testdata/cmd/zksync_setkey_pk_again.txt +++ /dev/null @@ -1,58 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xc073624ce79d35f60581134500771cecf21c34e07f5f599033cabaceeb187fef -c http://localhost:8545 -fi zksync_setkey_pk_again.txt zksync_set_key ETH -debug - -:: time 1610972451 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x3b98be46d348d45ddcd3343f7fdb21373e7f197c"]}] - -:: response account_info 0 http://localhost:3030 0 208 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c", - "id":8, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"1990456164000000000" - }, - "nonce":1, - "pubKeyHash":"sync:9d9625572548e7fe4ba248ac60c0c7fdaca432cc" - }, - "verified":{ - "balances":{ - - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: result -Error: Signer key is already set diff --git a/c/test/testdata/cmd/zksync_transfer_c2.txt b/c/test/testdata/cmd/zksync_transfer_c2.txt deleted file mode 100644 index 40d9f118c..000000000 --- a/c/test/testdata/cmd/zksync_transfer_c2.txt +++ /dev/null @@ -1,93 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -c http://localhost:8545 -fi zksync_transfer_c2.txt -zka 0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 -zkat create2 -zsk 0xd818beb367732ed0bc2ada1750fcce827040edf96ea3cdacf420b5c3cb1456df zksync_transfer 0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec 0.1eth ETH - -:: time 1611139221 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 0 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 0 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 0 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 0 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 0 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02"]}] - -:: response account_info 0 http://localhost:3030 0 255 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02", - "id":7, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"1499883400000000000" - }, - "nonce":1, - "pubKeyHash":"sync:41b98bc1f219e0defd42078d018826f4404f801c" - }, - "verified":{ - "balances":{ - - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 0 - -:: cache zksync_tokens_69f5 1 -010044414900000000001264e9e65f1cee1fa5071d0075cd211e7b7274a444000300424154000000000012a41da0108ab9875d60169b8a9cd4c8a13b3b2ef20000004554480000000000120000000000000000000000000000000000000000000200774254430000000008cb18f9f65af976000931ca251741068e6aabbd2f0004004d4c545400000000124368e824754a7de8d8a82db5725c48a1e191cbc600 - -:: request http://localhost:3030 - [{"id":3,"jsonrpc":"2.0","method":"get_tx_fee","params":["Transfer","0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 84 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":"TransferToNew", - "gasTxAmount":"6862", - "gasPriceWei":"9000000000", - "gasFee":"61758000000000", - "zkpFee":"32336663980566", - "totalFee":"94000000000000" - }, - "id":3 -}] - - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 1 -00000007 - -:: request http://localhost:3030 - [{"id":4,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"Transfer","accountId":7,"from":"0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02","to":"0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec","token":0,"amount":100000000000000000,"fee":94000000000000,"nonce":1,"signature":{"pubKey":"7232c12955637f5fb5495ab9279bba0a0830bc48f72c6e0c380a2fe40888c8ac","signature":"c8bbd3befec8a6581381398b91166d8405a372f5ff43d773c017f422e3171786191a9f8abccda8c5542a487de50fd6d4847ec402331abdbf3547647274b0be05"}},{"type":"EthereumSignature","signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"}]}] - -:: response tx_submit 0 http://localhost:3030 0 335 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:3390ff0452e19593166cc6c4883b46a3a78c82fbde01a96b9ae03bf7f74a7c28", - "id":4 -}] - - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 1 -00000007 - -:: result -{"type":"Transfer","accountId":7,"from":"0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02","to":"0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec","token":0,"amount":100000000000000000,"fee":94000000000000,"nonce":1,"txHash":"sync-tx:3390ff0452e19593166cc6c4883b46a3a78c82fbde01a96b9ae03bf7f74a7c28"} diff --git a/c/test/testdata/cmd/zksync_transfer_eip1271.txt b/c/test/testdata/cmd/zksync_transfer_eip1271.txt deleted file mode 100644 index 4454f9dc8..000000000 --- a/c/test/testdata/cmd/zksync_transfer_eip1271.txt +++ /dev/null @@ -1,141 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xe20eb92b34a3c5bd2ef0802a4bc443a90e73fc4a0edc4781446d7b22a44cc5d8 -c http://localhost:8545 -fi zksync_transfer_eip1271.txt -ms 0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 -zka 0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 -zkat contract zksync_transfer 0xab6ff744cf1276a78460e93972d22725abd3e095 9433236000000000 ETH - -:: time 1610970586 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: request http://localhost:8545 - [{"id":2,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xa3f4df7e"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 18 -[{ - "jsonrpc":"2.0", - "id":2, - "result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000949414d4f20536166650000000000000000000000000000000000000000000000" -}] - - -:: request http://localhost:8545 - [{"id":3,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xf698da25"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":3, - "result":"0x4846ae0437a0613c8742fb643c665ce1a680fbab2a296b40bc5d09e317c13acb" -}] - - -:: request http://localhost:8545 - [{"id":4,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xa0e67e2b"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 13 -[{ - "jsonrpc":"2.0", - "id":4, - "result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000072876ced28f246e1d1e44b38f7825c999a951ea70000000000000000000000008a91dc2d28b689474298d91899f0c1baf62cb85b" -}] - - -:: request http://localhost:8545 - [{"id":5,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xe75235b8"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 9 -[{ - "jsonrpc":"2.0", - "id":5, - "result":"0x0000000000000000000000000000000000000000000000000000000000000001" -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 0 - -:: request http://localhost:3030 - [{"id":6,"jsonrpc":"2.0","method":"account_info","params":["0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4"]}] - -:: response account_info 0 http://localhost:3030 0 559 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4", - "id":6, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"3999959300000000000" - }, - "nonce":1, - "pubKeyHash":"sync:2634ea722a88f1d178fe4b1b63c2e91a7089cfac" - }, - "verified":{ - "balances":{ - - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":6 -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 0 - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:3030 - [{"id":7,"jsonrpc":"2.0","method":"get_tx_fee","params":["Transfer","0xab6ff744cf1276a78460e93972d22725abd3e095","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 57 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":"TransferToNew", - "gasTxAmount":"1650", - "gasPriceWei":"1000000000", - "gasFee":"1650000000000", - "zkpFee":"30801359919309", - "totalFee":"32400000000000" - }, - "id":7 -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 1 -00000006 - -:: request http://localhost:3030 - [{"id":8,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"Transfer","accountId":6,"from":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","to":"0xab6ff744cf1276a78460e93972d22725abd3e095","token":0,"amount":9433236000000000,"fee":32400000000000,"nonce":1,"signature":{"pubKey":"9210334638e3c73a45accc0f791339fab57bd0714a2c7295c252f270c5a24805","signature":"c04dbdc1c668fafb162584ec816ff6ae94db7d598c4bfc69ca2f93411ff922b089cd1cdeb3a99266bffb37d96a9183088139c9b0a01dd039c9fab5346b9eb204"}},{"type":"EIP1271Signature","signature":"0x8dda163e0af2140d0034eefcc8c37262a23cc4a7cfa12db2aeb6d199534513b8412876637b12d104a61d59af49e11e26a96cc798fff3361346cd602f508951681c"}]}] - -:: response tx_submit 0 http://localhost:3030 0 668 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:0742270d982f9296b79c85e5d83148e581509e823a4c60da3aa5dfa662b7451b", - "id":8 -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 1 -00000006 - -:: result -{"type":"Transfer","accountId":6,"from":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","to":"0xab6ff744cf1276a78460e93972d22725abd3e095","token":0,"amount":9433236000000000,"fee":32400000000000,"nonce":1,"txHash":"sync-tx:0742270d982f9296b79c85e5d83148e581509e823a4c60da3aa5dfa662b7451b"} diff --git a/c/test/testdata/cmd/zksync_transfer_pk.txt b/c/test/testdata/cmd/zksync_transfer_pk.txt deleted file mode 100644 index a13a880cf..000000000 --- a/c/test/testdata/cmd/zksync_transfer_pk.txt +++ /dev/null @@ -1,97 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xc073624ce79d35f60581134500771cecf21c34e07f5f599033cabaceeb187fef -c http://localhost:8545 -fi zksync_transfer_pk.txt zksync_transfer 0x8a91dc2d28b689474298d91899f0c1baf62cb85b 990493664000000000 ETH - -:: time 1610972571 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 0 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x3b98be46d348d45ddcd3343f7fdb21373e7f197c"]}] - -:: response account_info 0 http://localhost:3030 0 436 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c", - "id":8, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"1990456164000000000" - }, - "nonce":1, - "pubKeyHash":"sync:9d9625572548e7fe4ba248ac60c0c7fdaca432cc" - }, - "verified":{ - "balances":{ - "ETH":"1990493664000000000" - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 0 - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:3030 - [{"id":3,"jsonrpc":"2.0","method":"get_tx_fee","params":["Transfer","0x8a91dc2d28b689474298d91899f0c1baf62cb85b","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 536 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":"Transfer", - "gasTxAmount":"550", - "gasPriceWei":"1000000000", - "gasFee":"550000000000", - "zkpFee":"9171438416430", - "totalFee":"9720000000000" - }, - "id":3 -}] - - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 1 -00000008 - -:: request http://localhost:3030 - [{"id":4,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"Transfer","accountId":8,"from":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c","to":"0x8a91dc2d28b689474298d91899f0c1baf62cb85b","token":0,"amount":990493664000000000,"fee":9720000000000,"nonce":1,"signature":{"pubKey":"33624d532da9ec053a183287a59f0af93750fa98f4c6c43f1beec1381bf03d12","signature":"04795df4f451f7ae26a17c76530f910b44d0835f29984a2fb0ef512271514a16c911fd0642979a2d67f9dd2c137a2e53d4030e585f3436e65930a389ae5dfe01"}},{"type":"EthereumSignature","signature":"0xfa944c2cdf233ff6a423e43cc1720bdd802f48f4ad45e3607e910e7ae75ecd2c6d5d7f16ec1ff6c428a908ac0ccd67a7db972f9c3b31b3b405f4b73e13686b731b"}]}] - -:: response tx_submit 0 http://localhost:3030 0 572 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:343c96affe05c9b6c65d0c0eb8a1cf5a5a6c9b3f30dee85011c433db5680db60", - "id":4 -}] - - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 1 -00000008 - -:: result -{"type":"Transfer","accountId":8,"from":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c","to":"0x8a91dc2d28b689474298d91899f0c1baf62cb85b","token":0,"amount":990493664000000000,"fee":9720000000000,"nonce":1,"txHash":"sync-tx:343c96affe05c9b6c65d0c0eb8a1cf5a5a6c9b3f30dee85011c433db5680db60"} diff --git a/c/test/testdata/cmd/zksync_withdraw_c2.txt b/c/test/testdata/cmd/zksync_withdraw_c2.txt deleted file mode 100644 index 6af2a6169..000000000 --- a/c/test/testdata/cmd/zksync_withdraw_c2.txt +++ /dev/null @@ -1,95 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -c http://localhost:8545 -fi zksync_withdraw_c2.txt -zka 0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 -zkat create2 -zsk 0xd818beb367732ed0bc2ada1750fcce827040edf96ea3cdacf420b5c3cb1456df zksync_withdraw 0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec 0.05eth ETH - -:: time 1611139373 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 0 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 0 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 0 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 0 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 1 -00000007 - -:: cache zksync_tokens_69f5 1 -010044414900000000001264e9e65f1cee1fa5071d0075cd211e7b7274a444000300424154000000000012a41da0108ab9875d60169b8a9cd4c8a13b3b2ef20000004554480000000000120000000000000000000000000000000000000000000200774254430000000008cb18f9f65af976000931ca251741068e6aabbd2f0004004d4c545400000000124368e824754a7de8d8a82db5725c48a1e191cbc600 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02"]}] - -:: response account_info 0 http://localhost:3030 0 255 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02", - "id":7, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"1399789400000000000" - }, - "nonce":2, - "pubKeyHash":"sync:41b98bc1f219e0defd42078d018826f4404f801c" - }, - "verified":{ - "balances":{ - - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 1 -00000007 - -:: request http://localhost:3030 - [{"id":3,"jsonrpc":"2.0","method":"get_tx_fee","params":["Withdraw","0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 67 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":"Withdraw", - "gasTxAmount":"96991", - "gasPriceWei":"9000000000", - "gasFee":"872919000000000", - "zkpFee":"32336663980566", - "totalFee":"905000000000000" - }, - "id":3 -}] - - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 1 -00000007 - -:: request http://localhost:3030 - [{"id":4,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"Withdraw","accountId":7,"from":"0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02","to":"0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec","token":0,"amount":50000000000000000,"fee":905000000000000,"nonce":2,"signature":{"pubKey":"7232c12955637f5fb5495ab9279bba0a0830bc48f72c6e0c380a2fe40888c8ac","signature":"fb770eb76ac492d6a01e996fdff52fee747f057579a876866cfd695de44ac6169f34e1481b31f2379f90a42f9d10825239c496c978b85d3fe42dc2303a996a03"}},{"type":"EthereumSignature","signature":"0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b"}]}] - -:: response tx_submit 0 http://localhost:3030 0 52 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:bd574e8b0d8f93a205801cdb1a6a15521e089b3b927436b22a7f9a24625c407e", - "id":4 -}] - - -:: cache zksync_ac8f6883b78d71ac2871f4b01d6fc5e047ccecfd02 1 -00000007 - -:: result -{"type":"Withdraw","accountId":7,"from":"0x8f6883b78d71ac2871f4b01d6fc5e047ccecfd02","to":"0xce93a4561fe6445653a8f6e4ff73c971ce1e9eec","token":0,"amount":50000000000000000,"fee":905000000000000,"nonce":2,"txHash":"sync-tx:bd574e8b0d8f93a205801cdb1a6a15521e089b3b927436b22a7f9a24625c407e"} diff --git a/c/test/testdata/cmd/zksync_withdraw_eip1271.txt b/c/test/testdata/cmd/zksync_withdraw_eip1271.txt deleted file mode 100644 index 40261189d..000000000 --- a/c/test/testdata/cmd/zksync_withdraw_eip1271.txt +++ /dev/null @@ -1,143 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xe20eb92b34a3c5bd2ef0802a4bc443a90e73fc4a0edc4781446d7b22a44cc5d8 -c http://localhost:8545 -fi zksync_withdraw_eip1271.txt -ms 0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 -zka 0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 -zkat contract zksync_withdraw 0xab6ff744cf1276a78460e93972d22725abd3e095 1990493664000000000 ETH - -:: time 1610970705 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: request http://localhost:8545 - [{"id":2,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xa3f4df7e"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 121 -[{ - "jsonrpc":"2.0", - "id":2, - "result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000949414d4f20536166650000000000000000000000000000000000000000000000" -}] - - -:: request http://localhost:8545 - [{"id":3,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xf698da25"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 93 -[{ - "jsonrpc":"2.0", - "id":3, - "result":"0x4846ae0437a0613c8742fb643c665ce1a680fbab2a296b40bc5d09e317c13acb" -}] - - -:: request http://localhost:8545 - [{"id":4,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xa0e67e2b"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 10 -[{ - "jsonrpc":"2.0", - "id":4, - "result":"0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000072876ced28f246e1d1e44b38f7825c999a951ea70000000000000000000000008a91dc2d28b689474298d91899f0c1baf62cb85b" -}] - - -:: request http://localhost:8545 - [{"id":5,"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","data":"0xe75235b8"},"latest"]}] - -:: response eth_call 0 http://localhost:8545 0 12 -[{ - "jsonrpc":"2.0", - "id":5, - "result":"0x0000000000000000000000000000000000000000000000000000000000000001" -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 1 -00000006 - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:3030 - [{"id":6,"jsonrpc":"2.0","method":"account_info","params":["0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4"]}] - -:: response account_info 0 http://localhost:3030 0 332 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4", - "id":6, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"3990493664000000000" - }, - "nonce":2, - "pubKeyHash":"sync:2634ea722a88f1d178fe4b1b63c2e91a7089cfac" - }, - "verified":{ - "balances":{ - "ETH":"4000000000000000000" - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":6 -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 1 -00000006 - -:: request http://localhost:3030 - [{"id":7,"jsonrpc":"2.0","method":"get_tx_fee","params":["Withdraw","0xab6ff744cf1276a78460e93972d22725abd3e095","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 48 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":"Withdraw", - "gasTxAmount":"45000", - "gasPriceWei":"1000000000", - "gasFee":"45000000000000", - "zkpFee":"30801359919309", - "totalFee":"75800000000000" - }, - "id":7 -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 1 -00000006 - -:: request http://localhost:3030 - [{"id":8,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"Withdraw","accountId":6,"from":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","to":"0xab6ff744cf1276a78460e93972d22725abd3e095","token":0,"amount":1990493664000000000,"fee":75800000000000,"nonce":2,"signature":{"pubKey":"9210334638e3c73a45accc0f791339fab57bd0714a2c7295c252f270c5a24805","signature":"dc799017375a065c3bcd8185990a46e6b73612de1eebde429579a1538755e00b3fbd0bdcc3e3ef2297956c48c12c395abd35cb1b0b7c778c396b71f25a2c0703"}},{"type":"EIP1271Signature","signature":"0x7fe6d8e1b56998d1a451854e7417dd546a0e832c20c3433b020cb61200c6847f4fe99e3eb755b26f904135b6643e1c99e6c86488a18cd7bcbb813802586323c61b"}]}] - -:: response tx_submit 0 http://localhost:3030 0 95 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:e3319b79867e81d7b928d19b526b26580d7f0dff82f911cd73c461847238db68", - "id":8 -}] - - -:: cache zksync_ac7ff5c6fab254833c037ad23ca32e2b2b2c6083a4 1 -00000006 - -:: result -{"type":"Withdraw","accountId":6,"from":"0x7ff5c6fab254833c037ad23ca32e2b2b2c6083a4","to":"0xab6ff744cf1276a78460e93972d22725abd3e095","token":0,"amount":1990493664000000000,"fee":75800000000000,"nonce":2,"txHash":"sync-tx:e3319b79867e81d7b928d19b526b26580d7f0dff82f911cd73c461847238db68"} diff --git a/c/test/testdata/cmd/zksync_withdraw_pk.txt b/c/test/testdata/cmd/zksync_withdraw_pk.txt deleted file mode 100644 index 4270413c2..000000000 --- a/c/test/testdata/cmd/zksync_withdraw_pk.txt +++ /dev/null @@ -1,99 +0,0 @@ -:: cmd in3 -x -zks http://localhost:3030 -pk 0xc073624ce79d35f60581134500771cecf21c34e07f5f599033cabaceeb187fef -c http://localhost:8545 -fi zksync_withdraw_pk.txt zksync_withdraw 0xec8b8b4e05f647b36fa31d76273a7dc2860d0e73 90493664000000000 ETH - -:: time 1610972724 - -:: cache nodelist_1_0xac1b824795e1eb1f6e609fe0da9b9af8beaab60f 1 -06ac1b824795e1eb1f6e609fe0da9b9af8beaab60f0000000000aa13280000001801000000cc000000000000000000000001000000ba0000000000000000000000c301000026c106000000000000000000c301000014c306000000000000000000c301000011ce06000000000000000000c2010000254305000000000000000000c20100004e9b05000000000000000000c2010000d08806000000000000000000c201000005d70600000000000000000002000000f42700000000000000000000c301000006b806000000000000000000010000004a0600000000000000000000c2010000678207000000000000000000c3010000bee107000138bd5f00000000c20100008bec07000000000000000000c2010000e76607000000000000000000c30100009f4907000000000000000000c3010000c9d10700d77bc75f00000000c3010000e0d307000000000000000000c301000010e207000000000000000000c20100008d8a07000000000000000000c3010000080c08000000000000000000c3010000e4ea0e000000000000000000c301000017de07005413a15f0000000000000001000000000e043da61725000000000006000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d310000000001000000011be4f459be89000000000006000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3200000000010000000245871874b4b5000000000006000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d330000000001000000038aeaa9f6f9a9000000000006000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3400000000010000000415b1ccfb8391000100000006000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6d61696e6e65742f6e642d3500000000010000000503782dace9d9000000000006000001dde9a1f583c6591566b0cda30dd2a647126d1ce0c2687474703a2f2f6574682d6d61696e6e65742d30312e696e63756265642d6e6f64652e64653a38353030000000000100000006016345785d8a00000000000a000001d1cff39dbe511bcf14b65e4030b3d8700e0ed67cea68747470733a2f2f696e332d6e6f64652d312e6b65696c2d636f6e6e6563742e636f6d000000000100000007002386f26fc100000000000a000001d9792cc04f89b012ac1f98c19b7281fbda8a3e1a9268747470733a2f2f696e332d6e6f64652d66726f6d2e7370616365000000000100000008002386f26fc100000000000a000001d1b8cf3f02c24ca897b157a34019711679c6e556a868747470733a2f2f696e332e696f74656e61626c65722e636f6d000000000100000009002386f26fc100000000000f000001d977a4a0e80d7786dec85e3087cc1c6ac3802af9cd68747470733a2f2f696e63756265642e6f6e6c696e6500000000010000000a002386f26fc100000000000f000001d9591761898ba2dfcf3b230bd3ab7d0de0c4ef168f68747470733a2f2f696e332e696e64656e776f6c6b656e2e7465636800000000010000000b002386f26fc100000000000f000001d90cea2ff03adcfa047e8f54f98d41d9147c3ccd4d68747470733a2f2f696e332d672e6f70656e2d646e612e646500000000010000000c002386f26fc100000000000a000001d1510ee7f6f198e018e3529164da2473a96eeb3dc868747470733a2f2f303030312e6d61696e6e65742e696e332e616e79626c6f636b2e746f6f6c7300000000010000000d002386f26fc100000000000a000001d14323d321a39ed4a197524c6acd18c072f753c1b068747470733a2f2f696e332d7061726974792e696f74656e61626c65722e636f6d2f00000000010000000e002386f26fc100000000000a000001d18db016dbdc03e76990adda627aab64db8106be4b68747470733a2f2f696e332d676574682e696f74656e61626c65722e636f6d2f00000000010000000f002386f26fc100000000000a000001d119580b648bb23163136b583ff5f56657fffb661068747470733a2f2f696e332d696e667572612e696f74656e61626c65722e636f6d2f000000000100000010002386f26fc1000000000006000001d965b9464bc41dc35534f8b6ea5108f4905161efd268747470733a2f2f696e336e6f2e6465000000000100000011002386f26fc1000000000006000001d91389e3b68bbef358e4ee6fabf94d3c884deda37668747470733a2f2f6c657473696e63756265642e636f6d000000000100000012002386f26fc100000000000f000001d900a329c0648769a73afac7f9381e08fb43dbea7268747470733a2f2f696e336e6f64652e636f6d000000000100000013002386f26fc1000000000006000001d9ccd12a2222995e62eca64426989c2688d828aa4768747470733a2f2f636861696e642e64652f6574682f6d61696e6e657431000000000100000014002386f26fc100000000000a000001d16d172460c0303736c2fd4a329664fe03c2f0951268747470733a2f2f696e332e74756334372e78797a000000000100000015002386f26fc1000000000006000001d96e314c4c7b5ae5c6f49aea2d90a1d083abab045d68747470733a2f2f696e332e61686e656e706f737465722e6465000000000100000016002386f26fc100000000000a000001d1daaf752eabf0dc0e8b0ef00045efed6cfda727d668747470733a2f2f696e332e7468656d7973746572696f7573636c6f776e2e636f6d000000000100000017002386f26fc100000000000a000001d150ac4400652a4a0b29162cf7eac173bd6a67162768747470733a2f2f626974636875722e63682f696e332f6d61696e6e657400000000012813aa00000000001905cf372e0a06cef38dfa96691b336e4a1328ed9c93a769aac93db7328d1828 - -:: cache nodelist_5_0x5f51e413581dd76759e9eed51e63d14c8d1379c8 1 -065f51e413581dd76759e9eed51e63d14c8d1379c80000000000353ec000000008020000000302000000000000000000000400000077050000c57cc75f0000000079170000cfbb0b0000000000000000007a17000013f50b00c57cc75f000000007a1700003ae10b00c57cc75f00000000771700006a9222000000000000000000771700005d341300000000000000000079170000b91d1600487ec75f000000000000000100000000002386f26fc1000000000000000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d31000000000100000001002386f26fc1000000000000000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d32000000000100000002002386f26fc1000000000000000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d33000000000100000003002386f26fc1000000000000000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d34000000000100000004002386f26fc1000000000000000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f676f65726c692f6e642d35000000000100000005002386f26fc1000000001d7e0000000af944d416ebdf7f6e22eaf79a5a53ad1a487ddd9a68747470733a2f2f74696e6375626574682e6b6f6d707574696e672e6f72672f000000000100000006002386f26fc10000000021660000000a56d986deb3b5d14cb230d0f39247cc32416020b668747470733a2f2f68356c3435666b7a7a376f6333676d622e6f6e696f6e2f000000000100000007002386f26fc100000000000a000001d11821354870a09e3c4d2ed1a5c4b481e38e3d6ba168747470733a2f2f696e336e6f64652e636f6d0000000001c03e3500000000003be744c439cc9951feb98e36d776f408503c0c146a9fe37c30c142e82a634960 - -:: cache nodelist_2000_0xa93b57289070550c82edb1106e12bb37138948b8 0 - -:: cache nodelist_153_0xc2c05fbfe76ee7748ae5f5b61b57a46cc4061c32 1 -06c2c05fbfe76ee7748ae5f5b61b57a46cc4061c32000000000000000000000001170000004b930200722abd5f0000000000000001000000000000000000000000000000000000ffff1234567890123456789012345678901234567890687474703a2f2f6c6f63616c686f73743a383530300000000000 - -:: cache nodelist_246_0x039562872008f7a76674a6e7842804f0ad37cb13 1 -06039562872008f7a76674a6e7842804f0ad37cb1300000000007323630000000501000000020500000000000000000000000000000000000000000000000000001b250000e0c40d0080510100000000001b2500006ac30d0080510100000000001b25000026230e0080510100000000000000000100000000002386f26fc1000000000002000001dd45d45e6ff99e6c34a235d263965910298985fcfe68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d31000000000100000001002386f26fc1000000000002000001dd1fe2e9bf29aa1938859af64c413361227d04059a68747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d32000000000100000002002386f26fc1000000000002000001dd945f75c0408c0026a3cd204d36f5e47745182fd468747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d33000000000100000003002386f26fc1000000000002000001ddc513a534de5a9d3f413152c41b09bd8116237fc868747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d34000000000100000004002386f26fc1000000000002000001ddbcdf4e3e90cc7288b578329efd7bcc90655148d268747470733a2f2f696e332d76322e736c6f636b2e69742f6577632f6e642d3500000000016323730000000000822678d7e7ff274c3ee8c55199d2ce12c45637362c511f86fc948592d585be24 - -:: cache nodelist_17_0xf0fb87f4757c77ea3416afe87f36acaa0496c7e9 0 - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 1 -00000008 - -:: cache zksync_tokens_69f5 1 -000045544800000000001200000000000000000000000000000000000000000002007742544300000000083fad2b2e21ea1c96618cc76a42fb5a77c3f71c6f0001004441490000000000125e6d086f5ec079adff4fb3774cdf3e8d6a34f7e90003004241540000000000122c7e84980191210883d2df3167a3ab6a2cc15e010004004d4c545400000000125c55e2cf0a4243b9c7676e0ad8687c308959a15300 - -:: request http://localhost:3030 - [{"id":2,"jsonrpc":"2.0","method":"account_info","params":["0x3b98be46d348d45ddcd3343f7fdb21373e7f197c"]}] - -:: response account_info 0 http://localhost:3030 0 549 -[{ - "jsonrpc":"2.0", - "result":{ - "address":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c", - "id":8, - "depositing":{ - "balances":{ - - } - }, - "committed":{ - "balances":{ - "ETH":"999952780000000000" - }, - "nonce":2, - "pubKeyHash":"sync:9d9625572548e7fe4ba248ac60c0c7fdaca432cc" - }, - "verified":{ - "balances":{ - "ETH":"1990493664000000000" - }, - "nonce":0, - "pubKeyHash":"sync:0000000000000000000000000000000000000000" - } - }, - "id":2 -}] - - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 1 -00000008 - -:: request http://localhost:3030 - [{"id":3,"jsonrpc":"2.0","method":"get_tx_fee","params":["Withdraw","0xec8b8b4e05f647b36fa31d76273a7dc2860d0e73","ETH"]}] - -:: response get_tx_fee 0 http://localhost:3030 0 329 -[{ - "jsonrpc":"2.0", - "result":{ - "feeType":"Withdraw", - "gasTxAmount":"45000", - "gasPriceWei":"1000000000", - "gasFee":"45000000000000", - "zkpFee":"29128020447628", - "totalFee":"74100000000000" - }, - "id":3 -}] - - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 1 -00000008 - -:: request http://localhost:3030 - [{"id":4,"jsonrpc":"2.0","method":"tx_submit","params":[{"type":"Withdraw","accountId":8,"from":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c","to":"0xec8b8b4e05f647b36fa31d76273a7dc2860d0e73","token":0,"amount":90493664000000000,"fee":74100000000000,"nonce":2,"signature":{"pubKey":"33624d532da9ec053a183287a59f0af93750fa98f4c6c43f1beec1381bf03d12","signature":"f0d1f59463c754c2251c7e7c37c939e67bf51a5287bbd6fdc6c882842135c793f97a7727faf5d9a9c4a378b66a24f0c04a72ada926a31c96e81709e34c2f3101"}},{"type":"EthereumSignature","signature":"0x06be65908613dc12a1ddaffadd9d674543b0ff75395ae744714c5b787fb398da18cfc2fa057e214e2163e6bf2a6f24b17f207c123157e5db382620b4e8bf225d1c"}]}] - -:: response tx_submit 0 http://localhost:3030 0 36 -[{ - "jsonrpc":"2.0", - "result":"sync-tx:3a2eabf1f9f6f1fdba6830c4479306151dc54814535cca9451ab45f6f133512f", - "id":4 -}] - - -:: cache zksync_ac3b98be46d348d45ddcd3343f7fdb21373e7f197c 1 -00000008 - -:: result -{"type":"Withdraw","accountId":8,"from":"0x3b98be46d348d45ddcd3343f7fdb21373e7f197c","to":"0xec8b8b4e05f647b36fa31d76273a7dc2860d0e73","token":0,"amount":90493664000000000,"fee":74100000000000,"nonce":2,"txHash":"sync-tx:3a2eabf1f9f6f1fdba6830c4479306151dc54814535cca9451ab45f6f133512f"} diff --git a/c/test/unit_tests/test_abi.c b/c/test/unit_tests/test_abi.c index afccc38b6..928f67bde 100644 --- a/c/test/unit_tests/test_abi.c +++ b/c/test/unit_tests/test_abi.c @@ -211,7 +211,7 @@ static void test_abi_tuples() { "0000000000000000000000000000000000000000000000001234567890abcdef0000000000000000000000000000000000000000000000000000000000000000") } static void test_json() { - char* json_data = read_json_response_buffer("../c/test/testdata/api/abi.json"); + char* json_data = read_json_response_buffer(TESTDATA_DIR "/api/abi.json"); TEST_ASSERT_NOT_NULL_MESSAGE(json_data, "You must start this test from build-directory"); json_ctx_t* jctx = parse_json(json_data); TEST_ASSERT_NOT_NULL_MESSAGE(jctx, "Invalid json"); diff --git a/c/test/unit_tests/test_config.c b/c/test/unit_tests/test_config.c index c87b72a9e..2822946a2 100644 --- a/c/test/unit_tests/test_config.c +++ b/c/test/unit_tests/test_config.c @@ -40,6 +40,7 @@ #endif #include "../../src/api/eth1/eth_api.h" +#include "../../src/api/core/core_api.h" #include "../../src/core/client/plugin.h" #include "../../src/core/util/data.h" #include "../../src/core/util/debug.h" @@ -54,6 +55,7 @@ void test_get_config() { in3_register_default(in3_register_eth_nano); + in3_register_default(in3_register_core_api); in3_register_default(in3_register_eth_api); in3_register_default(in3_register_nodeselect_def); in3_t* c = in3_for_chain(0); diff --git a/c/test/unit_tests/test_core.c b/c/test/unit_tests/test_core.c index e543948db..d80981d4c 100644 --- a/c/test/unit_tests/test_core.c +++ b/c/test/unit_tests/test_core.c @@ -131,15 +131,15 @@ void test_str_replace() { } void test_json() { - char* data = "abc"; - json_ctx_t* json = json_create(); - json->result = json_create_array(json); - json_array_add_value(json->result, json_create_bool(json, true)); - json_object_add_prop(json->result, key("key"), json_create_null(json)); - json_array_add_value(json->result, json_create_object(json)); - json_array_add_value(json->result, json_create_bytes(json, bytes((uint8_t*) data, 3))); - json_array_add_value(json->result, json_create_string(json, data, -1)); - json_array_add_value(json->result, json_create_int(json, 10)); + char* data = "abc"; + json_ctx_t* json = json_create(); + int array = json_create_array(json); + json_array_add_value(json, array, json_create_bool(json, true)); + json_object_add_prop(json, array, key("key"), json_create_null(json)); + json_array_add_value(json, array, json->result + json_create_object(json)); + json_array_add_value(json, array, json_create_bytes(json, bytes((uint8_t*) data, 3))); + json_array_add_value(json, array, json_create_string(json, data, -1)); + json_array_add_value(json, array, json_create_int(json, 10)); char* jdata = d_create_json(json, json->result); TEST_ASSERT_EQUAL_STRING("[true,null,{},\"0x616263\",\"abc\",\"0xa\"]", jdata); free(jdata); diff --git a/c/test/unit_tests/test_request.c b/c/test/unit_tests/test_request.c index 704cbebfb..80ee42d2e 100644 --- a/c/test/unit_tests/test_request.c +++ b/c/test/unit_tests/test_request.c @@ -39,6 +39,7 @@ #define DEBUG #endif +#include "../../src/api/core/core_api.h" #include "../../src/api/eth1/eth_api.h" #include "../../src/core/client/keys.h" #include "../../src/core/client/request_internal.h" @@ -922,6 +923,7 @@ int main() { // in3_log_set_level(LOG_TRACE); in3_register_default(in3_register_eth_basic); in3_register_default(in3_register_eth_api); + in3_register_default(in3_register_core_api); in3_register_default(in3_register_nodeselect_def); TESTS_BEGIN(); diff --git a/c/test/unit_tests/test_rpc_api.c b/c/test/unit_tests/test_rpc_api.c index d9feb0372..94e1afe5b 100644 --- a/c/test/unit_tests/test_rpc_api.c +++ b/c/test/unit_tests/test_rpc_api.c @@ -39,6 +39,7 @@ #define DEBUG #endif #include "../../src/api/eth1/abi.h" +#include "../../src/api/core/core_api.h" #include "../../src/api/eth1/eth_api.h" #include "../../src/core/client/keys.h" #include "../../src/core/client/request_internal.h" @@ -318,6 +319,7 @@ static void test_in3_verified_hashes() { int main() { in3_register_default(in3_register_eth_full); in3_register_default(in3_register_eth_api); + in3_register_default(in3_register_core_api); in3_register_default(in3_register_nodeselect_def); in3_log_set_quiet(true); diff --git a/c/test/util/transport.c b/c/test/util/transport.c index c491d6fa4..9955cc55c 100644 --- a/c/test/util/transport.c +++ b/c/test/util/transport.c @@ -6,7 +6,7 @@ #include "nodeselect/full/nodelist.h" #include #include -#define MOCK_PATH "../c/test/testdata/mock/%s.json" +#define MOCK_PATH TESTDATA_DIR "/mock/%s.json" static void clean_json_str(char* s) { const char* d = s; diff --git a/java/src/CMakeLists.txt b/java/src/CMakeLists.txt index e7d16af5e..cd4579b55 100644 --- a/java/src/CMakeLists.txt +++ b/java/src/CMakeLists.txt @@ -38,6 +38,10 @@ target_link_libraries(in3_jni init) IF (NOT DEFINED ANDROID_ABI) + set(JAVA_AWT_LIBRARY NotNeeded) + set(JAVA_JVM_LIBRARY NotNeeded) + set(JAVA_AWT_INCLUDE_PATH NotNeeded) + find_package(Java REQUIRED) find_package(JNI REQUIRED) include(UseJava) @@ -83,6 +87,7 @@ IF (NOT DEFINED ANDROID_ABI) in3/utils/Account.java in3/utils/Crypto.java in3/utils/JSON.java + in3/utils/Converter.java in3/utils/Signature.java in3/utils/SignatureType.java in3/utils/Signer.java diff --git a/java/src/in3/IN3.java b/java/src/in3/IN3.java index bcfc03e7e..8f94a6ae2 100644 --- a/java/src/in3/IN3.java +++ b/java/src/in3/IN3.java @@ -70,6 +70,15 @@ private IN3(long chainAlias) { this.config = new ClientConfiguration(this.getDefaultConfig()); } + /** + * creates a client with the default config. + */ + public IN3() { + ptr = init(1); + this.config = new ClientConfiguration(this.getDefaultConfig()); + this.config.markAsSynced(); + } + /** * create a Incubed client using the chain-config. if chainId is * Chain.MULTICHAIN, the client can later be switched between different chains, diff --git a/java/src/in3/eth1/Block.java b/java/src/in3/eth1/Block.java index 877e850dc..878e445c8 100644 --- a/java/src/in3/eth1/Block.java +++ b/java/src/in3/eth1/Block.java @@ -151,8 +151,6 @@ public String[] getTransactionHashes() { /** * the transactions of the block. - * - * @throws if the Transactions are not available */ public Transaction[] getTransactions() throws Exception { Object[] tx = (Object[]) data.get("transactions"); diff --git a/java/src/in3/utils/Converter.java b/java/src/in3/utils/Converter.java new file mode 100644 index 000000000..229e57aa0 --- /dev/null +++ b/java/src/in3/utils/Converter.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * This file is part of the Incubed project. + * Sources: https://github.com/blockchainsllc/in3 + * + * Copyright (C) 2018-2020 slock.it GmbH, Blockchains LLC + * + * + * COMMERCIAL LICENSE USAGE + * + * Licensees holding a valid commercial license may use this file in accordance + * with the commercial license agreement provided with the Software or, alternatively, + * in accordance with the terms contained in a written agreement between you and + * slock.it GmbH/Blockchains LLC. For licensing terms and conditions or further + * information please contact slock.it at in3@slock.it. + * + * Alternatively, this file may be used under the AGPL license as follows: + * + * AGPL LICENSE USAGE + * + * This program is free software: you can redistribute it and/or modify it under the + * terms of the GNU Affero General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + * [Permissions of this strong copyleft license are conditioned on making available + * complete source code of licensed works and modifications, which include larger + * works using a licensed work, under the same license. Copyright and license notices + * must be preserved. Contributors provide an express grant of patent rights.] + * You should have received a copy of the GNU Affero General Public License along + * with this program. If not, see . + *******************************************************************************/ + +package in3.utils; + +/** + * a Interface for converting values. + */ +public interface Converter { + /** + * Applies this function to the given argument. + * + * @param t the function argument + * @return the function result + */ + R apply(T t); +} diff --git a/java/src/in3/utils/JSON.java b/java/src/in3/utils/JSON.java index 7d5165d61..cfc050c29 100644 --- a/java/src/in3/utils/JSON.java +++ b/java/src/in3/utils/JSON.java @@ -51,9 +51,9 @@ public class JSON { private HashMap map = new HashMap(); - private static native int key(String name); + protected static native int key(String name); - JSON() {} + public JSON() {} /** * gets the property @@ -109,6 +109,16 @@ public long getLong(String key /** the propertyName */ return asLong(get(key)); } + /** + * returns the property as double + * + * @return the long value + */ + public double getDouble(String key /** the propertyName */ + ) { + return asDouble(get(key)); + } + /** * returns the property as BigInteger * @@ -211,6 +221,20 @@ public static int asInt(Object o) { return 0; } + public static double asDouble(Object o) { + if (o == null) + return 0; + if (o instanceof Float) return ((Float) o).doubleValue(); + if (o instanceof Double) return ((Double) o).doubleValue(); + if (o instanceof String) + return (((String) o).length() > 2 && o.toString().charAt(1) == 'x') + ? JSON.asBigInteger(o).doubleValue() + : Double.parseDouble(o.toString()); + if (o instanceof Number) + return ((Number) o).doubleValue(); + return 0; + } + public static boolean asBoolean(Object o) { if (o == null) return false; @@ -266,6 +290,16 @@ public static void appendKey(StringBuilder sb, String key, Object value) { sb.append("\"").append(key).append("\":").append(toJson(value)).append(","); } + public void addProperty(StringBuilder sb, String key) { + Object o = get(key); + if (o != null) addPropertyJson(sb, key, JSON.toJson(o)); + } + + public void addPropertyJson(StringBuilder sb, String key, String json) { + if (json != null) + sb.append(sb.length() == 1 ? "" : ",").append("\"").append(key).append("\":").append(json); + } + @Override public int hashCode() { final int prime = 31; @@ -291,4 +325,34 @@ else if (!map.equals(other.map)) return false; return true; } + + public Map asMap(Converter converter) { + + final HashMap data = new HashMap(); + for (Integer k : map.keySet()) + data.put(k, converter.apply(map.get(k))); + return new HashMap() { + @Override + public T get(Object key) { + return data.get(JSON.key((String) key)); + } + + @Override + public boolean containsKey(Object key) { + return data.containsKey(JSON.key((String) key)); + } + + @Override + public T put(String key, T value) { + return data.put(JSON.key((String) key), value); + } + }; + } + + /** parses a String to a json-object. If the json represents + * - a object : JSON is returned + * - a Array : a Array is returned + * - other types the wrapped primative typed (Boolean, Integer, Long or String) will be returned. + */ + public static native Object parse(String json) throws Exception; } diff --git a/java/src/in3/zksync/API.java b/java/src/in3/zksync/API.java index efe26137a..f63cae74d 100644 --- a/java/src/in3/zksync/API.java +++ b/java/src/in3/zksync/API.java @@ -20,90 +20,90 @@ public API(IN3 in3) { * the address of the zksync contract. */ public String getContractAddress() { - return (String) in3.sendRPCasObject("zk_contract_address", new Object[] {}); + return (String) in3.sendRPCasObject("zksync_contract_address", new Object[] {}); } /** * the available tokens. */ public Token[] getTokens() { - return Token.asTokens(in3.sendRPCasObject("zk_token", new Object[] {})); + return Token.asTokens(in3.sendRPCasObject("zksync_tokens", new Object[] {})); } /** * returns the current balance, nonce and key for the given account. if address is null, the current configured Account will be used. */ public Account getAccountInfo(String address) { - return Account.asAccount(in3.sendRPCasObject("zk_account_info", address == null ? new Object[] {} : new Object[] {address})); + return Account.asAccount(in3.sendRPCasObject("zksync_account_info", address == null ? new Object[] {} : new Object[] {address})); } /** * the Transaction State. */ public Tx getTransactionInfo(String txid) { - return Tx.asTx(in3.sendRPCasObject("zk_tx_info", new Object[] {txid})); + return Tx.asTx(in3.sendRPCasObject("zksync_tx_info", new Object[] {txid})); } /** * the EthOp State. (State of a deposit or emergencyWithdraw - Transaction ) */ public EthOp getEthOpInfo(String txid) { - return EthOp.asEthOp(in3.sendRPCasObject("zk_ethop_info", new Object[] {txid})); + return EthOp.asEthOp(in3.sendRPCasObject("zksync_ethop_info", new Object[] {txid})); } /** * sets the sync keys and returns the confirmed pubkeyhash */ public String setKey(String token) { - return (String) in3.sendRPCasObject("zk_set_key", new Object[] {token}); + return (String) in3.sendRPCasObject("zksync_set_key", new Object[] {token}); } /** * returns the pubkeyhash based on the current config. */ public String getPubKeyHash() { - return (String) in3.sendRPCasObject("zk_pubkeyhash", new Object[] {}); + return (String) in3.sendRPCasObject("zksync_pubkeyhash", new Object[] {}); } /** * returns the public key based on the current config. */ public String getPubKey() { - return (String) in3.sendRPCasObject("zk_pubkey", new Object[] {}); + return (String) in3.sendRPCasObject("zksync_pubkey", new Object[] {}); } /** * returns the private key based on the current config. */ public String getSyncKey() { - return (String) in3.sendRPCasObject("zk_sync_key", new Object[] {}); + return (String) in3.sendRPCasObject("zksync_sync_key", new Object[] {}); } /** * returns the address of the account based on the current config. */ public String getAccountAddress() { - return (String) in3.sendRPCasObject("zk_account_address", new Object[] {}); + return (String) in3.sendRPCasObject("zksync_account_address", new Object[] {}); } /** * signs the data and returns a musig schnorr signature. */ public String sign(byte[] message) { - return (String) in3.sendRPCasObject("zk_sign", new Object[] {message}); + return (String) in3.sendRPCasObject("zksync_sign", new Object[] {message}); } /** * signs the data and returns a musig schnorr signature. */ public boolean verify(byte[] message, String signature) { - return (Boolean) in3.sendRPCasObject("zk_verify", new Object[] {message, signature}); + return (Boolean) in3.sendRPCasObject("zksync_verify", new Object[] {message, signature}); } /** * calculates the current tx fees for the specified */ public TxFee getTxFee(String txType, String toAddress, String token) { - return TxFee.asTxFee(in3.sendRPCasObject("zk_get_tx_fee", new Object[] {txType, toAddress, token})); + return TxFee.asTxFee(in3.sendRPCasObject("zksync_get_tx_fee", new Object[] {txType, toAddress, token})); } /** @@ -111,7 +111,7 @@ public TxFee getTxFee(String txType, String toAddress, String token) { * returns the ethopId */ public String deposit(BigInteger amount, String token, boolean approveDepositAmountForERC20, String account) { - return (String) in3.sendRPCasObject("zk_deposit", new Object[] {amount, token, approveDepositAmountForERC20, account}); + return (String) in3.sendRPCasObject("zksync_deposit", new Object[] {amount, token, approveDepositAmountForERC20, account}); } /** @@ -119,7 +119,7 @@ public String deposit(BigInteger amount, String token, boolean approveDepositAmo * returns the txid */ public String transfer(String toAddress, BigInteger amount, String token, String fromAccount) { - return (String) in3.sendRPCasObject("zk_transfer", new Object[] {toAddress, amount, token, fromAccount}); + return (String) in3.sendRPCasObject("zksync_transfer", new Object[] {toAddress, amount, token, fromAccount}); } /** @@ -127,7 +127,7 @@ public String transfer(String toAddress, BigInteger amount, String token, String * returns the txid */ public String withdraw(String toAddress, BigInteger amount, String token, String fromAccount) { - return (String) in3.sendRPCasObject("zk_withdraw", new Object[] {toAddress, amount, token, fromAccount}); + return (String) in3.sendRPCasObject("zksync_withdraw", new Object[] {toAddress, amount, token, fromAccount}); } /** @@ -135,7 +135,7 @@ public String withdraw(String toAddress, BigInteger amount, String token, String * returns the txId */ public String emergencyWithdraw(String token) { - return (String) in3.sendRPCasObject("zk_emergency_withdraw", new Object[] {token}); + return (String) in3.sendRPCasObject("zksync_emergency_withdraw", new Object[] {token}); } /** @@ -144,6 +144,6 @@ public String emergencyWithdraw(String token) { public String aggregatePubkey(String[] pubKeys) { StringBuilder s = new StringBuilder(pubKeys[0]); for (int i = 1; i < pubKeys.length; i++) s.append(pubKeys[i].substring(2)); - return (String) in3.sendRPCasObject("zk_aggregate_pubkey", new Object[] {s.toString()}); + return (String) in3.sendRPCasObject("zksync_aggregate_pubkey", new Object[] {s.toString()}); } } diff --git a/java/src/in3_jni.c b/java/src/in3_jni.c index e91614415..495f96e0b 100644 --- a/java/src/in3_jni.c +++ b/java/src/in3_jni.c @@ -64,10 +64,13 @@ typedef struct in3_storage_handler { } in3_storage_handler_t; static void* get_java_obj_ptr(in3_t* c) { + in3_log_debug(":: get_java_obj_ptr %p \n", c); + if (!c) return NULL; for (in3_plugin_t* p = c->plugins; p; p = p->next) { if (p->acts & PLGN_ACT_CACHE_GET) { in3_storage_handler_t* st = p->data; - return st->cptr; + in3_log_debug(":: found CACHE %p \n", st); + return st ? st->cptr : NULL; } } return NULL; @@ -548,13 +551,16 @@ JNIEXPORT jstring JNICALL Java_in3_eth1_SimpleWallet_decodeKeystore(JNIEnv* env, //in3_ret_t jsign(void* pk, d_signature_type_t type, bytes_t message, bytes_t account, uint8_t* dst) { in3_ret_t jsign(in3_sign_ctx_t* sc) { - in3_req_t* ctx = (in3_req_t*) sc->req; - void* jp = get_java_obj_ptr(ctx->client); - jclass cls = (*jni)->GetObjectClass(jni, jp); - jmethodID mid = (*jni)->GetMethodID(jni, cls, "getSigner", "()Lin3/utils/Signer;"); - jobject signer = (*jni)->CallObjectMethod(jni, jp, mid); + in3_req_t* ctx = (in3_req_t*) sc->req; + if (ctx == NULL) return IN3_EIGNORE; + void* jp = get_java_obj_ptr(ctx->client); + in3_log_debug(":: jsign for %p === %p\n", ctx->client, jp); + if (jp == NULL) return IN3_EIGNORE; + jclass cls = (*jni)->GetObjectClass(jni, jp); + jmethodID mid = (*jni)->GetMethodID(jni, cls, "getSigner", "()Lin3/utils/Signer;"); + jobject signer = (*jni)->CallObjectMethod(jni, jp, mid); - if (!signer) return -1; + if (!signer) return IN3_EIGNORE; char *data = alloca(sc->message.len * 2 + 3), address[43]; data[0] = address[0] = '0'; @@ -568,7 +574,7 @@ in3_ret_t jsign(in3_sign_ctx_t* sc) { mid = (*jni)->GetMethodID(jni, cls, "sign", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); jstring jsignature = (*jni)->CallObjectMethod(jni, signer, mid, jdata, jaddress); - if (!jsignature) return -2; + if (!jsignature) return IN3_EIGNORE; const char* signature = (*jni)->GetStringUTFChars(jni, jsignature, 0); int l = (strlen(signature) + 1) / 2; if (l && signature[0] == '0' && signature[1] == 'x') l--; @@ -639,13 +645,14 @@ JNIEXPORT jlong JNICALL Java_in3_IN3_init(JNIEnv* env, jobject ob, jlong jchain) in3_init(); in3_t* in3 = in3_for_chain(jchain); void* p = (*env)->NewGlobalRef(env, ob); + // in3_log_set_level(LOG_TRACE); + // in3_log_set_quiet(false); + in3_log_debug("New Global ref for %p === %p\n", ob, p); in3_set_storage_handler(in3, storage_get_item, storage_set_item, storage_clear, p); in3_plugin_register(in3, PLGN_ACT_TRANSPORT, Java_in3_IN3_transport, NULL, true); in3_plugin_register(in3, PLGN_ACT_SIGN, jsign_fn, p, false); jni = env; // turn to debug - // in3_log_set_level(LOG_TRACE); - // in3_log_set_quiet(false); return (jlong)(size_t) in3; } @@ -665,3 +672,28 @@ JNIEXPORT void JNICALL Java_in3_Loader_libInit(JNIEnv* env, jclass c) { UNUSED_VAR(c); in3_init(); } + +/* + * Class: in3_utils_JSON + * Method: parse + * Signature: (Ljava/lang/String;)Lin3/JSON; + */ +JNIEXPORT jobject JNICALL Java_in3_utils_JSON_parse(JNIEnv* env, jclass cl, jstring jdata) { + UNUSED_VAR(cl); + jobject ob = NULL; + const char* data = (*env)->GetStringUTFChars(env, jdata, 0); + json_ctx_t* ctx = parse_json(data); + (*env)->ReleaseStringUTFChars(env, jdata, data); + if (ctx == NULL) { + char* error = _malloc(strlen(data) + 50); + sprintf(error, "Error parsing the json-data : '%s'", data); + (*env)->ThrowNew(env, (*env)->FindClass(env, "java/lang/RuntimeException"), error); + _free(error); + } + else { + ob = toObject(env, ctx->result); + json_free(ctx); + } + + return ob; +} diff --git a/java/src/in3_jni.h b/java/src/in3_jni.h index ca584c53c..e7f84561d 100644 --- a/java/src/in3_jni.h +++ b/java/src/in3_jni.h @@ -171,6 +171,13 @@ JNIEXPORT jstring JNICALL Java_in3_eth1_TransactionRequest_abiEncode(JNIEnv*, jc */ JNIEXPORT jobject JNICALL Java_in3_eth1_TransactionRequest_abiDecode(JNIEnv*, jclass, jstring, jstring); +/* + * Class: in3_utils_JSON + * Method: parse + * Signature: (Ljava/lang/String;)Lin3/JSON; + */ +JNIEXPORT jobject JNICALL Java_in3_utils_JSON_parse(JNIEnv*, jclass, jstring); + /* * Class: in3_eth1_SimpleWallet * Method: getAddressFromKey diff --git a/python/ci.yml b/python/ci.yml index 05bdacbc2..f7edfa699 100644 --- a/python/ci.yml +++ b/python/ci.yml @@ -113,6 +113,7 @@ python_macos: before_script: - *cache_scipt_before script: + - rm -rf ~/.in3 || echo "could not delete cache" - cd python_multilib - pip3 install --upgrade pip - pip3 install -r requirements.txt diff --git a/python/tests/integrated/negative/client_test.py b/python/tests/integrated/negative/client_test.py index 9d4fa096a..7d07fdb07 100644 --- a/python/tests/integrated/negative/client_test.py +++ b/python/tests/integrated/negative/client_test.py @@ -40,11 +40,6 @@ def test_ens_address(self): with self.assertRaises(in3.ClientException): self.client.ens_owner('deprazz.eth') - def test_ens_namehash(self): - for i in range(50): - with self.assertRaises(in3.ClientException): - self.client.ens_namehash('0x0.eth') - class ClientParsingTest(unittest.TestCase): diff --git a/rust/ci.yml b/rust/ci.yml index c28251e6e..9ba279a16 100644 --- a/rust/ci.yml +++ b/rust/ci.yml @@ -34,7 +34,7 @@ - export RUST_TEST_THREADS=1 - export OPENSSL_DIR=/usr/lib/ssl - mkdir -p rust/in3-sys/in3-core/c - - cp -r c/CMakeLists.txt c/macro.cmake c/compiler.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ + - cp -r c/CMakeLists.txt c/*.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ - cp CMakeLists.txt rust/in3-sys/in3-core/ - cd rust/ - export UPDATE_IN3_BINDINGS=1 @@ -83,7 +83,7 @@ rust_valgrind: script: - export RUST_BACKTRACE=1 - mkdir -p rust/in3-sys/in3-core/c - - cp -r c/CMakeLists.txt c/macro.cmake c/compiler.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ + - cp -r c/CMakeLists.txt c/*.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ - cp CMakeLists.txt rust/in3-sys/in3-core/ - export UPDATE_IN3_BINDINGS=1 - cd rust @@ -94,8 +94,8 @@ rust_valgrind: - export CARGO_MANIFEST_DIR=. - export IN3_MODE="DEBUG" - printf "{\n ignore_libcrypto_conditional_jump_errors\n Memcheck:Leak\n ...\n obj:*/libcrypto.so.*\n}\n" > suppress.valgrind - - export IN3_EXEC="`basename target/debug/in3-*`" - - valgrind $VALGRIND_OPTS target/debug/$IN3_EXEC test_eth_api + - export IN3_EXEC="`ls target/debug/deps/in3-* | grep -v "\.d" | xargs basename`" + - valgrind $VALGRIND_OPTS target/debug/deps/$IN3_EXEC test_eth_api tags: - long-jobs @@ -105,7 +105,7 @@ rust: script: - export RUST_BACKTRACE=1 - mkdir -p rust/in3-sys/in3-core/c - - cp -r c/CMakeLists.txt c/macro.cmake c/compiler.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ + - cp -r c/CMakeLists.txt c/*.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ - cp CMakeLists.txt rust/in3-sys/in3-core/ - cd rust/ - export UPDATE_IN3_BINDINGS=1 @@ -129,14 +129,14 @@ deploy_rust: - cargo login $CARGO_TOKEN script: - mkdir -p rust/in3-sys/in3-core/c - - cp -r c/CMakeLists.txt c/macro.cmake c/compiler.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ + - cp -r c/CMakeLists.txt c/*.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ - cp CMakeLists.txt rust/in3-sys/in3-core/ - find rust -name 'Cargo.toml' -exec sed -i '' -e "s/version = \"0.0.0\"/version = \""${CI_COMMIT_TAG:1}"\"/g" {} \; - cd rust/ - rm -rf in3-sys/in3-core/c/src/third-party/hidapi/ - - rm in3-sys/.gitignore + - echo "" > in3-sys/.gitignore - cd in3-sys/ && cargo publish --allow-dirty - sleep 30 - - cd ../in3-rs/ && cargo publish --allow-dirty + - cd ../in3-rs/ && cargo publish --allow-dirty || echo "Not updating in3-rs, because no new version." tags: - mac-os diff --git a/scripts/_in3.sh b/scripts/_in3.sh index 6b4cd6f65..e41437e03 100755 --- a/scripts/_in3.sh +++ b/scripts/_in3.sh @@ -2,36 +2,6 @@ local -a subcmds args sig_in3 sig_erc20 sig_ms subcmds=( - 'eth_blockNumber:last block number' - 'eth_getBlockByNumber: get block data ' - 'eth_getTransactionByHash: get the transaction ' - 'eth_getTransactionByBlockHashAndIndex: get the transaction ' - 'eth_getTransactionByBlockNumberAndIndex: get the transaction ' - 'eth_getTransactionReceipt: get the transaction receipt ' - 'eth_getLogs: get the events ' - 'eth_getBlockByHash: get block data ' - 'eth_getCode: get code of a contract
' - 'eth_getBalance: get balance of address
' - 'web3_clientVersion: returns the client version' - 'web3_sha3: hashes the gives data ' - 'net_version: eth version' - 'eth_protocolVersion: RPC-Spec Version' - 'eth_gasPrice: average gas price used in the last blocks' - 'eth_getStorageAt: storage value of an contract
' - 'eth_getTransactionCount: nonce of the account
' - 'eth_getBlockTransactionCountByHash: Number of Transaction in the Block ' - 'eth_getBlockTransactionCountByNumber: Number of Transaction in the Block ' - 'eth_sendTransaction: sends a transaction ' - 'eth_sendRawTransaction: sends a signed transaction ' - 'eth_call: calls a function of a contract ' - 'eth_estimateGas: estimates the gas for a call a contract ' - 'in3_nodeList: shows the nodeList' - 'in3_weights: shows the weights of nodeList' - 'in3_cacheClear: clears the cache' - 'in3_sign: requests a node to sign. ' - 'in3_ens: resolve ens-name. ' - 'ipfs_get: requests and verifies the content for a given ipfs-hash and write the content to stdout ' - 'ipfs_put: reads data from stdin and pushes to the ipfs-network. it write the ipfs-hash to stdout.' 'send: sends a transaction ...args' 'call: calls a contract ...args' 'abi_encode: encodes the arguments as described in the method signature using ABI-Encoding. ...args' @@ -43,42 +13,236 @@ subcmds=( 'createkey: generates a random key' 'in3_checksumAddress: display the address as checksumAddress' 'keccak: calculate the keccak hash of the ' - 'in3_toWei: returns the amount of wei of the value passed as float of eth.' - 'in3_signData: signs the as passed as argument with the configured signer. optional ' - 'in3_cacheClear: clears the local filecache' - 'in3_signTx: signs a raw transaction ' - 'zk_deposit: sends a L1-Transaction to add to the deposit ' - 'zk_transfer: transfers the given amount from the configured account to another account in L2 ' - 'zk_withdraw: transfers the given amount from the configured account in L2 to another account in L1 ' - 'zk_set_key: sets the configured key as signer for the account args: ' - 'zk_emergency_withdraw: sends a L1 Tx to withdraw all tokens from L2 ' - 'zk_sync_key: calculates and returns the pk used to sign zksync-tx (using babyjupjup curve)' - 'zk_aggregate_pubkey: calculates the combined musig public key for the concatinated public keys (each with 32 bytes)' - 'zk_pubkeyhash: returns the pubkeyhash of the signer based on the config' - 'zk_pubkey: returns the compact pubkey(32 bytes) of the signer based on the config or from the , if given' - 'zk_account_address: returns address of the zksync-account based on the config' - 'zk_account_info: returns the current balance and state of the account' - 'zk_tokens: returns a list of all available tokens' - 'zk_sign: sign the given with the syncKey. If the -zms and -zmu are passed, it will use musig schnirr sigs to create MPC-Signature' - 'zk_verify: verifies the signature of the ' - 'iamo_add_user: adds a new user to the vault-api' - 'iamo_zk_add_wallet: add a json-definition of a mutisig.' - 'iamo_zk_create_wallet: creates a multisig-definition based on the Approver-API (-zmu) ....' - 'iamo_zk_get_config: returns the configured master copy, codehash and creator for creating create2-accounts' - 'iamo_zk_create_signatures: creates a array of signatures to reach the threshold of a multisig based on and ' - 'iamo_zk_verify_signatures: verfies signatures to reach the threshold of a multisig based on and ' + 'eth_accounts: returns a array of account-addresss the incubed client is able to sign with' + 'eth_sign: The sign method calculates an Ethereum specific signature with: ' + 'eth_signTransaction: Signs a transaction that can be submitted to the network at a later time using with eth_sendRawTrans... ' + 'in3_addRawKey: adds a raw private key as signer, which allows signing transactions ' + 'in3_createKey: Generates 32 random bytes ' + 'in3_decryptKey: decrypts a JSON Keystore file as defined in the ' + 'in3_ecrecover: extracts the public key and address from signature ' + 'in3_pk2address: extracts the address from a private key ' + 'in3_pk2public: extracts the public key from a private key ' + 'in3_prepareTx: prepares a Transaction by filling the unspecified values and returens the unsigned raw Transaction ' + 'in3_signData: signs the given data ' + 'in3_signTx: signs the given raw Tx (as prepared by in3_prepareTx ) ' + 'btc_proofTarget: Whenever the client is not able to trust the changes of the target (which is the case if a block can... ' + 'getbestblockhash: Returns the hash of the best (tip) block in the longest blockchain' + 'getblock: Returns data of block for given block hash ' + 'getblockcount: Returns the number of blocks in the longest blockchain' + 'getblockheader: Returns data of block header for given block hash ' + 'getdifficulty: Returns the proof-of-work difficulty as a multiple of the minimum difficulty ' + 'getrawtransaction: Returns the raw transaction data ' + 'in3_config: changes the configuration of a client ' + 'eth_blockNumber: returns the number of the most recent block' + 'eth_call: calls a function of a contract (or simply executes the evm opcodes) and returns the result ' + 'eth_estimateGas: calculates the gas needed to execute a transaction ' + 'eth_getBalance: gets the balance of an account for a given block ' + 'eth_getBlockByHash: Returns information about a block by hash ' + 'eth_getBlockByNumber: returns information about a block by block number ' + 'eth_getBlockTransactionCountByHash: returns the number of transactions ' + 'eth_getBlockTransactionCountByNumber: returns the number of transactions ' + 'eth_getCode: gets the code of a given contract ' + 'eth_getLogs: searches for events matching the given criteria ' + 'eth_getStorageAt: gets the storage value of a given key ' + 'eth_getTransactionByBlockHashAndIndex: returns the transaction data ' + 'eth_getTransactionByBlockNumberAndIndex: returns the transaction data ' + 'eth_getTransactionByHash: returns the transaction data ' + 'eth_getTransactionCount: gets the nonce or number of transaction sent from this account at a given block ' + 'eth_getTransactionReceipt: The Receipt of a Transaction ' + 'eth_getUncleCountByBlockHash: returns the number of uncles ' + 'eth_getUncleCountByBlockNumber: returns the number of uncles ' + 'eth_sendRawTransaction: sends or broadcasts a prviously signed raw transaction ' + 'eth_sendTransaction: signs and sends a Transaction ' + 'eth_sendTransactionAndWait: signs and sends a Transaction, but then waits until the transaction receipt can be verified ' + 'ipfs_get: Fetches the data for a requested ipfs-hash ' + 'ipfs_put: Stores ipfs-content to the ipfs network ' + 'in3_nodeList: fetches and verifies the nodeList from a node ' + 'in3_sign: requests a signed blockhash from the node ' + 'in3_whitelist: Returns whitelisted in3-nodes addresses
' + 'in3_abiDecode: based on the ' + 'in3_abiEncode: based on the ' + 'in3_cacheClear: clears the incubed cache (usually found in the ' + 'in3_calcDeployAddress: calculates the address of a contract about to deploy ' + 'in3_checksumAddress: Will convert an upper or lowercase Ethereum address to a checksum address
' + 'in3_fromWei: converts a given uint (also as hex) with a wei-value into a specified unit ' + 'in3_toWei: converts the given value into wei ' + 'keccak: Returns Keccak-256 (not the standardized SHA3-256) of the given data' + 'net_version: Returns the current network id' + 'sha256: Returns sha-256 of the given data ' + 'web3_clientVersion: Returns the underlying client version' + 'web3_sha3: Returns Keccak-256 (not the standardized SHA3-256) of the given data ' + 'zksync_account_address: returns the address of the account used' + 'zksync_account_history: returns the history of transaction for a given account ' + 'zksync_account_info: returns account_info from the server
' + 'zksync_aggregate_pubkey: calculate the public key based on multiple public keys signing together using schnorr musig signatur... ' + 'zksync_contract_address: returns the contract address' + 'zksync_deposit: sends a deposit-transaction and returns the opId, which can be used to tradck progress ' + 'zksync_emergency_withdraw: withdraws all tokens for the specified token as a onchain-transaction ' + 'zksync_ethop_info: returns the state or receipt of the the PriorityOperation ' + 'zksync_get_token_price: returns current token-price ' + 'zksync_get_tx_fee: calculates the fees for a transaction
' + 'zksync_pubkey: returns the current packed PubKey based on the config set' + 'zksync_pubkeyhash: returns the current PubKeyHash based on the configuration set ' + 'zksync_set_key: sets the signerkey based on the current pk or as configured in the config ' + 'zksync_sign: returns the schnorr musig signature based on the current config ' + 'zksync_sync_key: returns private key used for signing zksync-transactions' + 'zksync_tokens: returns the list of all available tokens' + 'zksync_transfer: sends a zksync-transaction and returns data including the transactionHash ' + 'zksync_tx_data: returns the full input data of a transaction ' + 'zksync_tx_info: returns the state or receipt of the the zksync-tx ' + 'zksync_verify: returns 0 or 1 depending on the successfull verification of the signature ' + 'zksync_withdraw: withdraws the amount to the given `ethAddress` for the given token ' ) args=( - '-c[chain]:chain id:(mainnet goerli ewc ipfs btc local)' +'--chainId=[the chainId or the name of a known chain]:chainId:(mainnet goerli ewc btc ipfs local)' +'-c[the chainId or the name of a known chain]:chainId:(mainnet goerli ewc btc ipfs local)' +'--finality=[the number in percent needed in order reach finality (% of signature of the validators)]:finality:()' +'-f[the number in percent needed in order reach finality (% of signature of the validators)]:finality:()' +'--includeCode[if true, the request should include the codes of all accounts]' +'--debug[if true, debug messages will be written to stderr]' +'--maxAttempts=[max number of attempts in case a response is rejected]:maxAttempts:()' +'-a[max number of attempts in case a response is rejected]:maxAttempts:()' +'--keepIn3[if true, requests sent to the input sream of the comandline util will be send theor responses in the...]' +'-kin3[if true, requests sent to the input sream of the comandline util will be send theor responses in the...]' +'--stats[if true, requests sent will be used for stats]' +'--useBinary[if true the client will use binary format]' +'--experimental[if true the client allows to use use experimental features, otherwise a exception is thrown if those...]' +'-x[if true the client allows to use use experimental features, otherwise a exception is thrown if those...]' +'--timeout=[specifies the number of milliseconds before the request times out]:timeout:()' +'--proof=[if true the nodes should send a proof of the response]:proof:(none standard full)' +'-p[if true the nodes should send a proof of the response]:proof:(none standard full)' +'--replaceLatestBlock=[if specified, the blocknumber *latest* will be replaced by blockNumber- specified value]:replaceLatestBlock:()' +'-l[if specified, the blocknumber *latest* will be replaced by blockNumber- specified value]:replaceLatestBlock:()' +'--autoUpdateList[if true the nodelist will be automaticly updated if the lastBlock is newer]' +'--signatureCount=[number of signatures requested in order to verify the blockhash]:signatureCount:()' +'-s[number of signatures requested in order to verify the blockhash]:signatureCount:()' +'--bootWeights[if true, the first request (updating the nodelist) will also fetch the current health status and use...]' +'-bw[if true, the first request (updating the nodelist) will also fetch the current health status and use...]' +'--useHttp[if true the client will try to use http instead of https]' +'--minDeposit=[min stake of the server]:minDeposit:()' +'--nodeProps=[used to identify the capabilities of the node]:nodeProps:()' +'--requestCount=[the number of request send in parallel when getting an answer]:requestCount:()' +'-rc[the number of request send in parallel when getting an answer]:requestCount:()' +'--rpc=[url of one or more direct rpc-endpoints to use]:rpc:()' +'--nodes.contract=[address of the registry contract]:contract:()' +'--nodes.whiteListContract=[address of the whiteList contract]:whiteListContract:()' +'--nodes.whiteList=[manual whitelist]:whiteList:()' +'--nodes.registryId=[identifier of the registry]:registryId:()' +'--nodes.needsUpdate[if set, the nodeList will be updated before next request]' +'--nodes.avgBlockTime=[average block time (seconds) for this chain]:avgBlockTime:()' +'--nodes.verifiedHashes.block=[block number]:block:()' +'--nodes.verifiedHashes.hash=[verified hash corresponding to block number]:hash:()' +'--nodes.nodeList.url=[URL of the node]:url:()' +'--nodes.nodeList.address=[address of the node]:address:()' +'--nodes.nodeList.props=[used to identify the capabilities of the node (defaults to 0xFFFF)]:props:()' +'--zksync.provider_url=[url of the zksync-server (if not defined it will be choosen depending on the chain)]:provider_url:()' +'-zks[url of the zksync-server (if not defined it will be choosen depending on the chain)]:provider_url:()' +'--zksync.rest_api=[url of the zksync rest api (if not defined it will be choosen depending on the chain)]:rest_api:()' +'-zkr[url of the zksync rest api (if not defined it will be choosen depending on the chain)]:rest_api:()' +'--zksync.account=[the account to be used]:account:()' +'-zka[the account to be used]:account:()' +'--zksync.sync_key=[the seed used to generate the sync_key]:sync_key:()' +'-zsk[the seed used to generate the sync_key]:sync_key:()' +'--zksync.main_contract=[address of the main contract- If not specified it will be taken from the server]:main_contract:()' +'--zksync.signer_type=[type of the account]:signer_type:(pk contract create2)' +'-zkat[type of the account]:signer_type:(pk contract create2)' +'--zksync.musig_pub_keys=[concatenated packed public keys (32byte) of the musig signers]:musig_pub_keys:()' +'-zms[concatenated packed public keys (32byte) of the musig signers]:musig_pub_keys:()' +'--zksync.musig_urls=[a array of strings with urls based on the `musig_pub_keys`]:musig_urls:()' +'-zmu[a array of strings with urls based on the `musig_pub_keys`]:musig_urls:()' +'--zksync.create2.creator=[The address of contract or EOA deploying the contract ( for example the GnosisSafeFactory )]:creator:()' +'--zksync.create2.saltarg=[a salt-argument, which will be added to the pubkeyhash and create the create2-salt]:saltarg:()' +'--zksync.create2.codehash=[the hash of the actual deploy-tx including the constructor-arguments]:codehash:()' +'--zksync.verify_proof_method=[rpc-method, which will be used to verify the incomming proof before cosigning]:verify_proof_method:()' +'-zvpm[rpc-method, which will be used to verify the incomming proof before cosigning]:verify_proof_method:()' +'--zksync.create_proof_method=[rpc-method, which will be used to create the proof needed for cosigning]:create_proof_method:()' +'-zcpm[rpc-method, which will be used to create the proof needed for cosigning]:create_proof_method:()' +'--key=[the client key to sign requests]:key:()' +'-k[the client key to sign requests]:key:()' +'--pk=[registers raw private keys as signers for transactions]:pk:()' +'-pk[registers raw private keys as signers for transactions]:pk:()' +'--btc.maxDAP=[max number of DAPs (Difficulty Adjustment Periods) allowed when accepting new targets]:maxDAP:()' +'--btc.maxDiff=[max increase (in percent) of the difference between targets when accepting new targets]:maxDiff:()' +'--clearCache[clears the cache before performing any operation]' +'-ccache[clears the cache before performing any operation]' +'--eth[converts the result (as wei) to ether]' +'-e[converts the result (as wei) to ether]' +'--port=[if specified it will run as http-server listening to the given port]:port:()' +'-port[if specified it will run as http-server listening to the given port]:port:()' +'--allowed-methods=[only works if port is specified and declares a comma-seperated list of rpc-methods which are allowed]:allowed-methods:()' +'-am[only works if port is specified and declares a comma-seperated list of rpc-methods which are allowed]:allowed-methods:()' +'--block=[the blocknumber to use when making calls]:block:()' +'-b[the blocknumber to use when making calls]:block:()' +'--to=[the target address of the call]:to:()' +'-to[the target address of the call]:to:()' +'--from=[the sender of a call or tx (only needed if no signer is registered)]:from:()' +'-from[the sender of a call or tx (only needed if no signer is registered)]:from:()' +'--data=[the data for a transaction]:data:()' +'-d[the data for a transaction]:data:()' +'--gas_price=[the gas price to use when sending transactions]:gas_price:()' +'-gp[the gas price to use when sending transactions]:gas_price:()' +'--gas=[the gas limit to use when sending transactions]:gas:()' +'-gas[the gas limit to use when sending transactions]:gas:()' +'--nonce=[the nonce]:nonce:()' +'-nonce[the nonce]:nonce:()' +'--test=[creates a new json-test written to stdout with the name as specified]:test:()' +'-test[creates a new json-test written to stdout with the name as specified]:test:()' +'--path=[the HD wallet derivation path ]:path:()' +'-path[the HD wallet derivation path ]:path:()' +'--sigtype=[the type of the signature data]:sigtype:(raw hash eth_sign)' +'-st[the type of the signature data]:sigtype:(raw hash eth_sign)' +'--password=[password to unlock the key]:password:()' +'-pwd[password to unlock the key]:password:()' +'--value=[the value to send when sending a transaction]:value:()' +'-value[the value to send when sending a transaction]:value:()' +'--wait[if given, instead returning the transaction, it will wait until the transaction is mined and return ...]' +'-w[if given, instead returning the transaction, it will wait until the transaction is mined and return ...]' +'--json[if given the result will be returned as json, which is especially important for eth_call results wit...]' +'-json[if given the result will be returned as json, which is especially important for eth_call results wit...]' +'--hex[if given the result will be returned as hex]' +'-hex[if given the result will be returned as hex]' +'--debug[if given incubed will output debug information when executing]' +'-debug[if given incubed will output debug information when executing]' +'--quiet[quiet]' +'-q[quiet]' +'--human[human readable, which removes the json -structure and oly displays the values]' +'-h[human readable, which removes the json -structure and oly displays the values]' +'--test-request[runs test request when showing in3_weights]' +'-tr[runs test request when showing in3_weights]' +'--test-health-request[runs test request including health-check when showing in3_weights]' +'-thr[runs test request including health-check when showing in3_weights]' +'--multisig=[adds a multisig as signer this needs to be done in the right order! (first the pk then the multisaig...]:multisig:()' +'-ms[adds a multisig as signer this needs to be done in the right order! (first the pk then the multisaig...]:multisig:()' +'--ms.signatures=[add additional signatures, which will be useds when sending through a multisig!]:ms.signatures:()' +'-sigs[add additional signatures, which will be useds when sending through a multisig!]:ms.signatures:()' +'--response.in[read response from stdin]' +'-ri[read response from stdin]' +'--response.out[write raw response to stdout]' +'-ro[write raw response to stdout]' +'--file.in=[reads a prerecorded request from the filepath and executes it with the recorded data]:file.in:()' +'-fi[reads a prerecorded request from the filepath and executes it with the recorded data]:file.in:()' +'--file.out=[records a request and writes the reproducable data in a file (including all cache-data, timestamps ]:file.out:()' +'-fo[records a request and writes the reproducable data in a file (including all cache-data, timestamps ]:file.out:()' +'--nodelist=[a coma seperated list of urls (or address:url) to be used as fixed nodelist]:nodelist:()' +'-nl[a coma seperated list of urls (or address:url) to be used as fixed nodelist]:nodelist:()' +'--bootnodes=[a coma seperated list of urls (or address:url) to be used as boot nodes]:bootnodes:()' +'-bn[a coma seperated list of urls (or address:url) to be used as boot nodes]:bootnodes:()' +'--onlysign[only sign, do not send the raw Transaction]' +'-os[only sign, do not send the raw Transaction]' +'--noproof[alias for --proof=none]' +'-np[alias for --proof=none]' +'--nostats[alias for --stats=false, which will mark all requests as not counting in the stats]' +'-ns[alias for --stats=false, which will mark all requests as not counting in the stats]' +'--version[displays the version]' +'-v[displays the version]' +'--help[displays this help message]' +'-h[displays this help message]' '-st[the type of the signature data]:st:(eth_sign raw hash)' - '-p[the Verification level]:p:(none standard full)' '-pwd[password to unlock the key]:pwd:()' '-np[short for -p none]' '-ns[short for no stats, which does count this request in the public stats]' '-eth[onverts the result (as wei) to ether]' - '-l[replaces "latest" with latest BlockNumber - the number of blocks given]:latest:(1 2 3 4 5 6 7 8 9 10)' - '-s[number of signatures to use when verifying]:sigs:(1 2 3 4 5 6 7 8 9 10)' '-port[if specified it will run as http-server listening to the given port]:port:(8545)' '-am[Allowed Methods when used with -port as comma seperated list of methods]:allowed_methods:()' '-b[the blocknumber to use when making calls]:b:(latest earliest 0x)' @@ -87,7 +251,6 @@ args=( '-gas[the gas limit to use when sending transactions]:gas:(21000 100000 250000 500000 1000000 2000000)' '-gas_price[the gas price to use within a tx]:gas_price:()' '-pk[the private key as raw or path to the keystorefile]:pk:()' - '-k[the private key to sign request for incetives payments]:k:()' '-help[displays this help message]' '-tr[runs test request when showing in3_weights]' '-thr[runs test request including health-check when showing in3_weights]' @@ -104,25 +267,10 @@ args=( '-os[only sign, do not send the raw Transaction]' '-ri[read response from stdin]' '-ro[write raw response to stdout]' - '-a[max number of attempts before giving up (default 5)]:attempts:(1 2 3 4 5 6 7 8 9)' - '-rc[number of request per try (default 1)]:requestCount:(1 2 3 4 5 6 7 8 9)' '-fi[read recorded request from file]:fi:()' '-fo[recorded request and write to file]:fo:()' '-nl[a comma seperated list of urls as address:url to be used as fixed nodelist]:nl:()' '-bn[a comma seperated list of urls as address:url to be used as boot nodes]:bn:()' - '-zks[URL of the zksync-server]:zks:(https\://api.zksync.io/jsrpc http\://localhost\:3030)' - '-zkss[zksync signatures to pass along when signing]:zkss:()' - '-zka[zksync account to use]:zka:()' - '-zkat[zksync account type]:zkat:(pk contract create2)' - '-zsk[zksync signer seed - if not set this key will be derrived from account unless create2]:zsk:()' - '-zc2[zksync create2 arguments in the form ::. if set the account type is also changed to create2]:zc2:()' - '-zms[public keys of a musig schnorr signatures to sign with]:zms:()' - '-zmu[url for signing service matching the first remote public key]:zmu:(http\://localhost\:8080)' - '-zvpm[method for calling to verify the proof in server mode]:zvpm:(iamo_zk_verify_signatures)' - '-zcpm[method for calling to create the proof]:zcpm:(iamo_zk_create_signatures)' - '-idk[iamo device key]:idk:()' - '-imc[the master copy address to be used]:imc:()' - '-if[iamo factory address]:if:()' ':method:{_describe command subcmds}' ':arg1:{_describe command sig_in3 -- sig_erc20 -- sig_ms}' diff --git a/scripts/_in3.template b/scripts/_in3.template new file mode 100755 index 000000000..efdb7d9ba --- /dev/null +++ b/scripts/_in3.template @@ -0,0 +1,97 @@ +#compdef in3 + +local -a subcmds args sig_in3 sig_erc20 sig_ms +subcmds=( + 'send: sends a transaction ...args' + 'call: calls a contract ...args' + 'abi_encode: encodes the arguments as described in the method signature using ABI-Encoding. ...args' + 'abi_decode: decodes the data based on the signature.. ' + 'pk2address: extracts the public address from a private key ' + 'pk2public: extracts the public key from a private key ' + 'ecrecover: extracts the address and public key from a signature ' + 'key: reads the private key from JSON-Keystore file and returns the private key. ' + 'createkey: generates a random key' + 'in3_checksumAddress: display the address as checksumAddress' + 'keccak: calculate the keccak hash of the ' +$CMDS + ) + +args=( +$CONFS + '-st[the type of the signature data]:st:(eth_sign raw hash)' + '-pwd[password to unlock the key]:pwd:()' + '-np[short for -p none]' + '-ns[short for no stats, which does count this request in the public stats]' + '-eth[onverts the result (as wei) to ether]' + '-port[if specified it will run as http-server listening to the given port]:port:(8545)' + '-am[Allowed Methods when used with -port as comma seperated list of methods]:allowed_methods:()' + '-b[the blocknumber to use when making calls]:b:(latest earliest 0x)' + '-to[the target address of the call or send]:to:(0x)' + '-d[the data for a transaction. This can be a filepath, a 0x-hexvalue or - for stdin]:date:()' + '-gas[the gas limit to use when sending transactions]:gas:(21000 100000 250000 500000 1000000 2000000)' + '-gas_price[the gas price to use within a tx]:gas_price:()' + '-pk[the private key as raw or path to the keystorefile]:pk:()' + '-help[displays this help message]' + '-tr[runs test request when showing in3_weights]' + '-thr[runs test request including health-check when showing in3_weights]' + '-ms[address of a imao multisig to redirect all tx through]:ms:()' + '-version[displays the version]' + '-debug[if given incubed will output debug information when executing]' + '-value[the value to send when sending a transaction. (hexvalue or float/integer with the suffix eth]:value:(1.0eth)' + '-w[instead returning the transaction, it will wait until the transaction is mined and return the transactionreceipt]' + '-md[specifies the minimum Deposit of a node in order to be selected as a signer]' + '-json[the result will be returned as json]' + '-hex[the result will be returned as hex]' + '-kin3[the response including in3-section is returned]' + '-q[quiet no debug output]' + '-os[only sign, do not send the raw Transaction]' + '-ri[read response from stdin]' + '-ro[write raw response to stdout]' + '-fi[read recorded request from file]:fi:()' + '-fo[recorded request and write to file]:fo:()' + '-nl[a comma seperated list of urls as address:url to be used as fixed nodelist]:nl:()' + '-bn[a comma seperated list of urls as address:url to be used as boot nodes]:bn:()' + + ':method:{_describe command subcmds}' + ':arg1:{_describe command sig_in3 -- sig_erc20 -- sig_ms}' +) + +sig_in3=( + 'minDeposi()\:uint: minimal deposit' + 'adminKey()\:address: admin key' + 'nodeRegistryData()\:address:addres of the data contract' + 'supportedToken()\:address: supported token' + 'totalNodes()\:uint: number of nodes' + 'blockRegistry()\:address: BlockHashRegistry' + 'nodes(uint256)\:(string,uint256,uint64,uint192,uint64,address,bytes32): node data' + 'unregisteringNode(address):unregister a node' + 'updateNode(address,string,uint192,uint64,uint256): update nod properties' + 'transferOwnership(address,address): transfers ownership from signer to new owner', + 'registerNode(string,uint192,uint64,uint256): registers a Node' + 'snapshot(): creates a snapshot for the current block' +) +sig_erc20=( + 'balanceOf(address)\:uint:getting the balance of' + 'name()\:string:token name' + 'totalSupply()\:uint:total Balance' + 'transfer(address,uint256):transfer tokens' + 'transferFrom(address,address,uint256):transfer from to account tokens' + 'approve(address,uint256):approve the amount for the given address' + 'allowance(address,address)\:uint: the approved amount' +) +sig_ms=( + 'getOwners()\:(address[]): multisig' + 'getMessageHash(bytes)\:bytes: gets the message hash of a transaction' + 'isOwner(address)\:bool:is owner' + 'signedMessages(bytes32)\:uint: number of signed messages' + 'approvedHashes(address,bytes32)\:uint:check if the hash was approved' + 'nonce()\:uint:the nonce of the multisig' + 'getModules()\:address[]:List of modules' + 'getTransactionHash(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,uint256)\:bytes32:calculates the transaction hash' + 'getThreshold()\:uint' + 'addOwnerWithThreshold(address,uint256):adds an owner with the given threshold' + 'changeThreshold(uint256): changes the threshold' + 'execTransaction(address,uint256,bytes,uint8,uint256,uint256,uint256,address,address,bytes): executes a transaction' +) + +_arguments -C $args diff --git a/scripts/build.sh b/scripts/build.sh index 527309661..faa5bb2b0 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -114,7 +114,7 @@ elif [ "$CONTAINER" = "android" ]; then docker run --rm -v $RD:$RD $CONTAINER /bin/bash -c "$CMD" elif [ "$CONTAINER" = "wasm_local" ]; then cd build - source ~/ws/tools/emsdk/emsdk_env.sh > /dev/null +# source ~/ws/tools/emsdk/emsdk_env.sh > /dev/null emcmake cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=true -DWASM=true -DASMJS=false -DWASM_EMMALLOC=true -DIPFS=false -DZKSYNC=true -DWASM_EMBED=false -DCMAKE_BUILD_TYPE=$BUILDTYPE .. make -j8 in3_wasm elif [ "$CONTAINER" = "wasm" ]; then diff --git a/scripts/build_includeh.sh b/scripts/build_includeh.sh index d4b296480..31d226e05 100755 --- a/scripts/build_includeh.sh +++ b/scripts/build_includeh.sh @@ -48,3 +48,24 @@ cat <../c/include/in3.rs.h #include "in3/in3_init.h" #include "in3/log.h" EOF + +# create swift binding header +cat <../c/include/in3.swift.h +// AUTO-GENERATED FILE +// See scripts/build_includeh.sh +#include "../src/core/client/request_internal.h" +#include "../src/signer/pk-signer/signer.h" +#include "../src/tools/clientdata/client_data.h" +#include "../src/api/btc/btc_api.h" +#include "in3/bytes.h" +#include "in3/client.h" +#include "in3/request.h" +#include "in3/plugin.h" +#include "in3/error.h" +#include "in3/eth_api.h" +#include "in3/in3_curl.h" +#include "in3/in3_init.h" +#include "in3/log.h" +#include "../src/tools/swift/swift.h" +#include "../src/third-party/tommath/tommath.h" +EOF diff --git a/scripts/build_multi_wasm.sh b/scripts/build_multi_wasm.sh index 2dfc9da81..cb01ba469 100755 --- a/scripts/build_multi_wasm.sh +++ b/scripts/build_multi_wasm.sh @@ -1,4 +1,7 @@ #!/bin/bash +# exit if a command fails +set -e + function build { NAME=$1 OPTIONS=$2 @@ -28,7 +31,7 @@ function build { } # define options -source ~/ws/tools/emsdk/emsdk_env.sh > /dev/null +source ~/ws/tools/emsdk/emsdk_env.sh > /dev/null || echo "env should be set first" CWD=$PWD cd $(dirname $0)/.. DST="$CWD/$1" diff --git a/scripts/build_rpc.sh b/scripts/build_rpc.sh new file mode 100755 index 000000000..e28aa2b9a --- /dev/null +++ b/scripts/build_rpc.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +generator/generate.js --src=../c/src --doc=../../in3-doc/docs --zsh=_in3.template --arg=../c/src/cmd/in3/args.h diff --git a/scripts/build_rpc_docu.js b/scripts/build_rpc_docu.js new file mode 100644 index 000000000..b7ea573f9 --- /dev/null +++ b/scripts/build_rpc_docu.js @@ -0,0 +1,244 @@ +const yaml = require('../wasm/test/node_modules/yaml') +const fs = require('fs') +const swift = require('./generator/swift') +const { + getType, + asArray, + camelCaseLow, + camelCaseUp, + link, + toCmdParam, + short_descr +} = require('./generator/util') + +const doc_dir = process.argv[process.argv.length - 1] +const main_conf = yaml.parse(fs.readFileSync('../c/src/cmd/in3/in3.yml', 'utf-8')) +const typeName = (def, code) => (code ? '`' : '') + ((def.key ? '{key:$t}' : (def.array ? '$t[]' : "$t")) + (def.optional ? '?' : '')).replace('$t', typeof (def.type) === 'string' ? def.type : 'object') + (code ? '`' : '') +const rpc_doc = [] +const config_doc = [] +const main_help = [] +const main_aliases = [] +const bool_props = [] + +let docs = {}, config = {}, types = {} + + +function scan(dir) { + for (const f of fs.readdirSync(dir, { withFileTypes: true })) { + if (f.name == 'rpc.yml') { + console.error('parse ' + dir + '/' + f.name) + const ob = yaml.parse(fs.readFileSync(dir + '/' + f.name, 'utf-8')) + if (ob.types) { + types = { ...types, ...ob.types } + delete ob.types + } + for (const k of Object.keys(ob)) { + if (ob[k].config) config = { ...config, ...ob[k].config } + delete ob[k].config + docs[k] = { ...docs[k], ...ob[k] } + } + } + else if (f.isDirectory()) scan(dir + '/' + f.name) + } +} + + + +function print_object(def, pad, useNum, doc) { + let i = 1 + for (const prop of Object.keys(def)) { + let s = pad + (useNum ? ((i++) + '.') : '*') + ' **' + prop + '**' + const p = def[prop] + const pt = getType(p.type, types) + if (p.type) s += ' : ' + typeName(p, true) + if (p.optional) s += ' *(optional)*' + if (p.descr) s += ' - ' + p.descr + if (p.key) s += ' with ' + p.key + ' as keys in the object' + if (p.default) s += ' (default: `' + JSON.stringify(p.default) + '`)' + if (p.enum) s += '\n' + pad + 'Possible Values are:\n\n' + Object.keys(p.enum).map(v => pad + ' - `' + v + '` : ' + p.enum[v]).join('\n') + '\n' + if (p.alias) s += '\n' + pad + 'The data structure of ' + prop + ' is the same as ' + link(p.alias) + '. See Details there.' + doc.push(s) + if (typeof pt === 'object') { + rpc_doc.push('The ' + prop + ' object supports the following properties :\n' + pad) + print_object(pt, pad + ' ', false, doc) + } + if (p.example) rpc_doc.push('\n' + pad + ' *Example* : ' + prop + ': ' + JSON.stringify(p.example)) + doc.push(pad + '\n') + } +} + + +function handle_config(conf, pre, title, descr) { + if (title) config_doc.push('\n## ' + title + '\n') + for (const key of Object.keys(conf)) { + const c = conf[key] + // handle bindings + + swift.updateConfig(pre, c, key) + + // handle doc + if (!pre) { + let s = '\n' + (title ? '#' : '') + '## ' + key + '\n\n' + c.descr + if (c.optional) s += ' *This config is optional.*' + if (c.default) s += ' (default: `' + JSON.stringify(c.default) + '`)' + if (c.type) s += '\n\n Type: ' + typeName(c, true) + if (c.enum) s += '\n\nPossible Values are:\n\n' + Object.keys(c.enum).map(v => '- `' + v + '` : ' + c.enum[v]).join('\n') + '\n' + config_doc.push(s) + if (typeof (c.type) === 'object') { + config_doc.push('The ' + key + ' object supports the following properties :\n') + print_object(c.type, '', false, config_doc) + } + if (c.example !== undefined) { + config_doc.push('\n*Example:*\n') + asArray(c.example).forEach(ex => { + config_doc.push('```sh') + if (typeof (ex) == 'object') + config_doc.push('> in3 ' + Object.keys(ex).filter(_ => typeof (ex[_]) !== 'object').map(k => '--' + pre + key + '.' + k + '=' + ex[k]).join(' ') + ' ....\n') + else + config_doc.push([...asArray(c.cmd).map(_ => '-' + _), '--' + pre + key].map(_ => '> in3 ' + _ + (ex === true ? '' : (_.startsWith('--') ? '=' : ' ') + ex) + ' ....').join('\n') + '\n') + config_doc.push('```\n') + if (!title) + config_doc.push('```js\nconst in3 = new IN3(' + JSON.stringify({ [key]: ex }, null, 2) + ')\n```\n') + }) + + } + } + asArray(c.cmd).forEach(_ => main_aliases.push(' "' + _ + '", "' + (c.alias || (pre + key + (c.type == 'bool' ? '=true' : ''))) + '",')); + if (c.type == 'bool') bool_props.push(pre + key); + main_help.push(('--' + pre + key).padEnd(30) + (c.cmd ? ('-' + c.cmd) : '').padEnd(7) + short_descr(c.descr)) + let s = '' + if (c.descr) s += '[' + short_descr(c.descr) + ']' + if (c.type != 'bool') + s += ':' + key + ':(' + (c.enum ? Object.keys(c.enum).join(' ') : '') + ')' + if (typeof (c.type) === 'object') + handle_config(c.type, pre + key + '.') + else { + zsh_conf.push("'--" + pre + key + (c.type != 'bool' ? '=' : '') + s + "'") + asArray(c.cmd).forEach(_ => zsh_conf.push("'-" + _ + s + "'")) + } + } +} + + +scan('../c/src') +docs.config.in3_config.params.config.type = config +rpc_doc.push('# API RPC\n\n') +rpc_doc.push('This section describes the behavior for each RPC-method supported with incubed.\n\nThe core of incubed is to execute rpc-requests which will be send to the incubed nodes and verified. This means the available RPC-Requests are defined by the clients itself.\n\n') +config_doc.push('# Configuration\n\n') +config_doc.push('When creating a new Incubed Instance you can configure it. The Configuration depends on the registered plugins. This page describes the available configuration parameters.\n\n') +const zsh_complete = fs.readFileSync('_in3.template', 'utf8') +let zsh_cmds = [], zsh_conf = [] +for (const s of Object.keys(docs).sort()) { + const rpcs = docs[s] + const rdescr = rpcs.descr + + rpc_doc.push("## " + s + "\n\n") + if (rdescr) rpc_doc.push(rdescr + '\n') + delete rpcs.descr + + for (const rpc of Object.keys(rpcs).sort()) { + const def = rpcs[rpc] + def.returns = def.returns || def.result + def.result = def.returns || def.result + let z = " '" + rpc + ': ' + short_descr((def.descr || (def.alias && rpcs[def.alias].descr) || '')) + + rpc_doc.push('### ' + rpc + '\n\n') + asArray(def.alias).forEach(_ => rpc_doc.push(rpc + ' is just an alias for ' + link(_) + '.See Details there.\n\n')) + if (def.descr) + rpc_doc.push(def.descr + '\n') + if (def.params) { + rpc_doc.push("*Parameters:*\n") + print_object(def.params, '', true, rpc_doc) + rpc_doc.push() + z += ' ' + Object.keys(def.params).map(_ => '<' + _ + '>').join(' ') + } + else if (!def.alias) + rpc_doc.push("*Parameters:* - \n") + if (def.in3Params) { + rpc_doc.push('The following in3-configuration will have an impact on the result:\n\n'); + print_object(getType(def.in3Params, types), '', false, rpc_doc) + rpc_doc.push() + } + if (def.validation) rpc_doc.push('\n' + def.validation + '\n') + + if (def.returns) { + if (def.returns.type) { + rpc_doc.push('*Returns:* ' + typeName(def.returns, true) + '\n\n' + def.returns.descr + '\n') + const pt = getType(def.returns.type, types) + if (typeof pt === 'object') { + rpc_doc.push('\nThe return value contains the following properties :\n') + print_object(pt, '', false, rpc_doc) + } + } + else if (def.returns.alias) + rpc_doc.push('*Returns:*\n\nThe Result of `' + rpc + '` is the same as ' + link(def.returns.alias) + '. See Details there.\n') + else + rpc_doc.push('*Returns:*\n\n' + (def.returns.descr || '') + '\n') + } + + if (def.proof) { + rpc_doc.push('*Proof:*\n\n' + (def.proof.descr || '') + '\n') + const pt = getType(def.proof.type, types) + if (def.proof.alias) + rpc_doc.push('The proof will be calculated as described in ' + link(def.proof.alias) + '. See Details there.\n\n') + + if (pt) { + rpc_doc.push("This proof section contains the following properties:\n\n") + print_object(pt, '', false, rpc_doc) + rpc_doc.push("\n\n") + } + } + + asArray(def.example).forEach(ex => { + const req = { method: rpc, params: ex.request || [] } + if (def.proof) req.in3 = { "verification": "proof", ...ex.in3Params } + const data = { result: ex.response || null } + const is_json = (typeof data.result == 'object' || Array.isArray(data.result)) + if (ex.in3) data.in3 = ex.in3 + + rpc_doc.push('*Example:*\n') + if (ex.descr) rpc_doc.push('\n' + ex.descr + '\n') + + /* + rpc_doc.push('```yaml\n# ---- Request -----\n\n' + yaml.stringify(req)) + rpc_doc.push('\n# ---- Response -----\n\n' + yaml.stringify(data)) + rpc_doc.push('```\n') + */ + rpc_doc.push('```sh\n> in3 ' + (ex.cmdParams ? (ex.cmdParams + ' ') : '') + req.method + ' ' + (req.params.map(toCmdParam).join(' ').trim()) + (is_json ? ' | jq' : '')) + rpc_doc.push(is_json ? JSON.stringify(data.result, null, 2) : '' + data.result) + rpc_doc.push('```\n') + + rpc_doc.push('```js\n//---- Request -----\n\n' + JSON.stringify(req, null, 2)) + rpc_doc.push('\n//---- Response -----\n\n' + JSON.stringify(data, null, 2)) + rpc_doc.push('```\n') + + }) + z += "'" + zsh_cmds.push(z) + } + console.log('generate ' + s + '\n ' + Object.keys(rpcs).join('\n ')) + + if (Object.values(rpcs).filter(_ => !_.skipApi).length) + swift.generateAPI(s, rpcs, rdescr, types) + +} + +handle_config(config, '') + +swift.generate_config() + + +handle_config(main_conf.config, '', 'cmdline options\n\nThose special options are used in the comandline client to pass additional options.\n') +main_help.push('') +main_help.push('In addition to the documented rpc-methods, those methods are also supported:') +main_help.push('') +Object.keys(main_conf.rpc).forEach(k => { + (k + ' ' + main_conf.rpc[k]).split("\n").map(_ => _.trim()).map((_, i) => i ? ' ' + _ : _) + .forEach(l => main_help.push(l)) +}) + +fs.writeFileSync('_in3.sh', zsh_complete.replace('$CMDS', zsh_cmds.join('\n')).replace('$CONFS', zsh_conf.join('\n')), { encoding: 'utf8' }) +fs.writeFileSync(doc_dir + '/rpc.md', rpc_doc.join('\n') + '\n', { encoding: 'utf8' }) +fs.writeFileSync(doc_dir + '/config.md', config_doc.join('\n') + '\n', { encoding: 'utf8' }) +fs.writeFileSync('../c/src/cmd/in3/args.h', '// This is a generated file, please don\'t edit it manually!\n\n#include \n\nconst char* bool_props[] = {' + bool_props.map(_ => '"' + _ + '", ').join('') + 'NULL};\n\nconst char* help_args = "\\\n' + main_help.map(_ => _ + '\\n').join('\\\n') + '";\n\nconst char* aliases[] = {\n' + main_aliases.join('\n') + '\n NULL};\n', { encoding: 'utf8' }) + diff --git a/scripts/build_rust.sh b/scripts/build_rust.sh index cfabd35f1..6d986f376 100755 --- a/scripts/build_rust.sh +++ b/scripts/build_rust.sh @@ -2,7 +2,7 @@ cd .. mkdir -p rust/in3-sys/in3-core mkdir -p rust/in3-sys/in3-core/c -cp -r c/CMakeLists.txt c/macro.cmake c/compiler.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ +cp -r c/CMakeLists.txt c/*.cmake c/docs c/src c/include rust/in3-sys/in3-core/c/ cp CMakeLists.txt rust/in3-sys/in3-core/ export UPDATE_IN3_BINDINGS=1 cd rust && cargo clean && cargo build diff --git a/scripts/check_bindings.js b/scripts/check_bindings.js new file mode 100755 index 000000000..4f616b8bd --- /dev/null +++ b/scripts/check_bindings.js @@ -0,0 +1,95 @@ +#!/usr/bin/env node + +const fs = require('fs') +const { execSync } = require('child_process') + +const exists = (a, name) => a.find(_ => _.name == name) +const grep = (pattern, start) => execSync('grep -r -E ' + pattern + ' --exclude-dir third-party --exclude "debug.h" ' + start).toString().split('\n').filter(_ => _) +const strings = (start, c) => grep(c + '[0-9a-zA-Z_]{4,}' + c, start).map(_ => ((/.*[\"']([0-9a-zA-Z_]{4,})[\"'].*/g).exec(_) || ['', ''])[1]).filter(_ => _) +const getRPCHandlers = () => grep('TRY_RPC', '../c/src').map(line => { + const r = (/(.*?\.c).*TRY_RPC\(\"([^\"]+).*/gm).exec(line) + return { file: r[1], name: r[1].indexOf('zksync') == -1 ? r[2] : 'zksync_' + r[2], type: 'handler' } +}) +const getRPCVerifiers = () => grep('VERIFY_RPC', '../c/src').reduce((p, line) => { + let r = (/(.*?\.c).*?VERIFY_RPC\(\"([^\"]+)(.*)/gm).exec(line) + const file = r[1] + if (!exists(p, r[2])) p.push({ file, name: r[2], type: 'verifier' }) + while (r = (/(.*?VERIFY_RPC)\(\"([^\"]+)(.*)/gm).exec(r[3])) { + if (!exists(p, r[2])) p.push({ file, name: r[2], type: 'verifier' }) + } + return p +}, []) +const getConfigs = () => grep('CONFIG_KEY', '../c/src').reduce((p, line) => { + let r = (/(.*?\.c).*?CONFIG_KEY\(\"([^\"]+)(.*)/gm).exec(line) + const file = r[1] + if (!exists(p, r[2])) p.push({ file, name: r[2], type: 'config' }) + while (r = (/(.*?CONFIG_KEY)\(\"([^\"]+)(.*)/gm).exec(r[3])) { + if (!exists(p, r[2])) p.push({ file, name: r[2], type: 'config' }) + } + return p +}, []) +const lastToken = l => l.substring(l.lastIndexOf(' ') + 1); +const label = (n, l) => l ? ('\033[' + l + 'm' + n.replace('*', '') + '\033[0m') : n +const is_allowed = ['btc_proofTarget'] +const check = (list, name, c) => list.indexOf(name) != -1 ? ((++res[c]) && ' \u2705 ') : ((list.length && is_allowed.indexOf(name) == -1) ? ' \u274c ' : ((++res[c]) && ' \u274e ')) +const bindings = { + doc: grep('\"^### \"', '../../../doc/docs/rpc.md').map(_ => _.substring(_.indexOf('# ') + 2).trim()), + java: strings('../java/src', '"'), + wasm: strings('../wasm/src', '\''), + python: strings('../python/in3', '"'), + rust: strings('../rust/in3-rs/src', '"'), + dotnet: strings('../dotnet/In3', '"', '*.cs'), + swift: strings('../swift/Sources', '"'), + c_api: strings('../c/src/api', '"',), + // test: [...strings('../c/test/testdata/requests', '"'), ...strings('../c/test/testdata/api', '"'), ...strings('../c/test/testdata/cmd', ' ')], + test: [ + ...strings('../c/test/testdata/requests', '"'), + ...strings('../c/test/testdata/api', '"'), + ...grep('\"^.*_.*\"', '../c/test/testdata/cmd').map(_ => ((/.* ([0-9a-zA-Z]+_[0-9a-zA-Z_]+).*/g).exec(_) || ['', ''])[1]).filter(_ => _)], + autocmpl: grep("\"'.*?:\"", '_in3.sh').map(_ => ((/'([a-zA-Z0-9_]+):/gm).exec(_) || ["", ""])[1]), +} +const res = Object.keys(bindings).reduce((p, c) => ({ ...p, [c]: 0 }), {}) +const all_rpc_names = [...getRPCHandlers(), ...getRPCVerifiers()].map(_ => _.name).filter((v, i, a) => a.indexOf(v) === i) +const configs = getConfigs().map(_ => _.name).filter((v, i, a) => a.indexOf(v) === i).sort() +bindings.doc.filter(_ => all_rpc_names.indexOf(_) == -1).forEach(_ => all_rpc_names.push(_ + '*')) +all_rpc_names.sort() +console.log('RPC-Method'.padEnd(40) + ' ' + Object.keys(bindings).map(_ => _.padEnd(7)).join('')) +console.log('-'.padEnd(45 + 7 * Object.keys(bindings).length, '-')) + + +all_rpc_names.forEach(rpc => { + let s = '', c = 0, l = rpc.replace('*', '') + Object.keys(bindings).forEach(k => { + c -= res[k] + s += check(bindings[k], l, k) + c += res[k] + }) + console.log(label(l.padEnd(40), rpc.endsWith('*') ? '31' : (Object.keys(bindings).length == c ? '32' : '33')) + ' : ' + s) +}) + +console.log('\nConfig'.padEnd(40) + ' ' + Object.keys(bindings).map(_ => _.padEnd(7)).join('')) +console.log('-'.padEnd(45 + 7 * Object.keys(bindings).length, '-')) +bindings.doc = grep('\"\\*\\*[a-zA-Z0-9_]+\\*\\*\"', '../../../doc/docs/rpc.md').map(_ => _.substring(_.indexOf('**') + 2, _.lastIndexOf('**')).trim()) +bindings.wasm = grep('\"^[ ]*[a-zA-Z0-9_]+[\\?]*:.*\"', '../wasm/src').map(_ => lastToken(_.substring(0, _.lastIndexOf(':')).replace('?', '').trim())) +bindings.autocmpl = [] +bindings.c_api = [] +bindings.test = [] +bindings.python = grep("\"self\\.[a-zA-Z0-9_]+\"", '../python/in3/model.py').map(_ => ((/self.([a-zA-Z0-9_]+)/gm).exec(_) || ["", ""])[1]) +bindings.swift = grep("\"var [a-zA-Z0-9_]+\"", '../swift/Sources/In3/Config.swift').map(_ => ((/var ([a-zA-Z0-9_]+)/gm).exec(_) || ["", ""])[1]) + + +for (const conf of configs) { + let s = '', c = 0 + Object.keys(bindings).forEach(k => { + c -= res[k] + s += check(bindings[k], conf, k) + c += res[k] + }) + console.log(label(conf.padEnd(40), (Object.keys(bindings).length == c ? '32' : '33')) + ' : ' + s) +} + + + +console.log("\nSummary:") +Object.keys(res).forEach(k => console.log(k.padEnd(8) + ': ' + (res[k] * 100 / (all_rpc_names.length + configs.length)).toFixed(0) + ' % ')) + diff --git a/scripts/generator/generate.js b/scripts/generator/generate.js new file mode 100755 index 000000000..9ae19aef2 --- /dev/null +++ b/scripts/generator/generate.js @@ -0,0 +1,280 @@ +#!/usr/bin/env node + +const yaml = require('yaml') +const fs = require('fs') +const { + getType, + asArray, + camelCaseLow, + camelCaseUp, + link, + toCmdParam, + short_descr +} = require('./util') + +const pargs = process.argv.slice(2) +const in3_core_dir = process.argv[1].replace('/scripts/generator/generate.js', '') +const src_dirs = [] +const doc_dir = [] +const args_file = [] +const zsh_file = [] +const generators = [] +let cmdName = 'in3' +let sdkName = 'IN3' +process.argv.slice(2).forEach(a => { + if (a.startsWith('--src=')) src_dirs.push(a.substr(6)) + else if (a.startsWith('--doc=')) doc_dir.push(a.substr(6)) + else if (a.startsWith('--arg=')) args_file.push(a.substr(6)) + else if (a.startsWith('--zsh=')) zsh_file.push(a.substr(6)) + else if (a.startsWith('--cmd=')) cmdName = a.substr(6) + else if (a.startsWith('--sdk=')) sdkName = a.substr(6) + else if (a.startsWith('--gen=')) generators.push(require(a.substr(6))) + else throw new Error('Invalid argument : ' + a) +}) +if (!src_dirs.length) src_dirs.push('../c/src') + +//const doc_dir = process.argv[process.argv.length - 1] +const main_conf = yaml.parse(fs.readFileSync(in3_core_dir + '/c/src/cmd/in3/in3.yml', 'utf-8')) +const typeName = (def, code) => (code ? '`' : '') + ((def.key ? '{key:$t}' : (def.array ? '$t[]' : "$t")) + (def.optional ? '?' : '')).replace('$t', typeof (def.type) === 'string' ? def.type : 'object') + (code ? '`' : '') +const rpc_doc = [] +const config_doc = [] +const main_help = [] +const main_aliases = [] +const bool_props = [] + +let docs = {}, config = {}, types = {} + + +function scan(dir) { + for (const f of fs.readdirSync(dir, { withFileTypes: true })) { + if (f.name == 'rpc.yml') { + console.error('parse ' + dir + '/' + f.name) + const ob = yaml.parse(fs.readFileSync(dir + '/' + f.name, 'utf-8')) + if (ob.types) { + types = { ...types, ...ob.types } + delete ob.types + } + for (const k of Object.keys(ob)) { + if (ob[k].config) config = { ...config, ...ob[k].config } + delete ob[k].config + docs[k] = { ...docs[k], ...ob[k] } + } + } + else if (f.isDirectory()) scan(dir + '/' + f.name) + } +} + + + +function print_object(def, pad, useNum, doc, pre) { + let i = 1 + for (const prop of Object.keys(def)) { + let s = pad + (useNum ? ((i++) + '.') : '*') + ' **' + prop + '**' + const p = def[prop] + const pt = getType(p.type, types) + if (p.type) s += ' : ' + typeName(p, true) + if (p.optional) s += ' *(optional)*' + if (p.descr) s += ' - ' + p.descr + if (p.key) s += ' with ' + p.key + ' as keys in the object' + if (p.default) s += ' (default: `' + JSON.stringify(p.default) + '`)' + if (p.enum) s += '\n' + pad + 'Possible Values are:\n\n' + Object.keys(p.enum).map(v => pad + ' - `' + v + '` : ' + p.enum[v]).join('\n') + '\n' + if (p.alias) s += '\n' + pad + 'The data structure of ' + prop + ' is the same as ' + link(p.alias) + '. See Details there.' + if (p.cmd) asArray(p.cmd).forEach(_ => s += '\n' + pad + 'This option can also be used in its short-form in the comandline client `-' + _ + '` .') + doc.push(s) + if (typeof pt === 'object') { + doc.push('The ' + prop + ' object supports the following properties :\n' + pad) + print_object(pt, pad + ' ', false, doc) + } + if (rpc_doc === doc) { + if (p.example) doc.push('\n' + pad + ' *Example* : ' + prop + ': ' + JSON.stringify(p.example)) + } + else if (config_doc === doc) + asArray(p.example).forEach(ex => { + key = prop + doc.push(pad+'```sh') + if (typeof (ex) == 'object') + doc.push(pad+'> ' + cmdName + ' ' + Object.keys(ex).filter(_ => typeof (ex[_]) !== 'object').map(k => '--' + pre + key + '.' + k + '=' + ex[k]).join(' ') + ' ....\n') + else + doc.push(pad+[...asArray(p.cmd).map(_ => '-' + _), '--' + pre + key].map(_ => '> ' + cmdName + ' ' + _ + (ex === true ? '' : (_.startsWith('--') ? '=' : ' ') + ex) + ' ....').join('\n') + '\n') + doc.push(pad+'```\n') + }) + + doc.push(pad + '\n') + } +} + + +function handle_config(conf, pre, title, descr) { + if (title) config_doc.push('\n## ' + title + '\n') + for (const key of Object.keys(conf)) { + const c = conf[key] + // handle bindings + generators.forEach(_ => _.updateConfig(pre, c, key)) + + // handle doc + if (!pre) { + let s = '\n' + (title ? '#' : '') + '## ' + key + '\n\n' + c.descr + if (c.optional) s += ' *This config is optional.*' + if (c.default) s += ' (default: `' + JSON.stringify(c.default) + '`)' + if (c.type) s += '\n\n Type: ' + typeName(c, true) + if (c.enum) s += '\n\nPossible Values are:\n\n' + Object.keys(c.enum).map(v => '- `' + v + '` : ' + c.enum[v]).join('\n') + '\n' + config_doc.push(s) + if (typeof (c.type) === 'object') { + config_doc.push('The ' + key + ' object supports the following properties :\n') + print_object(c.type, '', false, config_doc, key+".") + } + if (c.example !== undefined) { + config_doc.push('\n*Example:*\n') + asArray(c.example).forEach(ex => { + config_doc.push('```sh') + if (typeof (ex) == 'object') + config_doc.push('> ' + cmdName + ' ' + Object.keys(ex).filter(_ => typeof (ex[_]) !== 'object').map(k => '--' + pre + key + '.' + k + '=' + ex[k]).join(' ') + ' ....\n') + else + config_doc.push([...asArray(c.cmd).map(_ => '-' + _), '--' + pre + key].map(_ => '> ' + cmdName + ' ' + _ + (ex === true ? '' : (_.startsWith('--') ? '=' : ' ') + ex) + ' ....').join('\n') + '\n') + config_doc.push('```\n') + if (!title) + config_doc.push('```js\nconst ' + cmdName + ' = new ' + sdkName + '(' + JSON.stringify({ [key]: ex }, null, 2) + ')\n```\n') + }) + + } + } + asArray(c.cmd).forEach(_ => main_aliases.push(' "' + _ + '", "' + (c.alias || (pre + key + (c.type == 'bool' ? '=true' : ''))) + '",')); + if (c.type == 'bool') bool_props.push(pre + key); + main_help.push(('--' + pre + key).padEnd(30) + (c.cmd ? ('-' + c.cmd) : '').padEnd(7) + short_descr(c.descr)) + let s = '' + if (c.descr) s += '[' + short_descr(c.descr) + ']' + if (c.type != 'bool') + s += ':' + key + ':(' + (c.enum ? Object.keys(c.enum).join(' ') : '') + ')' + if (typeof (c.type) === 'object') + handle_config(c.type, pre + key + '.') + else { + zsh_conf.push("'--" + pre + key + (c.type != 'bool' ? '=' : '') + s + "'") + asArray(c.cmd).forEach(_ => zsh_conf.push("'-" + _ + s + "'")) + } + } +} + +src_dirs.forEach(scan) +docs.config.in3_config.params.config.type = config +rpc_doc.push('# API RPC\n\n') +rpc_doc.push('This section describes the behavior for each RPC-method supported with incubed.\n\nThe core of incubed is to execute rpc-requests which will be send to the incubed nodes and verified. This means the available RPC-Requests are defined by the clients itself.\n\n') +config_doc.push('# Configuration\n\n') +config_doc.push('When creating a new Incubed Instance you can configure it. The Configuration depends on the registered plugins. This page describes the available configuration parameters.\n\n') +let zsh_cmds = [], zsh_conf = [] +for (const s of Object.keys(docs).sort()) { + const rpcs = docs[s] + const rdescr = rpcs.descr + + rpc_doc.push("## " + s + "\n\n") + if (rdescr) rpc_doc.push(rdescr + '\n') + delete rpcs.descr + + for (const rpc of Object.keys(rpcs).sort()) { + const def = rpcs[rpc] + def.returns = def.returns || def.result + def.result = def.returns || def.result + let z = " '" + rpc + ': ' + short_descr((def.descr || (def.alias && rpcs[def.alias].descr) || '')) + + rpc_doc.push('### ' + rpc + '\n\n') + asArray(def.alias).forEach(_ => rpc_doc.push(rpc + ' is just an alias for ' + link(_) + '.See Details there.\n\n')) + if (def.descr) + rpc_doc.push(def.descr + '\n') + if (def.params) { + rpc_doc.push("*Parameters:*\n") + print_object(def.params, '', true, rpc_doc) + rpc_doc.push() + z += ' ' + Object.keys(def.params).map(_ => '<' + _ + '>').join(' ') + } + else if (!def.alias) + rpc_doc.push("*Parameters:* - \n") + if (def.in3Params) { + rpc_doc.push('The following in3-configuration will have an impact on the result:\n\n'); + print_object(getType(def.in3Params, types), '', false, rpc_doc) + rpc_doc.push() + } + if (def.validation) rpc_doc.push('\n' + def.validation + '\n') + + if (def.returns) { + if (def.returns.type) { + rpc_doc.push('*Returns:* ' + typeName(def.returns, true) + '\n\n' + def.returns.descr + '\n') + const pt = getType(def.returns.type, types) + if (typeof pt === 'object') { + rpc_doc.push('\nThe return value contains the following properties :\n') + print_object(pt, '', false, rpc_doc) + } + } + else if (def.returns.alias) + rpc_doc.push('*Returns:*\n\nThe Result of `' + rpc + '` is the same as ' + link(def.returns.alias) + '. See Details there.\n') + else + rpc_doc.push('*Returns:*\n\n' + (def.returns.descr || '') + '\n') + } + + if (def.proof) { + rpc_doc.push('*Proof:*\n\n' + (def.proof.descr || '') + '\n') + const pt = getType(def.proof.type, types) + if (def.proof.alias) + rpc_doc.push('The proof will be calculated as described in ' + link(def.proof.alias) + '. See Details there.\n\n') + + if (pt) { + rpc_doc.push("This proof section contains the following properties:\n\n") + print_object(pt, '', false, rpc_doc) + rpc_doc.push("\n\n") + } + } + + asArray(def.example).forEach(ex => { + const req = { method: rpc, params: ex.request || [] } + if (def.proof) req.in3 = { "verification": "proof", ...ex.in3Params } + const data = { result: ex.response || null } + const is_json = (typeof data.result == 'object' || Array.isArray(data.result)) + if (ex.in3) data.in3 = ex.in3 + + rpc_doc.push('*Example:*\n') + if (ex.descr) rpc_doc.push('\n' + ex.descr + '\n') + + /* + rpc_doc.push('```yaml\n# ---- Request -----\n\n' + yaml.stringify(req)) + rpc_doc.push('\n# ---- Response -----\n\n' + yaml.stringify(data)) + rpc_doc.push('```\n') + */ + rpc_doc.push('```sh\n> ' + cmdName + ' ' + (ex.cmdParams ? (ex.cmdParams + ' ') : '') + req.method + ' ' + (req.params.map(toCmdParam).join(' ').trim()) + (is_json ? ' | jq' : '')) + rpc_doc.push(is_json ? JSON.stringify(data.result, null, 2) : '' + data.result) + rpc_doc.push('```\n') + + rpc_doc.push('```js\n//---- Request -----\n\n' + JSON.stringify(req, null, 2)) + rpc_doc.push('\n//---- Response -----\n\n' + JSON.stringify(data, null, 2)) + rpc_doc.push('```\n') + + }) + z += "'" + zsh_cmds.push(z) + } + console.log('generate ' + s + '\n ' + Object.keys(rpcs).join('\n ')) + + if (Object.values(rpcs).filter(_ => !_.skipApi).length) + generators.forEach(_ => _.generateAPI(s, rpcs, rdescr, types)) + +} + +handle_config(config, '') + +generators.forEach(_ => _.generate_config()) + +handle_config(main_conf.config, '', 'cmdline options\n\nThose special options are used in the comandline client to pass additional options.\n') +main_help.push('') +main_help.push('In addition to the documented rpc-methods, those methods are also supported:') +main_help.push('') +Object.keys(main_conf.rpc).forEach(k => { + (k + ' ' + main_conf.rpc[k]).split("\n").map(_ => _.trim()).map((_, i) => i ? ' ' + _ : _) + .forEach(l => main_help.push(l)) +}) + +if (zsh_file.length) + fs.writeFileSync(zsh_file[0].replace('.template', '.sh'), fs.readFileSync(zsh_file[0], 'utf8').replace('$CMDS', zsh_cmds.join('\n')).replace('$CONFS', zsh_conf.join('\n')), { encoding: 'utf8' }) +if (doc_dir.length) { + fs.writeFileSync(doc_dir[0] + '/rpc.md', rpc_doc.join('\n') + '\n', { encoding: 'utf8' }) + fs.writeFileSync(doc_dir[0] + '/config.md', config_doc.join('\n') + '\n', { encoding: 'utf8' }) +} +if (args_file.length) + fs.writeFileSync(args_file[0], '// This is a generated file, please don\'t edit it manually!\n\n#include \n\nconst char* bool_props[] = {' + bool_props.map(_ => '"' + _ + '", ').join('') + 'NULL};\n\nconst char* help_args = "\\\n' + main_help.map(_ => _ + '\\n').join('\\\n') + '";\n\nconst char* aliases[] = {\n' + main_aliases.join('\n') + '\n NULL};\n', { encoding: 'utf8' }) + diff --git a/scripts/generator/swift.js b/scripts/generator/swift.js new file mode 100644 index 000000000..892f4e3e7 --- /dev/null +++ b/scripts/generator/swift.js @@ -0,0 +1,365 @@ +const fs = require('fs') +const yaml = require('../../wasm/test/node_modules/yaml') +const { + getType, + asArray, + camelCaseLow, + camelCaseUp, +} = require('./util') +const swiftDir = '../../in3-swift' +const isStruct = (c, typeConfigs) => typeof c.type == 'string' ? typeConfigs[c.type] : typeof c.type === 'object' +const configs = { + In3Config: [ + '/// The main Incubed Configuration', + 'public struct In3Config : Codable {', + '', + ' /// create a new Incubed Client based on the Configuration', + ' public func createClient() throws -> In3 {', + ' return try In3(self)', + ' }', + '' + ] +} +function converterName(swiftType, asFn) { + const type = swiftType.replace("String:", "").split(/[\[\]_\?\!]+/).join('') + if (type.startsWith("Int") || type.startsWith("UInt") || type == 'Double' || type == 'Bool' || type == 'String' || type == 'AnyObject') return 'to' + type + if (swiftType.startsWith('[') && asFn) { + if (swiftType.indexOf(':') >= 0) { + if (swiftType.endsWith('?')) + return '{ if let dict = try toObject($0,$1) { try dict.mapValues({ try ' + type + '($0,true)! }) } }' + else + return '{ try toObject($0,$1)!.mapValues({ try ' + type + '($0,false)! }) }' + } + else { + if (swiftType.endsWith('?')) + return '{ if let array = try toArray($0,$1) { try array.map({ try ' + type + '($0,true)! }) } }' + else + return '{ try toArray($0,$1)!.map({ try ' + type + '($0,false)! }) }' + } + } + return asFn ? '{ try ' + type + '($0,$1) }' : type +} + +function asHex(t, varName) { + if (t.startsWith('UInt256')) + return varName + '.hexValue' + return (t.startsWith('UInt') || t.startsWith('Int')) + ? 'String(format: "0x%1x", arguments: [' + varName + '])' + : varName +} + +function generateStruct(swiftType, conf, descr, typeConfigs, typesGenerated, api) { + typesGenerated[swiftType] = 'placeholder' + let content = ['/// ' + (descr || swiftType).split('\n').join('\n/// '), + 'public struct ' + swiftType + ' {' + ] + + let toRPC = '\n internal func toRPCDict() -> [String:RPCObject] {\n var obj:[String:RPCObject] = [:]' + let init = ' internal init?(_ rpc:RPCObject?, _ optional: Bool = true) throws {' + + '\n guard let obj = try toObject(rpc, optional) else { return nil }' + let pubInitHead = ' public init(' + let pubInitBody = '' + let pubInitDescr = '\n\n /// initialize the ' + swiftType + '\n ///' + + + for (let name of Object.keys(conf)) { + let p = conf[name] + const t = getAPIType(p, typeConfigs, typesGenerated, name, api) + content.push(' /// ' + (p.descr || ('the ' + camelCaseUp(name))).split('\n').join('\n /// ')) + content.push(' public var ' + name + ': ' + t + '\n') + pubInitHead += (pubInitHead.endsWith('(') ? '' : ', ') + name + ': ' + t + (t.endsWith('?') ? ' = nil' : '') + pubInitDescr += '\n /// - Parameter ' + name + ' : ' + (p.descr || ('the ' + camelCaseUp(name))).split('\n').join('\n /// ') + pubInitBody += '\n self.' + name + ' = ' + name + if (p.array) { + if (p.optional) { + init += '\n if let ' + name + ' = try toArray(obj["' + name + '"],' + (p.optional ? 'true' : 'false') + ') {' + init += '\n self.' + name + ' = try ' + name + '.map({ try ' + converterName(t, false) + '($0,' + (p.nullable ? 'true' : 'false') + ')' + (p.nullable ? '' : '!') + ' })' + init += '\n } else {' + init += '\n self.' + name + ' = nil' + init += '\n }' + if (!isStruct(p, typeConfigs)) + toRPC += '\n if let x = ' + name + ' { obj["' + name + '"] = RPCObject( x ) }' + } + else { + init += '\n ' + name + ' = try toArray(obj["' + name + '"])!.map({ try ' + converterName(t, false) + '($0,' + (p.optional ? 'true' : 'false') + ')! })' + if (!isStruct(p, typeConfigs)) + toRPC += '\n obj["' + name + '"] = RPCObject( ' + name + ' )' + } + } + else if (p.key) { + if (p.optional) { + init += '\n if let ' + name + ' = try toObject(obj["' + name + '"],' + (p.optional ? 'true' : 'false') + ') {' + init += '\n self.' + name + ' = try ' + name + '.mapValues({ try ' + converterName(t, false) + '($0,' + (p.optional ? 'true' : 'false') + ')! })' + init += '\n } else {' + init += '\n self.' + name + ' = nil' + init += '\n }' + } + else + init += '\n ' + name + ' = try toObject(obj["' + name + '"])!.mapValues({ try ' + converterName(t, false) + '($0,false)! })' + } + else { + init += '\n ' + name + ' = try ' + converterName(t, false) + '(obj["' + name + '"],' + (p.optional ? 'true' : 'false') + ')!' + if (!isStruct(p, typeConfigs)) { + if (p.optional || p.optionalAPI) + toRPC += '\n if let x = ' + name + ' { obj["' + name + '"] = RPCObject( ' + asHex(t, 'x') + ' ) }' + else + toRPC += '\n obj["' + name + '"] = RPCObject( ' + asHex(t, name) + ' )' + + } + } + + } + + typesGenerated[swiftType] = content.join('\n') + '\n' + init + '\n }\n' + (toRPC.indexOf('obj["') == -1 ? '' : (toRPC + '\n return obj\n }')) + + pubInitDescr + '\n' + + pubInitHead + ') {' + + pubInitBody + '\n }\n}' +} + +function getAPIType(c, typeConfigs, typesGenerated, prefix, api) { + let swiftType = camelCaseUp(('' + (c.typeName || c.type)).split('|')[0].trim()) + let typedef = null + if (typeof c.type === 'object') { + typedef = getType(c.type, typeConfigs) + swiftType = c.typeName || camelCaseUp(api + camelCaseUp(c.typeName || prefix.startsWith('get') ? prefix.substr(3) : prefix)) + } + else if (typeConfigs[c.type]) { + typedef = getType(c.type, typeConfigs) + swiftType = c.typeName || camelCaseUp((c.type.toLowerCase().startsWith(api.toLowerCase()) ? '' : api) + camelCaseUp(c.type)) + } + else if (swiftType == 'Uint') swiftType = 'UInt64' + else if (swiftType.startsWith('Uint')) swiftType = swiftType.replace('Uint', 'UInt') + else if (swiftType == 'Float') swiftType = 'Double' + else if (swiftType == 'Any') swiftType = 'AnyObject' + else if (swiftType.startsWith('Byte') || swiftType == 'Address' || swiftType == 'Hex') swiftType = 'String' + if (swiftType.endsWith('[]')) { + swiftType = swiftType.substr(0, swiftType.length - 2) + c = { ...c, array: true } + } + if (typedef && typesGenerated && !typesGenerated[swiftType]) + generateStruct(swiftType, typedef, c.descr, typeConfigs, typesGenerated, api); + + if (c.array) swiftType = '[' + swiftType + (c.nullable ? '?' : '') + ']' + if (c.key) swiftType = '[String:' + swiftType + ']' + if (c.optional || c.optionalAPI) swiftType += '?' + return swiftType +} + +exports.updateConfig = function (pre, c, key) { + // generate swift + const swift = configs[camelCaseUp(pre || 'In3Config')] + const pad = pre ? ' ' : '' + if (swift && key.indexOf('-') == -1 && key.indexOf('.') == -1) { + let swiftType = getAPIType({ ...c, optional: false, array: false }, {}, {}, key, '') + if (typeof c.type === 'object') + configs[swiftType] = [ + ' /// ' + c.descr.replace(/\n/gm, '\n/// '), + ' public struct ' + swiftType + ' : Codable {' + ] + if (c.array) swiftType = '[' + swiftType + ']' + swift.push('\n' + pad + ' /// ' + ( + c.descr + + (c.default ? ('\n(default: `' + JSON.stringify(c.default) + '`)') : '') + + (c.enum ? ('\n\nPossible Values are:\n\n' + Object.keys(c.enum).map(v => '- `' + v + '` : ' + c.enum[v]).join('\n') + '\n') : '') + + (c.example ? ('\n\nExample: ' + (Array.isArray(c.example) ? '\n```\n' : '`') + asArray(c.example).map(ex => yaml.stringify(ex).trim()).join('\n') + (Array.isArray(c.example) ? '\n```' : '`')) : '') + ).replace(/\n/gm, '\n' + pad + ' /// ')) + swift.push(pad + ' public var ' + key + ' : ' + swiftType + ((c.optional || c.optionalAPI || !pre) ? '?' : '')) + } +} + + +function createSwiftInitForStruct(s, pad) { + let comments = '\n' + pad + ' /// initialize it memberwise' + let code = '' + let init = '' + let lastDescr = '' + for (let l of s) { + l = l.trim() + if (lastDescr && !l) lastDescr = '' + if (!lastDescr && l.startsWith('/// ')) lastDescr = l.substr(4).split('\n')[0].trim() + if (l.startsWith('public var ')) { + l = l.substr('public var '.length) + const pname = l.substr(0, l.indexOf(':')).trim() + comments += '\n' + pad + ' /// - Parameter ' + pname + ' : ' + lastDescr + code += '\n' + pad + ' self.' + pname + ' = ' + pname + init += (init ? ', ' : '') + l + (l.endsWith('?') ? ' = nil' : '') + lastDescr = '' + } + } + s.push(comments + '\n' + pad + ' public init(' + init + ') {' + code + '\n' + pad + ' }') +} + +exports.generate_config = function () { + Object.keys(configs).forEach(_ => createSwiftInitForStruct(configs[_], _ == 'In3Config' ? '' : ' ')) + fs.writeFileSync(swiftDir + '/Sources/In3/Config.swift', '// This is a generated file, please don\'t edit it manually!\n\nimport Foundation\n\n' + ( + configs.In3Config.join('\n') + '\n\n' + + Object.keys(configs).filter(_ => _ != 'In3Config').map(type => configs[type].join('\n') + '\n }\n\n').join('') + + '\n}\n' + ), { encoding: 'utf8' }) +} + +function toParam(name, p, types) { + if (p.fixed !== undefined) + return ' RPCObject(' + JSON.stringify(p.fixed) + ')' + + const optional = (p.optional || p.optionalAPI) ? '!' : '' + let expr = '' + if (isStruct(p, types)) + expr = name + optional + '.toRPCDict()' + else if (p.type == 'uint256') + expr = name + optional + else if (p.type.startsWith('uint') || p.type.startsWith('int')) + expr = 'String(format: "0x%1x", ' + name + optional + ')' + else + expr = name + optional + + if (p.optional || p.optionalAPI) { + return name + ' == nil' + + ' ? RPCObject' + (p.internalDefault ? ('(' + JSON.stringify(p.internalDefault) + ')') : '.none') + + ' : RPCObject( ' + expr + ' )' + } + else + return 'RPCObject( ' + expr + ')' +} + +function createApiFunction(rpc_name, rpc, content, api_name, structs, types, rpcs) { + if (!rpc || rpc.skipApi) // ignore this rpc-function + return + if (rpc.alias) // create function from link but replace the name + return createApiFunction(rpc_name, rpcs[rpc.alias], content, api_name, structs, types, rpcs) + + // we create a resulting typ with string as default. + const r = { ...rpc.result, type: (rpc.result || {}).type || 'string' } + + // options means, we have different result types based on the input params + if (r.options) { + for (option of r.options) { + let rr = { ...rpc, result: { ...r, ...option.result } } + if (option.example && rr.example) + rr.example = { ...rr.example, response: option.example } + if (option.params && rr.example && rr.example.request) + rr.example = { ...rr.example, request: rr.example.request.slice(0, Object.keys(rpc.params).length - Object.keys(option.params).length) } + rr.params = {} + for (let pp of Object.keys(rpc.params)) { + rr.params[pp] = { ...rpc.params[pp] } + if (option.params[pp] != undefined) rr.params[pp].fixed = option.params[pp] + } + delete rr.result.options + rr.apiName = option.name || rr.apiName + rr.descr = option.descr || rr.descr + createApiFunction(rpc_name, rr, content, api_name, structs, types, rpcs) + } + return + } + + // add description + content.push(' /// ' + (rpc.descr || rpc_name).split('\n').join('\n /// ')) + + // generate the function name + const fnName = rpc.apiName || camelCaseLow(rpc_name.substr(rpc_name.indexOf('_') + 1)) + + let s = ' public func ' + fnName + '(' + let params = '' + for (let name of Object.keys(rpc.params || {})) { + let p = rpc.params[name] + let type = getAPIType(p, types, structs, name, camelCaseUp(api_name)) + + // add param as argument, (if not fixed) + if (p.fixed === undefined) { + if (!s.endsWith('(')) s += ', ' + content.push(' /// - Parameter ' + name + ' : ' + (p.descr || name).split('\n').join(' /// ')) + s += name + ': ' + type + (p.optional || p.optionalAPI || p.default !== undefined ? ' = ' + (p.default !== undefined ? JSON.stringify(p.default) : 'nil') : '') + } + // add the param to the rpc call + params += (params ? ' ' : ' params:') + toParam(name, p, types) + ',' + } + + // handle return type + const returnType = getAPIType(r, types, structs, fnName, camelCaseUp(api_name)) + if (rpc.sync) { + s += ') throws -> ' + returnType.replace("AnyObject", "RPCObject") + ' {' + s += '\n return try execLocalAndConvert(in3: in3, method: "' + rpc_name + '",' + params + if (returnType == '[AnyObject]') + s += ' convertWith: { try toArray($0,$1)! } )' + else if (r.array) { + s += ' convertWith: { try toArray($0,$1)!.map({ try ' + converterName(returnType, true) + '($0, ' + (!!r.optional) + ')! }) } )' + } + else + s += ' convertWith: ' + converterName(returnType, true) + ' )' + s += '\n }\n' + } + else { + s += ') -> Future<' + returnType + '> {' + s += '\n return execAndConvert' + (r.optional ? 'Optional' : '') + '(in3: in3, method: "' + rpc_name + '",' + params + s += ' convertWith: ' + converterName(returnType, true) + ' )' + s += '\n }\n' + } + if (r.descr) content.push(' /// - Returns: ' + rpc.result.descr.split('\n').join('\n /// ')) + + // add examples as comments + asArray(rpc.example).forEach(ex => { + function toSwiftValue(val, pIndex) { + const name = paramNames[pIndex] + const def = rpc.params[name] + if (!def) return JSON.stringify(val) + if (isStruct(def, types) && typeof val === 'object') { + let swiftType = getAPIType(def, types, structs, name, camelCaseUp(api_name)) + return swiftType + '(' + Object.keys(val).map(_ => _ + ': ' + JSON.stringify(val[_])).join(', ') + ')' + } + return JSON.stringify(val) + } + const paramNames = Object.keys(rpc.params || {}) + let x = '\n**Example**\n\n```swift\n' + let call = 'in3.' + camelCaseUp(api_name).toLowerCase() + '.' + fnName + '(' + (ex.request || []) + .filter((_, i) => _ != rpc.params[paramNames[i]].internalDefault) + .map((_, i) => paramNames[i] + ': ' + toSwiftValue(_, i)).join(', ') + ')' + if (rpc.sync) { + x += 'let result = try ' + call + '\n' + x += '// result = ' + (typeof ex.response === 'object' ? '\n// ' : '') + yaml.stringify(ex.response).trim().split('\n').join('\n// ') + } + else { + x += call + ' .observe(using: {\n' + x += ' switch $0 {\n' + x += ' case let .failure(err):\n' + x += ' print("Failed because : \\(err.localizedDescription)")\n' + x += ' case let .success(val):\n' + x += ' print("result : \\(val)")\n' + x += '// result = ' + (typeof ex.response === 'object' ? '\n// ' : '') + yaml.stringify(ex.response).trim().split('\n').join('\n// ') + x += '\n }\n' + x += '}\n' + } + x += '\n```\n' + content.push(' /// ' + x.split('\n').join('\n /// ')) + }) + content.push(s) +} + +exports.generateAPI = function (api_name, rpcs, descr, types) { + const structs = {} + const apiName = camelCaseUp(api_name) + const content = [ + '/// this is generated file don\'t edit it manually!', + '', + 'import Foundation', + '', + '/// ' + descr.split('\n').join('\n/// '), + 'public class ' + apiName + ' {', + ' internal var in3: In3', + '', + ' /// initialiazes the ' + camelCaseUp(api_name) + ' API', + ' /// - Parameter in3 : the incubed Client', + ' init(_ in3: In3) {', + ' self.in3 = in3', + ' }', + '' + ] + + // generate functions + Object.keys(rpcs).forEach(rpc_name => createApiFunction(rpc_name, rpcs[rpc_name], content, api_name, structs, types, rpcs)) + + // write the API to the filesystem + fs.writeFileSync(swiftDir + '/Sources/In3/API/' + apiName + '.swift', ( + content.join('\n') + '\n\n}\n' + + Object.values(structs).join('\n\n') + ), 'utf8') +} diff --git a/scripts/generator/util.js b/scripts/generator/util.js new file mode 100644 index 000000000..4d5d7a7c1 --- /dev/null +++ b/scripts/generator/util.js @@ -0,0 +1,34 @@ +const camelCase = n => n.split('_').map(_ => _.substr(0, 1).toUpperCase() + _.substr(1)).join('') + +exports.camelCase = camelCase +exports.camelCaseUp = s => { + if (!s) return '' + if (s[s.length - 1] == '.') s = s.substr(0, s.length - 1) + s = s.substr(s.lastIndexOf('.') + 1) + return s.substr(0, 1).toUpperCase() + camelCase(s).substr(1) +} +exports.camelCaseLow = s => s ? s.substr(0, 1).toLowerCase() + camelCase(s).substr(1) : '' + +exports.asArray = val => val == undefined ? [] : (Array.isArray(val) ? val : [val]) +exports.link = (name, label) => '[' + (label || name) + '](#' + name.toLowerCase().replace('_', '-') + ')' +exports.getType = (val, types) => { + if (typeof val === 'object') { + if (val._extends) { + const base = exports.getType(val._extends, types) + delete val._extends + Object.assign(val, { ...base, ...val }) + } + return val + } + if (!val) return undefined + return exports.getType(types['' + val], types) || val +} +exports.toCmdParam = val => (typeof val == 'object' || Array.isArray(val) || ('' + val).indexOf(' ') >= 0) ? "'" + JSON.stringify(val) + "'" : ('' + val) +exports.short_descr = function (d) { + let zd = (d || '').trim() + if (zd.indexOf('.') >= 0) zd = zd.substr(0, zd.indexOf('.')) + if (zd.indexOf('\n') >= 0) zd = zd.substr(0, zd.indexOf('\n')) + if (zd.indexOf('[') >= 0) zd = zd.substr(0, zd.indexOf('[')) + if (zd.length > 100) zd = zd.substr(0, 100) + '...' + return zd +} \ No newline at end of file diff --git a/wasm/src/CMakeLists.txt b/wasm/src/CMakeLists.txt index a140ad36d..b849dca0b 100644 --- a/wasm/src/CMakeLists.txt +++ b/wasm/src/CMakeLists.txt @@ -37,7 +37,7 @@ set(EMC_PROPS "-s ALLOW_MEMORY_GROWTH=1 -s NODEJS_CATCH_REJECTION=0 -s EXPORT_NA IF (ASMJS) set(WASM_EMBED true) set(CMAKE_EXECUTABLE_SUFFIX ".js") - set(EMC_PROPS "${EMC_PROPS} -s FINALIZE_ASM_JS=1 -s SEPARATE_ASM=1 -s WASM=0 -s ASM_JS=1 ") + set(EMC_PROPS "${EMC_PROPS} -s FINALIZE_ASM_JS=1 -s WASM=0 -s ASM_JS=1 ") else(ASMJS) set(EMC_PROPS "${EMC_PROPS} -s WASM=1") endif(ASMJS) diff --git a/wasm/src/in3.js b/wasm/src/in3.js index e43bd4785..098bef896 100644 --- a/wasm/src/in3.js +++ b/wasm/src/in3.js @@ -47,7 +47,8 @@ if (isBrowserEnvironment) { // for browsers in3w.in3_cache = { get: key => window.localStorage.getItem('in3.' + key), - set: (key, value) => window.localStorage.setItem('in3.' + key, value) + set: (key, value) => window.localStorage.setItem('in3.' + key, value), + clear: () => window.localStorage.clear() } in3w.transport = (url, payload, timeout, method, headers) => Promise.race([ fetch(url, { @@ -75,6 +76,12 @@ else { }, set(key, value) { fs.writeFileSync('.in3/' + key, Buffer.from(value, 'hex')) + }, + clear() { + try { + fs.rmdirSync('.in3', { recursive: true }) + fs.mkdirSync('.in3') + } catch (x) { } } } @@ -215,7 +222,7 @@ class IN3 { this.transport = this.config.transport delete this.config.transport } - const r = in3w.ccall('in3_config', 'number', ['number', 'string'], [this.ptr, JSON.stringify(this.config)]); + const r = in3w.ccall('in3_config', 'number', ['number', 'string'], [this.ptr, JSON.stringify(this.config)]); // shortcut for 'in3_config' if (r) { const ex = new Error(UTF8ToString(r)) _free(r) @@ -224,6 +231,10 @@ class IN3 { } } + getConfig() { + return this.execLocal('in3_getConfig', []) + } + /** * sends one or a multiple requests. * if the request is a array the response will be a array as well. @@ -484,6 +495,11 @@ IN3.setStorage = function (fn) { in3w.in3_cache = fn } +// deletes the cache +IN3.clearStorage = function () { // same as 'in3_cacheClear' + if (in3w.in3_cache.clear) in3w.in3_cache.clear() +} + IN3.freeAll = function () { Object.keys(clients).forEach(_ => clients[_].free()) } diff --git a/wasm/src/in3_util.js b/wasm/src/in3_util.js index 3ddf7f1d6..7c963c4e7 100644 --- a/wasm/src/in3_util.js +++ b/wasm/src/in3_util.js @@ -149,21 +149,33 @@ function toBigInt(val) { return BigInt(toHex(val)) } -function keccak(val) { +function keccak(val) { // shortcut for the 'keccak' rpc-call if (!val) return val val = toUint8Array(val) - return toBuffer(call_buffer('hash_keccak', 32, val, val.byteLength)) + return toBuffer(call_buffer('hash_keccak', 32, val, val.byteLength)) // keccak is also a alias for 'web3_sha3' +} + +function sha256(val) { + if (!val) return val + val = toUint8Array(val) + return toBuffer(call_buffer('hash_sha256', 32, val, val.byteLength)) // shortcut for 'sha256' } function toChecksumAddress(val, chainId = 0) { if (!val) return val - return call_string('to_checksum_address', toUint8Array(val, 20), chainId); + return call_string('to_checksum_address', toUint8Array(val, 20), chainId); // shortcut for 'in3_checksumAddress' } function private2address(pk) { if (!pk) return pk pk = toUint8Array(pk) - return toChecksumAddress(call_buffer('private_to_address', 20, pk, pk.byteLength)) + return toChecksumAddress(call_buffer('private_to_address', 20, pk, pk.byteLength)) // alias for 'in3_pk2address' +} + +function private2public(pk) { + if (!pk) return pk + pk = toUint8Array(pk) + return call_buffer('private_to_public', 64, pk, pk.byteLength) // alias for 'in3_pk2public' } function checkAddressChecksum(ad, chain = 0) { @@ -177,7 +189,7 @@ function abiEncode(sig, ...params) { ? convert(Object.values(a)) : toHex(a) try { - return call_string('wasm_abi_encode', sig, JSON.stringify(convert(params))) + return call_string('wasm_abi_encode', sig, JSON.stringify(convert(params))) // shortcut for 'in3_abiDecode'-method } catch (x) { throw new Error("Error trying to abi encode '" + sig + '": ' + x.message + ' with ' + JSON.stringify(params)) @@ -200,7 +212,7 @@ function abiDecode(sig, data) { else return [] } try { - let res = JSON.parse(call_string('wasm_abi_decode', sig, data, data.byteLength)) + let res = JSON.parse(call_string('wasm_abi_decode', sig, data, data.byteLength)) // shortcut for 'in3_abiEncode'-method return allowOne ? convertType(res, types[0]) : convertTypes(types, res) } catch (x) { throw new Error('Error decoding ' + sig + ' with ' + toHex(data) + ' : ' + x.message) @@ -209,11 +221,7 @@ function abiDecode(sig, data) { function convertType(val, t) { const isArray = t.indexOf('[') - if (isArray >= 0) { - t = t.substr(0, isArray) - if (t !== 'string' && t != 'bytes') - return val ? val.map(_ => convertType(_, t)) : [] - } + if (isArray >= 0) return val ? val.map(_ => convertType(_, t.substr(0, isArray))) : [] if (t.startsWith('(')) return convertTypes(splitTypes(t), val) switch (t) { @@ -339,10 +347,9 @@ function toNumber(val) { case 'string': return parseInt(val) case 'undefined': - case 'null': - return 0 + return undefined default: - if (!val) return 0 + if (!val) return val if (val.readBigInt64BE) //nodejs Buffer return val.length == 0 ? 0 : parseInt(toMinHex(val)) else if (val.redIMul) @@ -573,12 +580,14 @@ const util = { padStart, padEnd, keccak, + sha256, toChecksumAddress, abiEncode, abiDecode, ecSign, splitSignature, private2address, + private2public, soliditySha3, randomBytes, createSignatureHash, diff --git a/wasm/src/index.d.ts b/wasm/src/index.d.ts index 2fb960da1..62c20b4d4 100644 --- a/wasm/src/index.d.ts +++ b/wasm/src/index.d.ts @@ -755,6 +755,12 @@ export declare interface Utils { */ keccak(data: BufferType | Data): BufferType + /** + * calculates the sha256 hash for the given data. + * @param data the data as Uint8Array or hex data. + */ + sha256(data: BufferType | Data): BufferType + /** * returns a Buffer with strong random bytes. * Thsi will use the browsers crypto-module or in case of nodejs use the crypto-module there. @@ -820,4 +826,10 @@ export declare interface Utils { * @param pk the private key. */ private2address(pk: Hex | BufferType): Address + + /** + * generates the public address (64 bytes) from the private key + * @param pk the raw private key + */ + private2public(pk: Hex | BufferType): BufferType } diff --git a/wasm/src/modules/btc.d.ts b/wasm/src/modules/btc.d.ts index 03a116e5a..d97b977ce 100644 --- a/wasm/src/modules/btc.d.ts +++ b/wasm/src/modules/btc.d.ts @@ -203,6 +203,9 @@ export declare interface BtcAPI { /** retrieves the serialized block (bytes) including all transactions */ getBlockBytes(blockHash: Hash): Promise + + /** Returns the number of blocks in the longest blockchain. */ + getBlockCount(): Promise } /** diff --git a/wasm/src/modules/btc.js b/wasm/src/modules/btc.js index 15d9cae16..fd0ed7056 100644 --- a/wasm/src/modules/btc.js +++ b/wasm/src/modules/btc.js @@ -77,6 +77,12 @@ class BtcAPI { } + getBlockCount() { + return this.client.sendRPC('getblockcount', []) + .then(response => response || Promise.reject(new Error(response.error))) + } + + } diff --git a/wasm/src/modules/eth.d.ts b/wasm/src/modules/eth.d.ts index 568bc549d..cd9431849 100644 --- a/wasm/src/modules/eth.d.ts +++ b/wasm/src/modules/eth.d.ts @@ -257,7 +257,55 @@ export interface AccountAPI { * This method returns address of the pk * @param pk */ - add(pk: string | BufferType): Promise + add(pk: string | BufferType): string + + /** + * decrypts a JSON Keystore file as defined in the Web3 Secret Storage Definition . The result is the raw private key. + * @param keystore the keystore data + * @param passphrase the passphrase + */ + decryptKeystore(keystore: any, passphrase: string): BufferType + + /** + * adds a key from a JSON Keystore file as defined in the Web3 Secret Storage Definition . This method returns address of the pk. + * @param keystore the keystore data + * @param passphrase the passphrase + */ + addKeyStore(keystore: any, passphrase: string): String + + /** + * recovers a ecdsa signature. + * @param msg the message + * @param sig the signature (must be 65bytes in hex or Buffer) + * @param msgtype the type (raw : the message must be hashed first (default), 'hash' the message is already a 32byte hash, 'eth_sign' - the message will be hashed with EthereumSignedMessage-Prefix) + */ + ecrecover(msg: string | BufferType, sig: string | BufferType, msgtype?: 'eth_sign' | 'raw' | 'hash'): { + address: string, + publicKey: string + } + + /** + * creates a signature with a previously registered signer based on the data + * @param msg the message to sign + * @param account the address of the account ( if null, the first available account is used ). If the account is a 32byte hex or buffer, it it will be used as raw private key. + * @param msgtype the type (raw : the message must be hashed first (default), 'hash' the message is already a 32byte hash, 'eth_sign' - the message will be hashed with EthereumSignedMessage-Prefix) + */ + signData(msg: string | BufferType, account: string | BufferType, msgtype?: 'eth_sign' | 'raw' | 'hash'): Promise + + + /** + * creates a signature with a previously registered signer based on the data + * @param msg the message to sign + * @param account the address of the account ( if null, the first available account is used ). If the account is a 32byte hex or buffer, it it will be used as raw private key. + * @param msgtype the type (raw : the message must be hashed first (default), 'hash' the message is already a 32byte hash, 'eth_sign' - the message will be hashed with EthereumSignedMessage-Prefix) + */ + signRawTx(msg: string | BufferType, account: string | BufferType, msgtype?: 'eth_sign' | 'raw' | 'hash'): Promise + + /** + * prepares a Transaction by creating a unsigned raw transaction. + * @param tx the tx parameter + */ + prepareTx(tx: TxRequest): Promise } @@ -462,6 +510,12 @@ export interface EthAPI { * Returns the number of uncles in a block from a block matching the given block hash. */ getUncleCountByBlockNumber(block: BlockType): Promise; + + /** + * adds a filter for pending transaction (only available for local rpc) + */ + newPendingFilter(): Promise; + /** * Creates a filter in the node, to notify when a new block arrives. To check if the state has changed, call eth_getFilterChanges. */ @@ -496,10 +550,14 @@ export interface EthAPI { /** * Returns the value in wei as hexstring. */ - toWei(value: string, unit: string): string; + toWei(value: string, unit?: string): string; /** - * Returns the state of the underlying node. - */ + * Returns a formated String in the specified unit (or eth if not specified). If digits are specified, the number of digits behind the comma can be limited. + */ + fromWei(value: string, unit?: string, digits?: number): string; + /** + * Returns the state of the underlying node. + */ syncing(): Promisewait); sb_add_chars(sb, ",\"payload\":"); - sb_add_chars(sb, request->payload); + sb_add_chars(sb, (request->payload && strlen(request->payload))?request->payload:"null"); sb_add_chars(sb, ",\"method\":\""); sb_add_chars(sb, request->method); sb_add_chars(sb, "\",\"urls\":["); @@ -394,6 +395,20 @@ uint8_t* EMSCRIPTEN_KEEPALIVE hash_keccak(uint8_t* data, int len) { return result; } +uint8_t* EMSCRIPTEN_KEEPALIVE hash_sha256(uint8_t* data, int len) { + uint8_t* result = malloc(32); + if (result) { + SHA256_CTX c; + sha256_Init(&c); + sha256_Update(&c, data, len); + sha256_Final(&c, result); + } + else + in3_set_error("malloc failed"); + + return result; +} + char* EMSCRIPTEN_KEEPALIVE to_checksum_address(address_t adr, int chain_id) { char* result = malloc(43); if (!result) return err_string("malloc failed"); @@ -496,11 +511,32 @@ uint8_t* EMSCRIPTEN_KEEPALIVE private_to_address(bytes32_t prv_key) { return dst; } +/** private key to address */ +uint8_t* EMSCRIPTEN_KEEPALIVE private_to_public(bytes32_t prv_key) { + uint8_t* dst = malloc(64); + uint8_t public_key[65], sdata[32]; + ecdsa_get_public_key65(&secp256k1, prv_key, public_key); + memcpy(dst, public_key + 1, 64); + return dst; +} + /** signs the given data */ uint8_t* EMSCRIPTEN_KEEPALIVE ec_sign(bytes32_t pk, d_signature_type_t type, uint8_t* data, int len, bool adjust_v) { uint8_t* dst = malloc(65); int error = -1; switch (type) { + case SIGN_EC_PREFIX: { + bytes32_t hash; + struct SHA3_CTX kctx; + sha3_256_Init(&kctx); + const char* PREFIX = "\x19" "Ethereum Signed Message:\n"; + sha3_Update(&kctx, (uint8_t*) PREFIX, strlen(PREFIX)); + sha3_Update(&kctx, hash, sprintf((char*)hash,"%d", len) ); + if (len) sha3_Update(&kctx, data, len); + keccak_Final(&kctx, hash); + error = ecdsa_sign_digest(&secp256k1, pk, hash, dst, dst + 64, NULL); + break; + } case SIGN_EC_RAW: error = ecdsa_sign_digest(&secp256k1, pk, data, dst, dst + 64, NULL); break; diff --git a/wasm/test/package.json b/wasm/test/package.json index a7c7edaec..780218146 100644 --- a/wasm/test/package.json +++ b/wasm/test/package.json @@ -54,7 +54,8 @@ "eth-lib": "^0.2.8", "in3-common": "^2.0.3-RC1", "ts-node": "^8.10.2", - "typescript": "^3.9.5" + "typescript": "^3.9.5", + "yaml": "^1.10.2" }, "keywords": [ "ethereum", @@ -64,4 +65,4 @@ "client", "jsonrpc" ] -} \ No newline at end of file +} diff --git a/wasm/test/testEthApi.js b/wasm/test/testEthApi.js index 190cedf64..7a5f188ba 100644 --- a/wasm/test/testEthApi.js +++ b/wasm/test/testEthApi.js @@ -470,6 +470,12 @@ describe('EthAPI-Tests', () => { assert.equal(val, "0x01159183c4793db800") }) + it('eth.fromWei()', async () => { + let client = createClient() + const val = client.eth.fromWei('1450000000000000000', 'eth') + assert.equal(val, "1.45") + }) + it('eth.newFilter()', async () => { mockResponse('eth_blockNumber', '0x8e4daa') let client = createClient() diff --git a/wasm/test/testUtil.js b/wasm/test/testUtil.js index 47254579d..41a317b10 100644 --- a/wasm/test/testUtil.js +++ b/wasm/test/testUtil.js @@ -163,8 +163,8 @@ describe('Util-Tests', () => { assert.equal(0, IN3.util.toNumber(false)) assert.equal(65535, IN3.util.toNumber(65535n)) assert.equal(65535, IN3.util.toNumber("65535")) - assert.equal(0, IN3.util.toNumber(undefined)) - assert.equal(0, IN3.util.toNumber(null)) + assert.equal(undefined, IN3.util.toNumber(undefined)) + assert.equal(null, IN3.util.toNumber(null)) assert.equal(97, IN3.util.toNumber(Buffer.from('a', 'utf8'))) assert.equal(255, IN3.util.toNumber("0xff")) assert.Throw(() => IN3.util.toNumber({}))