diff --git a/.github/workflows/build-ton-linux-x86-64-werror.yml b/.github/workflows/build-ton-linux-x86-64-werror.yml new file mode 100644 index 000000000..84e76ea98 --- /dev/null +++ b/.github/workflows/build-ton-linux-x86-64-werror.yml @@ -0,0 +1,43 @@ +name: Ubuntu TON -Werror build (shared, x86-64) + +on: [push, pull_request, workflow_dispatch, workflow_call] + +jobs: + build: + runs-on: ubuntu-24.04 + + steps: + - name: Check out repository + uses: actions/checkout@v3 + with: + submodules: 'recursive' + + - name: Date Stamp + shell: bash + id: date-stamp + run: | + echo "timestamp=$(date -u "+%Y%m%d%H%M_%S")" >> "$GITHUB_OUTPUT" + + - name: Install system libraries + run: | + sudo apt-get update + sudo apt-get install -y build-essential git cmake ninja-build zlib1g-dev libsecp256k1-dev libmicrohttpd-dev libsodium-dev liblz4-dev libjemalloc-dev + + - name: Install Clang 21 + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 21 clang + + # TODO: Preserve ccache cache between runs once we get rid of warning suppression mappings. + + - name: Build TON + run: | + cmake -S . -B build -G Ninja \ + -DCMAKE_C_COMPILER=clang-21 \ + -DCMAKE_CXX_COMPILER=clang++-21 \ + -DCMAKE_BUILD_TYPE=Release \ + -DTON_USE_JEMALLOC=ON \ + -DTON_USE_LLD=On \ + -DTON_WERROR_BUILD=On + ninja -C build diff --git a/.gitignore b/.gitignore index 29ef00d32..4988f0fe3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ parsetab.py parsetab.pyc tdutils/generate/auto/ tl/generate/auto/ +tl/generate/scheme/*.tlo compile_commands.json crypto/block/block-auto.cpp crypto/block/block-auto.h diff --git a/.gitmodules b/.gitmodules index f0e34181f..a66b26c5d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,6 +7,7 @@ [submodule "third-party/abseil-cpp"] path = third-party/abseil-cpp url = https://github.com/abseil/abseil-cpp.git + branch = 20250814.1 [submodule "third-party/libraptorq"] path = third-party/libraptorq url = https://github.com/ton-blockchain/libRaptorQ @@ -20,6 +21,9 @@ path = third-party/secp256k1 url = https://github.com/bitcoin-core/secp256k1 branch = v0.3.2 +[submodule "third-party/tl-parser"] + path = third-party/tl-parser + url = https://github.com/ton-blockchain/tl-parser.git [submodule "third-party/pybind11"] path = third-party/pybind11 url = https://github.com/pybind/pybind11.git diff --git a/CMake/AddCXXCompilerFlag.cmake b/CMake/AddCXXCompilerFlag.cmake deleted file mode 100644 index 6fb615a1f..000000000 --- a/CMake/AddCXXCompilerFlag.cmake +++ /dev/null @@ -1,74 +0,0 @@ -# - Adds a compiler flag if it is supported by the compiler -# -# This function checks that the supplied compiler flag is supported and then -# adds it to the corresponding compiler flags -# -# add_cxx_compiler_flag( []) -# -# - Example -# -# include(AddCXXCompilerFlag) -# add_cxx_compiler_flag(-Wall) -# add_cxx_compiler_flag(-no-strict-aliasing RELEASE) -# Requires CMake 2.6+ - -if (__add_cxx_compiler_flag) - return() -endif() -set(__add_cxx_compiler_flag INCLUDED) - -include(CheckCXXCompilerFlag) - -function(mangle_compiler_flag FLAG OUTPUT) - string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG) - string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG}) - string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG}) - string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG}) - set(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE) -endfunction(mangle_compiler_flag) - -function(add_cxx_compiler_flag FLAG) - string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG}) - mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME) - if (DEFINED CMAKE_REQUIRED_FLAGS) - set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}") - else() - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - endif() - check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME}) - if (DEFINED OLD_CMAKE_REQUIRED_FLAGS) - set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}") - else() - unset(CMAKE_REQUIRED_FLAGS) - endif() - if (${MANGLED_FLAG_NAME}) - set(VARIANT ${ARGV1}) - if (ARGV1) - string(TOUPPER "_${VARIANT}" VARIANT) - endif() - set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE) - endif() -endfunction() - -function(add_required_cxx_compiler_flag FLAG) - string(REPLACE "-Wno-" "-W" MAIN_FLAG ${FLAG}) - mangle_compiler_flag("${MAIN_FLAG}" MANGLED_FLAG_NAME) - set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}") - check_cxx_compiler_flag("${MAIN_FLAG}" ${MANGLED_FLAG_NAME}) - set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}") - if (${MANGLED_FLAG_NAME}) - set(VARIANT ${ARGV1}) - if (ARGV1) - string(TOUPPER "_${VARIANT}" VARIANT) - endif() - set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE) - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE) - set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE) - set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE) - else() - message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler") - endif() -endfunction() diff --git a/CMakeLists.txt b/CMakeLists.txt index f43a4e9d3..a57502559 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,16 @@ set(CMAKE_CXX_EXTENSIONS FALSE) set(CMAKE_COLOR_DIAGNOSTICS TRUE) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(GCC 1) +elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set(CLANG 1) +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + set(INTEL 1) +elseif (NOT MSVC) + message(FATAL_ERROR "Compiler isn't supported") +endif() + #BEGIN internal option(BUILD_SHARED_LIBS "Use \"ON\" to build shared libraries instead of static where it's not specified (not recommended)" OFF) option(USE_EMSCRIPTEN "Use \"ON\" for config building wasm." OFF) @@ -80,16 +90,29 @@ if ((ARCHITECTURE MATCHES "arm64") AND (CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND endif() #END M1 support +if (CLANG AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "20") + add_compile_options(--warning-suppression-mappings=${CMAKE_SOURCE_DIR}/suppression_mappings.txt) +endif() + +option(TON_WERROR_BUILD "Use -Werror" OFF) + +# If we want to check -Werror build in CI, we want to use it for third party projects as well but we +# don't want to force our warning options on them. +if (TON_WERROR_BUILD) + add_compile_options(-Werror) +endif() + if (TON_USE_ABSEIL) message("Add abseil-cpp") - set(ABSL_PROPAGATE_CXX_STD TRUE) - add_subdirectory(third-party/abseil-cpp EXCLUDE_FROM_ALL) + function(abseil_scope) + set(ABSL_PROPAGATE_CXX_STD TRUE) + set(CMAKE_POLICY_VERSION_MINIMUM "3.10") + add_subdirectory(third-party/abseil-cpp EXCLUDE_FROM_ALL) + endfunction() + abseil_scope() set(ABSL_FOUND 1) endif() -#add_subdirectory(third-party/libcuckoo EXCLUDE_FROM_ALL) -#add_subdirectory(third-party/junction EXCLUDE_FROM_ALL) - if (WIN32) message("Add wingetopt") function(wingetopt_scope) @@ -155,6 +178,9 @@ add_subdirectory(third-party/cppkafka) set(CPPKAFKA_INCLUDE_DIRECTORIES third-party/cppkafka/include) include_directories(${CPPKAFKA_INCLUDE_DIRECTORIES}) # I feel bad for doing this, could not manage to do the proper way +message("Add tl-parser") +add_subdirectory(third-party/tl-parser EXCLUDE_FROM_ALL) + message("Add ton") set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake" ${CMAKE_MODULE_PATH}) @@ -173,16 +199,6 @@ else() message(STATUS "Could NOT find ccache") endif() -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - set(GCC 1) -elseif (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - set(CLANG 1) -elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") - set(INTEL 1) -elseif (NOT MSVC) - message(FATAL_ERROR "Compiler isn't supported") -endif() - include(CheckCXXCompilerFlag) set(CMAKE_THREAD_PREFER_PTHREAD ON) @@ -289,33 +305,37 @@ set(TONLIB_COMPILE "0") set(TONLIB_COMPILE "1") #END tonlib -include(AddCXXCompilerFlag) if (MSVC) - add_cxx_compiler_flag("/experimental:external /external:anglebrackets /external:W0") -endif() -if (NOT MSVC) - add_cxx_compiler_flag("-Wall") - add_cxx_compiler_flag("-Wextra") -endif() - -add_cxx_compiler_flag("-Wimplicit-fallthrough=2") -add_cxx_compiler_flag("-Wpointer-arith") -add_cxx_compiler_flag("-Wcast-qual") -add_cxx_compiler_flag("-Wsign-compare") -add_cxx_compiler_flag("-Wduplicated-branches") -add_cxx_compiler_flag("-Wduplicated-cond") -add_cxx_compiler_flag("-Walloc-zero") -add_cxx_compiler_flag("-Wlogical-op") -add_cxx_compiler_flag("-Wno-tautological-compare") -add_cxx_compiler_flag("-Wpointer-arith") -add_cxx_compiler_flag("-Wvla") -add_cxx_compiler_flag("-Wnon-virtual-dtor") -add_cxx_compiler_flag("-Wno-unused-parameter") -add_cxx_compiler_flag("-Wconversion") -add_cxx_compiler_flag("-Wno-sign-conversion") -add_cxx_compiler_flag("-Qunused-arguments") -add_cxx_compiler_flag("-Wno-unused-private-field") -add_cxx_compiler_flag("-Wno-redundant-move") + add_compile_options(/experimental:external /external:anglebrackets /external:W0) +endif() + +if (GCC OR CLANG) + add_compile_options(-Wall) + add_compile_options(-Wextra) + + add_compile_options(-Wpointer-arith) + add_compile_options(-Wcast-qual) + add_compile_options(-Wsign-compare) + add_compile_options(-Wpointer-arith) + add_compile_options(-Wvla) + add_compile_options(-Wnon-virtual-dtor) + add_compile_options(-Wconversion) + + # TODO: Either justify all of these or remove. + add_compile_options(-Wno-tautological-compare) + add_compile_options(-Wno-unused-parameter) + add_compile_options(-Wno-sign-conversion) + add_compile_options(-Wno-unused-private-field) + add_compile_options(-Wno-redundant-move) + + if (GCC) + add_compile_options(-Wimplicit-fallthrough=2) + add_compile_options(-Wduplicated-branches) + add_compile_options(-Wduplicated-cond) + add_compile_options(-Walloc-zero) + add_compile_options(-Wlogical-op) + endif() +endif() if (GCC OR CLANG) if (CMAKE_BUILD_TYPE MATCHES "RelWithDebInfo") @@ -325,30 +345,19 @@ if (GCC OR CLANG) endif() endif() -#add_cxx_compiler_flag("-Wno-unused-function") -#add_cxx_compiler_flag("-Wno-unused-variable") -#add_cxx_compiler_flag("-Wno-shorten-64-to-32") -#add_cxx_compiler_flag("-Werror") - -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem /usr/include/c++/v1") -if (CLANG) - #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") -endif() if (TON_USE_ASAN) - add_cxx_compiler_flag("-fsanitize=address") + add_compile_options(-fsanitize=address) + add_link_options(-fsanitize=address) add_definitions(-DTD_USE_ASAN=1) endif() if (TON_USE_TSAN) - add_cxx_compiler_flag("-fsanitize=thread") + add_compile_options(-fsanitize=thread) + add_link_options(-fsanitize=thread) endif() if (TON_USE_UBSAN) - add_cxx_compiler_flag("-fsanitize=undefined") + add_compile_options(-fsanitize=undefined) + add_link_options(-fsanitize=undefined) endif() -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak") -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -finstrument-functions") #Compilation database set(CMAKE_EXPORT_COMPILE_COMMANDS 1) @@ -368,6 +377,10 @@ if (NOT LATEX_FOUND) endif() #END internal +if (NOT OPENSSL_FOUND) + find_package(OpenSSL REQUIRED) +endif() + function(target_link_libraries_system target) set(libs ${ARGN}) foreach(lib ${libs}) @@ -475,10 +488,6 @@ target_link_libraries(test-fift PRIVATE fift-lib) add_executable(test-tdutils test/test-td-main.cpp ${TDUTILS_TEST_SOURCE}) target_link_libraries(test-tdutils PRIVATE tdutils ${CMAKE_THREAD_LIBS_INIT} memprof ${JEMALLOC_LIBRARIES}) -#target_link_libraries_system(test-tdutils absl::base absl::container absl::hash ) -#target_link_libraries_system(test-tdutils libcuckoo) -#target_include_directories(test-tdutils PRIVATE SYSTEM ${JUNCTION_ALL_INCLUDE_DIRS}) -#target_link_libraries(test-tdutils PRIVATE ${JUNCTION_ALL_LIBRARIES}) add_executable(test-tdactor test/test-td-main.cpp ${TDACTOR_TEST_SOURCE}) target_link_libraries(test-tdactor PRIVATE tdactor ${CMAKE_THREAD_LIBS_INIT}) @@ -559,7 +568,7 @@ if (HAS_PARENT) ${ED25519_TEST_SOURCE} ${TONDB_TEST_SOURCE} ${BIGNUM_TEST_SOURCE} - ${CELLS_TEST_SOURCE} # ${TONVM_TEST_SOURCE} ${FIFT_TEST_SOURCE} ${TONLIB_ONLINE_TEST_SOURCE} + ${CELLS_TEST_SOURCE} PARENT_SCOPE) endif() add_library(all_tests INTERFACE) diff --git a/Changelog.md b/Changelog.md index 8c6ed697d..b5ae89f86 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,13 @@ +## 2025.11 Update + +1. [TVM version v12](./doc/GlobalVersions.md) update: [forbid unused high bits in extra_flags](https://github.com/ton-blockchain/TEPs/pull/503/commits/d949d70d5a69026d273cbbc07653d12c4373117a), [bounce extra_flags equal to initial message extra_flags](https://github.com/ton-blockchain/TEPs/pull/503/commits/d33ff342d69de04f1c33d11360dcf06b63a6c21e), [new TVM opcodes](https://github.com/ton-blockchain/ton/commit/ecd8fbb833c408eb34ec1aa4516e9e4344b54a22). +2. Abseil upgrade +3. Improvements in node synchronisation +4. Fixing rare ArchiveManager issues +5. Various improvements in logging, builds, DHT node behavior, private net launching, failure handlers. + +Besides the work of the core team, this update is based on the efforts of the @Lapo4kaKek and [Vahagn x.com/vah_13](https://x.com/vah_13). + ## 2025.10 Update 1. [TVM version v12](./doc/GlobalVersions.md): full bounces, new `BTOS` and `HASHBU` instuctions, limit on contract size in masterchain. diff --git a/adnl/adnl-packet.cpp b/adnl/adnl-packet.cpp index b12493e77..7159fbdde 100644 --- a/adnl/adnl-packet.cpp +++ b/adnl/adnl-packet.cpp @@ -56,7 +56,7 @@ td::Result AdnlPacket::create(tl_object_ptraddress_))); + TRY_RESULT(addr_list, AdnlAddressList::create(std::move(packet->priority_address_))); R.priority_addr_ = std::move(addr_list); } if (R.flags_ & Flags::f_seqno) { diff --git a/catchain/CMakeLists.txt b/catchain/CMakeLists.txt index 6175cb86b..46b7c1342 100644 --- a/catchain/CMakeLists.txt +++ b/catchain/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(CATCHAIN_SOURCE catchain-received-block.cpp #catchain-receiver-fork.cpp diff --git a/create-hardfork/CMakeLists.txt b/create-hardfork/CMakeLists.txt index daa7f2e73..48bbc1e0f 100644 --- a/create-hardfork/CMakeLists.txt +++ b/create-hardfork/CMakeLists.txt @@ -1,8 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - - set(CREATE_HARDFORK_SOURCE create-hardfork.cpp ) diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index cfc4d7a42..d5be678fd 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(TON_CRYPTO_CORE_SOURCE Ed25519.cpp common/bigint.cpp diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index d3fbd26aa..37e3cab2b 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -148,7 +148,7 @@ bool Account::check_addr_rewrite_length(int length) const { /** * Parses anycast data of the account address. - * + * * Initializes addr_rewrite. * * @param cs The cell slice containing partially-parsed account address. @@ -450,7 +450,7 @@ bool Account::unpack(Ref shard_account, ton::UnixTime now, bool s return false; } if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { shard_account->print_rec(sb, 2); block::gen::t_ShardAccount.print(sb, shard_account); }; @@ -906,7 +906,7 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* return false; } if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "unpacking inbound message for a new transaction: "; block::gen::t_Message_Any.print_ref(sb, in_msg); load_cell_slice(in_msg).print_rec(sb); @@ -927,9 +927,9 @@ bool Transaction::unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* td::RefInt256 ihr_fee; if (cfg->global_version >= 12) { ihr_fee = td::zero_refint(); - td::RefInt256 extra_flags = tlb::t_Grams.as_integer(in_msg_info.extra_flags); - new_bounce_format = extra_flags->get_bit(0); - new_bounce_format_full_body = extra_flags->get_bit(1); + in_msg_extra_flags = tlb::t_Grams.as_integer(in_msg_info.extra_flags); + new_bounce_format = in_msg_extra_flags->get_bit(0); + new_bounce_format_full_body = in_msg_extra_flags->get_bit(1); } else { // Legacy: extra_flags was previously ihr_fee ihr_fee = tlb::t_Grams.as_integer(in_msg_info.extra_flags); @@ -1412,7 +1412,7 @@ td::uint64 Transaction::gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt gas_limit_overridden = true; // Same as ComputePhaseConfig::gas_bought for, but with other gas_limit and max_gas_threshold auto gas_limit = new_limit.value(); - LOG(INFO) << "overridding gas limit for account " << account.workchain << ":" << account.addr.to_hex() << " to " + LOG(DEBUG) << "overridding gas limit for account " << account.workchain << ":" << account.addr.to_hex() << " to " << gas_limit; auto max_gas_threshold = compute_max_gas_threshold(cfg.gas_price256, gas_limit, cfg.flat_gas_limit, cfg.flat_gas_price); @@ -1785,7 +1785,7 @@ bool Transaction::run_precompiled_contract(const ComputePhaseConfig& cfg, precom cp.gas_used = gas_usage; cp.accepted = result.accepted; cp.success = (cp.accepted && result.committed); - LOG(INFO) << "Running precompiled smart contract " << impl.get_name() << ": exit_code=" << result.exit_code + LOG(DEBUG) << "Running precompiled smart contract " << impl.get_name() << ": exit_code=" << result.exit_code << " accepted=" << result.accepted << " success=" << cp.success << " gas_used=" << gas_usage << " time=" << time_tvm.real << "s cpu_time=" << time_tvm.cpu; if (cp.accepted & use_msg_state) { @@ -1802,7 +1802,7 @@ bool Transaction::run_precompiled_contract(const ComputePhaseConfig& cfg, precom cp.actions = impl.get_c5(); int out_act_num = output_actions_count(cp.actions); if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "new smart contract data: "; bool can_be_special = true; load_cell_slice_special(cp.new_data, can_be_special).print_rec(sb); @@ -1953,7 +1953,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { // Contract is marked as precompiled in global config, but implementation is not available // In this case we run TVM and override gas_used - LOG(INFO) << "Unknown precompiled contract (code_hash=" << new_code->get_hash().to_hex() + LOG(DEBUG) << "Unknown precompiled contract (code_hash=" << new_code->get_hash().to_hex() << ", gas_usage=" << gas_usage << "), running VM"; long long limit = account.is_special ? cfg.special_gas_limit : cfg.gas_limit; gas = vm::GasLimits{limit, limit, gas.gas_credit ? limit : 0}; @@ -2029,9 +2029,9 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { return false; } } - LOG(INFO) << "steps: " << vm.get_steps_count() << " gas: used=" << gas.gas_consumed() << ", max=" << gas.gas_max + LOG(DEBUG) << "steps: " << vm.get_steps_count() << " gas: used=" << gas.gas_consumed() << ", max=" << gas.gas_max << ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit; - LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success + LOG(DEBUG) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success << ", time=" << time_tvm.real << "s, cpu_time=" << time_tvm.cpu; if (logger != nullptr) { cp.vm_log = logger->get_log(); @@ -2041,7 +2041,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { cp.actions = vm.get_committed_state().c5; // c5 -> action list int out_act_num = output_actions_count(cp.actions); if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "new smart contract data: "; bool can_be_special = true; load_cell_slice_special(cp.new_data, can_be_special).print_rec(sb); @@ -2114,7 +2114,7 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { return -1; } // Rollback changes to state, fail action phase - LOG(INFO) << "Account state size exceeded limits: " << S.move_as_error(); + LOG(DEBUG) << "Account state size exceeded limits: " << S.move_as_error(); new_account_storage_stat = {}; new_code = old_code; new_data = old_data; @@ -2154,7 +2154,7 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { ap.result_code = 33; // too many actions ap.result_arg = n; ap.action_list_invalid = true; - LOG(DEBUG) << "action list too long: more than " << cfg.max_actions << " actions"; + LOG(DEBUG) << " action list too long: more than " << cfg.max_actions << " actions"; return true; } } @@ -2186,11 +2186,12 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { } ap.result_code = 34; // action #i invalid or unsupported ap.action_list_invalid = true; - LOG(DEBUG) << "invalid action " << ap.result_arg << " found while preprocessing action list: error code " + LOG(DEBUG) << " invalid action " << ap.result_arg << " found while preprocessing action list: error code " << ap.result_code; return true; } } + ap.valid = true; for (int i = n - 1; i >= 0; --i) { if(ap.action_list[i].is_null()) { @@ -2208,11 +2209,11 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { err_code = try_action_set_code(cs, ap, cfg); break; case block::gen::OutAction::action_send_msg: - err_code = try_action_send_msg(cs, ap, cfg); + err_code = try_action_send_msg(cs, ap, cfg, 0, ap.result_arg); if (err_code == -2) { - err_code = try_action_send_msg(cs, ap, cfg, 1); + err_code = try_action_send_msg(cs, ap, cfg, 1, ap.result_arg); if (err_code == -2) { - err_code = try_action_send_msg(cs, ap, cfg, 2); + err_code = try_action_send_msg(cs, ap, cfg, 2, ap.result_arg); } } break; @@ -2639,7 +2640,7 @@ bool Transaction::check_rewrite_dest_addr(Ref& dest_addr, const A * Returns -2 if the action should be attempted again. */ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, const ActionPhaseConfig& cfg, - int redoing) { + int redoing, int action_position) { block::gen::OutAction::Record_action_send_msg act_rec; // mode: // +128 = attach all remaining balance @@ -2669,6 +2670,24 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, } return error_code; }; + auto log_fail = [&](int code, const std::string& desc, bool ignored) { + LOG(INFO) << "{ \"type\":\"try_action_send_msg_fail\", \"in_msg_hash\":\"" << in_msg->get_hash().to_hex() + << "\", \"action_id\":" << action_position + << ", \"description\":\"" << desc + << "\", \"exit_code\":" << code + << ", \"action_fail_ignored\":" << (ignored ? "true" : "false") + << " }"; + }; + auto log_fail_req = [&](int code, const std::string& desc, const td::RefInt256& requested, const td::RefInt256& remaining, bool ignored) { + LOG(INFO) << "{ \"type\":\"try_action_send_msg_fail\", \"in_msg_hash\":\"" << in_msg->get_hash().to_hex() + << "\", \"action_id\":" << action_position + << ", \"description\":\"" << desc + << "\", \"exit_code\":" << code + << ", \"action_fail_ignored\":" << (ignored ? "true" : "false") + << ", \"requested\":\"" << requested << "\"" + << ", \"remaining\":\"" << remaining << "\"" + << " }"; + }; // try to parse suggested message in act_rec.out_msg td::RefInt256 fwd_fee, ihr_fee; block::gen::MessageRelaxed::Record msg; @@ -2744,6 +2763,13 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, if (cfg.disable_ihr_flag) { info.ihr_disabled = true; } + if (cfg.global_version >= 12) { + td::RefInt256 extra_flags = tlb::t_Grams.as_integer(info.extra_flags); + if (td::cmp(extra_flags & td::make_refint(3), extra_flags) != 0) { + LOG(DEBUG) << "invalid extra_flags in a proposed outbound message"; + return check_skip_invalid(45); + } + } } // set created_at and created_lt to correct values info.created_at = now; @@ -2753,23 +2779,22 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, // have to check source address // it must be either our source address, or empty if (!check_replace_src_addr(info.src)) { - LOG(DEBUG) << "invalid source address in a proposed outbound message"; + log_fail(35, "invalid source address in a proposed outbound message", false); return 35; // invalid source address } bool to_mc = false; if (!check_rewrite_dest_addr(info.dest, cfg, &to_mc, !cfg.disable_anycast)) { - LOG(DEBUG) << "invalid destination address in a proposed outbound message"; + log_fail(36, "invalid destination address in a proposed outbound message", skip_invalid); return check_skip_invalid(36); // invalid destination address } if (!ext_msg && cfg.extra_currency_v2) { CurrencyCollection value; if (!value.unpack(info.value)) { - LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message"; + log_fail(37, "invalid value:ExtraCurrencies in a proposed outbound message", skip_invalid); return check_skip_invalid(37); // invalid value:CurrencyCollection } if (!CurrencyCollection::remove_zero_extra_currencies(value.extra, cfg.size_limits.max_msg_extra_currencies)) { - LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message: too many currencies (max " - << cfg.size_limits.max_msg_extra_currencies << ")"; + log_fail(44, "invalid value:ExtraCurrencies in a proposed outbound message: too many currencies", skip_invalid); // Dict should be valid, since it was checked in t_OutListNode.validate_ref, so error here means limit exceeded return check_skip_invalid(44); // invalid value:CurrencyCollection : too many extra currencies } @@ -2787,7 +2812,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, td::RefInt256 funds = ap.remaining_balance.grams; if (!ext_msg && !(act_rec.mode & 0x80) && !(act_rec.mode & 1)) { if (!block::tlb::t_CurrencyCollection.validate_csr(info.value)) { - LOG(DEBUG) << "invalid value:CurrencyCollection in proposed outbound message"; + log_fail(37, "invalid value:CurrencyCollection in proposed outbound message", skip_invalid); return check_skip_invalid(37); } block::CurrencyCollection value; @@ -2803,8 +2828,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, } new_funds -= ap.action_fine; if (new_funds->sgn() < 0) { - LOG(DEBUG) - << "not enough value to transfer with the message: all of the inbound message value has been consumed"; + log_fail(37, "not enough value to transfer with the message: all of the inbound message value has been consumed", skip_invalid); return check_skip_invalid(37); } } @@ -2841,17 +2865,19 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, } }; if (sstat.cells > max_cells && max_cells < cfg.size_limits.max_msg_cells) { - LOG(DEBUG) << "not enough funds to process a message (max_cells=" << max_cells << ")"; + // insufficient funds to process message cells + td::RefInt256 requested_cells_fine = td::make_refint((td::uint64)sstat.cells * fine_per_cell); + log_fail_req(40, "not enough funds to process a message", requested_cells_fine, ap.remaining_balance.grams, skip_invalid); collect_fine(); return check_skip_invalid(40); } if (sstat.bits > cfg.size_limits.max_msg_bits || sstat.cells > max_cells) { - LOG(DEBUG) << "message too large, invalid"; + log_fail(40, "message too large, invalid", skip_invalid); collect_fine(); return check_skip_invalid(40); } if (max_merkle_depth > max_allowed_merkle_depth) { - LOG(DEBUG) << "message has too big merkle depth, invalid"; + log_fail(40, "message has too big merkle depth, invalid", skip_invalid); collect_fine(); return check_skip_invalid(40); } @@ -2883,7 +2909,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, // check value, check/compute ihr_fees, fwd_fees // ... if (!block::tlb::t_CurrencyCollection.validate_csr(info.value)) { - LOG(DEBUG) << "invalid value:CurrencyCollection in proposed outbound message"; + log_fail(37, "invalid value:CurrencyCollection in proposed outbound message", skip_invalid); collect_fine(); return check_skip_invalid(37); } @@ -2917,8 +2943,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, req -= compute_phase->gas_fees; } if (!req.is_valid()) { - LOG(DEBUG) - << "not enough value to transfer with the message: all of the inbound message value has been consumed"; + log_fail(37, "not enough value to transfer with the message: all of the inbound message value has been consumed", skip_invalid); collect_fine(); return check_skip_invalid(37); } @@ -2933,8 +2958,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, req_grams_brutto += fees_total; } else if (req.grams < fees_total) { // receiver pays the fees (but cannot) - LOG(DEBUG) << "not enough value attached to the message to pay forwarding fees : have " << req.grams << ", need " - << fees_total; + log_fail(37, std::string("not enough value attached to the message to pay forwarding fees"), skip_invalid); collect_fine(); return check_skip_invalid(37); // not enough grams } else { @@ -2944,23 +2968,21 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, // check that we have at least the required value if (ap.remaining_balance.grams < req_grams_brutto) { - LOG(DEBUG) << "not enough grams to transfer with the message : remaining balance is " - << ap.remaining_balance.to_str() << ", need " << req_grams_brutto << " (including forwarding fees)"; + // insufficient contract grams + log_fail_req(37, "not enough grams to transfer with the message (including forwarding fees)", req_grams_brutto, ap.remaining_balance.grams, skip_invalid); collect_fine(); return check_skip_invalid(37); // not enough grams } if (cfg.extra_currency_v2 && !req.check_extra_currency_limit(cfg.size_limits.max_msg_extra_currencies)) { - LOG(DEBUG) << "too many extra currencies in the message : max " << cfg.size_limits.max_msg_extra_currencies; + log_fail(44, "too many extra currencies in the message", skip_invalid); return check_skip_invalid(44); // to many extra currencies } Ref new_extra; if (!block::sub_extra_currency(ap.remaining_balance.extra, req.extra, new_extra)) { - LOG(DEBUG) << "not enough extra currency to send with the message: " - << block::CurrencyCollection{0, req.extra}.to_str() << " required, only " - << block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " available"; + log_fail(38, "not enough extra currency to send with the message", skip_invalid); collect_fine(); return check_skip_invalid(38); // not enough (extra) funds } @@ -2985,11 +3007,12 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, CHECK(tlb::csr_pack(msg.info, info)); vm::CellBuilder cb; if (!tlb::type_pack(cb, block::gen::t_MessageRelaxed_Any, msg)) { - LOG(DEBUG) << "outbound message does not fit into a cell after rewriting"; if (redoing == 2) { + log_fail(39, "outbound message does not fit into a cell after rewriting", skip_invalid); collect_fine(); return check_skip_invalid(39); } + log_fail(-2, "outbound message does not fit into a cell after rewriting (will retry)", false); return -2; } @@ -3015,7 +3038,8 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, } else { // external messages also have forwarding fees if (ap.remaining_balance.grams < fwd_fee) { - LOG(DEBUG) << "not enough funds to pay for an outbound external message"; + // insufficient contract grams for external message fwd fee + log_fail_req(37, "not enough funds to pay for an outbound external message", fwd_fee, ap.remaining_balance.grams, skip_invalid); collect_fine(); return check_skip_invalid(37); // not enough grams } @@ -3029,11 +3053,12 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, CHECK(tlb::csr_pack(msg.info, erec)); vm::CellBuilder cb; if (!tlb::type_pack(cb, block::gen::t_MessageRelaxed_Any, msg)) { - LOG(DEBUG) << "outbound message does not fit into a cell after rewriting"; if (redoing == 2) { + log_fail(39, "outbound message does not fit into a cell after rewriting", skip_invalid); collect_fine(); return check_skip_invalid(39); } + log_fail(-2, "outbound message does not fit into a cell after rewriting (will retry)", false); return -2; } @@ -3054,7 +3079,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, } if (!block::gen::t_Message_Any.validate_ref(new_msg)) { LOG(ERROR) << "generated outbound message is not a valid (Message Any) according to automated check"; - FLOG(INFO) { + FLOG(DEBUG) { block::gen::t_Message_Any.print_ref(sb, new_msg); vm::load_cell_slice(new_msg).print_rec(sb); }; @@ -3062,7 +3087,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, return -1; } if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "converted outbound message: "; block::gen::t_Message_Any.print_ref(sb, new_msg); }; @@ -3413,15 +3438,16 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) { end_lt++; info.created_at = now; vm::CellBuilder cb; - CHECK(cb.store_long_bool(5, 4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool - && cb.append_cellslice_bool(info.src) // src:MsgAddressInt - && cb.append_cellslice_bool(info.dest) // dest:MsgAddressInt - && msg_balance.store(cb) // value:CurrencyCollection - && block::tlb::t_Grams.store_long(cb, 0) // extra_flags:(VarUInteger 16) - && block::tlb::t_Grams.store_long(cb, bp.fwd_fees) // fwd_fee:Grams - && cb.store_long_bool(info.created_lt, 64) // created_lt:uint64 - && cb.store_long_bool(info.created_at, 32) // created_at:uint32 - && cb.store_bool_bool(false)); // init:(Maybe ...) + CHECK(cb.store_long_bool(5, 4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + && cb.append_cellslice_bool(info.src) // src:MsgAddressInt + && cb.append_cellslice_bool(info.dest) // dest:MsgAddressInt + && msg_balance.store(cb) // value:CurrencyCollection + && block::tlb::t_Grams.store_integer_ref( + cb, in_msg_extra_flags & td::make_refint(3)) // extra_flags:(VarUInteger 16) + && block::tlb::t_Grams.store_long(cb, bp.fwd_fees) // fwd_fee:Grams + && cb.store_long_bool(info.created_lt, 64) // created_lt:uint64 + && cb.store_long_bool(info.created_at, 32) // created_at:uint32 + && cb.store_bool_bool(false)); // init:(Maybe ...) if (cb.can_extend_by(1 + body.size(), body.size_refs())) { // body:(Either X ^X) -> left X CHECK(cb.store_bool_bool(false) && cb.append_builder_bool(body)); @@ -3431,7 +3457,7 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) { } CHECK(cb.finalize_to(bp.out_msg)); if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "generated bounced message: "; block::gen::t_Message_Any.print_ref(sb, bp.out_msg); }; @@ -3524,7 +3550,7 @@ bool Transaction::compute_state(const SerializeConfig& cfg) { auto frozen_state = cb2.finalize(); frozen_hash = frozen_state->get_hash().bits(); if (verbosity >= 3 * 1) { // !!!DEBUG!!! - FLOG(INFO) { + FLOG(DEBUG) { sb << "freezing state of smart contract: "; block::gen::t_StateInit.print_ref(sb, frozen_state); CHECK(block::gen::t_StateInit.validate_ref(frozen_state)); @@ -3631,7 +3657,7 @@ bool Transaction::compute_state(const SerializeConfig& cfg) { new_storage_dict_hash = r_hash.move_as_ok(); } if (timer.elapsed() > 0.1) { - LOG(INFO) << "Compute used storage (2) took " << timer.elapsed() << "s"; + LOG(DEBUG) << "Compute used storage (2) took " << timer.elapsed() << "s"; } } else { new_storage_used = account.storage_used; @@ -3659,7 +3685,7 @@ bool Transaction::compute_state(const SerializeConfig& cfg) { CHECK(cb.append_cellslice_bool(new_storage)); new_total_state = cb.finalize(); if (verbosity > 2) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "new account state: "; block::gen::t_Account.print_ref(sb, new_total_state); }; @@ -3756,7 +3782,7 @@ bool Transaction::serialize(const SerializeConfig& cfg) { return false; } if (verbosity >= 3 * 1) { - FLOG(INFO) { + FLOG(DEBUG) { sb << "new transaction: "; block::gen::t_Transaction.print_ref(sb, root); vm::load_cell_slice(root).print_rec(sb); @@ -3765,7 +3791,7 @@ bool Transaction::serialize(const SerializeConfig& cfg) { if (!block::gen::t_Transaction.validate_ref(4096, root)) { LOG(ERROR) << "newly-generated transaction failed to pass automated validation:"; - FLOG(INFO) { + FLOG(DEBUG) { vm::load_cell_slice(root).print_rec(sb); block::gen::t_Transaction.print_ref(sb, root); }; @@ -3774,7 +3800,7 @@ bool Transaction::serialize(const SerializeConfig& cfg) { } if (!block::tlb::t_Transaction.validate_ref(4096, root)) { LOG(ERROR) << "newly-generated transaction failed to pass hand-written validation:"; - FLOG(INFO) { + FLOG(DEBUG) { vm::load_cell_slice(root).print_rec(sb); block::gen::t_Transaction.print_ref(sb, root); }; diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 8da7df3a5..753f9e36f 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -359,6 +359,7 @@ struct Transaction { bool bounce_enabled{false}; bool in_msg_extern{false}; gen::CommonMsgInfo::Record_int_msg_info in_msg_info; + td::RefInt256 in_msg_extra_flags = td::zero_refint(); bool new_bounce_format{false}; bool new_bounce_format_full_body{false}; bool use_msg_state{false}; @@ -440,7 +441,7 @@ struct Transaction { bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const; int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); int try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); - int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0); + int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0, int action_position = 0); int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); bool check_replace_src_addr(Ref& src_addr) const; bool check_rewrite_dest_addr(Ref& dest_addr, const ActionPhaseConfig& cfg, diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 082c2436c..0ddec0722 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1458,6 +1458,14 @@ x{FA44} @Defop REWRITESTDADDR x{FA45} @Defop REWRITESTDADDRQ x{FA46} @Defop REWRITEVARADDR x{FA47} @Defop REWRITEVARADDRQ +x{FA48} @Defop LDSTDADDR +x{FA49} @Defop LDSTDADDRQ +x{FA50} @Defop LDOPTSTDADDR +x{FA51} @Defop LDOPTSTDADDRQ +x{FA52} @Defop STSTDADDR +x{FA53} @Defop STSTDADDRQ +x{FA54} @Defop STOPTSTDADDR +x{FA55} @Defop STOPTSTDADDRQ x{FB00} @Defop SENDRAWMSG x{FB02} @Defop RAWRESERVE diff --git a/crypto/smartcont/config-with-ownable-params.fc b/crypto/smartcont/config-with-ownable-params.fc new file mode 100644 index 000000000..41893678e --- /dev/null +++ b/crypto/smartcont/config-with-ownable-params.fc @@ -0,0 +1,690 @@ +#pragma version =0.4.6; +;; Currently deployed config-contract in mainnet can be found +;; on https://verifier.ton.org/Ef9VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVbxn +;; Tests and other artifacts of this contract version are available at https://github.com/ton-blockchain/config-with-ownable-params + +(slice, int) check_exotic(cell c) asm "XCTOS"; +int equal_slice_bits (slice a, slice b) asm "SDEQ"; + +const int CUSTOM_PARAM_1 = -1024; +const int CUSTOM_PARAM_2 = -1025; +const slice ADMIN_ADDRESS_FOR_CUSTOM_PARAMS = "EQA_o6NFLu73wozeYNERTsW8lkU5OarbRbIkoNuWdy5SPDA_"a; +const int OP_SET_CUSTOM_PARAM = 0x6e43536c; + +() set_conf_param(int index, cell value) impure { + var cs = get_data().begin_parse(); + var cfg_dict = cs~load_ref(); + cfg_dict~idict_set_ref(32, index, value); + set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); +} + +(cell, int, int, cell) load_data() inline { + var cs = get_data().begin_parse(); + var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict()); + cs.end_parse(); + return res; +} + +() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline { + set_data(begin_cell() + .store_ref(cfg_dict) + .store_uint(stored_seqno, 32) + .store_uint(public_key, 256) + .store_dict(vote_dict) + .end_cell()); +} + +;; (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) +_ parse_vote_config(cell c) inline { + var cs = c.begin_parse(); + throw_unless(44, cs~load_uint(8) == 0x36); + var res = (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); + cs.end_parse(); + return res; +} + +;; cfg_vote_setup#91 normal_params:^ConfigProposalSetup critical_params:^ConfigProposalSetup = ConfigVotingSetup; +_ get_vote_config_internal(int critical?, cell cparam11) inline_ref { + var cs = cparam11.begin_parse(); + throw_unless(44, cs~load_uint(8) == 0x91); + if (critical?) { + cs~load_ref(); + } + return parse_vote_config(cs.preload_ref()); +} + +_ get_vote_config(int critical?) inline { + return get_vote_config_internal(critical?, config_param(11)); +} + +(int, int) check_validator_set(cell vset) { + var cs = vset.begin_parse(); + throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only + int utime_since = cs~load_uint(32); + int utime_until = cs~load_uint(32); + int total = cs~load_uint(16); + int main = cs~load_uint(16); + throw_unless(9, main > 0); + throw_unless(9, total >= main); + return (utime_since, utime_until); +} + +() send_answer(addr, query_id, ans_tag, mode) impure { + ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 + send_raw_message(begin_cell() + .store_uint(0x18, 6) + .store_slice(addr) + .store_uint(0, 5 + 4 + 4 + 64 + 32 + 1 + 1) + .store_uint(ans_tag, 32) + .store_uint(query_id, 64) + .end_cell(), mode); +} + +() send_confirmation(addr, query_id, ans_tag) impure inline { + return send_answer(addr, query_id, ans_tag, 64); +} + +() send_error(addr, query_id, ans_tag) impure inline { + return send_answer(addr, query_id, ans_tag, 64); +} + +;; forward a message to elector smart contract to make it upgrade its code +() change_elector_code(slice cs) impure { + var dest_addr = config_param(1).begin_parse().preload_uint(256); + var query_id = now(); + send_raw_message(begin_cell() + .store_uint(0xc4ff, 17) + .store_uint(dest_addr, 256) + .store_grams(1 << 30) ;; ~ 1 Gram (will be returned back) + .store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1) + .store_uint(0x4e436f64, 32) ;; action + .store_uint(query_id, 64) + .store_slice(cs) + .end_cell(), 0); +} + +() after_code_upgrade(slice param, cont old_code) impure method_id(1666) { +} + +_ perform_action(cfg_dict, public_key, action, cs) inline_ref { + if (action == 0x43665021) { + ;; change one configuration parameter + var param_index = cs~load_int(32); + var param_value = cs~load_ref(); + cs.end_parse(); + cfg_dict~idict_set_ref(32, param_index, param_value); + return (cfg_dict, public_key); + } elseif (action == 0x4e436f64) { + ;; change configuration smart contract code + var new_code = cs~load_ref(); + set_code(new_code); + var old_code = get_c3(); + set_c3(new_code.begin_parse().bless()); + after_code_upgrade(cs, old_code); + throw(0); + return (cfg_dict, public_key); + } elseif (action == 0x50624b21) { + ;; change configuration master public key + public_key = cs~load_uint(256); + cs.end_parse(); + return (cfg_dict, public_key); + } elseif (action == 0x4e43ef05) { + ;; change election smart contract code + change_elector_code(cs); + return (cfg_dict, public_key); + } else { + throw_if(32, action); + return (cfg_dict, public_key); + } +} + +(cell, int, cell) get_current_vset() inline_ref { + var vset = config_param(34); + var cs = begin_parse(vset); + ;; validators_ext#12 utime_since:uint32 utime_until:uint32 + ;; total:(## 16) main:(## 16) { main <= total } { main >= 1 } + ;; total_weight:uint64 + throw_unless(40, cs~load_uint(8) == 0x12); + cs~skip_bits(32 + 32 + 16 + 16); + var (total_weight, dict) = (cs~load_uint(64), cs~load_dict()); + cs.end_parse(); + return (vset, total_weight, dict); +} + +(slice, int) get_validator_descr(int idx) inline_ref { + var (vset, total_weight, dict) = get_current_vset(); + var (value, _) = dict.udict_get?(16, idx); + return (value, total_weight); +} + +(int, int) unpack_validator_descr(slice cs) inline { + ;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; + ;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr; + ;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; + throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53); + throw_unless(41, cs~load_uint(32) == 0x8e81278a); + return (cs~load_uint(256), cs~load_uint(64)); +} + +;; cfg_proposal#f3 param_id:int32 param_value:(Maybe ^Cell) if_hash_equal:(Maybe uint256) +;; c -> (param-id param-cell maybe-hash) +(int, cell, int) parse_config_proposal(cell c) inline_ref { + var cs = c.begin_parse(); + throw_unless(44, cs~load_int(8) == 0xf3 - 0x100); + var (id, val, hash) = (cs~load_int(32), cs~load_maybe_ref(), cs~load_int(1)); + if (hash) { + hash = cs~load_uint(256); + } else { + hash = -1; + } + cs.end_parse(); + return (id, val, hash); +} + +(cell, int, cell) accept_proposal(cell cfg_dict, cell proposal, int critical?) inline_ref { + var (param_id, param_val, req_hash) = parse_config_proposal(proposal); + cell cur_val = cfg_dict.idict_get_ref(32, param_id); + int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val); + if ((cur_hash != req_hash) & (req_hash >= 0)) { + ;; current value has incorrect hash, do not apply changes + return (cfg_dict, 0, null()); + } + if ((param_id == CUSTOM_PARAM_1) | (param_id == CUSTOM_PARAM_2)) { + ;; Custom parameters cannot be changed by validator voting, do not apply changes + return (cfg_dict, 0, null()); + } + cell mparams = cfg_dict.idict_get_ref(32, 9); ;; mandatory parameters + var (_, found?) = mparams.idict_get?(32, param_id); + if (found? & param_val.null?()) { + ;; cannot set a mandatory parameter to (null) + return (cfg_dict, 0, null()); + } + cell cparams = cfg_dict.idict_get_ref(32, 10); ;; critical parameters + (_, found?) = cparams.idict_get?(32, param_id); + if (found? < critical?) { + ;; trying to set a critical parameter after a non-critical voting + return (cfg_dict, 0, null()); + } + ;; CHANGE ONE CONFIGURATION PARAMETER (!) + cfg_dict~idict_set_ref(32, param_id, param_val); + return (cfg_dict, param_id, param_val); +} + +(cell, int) perform_proposed_action(cell cfg_dict, int public_key, int param_id, cell param_val) inline_ref { + if (param_id == -999) { + ;; appoint or depose dictator + return (cfg_dict, param_val.null?() ? 0 : param_val.begin_parse().preload_uint(256)); + } + if (param_val.null?()) { + return (cfg_dict, public_key); + } + if (param_id == -1000) { + ;; upgrade code + var cs = param_val.begin_parse(); + var new_code = cs~load_ref(); + set_code(new_code); + var old_code = get_c3(); + set_c3(new_code.begin_parse().bless()); + after_code_upgrade(cs, old_code); + throw(0); + return (cfg_dict, public_key); + } + if (param_id == -1001) { + ;; update elector code + var cs = param_val.begin_parse(); + change_elector_code(cs); + } + return (cfg_dict, public_key); +} + +;; cfg_proposal_status#ce expires:uint32 proposal:^ConfigProposal is_critical:Bool +;; voters:(HashmapE 16 True) remaining_weight:int64 validator_set_id:uint256 +;; rounds_remaining:uint8 wins:uint8 losses:uint8 = ConfigProposalStatus; +(int, cell, int, cell, int, int, slice) unpack_proposal_status(slice cs) inline_ref { + throw_unless(44, cs~load_int(8) == 0xce - 0x100); + return (cs~load_uint(32), cs~load_ref(), cs~load_int(1), cs~load_dict(), cs~load_int(64), cs~load_uint(256), cs); +} + +slice update_proposal_status(slice rest, int weight_remaining, int critical?) inline_ref { + var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?); + var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8)); + losses -= (weight_remaining >= 0); + if (losses > max_losses) { + ;; lost too many times + return null(); + } + rounds_remaining -= 1; + if (rounds_remaining < 0) { + ;; existed for too many rounds + return null(); + } + return begin_cell() + .store_uint(rounds_remaining, 8) + .store_uint(wins, 8) + .store_uint(losses, 8) + .end_cell().begin_parse(); +} + +builder begin_pack_proposal_status(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id) inline { + return begin_cell() + .store_int(0xce - 0x100, 8) + .store_uint(expires, 32) + .store_ref(proposal) + .store_int(critical?, 1) + .store_dict(voters) + .store_int(weight_remaining, 64) + .store_uint(vset_id, 256); +} + +(cell, cell, int) register_vote(vote_dict, phash, idx, weight) inline_ref { + var (pstatus, found?) = vote_dict.udict_get?(256, phash); + ifnot (found?) { + ;; config proposal not found + return (vote_dict, null(), -1); + } + var (cur_vset, total_weight, _) = get_current_vset(); + int cur_vset_id = cur_vset.cell_hash(); + var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus); + if (expires <= now()) { + ;; config proposal expired, delete and report not found + vote_dict~udict_delete?(256, phash); + return (vote_dict, null(), -1); + } + if (vset_id != cur_vset_id) { + ;; config proposal belongs to a previous validator set + vset_id = cur_vset_id; + rest = update_proposal_status(rest, weight_remaining, critical?); + voters = null(); + weight_remaining = muldiv(total_weight, 3, 4); + } + if (rest.null?()) { + ;; discard proposal (existed for too many rounds, or too many losses) + vote_dict~udict_delete?(256, phash); + return (vote_dict, null(), -1); + } + var (_, found?) = voters.udict_get?(16, idx); + if (found?) { + ;; already voted for this proposal, ignore vote + return (vote_dict, null(), -2); + } + ;; register vote + voters~udict_set_builder(16, idx, begin_cell().store_uint(now(), 32)); + int old_wr = weight_remaining; + weight_remaining -= weight; + if ((weight_remaining ^ old_wr) >= 0) { + ;; not enough votes, or proposal already accepted in this round + ;; simply update weight_remaining + vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest)); + return (vote_dict, null(), 2); + } + ;; proposal wins in this round + var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, _, _, _, _) = get_vote_config(critical?); + var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8)); + wins += 1; + if (wins >= min_wins) { + ;; proposal is accepted, remove and process + vote_dict~udict_delete?(256, phash); + return (vote_dict, proposal, 6 - critical?); + } + ;; update proposal info + vote_dict~udict_set_builder(256, phash, + begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id) + .store_uint(rounds_remaining, 8) + .store_uint(wins, 8) + .store_uint(losses, 8)); + return (vote_dict, null(), 2); +} + +int proceed_register_vote(phash, idx, weight) impure inline_ref { + var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data(); + (vote_dict, var accepted_proposal, var status) = register_vote(vote_dict, phash, idx, weight); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + ifnot (accepted_proposal.null?()) { + var critical? = 6 - status; + (cfg_dict, var param_id, var param_val) = accept_proposal(cfg_dict, accepted_proposal, critical?); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + if (param_id) { + commit(); + (cfg_dict, public_key) = perform_proposed_action(cfg_dict, public_key, param_id, param_val); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + } + } + return status; +} + +(slice, int) scan_proposal(int phash, slice pstatus) inline_ref { + var (cur_vset, total_weight, _) = get_current_vset(); + int cur_vset_id = cur_vset.cell_hash(); + var (expires, proposal, critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus); + if (expires <= now()) { + ;; config proposal expired, delete + return (null(), true); + } + if (vset_id == cur_vset_id) { + ;; config proposal already processed or voted for in this round, change nothing + return (pstatus, false); + } + ;; config proposal belongs to a previous validator set + vset_id = cur_vset_id; + rest = update_proposal_status(rest, weight_remaining, critical?); + voters = null(); + weight_remaining = muldiv(total_weight, 3, 4); + if (rest.null?()) { + ;; discard proposal (existed for too many rounds, or too many losses) + return (null(), true); + } + ;; return updated proposal + return (begin_pack_proposal_status(expires, proposal, critical?, voters, weight_remaining, vset_id).store_slice(rest).end_cell().begin_parse(), true); +} + +cell scan_random_proposal(cell vote_dict) inline_ref { + var (phash, pstatus, found?) = vote_dict.udict_get_nexteq?(256, random()); + ifnot (found?) { + return vote_dict; + } + (pstatus, var changed?) = scan_proposal(phash, pstatus); + if (changed?) { + if (pstatus.null?()) { + vote_dict~udict_delete?(256, phash); + } else { + vote_dict~udict_set(256, phash, pstatus); + } + } + return vote_dict; +} + +int register_voting_proposal(slice cs, int msg_value) impure inline_ref { + var (expire_at, proposal, critical?) = (cs~load_uint(32), cs~load_ref(), cs~load_int(1)); + if (expire_at >> 30) { + expire_at -= now(); + } + var (param_id, param_val, hash) = parse_config_proposal(proposal); + if ((param_id == CUSTOM_PARAM_1) | (param_id == CUSTOM_PARAM_2)) { + return -0xc3726956; ;; negative hash means error and will be returned as answer tag + } + if (hash >= 0) { + cell cur_val = config_param(param_id); + int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val); + if (cur_hash != hash) { + hash = -0xe2646356; ;; bad current value + } + } else { + var m_params = config_param(9); + var (_, found?) = m_params.idict_get?(32, param_id); + if (found?) { + hash = -0xcd506e6c; ;; cannot set mandatory parameter to null + } + } + if (param_val.cell_depth() >= 256) { + hash = -0xc2616456; ;; bad value + } + if (hash < -1) { + return hash; ;; return error if any + } + ifnot (critical?) { + var crit_params = config_param(10); + var (_, found?) = crit_params.idict_get?(32, param_id); + if (found?) { + hash = -0xc3726954; ;; trying to set a critical parameter without critical flag + } + } + if (hash < -1) { + return hash; + } + ;; obtain vote proposal configuration + var vote_cfg = get_vote_config(critical?); + var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = vote_cfg; + if (expire_at < min_store_sec) { + return -0xc5787069; ;; expired + } + expire_at = min(expire_at, max_store_sec); + ;; compute price + var (_, bits, refs) = compute_data_size(param_val, 1024); + var pps = bit_price * (bits + 1024) + cell_price * (refs + 2); + var price = pps * expire_at; + expire_at += now(); + var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data(); + int phash = proposal.cell_hash(); + var (pstatus, found?) = vote_dict.udict_get?(256, phash); + if (found?) { + ;; proposal already exists; we can only extend it + var (expires, r_proposal, r_critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus); + if (r_critical? != critical?) { + return -0xc3726955; ;; cannot upgrade critical parameter to non-critical... + } + if (expires >= expire_at) { + return -0xc16c7245; ;; proposal already exists + } + ;; recompute price + price = pps * (expire_at - expires + 16384); + if (msg_value - price < (1 << 30)) { + return -0xf0617924; ;; need more money + } + ;; update expiration time + vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expire_at, r_proposal, r_critical?, voters, weight_remaining, vset_id).store_slice(rest)); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + return price; + } + if (msg_value - price < (1 << 30)) { + return -0xf0617924; ;; need more money + } + ;; obtain current validator set data + var (vset, total_weight, _) = get_current_vset(); + int weight_remaining = muldiv(total_weight, 3, 4); + ;; create new proposal + vote_dict~udict_set_builder(256, phash, + begin_pack_proposal_status(expire_at, proposal, critical?, null(), weight_remaining, vset.cell_hash()) + .store_uint(max_tot_rounds, 8).store_uint(0, 16)); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + return price; +} + +() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { + var cs = in_msg_cell.begin_parse(); + var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + var s_addr = cs~load_msg_addr(); + (int src_wc, int src_addr) = s_addr.parse_std_addr(); + int is_admin_of_custom_param = equal_slice_bits(s_addr, ADMIN_ADDRESS_FOR_CUSTOM_PARAMS); + int is_valid_workchain = is_admin_of_custom_param | (src_wc == -1); + if ((~ is_valid_workchain) | (flags & 1) | in_msg.slice_empty?()) { + ;; source not in masterchain, or a bounced message, or a simple transfer + return (); + } + int tag = in_msg~load_uint(32); + int query_id = in_msg~load_uint(64); + + throw_if(37, is_admin_of_custom_param & (tag != OP_SET_CUSTOM_PARAM)); + + if (tag == 0x4e565354) { + ;; set next validator set + var vset = in_msg~load_ref(); + in_msg.end_parse(); + var elector_param = config_param(1); + var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256); + var ok = false; + if (src_addr == elector_addr) { + ;; message from elector smart contract + ;; set next validator set + (var t_since, var t_until) = check_validator_set(vset); + var t = now(); + ok = (t_since > t) & (t_until > t_since); + } + if (ok) { + set_conf_param(36, vset); + ;; send confirmation + return send_confirmation(s_addr, query_id, 0xee764f4b); + } else { + return send_error(s_addr, query_id, 0xee764f6f); + } + } + if (tag == OP_SET_CUSTOM_PARAM) { + cell value = in_msg~load_ref(); + int index = in_msg~load_int(32); + slice receiver_address = in_msg~load_msg_addr(); + in_msg.end_parse(); + (_, int exotic) = value.check_exotic(); + int depth = value.cell_depth(); + + (int response_wc, int response_address_hash) = receiver_address.parse_std_addr(); ;; Checking the validity of the response address + response_wc~impure_touch(); ;; Prevent compiler optimisation from removing parse_std_addr checking + + if ( + (exotic == 0) & ;; forbid exotic cells in config + (depth == 0) & ;; forbid cells with more than one ref + (((index == CUSTOM_PARAM_1) | (index == CUSTOM_PARAM_2)) & is_admin_of_custom_param) + ) { + set_conf_param(index, value); + ;; send confirmation + return send_confirmation(receiver_address, query_id, 0xef764f4b); + } else { + return send_error(receiver_address, query_id, 0xef764f6f); + } + } + if (tag == 0x6e565052) { + ;; new voting proposal + var price = register_voting_proposal(in_msg, msg_value); + int mode = 64; + int ans_tag = - price; + if (price >= 0) { + ;; ok, debit price + raw_reserve(price, 4); + ans_tag = 0xee565052; + mode = 128; + } + return send_answer(s_addr, query_id, ans_tag, mode); + } + if (tag == 0x566f7465) { + ;; vote for a configuration proposal + var signature = in_msg~load_bits(512); + var msg_body = in_msg; + var (sign_tag, idx, phash) = (in_msg~load_uint(32), in_msg~load_uint(16), in_msg~load_uint(256)); + in_msg.end_parse(); + throw_unless(37, sign_tag == 0x566f7445); + var (vdescr, total_weight) = get_validator_descr(idx); + var (val_pubkey, weight) = unpack_validator_descr(vdescr); + throw_unless(34, check_data_signature(msg_body, signature, val_pubkey)); + int res = proceed_register_vote(phash, idx, weight); + return send_confirmation(s_addr, query_id, res + 0xd6745240); + } + ;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query) + ;; to bounce message back to sender + throw_unless(37, (tag == 0) | (tag & (1 << 31))); + ;; do nothing for other internal messages +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + int action = cs~load_uint(32); + int msg_seqno = cs~load_uint(32); + var valid_until = cs~load_uint(32); + throw_if(35, valid_until < now()); + throw_if(39, slice_depth(cs) > 128); + var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data(); + throw_unless(33, msg_seqno == stored_seqno); + if (action == 0x566f7465) { + ;; vote for a configuration proposal + var (idx, phash) = (cs~load_uint(16), cs~load_uint(256)); + cs.end_parse(); + var (vdescr, total_weight) = get_validator_descr(idx); + var (val_pubkey, weight) = unpack_validator_descr(vdescr); + throw_unless(34, check_data_signature(in_msg, signature, val_pubkey)); + accept_message(); + stored_seqno = (stored_seqno + 1) % (1 << 32); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + commit(); + proceed_register_vote(phash, idx, weight); + return (); + } + {- + throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); + stored_seqno = (stored_seqno + 1) % (1 << 32); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + commit(); + (cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + -} +} + +() run_ticktock(int is_tock) impure { + var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data(); + int kl = 32; + var next_vset = cfg_dict.idict_get_ref(kl, 36); + var updated? = false; + ifnot (next_vset.null?()) { + ;; check whether we have to set next_vset as the current validator set + var ds = next_vset.begin_parse(); + if (ds.slice_bits() >= 40) { + var tag = ds~load_uint(8); + var since = ds.preload_uint(32); + if ((since <= now()) & (tag == 0x12)) { + ;; next validator set becomes active! + var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset); ;; next_vset -> cur_vset + cfg_dict~idict_set_get_ref(kl, 32, cur_vset); ;; cur_vset -> prev_vset + cfg_dict~idict_delete?(kl, 36); ;; (null) -> next_vset + updated? = true; + } + } + } + ifnot (updated?) { + ;; if nothing has been done so far, scan a random voting proposal instead + vote_dict = scan_random_proposal(vote_dict); + } + ;; save data and return + return store_data(cfg_dict, stored_seqno, public_key, vote_dict); +} + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} + +_ unpack_proposal(slice pstatus) inline_ref { + (int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id, slice rest) = unpack_proposal_status(pstatus); + var voters_list = null(); + var voter_id = (1 << 32); + do { + (voter_id, _, var f) = voters.udict_get_prev?(16, voter_id); + if (f) { + voters_list = cons(voter_id, voters_list); + } + } until (~ f); + var (rounds_remaining, wins, losses) = (rest~load_uint(8), rest~load_uint(8), rest~load_uint(8)); + rest.end_parse(); + var (param_id, param_val, param_hash) = parse_config_proposal(proposal); + return [expires, critical?, [param_id, param_val, param_hash], vset_id, voters_list, weight_remaining, rounds_remaining, wins, losses]; +} + +_ get_proposal(int phash) method_id { + (_, _, _, var vote_dict) = load_data(); + var (pstatus, found?) = vote_dict.udict_get?(256, phash); + ifnot (found?) { + return null(); + } + return unpack_proposal(pstatus); +} + +_ list_proposals() method_id { + (_, _, _, var vote_dict) = load_data(); + var phash = (1 << 255) + ((1 << 255) - 1); + var list = null(); + do { + (phash, var pstatus, var f) = vote_dict.udict_get_prev?(256, phash); + if (f) { + list = cons([phash, unpack_proposal(pstatus)], list); + } + } until (~ f); + return list; +} + +_ proposal_storage_price(int critical?, int seconds, int bits, int refs) method_id { + var cfg_dict = get_data().begin_parse().preload_ref(); + var cparam11 = cfg_dict.idict_get_ref(32, 11); + var (min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price) = get_vote_config_internal(critical?, cparam11); + if (seconds < min_store_sec) { + return -1; + } + seconds = min(seconds, max_store_sec); + return (bit_price * (bits + 1024) + cell_price * (refs + 2)) * seconds; +} + diff --git a/crypto/test/fift.cpp b/crypto/test/fift.cpp index 55a6ded75..4d522ce73 100644 --- a/crypto/test/fift.cpp +++ b/crypto/test/fift.cpp @@ -179,3 +179,7 @@ TEST(Fift, test_get_extra_balance) { TEST(Fift, test_p256) { run_fift("p256.fif"); } + +TEST(Fift, test_load_store_std_addr) { + run_fift("load-store-std-addr.fif"); +} diff --git a/crypto/test/fift/load-store-std-addr.fif b/crypto/test/fift/load-store-std-addr.fif new file mode 100644 index 000000000..18c3e39d6 --- /dev/null +++ b/crypto/test/fift/load-store-std-addr.fif @@ -0,0 +1,405 @@ +"Asm.fif" include + +{ { drop } depth 1- times } : clear-stack +{ ."Running test: " .dump cr 0 runvmx 0= { "test failed" abort } ifnot clear-stack } : run-test +{ ."Running test: " .dump cr 0 runvmx 0= { "test failed: exit code = 0 but expected to be non zero" abort } if clear-stack } : run-fail-test + +variable @addr_none +variable @valid_std_address +variable @valid_opt_std_address +variable @invalid_std_address + +b{00} @addr_none ! +x{8016316F1DAA41369BD888538DAA8F9D3BB8D449BA0F22AC0C882CFC98BAB534843_} @valid_std_address ! + s "should load address via LDSTDADDR" run-test + +<{ + @addr_none @ PUSHSLICE + LDSTDADDR + SBITS + 100 THROWIF // check that slice is empty + REWRITESTDADDR // check that address really is std + DROP2 +}>s "should not load addr_none address via LDSTDADDR" run-fail-test + +<{ + b{} PUSHSLICE + LDSTDADDR // will throw since no data available +}>s "should fail to load address via LDSTDADDR from empty slice" run-fail-test + +// ============================= LDSTDADDRQ ============================= + +<{ + @valid_std_address @ PUSHSLICE + LDSTDADDRQ + 100 THROWIFNOT // check that status is -1 + @valid_std_address @ PUSHSLICE + SDEQ // slice must be intact + DROP // drop slice + REWRITESTDADDR // check that address really is std + DROP2 +}>s "should load address via LDSTDADDRQ" run-test + +<{ + @addr_none @ PUSHSLICE + LDSTDADDRQ + 100 THROWIF // check that status is 0 + @addr_none @ PUSHSLICE + SDEQ // slice must be intact + DROP // drop slice +}>s "should not load addr_none address via LDSTDADDRQ" run-test + +<{ + b{} PUSHSLICE + LDSTDADDRQ // fail since no data available + 100 THROWIF // check that status is 0 +}>s "should fail to load address via LDSTDADDRQ from empty slice" run-test + +// ============================= LDOPTSTDADDR ============================= + +<{ + @valid_std_address @ PUSHSLICE + LDOPTSTDADDR + SBITS + 100 THROWIF // check that slice is empty + REWRITESTDADDR // check that address really is std + DROP2 +}>s "should load address via LDOPTSTDADDR" run-test + +<{ + @addr_none @ PUSHSLICE + LDOPTSTDADDR + SBITS + 100 THROWIF // check that slice is empty + ISNULL // loaded value must be null + 100 THROWIFNOT +}>s "should load null via LDOPTSTDADDR from addr_none slice" run-test + +<{ + b{10} PUSHSLICE + LDOPTSTDADDR // will throw since no data available for the address bpdy +}>s "should fail to load address via LDOPTSTDADDR from b{10} slice" run-fail-test + +<{ + b{1} PUSHSLICE + LDOPTSTDADDR // will throw since no data available for the tag +}>s "should fail to load address via LDOPTSTDADDR from b{1} slice" run-fail-test + +<{ + b{} PUSHSLICE + LDOPTSTDADDR // will throw since no data available +}>s "should fail to load address via LDOPTSTDADDR from empty slice" run-fail-test + +// ============================= LDOPTSTDADDRQ ============================= + +<{ + @valid_std_address @ PUSHSLICE + LDOPTSTDADDRQ + 100 THROWIFNOT // check that status is -1 + SBITS + 100 THROWIF // check that slice is empty + REWRITESTDADDR // check that address really is std + DROP2 +}>s "should load address via LDOPTSTDADDRQ" run-test + +<{ + @addr_none @ PUSHSLICE + LDOPTSTDADDRQ + 100 THROWIFNOT // check that status is -1 + SBITS + 100 THROWIF // check that slice is empty + ISNULL // loaded value must be null + 100 THROWIFNOT +}>s "should load null via LDOPTSTDADDRQ from addr_none" run-test + +<{ + b{10} PUSHSLICE + LDOPTSTDADDRQ // will return slice status=0 + 100 THROWIF // check that status is 0 + b{10} PUSHSLICE + SDEQ // slice must be intact +}>s "should not load address via LDOPTSTDADDRQ from b{10} slice" run-test + +<{ + b{1} PUSHSLICE + LDOPTSTDADDRQ // will return slice status=0 + 100 THROWIF // check that status is 0 + b{1} PUSHSLICE + SDEQ // slice must be intact +}>s "should not load address via LDOPTSTDADDRQ from b{1} slice" run-test + +<{ + b{} PUSHSLICE + LDOPTSTDADDRQ // fail since no data available + 100 THROWIF // check that status is 0 +}>s "should fail to load address via LDOPTSTDADDRQ from empty slice" run-test + +// ============================= STSTDADDR ============================= + +<{ + @valid_std_address @ PUSHSLICE + NEWC + STSTDADDR + DROP // drop builder +}>s "should store address via STSTDADDR" run-test + +<{ + @addr_none @ PUSHSLICE + NEWC + STSTDADDR // should fail +}>s "should fail to store addr_none address via STSTDADDR" run-fail-test + +<{ + @valid_opt_std_address @ PUSHSLICE + NEWC + STSTDADDR // should fail +}>s "should fail to store opt std address via STSTDADDR" run-fail-test + +<{ + @valid_std_address @ PUSHSLICE + NEWC + s "should fail to store opt std address via STSTDADDR to full builder" run-fail-test + +<{ + @invalid_std_address @ PUSHSLICE + NEWC + STSTDADDR // should fail +}>s "should fail to store too short slice via STSTDADDR" run-fail-test + +<{ + b{110011001111} PUSHSLICE + NEWC + STSTDADDR // should fail +}>s "should fail to store random slice via STSTDADDR" run-fail-test + +// ============================= STSTDADDRQ ============================= + +<{ + @valid_std_address @ PUSHSLICE + NEWC + STSTDADDRQ + 100 THROWIF // check that status is 0 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP // drop builder +}>s "should store address via STSTDADDRQ" run-test + +<{ + @addr_none @ PUSHSLICE + NEWC + STSTDADDRQ + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store addr_none address via STSTDADDRQ" run-test + +<{ + @valid_opt_std_address @ PUSHSLICE + NEWC + STSTDADDRQ + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store opt std address via STSTDADDRQ" run-test + +<{ + @invalid_std_address @ PUSHSLICE + NEWC + STSTDADDRQ // should fail + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store too short slice via STSTDADDRQ" run-test + +<{ + b{110011001111} PUSHSLICE + NEWC + STSTDADDRQ // should fail + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store random slice via STSTDADDRQ" run-test + + +// ============================= STOPTSTDADDR ============================= + +<{ + @valid_std_address @ PUSHSLICE + DUP + NEWC + STOPTSTDADDR + ENDC + CTOS + LDOPTSTDADDR + DROP + SDEQ // check that address loaded back is equal to original one + 100 THROWIFNOT +}>s "should store address via STOPTSTDADDR" run-test + +<{ + PUSHNULL + NEWC + STOPTSTDADDR + ENDC + CTOS + LDOPTSTDADDR // we should be able to load address back + DROP + ISNULL // check that address loaded back is actually a Null + 100 THROWIFNOT +}>s "should store null via STOPTSTDADDR as addr_none" run-test + +<{ + PUSHNULL + NEWC + STOPTSTDADDR + ENDC + CTOS + b{00} PUSHSLICE + SDEQ // check that address stored as b{00} + 100 THROWIFNOT +}>s "should store null via STOPTSTDADDR as b{00}" run-test + +<{ + @addr_none @ PUSHSLICE + NEWC + STOPTSTDADDR // should fail +}>s "should fail to store addr_none address via STOPTSTDADDR" run-fail-test + +<{ + @valid_std_address @ PUSHSLICE + NEWC + s "should fail to store address via STOPTSTDADDR in full builder" run-fail-test + +<{ + PUSHNULL + NEWC + s "should fail to store null via STOPTSTDADDR in full builder" run-fail-test + +<{ + PUSHNULL + NEWC + s "should fail to store null via STOPTSTDADDR in almost full builder" run-fail-test + +<{ + @invalid_std_address @ PUSHSLICE + NEWC + STOPTSTDADDR // should fail +}>s "should fail to store too short slice via STOPTSTDADDR" run-fail-test + +<{ + b{110011001111} PUSHSLICE + NEWC + STOPTSTDADDR // should fail +}>s "should fail to store random slice via STOPTSTDADDR" run-fail-test + +<{ + 10 PUSHINT + NEWC + STOPTSTDADDR // should fail +}>s "should fail to store int via STOPTSTDADDR" run-fail-test + +// ============================= STOPTSTDADDRQ ============================= + +<{ + @valid_std_address @ PUSHSLICE + DUP + NEWC + STOPTSTDADDRQ + 100 THROWIF // check that status is 0 (for some reason STSLICEQ returns 0 for success and -1 for fail) + ENDC + CTOS + LDOPTSTDADDR + DROP + SDEQ // check that address loaded back is equal to original one + 100 THROWIFNOT +}>s "should store address via STOPTSTDADDRQ" run-test + +<{ + PUSHNULL + NEWC + STOPTSTDADDRQ // stored as b{00} + 100 THROWIF // check that status is 0 (for some reason STSLICEQ returns 0 for success and -1 for fail) + ENDC + CTOS // b{00} + LDOPTSTDADDR // null slice + SBITS + 100 THROWIF // check that slice is empty + ISNULL + 100 THROWIFNOT // check that loaded address is null +}>s "should store null via STOPTSTDADDRQ" run-test + +<{ + @addr_none @ PUSHSLICE + NEWC + STOPTSTDADDRQ // should fail + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) +}>s "should fail to store addr_none address via STOPTSTDADDRQ" run-test + +<{ + @valid_std_address @ PUSHSLICE + NEWC + s "should fail to store address via STOPTSTDADDRQ in full builder" run-test + +<{ + PUSHNULL + NEWC + s "should fail to store null via STOPTSTDADDR in full builder" run-test + +<{ + PUSHNULL + NEWC + s "should fail to store null via STOPTSTDADDR in almost full builder" run-test + +<{ + @invalid_std_address @ PUSHSLICE + NEWC + STOPTSTDADDRQ // should fail + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store too short slice via STOPTSTDADDRQ" run-test + +<{ + b{110011001111} PUSHSLICE + NEWC + STOPTSTDADDRQ // should fail + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store random slice via STOPTSTDADDRQ" run-test + +<{ + 100 PUSHINT + NEWC + STOPTSTDADDRQ // should fail + 100 THROWIFNOT // check that status is -1 (for some reason STSLICEQ returns 0 for success and -1 for fail) + DROP2 // drop builder and slice +}>s "should fail to store int via STOPTSTDADDRQ" run-test diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index ae624fe03..a6934e6c0 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -1534,6 +1534,20 @@ bool skip_message_addr(CellSlice& cs, int global_version) { } } +bool skip_std_message_addr(CellSlice& cs, int global_version) { + switch ((unsigned)cs.fetch_ulong(2)) { + case 2: { // addr_std$10 + return skip_maybe_anycast(cs, global_version) // anycast:(Maybe Anycast) + && cs.advance(8 + 256); // workchain_id:int8 address:bits256 = MsgAddressInt; + } + case 0: // addr_none$00 = MsgAddressExt; + case 1: // addr_extern$01 + case 3: // addr_var$11 + default: + return false; + } +} + int exec_load_message_addr(VmState* st, bool quiet) { VM_LOG(st) << "execute LDMSGADDR" << (quiet ? "Q" : ""); Stack& stack = st->get_stack(); @@ -1552,6 +1566,66 @@ int exec_load_message_addr(VmState* st, bool quiet) { return 0; } +int exec_load_std_message_addr(VmState* st, bool quiet) { + VM_LOG(st) << "execute LDSTDADDR" << (quiet ? "Q" : ""); + Stack& stack = st->get_stack(); + auto csr = stack.pop_cellslice(); + td::Ref addr{true}; + if (util::load_std_msg_addr_q(csr.write(), addr.write(), st->get_global_version(), quiet)) { + stack.push_cellslice(std::move(addr)); + stack.push_cellslice(std::move(csr)); + if (quiet) { + stack.push_bool(true); + } + } else { + stack.push_cellslice(std::move(csr)); + stack.push_bool(false); + } + return 0; +} + +int exec_load_opt_std_message_addr(VmState* st, bool quiet) { + VM_LOG(st) << "execute LDOPTSTDADDR" << (quiet ? "Q" : ""); + Stack& stack = st->get_stack(); + auto csr = stack.pop_cellslice(); + + if (!csr->have(2)) { + // No data to load the tag for the address (0b00 or 0b10) + if (quiet) { + stack.push_cellslice(std::move(csr)); + stack.push_bool(false); + return 0; + } + + throw VmError{Excno::cell_und}; + } + + auto tag = csr.write().prefetch_ulong(2); + if (tag == 0b00) { // addr_none$00 + csr.write().skip_first(2); + // addr_none -> push null + stack.push_null(); + stack.push_cellslice(std::move(csr)); + if (quiet) { + stack.push_bool(true); + } + return 0; + } + + td::Ref addr{true}; + if (util::load_std_msg_addr_q(csr.write(), addr.write(), st->get_global_version(), quiet)) { + stack.push_cellslice(std::move(addr)); + stack.push_cellslice(std::move(csr)); + if (quiet) { + stack.push_bool(true); + } + } else { + stack.push_cellslice(std::move(csr)); + stack.push_bool(false); + } + return 0; +} + bool parse_maybe_anycast(CellSlice& cs, StackEntry& res, int global_version) { res = StackEntry{}; if (cs.prefetch_ulong(1) != 1) { @@ -1723,6 +1797,118 @@ int exec_rewrite_message_addr(VmState* st, bool allow_var_addr, bool quiet) { return 0; } +bool is_valid_std_msg_addr(const Ref& cs, int global_version) { + if ((unsigned)cs->prefetch_ulong(2) != 0b10) { + // not a addr_std$10 + return false; + } + + if (global_version >= 10) { + if (cs->prefetch_ulong(3) == 0b10'1) { // 0b10 prefix and 0b1 for Maybe Anycast + // anycast addresses is disabled since TVM 11 + return false; + } + + return cs->size() == 3 + 8 + 256 && cs->size_refs() == 0; // anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt; + } + + // Fallback to copy cell slice and check with anycast + Ref cloned_cs = cs; + if (!skip_std_message_addr(cloned_cs.write(), global_version)) { + return false; + } + return cloned_cs->size() == 0 && cloned_cs->size_refs() == 0; +} + +int exec_store_std_address(VmState* st, bool quiet) { + Stack& stack = st->get_stack(); + VM_LOG(st) << "execute STSTDADDR" << (quiet ? "Q" : ""); + stack.check_underflow(2); + auto builder = stack.pop_builder(); + auto cs = stack.pop_cellslice(); + bool is_std = is_valid_std_msg_addr(cs, st->get_global_version()); + + if (!builder->can_extend_by(cs->size(), cs->size_refs()) || !is_std) { + if (!quiet) { + if (!is_std) { + throw VmError{Excno::cell_ov, "not a MsgAddressInt"}; + } + throw VmError{Excno::cell_ov}; + } + stack.push_cellslice(std::move(cs)); + stack.push_builder(std::move(builder)); + stack.push_bool(true); + return 0; + } + cell_builder_add_slice(builder.write(), *cs); + stack.push_builder(std::move(builder)); + if (quiet) { + stack.push_bool(false); + } + return 0; +} + +int exec_store_opt_std_address(VmState* st, bool quiet) { + Stack& stack = st->get_stack(); + VM_LOG(st) << "execute STOPTSTDADDR" << (quiet ? "Q" : ""); + stack.check_underflow(2); + auto builder = stack.pop_builder(); + + auto cs_or_null = stack.pop(); + if (cs_or_null.is_null()) { + if (!builder->can_extend_by(2, 0)) { + // No room to store 0b00 for addr_none + if (!quiet) { + throw VmError{Excno::cell_ov}; + } + stack.push_null(); + stack.push_builder(std::move(builder)); + stack.push_bool(true); + return 0; + } + + // null is stored as addr_none$00 + builder.write().store_zeroes_bool(2); + stack.push_builder(std::move(builder)); + if (quiet) { + stack.push_bool(false); + } + return 0; + } + + auto cs = cs_or_null.as_slice(); + if (cs.is_null()) { + if (!quiet) { + throw VmError{Excno::type_chk, "not a cell slice"}; + } + stack.push_cellslice(std::move(cs)); + stack.push_builder(std::move(builder)); + stack.push_bool(true); + return 0; + } + bool is_std = is_valid_std_msg_addr(cs, st->get_global_version()); + + if (!builder->can_extend_by(cs->size(), cs->size_refs()) || !is_std) { + if (!quiet) { + if (!is_std) { + throw VmError{Excno::cell_ov, "not a MsgAddressInt"}; + } + throw VmError{Excno::cell_ov}; + } + stack.push_cellslice(std::move(cs)); + stack.push_builder(std::move(builder)); + stack.push_bool(true); + return 0; + } + + cell_builder_add_slice(builder.write(), *cs); + stack.push_builder(std::move(builder)); + if (quiet) { + stack.push_bool(false); + } + return 0; +} + void register_ton_currency_address_ops(OpcodeTable& cp0) { using namespace std::placeholders; cp0.insert(OpcodeInstr::mksimple(0xfa00, 16, "LDGRAMS", std::bind(exec_load_var_integer, _1, 4, false, false))) @@ -1744,7 +1930,15 @@ void register_ton_currency_address_ops(OpcodeTable& cp0) { .insert( OpcodeInstr::mksimple(0xfa46, 16, "REWRITEVARADDR", std::bind(exec_rewrite_message_addr, _1, true, false))) .insert( - OpcodeInstr::mksimple(0xfa47, 16, "REWRITEVARADDRQ", std::bind(exec_rewrite_message_addr, _1, true, true))); + OpcodeInstr::mksimple(0xfa47, 16, "REWRITEVARADDRQ", std::bind(exec_rewrite_message_addr, _1, true, true))) + .insert(OpcodeInstr::mksimple(0xfa48, 16, "LDSTDADDR", std::bind(exec_load_std_message_addr, _1, false))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa49, 16, "LDSTDADDRQ", std::bind(exec_load_std_message_addr, _1, true))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa50, 16, "LDOPTSTDADDR", std::bind(exec_load_opt_std_message_addr, _1, false))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa51, 16, "LDOPTSTDADDRQ", std::bind(exec_load_opt_std_message_addr, _1, true))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa52, 16, "STSTDADDR", std::bind(exec_store_std_address, _1, false))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa53, 16, "STSTDADDRQ", std::bind(exec_store_std_address, _1, true))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa54, 16, "STOPTSTDADDR", std::bind(exec_store_opt_std_address, _1, false))->require_version(12)) + .insert(OpcodeInstr::mksimple(0xfa55, 16, "STOPTSTDADDRQ", std::bind(exec_store_opt_std_address, _1, true))->require_version(12)); } static constexpr int output_actions_idx = 5; @@ -2171,6 +2365,18 @@ bool load_msg_addr_q(CellSlice& cs, CellSlice& res, int global_version, bool qui res.cut_tail(cs); return true; } +bool load_std_msg_addr_q(CellSlice& cs, CellSlice& res, int global_version, bool quiet) { + res = cs; + if (!skip_std_message_addr(cs, global_version)) { + cs = res; + if (quiet) { + return false; + } + throw VmError{Excno::cell_und, "cannot load a MsgAddressInt"}; + } + res.cut_tail(cs); + return true; +} bool parse_std_addr_q(CellSlice cs, ton::WorkchainId& res_wc, ton::StdSmcAddress& res_addr, int global_version, bool quiet) { // Like exec_rewrite_message_addr, but for std address case diff --git a/crypto/vm/tonops.h b/crypto/vm/tonops.h index d33505085..a5597202d 100644 --- a/crypto/vm/tonops.h +++ b/crypto/vm/tonops.h @@ -33,6 +33,7 @@ namespace util { bool load_var_integer_q(CellSlice& cs, td::RefInt256& res, int len_bits, bool sgnd, bool quiet); bool load_coins_q(CellSlice& cs, td::RefInt256& res, bool quiet); bool load_msg_addr_q(CellSlice& cs, CellSlice& res, int global_version, bool quiet); +bool load_std_msg_addr_q(CellSlice& cs, CellSlice& res, int global_version, bool quiet); bool parse_std_addr_q(CellSlice cs, ton::WorkchainId& res_wc, ton::StdSmcAddress& res_addr, int global_version, bool quiet); diff --git a/dht-server/CMakeLists.txt b/dht-server/CMakeLists.txt index 07c93f247..b7917fe43 100644 --- a/dht-server/CMakeLists.txt +++ b/dht-server/CMakeLists.txt @@ -1,8 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - - set (DHT_SERVER_SOURCE dht-server.cpp dht-server.hpp diff --git a/dht-server/dht-server.cpp b/dht-server/dht-server.cpp index 46a011352..c8fc4508d 100644 --- a/dht-server/dht-server.cpp +++ b/dht-server/dht-server.cpp @@ -433,14 +433,6 @@ td::Status DhtServer::load_global_config() { TRY_RESULT_PREFIX(dht, ton::dht::Dht::create_global_config(std::move(conf.dht_)), "bad [dht] section: "); dht_config_ = std::move(dht); - if (!conf.validator_) { - return td::Status::Error(ton::ErrorCode::error, "does not contain [validator] section"); - } - - if (!conf.validator_->zero_state_) { - return td::Status::Error(ton::ErrorCode::error, "[validator] section does not contain [zero_state]"); - } - return td::Status::OK(); } diff --git a/dht/CMakeLists.txt b/dht/CMakeLists.txt index f8d49a2d8..a2f63af58 100644 --- a/dht/CMakeLists.txt +++ b/dht/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(DHT_SOURCE dht.cpp dht-remote-node.cpp diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 79dbc6bcc..57387227a 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -237,6 +237,9 @@ This field does not represent fees. `ihr_fee` is always zero since version 11, s `(extra_flags & 1) = 1` enables the new bounce format for the message. The bounced message contains information about the transaction. If `(extra_flags & 3) = 3`, the bounced message contains the whole body of the original message. Otherwise, only the bits from the root of the original body are returned. + +All other bits in `extra_flags` are reserved for future use and are not allowed now (internal messages with flags other than `0..3` are invalid). + When the message with new bounce flag is bounced, the bounced message body has the following format (`new_bounce_body`): ``` _ value:CurrencyCollection created_lt:uint64 created_at:uint32 = NewBounceOriginalInfo; @@ -263,9 +266,19 @@ new_bounce_body#fffffffe - `compute_phase` - exists if it was not skipped (`bounced_by_phase > 0`): - `gas_used`, `vm_steps` - same as in `TrComputePhase` of the transaction. +The bounced message has the same 0th and 1st bits in `extra_flags` as the original message. + ### New TVM instructions - `BTOS` (`b - s`) - same as `ENDC CTOS`, but without gas cost for cell creation and loading. Gas cost: `26`. - `HASHBU` (`b - hash`) - same as `ENDC HASHCU`, but without gas cost for cell creation. Gas cost: `26`. +- `LDSTDADDR` (`s - a s'`) - loads `addr_std$10`, if address is not `addr_std`, throws an error 9 (`cannot load a MsgAddressInt`). Gas cost: `26`. +- `LDSTDADDRQ` (`s - a s' -1 or s 0`) - quiet version of `LDSTDADDR`. Gas cost: `26`. +- `LDOPTSTDADDR` (`s - a s or null s`) - loads `addr_std$10` or `addr_none$00`, if address is `addr_none$00` pushes a Null, if address is not `addr_std` or `addr_none`, throws an error 9 (`cannot load a MsgAddressInt`). Gas cost: `26`. +- `LDOPTSTDADDRQ` (`s - (a s' -1 or null s' -1) or s 0`) - quiet version of `LDOPTSTDADDR`. Gas cost: `26`. +- `STSTDADDR` (`s b - b'`) - stores `addr_std$10`, if address is not `addr_std`, throws an error 9 (`cannot load a MsgAddressInt`). Gas cost: `26`. +- `STSTDADDRQ` (`s b - b' 0 or s b -1`) - quiet version of `STSTDADDR`. Gas cost: `26`. +- `STOPTSTDADDR` (`s b - b'`) - stores `addr_std$10` or Null. Null is stored as `addr_none$00`, if address is not `addr_std`, throws an error 9 (`cannot load a MsgAddressInt`). Gas cost: `26`. +- `STOPTSTDADDRQ` (`s b - b' 0 or s b -1`) - quiet version of `STOPTSTDADDR`. Gas cost: `26`. ### Other TVM changes - `SENDMSG` instruction treats `extra_flags` field accordingly (see above). diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index 63cb28d6a..a802d8df3 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -63,8 +63,8 @@ if (USE_EMSCRIPTEN) target_link_options(emulator-emscripten PRIVATE -sIGNORE_MISSING_MAIN=1) target_link_options(emulator-emscripten PRIVATE -sAUTO_NATIVE_LIBRARIES=0) target_link_options(emulator-emscripten PRIVATE -sMODULARIZE=1) - target_link_options(emulator-emscripten PRIVATE -sENVIRONMENT=web) - target_link_options(emulator-emscripten PRIVATE -sFILESYSTEM=0) + target_link_options(emulator-emscripten PRIVATE -sENVIRONMENT=node) + target_link_options(emulator-emscripten PRIVATE -sFORCE_FILESYSTEM=1) target_link_options(emulator-emscripten PRIVATE -sALLOW_MEMORY_GROWTH=1) target_link_options(emulator-emscripten PRIVATE -fexceptions) if (USE_EMSCRIPTEN_NO_WASM) diff --git a/emulator/transaction-emulator.cpp b/emulator/transaction-emulator.cpp index 1ae07df0b..cd41c00fa 100644 --- a/emulator/transaction-emulator.cpp +++ b/emulator/transaction-emulator.cpp @@ -11,38 +11,38 @@ namespace emulator { td::Result> TransactionEmulator::emulate_transaction( block::Account&& account, td::Ref msg_root, ton::UnixTime utime, ton::LogicalTime lt, int trans_type, int vm_ver) { + td::Ref old_mparams; + std::vector storage_prices; + block::StoragePhaseConfig storage_phase_cfg{&storage_prices}; + block::ComputePhaseConfig compute_phase_cfg; + block::ActionPhaseConfig action_phase_cfg; + block::SerializeConfig serialize_config; + td::RefInt256 masterchain_create_fee, basechain_create_fee; + + if (!utime) { + utime = unixtime_; + } + if (!utime) { + utime = (unsigned)std::time(nullptr); + } - td::Ref old_mparams; - std::vector storage_prices; - block::StoragePhaseConfig storage_phase_cfg{&storage_prices}; - block::ComputePhaseConfig compute_phase_cfg; - block::ActionPhaseConfig action_phase_cfg; - block::SerializeConfig serialize_config; - td::RefInt256 masterchain_create_fee, basechain_create_fee; - - if (!utime) { - utime = unixtime_; - } - if (!utime) { - utime = (unsigned)std::time(nullptr); - } - - auto fetch_res = block::FetchConfigParams::fetch_config_params( - *config_, prev_blocks_info_, &old_mparams, &storage_prices, &storage_phase_cfg, &rand_seed_, &compute_phase_cfg, - &action_phase_cfg, &serialize_config, &masterchain_create_fee, &basechain_create_fee, account.workchain, utime); - if(fetch_res.is_error()) { - return fetch_res.move_as_error_prefix("cannot fetch config params "); - } + auto fetch_res = block::FetchConfigParams::fetch_config_params( + *config_, prev_blocks_info_, &old_mparams, &storage_prices, &storage_phase_cfg, &rand_seed_, &compute_phase_cfg, + &action_phase_cfg, &serialize_config, &masterchain_create_fee, &basechain_create_fee, account.workchain, utime); + if (fetch_res.is_error()) { + return fetch_res.move_as_error_prefix("cannot fetch config params "); + } TRY_STATUS(vm::init_vm(debug_enabled_)); - if (!lt) { - lt = lt_; - } - if (!lt) { - lt = (account.last_trans_lt_ / block::ConfigInfo::get_lt_align() + 1) * block::ConfigInfo::get_lt_align(); // next block after account_.last_trans_lt_ - } - account.block_lt = lt - lt % block::ConfigInfo::get_lt_align(); + if (!lt) { + lt = lt_; + } + if (!lt) { + lt = (account.last_trans_lt_ / block::ConfigInfo::get_lt_align() + 1) * block::ConfigInfo::get_lt_align(); + // next block after account_.last_trans_lt_ + } + account.block_lt = lt - lt % block::ConfigInfo::get_lt_align(); compute_phase_cfg.libraries = std::make_unique(libraries_); compute_phase_cfg.ignore_chksig = ignore_chksig_; @@ -68,9 +68,10 @@ td::Result> TransactionEmu elapsed); } - if (!trans->serialize(serialize_config)) { - return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex()); - } + if (!trans->serialize(serialize_config)) { + return td::Status::Error( + -669, "cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex()); + } auto trans_root = trans->commit(account); if (trans_root.is_null()) { @@ -133,29 +134,29 @@ td::Result TransactionEmulator::emulate_t } } - TRY_RESULT(emulation, emulate_transaction(std::move(account), msg_root, utime, lt, trans_type)); - - if (auto emulation_result_ptr = dynamic_cast(emulation.get())) { - auto& emulation_result = *emulation_result_ptr; + TRY_RESULT(emulation, emulate_transaction(std::move(account), msg_root, utime, lt, trans_type)); - if (td::Bits256(emulation_result.transaction->get_hash().bits()) != td::Bits256(original_trans->get_hash().bits())) { - return td::Status::Error("transaction hash mismatch"); - } + if (auto emulation_result_ptr = dynamic_cast(emulation.get())) { + auto& emulation_result = *emulation_result_ptr; - if (!check_state_update(emulation_result.account, record_trans)) { - return td::Status::Error("account hash mismatch"); - } - - return std::move(emulation_result); + if (td::Bits256(emulation_result.transaction->get_hash().bits()) != + td::Bits256(original_trans->get_hash().bits())) { + return td::Status::Error("transaction hash mismatch"); + } - } else if (auto emulation_not_accepted_ptr = dynamic_cast(emulation.get())) { - return td::Status::Error( PSTRING() - << "VM Log: " << emulation_not_accepted_ptr->vm_log - << ", VM Exit Code: " << emulation_not_accepted_ptr->vm_exit_code - << ", Elapsed Time: " << emulation_not_accepted_ptr->elapsed_time); - } else { - return td::Status::Error("emulation failed"); + if (!check_state_update(emulation_result.account, record_trans)) { + return td::Status::Error("account hash mismatch"); } + + return std::move(emulation_result); + } else if (auto emulation_not_accepted_ptr = dynamic_cast(emulation.get())) { + return td::Status::Error(PSTRING() + << "VM Log: " << emulation_not_accepted_ptr->vm_log + << ", VM Exit Code: " << emulation_not_accepted_ptr->vm_exit_code + << ", Elapsed Time: " << emulation_not_accepted_ptr->elapsed_time); + } else { + return td::Status::Error("emulation failed"); + } } td::Result TransactionEmulator::emulate_transactions_chain( @@ -205,7 +206,7 @@ td::Result> TransactionEmulator if (external) { // inbound external message was not accepted return td::Status::Error(-701, "inbound external message rejected by account "s + acc->addr.to_hex() + - " before smart-contract execution"); + " before smart-contract execution"); } return td::Status::Error(-669, "cannot unpack input message for a new transaction"); } @@ -238,7 +239,7 @@ td::Result> TransactionEmulator if (!trans->compute_phase->accepted) { if (!external && trans->compute_phase->skip_reason == block::ComputePhase::sk_none) { return td::Status::Error(-669, "new ordinary transaction for smart contract "s + acc->addr.to_hex() + - " has not been accepted by the smart contract (?)"); + " has not been accepted by the smart contract (?)"); } } @@ -248,9 +249,10 @@ td::Result> TransactionEmulator } if (trans->bounce_enabled - && (!trans->compute_phase->success || trans->action_phase->state_exceeds_limits || trans->action_phase->bounce) - && !trans->prepare_bounce_phase(*action_phase_cfg)) { - return td::Status::Error(-669,"cannot create bounce phase of a new transaction for smart contract "s + acc->addr.to_hex()); + && (!trans->compute_phase->success || trans->action_phase->state_exceeds_limits || trans->action_phase->bounce) + && !trans->prepare_bounce_phase(*action_phase_cfg)) { + return td::Status::Error( + -669, "cannot create bounce phase of a new transaction for smart contract "s + acc->addr.to_hex()); } return trans; @@ -272,6 +274,10 @@ void TransactionEmulator::set_ignore_chksig(bool ignore_chksig) { ignore_chksig_ = ignore_chksig; } +void TransactionEmulator::set_vm_verbosity_level(int verbosity) { + vm_log_verbosity_ = verbosity; +} + void TransactionEmulator::set_config(std::shared_ptr config) { config_ = std::move(config); } @@ -287,5 +293,4 @@ void TransactionEmulator::set_debug_enabled(bool debug_enabled) { void TransactionEmulator::set_prev_blocks_info(td::Ref prev_blocks_info) { prev_blocks_info_ = std::move(prev_blocks_info); } - -} // namespace emulator +} // namespace emulator \ No newline at end of file diff --git a/emulator/transaction-emulator.h b/emulator/transaction-emulator.h index 675120109..8aa7bda01 100644 --- a/emulator/transaction-emulator.h +++ b/emulator/transaction-emulator.h @@ -78,6 +78,7 @@ class TransactionEmulator { void set_lt(ton::LogicalTime lt); void set_rand_seed(td::BitArray<256>& rand_seed); void set_ignore_chksig(bool ignore_chksig); + void set_vm_verbosity_level(int vm_verbosity); void set_config(std::shared_ptr config); void set_libs(vm::Dictionary &&libs); void set_debug_enabled(bool debug_enabled); diff --git a/fec/CMakeLists.txt b/fec/CMakeLists.txt index 8549dc0e5..721f988de 100644 --- a/fec/CMakeLists.txt +++ b/fec/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(FEC_SOURCE fec.h fec.cpp diff --git a/overlay/CMakeLists.txt b/overlay/CMakeLists.txt index c2c9dc617..e7b91d6e2 100644 --- a/overlay/CMakeLists.txt +++ b/overlay/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(OVERLAY_SOURCE overlay-manager.cpp overlay.cpp diff --git a/recent_changelog.md b/recent_changelog.md index fc4ae86c4..422d4d8a1 100644 --- a/recent_changelog.md +++ b/recent_changelog.md @@ -1,9 +1,9 @@ -## 2025.10 Update +## 2025.11 Update -1. [TVM version v12](./doc/GlobalVersions.md): full bounces, new `BTOS` and `HASHBU` instuctions, limit on contract size in masterchain. -2. Optimistic collation/validation: allow nodes to generate and check block candidates before previous block is fully signed (not fully activated yet). -3. Introduced custom block compression algorithm. -4. Overlay improvements: improved overlay discovery on shard configuration update, private externals in custom overlays. -5. Various improvements: session stats, telemetry in fast-sync overlay, earlier block broadcasts, limiting ttl for values in DHT, fixing search by utime in native blockexplorer, faster downloading candidates in validator session, parallelization of storing to cell_db, avoiding touching packfiles on startup. +1. [TVM version v12](./doc/GlobalVersions.md) update: [forbid unused high bits in extra_flags](https://github.com/ton-blockchain/TEPs/pull/503/commits/d949d70d5a69026d273cbbc07653d12c4373117a), [bounce extra_flags equal to initial message extra_flags](https://github.com/ton-blockchain/TEPs/pull/503/commits/d33ff342d69de04f1c33d11360dcf06b63a6c21e), [new TVM opcodes](https://github.com/ton-blockchain/ton/commit/ecd8fbb833c408eb34ec1aa4516e9e4344b54a22). +2. Abseil upgrade +3. Improvements in node synchronisation +4. Fixing rare ArchiveManager issues +5. Various improvements in logging, builds, DHT node behavior, private net launching, failure handlers. -Besides the work of the core team, this update is based on the efforts of the Tonstudio team: @hacker-volodya @Shvandre; and @mkiesel (avoiding touching packfiles on startup). +Besides the work of the core team, this update is based on the efforts of the @Lapo4kaKek and [Vahagn x.com/vah_13](https://x.com/vah_13). diff --git a/rldp/CMakeLists.txt b/rldp/CMakeLists.txt index 688476328..82e238d3c 100644 --- a/rldp/CMakeLists.txt +++ b/rldp/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(RLDP_SOURCE rldp.cpp rldp-peer.cpp diff --git a/rldp2/CMakeLists.txt b/rldp2/CMakeLists.txt index 984e7bb1a..bf0c212c2 100644 --- a/rldp2/CMakeLists.txt +++ b/rldp2/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - if (NOT GSL_FOUND) find_package(GSL) endif() diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt index 5a53e7398..9bd16356e 100644 --- a/storage/CMakeLists.txt +++ b/storage/CMakeLists.txt @@ -1,8 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - - set(STORAGE_SOURCE LoadSpeed.cpp MerkleTree.cpp diff --git a/storage/storage-daemon/CMakeLists.txt b/storage/storage-daemon/CMakeLists.txt index 5392d4ab9..c4f28dea5 100644 --- a/storage/storage-daemon/CMakeLists.txt +++ b/storage/storage-daemon/CMakeLists.txt @@ -5,7 +5,7 @@ add_custom_command( COMMAND embed-provider-code smartcont/storage-provider-code.boc smartcont/provider-code.h COMMENT "Generate provider-code.h" DEPENDS embed-provider-code smartcont/storage-provider-code.boc - OUTPUT smartcont/provider-code.h + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/smartcont/provider-code.h" ) set(STORAGE_DAEMON_SOURCE diff --git a/suppression_mappings.txt b/suppression_mappings.txt new file mode 100644 index 000000000..2fc34a30a --- /dev/null +++ b/suppression_mappings.txt @@ -0,0 +1,94 @@ +[deprecated-literal-operator] +src:*/tdutils/td/utils/date.h + +[missing-field-initializers] +src:*/emulator/test/emulator-tests.cpp + +[missing-designated-field-initializers] +src:*/tdutils/td/utils/Timer.cpp +src:*/validator/db/permanent-celldb/permanent-celldb-utils.cpp +src:*/validator/validator-options.cpp + +[implicit-int-float-conversion] +src:*/overlay/overlay.cpp +src:*/tdactor/td/actor/ActorStats.cpp +src:*/tdactor/td/actor/ActorStats.h +src:*/tonlib/test/online.cpp + +[unused-variable] +src:*/crypto/block/precompiled-smc/PrecompiledSmartContract.cpp +src:*/crypto/test/test-db.cpp +src:*/crypto/test/test-smartcont.cpp +src:*/crypto/tl/tlbc-gen-cpp.cpp +src:*/crypto/vm/boc-compression.cpp +src:*/crypto/vm/large-boc-serializer.cpp +src:*/tdactor/td/actor/ActorStats.cpp +src:*/validator/full-node-fast-sync-overlays.cpp + +[unused-but-set-variable] +src:*/tdtl/td/tl/tl_config.cpp +src:*/tdtl/td/tl/tl_generate.cpp +src:*/tdutils/test/misc.cpp + +[return-type] +src:*/tl/generate/tl_writer_hpp.cpp + +[deprecated-declarations] +src:*/tdutils/test/crypto.cpp +src:*/third-party/rocksdb/db/memtable.cc + +[implicit-int-conversion] +src:*/crypto/vm/boc-compression.cpp +src:*/crypto/vm/boc.cpp + +[shorten-64-to-32] +src:*/crypto/parser/lexer.cpp +src:*/crypto/vm/boc-compression.cpp +src:*/crypto/vm/dict.cpp +src:*/lite-client/lite-client.cpp +src:*/tl/generate/auto/tl/tonlib_api.h +src:*/tl/tl/TlObject.h +src:*/tonlib/tonlib/TonlibClient.cpp +src:*/validator-session/validator-session.cpp + +[sign-compare] +src:*/crypto/block/transaction.cpp +src:*/crypto/vm/boc-compression.cpp +src:*/crypto/vm/dict.cpp +src:*/lite-client/lite-client.cpp +src:*/tonlib/tonlib/TonlibClient.cpp +src:*/validator-session/candidate-serializer.cpp +src:*/validator/db/package.cpp +src:*/validator/db/permanent-celldb/permanent-celldb-utils.cpp +src:*/validator/full-node-fast-sync-overlays.cpp + +[nontrivial-memcall] +src:*/third-party/rocksdb/port/mmap.cc + +[unused-const-variable] +src:*/catchain/catchain-receiver.cpp +src:*/crypto/block/block-parse.cpp + +[unused-function] +src:*/crypto/vm/db/DynamicBagOfCellsDb.cpp + +[logical-op-parentheses] +src:*/validator/import-db-slice-local.cpp + +[float-conversion] +src:*/validator-engine-console/validator-engine-console-query.cpp +src:*/validator/db/archive-manager.cpp + +[deprecated-this-capture] +src:*/tonlib/tonlib/TonlibClient.cpp +src:*/validator/impl/liteserver.cpp + +[unused-result] +src:*/crypto/test/test-cells.cpp + +[deprecated-enum-float-conversion] +src:*/validator/impl/liteserver.cpp + +[unused-lambda-capture] +src:*/lite-client/lite-client.cpp +src:*/storage/storage-daemon/StorageProvider.cpp diff --git a/tddb/CMakeLists.txt b/tddb/CMakeLists.txt index 57e69403b..ca4cbd48a 100644 --- a/tddb/CMakeLists.txt +++ b/tddb/CMakeLists.txt @@ -58,7 +58,6 @@ if (TDDB_USE_ROCKSDB) target_sources(tddb PRIVATE ${TDDB_ROCKSDB_SOURCE}) target_compile_definitions(tddb PUBLIC -DTDDB_USE_ROCKSDB) target_link_libraries(tddb PUBLIC rocksdb) - target_include_directories(tddb PRIVATE $) endif() add_executable(io-bench test/io-bench.cpp) diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index e1d0c87b8..2450eb4a5 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -1,5 +1,7 @@ option(TDUTILS_MIME_TYPE "Generate mime types conversion (gperf is required)" ON) +set(TD_HAVE_OPENSSL 1) + if (WIN32) if (WINGETOPT_FOUND) set(TD_HAVE_GETOPT 1) @@ -19,14 +21,6 @@ endif() if (ZLIB_FOUND) set(TD_HAVE_ZLIB 1) message(STATUS "Found ZLIB: ${ZLIB_INCLUDE_DIR} ${ZLIB_LIBRARIES}") - - # OpenSSL internally depends on zlib - if (NOT OPENSSL_FOUND) - find_package(OpenSSL) - endif() - if (OPENSSL_FOUND) - set(TD_HAVE_OPENSSL 1) - endif() endif() if (CRC32C_FOUND) diff --git a/tdutils/td/utils/port/signals.cpp b/tdutils/td/utils/port/signals.cpp index 12962f29e..1bb485bec 100644 --- a/tdutils/td/utils/port/signals.cpp +++ b/tdutils/td/utils/port/signals.cpp @@ -317,7 +317,26 @@ static void block_stdin() { #endif } +#if TD_PORT_POSIX +[[maybe_unused]] static void init_alarm_signal() { + struct sigaction act; + act.sa_handler = [](int) { + signal_safe_write("Signal handler timeout\n"); + block_stdin(); + _Exit(EXIT_FAILURE); + }; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESETHAND | SA_ONSTACK; + sigaction(SIGALRM, &act, nullptr); + alarm(3); +} +#endif + [[maybe_unused]] static void default_failure_signal_handler(int sig) { +#if TD_PORT_POSIX + init_alarm_signal(); +#endif + Stacktrace::init(); signal_safe_write_signal_number(sig); @@ -325,6 +344,9 @@ static void block_stdin() { options.use_gdb = true; Stacktrace::print_to_stderr(options); +#if TD_PORT_POSIX + alarm(0); +#endif block_stdin(); _Exit(EXIT_FAILURE); } diff --git a/terminal/CMakeLists.txt b/terminal/CMakeLists.txt index e589cc0bb..49437219a 100644 --- a/terminal/CMakeLists.txt +++ b/terminal/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(TERMINAL_SOURCE terminal.cpp diff --git a/test/regression-tests.ans b/test/regression-tests.ans index 16db1c9f1..8fccc23ab 100644 --- a/test/regression-tests.ans +++ b/test/regression-tests.ans @@ -20,6 +20,7 @@ Test_Fift_test_get_extra_balance_default cc2428172b660ae66489e1b4868786654195137 Test_Fift_test_hash_ext_default 686fc5680feca5b3bb207768215b27f6872a95128762dee0d7f2c88bc492d62d Test_Fift_test_hmap_default c269246882039824bb5822e896c3e6e82ef8e1251b6b251f5af8ea9fb8d05067 Test_Fift_test_levels_default 9fba4a7c98aec9000f42846d6e5fd820343ba61d68f9139dd16c88ccda757cf3 +Test_Fift_test_load_store_std_addr_default 02a56bbe0e8db387858d4c6f111650f8470b9c22c82c0c0afcfc6c8da487e563 Test_Fift_test_namespaces_default e6419619c51332fb5e8bf22043ef415db686c47fe24f03061e5ad831014e7c6c Test_Fift_test_p256_default e1948ddd3d2686baa9f70fdf376ffcebbc2ec5f20eeb366cd856254e61fbfa31 Test_Fift_test_rist255_default f4d7558f200a656934f986145c19b1dedbe2ad029292a5a975576d6891e25fc4 diff --git a/third-party/abseil-cpp b/third-party/abseil-cpp index 8c6e53ef3..d38452e1e 160000 --- a/third-party/abseil-cpp +++ b/third-party/abseil-cpp @@ -1 +1 @@ -Subproject commit 8c6e53ef3adb1227fffa442c50349dab134a54bc +Subproject commit d38452e1ee03523a208362186fd42248ff2609f6 diff --git a/third-party/tl-parser b/third-party/tl-parser new file mode 160000 index 000000000..aba892588 --- /dev/null +++ b/third-party/tl-parser @@ -0,0 +1 @@ +Subproject commit aba8925886a599b439f0a9a919abdaa363b8cb2e diff --git a/tl/generate/CMakeLists.txt b/tl/generate/CMakeLists.txt index 8ff7f79a4..4dac089e2 100644 --- a/tl/generate/CMakeLists.txt +++ b/tl/generate/CMakeLists.txt @@ -60,7 +60,14 @@ add_executable(tonlib_generate_java_api ${TL_GENERATE_JAVA_SOURCE}) target_link_libraries(tonlib_generate_java_api PRIVATE tdtl) if (NOT CMAKE_CROSSCOMPILING) - find_program(PHP_EXECUTABLE php) + foreach(tl_scheme lite_api ton_api tonlib_api) + add_custom_command( + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/scheme + COMMAND $ -e "${tl_scheme}.tlo" "${tl_scheme}.tl" + DEPENDS tl-parser "scheme/${tl_scheme}.tl" + OUTPUT "${CMAKE_CURRENT_SOURCE_DIR}/scheme/${tl_scheme}.tlo" + ) + endforeach() set(GENERATE_COMMON_CMD generate_common) add_executable(generate_common ${TL_GENERATE_COMMON_SOURCE}) diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo deleted file mode 100644 index 2d1973a9b..000000000 Binary files a/tl/generate/scheme/lite_api.tlo and /dev/null differ diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo deleted file mode 100644 index 2f383fd3a..000000000 Binary files a/tl/generate/scheme/ton_api.tlo and /dev/null differ diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo deleted file mode 100644 index a92727eb2..000000000 Binary files a/tl/generate/scheme/tonlib_api.tlo and /dev/null differ diff --git a/tl/generate/scheme/update-tlo.sh b/tl/generate/scheme/update-tlo.sh deleted file mode 100755 index decc73b18..000000000 --- a/tl/generate/scheme/update-tlo.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -cd $(dirname $0) -tl-parser -e ton_api.tlo ton_api.tl -tl-parser -e tonlib_api.tlo tonlib_api.tl -tl-parser -e lite_api.tlo lite_api.tl diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index f28176223..a59bd9459 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -1,9 +1,5 @@ option(TONLIBJSON_STATIC "Build tonlibjson as static library" OFF) -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(TONLIB_SOURCE tonlib/Client.cpp tonlib/Config.cpp diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index d28cb805d..bef1d7ce9 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -5990,10 +5990,10 @@ td::Status check_lookup_block_proof(lite_api_ptr utime) { - return td::Status::Error("prev header end_lt > lt"); + return td::Status::Error("prev header gen_utime > utime"); } if (info.gen_utime < utime) { - return td::Status::Error("header end_lt < lt"); + return td::Status::Error("header gen_utime < utime"); } } } diff --git a/tvm-python/PyEmulator.cpp b/tvm-python/PyEmulator.cpp index 61fed3606..d7b66c525 100644 --- a/tvm-python/PyEmulator.cpp +++ b/tvm-python/PyEmulator.cpp @@ -45,6 +45,11 @@ bool PyEmulator::set_ignore_chksig(bool ignore_chksig) { return true; } +bool PyEmulator::set_vm_verbosity_level(int verbosity) { + emulator->set_vm_verbosity_level(verbosity); + return true; +} + bool PyEmulator::set_libs(const PyCell &shardchain_libs_cell) { emulator->set_libs(vm::Dictionary(shardchain_libs_cell.my_cell, 256)); return true; @@ -151,6 +156,7 @@ bool PyEmulator::emulate_transaction(const PyCell &shard_account_cell, const PyC } const auto &emulation_success = dynamic_cast(*emulation_result); + vm_log = emulation_success.vm_log; transaction_cell = std::move(emulation_success.transaction); auto new_shard_account_cell = vm::CellBuilder() @@ -225,7 +231,8 @@ bool PyEmulator::emulate_tick_tock_transaction(const PyCell &shard_account_boc, } const auto &emulation_success = dynamic_cast(*emulation_result); - transaction_cell = std::move(emulation_success.transaction); + vm_log = emulation_success.vm_log; + transaction_cell = emulation_success.transaction; auto new_shard_account_cell = vm::CellBuilder() .store_ref(emulation_success.account.total_state) diff --git a/tvm-python/PyEmulator.h b/tvm-python/PyEmulator.h index 748a9d73a..b54849726 100644 --- a/tvm-python/PyEmulator.h +++ b/tvm-python/PyEmulator.h @@ -59,6 +59,7 @@ class PyEmulator { ~PyEmulator() = default; bool set_rand_seed(const std::string& rand_seed_hex); + bool set_vm_verbosity_level(int verbosity); bool set_ignore_chksig(bool ignore_chksig); bool set_libs(const PyCell& shardchain_libs_cell); bool set_debug_enabled(bool debug_enabled); diff --git a/tvm-python/PyGlobal.cpp b/tvm-python/PyGlobal.cpp index a119557cf..7f82f1c22 100644 --- a/tvm-python/PyGlobal.cpp +++ b/tvm-python/PyGlobal.cpp @@ -2,69 +2,77 @@ #include #include - +#include "third-party/pybind11/include/pybind11/pybind11.h" #include "PyGlobal.h" std::mutex scheduler_init_mutex; bool scheduler_running = false; static std::unique_ptr thread_local_scheduler; static std::unique_ptr scheduler_thread; +namespace py = pybind11; namespace pyglobal { - class Runner : public td::actor::Actor { - public: - explicit Runner(std::function f) : f_(std::move(f)) { - } +class Runner : public td::actor::Actor { +public: + explicit Runner(std::function f) + : f_(std::move(f)) { + } - void start_up() override { - f_(); - stop(); - } + ~Runner() override { + py::gil_scoped_acquire gil; + f_ = {}; + } - private: - std::function f_; - }; + void start_up() override { + f_(); + stop(); + } - void init_thread_scheduler(int thread_count) { - std::lock_guard lock(scheduler_init_mutex); - if (!thread_local_scheduler) { - thread_local_scheduler = std::unique_ptr(new td::actor::Scheduler({thread_count})); - scheduler_running = true; +private: + std::function f_; +}; - scheduler_thread = std::make_unique([&] { - thread_local_scheduler->run(); - }); - } - } - td::actor::Scheduler *get_thread_scheduler() { - if (!scheduler_running) { - init_thread_scheduler(6); - } - return thread_local_scheduler.get(); - } +void init_thread_scheduler(int thread_count) { + std::lock_guard lock(scheduler_init_mutex); + if (!thread_local_scheduler) { + thread_local_scheduler = std::unique_ptr(new td::actor::Scheduler({thread_count})); + scheduler_running = true; - void stop_scheduler_thread() { - if (scheduler_running) { - std::lock_guard lock(scheduler_init_mutex); - if (thread_local_scheduler) { - thread_local_scheduler->run_in_context_external([] { - td::actor::SchedulerContext::get()->stop(); - }); - } - if (scheduler_thread) { - scheduler_thread->join(); - scheduler_thread.reset(); - } - thread_local_scheduler.reset(); - scheduler_running = false; - } - } + scheduler_thread = std::make_unique([&] { + thread_local_scheduler->run(); + }); + } +} - void execute_async(std::function f) { - get_thread_scheduler()->run_in_context_external([&] { - td::actor::create_actor("executeasync", std::move(f)).release(); +td::actor::Scheduler* get_thread_scheduler() { + if (!scheduler_running) { + init_thread_scheduler(6); + } + return thread_local_scheduler.get(); +} + +void stop_scheduler_thread() { + if (scheduler_running) { + std::lock_guard lock(scheduler_init_mutex); + if (thread_local_scheduler) { + thread_local_scheduler->run_in_context_external([] { + td::actor::SchedulerContext::get()->stop(); }); } + if (scheduler_thread) { + scheduler_thread->join(); + scheduler_thread.reset(); + } + thread_local_scheduler.reset(); + scheduler_running = false; + } +} + +void execute_async(std::function f) { + get_thread_scheduler()->run_in_context_external([&] { + td::actor::create_actor("executeasync", std::move(f)).release(); + }); } +} \ No newline at end of file diff --git a/tvm-python/PyTVM.cpp b/tvm-python/PyTVM.cpp index 19ae6e96a..3292cce0e 100644 --- a/tvm-python/PyTVM.cpp +++ b/tvm-python/PyTVM.cpp @@ -35,8 +35,10 @@ void PyTVM::set_c7(PyStackEntry x) { void PyTVM::log(const std::string &log_string, int level) { if (log_level >= level && level == LOG_INFO) { + py::gil_scoped_acquire gil; py::print("INFO: " + log_string); } else if (log_level >= level && level == LOG_DEBUG) { + py::gil_scoped_acquire gil; py::print("DEBUG: " + log_string); } } @@ -129,6 +131,7 @@ class PythonLogger : public td::LogInterface { } if (!muted) { + py::gil_scoped_acquire gil; py::print(slice.str()); } } diff --git a/tvm-python/python_ton.cpp b/tvm-python/python_ton.cpp index 311b50c6d..f05d4fd6f 100644 --- a/tvm-python/python_ton.cpp +++ b/tvm-python/python_ton.cpp @@ -23,7 +23,7 @@ #include "PyGlobal.h" namespace py = pybind11; -using namespace pybind11::literals; // to bring in the `_a` literal +using namespace pybind11::literals; // to bring in the `_a` literal void globalSetVerbosity(int vb) { int v = VERBOSITY_NAME(FATAL) + vb; @@ -55,27 +55,63 @@ py::object async_wrapper(Func&& func, Args&&... args) { future.inc_ref(); pyglobal::execute_async([loop, future, func = std::forward(func), args...]() mutable { - try { - auto result = func(std::forward(args)...); + try { + auto result = func(std::forward(args)...); - { - py::gil_scoped_acquire acquire; - auto call_soon_threadsafe = loop.attr("call_soon_threadsafe"); - call_soon_threadsafe(future.attr("set_result"), py::cast(std::move(result))); - } - } catch (const std::exception& e) { + { py::gil_scoped_acquire acquire; - future.attr("set_exception")(py::cast(std::runtime_error(e.what()))); + auto call_soon_threadsafe = loop.attr("call_soon_threadsafe"); + call_soon_threadsafe(future.attr("set_result"), py::cast(std::move(result))); } + } catch (const std::exception& e) { + py::gil_scoped_acquire acquire; + future.attr("set_exception")(py::cast(std::runtime_error(e.what()))); + } }); return future; } +class LogCollector : public td::LogInterface { +public: + void append(td::CSlice slice) override { + if (slice.empty()) + return; + + std::lock_guard lock(mu_); + out_.emplace_back(slice.str()); + + // keep only latest 1000 + if (out_.size() > max_size_) { + out_.erase(out_.begin(), out_.end() - max_size_); + } + } + + std::vector get_logs() { + std::lock_guard lock(mu_); + return out_; + } + +private: + std::vector out_; + std::mutex mu_; + const size_t max_size_ = 1000; +}; + +LogCollector global_log_collector; + +bool create_log_collector() { + td::log_interface = &global_log_collector; + return true; +} + +std::vector get_logs() { + return global_log_collector.get_logs(); +} template struct py::detail::type_caster> { - public: +public: PYBIND11_TYPE_CASTER(td::optional, _("td::optional")); bool load(handle src, bool) { @@ -316,8 +352,10 @@ PYBIND11_MODULE(python_ton, m) { .def("clear_stack", &PyTVM::clear_stack) .def("set_gasLimit", &PyTVM::set_gasLimit, py::arg("gas_limit") = "0", py::arg("gas_max") = "-1") .def("run_vm", &PyTVM::run_vm) - .def("arun_vm", [](PyTVM &self) { - return async_wrapper([&self]() { return self.run_vm(); }); + .def("arun_vm", [](PyTVM& self) { + return async_wrapper([&self]() { + return self.run_vm(); + }); }) .def("get_stacks", &PyTVM::get_stacks) .def("set_c7", &PyTVM::set_c7, py::arg("stack")) @@ -375,21 +413,22 @@ PYBIND11_MODULE(python_ton, m) { .def("set_libs", &PyEmulator::set_libs, py::arg("shardchain_libs_boc")) .def("set_prev_blocks_info", &PyEmulator::set_prev_blocks_info, py::arg("prev_blocks")) .def("set_debug_enabled", &PyEmulator::set_debug_enabled, py::arg("debug_enabled")) + .def("set_vm_verbosity_level", &PyEmulator::set_vm_verbosity_level, py::arg("vm_verbosity")) .def("emulate_transaction", &PyEmulator::emulate_transaction, py::arg("shard_account_cell"), py::arg("message_cell"), py::arg("unixtime") = "0", py::arg("lt") = "0", py::arg("vm_ver") = 1, py::arg("force_uninit") = false) .def("aemulate_transaction", - [](PyEmulator &self, + [](PyEmulator& self, const PyCell& shard_account_cell, const PyCell& message_cell, const std::string& unixtime, const std::string& lt, int vm_ver, bool force_uninit) { - return async_wrapper([=, &self]() { - return self.emulate_transaction( - shard_account_cell, message_cell, unixtime, lt, vm_ver, force_uninit); - }); + return async_wrapper([=, &self]() { + return self.emulate_transaction( + shard_account_cell, message_cell, unixtime, lt, vm_ver, force_uninit); + }); }, py::arg("shard_account_cell"), py::arg("message_cell"), @@ -400,16 +439,16 @@ PYBIND11_MODULE(python_ton, m) { .def("emulate_tick_tock_transaction", &PyEmulator::emulate_tick_tock_transaction, py::arg("shard_account_boc"), py::arg("is_tock"), py::arg("unixtime") = "0", py::arg("lt") = "0", py::arg("vm_ver") = 1) .def("aemulate_tick_tock_transaction", - [](PyEmulator &self, + [](PyEmulator& self, const PyCell& shard_account_boc, bool is_tock, const std::string& unixtime, const std::string& lt, int vm_ver) { - return async_wrapper([=, &self]() { - return self.emulate_tick_tock_transaction( - shard_account_boc, is_tock, unixtime, lt, vm_ver); - }); + return async_wrapper([=, &self]() { + return self.emulate_tick_tock_transaction( + shard_account_boc, is_tock, unixtime, lt, vm_ver); + }); }, py::arg("shard_account_boc"), py::arg("is_tock"), @@ -465,15 +504,23 @@ PYBIND11_MODULE(python_ton, m) { .def_readonly("workchain", &ton::BlockId::workchain) .def_readonly("shard", &ton::BlockId::shard) .def_readonly("seqno", &ton::BlockId::seqno) - .def("__str__", [](ton::BlockId obj) -> std::string { return obj.to_str(); }) - .def("to_string", [](ton::BlockId obj) -> std::string { return obj.to_str(); }); + .def("__str__", [](ton::BlockId obj) -> std::string { + return obj.to_str(); + }) + .def("to_string", [](ton::BlockId obj) -> std::string { + return obj.to_str(); + }); py::class_(m, "BlockHdrInfo", py::module_local()) .def_readonly("blk_id", &TestNode::BlockHdrInfo::blk_id) .def_readonly("mode", &TestNode::BlockHdrInfo::mode) - .def_property_readonly("proof", [](TestNode::BlockHdrInfo obj) -> PyCell { return PyCell(obj.proof); }) + .def_property_readonly("proof", [](TestNode::BlockHdrInfo obj) -> PyCell { + return PyCell(obj.proof); + }) .def_property_readonly("virt_blk_root", - [](TestNode::BlockHdrInfo obj) -> PyCell { return PyCell(obj.virt_blk_root); }); + [](TestNode::BlockHdrInfo obj) -> PyCell { + return PyCell(obj.virt_blk_root); + }); py::class_(m, "BlockIdExt", py::module_local()) .def(py::init([](ton::BlockId id_, std::string root_hash_, std::string file_hash_) -> ton::BlockIdExt { @@ -490,10 +537,18 @@ PYBIND11_MODULE(python_ton, m) { return ton::BlockIdExt(std::move(id_), std::move(root_hash_bits), std::move(file_hash_bits)); })) .def_readonly("id", &ton::BlockIdExt::id) - .def_property_readonly("root_hash", [](ton::BlockIdExt obj) -> std::string { return obj.root_hash.to_hex(); }) - .def_property_readonly("file_hash", [](ton::BlockIdExt obj) -> std::string { return obj.file_hash.to_hex(); }) - .def("__str__", [](ton::BlockIdExt obj) -> std::string { return obj.to_str(); }) - .def("to_string", [](ton::BlockIdExt obj) -> std::string { return obj.to_str(); }); + .def_property_readonly("root_hash", [](ton::BlockIdExt obj) -> std::string { + return obj.root_hash.to_hex(); + }) + .def_property_readonly("file_hash", [](ton::BlockIdExt obj) -> std::string { + return obj.file_hash.to_hex(); + }) + .def("__str__", [](ton::BlockIdExt obj) -> std::string { + return obj.to_str(); + }) + .def("to_string", [](ton::BlockIdExt obj) -> std::string { + return obj.to_str(); + }); py::class_(m, "liteServer_masterchainInfoExt", py::module_local()) .def(py::init<>()) @@ -514,34 +569,60 @@ PYBIND11_MODULE(python_ton, m) { py::class_(m, "block_AccountState_Info", py::module_local()) .def(py::init<>()) .def_property_readonly("last_trans_lt", - [](const block::AccountState::Info& obj) -> long long { return obj.last_trans_lt; }) + [](const block::AccountState::Info& obj) -> long long { + return obj.last_trans_lt; + }) .def_property_readonly( "last_trans_hash", - [](const block::AccountState::Info& obj) -> std::string { return obj.last_trans_hash.to_hex(); }) - .def_property_readonly("gen_lt", [](const block::AccountState::Info& obj) -> long long { return obj.gen_lt; }) + [](const block::AccountState::Info& obj) -> std::string { + return obj.last_trans_hash.to_hex(); + }) + .def_property_readonly("gen_lt", [](const block::AccountState::Info& obj) -> long long { + return obj.gen_lt; + }) .def_property_readonly("gen_utime", - [](const block::AccountState::Info& obj) -> long long { return obj.gen_utime; }) - .def_property_readonly("root", [](const block::AccountState::Info& obj) -> PyCell { return PyCell(obj.root); }) + [](const block::AccountState::Info& obj) -> long long { + return obj.gen_utime; + }) + .def_property_readonly("root", [](const block::AccountState::Info& obj) -> PyCell { + return PyCell(obj.root); + }) .def_property_readonly("true_root", - [](const block::AccountState::Info& obj) -> PyCell { return PyCell(obj.true_root); }); + [](const block::AccountState::Info& obj) -> PyCell { + return PyCell(obj.true_root); + }); py::class_(m, "block_Transaction_Info", py::module_local()) .def(py::init<>()) - .def_property_readonly("blkid", [](const block::Transaction::Info& obj) -> ton::BlockIdExt { return obj.blkid; }) + .def_property_readonly("blkid", [](const block::Transaction::Info& obj) -> ton::BlockIdExt { + return obj.blkid; + }) .def_property_readonly( - "prev_trans_lt", [](const block::Transaction::Info& obj) -> unsigned long long { return obj.prev_trans_lt; }) - .def_property_readonly("now", [](const block::Transaction::Info& obj) -> unsigned long long { return obj.now; }) + "prev_trans_lt", [](const block::Transaction::Info& obj) -> unsigned long long { + return obj.prev_trans_lt; + }) + .def_property_readonly("now", [](const block::Transaction::Info& obj) -> unsigned long long { + return obj.now; + }) .def_property_readonly( "prev_trans_hash", - [](const block::Transaction::Info& obj) -> std::string { return obj.prev_trans_hash.to_hex(); }) + [](const block::Transaction::Info& obj) -> std::string { + return obj.prev_trans_hash.to_hex(); + }) .def_property_readonly("transaction", - [](const block::Transaction::Info& obj) -> PyCell { return PyCell(obj.transaction); }); + [](const block::Transaction::Info& obj) -> PyCell { + return PyCell(obj.transaction); + }); py::class_(m, "block_TransactionList_Info", py::module_local()) .def(py::init<>()) - .def_property_readonly("lt", [](const block::TransactionList::Info& obj) -> unsigned long long { return obj.lt; }) + .def_property_readonly("lt", [](const block::TransactionList::Info& obj) -> unsigned long long { + return obj.lt; + }) .def_property_readonly("hash", - [](const block::TransactionList::Info& obj) -> std::string { return obj.hash.to_hex(); }) + [](const block::TransactionList::Info& obj) -> std::string { + return obj.hash.to_hex(); + }) .def_property_readonly("transactions", [](const block::TransactionList::Info& obj) -> std::vector { return obj.transactions; @@ -603,6 +684,9 @@ PYBIND11_MODULE(python_ton, m) { m.def("init_thread_scheduler", pyglobal::init_thread_scheduler); m.def("stop_scheduler_thread", pyglobal::stop_scheduler_thread); + m.def("create_log_collector", create_log_collector); + m.def("get_logs", get_logs); + m.def("cleanup", &cleanup); py::module::import("atexit").attr("register")(py::cpp_function(cleanup)); -} +} \ No newline at end of file diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 0afdf7389..284b61217 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - add_executable(generate-random-id generate-random-id.cpp ) target_link_libraries(generate-random-id tl_api ton_crypto keys adnl git) diff --git a/validator-engine/CMakeLists.txt b/validator-engine/CMakeLists.txt index 07ed478b4..6a0703727 100644 --- a/validator-engine/CMakeLists.txt +++ b/validator-engine/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif () - if (NIX) find_package(PkgConfig REQUIRED) pkg_check_modules(MHD REQUIRED libmicrohttpd) diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 857b4a0e6..0d282fc24 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -2305,7 +2305,8 @@ void ValidatorEngine::start_full_node() { ton::validator::fullnode::FullNodeOptions full_node_options{ .config_ = config_.full_node_config, .public_broadcast_speed_multiplier_ = broadcast_speed_multiplier_public_, - .private_broadcast_speed_multiplier_ = broadcast_speed_multiplier_private_}; + .private_broadcast_speed_multiplier_ = broadcast_speed_multiplier_private_, + .initial_sync_delay_ = initial_sync_delay_}; full_node_ = ton::validator::fullnode::FullNode::create( short_id, full_node_id_, validator_options_->zero_block_id().file_hash, full_node_options, keyring_.get(), adnl_.get(), rldp_.get(), rldp2_.get(), @@ -5337,7 +5338,7 @@ int main(int argc, char *argv[]) { td::log_interface = td::default_log_interface; }; - LOG_STATUS(td::change_maximize_rlimit(td::RlimitType::nofile, 786432)); + LOG_STATUS(td::change_maximize_rlimit(td::RlimitType::nofile, 1572864)); std::vector> acts; @@ -5716,6 +5717,16 @@ int main(int argc, char *argv[]) { "don't select the best persistent state on initial sync, start on init_block from global config", [&]() { acts.push_back([&x]() { td::actor::send_closure(x, &ValidatorEngine::set_skip_key_sync, true); }); }); + p.add_checked_option( + '\0', "initial-sync-delay", "delay before initial sync completion (in seconds, default: 60.0)", + [&](td::Slice s) -> td::Status { + auto v = td::to_double(s); + if (v < 0) { + return td::Status::Error("initial-sync-delay should be non-negative"); + } + acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_initial_sync_delay, v); }); + return td::Status::OK(); + }); p.add_checked_option( '\0', "sync-shards-upto", "stop syncing shards on this masterchain seqno", [&](td::Slice s) -> td::Status { TRY_RESULT(v, td::to_integer_safe(s)); diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 1c66e0357..65537449a 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -270,6 +270,7 @@ class ValidatorEngine : public td::actor::Actor { td::optional sync_shards_upto_; ton::adnl::AdnlNodeIdShort shard_block_retainer_adnl_id_ = ton::adnl::AdnlNodeIdShort::zero(); bool shard_block_retainer_adnl_id_fullnode_ = false; + double initial_sync_delay_ = 60.0; std::set unsafe_catchains_; std::map> unsafe_catchain_rotations_; @@ -404,6 +405,9 @@ class ValidatorEngine : public td::actor::Actor { void set_shard_block_retainer_adnl_id_fullnode() { shard_block_retainer_adnl_id_fullnode_ = true; } + void set_initial_sync_delay(double value) { + initial_sync_delay_ = value; + } void set_prometheus_port(td::uint32 port){ prometheus_available_ = true; diff --git a/validator-session/CMakeLists.txt b/validator-session/CMakeLists.txt index 6d4b00bd6..c57fad54e 100644 --- a/validator-session/CMakeLists.txt +++ b/validator-session/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - set(VALIDATOR_SESSION_SOURCE candidate-serializer.cpp persistent-vector.cpp diff --git a/validator/CMakeLists.txt b/validator/CMakeLists.txt index 0aeef4f9f..095c6d2ac 100644 --- a/validator/CMakeLists.txt +++ b/validator/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - if (NIX) find_package(PkgConfig REQUIRED) pkg_check_modules(MHD REQUIRED libmicrohttpd) @@ -124,8 +120,6 @@ set(VALIDATOR_SOURCE manager-init.cpp manager.cpp - ${VALIDATOR_DB_SOURCE} - ${VALIDATOR_HEADERS} ) @@ -146,8 +140,6 @@ set(DISK_VALIDATOR_SOURCE lite-server-rate-limiter.cpp manager-init.cpp manager-disk.cpp - - ${VALIDATOR_DB_SOURCE} ) set(HARDFORK_VALIDATOR_SOURCE @@ -166,8 +158,6 @@ set(HARDFORK_VALIDATOR_SOURCE manager-init.cpp manager-hardfork.cpp - - ${VALIDATOR_DB_SOURCE} ) set(FULL_NODE_SOURCE @@ -203,6 +193,10 @@ set(FULL_NODE_SOURCE net/get-next-key-blocks.cpp ) +add_library(validator-db OBJECT ${VALIDATOR_DB_SOURCE}) +target_include_directories(validator-db PUBLIC $) +target_link_libraries(validator-db PUBLIC tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession ton_db) + add_library(validator STATIC ${VALIDATOR_SOURCE}) add_library(validator-disk STATIC ${DISK_VALIDATOR_SOURCE}) add_library(validator-hardfork STATIC ${HARDFORK_VALIDATOR_SOURCE}) @@ -240,10 +234,11 @@ target_include_directories(full-node PUBLIC ${MHD_INCLUDE_DIR} ${MHD_STATIC_INCL target_include_directories(validator-hardfork PUBLIC ${MHD_INCLUDE_DIR} ${MHD_STATIC_INCLUDE_DIRS}) target_include_directories(validator-disk PUBLIC ${MHD_INCLUDE_DIR} ${MHD_STATIC_INCLUDE_DIRS}) target_include_directories(validator PUBLIC ${MHD_INCLUDE_DIR} ${MHD_STATIC_INCLUDE_DIRS}) +target_link_libraries(validator PRIVATE validator-db) -target_link_libraries(validator PRIVATE ${MHD_LIBRARY} ${MHD_STATIC_LIBRARIES} tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession ton_db) +target_link_libraries(validator-disk PRIVATE validator-db) -target_link_libraries(validator-disk PRIVATE ${MHD_LIBRARY} ${MHD_STATIC_LIBRARIES} ton_validator tdutils tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession ton_db) +target_link_libraries(validator-hardfork PRIVATE validator-db) target_link_libraries(validator-hardfork PRIVATE ${MHD_LIBRARY} ${MHD_STATIC_LIBRARIES} tdactor adnl rldp tl_api dht tdfec overlay catchain validatorsession ton_db) diff --git a/validator/apply-block.cpp b/validator/apply-block.cpp index 8c45f851e..c22875665 100644 --- a/validator/apply-block.cpp +++ b/validator/apply-block.cpp @@ -29,14 +29,14 @@ namespace validator { void ApplyBlock::abort_query(td::Status reason) { if (promise_) { - VLOG(VALIDATOR_WARNING) << "aborting apply block query for " << id_ << ": " << reason; + VLOG(VALIDATOR_WARNING) << "aborting apply block query for " << id_.to_str() << ": " << reason; promise_.set_error(std::move(reason)); } stop(); } void ApplyBlock::finish_query() { - VLOG(VALIDATOR_DEBUG) << "successfully finishing apply block query"; + VLOG(VALIDATOR_DEBUG) << "successfully finishing apply block query in " << perf_timer_.elapsed() << " s"; handle_->set_processed(); ValidatorInvariants::check_post_apply(handle_); @@ -51,7 +51,7 @@ void ApplyBlock::alarm() { } void ApplyBlock::start_up() { - VLOG(VALIDATOR_DEBUG) << "running apply_block for " << id_; + VLOG(VALIDATOR_DEBUG) << "running apply_block for " << id_.to_str() << ", mc_seqno=" << masterchain_block_id_.seqno(); if (id_.is_masterchain()) { masterchain_block_id_ = id_; @@ -71,6 +71,7 @@ void ApplyBlock::start_up() { } void ApplyBlock::got_block_handle(BlockHandle handle) { + VLOG(VALIDATOR_DEBUG) << "got_block_handle"; handle_ = std::move(handle); if (handle_->is_applied() && (!handle_->id().is_masterchain() || handle_->processed())) { @@ -79,6 +80,7 @@ void ApplyBlock::got_block_handle(BlockHandle handle) { } if (handle_->is_applied()) { + VLOG(VALIDATOR_DEBUG) << "already applied"; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = handle_->id().id.seqno](td::Result R) { R.ensure(); @@ -94,16 +96,19 @@ void ApplyBlock::got_block_handle(BlockHandle handle) { } if (handle_->id().id.seqno == 0) { + VLOG(VALIDATOR_DEBUG) << "seqno == 0"; written_block_data(); return; } if (handle_->is_archived()) { + VLOG(VALIDATOR_DEBUG) << "already archived"; finish_query(); return; } if (handle_->received()) { + VLOG(VALIDATOR_DEBUG) << "already received"; written_block_data(); return; } @@ -117,6 +122,7 @@ void ApplyBlock::got_block_handle(BlockHandle handle) { } }); + VLOG(VALIDATOR_DEBUG) << "storing block data"; td::actor::send_closure(manager_, &ValidatorManager::set_block_data, handle_, block_, std::move(P)); } else { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result> R) { @@ -128,13 +134,14 @@ void ApplyBlock::got_block_handle(BlockHandle handle) { } }); + VLOG(VALIDATOR_DEBUG) << "wait for block data"; td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, apply_block_priority(), timeout_, std::move(P)); } } void ApplyBlock::written_block_data() { - VLOG(VALIDATOR_DEBUG) << "apply block: written block data for " << id_; + VLOG(VALIDATOR_DEBUG) << "written_block_data"; if (!handle_->id().seqno()) { CHECK(handle_->inited_split_after()); CHECK(handle_->inited_state_root_hash()); @@ -165,24 +172,26 @@ void ApplyBlock::written_block_data() { } }); + VLOG(VALIDATOR_DEBUG) << "wait_block_state"; td::actor::send_closure(manager_, &ValidatorManager::wait_block_state, handle_, apply_block_priority(), timeout_, true, std::move(P)); } } void ApplyBlock::got_cur_state(td::Ref state) { - VLOG(VALIDATOR_DEBUG) << "apply block: received state for " << id_; + VLOG(VALIDATOR_DEBUG) << "got_cur_state"; state_ = std::move(state); CHECK(handle_->received_state()); written_state(); } void ApplyBlock::written_state() { + VLOG(VALIDATOR_DEBUG) << "written_state"; if (handle_->is_applied() && handle_->processed()) { finish_query(); return; } - VLOG(VALIDATOR_DEBUG) << "apply block: setting next for parents of " << id_; + VLOG(VALIDATOR_DEBUG) << "setting next for parents"; if (handle_->id().id.seqno != 0 && !handle_->is_applied()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { @@ -208,12 +217,13 @@ void ApplyBlock::written_state() { } void ApplyBlock::written_next() { + VLOG(VALIDATOR_DEBUG) << "written_next"; if (handle_->is_applied() && handle_->processed()) { finish_query(); return; } - VLOG(VALIDATOR_DEBUG) << "apply block: applying parents of " << id_; + VLOG(VALIDATOR_DEBUG) << "applying parents"; if (handle_->id().id.seqno != 0 && !handle_->is_applied()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { @@ -241,7 +251,8 @@ void ApplyBlock::written_next() { } void ApplyBlock::applied_prev() { - VLOG(VALIDATOR_DEBUG) << "apply block: waiting manager's confirm for " << id_; + VLOG(VALIDATOR_DEBUG) << "applying parents"; + VLOG(VALIDATOR_DEBUG) << "applied_prev, waiting manager's confirm"; if (!id_.is_masterchain()) { handle_->set_masterchain_ref_block(masterchain_block_id_.seqno()); } @@ -253,10 +264,11 @@ void ApplyBlock::applied_prev() { } }); td::actor::send_closure(manager_, &ValidatorManager::new_block, handle_, state_, std::move(P)); + } void ApplyBlock::applied_set() { - VLOG(VALIDATOR_DEBUG) << "apply block: setting apply bit for " << id_; + VLOG(VALIDATOR_DEBUG) << "applied_set"; handle_->set_applied(); auto publisher_ = manager_.get_actor_unsafe().get_block_publisher(); if (publisher_) { @@ -295,6 +307,7 @@ void ApplyBlock::applied_set() { td::actor::send_closure(SelfId, &ApplyBlock::finish_query); } }); + VLOG(VALIDATOR_DEBUG) << "flush handle"; handle_->flush(manager_, handle_, std::move(P)); } else { finish_query(); diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 9632f3fdf..9a0d8d671 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -70,8 +70,9 @@ void ArchiveManager::add_handle(BlockHandle handle, td::Promise promis handle->unix_time(), handle->logical_time(), handle->inited_is_key_block() && handle->is_key_block()) : get_package_id(handle->masterchain_ref_block()); - auto f = get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(), - handle->logical_time(), true); + TRY_RESULT_PROMISE(promise, f, + get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(), + handle->logical_time(), true)); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_handle, std::move(handle), std::move(promise)); } @@ -83,16 +84,17 @@ void ArchiveManager::update_handle(BlockHandle handle, td::Promise pro promise.set_value(td::Unit()); return; } - f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), handle->id().seqno(), - handle->unix_time(), handle->logical_time(), true); - if (!f) { + auto F = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), + handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); + if (F.is_error()) { handle->flushed_upto(handle->version()); promise.set_value(td::Unit()); return; } + f = F.move_as_ok(); } else { - f = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true); - CHECK(f); + TRY_RESULT_PROMISE_ASSIGN(promise, f, + get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true)); } td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::update_handle, std::move(handle), std::move(promise)); } @@ -110,13 +112,17 @@ void ArchiveManager::add_file(BlockHandle handle, FileReference ref_id, td::Buff auto ig = mp.init_guard(); ig.add_promise(std::move(promise)); auto f1 = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true); - td::actor::send_closure(f1->file_actor_id(), &ArchiveSlice::add_file, nullptr, std::move(ref_id), data.clone(), - ig.get_promise()); + if (f1.is_ok()) { + td::actor::send_closure(f1.ok()->file_actor_id(), &ArchiveSlice::add_file, nullptr, std::move(ref_id), + data.clone(), ig.get_promise()); + } if (copy_to_key) { auto f2 = get_file_desc(handle->id().shard_full(), get_key_package_id(handle->masterchain_ref_block()), handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); - td::actor::send_closure(f2->file_actor_id(), &ArchiveSlice::add_file, nullptr, ref_id, std::move(data), - ig.get_promise()); + if (f2.is_ok()) { + td::actor::send_closure(f2.ok()->file_actor_id(), &ArchiveSlice::add_file, nullptr, ref_id, std::move(data), + ig.get_promise()); + } } return; } @@ -128,24 +134,30 @@ void ArchiveManager::add_file(BlockHandle handle, FileReference ref_id, td::Buff ig.add_promise(std::move(promise)); auto f1 = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); - td::actor::send_closure(f1->file_actor_id(), &ArchiveSlice::add_file, handle, ref_id, data.clone(), ig.get_promise()); + if (f1.is_ok()) { + td::actor::send_closure(f1.ok()->file_actor_id(), &ArchiveSlice::add_file, handle, ref_id, data.clone(), + ig.get_promise()); + } if (copy_to_key) { auto f2 = get_file_desc(handle->id().shard_full(), get_key_package_id(handle->masterchain_ref_block()), handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); - td::actor::send_closure(f2->file_actor_id(), &ArchiveSlice::add_file, handle, ref_id, std::move(data), - ig.get_promise()); + if (f2.is_ok()) { + td::actor::send_closure(f2.ok()->file_actor_id(), &ArchiveSlice::add_file, handle, ref_id, std::move(data), + ig.get_promise()); + } } } void ArchiveManager::add_key_block_proof(UnixTime ts, BlockSeqno seqno, LogicalTime lt, FileReference ref_id, td::BufferSlice data, td::Promise promise) { - auto f = get_file_desc(ShardIdFull{masterchainId}, get_key_package_id(seqno), seqno, ts, lt, true); + TRY_RESULT_PROMISE(promise, f, + get_file_desc(ShardIdFull{masterchainId}, get_key_package_id(seqno), seqno, ts, lt, true)); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_file, nullptr, std::move(ref_id), std::move(data), std::move(promise)); } void ArchiveManager::add_temp_file_short(FileReference ref_id, td::BufferSlice data, td::Promise promise) { - auto f = get_file_desc(ref_id.shard(), get_temp_package_id(), 0, 0, 0, true); + TRY_RESULT_PROMISE(promise, f, get_file_desc(ref_id.shard(), get_temp_package_id(), 0, 0, 0, true)); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_file, nullptr, std::move(ref_id), std::move(data), std::move(promise)); } @@ -267,13 +279,14 @@ void ArchiveManager::get_key_block_proof(FileReference ref_id, td::Promise promise) { - get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise)); + get_temp_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise)); } -void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise) { +void ArchiveManager::get_temp_file_short_cont(FileReference ref_id, PackageId idx, + td::Promise promise) { auto f = get_temp_file_desc_by_idx(idx); if (!f) { - promise.set_error(td::Status::Error(ErrorCode::notready, "file not in db")); + promise.set_error(td::Status::Error(ErrorCode::notready, PSTRING() << "file not in db: " << ref_id.filename())); return; } auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, idx = get_prev_temp_file_desc_idx(idx), @@ -281,7 +294,8 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td if (R.is_ok()) { promise.set_value(R.move_as_ok()); } else { - td::actor::send_closure(SelfId, &ArchiveManager::get_file_short_cont, std::move(ref_id), idx, std::move(promise)); + td::actor::send_closure(SelfId, &ArchiveManager::get_temp_file_short_cont, std::move(ref_id), idx, + std::move(promise)); } }); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, nullptr, std::move(ref_id), std::move(P)); @@ -290,29 +304,27 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td void ArchiveManager::get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise promise) { if (handle->moved_to_archive()) { auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false); - if (f) { - td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(handle), std::move(ref_id), + if (f.is_ok()) { + td::actor::send_closure(f.ok()->file_actor_id(), &ArchiveSlice::get_file, std::move(handle), std::move(ref_id), std::move(promise)); return; } } if (handle->handle_moved_to_archive()) { auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false); - if (f) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, idx = get_max_temp_file_desc_idx(), - promise = std::move(promise)](td::Result R) mutable { + if (f.ok()) { + promise = [=, promise = std::move(promise), + file_actor = f.ok()->file_actor_id()](td::Result R) mutable { if (R.is_ok()) { promise.set_value(R.move_as_ok()); - } else { - td::actor::send_closure(SelfId, &ArchiveManager::get_file_short_cont, ref_id, idx, std::move(promise)); + return; } - }); - td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(handle), std::move(ref_id), - std::move(P)); - return; + td::actor::send_closure(file_actor, &ArchiveSlice::get_file, std::move(handle), std::move(ref_id), + std::move(promise)); + }; } } - get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise)); + get_temp_file_short(std::move(ref_id), std::move(promise)); } void ArchiveManager::register_perm_state(FileReferenceShort id) { @@ -662,13 +674,14 @@ void ArchiveManager::load_package(PackageId id) { update_permanent_slices(); } -const ArchiveManager::FileDescription *ArchiveManager::get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, - UnixTime ts, LogicalTime lt, bool force) { +td::Result ArchiveManager::get_file_desc(ShardIdFull shard, PackageId id, + BlockSeqno seqno, UnixTime ts, + LogicalTime lt, bool force) { auto &f = get_file_map(id); auto it = f.find(id); if (it != f.end()) { if (it->second.deleted) { - return nullptr; + return td::Status::Error("file is deleted"); } if (force && !id.temp) { update_desc(f, it->second, shard, seqno, ts, lt); @@ -676,7 +689,7 @@ const ArchiveManager::FileDescription *ArchiveManager::get_file_desc(ShardIdFull return &it->second; } if (!force) { - return nullptr; + return td::Status::Error("file not found"); } return add_file_desc(shard, id, seqno, ts, lt); @@ -1206,13 +1219,9 @@ void ArchiveManager::get_archive_id(BlockSeqno masterchain_seqno, ShardIdFull sh void ArchiveManager::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, td::Promise promise) { auto arch = static_cast(archive_id); - auto F = get_file_desc(ShardIdFull{masterchainId}, PackageId{arch, false, false}, 0, 0, 0, false); - if (!F) { - promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found")); - return; - } - - td::actor::send_closure(F->file_actor_id(), &ArchiveSlice::get_slice, archive_id, offset, limit, std::move(promise)); + TRY_RESULT_PROMISE(promise, f, + get_file_desc(ShardIdFull{masterchainId}, PackageId{arch, false, false}, 0, 0, 0, false)); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_slice, archive_id, offset, limit, std::move(promise)); } void ArchiveManager::commit_transaction() { diff --git a/validator/db/archive-manager.hpp b/validator/db/archive-manager.hpp index f226e2a41..6af0ed13b 100644 --- a/validator/db/archive-manager.hpp +++ b/validator/db/archive-manager.hpp @@ -206,10 +206,10 @@ class ArchiveManager : public td::actor::Actor { void deleted_package(PackageId seqno, td::Promise promise); void get_handle_cont(BlockIdExt block_id, PackageId id, td::Promise promise); void get_handle_finish(BlockHandle handle, td::Promise promise); - void get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise); + void get_temp_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise); - const FileDescription *get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, LogicalTime lt, - bool force); + td::Result get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, + LogicalTime lt, bool force); const FileDescription *add_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, LogicalTime lt); void update_desc(FileMap &f, const FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, UnixTime ts, LogicalTime lt); diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp index cbaaea0bc..05e5ca68c 100644 --- a/validator/db/archive-slice.cpp +++ b/validator/db/archive-slice.cpp @@ -417,7 +417,7 @@ void ArchiveSlice::get_temp_handle(BlockIdExt block_id, td::Promiseget(ref_id.hash().to_hex(), value); R.ensure(); if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - promise.set_error(td::Status::Error(ErrorCode::notready, "file not in archive slice")); + promise.set_error(td::Status::Error(ErrorCode::notready, PSTRING() << "file " << ref_id.filename() << " not in archive slice " << get_name())); return; } auto offset = td::to_integer(value); diff --git a/validator/db/archiver.cpp b/validator/db/archiver.cpp index 93ba18ca2..ffbfc9f05 100644 --- a/validator/db/archiver.cpp +++ b/validator/db/archiver.cpp @@ -30,6 +30,12 @@ BlockArchiver::BlockArchiver(BlockHandle handle, td::actor::ActorIdid().to_str(); + if (handle_->moved_to_archive()) { + VLOG(VALIDATOR_DEBUG) << "already moved"; + finish_query(); + return; + } if (handle_->id().is_masterchain()) { td::actor::send_closure(db_, &Db::get_block_state, handle_, [SelfId = actor_id(this), archive = archive_](td::Result> R) { @@ -46,6 +52,7 @@ void BlockArchiver::start_up() { } void BlockArchiver::move_handle() { + VLOG(VALIDATOR_DEBUG) << "move_handle"; if (handle_->handle_moved_to_archive()) { moved_handle(); } else { @@ -58,6 +65,7 @@ void BlockArchiver::move_handle() { } void BlockArchiver::moved_handle() { + VLOG(VALIDATOR_DEBUG) << "moved_handle"; CHECK(handle_->handle_moved_to_archive()); if (handle_->moved_to_archive()) { finish_query(); @@ -78,6 +86,7 @@ void BlockArchiver::moved_handle() { } void BlockArchiver::got_proof(td::BufferSlice data) { + VLOG(VALIDATOR_DEBUG) << "got_proof"; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_proof); @@ -87,6 +96,7 @@ void BlockArchiver::got_proof(td::BufferSlice data) { } void BlockArchiver::written_proof() { + VLOG(VALIDATOR_DEBUG) << "written_proof"; if (!handle_->inited_proof_link()) { written_proof_link(); return; @@ -102,6 +112,7 @@ void BlockArchiver::written_proof() { } void BlockArchiver::got_proof_link(td::BufferSlice data) { + VLOG(VALIDATOR_DEBUG) << "got_proof_link"; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_proof_link); @@ -111,6 +122,7 @@ void BlockArchiver::got_proof_link(td::BufferSlice data) { } void BlockArchiver::written_proof_link() { + VLOG(VALIDATOR_DEBUG) << "written_proof_link"; if (!handle_->received()) { written_block_data(); return; @@ -124,6 +136,7 @@ void BlockArchiver::written_proof_link() { } void BlockArchiver::got_block_data(td::BufferSlice data) { + VLOG(VALIDATOR_DEBUG) << "got_block_data"; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_block_data); @@ -133,6 +146,7 @@ void BlockArchiver::got_block_data(td::BufferSlice data) { } void BlockArchiver::written_block_data() { + VLOG(VALIDATOR_DEBUG) << "written_block_data"; handle_->set_moved_to_archive(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { @@ -143,6 +157,7 @@ void BlockArchiver::written_block_data() { } void BlockArchiver::finish_query() { + VLOG(VALIDATOR_DEBUG) << "finished archiving block in " << timer_.elapsed() << " s"; if (promise_) { promise_.set_value(td::Unit()); } @@ -150,8 +165,8 @@ void BlockArchiver::finish_query() { } void BlockArchiver::abort_query(td::Status reason) { + VLOG(VALIDATOR_WARNING) << "failed to archive block " << handle_->id() << ": " << reason; if (promise_) { - VLOG(VALIDATOR_WARNING) << "failed to archive block " << handle_->id() << ": " << reason; promise_.set_error(std::move(reason)); } stop(); diff --git a/validator/db/archiver.hpp b/validator/db/archiver.hpp index 9498977fd..531e114c5 100644 --- a/validator/db/archiver.hpp +++ b/validator/db/archiver.hpp @@ -54,6 +54,7 @@ class BlockArchiver : public td::actor::Actor { td::actor::ActorId archive_; td::actor::ActorId db_; td::Promise promise_; + td::Timer timer_; }; } // namespace validator diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index 98da35380..99125c60c 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -314,7 +314,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref state, }); if (R.is_error()) { - LOG(ERROR) << "Can't find handle for prev block state " << prev_id.to_str(); + LOG(ERROR) << "Can't find handle for prev block state " << prev_id.to_str() << " error: " << R.error().message(); ConstBlockHandle h(next_handle); publisher->storeBlockState(h, next_state->root_cell(), std::move(final_publish)); } else { @@ -322,7 +322,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref state, next_state = next_state, publisher = publisher, prev_id](td::Result> R) mutable { if (R.is_error()) { - LOG(ERROR) << "Can't find prev block state for" << prev_id.to_str(); + LOG(ERROR) << "Can't find prev block state for: " << prev_id.to_str() << " error: " << R.error().message(); ConstBlockHandle h(next_handle); publisher->storeBlockState(h, next_state->root_cell(), std::move(final_publish)); } else { @@ -335,7 +335,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref state, }); const auto new_handle = R.move_as_ok(); - td::actor::send_closure(SelfId, &RootDb::get_block_state_root_cell, new_handle, std::move(P2)); + td::actor::send_closure(SelfId, &RootDb::get_block_state_root_cell, new_handle, std::move(P2), true); } }); @@ -429,8 +429,8 @@ void RootDb::get_block_state(ConstBlockHandle handle, td::Promise> promise) { - if (handle->inited_state_boc()) { +void RootDb::get_block_state_root_cell(ConstBlockHandle handle, td::Promise> promise, bool force_load) { + if (handle->inited_state_boc() or force_load) { if (handle->deleted_state_boc()) { promise.set_error(td::Status::Error(ErrorCode::error, "state already gc'd")); return; @@ -513,12 +513,6 @@ void RootDb::try_get_static_file(FileHash file_hash, td::Promise promise) { - td::actor::create_actor("archiver", std::move(handle), archive_db_.get(), actor_id(this), - std::move(promise)) - .release(); -} - void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_lt, account, lt, std::move(promise)); } @@ -593,8 +587,26 @@ void RootDb::start_up() { } void RootDb::archive(BlockHandle handle, td::Promise promise) { - td::actor::create_actor("archiveblock", std::move(handle), archive_db_.get(), actor_id(this), - std::move(promise)) + auto [it, inserted] = archive_block_waiters_.emplace(handle->id(), std::vector>{}); + it->second.push_back(std::move(promise)); + if (!inserted) { + VLOG(VALIDATOR_DEBUG) << "archive block " << handle->id().id.to_str() << " : already in progress"; + return; + } + td::actor::create_actor( + PSTRING() << "archiver" << handle->id().id.to_str(), handle, archive_db_.get(), actor_id(this), + [this, SelfId = actor_id(this), block_id = handle->id()](td::Result R) { + td::actor::send_lambda(SelfId, [this, R = std::move(R), block_id]() { + auto it2 = archive_block_waiters_.find(block_id); + if (it2 == archive_block_waiters_.end()) { + return; + } + for (auto &promise : it2->second) { + promise.set_result(R.clone()); + } + archive_block_waiters_.erase(it2); + }); + }) .release(); } diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index 2ab261242..fa0c51c77 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -70,16 +70,19 @@ #include "validator.h" namespace ton { - namespace validator { - class RootDb : public Db { - public: +public: enum class Flags : td::uint32 { f_started = 1, f_ready = 2, f_switched = 4, f_archived = 8 }; + RootDb(td::actor::ActorId validator_manager, std::string root_path, td::Ref opts, bool read_only = false) - : validator_manager_(validator_manager), root_path_(std::move(root_path)), read_only_(read_only), opts_(opts) { + : validator_manager_(validator_manager) + , root_path_(std::move(root_path)) + , read_only_(read_only) + , opts_(opts) { } + void set_block_publisher(BlockParser* publisher) override { if (publisher == nullptr) { LOG(ERROR) << "Received nullptr IBlockPublisher"; @@ -94,6 +97,7 @@ class RootDb : public Db { void clear_boc_cache() override { td::actor::send_closure(cell_db_, &CellDb::clear_boc_cache); } + void start_up() override; void store_block_data(BlockHandle handle, td::Ref block, td::Promise promise) override; @@ -119,7 +123,7 @@ class RootDb : public Db { void store_block_state_from_data(BlockHandle handle, td::Ref block, td::Promise> promise) override; void store_block_state_from_data_preliminary(std::vector> blocks, - td::Promise promise) override; + td::Promise promise) override; void get_block_state(ConstBlockHandle handle, td::Promise> promise) override; void store_block_state_part(BlockId effective_block, td::Ref cell, td::Promise> promise) override; @@ -127,6 +131,7 @@ class RootDb : public Db { void store_block_handle(BlockHandle handle, td::Promise promise) override; void get_block_handle(BlockIdExt id, td::Promise promise) override; + void get_block_handle_external(BlockIdExt id, bool force, td::Promise promise) { td::actor::send_closure(validator_manager_, &ValidatorManager::get_block_handle, id, force, std::move(promise)); } @@ -151,7 +156,6 @@ class RootDb : public Db { void try_get_static_file(FileHash file_hash, td::Promise promise) override; - void apply_block(BlockHandle handle, td::Promise promise) override; void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, @@ -197,13 +201,15 @@ class RootDb : public Db { void set_async_mode(bool mode, td::Promise promise) override; void run_gc(UnixTime mc_ts, UnixTime gc_ts, double archive_ttl) override; - void add_persistent_state_description(td::Ref desc, td::Promise promise) override; - void get_persistent_state_descriptions(td::Promise>> promise) override; + void add_persistent_state_description(td::Ref desc, + td::Promise promise) override; + void get_persistent_state_descriptions( + td::Promise>> promise) override; - void iterate_temp_block_handles(std::function f) override; + void iterate_temp_block_handles(std::function f) override; void reinit(td::Promise) override; - private: +private: td::actor::ActorId validator_manager_; std::string root_path_; bool read_only_ = false; @@ -214,11 +220,10 @@ class RootDb : public Db { td::actor::ActorOwn static_files_db_; td::actor::ActorOwn archive_db_; td::actor::ActorOwn cluster_sync_; - + std::map>> archive_block_waiters_; BlockParser* publisher_ = nullptr; - void get_block_state_root_cell(ConstBlockHandle handle, td::Promise> promise) override; + void get_block_state_root_cell(ConstBlockHandle handle, td::Promise> promise, + bool force_load = false) override; }; - -} // namespace validator - -} // namespace ton +}; // namespace validator +} // namespace ton \ No newline at end of file diff --git a/validator/full-node-shard.cpp b/validator/full-node-shard.cpp index a175d79ac..d5a5d987f 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -213,7 +213,7 @@ void FullNodeShardImpl::got_next_block(td::Result R) { if (handle_->unix_time() > td::Clocks::system() - 300) { promise_.set_value(td::Unit()); } else { - sync_completed_at_ = td::Timestamp::in(60.0); + sync_completed_at_ = td::Timestamp::in(opts_.initial_sync_delay_); } } get_next_block(); @@ -1085,7 +1085,7 @@ void FullNodeShardImpl::set_handle(BlockHandle handle, td::Promise pro promise_ = std::move(promise); get_next_block(); - sync_completed_at_ = td::Timestamp::in(60.0); + sync_completed_at_ = td::Timestamp::in(opts_.initial_sync_delay_); alarm_timestamp().relax(sync_completed_at_); } diff --git a/validator/full-node.h b/validator/full-node.h index 4c11c1fbd..2d1fd7aaf 100644 --- a/validator/full-node.h +++ b/validator/full-node.h @@ -59,6 +59,7 @@ struct FullNodeOptions { FullNodeConfig config_; double public_broadcast_speed_multiplier_ = 1.0; double private_broadcast_speed_multiplier_ = 1.0; + double initial_sync_delay_ = 60.0; }; struct CustomOverlayParams { diff --git a/validator/impl/CMakeLists.txt b/validator/impl/CMakeLists.txt index 7c62f183f..5a1b64692 100644 --- a/validator/impl/CMakeLists.txt +++ b/validator/impl/CMakeLists.txt @@ -1,7 +1,3 @@ -if (NOT OPENSSL_FOUND) - find_package(OpenSSL REQUIRED) -endif() - if (NIX) find_package(PkgConfig REQUIRED) pkg_check_modules(MHD REQUIRED libmicrohttpd) @@ -53,9 +49,9 @@ set(TON_VALIDATOR_SOURCE add_library(ton_validator STATIC ${TON_VALIDATOR_SOURCE}) target_include_directories(ton_validator PUBLIC ${MHD_INCLUDE_DIR} ${MHD_STATIC_INCLUDE_DIRS}) -target_include_directories(ton_validator PUBLIC +target_include_directories(ton_validator PUBLIC $ - $/.. + $/.. ${OPENSSL_INCLUDE_DIR} ) diff --git a/validator/impl/accept-block.cpp b/validator/impl/accept-block.cpp index 883994089..8de97b6f4 100644 --- a/validator/impl/accept-block.cpp +++ b/validator/impl/accept-block.cpp @@ -350,6 +350,7 @@ bool AcceptBlockQuery::check_send_error(td::actor::ActorId Sel } void AcceptBlockQuery::finish_query() { + VLOG(VALIDATOR_DEBUG) << "finish_query()"; if (apply_) { ValidatorInvariants::check_post_accept(handle_); } @@ -448,6 +449,7 @@ void AcceptBlockQuery::got_block_candidate_data(td::BufferSlice data) { } void AcceptBlockQuery::got_block_handle_cont() { + VLOG(VALIDATOR_DEBUG) << "got_block_handle_cont()"; if (data_.not_null() && !handle_->received()) { td::actor::send_closure( manager_, &ValidatorManager::set_block_data, handle_, data_, [SelfId = actor_id(this)](td::Result R) { @@ -514,9 +516,11 @@ void AcceptBlockQuery::written_block_info() { td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_prev_state, R.move_as_ok()); }); + VLOG(VALIDATOR_DEBUG) << "wait_prev_block_state"; td::actor::send_closure(manager_, &ValidatorManager::wait_prev_block_state, handle_, priority(), timeout_, std::move(P)); } else { + VLOG(VALIDATOR_DEBUG) << "wait_block_data"; td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, priority(), timeout_, [SelfId = actor_id(this)](td::Result> R) { check_send_error(SelfId, R) || diff --git a/validator/impl/check-proof.cpp b/validator/impl/check-proof.cpp index 12d336ad1..ae0ce92fc 100644 --- a/validator/impl/check-proof.cpp +++ b/validator/impl/check-proof.cpp @@ -43,7 +43,7 @@ void CheckProof::alarm() { void CheckProof::abort_query(td::Status reason) { if (promise_) { - VLOG(VALIDATOR_WARNING) << "aborting check proof for " << id_ << " query: " << reason; + VLOG(VALIDATOR_WARNING) << "aborting check proof for " << id_.to_str() << " query: " << reason; promise_.set_error(std::move(reason)); } stop(); @@ -68,7 +68,7 @@ void CheckProof::finish_query() { ValidatorInvariants::check_post_check_proof_link(handle_); } if (promise_) { - VLOG(VALIDATOR_DEBUG) << "checked proof for " << handle_->id(); + VLOG(VALIDATOR_DEBUG) << "checked proof for " << handle_->id().to_str(); promise_.set_result(handle_); } stop(); @@ -271,6 +271,7 @@ bool CheckProof::init_parse(bool is_aux) { } void CheckProof::start_up() { + VLOG(VALIDATOR_DEBUG) << "started check proof for " << id_.to_str() << ", mode=" << mode_; alarm_timestamp() = timeout_; auto res = vm::std_boc_deserialize(proof_->data()); @@ -324,6 +325,7 @@ void CheckProof::start_up() { } void CheckProof::got_block_handle(BlockHandle handle) { + VLOG(VALIDATOR_DEBUG) << "got_block_handle"; handle_ = std::move(handle); CHECK(handle_); if (!is_proof() || skip_check_signatures_) { @@ -353,6 +355,7 @@ void CheckProof::got_block_handle(BlockHandle handle) { } void CheckProof::got_masterchain_state(td::Ref state) { + VLOG(VALIDATOR_DEBUG) << "got_masterchain_state #" << state->get_seqno(); CHECK(is_proof()); state_ = std::move(state); @@ -366,6 +369,7 @@ void CheckProof::got_masterchain_state(td::Ref state) { } void CheckProof::process_masterchain_state() { + VLOG(VALIDATOR_DEBUG) << "process_masterchain_state"; CHECK(is_proof()); CHECK(state_.not_null()); @@ -396,6 +400,7 @@ void CheckProof::process_masterchain_state() { } void CheckProof::check_signatures(Ref s) { + VLOG(VALIDATOR_DEBUG) << "check_signatures"; if (s->get_catchain_seqno() != catchain_seqno_) { abort_query(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad validator catchain seqno: expected " << s->get_catchain_seqno() << ", found " @@ -449,6 +454,7 @@ void CheckProof::check_signatures(Ref s) { } void CheckProof::got_block_handle_2(BlockHandle handle) { + VLOG(VALIDATOR_DEBUG) << "got_block_handle_2 " << handle->id().id.to_str(); handle_ = std::move(handle); handle_->set_split(before_split_); @@ -468,16 +474,19 @@ void CheckProof::got_block_handle_2(BlockHandle handle) { // do not save proof if we skipped signatures auto proof = Ref(proof_); CHECK(proof.not_null()); + VLOG(VALIDATOR_DEBUG) << "set_block_proof"; td::actor::send_closure_later(manager_, &ValidatorManager::set_block_proof, handle_, std::move(proof), std::move(P)); } else if (is_proof()) { auto proof = Ref(proof_); CHECK(proof.not_null()); CHECK(sig_ok_); + VLOG(VALIDATOR_DEBUG) << "set_block_proof"; td::actor::send_closure_later(manager_, &ValidatorManager::set_block_proof, handle_, std::move(proof), std::move(P)); } else { CHECK(proof_.not_null()); + VLOG(VALIDATOR_DEBUG) << "set_block_proof_link"; td::actor::send_closure_later(manager_, &ValidatorManager::set_block_proof_link, handle_, proof_, std::move(P)); } } diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index 5ceb842c9..13bbfce58 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -157,37 +157,39 @@ void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, void run_apply_block_query(BlockIdExt id, td::Ref block, BlockIdExt masterchain_block_id, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise) { - td::actor::create_actor(PSTRING() << "apply " << id, id, std::move(block), masterchain_block_id, manager, - timeout, std::move(promise)) + td::actor::create_actor(PSTRING() << "apply" << id.id.to_str(), id, std::move(block), + masterchain_block_id, manager, timeout, std::move(promise)) .release(); } void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, bool skip_check_signatures) { - td::actor::create_actor("checkproof", id, std::move(proof), manager, timeout, std::move(promise), - skip_check_signatures) + td::actor::create_actor(PSTRING() << "checkproof" << id.id.to_str(), id, std::move(proof), manager, + timeout, std::move(promise), skip_check_signatures) .release(); } void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, td::Ref rel_key_block_proof, bool skip_check_signatures) { - td::actor::create_actor("checkproof/key", id, std::move(proof), manager, timeout, std::move(promise), - skip_check_signatures, std::move(rel_key_block_proof)) + td::actor::create_actor(PSTRING() << "checkproof/key" << id.id.to_str(), id, std::move(proof), manager, + timeout, std::move(promise), skip_check_signatures, + std::move(rel_key_block_proof)) .release(); } void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, td::Ref rel_mc_state, bool skip_check_signatures) { - td::actor::create_actor("checkproof/st", id, std::move(proof), manager, timeout, std::move(promise), - skip_check_signatures, std::move(rel_mc_state)) + td::actor::create_actor(PSTRING() << "checkproof/st" << id.id.to_str(), id, std::move(proof), manager, + timeout, std::move(promise), skip_check_signatures, std::move(rel_mc_state)) .release(); } void run_check_proof_link_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise) { - td::actor::create_actor("checkprooflink", id, std::move(proof), manager, timeout, std::move(promise)) + td::actor::create_actor(PSTRING() << "checkprooflink" << id.id.to_str(), id, std::move(proof), manager, + timeout, std::move(promise)) .release(); } diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index e27f9c242..b2c63e5d9 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -24,17 +24,16 @@ #include "validator/interfaces/validator-manager.h" namespace ton { - namespace validator { - class Db : public td::actor::Actor { - public: +public: virtual ~Db() = default; + virtual void set_block_publisher(BlockParser* publisher) { -// LOG(ERROR) << "set_block_publisher"; + // LOG(ERROR) << "set_block_publisher"; } - virtual void clear_boc_cache() { + virtual void clear_boc_cache() { } virtual void store_block_data(BlockHandle handle, td::Ref data, td::Promise promise) = 0; @@ -60,8 +59,9 @@ class Db : public td::actor::Actor { virtual void store_block_state_from_data(BlockHandle handle, td::Ref block, td::Promise> promise) = 0; virtual void store_block_state_from_data_preliminary(std::vector> blocks, - td::Promise promise) = 0; - virtual void get_block_state_root_cell(ConstBlockHandle handle, td::Promise> promise) = 0; + td::Promise promise) = 0; + virtual void get_block_state_root_cell(ConstBlockHandle handle, td::Promise> promise, + bool force_load = false) = 0; virtual void get_block_state(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_state_part(BlockId effective_block, td::Ref cell, td::Promise> promise) = 0; @@ -95,7 +95,6 @@ class Db : public td::actor::Actor { virtual void store_block_handle(BlockHandle handle, td::Promise promise) = 0; virtual void get_block_handle(BlockIdExt id, td::Promise promise) = 0; - virtual void apply_block(BlockHandle handle, td::Promise promise) = 0; virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) = 0; @@ -147,9 +146,7 @@ class Db : public td::actor::Actor { virtual void get_persistent_state_descriptions( td::Promise>> promise) = 0; virtual void reinit(td::Promise) = 0; - virtual void iterate_temp_block_handles(std::function f) = 0; + virtual void iterate_temp_block_handles(std::function f) = 0; }; - -} // namespace validator - -} // namespace ton +} // namespace validator +} // namespace ton \ No newline at end of file diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index fea19b082..e416c8d19 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -758,7 +758,7 @@ void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td:: void ValidatorManagerImpl::get_shard_state_root_cell_from_db(ConstBlockHandle handle, td::Promise> promise) { - td::actor::send_closure(db_, &Db::get_block_state_root_cell, handle, std::move(promise)); + td::actor::send_closure(db_, &Db::get_block_state_root_cell, handle, std::move(promise), false); } void ValidatorManagerImpl::get_shard_state_from_db_short(BlockIdExt block_id, @@ -1049,7 +1049,7 @@ void ValidatorManagerImpl::new_block(BlockHandle handle, td::Ref sta std::move(promise)); } }); - td::actor::send_closure(db_, &Db::apply_block, handle, std::move(P)); + td::actor::send_closure(db_, &Db::archive, handle, std::move(P)); } } diff --git a/validator/manager.cpp b/validator/manager.cpp index 3a7d20233..defbb60be 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -229,9 +229,11 @@ void ValidatorManagerImpl::new_block_broadcast(BlockBroadcast broadcast, td::Pro } promise.set_result(std::move(R)); }; - td::actor::create_actor("broadcast", std::move(broadcast), last_masterchain_block_handle_, - last_masterchain_state_, last_known_key_block_handle_, publisher_.get(), - actor_id(this), td::Timestamp::in(2.0), std::move(promise)) + BlockIdExt block_id = broadcast.block_id; + td::actor::create_actor(PSTRING() << "broadcast" << block_id.id.to_str(), std::move(broadcast), + last_masterchain_block_handle_, last_masterchain_state_, + last_known_key_block_handle_, publisher_.get(), actor_id(this), td::Timestamp::in(20.0), + std::move(promise)) .release(); } @@ -1226,7 +1228,7 @@ void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td:: void ValidatorManagerImpl::get_shard_state_root_cell_from_db(ConstBlockHandle handle, td::Promise> promise) { - td::actor::send_closure(db_, &Db::get_block_state_root_cell, handle, std::move(promise)); + td::actor::send_closure(db_, &Db::get_block_state_root_cell, handle, std::move(promise), false); } void ValidatorManagerImpl::get_shard_state_from_db_short(BlockIdExt block_id, @@ -1353,7 +1355,6 @@ void ValidatorManagerImpl::finished_wait_state(BlockHandle handle, td::Resultsecond.waiting_preliminary_) { X.promise.set_error(S.clone()); } - wait_state_.erase(it); } else if (!it->second.waiting_.empty() || !it->second.waiting_preliminary_.empty()) { auto X = it->second.get_timeout(); auto P1 = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result> R) { @@ -1367,7 +1368,9 @@ void ValidatorManagerImpl::finished_wait_state(BlockHandle handle, td::Resultid())) .release(); it->second.actor_ = id; + return; } + wait_state_.erase(it); } } @@ -1599,6 +1602,7 @@ void ValidatorManagerImpl::new_block_cont(BlockHandle handle, td::Ref promise) { if (state->get_shard().is_masterchain() && handle->id().id.seqno > last_masterchain_seqno_) { if (handle->id().id.seqno == last_masterchain_seqno_ + 1) { + VLOG(VALIDATOR_DEBUG) << "new block " << handle->id().id.to_str() << " is the next masterchain block"; last_masterchain_seqno_ = handle->id().id.seqno; last_masterchain_state_ = td::Ref{state}; last_masterchain_block_id_ = handle->id(); @@ -1618,6 +1622,7 @@ void ValidatorManagerImpl::new_block_cont(BlockHandle handle, td::Refid(); last_masterchain_seqno_ = last_masterchain_block_id_.id.seqno; CHECK(it->first == last_masterchain_seqno_); + VLOG(VALIDATOR_DEBUG) << "processing pending masterchain block " << last_masterchain_block_id_.id.to_str(); auto l_promise = std::move(std::get<2>(it->second)); last_masterchain_block_handle_->set_processed(); @@ -1634,6 +1639,7 @@ void ValidatorManagerImpl::new_block_cont(BlockHandle handle, td::Refid().id.to_str() << " is too new masterchain block"; auto it = pending_masterchain_states_.find(handle->id().id.seqno); if (it != pending_masterchain_states_.end()) { std::get<2>(it->second).emplace_back(std::move(promise)); @@ -1646,12 +1652,14 @@ void ValidatorManagerImpl::new_block_cont(BlockHandle handle, td::Refid().id.to_str() << " is already processed"; handle->set_processed(); promise.set_value(td::Unit()); } } void ValidatorManagerImpl::new_block(BlockHandle handle, td::Ref state, td::Promise promise) { + VLOG(VALIDATOR_DEBUG) << "new block " << handle->id().id.to_str(); if (handle->is_applied()) { return new_block_cont(std::move(handle), std::move(state), std::move(promise)); } else { @@ -1664,7 +1672,7 @@ void ValidatorManagerImpl::new_block(BlockHandle handle, td::Ref sta std::move(promise)); } }); - td::actor::send_closure(db_, &Db::apply_block, handle, std::move(P)); + td::actor::send_closure(db_, &Db::archive, handle, std::move(P)); } } @@ -2406,7 +2414,9 @@ void ValidatorManagerImpl::new_masterchain_block() { td::actor::send_closure(actor, &ShardBlockRetainer::update_masterchain_state, last_masterchain_state_); } if (last_masterchain_seqno_ % 1024 == 0) { - LOG(WARNING) << "applied masterchain block " << last_masterchain_block_id_; + LOG(WARNING) << "applied masterchain block " << last_masterchain_block_id_.to_str(); + } else { + LOG(DEBUG) << "applied masterchain block " << last_masterchain_block_id_.to_str(); } } diff --git a/validator/net/download-block.cpp b/validator/net/download-block.cpp index c60955ed2..75dc57fc4 100644 --- a/validator/net/download-block.cpp +++ b/validator/net/download-block.cpp @@ -294,7 +294,7 @@ void DownloadBlock::got_block_partial_proof(td::BufferSlice proof) { } void DownloadBlock::checked_block_proof() { - VLOG(FULL_NODE_DEBUG) << "checked proof for " << block_id_; + VLOG(FULL_NODE_DEBUG) << "checked proof for " << block_id_.to_str(); if (!handle_) { CHECK(!short_); diff --git a/validator/validate-broadcast.cpp b/validator/validate-broadcast.cpp index 59cb1faca..189697c53 100644 --- a/validator/validate-broadcast.cpp +++ b/validator/validate-broadcast.cpp @@ -28,7 +28,8 @@ namespace validator { void ValidateBroadcast::abort_query(td::Status reason) { if (promise_) { - VLOG(VALIDATOR_WARNING) << "aborting validate broadcast query for " << broadcast_.block_id << ": " << reason; + VLOG(VALIDATOR_WARNING) << "aborting validate broadcast query for " << broadcast_.block_id.to_str() << ": " + << reason; promise_.set_error(std::move(reason)); } stop(); @@ -36,7 +37,8 @@ void ValidateBroadcast::abort_query(td::Status reason) { void ValidateBroadcast::finish_query() { if (promise_) { - VLOG(VALIDATOR_DEBUG) << "validated broadcast for " << broadcast_.block_id; + VLOG(VALIDATOR_DEBUG) << "validated broadcast for " << broadcast_.block_id.to_str() << " in " + << perf_timer_.elapsed() << " s"; promise_.set_result(td::Unit()); } stop(); @@ -47,7 +49,9 @@ void ValidateBroadcast::alarm() { } void ValidateBroadcast::start_up() { - VLOG(VALIDATOR_DEBUG) << "received broadcast for " << broadcast_.block_id; + VLOG(VALIDATOR_DEBUG) << "received broadcast for " << broadcast_.block_id.to_str() + << " : last_mc_seqno=" << last_masterchain_state_->get_seqno() + << " last_key_block_seqno=" << last_known_masterchain_block_handle_->id().seqno(); alarm_timestamp() = timeout_; auto hash = sha256_bits256(broadcast_.data.as_slice()); @@ -127,6 +131,7 @@ void ValidateBroadcast::start_up() { } void ValidateBroadcast::got_key_block_id(BlockIdExt block_id) { + VLOG(VALIDATOR_DEBUG) << "got_key_block_id " << block_id.id.to_str(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, @@ -139,6 +144,7 @@ void ValidateBroadcast::got_key_block_id(BlockIdExt block_id) { } void ValidateBroadcast::got_key_block_handle(ConstBlockHandle handle) { + VLOG(VALIDATOR_DEBUG) << "got_key_block_handle " << handle->id().id.to_str(); if (handle->id().seqno() == 0) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { @@ -172,6 +178,7 @@ void ValidateBroadcast::got_key_block_handle(ConstBlockHandle handle) { } void ValidateBroadcast::got_key_block_proof_link(td::Ref key_proof_link) { + VLOG(VALIDATOR_DEBUG) << "got_key_block_proof_link"; key_proof_link_ = key_proof_link; auto confR = key_proof_link->get_key_block_config(); if (confR.is_error()) { @@ -182,6 +189,7 @@ void ValidateBroadcast::got_key_block_proof_link(td::Ref key_proof_li } void ValidateBroadcast::got_zero_state(td::Ref state) { + VLOG(VALIDATOR_DEBUG) << "got_zero_state"; zero_state_ = state; auto confR = state->get_config_holder(); if (confR.is_error()) { @@ -192,6 +200,7 @@ void ValidateBroadcast::got_zero_state(td::Ref state) { } void ValidateBroadcast::check_signatures_common(td::Ref conf) { + VLOG(VALIDATOR_DEBUG) << "checking signatures"; auto val_set = conf->get_validator_set(broadcast_.block_id.shard_full(), header_info_.utime, header_info_.cc_seqno); if (val_set.is_null()) { abort_query(td::Status::Error(ErrorCode::notready, "failed to compute validator set")); @@ -216,6 +225,7 @@ void ValidateBroadcast::check_signatures_common(td::Ref conf) { } void ValidateBroadcast::checked_signatures() { + VLOG(VALIDATOR_DEBUG) << "checked_signatures"; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error_prefix("db error: ")); @@ -228,6 +238,7 @@ void ValidateBroadcast::checked_signatures() { } void ValidateBroadcast::got_block_handle(BlockHandle handle) { + VLOG(VALIDATOR_DEBUG) << "got_block_handle " << handle->id().id.to_str(); handle_ = std::move(handle); auto dataR = create_block(broadcast_.block_id, broadcast_.data.clone()); @@ -250,10 +261,12 @@ void ValidateBroadcast::got_block_handle(BlockHandle handle) { } }); + VLOG(VALIDATOR_DEBUG) << "writing block data for " << handle_->id().id.to_str(); td::actor::send_closure(manager_, &ValidatorManager::set_block_data, handle_, data_, std::move(P)); } void ValidateBroadcast::written_block_data() { + VLOG(VALIDATOR_DEBUG) << "written_block_data"; if (handle_->id().is_masterchain()) { if (handle_->inited_proof()) { checked_proof(); @@ -267,6 +280,7 @@ void ValidateBroadcast::written_block_data() { td::actor::send_closure(SelfId, &ValidateBroadcast::checked_proof); } }); + VLOG(VALIDATOR_DEBUG) << "checking proof"; if (!key_proof_link_.is_null()) { run_check_proof_query(broadcast_.block_id, proof_, manager_, timeout_, std::move(P), key_proof_link_); } else { @@ -288,11 +302,13 @@ void ValidateBroadcast::written_block_data() { td::actor::send_closure(SelfId, &ValidateBroadcast::checked_proof); } }); + VLOG(VALIDATOR_DEBUG) << "checking proof link"; run_check_proof_link_query(broadcast_.block_id, proof_link_, manager_, timeout_, std::move(P)); } } void ValidateBroadcast::checked_proof() { + VLOG(VALIDATOR_DEBUG) << "checked_proof"; if (handle_->inited_proof() && handle_->is_key_block()) { td::actor::send_closure(manager_, &ValidatorManager::update_last_known_key_block, handle_, false); } @@ -305,8 +321,9 @@ void ValidateBroadcast::checked_proof() { } }); - td::actor::create_actor("applyblock", handle_->id(), data_, handle_->id(), manager_, timeout_, - std::move(P)) + VLOG(VALIDATOR_DEBUG) << "apply block"; + td::actor::create_actor(PSTRING() << "apply" << handle_->id().id.to_str(), handle_->id(), data_, + handle_->id(), manager_, timeout_, std::move(P)) .release(); } else { finish_query();