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|...|