From d46261c1103742cc456edb661c3b0a3039e78da1 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Tue, 2 Jul 2024 17:12:43 +0300 Subject: [PATCH 1/5] Fix reading state_serializer_enabled from config (#1045) --- validator-engine/validator-engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 8f7baed0..d81dcb8f 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -1411,7 +1411,6 @@ td::Status ValidatorEngine::load_global_config() { h.push_back(b); } validator_options_.write().set_hardforks(std::move(h)); - validator_options_.write().set_state_serializer_enabled(config_.state_serializer_enabled); return td::Status::OK(); } @@ -1823,6 +1822,8 @@ void ValidatorEngine::started_overlays() { void ValidatorEngine::start_validator() { validator_options_.write().set_allow_blockchain_init(config_.validators.size() > 0); + validator_options_.write().set_state_serializer_enabled(config_.state_serializer_enabled); + validator_manager_ = ton::validator::ValidatorManagerFactory::create( validator_options_, db_root_, keyring_.get(), adnl_.get(), rldp_.get(), overlay_manager_.get()); From c54f095c1b306546d86efbcd228258d1c43a0f17 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Wed, 10 Jul 2024 09:17:37 +0300 Subject: [PATCH 2/5] Don't use user-provided fees for internal messages (#1050) --- crypto/block/transaction.cpp | 9 +++++++-- crypto/block/transaction.h | 1 + doc/GlobalVersions.md | 1 + validator/impl/validate-query.cpp | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 3f1be336..379d899e 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -2392,8 +2392,12 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, if (!tlb::csr_unpack(msg.info, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) { return -1; } - fwd_fee = block::tlb::t_Grams.as_integer(info.fwd_fee); - ihr_fee = block::tlb::t_Grams.as_integer(info.ihr_fee); + if (cfg.disable_custom_fess) { + fwd_fee = ihr_fee = td::zero_refint(); + } else { + fwd_fee = block::tlb::t_Grams.as_integer(info.fwd_fee); + ihr_fee = block::tlb::t_Grams.as_integer(info.ihr_fee); + } } // set created_at and created_lt to correct values info.created_at = now; @@ -3755,6 +3759,7 @@ td::Status FetchConfigParams::fetch_config_params( action_phase_cfg->action_fine_enabled = config.get_global_version() >= 4; action_phase_cfg->bounce_on_fail_enabled = config.get_global_version() >= 4; action_phase_cfg->message_skip_enabled = config.get_global_version() >= 8; + action_phase_cfg->disable_custom_fess = config.get_global_version() >= 8; action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr; } { diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 29290cfb..20d7cb29 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -168,6 +168,7 @@ struct ActionPhaseConfig { bool action_fine_enabled{false}; bool bounce_on_fail_enabled{false}; bool message_skip_enabled{false}; + bool disable_custom_fess{false}; td::optional mc_blackhole_addr; const MsgPrices& fetch_msg_prices(bool is_masterchain) const { return is_masterchain ? fwd_mc : fwd_std; diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index 86de6317..e649c009 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -109,3 +109,4 @@ Operations for working with Merkle proofs, where cells can have non-zero level a - Slightly change random seed generation to fix mix of `addr_rewrite` and `addr`. - Fill in `skipped_actions` for both invalid and valid messages with `IGNORE_ERROR` mode that can't be sent. - Allow unfreeze through external messages. +- Don't use user-provided `fwd_fee` and `ihr_fee` for internal messages. \ No newline at end of file diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 0de6d667..5345ed72 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -995,6 +995,7 @@ bool ValidateQuery::fetch_config_params() { action_phase_cfg_.action_fine_enabled = config_->get_global_version() >= 4; action_phase_cfg_.bounce_on_fail_enabled = config_->get_global_version() >= 4; action_phase_cfg_.message_skip_enabled = config_->get_global_version() >= 8; + action_phase_cfg_.disable_custom_fess = config_->get_global_version() >= 8; action_phase_cfg_.mc_blackhole_addr = config_->get_burning_config().blackhole_addr; } { From 57f95cc28224a9e938d029bd92125fdcd3f71c8d Mon Sep 17 00:00:00 2001 From: EmelyanenkoK Date: Wed, 10 Jul 2024 14:58:13 +0300 Subject: [PATCH 3/5] Add collator options (#1052) * Set collator options from validator console * Fix compilation error in manager-disk * Defer all messages if out msg queue is too big * Fix checking queue size in collator --------- Co-authored-by: SpyCheese --- crypto/block/block.tlb | 2 +- crypto/block/mc-config.cpp | 1 + crypto/block/mc-config.h | 1 + tl/generate/scheme/ton_api.tl | 7 ++ tl/generate/scheme/ton_api.tlo | Bin 90304 -> 90924 bytes .../validator-engine-console-query.cpp | 39 ++++++++ .../validator-engine-console-query.h | 41 ++++++++ .../validator-engine-console.cpp | 2 + validator-engine/validator-engine.cpp | 91 ++++++++++++++++++ validator-engine/validator-engine.hpp | 6 ++ validator/fabric.h | 4 +- validator/impl/collator-impl.h | 7 +- validator/impl/collator.cpp | 39 +++++--- validator/impl/fabric.cpp | 10 +- validator/impl/validate-query.cpp | 26 ++++- validator/impl/validate-query.hpp | 1 + validator/manager-disk.cpp | 4 +- validator/manager.cpp | 8 +- validator/validator-group.cpp | 4 +- validator/validator-group.hpp | 4 + validator/validator-options.hpp | 7 ++ validator/validator.h | 17 ++++ 22 files changed, 294 insertions(+), 27 deletions(-) diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index b96b4914..a3684f56 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -801,7 +801,7 @@ size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig; size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16 max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32 - max_acc_public_libraries:uint32 = SizeLimitsConfig; + max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 = SizeLimitsConfig; _ SizeLimitsConfig = ConfigParam 43; // key is [ wc:int32 addr:uint256 ] diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 1dbfeaed..6da0f034 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -1956,6 +1956,7 @@ td::Result Config::do_get_size_limits_config(td::RefA)f6dx8aHf(8H@Y^rYn delta 55 zcmZ2;jP<}lR^CUm^{p77fNdjhu<+&u!Y&GootvK;SI2Ianf#)Mv2Hu70%N$;_A^$D G2^s+I01^8D diff --git a/validator-engine-console/validator-engine-console-query.cpp b/validator-engine-console/validator-engine-console-query.cpp index 5385d2e6..72fbce2e 100644 --- a/validator-engine-console/validator-engine-console-query.cpp +++ b/validator-engine-console/validator-engine-console-query.cpp @@ -1203,3 +1203,42 @@ td::Status SetStateSerializerEnabledQuery::receive(td::BufferSlice data) { td::TerminalIO::out() << "success\n"; return td::Status::OK(); } + +td::Status SetCollatorOptionsJsonQuery::run() { + TRY_RESULT_ASSIGN(file_name_, tokenizer_.get_token()); + TRY_STATUS(tokenizer_.check_endl()); + return td::Status::OK(); +} + +td::Status SetCollatorOptionsJsonQuery::send() { + TRY_RESULT(data, td::read_file(file_name_)); + auto b = + ton::create_serialize_tl_object(data.as_slice().str()); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status SetCollatorOptionsJsonQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "success\n"; + return td::Status::OK(); +} + +td::Status ResetCollatorOptionsQuery::run() { + TRY_STATUS(tokenizer_.check_endl()); + return td::Status::OK(); +} + +td::Status ResetCollatorOptionsQuery::send() { + auto b = ton::create_serialize_tl_object("{}"); + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); + return td::Status::OK(); +} + +td::Status ResetCollatorOptionsQuery::receive(td::BufferSlice data) { + TRY_RESULT_PREFIX(f, ton::fetch_tl_object(data.as_slice(), true), + "received incorrect answer: "); + td::TerminalIO::out() << "success\n"; + return td::Status::OK(); +} diff --git a/validator-engine-console/validator-engine-console-query.h b/validator-engine-console/validator-engine-console-query.h index 3047350f..cb87f2ae 100644 --- a/validator-engine-console/validator-engine-console-query.h +++ b/validator-engine-console/validator-engine-console-query.h @@ -1229,3 +1229,44 @@ class SetStateSerializerEnabledQuery : public Query { private: bool enabled_; }; + +class SetCollatorOptionsJsonQuery : public Query { + public: + SetCollatorOptionsJsonQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "setcollatoroptionsjson"; + } + static std::string get_help() { + return "setcollatoroptionsjson \tset collator options from file "; + } + std::string name() const override { + return get_name(); + } + + private: + std::string file_name_; +}; + +class ResetCollatorOptionsQuery : public Query { + public: + ResetCollatorOptionsQuery(td::actor::ActorId console, Tokenizer tokenizer) + : Query(console, std::move(tokenizer)) { + } + td::Status run() override; + td::Status send() override; + td::Status receive(td::BufferSlice data) override; + static std::string get_name() { + return "resetcollatoroptions"; + } + static std::string get_help() { + return "resetcollatoroptions\tset collator options to default values"; + } + std::string name() const override { + return get_name(); + } +}; diff --git a/validator-engine-console/validator-engine-console.cpp b/validator-engine-console/validator-engine-console.cpp index 4878a292..dc92c476 100644 --- a/validator-engine-console/validator-engine-console.cpp +++ b/validator-engine-console/validator-engine-console.cpp @@ -147,6 +147,8 @@ void ValidatorEngineConsole::run() { add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); + add_query_runner(std::make_unique>()); } bool ValidatorEngineConsole::envelope_send_query(td::BufferSlice query, td::Promise promise) { diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index d81dcb8f..8a46ef53 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -1823,6 +1823,7 @@ void ValidatorEngine::started_overlays() { void ValidatorEngine::start_validator() { validator_options_.write().set_allow_blockchain_init(config_.validators.size() > 0); validator_options_.write().set_state_serializer_enabled(config_.state_serializer_enabled); + load_collator_options(); validator_manager_ = ton::validator::ValidatorManagerFactory::create( validator_options_, db_root_, keyring_.get(), adnl_.get(), rldp_.get(), overlay_manager_.get()); @@ -2414,6 +2415,69 @@ void ValidatorEngine::del_custom_overlay_from_config(std::string name, td::Promi promise.set_error(td::Status::Error(PSTRING() << "no overlay \"" << name << "\" in config")); } +static td::Result> parse_collator_options(td::MutableSlice json_str) { + td::Ref ref{true}; + ton::validator::CollatorOptions& opts = ref.write(); + + // Set default values (from_json leaves missing fields as is) + ton::ton_api::engine_validator_collatorOptions f; + f.deferring_enabled_ = opts.deferring_enabled; + f.defer_out_queue_size_limit_ = opts.defer_out_queue_size_limit; + f.defer_messages_after_ = opts.defer_messages_after; + f.dispatch_phase_2_max_total_ = opts.dispatch_phase_2_max_total; + f.dispatch_phase_3_max_total_ = opts.dispatch_phase_3_max_total; + f.dispatch_phase_2_max_per_initiator_ = opts.dispatch_phase_2_max_per_initiator; + f.dispatch_phase_3_max_per_initiator_ = + opts.dispatch_phase_3_max_per_initiator ? opts.dispatch_phase_3_max_per_initiator.value() : -1; + + TRY_RESULT_PREFIX(json, td::json_decode(json_str), "failed to parse json: "); + TRY_STATUS_PREFIX(ton::ton_api::from_json(f, json.get_object()), "json does not fit TL scheme: "); + + if (f.defer_messages_after_ <= 0) { + return td::Status::Error("defer_messages_after should be positive"); + } + if (f.defer_out_queue_size_limit_ < 0) { + return td::Status::Error("defer_out_queue_size_limit should be non-negative"); + } + if (f.dispatch_phase_2_max_total_ < 0) { + return td::Status::Error("dispatch_phase_2_max_total should be non-negative"); + } + if (f.dispatch_phase_3_max_total_ < 0) { + return td::Status::Error("dispatch_phase_3_max_total should be non-negative"); + } + if (f.dispatch_phase_2_max_per_initiator_ < 0) { + return td::Status::Error("dispatch_phase_2_max_per_initiator should be non-negative"); + } + + opts.deferring_enabled = f.deferring_enabled_; + opts.defer_messages_after = f.defer_messages_after_; + opts.defer_out_queue_size_limit = f.defer_out_queue_size_limit_; + opts.dispatch_phase_2_max_total = f.dispatch_phase_2_max_total_; + opts.dispatch_phase_3_max_total = f.dispatch_phase_3_max_total_; + opts.dispatch_phase_2_max_per_initiator = f.dispatch_phase_2_max_per_initiator_; + if (f.dispatch_phase_3_max_per_initiator_ >= 0) { + opts.dispatch_phase_3_max_per_initiator = f.dispatch_phase_3_max_per_initiator_; + } else { + opts.dispatch_phase_3_max_per_initiator = {}; + } + + return ref; +} + +void ValidatorEngine::load_collator_options() { + auto r_data = td::read_file(collator_options_file()); + if (r_data.is_error()) { + return; + } + td::BufferSlice data = r_data.move_as_ok(); + auto r_collator_options = parse_collator_options(data.as_slice()); + if (r_collator_options.is_error()) { + LOG(ERROR) << "Failed to read collator options from file: " << r_collator_options.move_as_error(); + return; + } + validator_options_.write().set_collator_options(r_collator_options.move_as_ok()); +} + void ValidatorEngine::check_key(ton::PublicKeyHash id, td::Promise promise) { if (keys_.count(id) == 1) { promise.set_value(td::Unit()); @@ -3684,6 +3748,33 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setStateS }); } +void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setCollatorOptionsJson &query, + td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, + td::Promise promise) { + if (!(perm & ValidatorEnginePermissions::vep_modify)) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized"))); + return; + } + if (!started_) { + promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started"))); + return; + } + auto r_collator_options = parse_collator_options(query.json_); + if (r_collator_options.is_error()) { + promise.set_value(create_control_query_error(r_collator_options.move_as_error_prefix("failed to parse json: "))); + return; + } + auto S = td::write_file(collator_options_file(), query.json_); + if (S.is_error()) { + promise.set_value(create_control_query_error(r_collator_options.move_as_error_prefix("failed to write file: "))); + return; + } + validator_options_.write().set_collator_options(r_collator_options.move_as_ok()); + td::actor::send_closure(validator_manager_, &ton::validator::ValidatorManagerInterface::update_options, + validator_options_); + promise.set_value(ton::create_serialize_tl_object()); +} + void ValidatorEngine::process_control_query(td::uint16 port, ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) { diff --git a/validator-engine/validator-engine.hpp b/validator-engine/validator-engine.hpp index 8adc8d9a..ad5e479b 100644 --- a/validator-engine/validator-engine.hpp +++ b/validator-engine/validator-engine.hpp @@ -384,12 +384,16 @@ class ValidatorEngine : public td::actor::Actor { std::string custom_overlays_config_file() const { return db_root_ + "/custom-overlays.json"; } + std::string collator_options_file() const { + return db_root_ + "/collator-options.json"; + } void load_custom_overlays_config(); td::Status write_custom_overlays_config(); void add_custom_overlay_to_config( ton::tl_object_ptr overlay, td::Promise promise); void del_custom_overlay_from_config(std::string name, td::Promise promise); + void load_collator_options(); void check_key(ton::PublicKeyHash id, td::Promise promise); @@ -477,6 +481,8 @@ class ValidatorEngine : public td::actor::Actor { ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); void run_control_query(ton::ton_api::engine_validator_setStateSerializerEnabled &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); + void run_control_query(ton::ton_api::engine_validator_setCollatorOptionsJson &query, td::BufferSlice data, + ton::PublicKeyHash src, td::uint32 perm, td::Promise promise); template void run_control_query(T &query, td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm, td::Promise promise) { diff --git a/validator/fabric.h b/validator/fabric.h index 6bb66845..949a6c9f 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -80,8 +80,8 @@ void run_validate_query(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_maste td::Promise promise, bool is_fake = false); void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& min_masterchain_block_id, std::vector prev, Ed25519_PublicKey local_id, td::Ref validator_set, - td::actor::ActorId manager, td::Timestamp timeout, - td::Promise promise); + td::Ref collator_opts, td::actor::ActorId manager, + td::Timestamp timeout, td::Promise promise); void run_collate_hardfork(ShardIdFull shard, const BlockIdExt& min_masterchain_block_id, std::vector prev, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index edb00721..44286e72 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -71,6 +71,7 @@ class Collator final : public td::actor::Actor { std::vector> prev_states; std::vector> prev_block_data; Ed25519_PublicKey created_by_; + Ref collator_opts_; Ref validator_set_; td::actor::ActorId manager; td::Timestamp timeout; @@ -90,7 +91,8 @@ class Collator final : public td::actor::Actor { public: Collator(ShardIdFull shard, bool is_hardfork, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, std::vector prev, Ref validator_set, Ed25519_PublicKey collator_id, - td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); + Ref collator_opts, td::actor::ActorId manager, td::Timestamp timeout, + td::Promise promise); ~Collator() override = default; bool is_busy() const { return busy_; @@ -195,6 +197,7 @@ class Collator final : public td::actor::Actor { std::unique_ptr in_msg_dict, out_msg_dict, out_msg_queue_, sibling_out_msg_queue_; std::map unprocessed_deferred_messages_; // number of messages from dispatch queue in new_msgs td::uint64 out_msg_queue_size_ = 0; + td::uint64 old_out_msg_queue_size_ = 0; bool have_out_msg_queue_size_in_state_ = false; std::unique_ptr ihr_pending; std::shared_ptr processed_upto_, sibling_processed_upto_; @@ -211,6 +214,8 @@ class Collator final : public td::actor::Actor { unsigned dispatch_queue_ops_{0}; std::map last_dispatch_queue_emitted_lt_; bool have_unprocessed_account_dispatch_queue_ = true; + td::uint64 defer_out_queue_size_limit_; + td::uint64 hard_defer_out_queue_size_limit_; bool msg_metadata_enabled_ = false; bool deferring_messages_enabled_ = false; diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 81ec5756..43b17dfd 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -50,7 +50,6 @@ static const td::uint32 SPLIT_MAX_QUEUE_SIZE = 100000; static const td::uint32 MERGE_MAX_QUEUE_SIZE = 2047; static const td::uint32 SKIP_EXTERNALS_QUEUE_SIZE = 8000; static const int HIGH_PRIORITY_EXTERNAL = 10; // don't skip high priority externals when queue is big -static const int DEFER_MESSAGES_AFTER = 10; // 10'th and later messages from address will be deferred #define DBG(__n) dbg(__n)&& #define DSTART int __dcnt = 0; @@ -72,20 +71,22 @@ static inline bool dbg(int c) { * @param prev A vector of BlockIdExt representing the previous blocks. * @param validator_set A reference to the ValidatorSet. * @param collator_id The public key of the block creator. + * @param collator_opts A reference to CollatorOptions. * @param manager The ActorId of the ValidatorManager. * @param timeout The timeout for the collator. * @param promise The promise to return the result. */ Collator::Collator(ShardIdFull shard, bool is_hardfork, UnixTime min_ts, BlockIdExt min_masterchain_block_id, - std::vector prev, td::Ref validator_set, Ed25519_PublicKey collator_id, - td::actor::ActorId manager, td::Timestamp timeout, - td::Promise promise) + std::vector prev, Ref validator_set, Ed25519_PublicKey collator_id, + Ref collator_opts, td::actor::ActorId manager, + td::Timestamp timeout, td::Promise promise) : shard_(shard) , is_hardfork_(is_hardfork) , min_ts(min_ts) , min_mc_block_id{min_masterchain_block_id} , prev_blocks(std::move(prev)) , created_by_(collator_id) + , collator_opts_(collator_opts) , validator_set_(std::move(validator_set)) , manager(manager) , timeout(timeout) @@ -1786,6 +1787,7 @@ bool Collator::try_collate() { last_proc_int_msg_.second.set_zero(); first_unproc_int_msg_.first = ~0ULL; first_unproc_int_msg_.second.set_ones(); + old_out_msg_queue_size_ = out_msg_queue_size_; if (is_masterchain()) { LOG(DEBUG) << "getting the list of special smart contracts"; auto res = config_->get_special_smartcontracts(); @@ -1970,6 +1972,10 @@ bool Collator::fetch_config_params() { return fatal_error(res.move_as_error()); } compute_phase_cfg_.libraries = std::make_unique(config_->get_libraries_root(), 256); + defer_out_queue_size_limit_ = std::max(collator_opts_->defer_out_queue_size_limit, + compute_phase_cfg_.size_limits.defer_out_queue_size_limit); + // This one is checked in validate-query + hard_defer_out_queue_size_limit_ = compute_phase_cfg_.size_limits.defer_out_queue_size_limit; return true; } @@ -3053,8 +3059,10 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R bool is_special_account = is_masterchain() && config_->is_special_smartcontract(src_addr); bool defer = false; if (!from_dispatch_queue) { - if (deferring_messages_enabled_ && !is_special && !is_special_account && msg.msg_idx != 0) { - if (++sender_generated_messages_count_[src_addr] >= DEFER_MESSAGES_AFTER) { + if (deferring_messages_enabled_ && collator_opts_->deferring_enabled && !is_special && !is_special_account && + msg.msg_idx != 0) { + if (++sender_generated_messages_count_[src_addr] >= collator_opts_->defer_messages_after || + out_msg_queue_size_ > defer_out_queue_size_limit_) { defer = true; } } @@ -3626,18 +3634,24 @@ int Collator::process_external_message(Ref msg) { * @returns True if the processing was successful, false otherwise. */ bool Collator::process_dispatch_queue() { + if (out_msg_queue_size_ > defer_out_queue_size_limit_ && old_out_msg_queue_size_ > hard_defer_out_queue_size_limit_) { + return true; + } have_unprocessed_account_dispatch_queue_ = true; - size_t max_total_count[3] = {1 << 30, 150, 150}; - size_t max_per_initiator[3] = {1 << 30, 20, 0}; - if (out_msg_queue_size_ <= 256) { + size_t max_total_count[3] = {1 << 30, collator_opts_->dispatch_phase_2_max_total, + collator_opts_->dispatch_phase_3_max_total}; + size_t max_per_initiator[3] = {1 << 30, collator_opts_->dispatch_phase_2_max_per_initiator, 0}; + if (collator_opts_->dispatch_phase_3_max_per_initiator) { + max_per_initiator[2] = collator_opts_->dispatch_phase_3_max_per_initiator.value(); + } else if (out_msg_queue_size_ <= 256) { max_per_initiator[2] = 10; } else if (out_msg_queue_size_ <= 512) { max_per_initiator[2] = 2; - } else if (out_msg_queue_size_ <= 2048) { + } else if (out_msg_queue_size_ <= 1500) { max_per_initiator[2] = 1; } for (int iter = 0; iter < 3; ++iter) { - if (max_per_initiator[iter] == 0) { + if (max_per_initiator[iter] == 0 || max_total_count[iter] == 0) { continue; } vm::AugmentedDictionary cur_dispatch_queue{dispatch_queue_->get_root(), 256, block::tlb::aug_DispatchQueue}; @@ -3676,7 +3690,8 @@ bool Collator::process_dispatch_queue() { // Remove message from DispatchQueue bool ok; - if (iter == 0 || (iter == 1 && sender_generated_messages_count_[src_addr] >= DEFER_MESSAGES_AFTER)) { + if (iter == 0 || + (iter == 1 && sender_generated_messages_count_[src_addr] >= collator_opts_->defer_messages_after)) { ok = cur_dispatch_queue.lookup_delete(src_addr).not_null(); } else { dict.lookup_delete(key); diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index 997fa9a1..d6949239 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -213,8 +213,8 @@ void run_validate_query(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_maste void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& min_masterchain_block_id, std::vector prev, Ed25519_PublicKey collator_id, td::Ref validator_set, - td::actor::ActorId manager, td::Timestamp timeout, - td::Promise promise) { + td::Ref collator_opts, td::actor::ActorId manager, + td::Timestamp timeout, td::Promise promise) { BlockSeqno seqno = 0; for (auto& p : prev) { if (p.seqno() > seqno) { @@ -223,7 +223,8 @@ void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& m } td::actor::create_actor(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1), shard, false, min_ts, min_masterchain_block_id, std::move(prev), std::move(validator_set), - collator_id, std::move(manager), timeout, std::move(promise)) + collator_id, std::move(collator_opts), std::move(manager), timeout, + std::move(promise)) .release(); } @@ -238,7 +239,8 @@ void run_collate_hardfork(ShardIdFull shard, const BlockIdExt& min_masterchain_b } td::actor::create_actor(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1), shard, true, 0, min_masterchain_block_id, std::move(prev), td::Ref{}, - Ed25519_PublicKey{Bits256::zero()}, std::move(manager), timeout, std::move(promise)) + Ed25519_PublicKey{Bits256::zero()}, td::Ref{true}, + std::move(manager), timeout, std::move(promise)) .release(); } diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 5345ed72..8b062ce9 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -3405,7 +3405,7 @@ bool ValidateQuery::check_account_dispatch_queue_update(td::Bits256 addr, Ref 0 && max_removed_lt == 0) { - have_unprocessed_account_dispatch_queue_ = true; + ++processed_account_dispatch_queues_; } return true; } @@ -3432,6 +3432,27 @@ bool ValidateQuery::unpack_dispatch_queue_update() { if (!res) { return reject_query("invalid DispatchQueue dictionary in the new state"); } + + if (old_out_msg_queue_size_ <= compute_phase_cfg_.size_limits.defer_out_queue_size_limit) { + // Check that at least one message was taken from each AccountDispatchQueue + try { + have_unprocessed_account_dispatch_queue_ = false; + td::uint64 total_account_dispatch_queues = 0; + ps_.dispatch_queue_->check_for_each([&](Ref, td::ConstBitPtr, int n) -> bool { + assert(n == 352); + ++total_account_dispatch_queues; + if (total_account_dispatch_queues > processed_account_dispatch_queues_) { + return false; + } + return true; + }); + have_unprocessed_account_dispatch_queue_ = + (total_account_dispatch_queues != processed_account_dispatch_queues_); + } catch (vm::VmVirtError&) { + // VmVirtError can happen if we have only a proof of ShardState + have_unprocessed_account_dispatch_queue_ = true; + } + } } catch (vm::VmError& err) { return reject_query("invalid DispatchQueue dictionary difference between the old and the new state: "s + err.get_msg()); @@ -3694,7 +3715,8 @@ bool ValidateQuery::check_in_msg(td::ConstBitPtr key, Ref in_msg) } if (have_unprocessed_account_dispatch_queue_ && tag != block::gen::InMsg::msg_import_ext && tag != block::gen::InMsg::msg_import_deferred_tr && tag != block::gen::InMsg::msg_import_deferred_fin) { - // Collator is requeired to take at least one message from each AccountDispatchQueue (unless the block is full) + // Collator is requeired to take at least one message from each AccountDispatchQueue + // (unless the block is full or unless out_msg_queue_size is big) // If some AccountDispatchQueue is unporcessed then it's not allowed to import other messages except for externals return reject_query("required DispatchQueue processing is not done, but some other internal messages are imported"); } diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index ec77d2c8..824afb49 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -241,6 +241,7 @@ class ValidateQuery : public td::actor::Actor { bool deferring_messages_enabled_ = false; bool store_out_msg_queue_size_ = false; + td::uint64 processed_account_dispatch_queues_ = 0; bool have_unprocessed_account_dispatch_queue_ = false; td::PerfWarningTimer perf_timer_; diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index 17b793c7..5678408c 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -128,8 +128,8 @@ void ValidatorManagerImpl::sync_complete(td::Promise promise) { } Ed25519_PublicKey created_by{td::Bits256::zero()}; td::as(created_by.as_bits256().data() + 32 - 4) = ((unsigned)std::time(nullptr) >> 8); - run_collate_query(shard_id, 0, last_masterchain_block_id_, prev, created_by, val_set, actor_id(this), - td::Timestamp::in(10.0), std::move(P)); + run_collate_query(shard_id, 0, last_masterchain_block_id_, prev, created_by, val_set, td::Ref{true}, + actor_id(this), td::Timestamp::in(10.0), std::move(P)); } void ValidatorManagerImpl::validate_fake(BlockCandidate candidate, std::vector prev, BlockIdExt last, diff --git a/validator/manager.cpp b/validator/manager.cpp index 2af818e6..3dbca852 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -3133,10 +3133,16 @@ void ValidatorManagerImpl::get_validator_groups_info_for_litequery( } void ValidatorManagerImpl::update_options(td::Ref opts) { - // Currently options can be updated only to change state_serializer_enabled flag + // Currently options can be updated only to change state_serializer_enabled flag and collator_options if (!serializer_.empty()) { td::actor::send_closure(serializer_, &AsyncStateSerializer::update_options, opts); } + for (auto &group : validator_groups_) { + td::actor::send_closure(group.second.actor, &ValidatorGroup::update_options, opts); + } + for (auto &group : next_validator_groups_) { + td::actor::send_closure(group.second.actor, &ValidatorGroup::update_options, opts); + } opts_ = std::move(opts); } diff --git a/validator/validator-group.cpp b/validator/validator-group.cpp index 68e2b07e..fc3ebe54 100644 --- a/validator/validator-group.cpp +++ b/validator/validator-group.cpp @@ -52,8 +52,8 @@ void ValidatorGroup::generate_block_candidate( })); run_collate_query( shard_, min_ts_, min_masterchain_block_id_, prev_block_ids_, - Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, validator_set_, manager_, td::Timestamp::in(10.0), - [SelfId = actor_id(this), cache = cached_collated_block_](td::Result R) { + Ed25519_PublicKey{local_id_full_.ed25519_value().raw()}, validator_set_, opts_->get_collator_options(), manager_, + td::Timestamp::in(10.0), [SelfId = actor_id(this), cache = cached_collated_block_](td::Result R) { td::actor::send_closure(SelfId, &ValidatorGroup::generated_block_candidate, std::move(cache), std::move(R)); }); } diff --git a/validator/validator-group.hpp b/validator/validator-group.hpp index f9940264..3499da9d 100644 --- a/validator/validator-group.hpp +++ b/validator/validator-group.hpp @@ -64,6 +64,10 @@ class ValidatorGroup : public td::actor::Actor { void get_validator_group_info_for_litequery( td::Promise> promise); + void update_options(td::Ref opts) { + opts_ = std::move(opts); + } + ValidatorGroup(ShardIdFull shard, PublicKeyHash local_id, ValidatorSessionId session_id, td::Ref validator_set, validatorsession::ValidatorSessionOptions config, td::actor::ActorId keyring, td::actor::ActorId adnl, diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 37006bda..9e776779 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -144,6 +144,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { bool get_state_serializer_enabled() const override { return state_serializer_enabled_; } + td::Ref get_collator_options() const override { + return collator_options_; + } void set_zero_block_id(BlockIdExt block_id) override { zero_block_id_ = block_id; @@ -227,6 +230,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { void set_state_serializer_enabled(bool value) override { state_serializer_enabled_ = value; } + void set_collator_options(td::Ref value) override { + collator_options_ = std::move(value); + } ValidatorManagerOptionsImpl *make_copy() const override { return new ValidatorManagerOptionsImpl(*this); @@ -279,6 +285,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { bool celldb_preload_all_ = false; td::optional catchain_max_block_delay_; bool state_serializer_enabled_ = true; + td::Ref collator_options_{true}; }; } // namespace validator diff --git a/validator/validator.h b/validator/validator.h index c4082c55..5bbb6647 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -51,6 +51,21 @@ struct PerfTimerStats { std::deque> stats; // }; +struct CollatorOptions : public td::CntObject { + bool deferring_enabled = true; + + // Defer messages from account after Xth message in block (excluding first messages from transactions) + td::uint32 defer_messages_after = 10; + // Defer all messages if out msg queue size is greater than X (excluding first messages from transactions) + td::uint64 defer_out_queue_size_limit = 2048; + + // See Collator::process_dispatch_queue + td::uint32 dispatch_phase_2_max_total = 150; + td::uint32 dispatch_phase_3_max_total = 150; + td::uint32 dispatch_phase_2_max_per_initiator = 20; + td::optional dispatch_phase_3_max_per_initiator; // Default - depends on out msg queue size +}; + struct ValidatorManagerOptions : public td::CntObject { public: enum class ShardCheckMode { m_monitor, m_validate }; @@ -91,6 +106,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual bool get_celldb_preload_all() const = 0; virtual td::optional get_catchain_max_block_delay() const = 0; virtual bool get_state_serializer_enabled() const = 0; + virtual td::Ref get_collator_options() const = 0; virtual void set_zero_block_id(BlockIdExt block_id) = 0; virtual void set_init_block_id(BlockIdExt block_id) = 0; @@ -120,6 +136,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual void set_celldb_preload_all(bool value) = 0; virtual void set_catchain_max_block_delay(double value) = 0; virtual void set_state_serializer_enabled(bool value) = 0; + virtual void set_collator_options(td::Ref value) = 0; static td::Ref create( BlockIdExt zero_block_id, BlockIdExt init_block_id, From 2792fc22f1ff47c40974820b235f0644a33db5ab Mon Sep 17 00:00:00 2001 From: neodix42 Date: Wed, 10 Jul 2024 13:59:08 +0200 Subject: [PATCH 4/5] Improved Docker image (#1051) * add github action for macOS 14 (arm64, M1) * add github action (portable) for macOS 14 (arm64, M1) * rename macOS arm64 output artifact * Update libsodium on windows * Compile libsodium * Update build-windows.bat * use upgraded libsodium 1.0.20; use compiled static libsodium for Windows instead of precompiled; * revert libsodium 1.0.20; use compiled static libsodium for Windows instead of precompiled; * use upgraded libsodium 1.0.20; use compiled static libsodium for Windows instead of precompiled; * fix libsodium version 1.0.19; use compiled static libsodium for Windows instead of precompiled; * try 1.0.20 libsodium precompiled on github * try 1.0.18 libsodium precompiled on github * try windows build on win server 2019 * and use PlatformToolset=v142 * use cmake -G "Visual Studio 16 2019" * fix path to msvc 2019 on github * separate github windows build on win server 2019 and build on win server 2022 * Update assembly/native/build-windows-2019.bat add retry mechanism Co-authored-by: Dr. Awesome Doge * rework docker image; provide installation, configuration and troubleshooting guidelines; add nc, ifconfig, netstat and iptraf-ng utilities for troubleshooting; * put back control.template * add tcpdump and curl to the docker image; update default validator ports; add kubernetes deployment guidelines with network=host; test metalLB load balancer * tested metalLB load balancer * tested aws deployment * tested gcp deployment * todo ali cloud and storage mount points, currently only the networking was tested * add storage/pv/pvc; repair broken links, adjust docu * change to dynamic storage provisioning without node affinity (statefulSet+headless service) WIP * modify gcp deployment WIP * modify aws deployment WIP * add resource requests/limits * some docu changes * some docu changes; aws tested * support $DUMP_URL parameter as well as $ZFS_POOL_NAME; add pv and plzip to docker image for dump extraction; use mainnet dump by default in k8s deployments; * support $DUMP_URL parameter as well as $ZFS_POOL_NAME; add pv and plzip to docker image for dump extraction; use mainnet dump by default in k8s deployments; add AliCloud support * minor remarks, final tests * remove ZFS_POOL_NAME parameter * improve docker github action - run test and add release tag, compile against arm64 * set docker test timeout * test if validator-engine inside the docker image is valid * test if validator-engine inside the docker image is valid * test if validator-engine inside the docker image is valid * adjust recommended node values for ali cloud deployment --------- Co-authored-by: neodiX Co-authored-by: Dr. Awesome Doge --- .github/workflows/docker-ubuntu-image.yml | 37 +- Dockerfile | 34 +- docker/README.md | 545 +++++++++++++++++++++- docker/control.template | 2 +- docker/init.sh | 112 ++++- docker/ton-ali.yaml | 121 +++++ docker/ton-aws.yaml | 122 +++++ docker/ton-gcp.yaml | 134 ++++++ docker/ton-metal-lb.yaml | 118 +++++ docker/ton-node-port.yaml | 126 +++++ 10 files changed, 1284 insertions(+), 67 deletions(-) create mode 100644 docker/ton-ali.yaml create mode 100644 docker/ton-aws.yaml create mode 100644 docker/ton-gcp.yaml create mode 100644 docker/ton-metal-lb.yaml create mode 100644 docker/ton-node-port.yaml diff --git a/.github/workflows/docker-ubuntu-image.yml b/.github/workflows/docker-ubuntu-image.yml index 449711d8..48c553ef 100644 --- a/.github/workflows/docker-ubuntu-image.yml +++ b/.github/workflows/docker-ubuntu-image.yml @@ -20,22 +20,49 @@ jobs: submodules: 'recursive' - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v1 + uses: docker/setup-buildx-action@v3 - name: Login to GitHub Container Registry - uses: docker/login-action@v1 + uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Build and export to Docker + uses: docker/build-push-action@v6 + with: + load: true + context: ./ + tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test + + - name: Test + run: | + docker run --rm -e "TEST=1" ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:test + + - name: Get next tag + id: tag + run: | + git fetch --all --tags + git tag -l + NEW_TAG=v$(date +'%Y.%m') + FOUND=$(git tag -l | grep $NEW_TAG | wc -l) + if [ $FOUND -eq 0 ]; then + echo "TAG=$NEW_TAG" >> $GITHUB_OUTPUT + else + echo "TAG=$NEW_TAG-$FOUND" >> $GITHUB_OUTPUT + fi + - name: Build and push id: docker_build - uses: docker/build-push-action@v2 + uses: docker/build-push-action@v6 with: + platforms: linux/amd64,linux/arm64 push: true context: ./ - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.TAG }} diff --git a/Dockerfile b/Dockerfile index c5120e83..cf418763 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,15 @@ -FROM ubuntu:22.04 as builder +FROM ubuntu:22.04 AS builder RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git ninja-build libsecp256k1-dev libsodium-dev libmicrohttpd-dev liblz4-dev pkg-config autoconf automake libtool libjemalloc-dev && \ - rm -rf /var/lib/apt/lists/* -ENV CC clang -ENV CXX clang++ -ENV CCACHE_DISABLE 1 + DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential cmake clang openssl libssl-dev zlib1g-dev gperf wget git ninja-build libsecp256k1-dev libsodium-dev libmicrohttpd-dev liblz4-dev pkg-config autoconf automake libtool libjemalloc-dev lsb-release software-properties-common gnupg + +RUN wget https://apt.llvm.org/llvm.sh && \ + chmod +x llvm.sh && \ + ./llvm.sh 16 all && \ + rm -rf /var/lib/apt/lists/* + +ENV CC=/usr/bin/clang-16 +ENV CXX=/usr/bin/clang++-16 +ENV CCACHE_DISABLE=1 WORKDIR / RUN mkdir ton @@ -13,17 +18,16 @@ WORKDIR /ton COPY ./ ./ RUN mkdir build && \ - cd build && \ - cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DTON_USE_JEMALLOC=ON .. && \ - ninja storage-daemon storage-daemon-cli tonlibjson fift func validator-engine validator-engine-console generate-random-id dht-server lite-client + cd build && \ + cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DPORTABLE=1 -DTON_ARCH= -DTON_USE_JEMALLOC=ON .. && \ + ninja storage-daemon storage-daemon-cli tonlibjson fift func validator-engine validator-engine-console generate-random-id dht-server lite-client FROM ubuntu:22.04 RUN apt-get update && \ - apt-get install -y wget libatomic1 openssl libsecp256k1-dev libsodium-dev libmicrohttpd-dev liblz4-dev libjemalloc-dev && \ + apt-get install -y wget curl libatomic1 openssl libsecp256k1-dev libsodium-dev libmicrohttpd-dev liblz4-dev libjemalloc-dev htop net-tools netcat iptraf-ng jq tcpdump pv plzip && \ rm -rf /var/lib/apt/lists/* -RUN mkdir -p /var/ton-work/db && \ - mkdir -p /var/ton-work/db/static +RUN mkdir -p /var/ton-work/db /var/ton-work/scripts COPY --from=builder /ton/build/storage/storage-daemon/storage-daemon /usr/local/bin/ COPY --from=builder /ton/build/storage/storage-daemon/storage-daemon-cli /usr/local/bin/ @@ -33,7 +37,7 @@ COPY --from=builder /ton/build/validator-engine-console/validator-engine-console COPY --from=builder /ton/build/utils/generate-random-id /usr/local/bin/ WORKDIR /var/ton-work/db -COPY ./docker/init.sh ./docker/control.template ./ -RUN chmod +x init.sh +COPY ./docker/init.sh ./docker/control.template /var/ton-work/scripts/ +RUN chmod +x /var/ton-work/scripts/init.sh -ENTRYPOINT ["/var/ton-work/db/init.sh"] +ENTRYPOINT ["/var/ton-work/scripts/init.sh"] diff --git a/docker/README.md b/docker/README.md index fd98374b..47e109db 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,28 +1,525 @@ -# The Open Network Node -Dockerfile for The Open Network Node +# Official TON Docker image + +1. [Dockerfile](#docker) +2. [Kubernetes deployment on-premises](#deploy-on-premises-with-metallb-load-balancer-) +3. [Kubernetes deployment on AWS](#deploy-on-aws-cloud-amazon-web-services) +4. [Kubernetes deployment on GCP](#deploy-on-gcp-google-cloud-platform) +5. [Kubernetes deployment on AliCloud](#deploy-on-ali-cloud) +6. [Troubleshooting](#troubleshooting) +## Prerequisites + +The TON node, whether it is validator or fullnode, requires a public IP address. +If your server is within an internal network or kubernetes you have to make sure that the required ports are available from the outside. + +Also pay attention at [hardware requirements](https://docs.ton.org/participate/run-nodes/full-node) for TON fullnodes and validators. Pods and StatefulSets in this guide imply these requirements. + +It is recommended to everyone to read Docker chapter first in order to get a better understanding about TON Docker image and its parameters. + +## Docker + +### Installation +```docker pull ghcr.io/ton-blockchain/ton:latest``` + +### Configuration +TON validator-engine supports number of command line parameters, +these parameters can be handed over to the container via environment variables. +Below is the list of supported arguments and their default values: + +| Argument | Description | Mandatory? | Default value | +|:------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|:-------------------------------------------------------:| +| PUBLIC_IP | This will be a public IP address of your TON node. Normally it is the same IP address as your server's external IP. This also can be your proxy server or load balancer IP address. | yes | | +| GLOBAL_CONFIG_URL | TON global configuration file. Mainnet - https://ton.org/global-config.json, Testnet - https://ton.org/testnet-global.config.json | no | https://api.tontech.io/ton/wallet-mainnet.autoconf.json | +| DUMP_URL | URL to TON dump. Specify dump from https://dump.ton.org. If you are using testnet dump, make sure to download global config for testnet. | no | | +| VALIDATOR_PORT | UDP port that must be available from the outside. Used for communication with other nodes. | no | 30001 | +| CONSOLE_PORT | This TCP port is used to access validator's console. Not necessarily to be opened for external access. | no | 30002 | +| LITE_PORT | Lite-server's TCP port. Used by lite-client. | no | 30003 | +| LITESERVER | true or false. Set to true if you want up and running lite-server. | no | false | +| STATE_TTL | Node's state will be gc'd after this time (in seconds). | no | 86400 | +| ARCHIVE_TTL | Node's archived blocks will be deleted after this time (in seconds). | no | 86400 | +| THREADS | Number of threads used by validator-engine. | no | 8 | +| VERBOSITY | Verbosity level. | no | 3 | +| CUSTOM_ARG | validator-engine might have some undocumented arguments. This is reserved for the test purposes.
For example you can pass **--logname /var/ton-work/log** in order to have log files. | no | | + +### Run the node - the quick way +The below command runs docker container with a TON node, that will start synchronization process. + +Notice **--network host** option, means that the Docker container will use the network namespace of the host machine. +In this case there is no need to map ports between the host and the container. The container will use the same IP address and ports as the host. +This approach simplifies networking configuration for the container, and usually is used on the dedicated server with assigned public IP. + +Keep in mind that this option can also introduce security concerns because the container has access to the host's network interfaces directly, which might not be desirable in a multi-tenant environment. + +Check your firewall configuration and make sure that at least UDP port 43677 is publicly available. +Find out your PUBLIC_IP: +``` +curl -4 ifconfig.me +``` +and replace it in the command below: +``` +docker run -d --name ton-node -v /data/db:/var/ton-work/db \ +-e "PUBLIC_IP=" \ +-e "LITESERVER=true" \ +-e "DUMP_URL=https://dump.ton.org/dumps/latest.tar.lz" \ +--network host \ +-it ghcr.io/ton-blockchain/ton +``` +If you don't need Lite-server, then remove -e "LITESERVER=true". + +### Run the node - isolated way +In production environments it is recommended to use **Port mapping** feature of Docker's default bridge network. +When you use port mapping, Docker allocates a specific port on the host to forward traffic to a port inside the container. +This is ideal for running multiple containers with isolated networks on the same host. +``` +docker run -d --name ton-node -v /data/db:/var/ton-work/db \ +-e "PUBLIC_IP=" \ +-e "DUMP_URL=https://dump.ton.org/dumps/latest.tar.lz" \ +-e "VALIDATOR_PORT=443" \ +-e "CONSOLE_PORT=88" \ +-e "LITE_PORT=443" \ +-e "LITESERVER=true" \ +-p 443:443/udp \ +-p 88:88/tcp \ +-p 443:443/tcp \ +-it ghcr.io/ton-blockchain/ton +``` +Adjust ports per your need. +Check your firewall configuration and make sure that customized ports (443/udp, 88/tcp and 443/tcp in this example) are publicly available. + +### Verify if TON node is operating correctly +After executing above command check the log files: + +```docker logs ton-node``` + +This is totally fine if in the log output for some time (up to 15 minutes) you see messages like: + +```log +failed to download proof link: [Error : 651 : no nodes] +``` + +After some time you should be able to see multiple messages similar to these below: +```log +failed to download key blocks: [Error : 652 : adnl query timeout] +last key block is [ w=-1 s=9223372036854775808 seq=34879845 rcEsfLF3E80PqQPWesW+rlOY2EpXd5UDrW32SzRWgus= C1Hs+q2Vew+WxbGL6PU1P6R2iYUJVJs4032CTS/DQzI= ] +getnextkey: [Error : 651 : not inited] +downloading state (-1,8000000000000000,38585739):9E86E166AE7E24BAA22762766381440C625F47E2B11D72967BB58CE8C90F7EBA:5BFFF759380097DF178325A7151E9C0571C4E452A621441A03A0CECAED970F57: total=1442840576 (71MB/s)downloading state (-1,8000000000000000,38585739):9E86E166AE7E24BAA22762766381440C625F47E2B11D72967BB58CE8C90F7EBA:5BFFF759380097DF178325A7151E9C0571C4E452A621441A03A0CECAED970F57: total=1442840576 (71MB/s) +finished downloading state (-1,8000000000000000,38585739):9E86E166AE7E24BAA22762766381440C625F47E2B11D72967BB58CE8C90F7EBA:5BFFF759380097DF178325A7151E9C0571C4E452A621441A03A0CECAED970F57: total=4520747390 +getnextkey: [Error : 651 : not inited] +getnextkey: [Error : 651 : not inited] +``` +As you noticed we have mounted docker volume to a local folder **/data/db**. +Go inside this folder on your server and check if its size is growing (```sudo du -h .*```) + +Now connect to the running container: +``` +docker exec -ti ton-node /bin/bash +``` +and try to connect and execute **getconfig** command via validator-engine-console: +``` +validator-engine-console -k client -p server.pub -a localhost:$(jq .control[].port <<< cat /var/ton-work/db/config.json) -c getconfig +``` +if you see a json output that means that validator-engine is up, now execute **last** command with a lite-client: +``` +lite-client -a localhost:$(jq .liteservers[].port <<< cat /var/ton-work/db/config.json) -p liteserver.pub -c last +``` +if you see the following output: +``` +conn ready +failed query: [Error : 652 : adnl query timeout] +cannot get server version and time (server too old?) +server version is too old (at least 1.1 with capabilities 1 required), some queries are unavailable +fatal error executing command-line queries, skipping the rest +``` +it means that the lite-server is up, but the node is not synchronized yet. +Once the node is synchronized, the output of **last** command will be similar to this one: + +``` +conn ready +server version is 1.1, capabilities 7 +server time is 1719306580 (delta 0) +last masterchain block is (-1,8000000000000000,20435927):47A517265B25CE4F2C8B3058D46343C070A4B31C5C37745390CE916C7D1CE1C5:279F9AA88C8146257E6C9B537905238C26E37DC2E627F2B6F1D558CB29A6EC82 +server time is 1719306580 (delta 0) +zerostate id set to -1:823F81F306FF02694F935CF5021548E3CE2B86B529812AF6A12148879E95A128:67E20AC184B9E039A62667ACC3F9C00F90F359A76738233379EFA47604980CE8 +``` +If you can't make it working, refer to the [Troubleshooting](#troubleshooting) section below. +### Use validator-engine-console +```docker exec -ti ton-node /bin/bash``` + +```validator-engine-console -k client -p server.pub -a 127.0.0.1:$(jq .control[].port <<< cat /var/ton-work/db/config.json)``` + +### Use lite-client +```docker exec -ti ton-node /bin/bash``` + +```lite-client -p liteserver.pub -a 127.0.0.1:$(jq .liteservers[].port <<< cat /var/ton-work/db/config.json)``` + +If you use lite-client outside the Docker container, copy the **liteserver.pub** from the container: + +```docker cp ton-node:/var/ton-work/db/liteserver.pub /your/path``` + +```lite-client -p /your/path/liteserver.pub -a :``` + +### Stop TON docker container +``` +docker stop ton-node +``` + +## Kubernetes +### Deploy in a quick way (without load balancer) +If the nodes within your kubernetes cluster have external IPs, +make sure that the PUBLIC_IP used for validator-engine matches the node's external IP. +If all Kubernetes nodes are inside DMZ - skip this section. + +#### Prepare +If you are using **flannel** network driver you can find node's IP this way: +```yaml +kubectl get nodes +kubectl describe node | grep public-ip +``` +for **calico** driver use: +```yaml +kubectl describe node | grep IPv4Address +``` +Double check if your Kubernetes node's external IP coincides with the host's IP address: +``` +kubectl run --image=ghcr.io/ton-blockchain/ton:latest validator-engine-pod --env="HOST_IP=1.1.1.1" --env="PUBLIC_IP=1.1.1.1" +kubectl exec -it validator-engine-pod -- curl -4 ifconfig.me +kubectl delete pod validator-engine-pod +``` +If IPs do not match, refer to the sections where load balancers are used. + +Now do the following: +* Add a label to this particular node. +* By this label our pod will know where to be deployed and what storage to use: +``` +kubectl label nodes node_type=ton-validator +``` +* Replace **** (and ports if needed) in file [ton-node-port.yaml](ton-node-port.yaml). +* Replace **** with a real path on host for Persistent Volume. +* If you change the ports, make sure you specify appropriate env vars in Pod section. +* If you want to use dynamic storage provisioning via volumeClaimTemplates, feel free to create own StorageClass. #### Install -```docker pull ghcr.io/ton-blockchain/ton:latest``` -#### Create volume -```docker volume create ton-db``` -#### Run -```docker run -d --name ton-node --mount source=ton-db,target=/var/ton-work/db --network host -e "PUBLIC_IP=" -e "CONSOLE_PORT=" -e "LITESERVER=true" -e "LITE_PORT=" -it ghcr.io/ton-blockchain/ton``` +```yaml +kubectl apply -f ton-node-port.yaml +``` + +this deployment uses host's network stack (**hostNetwork: true**) option and service of **NodePort** type. +Actually you can also use service of type **LoadBalancer**. +This way the service will get public IP assigned to the endpoints. + +#### Verify installation +See if service endpoints were correctly created: + +```yaml +kubectl get endpoints + +NAME ENDPOINTS +validator-engine-srv :30002,:30001,:30003 +``` +Check the logs for the deployment status: +```yaml +kubectl logs validator-engine-pod +``` +or go inside the pod and check if blockchain size is growing: +```yaml +kubectl exec --stdin --tty validator-engine-pod -- /bin/bash +du -h . +``` +### Deploy on-premises with metalLB load balancer + +Often Kubernetes cluster is located in DMZ, is behind corporate firewall and access is controlled via proxy configuration. +In this case we can't use host's network stack (**hostNetwork: true**) within a Pod and must manually proxy the access to the pod. + +A **LoadBalancer** service type automatically provisions an external load balancer (such as those provided by cloud providers like AWS, GCP, Azure) and assigns a public IP address to your service. In a non-cloud environment or in a DMZ setup, you need to manually configure the load balancer. + +If you are running your Kubernetes cluster on-premises or in an environment where an external load balancer is not automatically provided, you can use a load balancer implementation like MetalLB. + +#### Prepare +Select the node where persistent storage will be located for TON validator. +* Add a label to this particular node. By this label our pod will know where to be deployed: +``` +kubectl label nodes node_type=ton-validator +``` +* Replace **** (and ports if needed) in file [ton-metal-lb.yaml](ton-metal-lb.yaml). +* Replace **** with a real path on host for Persistent Volume. +* If you change the ports, make sure you specify appropriate env vars in Pod section. +* If you want to use dynamic storage provisioning via volumeClaimTemplates, feel free to create own StorageClass. + +* Install MetalLB +```yaml +kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml +``` + +* Configure MetalLB +Create a configuration map to define the IP address range that MetalLB can use for external load balancer services. +```yaml +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: first-pool + namespace: metallb-system +spec: + addresses: + - 10.244.1.0/24 <-- your CIDR address +``` +apply configuration +```yaml +kubectl apply -f metallb-config.yaml +``` +#### Install + +```yaml +kubectl apply -f ton-metal-lb.yaml +``` +We do not use Pod Node Affinity here, since the Pod will remember the host with local storage it was bound to. + +#### Verify installation +Assume your network CIDR (**--pod-network-cidr**) within cluster is 10.244.1.0/24, then you can compare the output with the one below: +```yaml +kubectl get service + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +kubernetes ClusterIP 443/TCP 28h +validator-engine-srv LoadBalancer 10.244.1.1 30001:30001/UDP,30002:30002/TCP,30003:30003/TCP 60m +``` +you can see that endpoints are pointing to metal-LB subnet: +``` +kubectl get endpoints + +NAME ENDPOINTS +kubernetes :6443 +validator-engine-srv 10.244.1.10:30002,10.244.1.10:30001,10.244.1.10:30003 +``` +and metal-LB itself operates with the right endpoint: +``` +kubectl describe service metallb-webhook-service -n metallb-system + +Name: metallb-webhook-service +Namespace: metallb-system +Selector: component=controller +Type: ClusterIP +IP: +IPs: +Port: 443/TCP +TargetPort: 9443/TCP +Endpoints: 10.244.2.3:9443 <-- CIDR +``` + +Use the commands from the previous chapter to see if node operates properly. + +### Deploy on AWS cloud (Amazon Web Services) + +#### Prepare +* AWS EKS is configured with worker nodes with selected add-ons: + * CoreDNS - Enable service discovery within your cluster. + * kube-proxy - Enable service networking within your cluster. + * Amazon VPC CNI - Enable pod networking within your cluster. +* Allocate Elastic IP. +* Replace **** with the newly created Elastic IP in [ton-aws.yaml](ton-aws.yaml) +* Replace **** with Elastic IP allocation ID (see in AWS console). +* Adjust StorageClass name. Make sure you are providing fast storage. + +#### Install + +```kubectl apply -f ton-aws.yaml``` + +#### Verify installation +Use instructions from the previous sections. + +### Deploy on GCP (Google Cloud Platform) + +#### Prepare +* Kubernetes cluster of type Standard (not Autopilot). +* Premium static IP address. +* Adjust firewall rules and security groups to allow ports 30001/udp, 30002/tcp and 30003/tcp (default ones). +* Replace **** (and ports if needed) in file [ton-gcp.yaml](ton-gcp.yaml). +* Adjust StorageClass name. Make sure you are providing fast storage. + +* Load Balancer will be created automatically according to Kubernetes service in yaml file. + +#### Install +```kubectl apply -f ton-gcp.yaml``` + +#### Verify installation +Use instructions from the previous sections. + +### Deploy on Ali Cloud + +#### Prepare +* AliCloud kubernetes cluster. +* Elastic IP. +* Replace **** with Elastic IP allocation ID (see in AliCloud console). +* Replace **** (and ports if needed) in file [ton-ali.yaml](ton-ali.yaml) with the elastic IP attached to your CLB. +* Adjust StorageClass name. Make sure you are providing fast storage. + +#### Install +```kubectl apply -f ton-ali.yaml``` + +As a result CLB (classic internal Load Balancer) will be created automatically with assigned external IP. + +#### Verify installation +Use instructions from the previous sections. + +## Troubleshooting +## Docker +### TON node cannot synchronize, constantly see messages [Error : 651 : no nodes] in the log + +Start the new container without starting validator-engine: + +``` +docker run -it -v /data/db:/var/ton-work/db \ +-e "HOST_IP=" \ +-e "PUBLIC_IP=" \ +-e "LITESERVER=true" \ +-p 43677:43677/udp \ +-p 43678:43678/tcp \ +-p 43679:43679/tcp \ +--entrypoint /bin/bash \ +ghcr.io/ton-blockchain/ton +``` +identify your PUBLIC_IP: +``` +curl -4 ifconfig.me +``` +compare if resulted IP coincides with your . +If it doesn't, exit container and launch it with the correct public IP. +Then open UDP port (inside the container) you plan to allocate for TON node using netcat utility: +``` +nc -ul 30001 +``` +and from any **other** linux machine check if you can reach this UDP port by sending a test message to that port: +``` +echo "test" | nc -u 30001 +``` +as a result inside the container you have to receive the "test" message. + +If you don't get the message inside the docker container, that means that either your firewall, LoadBalancer, NAT or proxy is blocking it. +Ask your system administrator for assistance. + +In the same way you can check if TCP port is available: + +Execute inside the container ```nc -l 30003``` and test connection from another server +```nc -vz 30003``` + +### Can't connect to lite-server +* check if lite-server was enabled on start by passing **"LITESERVER=true"** argument; +* check if TCP port (LITE_PORT) is available from the outside. From any other linux machine execute: + ``` +nc -vz +``` +### How to see what traffic is generated inside the TON docker container? +There is available a traffic monitoring utility inside the container, just execute: +``` +iptraf-ng +``` +Other tools like **tcpdump**, **nc**, **wget**, **curl**, **ifconfig**, **pv**, **plzip**, **jq** and **netstat** are also available. + +### How to build TON docker image from sources? +``` +git clone --recursive https://github.com/ton-blockchain/ton.git +cd ton +docker build . +``` + +## Kubernetes +### AWS +#### After installing AWS LB, load balancer is still not available (pending): +``` +kubectl get deployment -n kube-system aws-load-balancer-controller +``` +Solution: + +Try to install AWS LoadBalancer using ```Helm``` way. + +--- + +#### After installing AWS LB and running ton node, service shows error: + +```k describe service validator-engine-srv``` + +```log +Failed build model due to unable to resolve at least one subnet (0 match VPC and tags: [kubernetes.io/role/elb]) +``` +Solution: + +You haven't labeled the AWS subnets with the correct resource tags. + +* Public Subnets should be resource tagged with: kubernetes.io/role/elb: 1 +* Private Subnets should be tagged with: kubernetes.io/role/internal-elb: 1 +* Both private and public subnets should be tagged with: kubernetes.io/cluster/${your-cluster-name}: owned +* or if the subnets are also used by non-EKS resources kubernetes.io/cluster/${your-cluster-name}: shared + +So create tags for at least one subnet: +``` +kubernetes.io/role/elb: 1 +kubernetes.io/cluster/: owner +``` +--- +#### AWS Load Balancer works, but I still see ```[no nodes]``` in validator's log +It is required to add the security group for the EC2 instances to the load balancer along with the default security group. +It's a misleading that the default security group has "everything open." + +Add security group (default name is usually something like 'launch-wizard-1'). +And make sure you allow the ports you specified or default ports 30001/udp, 30002/tcp and 30003/tcp. + +You can also set inbound and outbound rules of new security group to allow ALL ports and for ALL protocols and for source CIDR 0.0.0.0/0 for testing purposes. + +--- + +#### Pending PersistentVolumeClaim ```Waiting for a volume to be created either by the external provisioner 'ebs.csi.aws.com' or manually by the system administrator.``` + +Solution: + +Configure Amazon EBS CSI driver for working PersistentVolumes in EKS. + +1. Enable IAM OIDC provider +``` +eksctl utils associate-iam-oidc-provider --region=us-west-2 --cluster=k8s-my --approve +``` + +2. Create Amazon EBS CSI driver IAM role +``` +eksctl create iamserviceaccount \ +--region us-west-2 \ +--name ebs-csi-controller-sa \ +--namespace kube-system \ +--cluster k8s-my \ +--attach-policy-arn arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy \ +--approve \ +--role-only \ +--role-name AmazonEKS_EBS_CSI_DriverRole +``` + +3. Add the Amazon EBS CSI add-on +```yaml +eksctl create addon --name aws-ebs-csi-driver --cluster k8s-my --service-account-role-arn arn:aws:iam::$(aws sts get-caller-identity --query Account --output text):role/AmazonEKS_EBS_CSI_DriverRole --force +``` +### Google Cloud +#### Load Balancer cannot obtain external IP (pending) + +``` +kubectl describe service validator-engine-srv + +Events: +Type Reason Age From Message + ---- ------ ---- ---- ------- +Warning LoadBalancerMixedProtocolNotSupported 7m8s g-cloudprovider LoadBalancers with multiple protocols are not supported. +Normal EnsuringLoadBalancer 113s (x7 over 7m8s) service-controller Ensuring load balancer +Warning SyncLoadBalancerFailed 113s (x7 over 7m8s) service-controller Error syncing load balancer: failed to ensure load balancer: mixed protocol is not supported for LoadBalancer +``` +Solution: + +Create static IP address of type Premium in GCP console and use it as a value for field ```loadBalancerIP``` in Kubernetes service. + +### Ali Cloud + +#### Validator logs always show +``` +Client got error [PosixError : Connection reset by peer : 104 : Error on [fd:45]] +[!NetworkManager][&ADNL_WARNING] [networkmanager]: received too small proxy packet of size 21 +``` +Solution: + +The node is sychnronizing, but very slow though. +Try to use Network Load Balancer (NLB) instead of default CLB. -If you don't need Liteserver, then remove -e "LITESERVER=true". - -#### Use -```docker exec -ti /bin/bash``` - -```./validator-engine-console -k client -p server.pub -a :``` - -IP:PORT is shown at start of container. - -#### Lite-client -To use lite-client you need to get liteserver.pub from container. - -```docker cp :/var/ton-work/db/liteserver.pub /your/path``` - -Then you can connect to it, but be sure you use right port, it's different from fullnode console port. - -```lite-client -a : -p liteserver.pub``` diff --git a/docker/control.template b/docker/control.template index 857bcebc..13b9b6b7 100644 --- a/docker/control.template +++ b/docker/control.template @@ -6,4 +6,4 @@ "permissions" : 15 } ] - } \ No newline at end of file + } diff --git a/docker/init.sh b/docker/init.sh index 1fe4f5e1..2d64690c 100644 --- a/docker/init.sh +++ b/docker/init.sh @@ -1,30 +1,93 @@ #!/usr/bin/env bash -# global config -if [ ! -z "$GCONFURL" ]; then +if [ ! -z "$TEST" ]; then + echo -e "Running simple validator-engine test..." + validator-engine -h + test $? -eq 2 || { echo "simple validator-engine test failed"; exit 1; } + exit 0; +fi + +# global config +if [ ! -z "$GLOBAL_CONFIG_URL" ]; then echo -e "\e[1;32m[+]\e[0m Downloading provided global config." - wget -q $GCONFURL -O /var/ton-work/db/ton-global.config + wget -q $GLOBAL_CONFIG_URL -O /var/ton-work/db/ton-global.config else - echo -e "\e[1;33m[=]\e[0m No global config provided, downloading default." + echo -e "\e[1;33m[=]\e[0m No global config provided, downloading mainnet default." wget -q https://api.tontech.io/ton/wallet-mainnet.autoconf.json -O /var/ton-work/db/ton-global.config fi +if [ -z "$VALIDATOR_PORT" ]; then + VALIDATOR_PORT=30001 + echo -e "\e[1;33m[=]\e[0m Using default VALIDATOR_PORT $VALIDATOR_PORT udp" +else + echo -e "\e[1;33m[=]\e[0m Using VALIDATOR_PORT $VALIDATOR_PORT udp" +fi + # Init local config with IP:PORT if [ ! -z "$PUBLIC_IP" ]; then - if [ -z "$CONSOLE_PORT" ]; then - CONSOLE_PORT="43678" - fi - echo -e "\e[1;32m[+]\e[0m Using provided IP: $PUBLIC_IP:$CONSOLE_PORT" - validator-engine -C /var/ton-work/db/ton-global.config --db /var/ton-work/db --ip "$PUBLIC_IP:$CONSOLE_PORT" + echo -e "\e[1;32m[+]\e[0m Using provided IP: $PUBLIC_IP:$VALIDATOR_PORT" else - echo -e "\e[1;31m[!]\e[0m No IP:PORT provided, exiting" + echo -e "\e[1;31m[!]\e[0m No PUBLIC_IP provided, exiting..." exit 1 fi +if [ ! -f "/var/ton-work/db/config.json" ]; then + echo -e "\e[1;32m[+]\e[0m Initializing validator-engine:" + echo validator-engine -C /var/ton-work/db/ton-global.config --db /var/ton-work/db --ip "$PUBLIC_IP:$VALIDATOR_PORT" + validator-engine -C /var/ton-work/db/ton-global.config --db /var/ton-work/db --ip "$PUBLIC_IP:$VALIDATOR_PORT" + test $? -eq 0 || { echo "Cannot initialize validator-engine"; exit 2; } +fi + +if [ ! -z "$DUMP_URL" ]; then + echo -e "\e[1;32m[+]\e[0m Using provided dump $DUMP_URL" + if [ ! -f "dump_downloaded" ]; then + echo -e "\e[1;32m[+]\e[0m Downloading dump..." + curl --retry 10 --retry-delay 30 -Ls $DUMP_URL | pv | plzip -d -n8 | tar -xC /var/ton-work/db + touch dump_downloaded + else + echo -e "\e[1;32m[+]\e[0m Dump has been already used." + fi +fi + +if [ -z "$STATE_TTL" ]; then + STATE_TTL=86400 + echo -e "\e[1;33m[=]\e[0m Using default STATE_TTL $STATE_TTL" +else + echo -e "\e[1;33m[=]\e[0m Using STATE_TTL $STATE_TTL" +fi + +if [ -z "$ARCHIVE_TTL" ]; then + ARCHIVE_TTL=86400 + echo -e "\e[1;33m[=]\e[0m Using default ARCHIVE_TTL $ARCHIVE_TTL" +else + echo -e "\e[1;33m[=]\e[0m Using ARCHIVE_TTL $ARCHIVE_TTL" +fi + +if [ -z "$THREADS" ]; then + THREADS=8 + echo -e "\e[1;33m[=]\e[0m Using default THREADS $THREADS" +else + echo -e "\e[1;33m[=]\e[0m Using THREADS $THREADS" +fi + +if [ -z "$VERBOSITY" ]; then + VERBOSITY=3 + echo -e "\e[1;33m[=]\e[0m Using default VERBOSITY $VERBOSITY" +else + echo -e "\e[1;33m[=]\e[0m Using VERBOSITY $VERBOSITY" +fi + +if [ -z "$CONSOLE_PORT" ]; then + CONSOLE_PORT=30002 + echo -e "\e[1;33m[=]\e[0m Using default CONSOLE_PORT $CONSOLE_PORT tcp" +else + echo -e "\e[1;33m[=]\e[0m Using CONSOLE_PORT $CONSOLE_PORT tcp" +fi + # Generating server certificate if [ -f "./server" ]; then echo -e "\e[1;33m[=]\e[0m Found existing server certificate, skipping" -else +else echo -e "\e[1;32m[+]\e[0m Generating and installing server certificate for remote control" read -r SERVER_ID1 SERVER_ID2 <<< $(generate-random-id -m keys -n server) echo "Server IDs: $SERVER_ID1 $SERVER_ID2" @@ -32,16 +95,16 @@ else fi # Generating client certificate -if [ -f "./client" ]; then +if [ -f "./client" ]; then echo -e "\e[1;33m[=]\e[0m Found existing client certificate, skipping" else read -r CLIENT_ID1 CLIENT_ID2 <<< $(generate-random-id -m keys -n client) echo -e "\e[1;32m[+]\e[0m Generated client private certificate $CLIENT_ID1 $CLIENT_ID2" echo -e "\e[1;32m[+]\e[0m Generated client public certificate" # Adding client permissions - sed -e "s/CONSOLE-PORT/\"$(printf "%q" $CONSOLE_PORT)\"/g" -e "s~SERVER-ID~\"$(printf "%q" $SERVER_ID2)\"~g" -e "s~CLIENT-ID~\"$(printf "%q" $CLIENT_ID2)\"~g" control.template > control.new - sed -e "s~\"control\"\ \:\ \[~$(printf "%q" $(cat control.new))~g" config.json > config.json.new - mv config.json.new config.json + sed -e "s/CONSOLE-PORT/\"$(printf "%q" $CONSOLE_PORT)\"/g" -e "s~SERVER-ID~\"$(printf "%q" $SERVER_ID2)\"~g" -e "s~CLIENT-ID~\"$(printf "%q" $CLIENT_ID2)\"~g" /var/ton-work/scripts/control.template > control.new + sed -e "s~\"control\"\ \:\ \[~$(printf "%q" $(cat control.new))~g" /var/ton-work/db/config.json > config.json.new + mv config.json.new /var/ton-work/db/config.json fi # Liteserver @@ -50,20 +113,25 @@ if [ -z "$LITESERVER" ]; then else if [ -f "./liteserver" ]; then echo -e "\e[1;33m[=]\e[0m Found existing liteserver certificate, skipping" - else + else echo -e "\e[1;32m[+]\e[0m Generating and installing liteserver certificate for remote control" read -r LITESERVER_ID1 LITESERVER_ID2 <<< $(generate-random-id -m keys -n liteserver) echo "Liteserver IDs: $LITESERVER_ID1 $LITESERVER_ID2" cp liteserver /var/ton-work/db/keyring/$LITESERVER_ID1 + if [ -z "$LITE_PORT" ]; then - LITE_PORT="43679" + LITE_PORT=30003 + echo -e "\e[1;33m[=]\e[0m Using default LITE_PORT $LITE_PORT tcp" + else + echo -e "\e[1;33m[=]\e[0m Using LITE_PORT $LITE_PORT tcp" fi + LITESERVERS=$(printf "%q" "\"liteservers\":[{\"id\":\"$LITESERVER_ID2\",\"port\":\"$LITE_PORT\"}") - sed -e "s~\"liteservers\"\ \:\ \[~$LITESERVERS~g" config.json > config.json.liteservers - mv config.json.liteservers config.json + sed -e "s~\"liteservers\"\ \:\ \[~$LITESERVERS~g" /var/ton-work/db/config.json > config.json.liteservers + mv config.json.liteservers /var/ton-work/db/config.json fi fi -echo -e "\e[1;32m[+]\e[0m Running validator-engine" - -exec validator-engine -c /var/ton-work/db/config.json -C /var/ton-work/db/ton-global.config --db /var/ton-work/db \ No newline at end of file +echo -e "\e[1;32m[+]\e[0m Starting validator-engine:" +echo validator-engine -c /var/ton-work/db/config.json -C /var/ton-work/db/ton-global.config --db /var/ton-work/db --state-ttl $STATE_TTL --archive-ttl $ARCHIVE_TTL --threads $THREADS --verbosity $VERBOSITY $CUSTOM_ARG +exec validator-engine -c /var/ton-work/db/config.json -C /var/ton-work/db/ton-global.config --db /var/ton-work/db --state-ttl $STATE_TTL --archive-ttl $ARCHIVE_TTL --threads $THREADS --verbosity $VERBOSITY $CUSTOM_ARG diff --git a/docker/ton-ali.yaml b/docker/ton-ali.yaml new file mode 100644 index 00000000..03ffbdb0 --- /dev/null +++ b/docker/ton-ali.yaml @@ -0,0 +1,121 @@ +apiVersion: "apps/v1" +kind: StatefulSet +metadata: + name: validator-engine-pod + labels: + name: validator-engine-pod +spec: + volumeClaimTemplates: + - metadata: + name: validator-engine-pvc + spec: + storageClassName: alicloud-disk-ssd + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 800Gi + serviceName: validator-engine-srv-headless + replicas: 1 + selector: + matchLabels: + name: validator-engine-pod + template: + metadata: + labels: + name: validator-engine-pod + spec: + containers: + - name: validator-engine-container + image: ghcr.io/neodix42/ton:latest + env: + - name: PUBLIC_IP + value: "" + - name: GLOBAL_CONFIG_URL + value: "https://api.tontech.io/ton/wallet-mainnet.autoconf.json" + - name: DUMP_URL + value: "https://dump.ton.org/dumps/latest.tar.lz" + - name: LITESERVER + value: "true" + - name: VALIDATOR_PORT + value: "30001" + - name: CONSOLE_PORT + value: "30002" + - name: LITE_PORT + value: "30003" + - name: STATE_TTL + value: "86400" + - name: ARCHIVE_TTL + value: "86400" + - name: THREADS + value: "8" + - name: VERBOSITY + value: "3" + ports: + - containerPort: 30001 + protocol: UDP + - containerPort: 30002 + protocol: TCP + - containerPort: 30003 + protocol: TCP + volumeMounts: + - mountPath: "/var/ton-work/db" + name: validator-engine-pvc + resources: + requests: + memory: "64Gi" + cpu: "16" + limits: + memory: "128Gi" + cpu: "32" +--- +kind: Service +apiVersion: v1 +metadata: + name: validator-engine-srv + annotations: + service.beta.kubernetes.io/alibaba-cloud-loadbalancer-eip-ids: "" + service.beta.kubernetes.io/alibaba-cloud-loadbalancer-address-type: "intranet" +spec: + type: LoadBalancer + externalTrafficPolicy: Local + ports: + - name: validator-udp + nodePort: 30001 + port: 30001 + targetPort: 30001 + protocol: UDP + - name: console-tcp + nodePort: 30002 + port: 30002 + targetPort: 30002 + protocol: TCP + - name: ls-tcp + nodePort: 30003 + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod +--- +apiVersion: v1 +kind: Service +metadata: + name: validator-engine-srv-headless +spec: + clusterIP: None + ports: + - name: validator-udp + port: 30001 + targetPort: 30001 + protocol: UDP + - name: console-tcp + port: 30002 + targetPort: 30002 + protocol: TCP + - name: ls-tcp + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod diff --git a/docker/ton-aws.yaml b/docker/ton-aws.yaml new file mode 100644 index 00000000..da16cbae --- /dev/null +++ b/docker/ton-aws.yaml @@ -0,0 +1,122 @@ +apiVersion: "apps/v1" +kind: StatefulSet +metadata: + name: validator-engine-pod + labels: + name: validator-engine-pod +spec: + volumeClaimTemplates: + - metadata: + name: validator-engine-pvc + spec: + storageClassName: gp2 + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 800Gi + serviceName: validator-engine-srv-headless + replicas: 1 + selector: + matchLabels: + name: validator-engine-pod + template: + metadata: + labels: + name: validator-engine-pod + spec: + containers: + - name: validator-engine-container + image: ghcr.io/neodix42/ton:latest + env: + - name: PUBLIC_IP + value: "" + - name: GLOBAL_CONFIG_URL + value: "https://api.tontech.io/ton/wallet-mainnet.autoconf.json" + - name: DUMP_URL + value: "https://dump.ton.org/dumps/latest.tar.lz" + - name: LITESERVER + value: "true" + - name: VALIDATOR_PORT + value: "30001" + - name: CONSOLE_PORT + value: "30002" + - name: LITE_PORT + value: "30003" + - name: STATE_TTL + value: "86400" + - name: ARCHIVE_TTL + value: "86400" + - name: THREADS + value: "8" + - name: VERBOSITY + value: "3" + ports: + - containerPort: 30001 + protocol: UDP + - containerPort: 30002 + protocol: TCP + - containerPort: 30003 + protocol: TCP + volumeMounts: + - mountPath: "/var/ton-work/db" + name: validator-engine-pvc + resources: + requests: + memory: "64Gi" + cpu: "16" + limits: + memory: "128Gi" + cpu: "32" +--- +kind: Service +apiVersion: v1 +metadata: + name: validator-engine-srv + annotations: + service.beta.kubernetes.io/aws-load-balancer-type: external + service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip + service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing + service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "" # Replace with your EIP allocation ID +spec: + type: LoadBalancer + ports: + - name: validator-udp + nodePort: 30001 + port: 30001 + targetPort: 30001 + protocol: UDP + - name: console-tcp + nodePort: 30002 + port: 30002 + targetPort: 30002 + protocol: TCP + - name: ls-tcp + nodePort: 30003 + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod +--- +apiVersion: v1 +kind: Service +metadata: + name: validator-engine-srv-headless +spec: + clusterIP: None + ports: + - name: validator-udp + port: 30001 + targetPort: 30001 + protocol: UDP + - name: console-tcp + port: 30002 + targetPort: 30002 + protocol: TCP + - name: ls-tcp + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod diff --git a/docker/ton-gcp.yaml b/docker/ton-gcp.yaml new file mode 100644 index 00000000..0ded5a79 --- /dev/null +++ b/docker/ton-gcp.yaml @@ -0,0 +1,134 @@ +apiVersion: "apps/v1" +kind: StatefulSet +metadata: + name: validator-engine-pod + labels: + name: validator-engine-pod +spec: + volumeClaimTemplates: + - metadata: + name: validator-engine-pvc + spec: + storageClassName: standard-rwo + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 800Gi + serviceName: validator-engine-srv-headless + replicas: 1 + selector: + matchLabels: + name: validator-engine-pod + template: + metadata: + labels: + name: validator-engine-pod + spec: + containers: + - name: validator-engine-container + image: ghcr.io/neodix42/ton:latest + env: + - name: PUBLIC_IP + value: "" + - name: GLOBAL_CONFIG_URL + value: "https://api.tontech.io/ton/wallet-mainnet.autoconf.json" + - name: DUMP_URL + value: "https://dump.ton.org/dumps/latest.tar.lz" + - name: LITESERVER + value: "true" + - name: VALIDATOR_PORT + value: "30001" + - name: CONSOLE_PORT + value: "30002" + - name: LITE_PORT + value: "30003" + - name: STATE_TTL + value: "86400" + - name: ARCHIVE_TTL + value: "86400" + - name: THREADS + value: "8" + - name: VERBOSITY + value: "3" + ports: + - containerPort: 30001 + protocol: UDP + - containerPort: 30002 + protocol: TCP + - containerPort: 30003 + protocol: TCP + volumeMounts: + - mountPath: "/var/ton-work/db" + name: validator-engine-pvc + resources: + requests: + memory: "64Gi" + cpu: "16" + limits: + memory: "128Gi" + cpu: "32" +--- +kind: Service +apiVersion: v1 +metadata: + name: validator-engine-srv +spec: + type: LoadBalancer + loadBalancerIP: + ports: + - port: 30001 + targetPort: 30001 + protocol: UDP + selector: + name: validator-engine-pod +--- +kind: Service +apiVersion: v1 +metadata: + name: validator-engine-console-srv +spec: + type: LoadBalancer + loadBalancerIP: + ports: + - port: 30002 + targetPort: 30002 + protocol: TCP + selector: + name: validator-engine-pod +--- +kind: Service +apiVersion: v1 +metadata: + name: lite-server-srv +spec: + type: LoadBalancer + loadBalancerIP: + ports: + - port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod +--- +apiVersion: v1 +kind: Service +metadata: + name: validator-engine-srv-headless +spec: + clusterIP: None + ports: + - name: validator-udp + port: 30001 + targetPort: 30001 + protocol: UDP + - name: console-tcp + port: 30002 + targetPort: 30002 + protocol: TCP + - name: ls-tcp + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod diff --git a/docker/ton-metal-lb.yaml b/docker/ton-metal-lb.yaml new file mode 100644 index 00000000..ceaf3a7c --- /dev/null +++ b/docker/ton-metal-lb.yaml @@ -0,0 +1,118 @@ +apiVersion: v1 +kind: Pod +metadata: + name: validator-engine-pod + labels: + name: validator-engine-pod +spec: + volumes: + - name: validator-engine-pv + persistentVolumeClaim: + claimName: validator-engine-pvc + containers: + - name: validator-engine-container + image: ghcr.io/neodix42/ton:latest + env: + - name: PUBLIC_IP + value: "" + - name: GLOBAL_CONFIG_URL + value: "https://api.tontech.io/ton/wallet-mainnet.autoconf.json" + - name: DUMP_URL + value: "https://dump.ton.org/dumps/latest.tar.lz" + - name: LITESERVER + value: "true" + - name: VALIDATOR_PORT + value: "30001" + - name: CONSOLE_PORT + value: "30002" + - name: LITE_PORT + value: "30003" + - name: STATE_TTL + value: "86400" + - name: ARCHIVE_TTL + value: "86400" + - name: THREADS + value: "8" + - name: VERBOSITY + value: "3" + volumeMounts: + - mountPath: "/var/ton-work/db" + name: validator-engine-pv + resources: + requests: + memory: "64Gi" + cpu: "16" + limits: + memory: "128Gi" + cpu: "32" +--- +kind: Service +apiVersion: v1 +metadata: + name: validator-engine-srv + annotations: + metallb.universe.tf/address-pool: first-pool +spec: + type: LoadBalancer + ports: + - name: validator-engine-public-udp-port + nodePort: 30001 + port: 30001 + targetPort: 30001 + protocol: UDP + - name: validator-console-tcp-port + nodePort: 30002 + port: 30002 + targetPort: 30002 + protocol: TCP + - name: lite-server-tcp-port + nodePort: 30003 + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: local-storage +provisioner: kubernetes.io/no-provisioner +volumeBindingMode: WaitForFirstConsumer +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: validator-engine-pv + labels: + type: local +spec: + storageClassName: local-storage + capacity: + storage: 800Gi + accessModes: + - ReadWriteOnce + - ReadOnlyMany + persistentVolumeReclaimPolicy: Retain + local: + path: + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: node_type + operator: In + values: + - ton-validator +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: validator-engine-pvc +spec: + storageClassName: local-storage + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 800Gi diff --git a/docker/ton-node-port.yaml b/docker/ton-node-port.yaml new file mode 100644 index 00000000..ec594031 --- /dev/null +++ b/docker/ton-node-port.yaml @@ -0,0 +1,126 @@ +apiVersion: v1 +kind: Pod +metadata: + name: validator-engine-pod + labels: + name: validator-engine-pod +spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node_type + operator: In + values: + - ton-validator + hostNetwork: true + volumes: + - name: validator-engine-pv + persistentVolumeClaim: + claimName: validator-engine-pvc + containers: + - name: validator-engine-container + image: ghcr.io/neodix42/ton:latest + env: + - name: PUBLIC_IP + value: "" + - name: GLOBAL_CONFIG_URL + value: "https://api.tontech.io/ton/wallet-mainnet.autoconf.json" + - name: DUMP_URL + value: "https://dump.ton.org/dumps/latest.tar.lz" + - name: LITESERVER + value: "true" + - name: VALIDATOR_PORT + value: "30001" + - name: CONSOLE_PORT + value: "30002" + - name: LITE_PORT + value: "30003" + - name: STATE_TTL + value: "86400" + - name: ARCHIVE_TTL + value: "86400" + - name: THREADS + value: "8" + - name: VERBOSITY + value: "3" + volumeMounts: + - mountPath: "/var/ton-work/db" + name: validator-engine-pv + resources: + requests: + memory: "64Gi" + cpu: "16" + limits: + memory: "128Gi" + cpu: "32" +--- +kind: Service +apiVersion: v1 +metadata: + name: validator-engine-srv +spec: + type: NodePort + ports: + - name: validator-engine-public-udp-port + nodePort: 30001 + port: 30001 + targetPort: 30001 + protocol: UDP + - name: validator-console-tcp-port + nodePort: 30002 + port: 30002 + targetPort: 30002 + protocol: TCP + - name: lite-server-tcp-port + nodePort: 30003 + port: 30003 + targetPort: 30003 + protocol: TCP + selector: + name: validator-engine-pod +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: local-storage +provisioner: kubernetes.io/no-provisioner +volumeBindingMode: WaitForFirstConsumer +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + name: validator-engine-pv + labels: + type: local +spec: + storageClassName: local-storage + capacity: + storage: 800Gi + accessModes: + - ReadWriteOnce + - ReadOnlyMany + persistentVolumeReclaimPolicy: Retain + local: + path: + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: node_type + operator: In + values: + - ton-validator +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: validator-engine-pvc +spec: + storageClassName: local-storage + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 800Gi From 5380e6fb8d69f55876188df2d454753bad1b202b Mon Sep 17 00:00:00 2001 From: neodix42 Date: Wed, 10 Jul 2024 14:00:18 +0200 Subject: [PATCH 5/5] add test-emulator (#1048) * add github action for macOS 14 (arm64, M1) * add github action (portable) for macOS 14 (arm64, M1) * rename macOS arm64 output artifact * Update libsodium on windows * Compile libsodium * Update build-windows.bat * use upgraded libsodium 1.0.20; use compiled static libsodium for Windows instead of precompiled; * revert libsodium 1.0.20; use compiled static libsodium for Windows instead of precompiled; * use upgraded libsodium 1.0.20; use compiled static libsodium for Windows instead of precompiled; * fix libsodium version 1.0.19; use compiled static libsodium for Windows instead of precompiled; * try 1.0.20 libsodium precompiled on github * try 1.0.18 libsodium precompiled on github * try windows build on win server 2019 * and use PlatformToolset=v142 * use cmake -G "Visual Studio 16 2019" * fix path to msvc 2019 on github * separate github windows build on win server 2019 and build on win server 2022 * Update assembly/native/build-windows-2019.bat add retry mechanism Co-authored-by: Dr. Awesome Doge * add test-emulator; disable test groovy pipeline * trigger all gh actions * fix win build * call test-emulator * fix test-emulator --------- Co-authored-by: neodiX Co-authored-by: Dr. Awesome Doge Co-authored-by: ms --- CMakeLists.txt | 1 + assembly/cicd/jenkins/test-builds.groovy | 21 ++-- assembly/native/build-macos-portable.sh | 2 +- assembly/native/build-macos-shared.sh | 2 +- assembly/native/build-ubuntu-portable.sh | 2 +- assembly/native/build-ubuntu-shared.sh | 2 +- assembly/native/build-windows-2019.bat | 2 +- assembly/native/build-windows.bat | 2 +- emulator/test/emulator-tests.cpp | 149 ++++++++++++++++++++++- 9 files changed, 164 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c7ee914..b92ff6f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -573,6 +573,7 @@ add_test(test-cells test-cells ${TEST_OPTIONS}) add_test(test-smartcont test-smartcont) add_test(test-net test-net) add_test(test-actors test-tdactor) +add_test(test-emulator test-emulator) #BEGIN tonlib add_test(test-tdutils test-tdutils) diff --git a/assembly/cicd/jenkins/test-builds.groovy b/assembly/cicd/jenkins/test-builds.groovy index a959d75a..47ce52e5 100644 --- a/assembly/cicd/jenkins/test-builds.groovy +++ b/assembly/cicd/jenkins/test-builds.groovy @@ -1,4 +1,5 @@ pipeline { + agent none stages { stage('Run Builds') { @@ -12,7 +13,7 @@ pipeline { sh ''' cp assembly/native/build-ubuntu-shared.sh . chmod +x build-ubuntu-shared.sh - ./build-ubuntu-shared.sh -t -a + ./build-ubuntu-shared.sh -a ''' sh ''' cd artifacts @@ -31,7 +32,7 @@ pipeline { sh ''' cp assembly/nix/build-linux-x86-64-nix.sh . chmod +x build-linux-x86-64-nix.sh - ./build-linux-x86-64-nix.sh -t + ./build-linux-x86-64-nix.sh ''' sh ''' cd artifacts @@ -50,7 +51,7 @@ pipeline { sh ''' cp assembly/native/build-ubuntu-shared.sh . chmod +x build-ubuntu-shared.sh - ./build-ubuntu-shared.sh -t -a + ./build-ubuntu-shared.sh -a ''' sh ''' cd artifacts @@ -69,7 +70,7 @@ pipeline { sh ''' cp assembly/nix/build-linux-arm64-nix.sh . chmod +x build-linux-arm64-nix.sh - ./build-linux-arm64-nix.sh -t + ./build-linux-arm64-nix.sh ''' sh ''' cd artifacts @@ -88,7 +89,7 @@ pipeline { sh ''' cp assembly/native/build-macos-shared.sh . chmod +x build-macos-shared.sh - ./build-macos-shared.sh -t -a + ./build-macos-shared.sh -a ''' sh ''' cd artifacts @@ -107,7 +108,7 @@ pipeline { sh ''' cp assembly/nix/build-macos-nix.sh . chmod +x build-macos-nix.sh - ./build-macos-nix.sh -t + ./build-macos-nix.sh ''' sh ''' cd artifacts @@ -126,7 +127,7 @@ pipeline { sh ''' cp assembly/native/build-macos-shared.sh . chmod +x build-macos-shared.sh - ./build-macos-shared.sh -t -a + ./build-macos-shared.sh -a ''' sh ''' cd artifacts @@ -145,7 +146,7 @@ pipeline { sh ''' cp assembly/nix/build-macos-nix.sh . chmod +x build-macos-nix.sh - ./build-macos-nix.sh -t + ./build-macos-nix.sh ''' sh ''' cd artifacts @@ -164,7 +165,7 @@ pipeline { sh ''' cp assembly/native/build-macos-shared.sh . chmod +x build-macos-shared.sh - ./build-macos-shared.sh -t -a + ./build-macos-shared.sh -a ''' sh ''' cd artifacts @@ -182,7 +183,7 @@ pipeline { timeout(time: 180, unit: 'MINUTES') { bat ''' copy assembly\\native\\build-windows.bat . - build-windows.bat -t + build-windows.bat ''' bat ''' cd artifacts diff --git a/assembly/native/build-macos-portable.sh b/assembly/native/build-macos-portable.sh index 32a09e45..b296d339 100644 --- a/assembly/native/build-macos-portable.sh +++ b/assembly/native/build-macos-portable.sh @@ -158,7 +158,7 @@ if [ "$with_tests" = true ]; then http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc emulator \ test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont \ test-net test-tdactor test-tdutils test-tonlib-offline test-adnl test-dht test-rldp \ - test-rldp2 test-catchain test-fec test-tddb test-db test-validator-session-state + test-rldp2 test-catchain test-fec test-tddb test-db test-validator-session-state test-emulator test $? -eq 0 || { echo "Can't compile ton"; exit 1; } else ninja storage-daemon storage-daemon-cli blockchain-explorer \ diff --git a/assembly/native/build-macos-shared.sh b/assembly/native/build-macos-shared.sh index 0f16eeda..7574f481 100644 --- a/assembly/native/build-macos-shared.sh +++ b/assembly/native/build-macos-shared.sh @@ -86,7 +86,7 @@ if [ "$with_tests" = true ]; then http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork tlbc emulator \ test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont \ test-net test-tdactor test-tdutils test-tonlib-offline test-adnl test-dht test-rldp \ - test-rldp2 test-catchain test-fec test-tddb test-db test-validator-session-state + test-rldp2 test-catchain test-fec test-tddb test-db test-validator-session-state test-emulator test $? -eq 0 || { echo "Can't compile ton"; exit 1; } else ninja storage-daemon storage-daemon-cli blockchain-explorer \ diff --git a/assembly/native/build-ubuntu-portable.sh b/assembly/native/build-ubuntu-portable.sh index a3a11f1b..b5a16762 100644 --- a/assembly/native/build-ubuntu-portable.sh +++ b/assembly/native/build-ubuntu-portable.sh @@ -150,7 +150,7 @@ ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \ adnl-proxy create-state emulator test-ed25519 test-ed25519-crypto test-bigint \ test-vm test-fift test-cells test-smartcont test-net test-tdactor test-tdutils \ test-tonlib-offline test-adnl test-dht test-rldp test-rldp2 test-catchain \ - test-fec test-tddb test-db test-validator-session-state + test-fec test-tddb test-db test-validator-session-state test-emulator test $? -eq 0 || { echo "Can't compile ton"; exit 1; } else ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \ diff --git a/assembly/native/build-ubuntu-shared.sh b/assembly/native/build-ubuntu-shared.sh index ec868ecd..4ce86d81 100644 --- a/assembly/native/build-ubuntu-shared.sh +++ b/assembly/native/build-ubuntu-shared.sh @@ -58,7 +58,7 @@ ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \ adnl-proxy create-state emulator test-ed25519 test-ed25519-crypto test-bigint \ test-vm test-fift test-cells test-smartcont test-net test-tdactor test-tdutils \ test-tonlib-offline test-adnl test-dht test-rldp test-rldp2 test-catchain \ - test-fec test-tddb test-db test-validator-session-state + test-fec test-tddb test-db test-validator-session-state test-emulator test $? -eq 0 || { echo "Can't compile ton"; exit 1; } else ninja storage-daemon storage-daemon-cli fift func tonlib tonlibjson tonlib-cli \ diff --git a/assembly/native/build-windows-2019.bat b/assembly/native/build-windows-2019.bat index c4722d54..b528b05a 100644 --- a/assembly/native/build-windows-2019.bat +++ b/assembly/native/build-windows-2019.bat @@ -160,7 +160,7 @@ tonlib-cli validator-engine lite-client pow-miner validator-engine-console gener json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator ^ test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont test-net ^ test-tdactor test-tdutils test-tonlib-offline test-adnl test-dht test-rldp test-rldp2 test-catchain ^ -test-fec test-tddb test-db test-validator-session-state +test-fec test-tddb test-db test-validator-session-state test-emulator IF %errorlevel% NEQ 0 ( echo Can't compile TON exit /b %errorlevel% diff --git a/assembly/native/build-windows.bat b/assembly/native/build-windows.bat index 9b7322e1..a9871aa9 100644 --- a/assembly/native/build-windows.bat +++ b/assembly/native/build-windows.bat @@ -161,7 +161,7 @@ tonlib-cli validator-engine lite-client pow-miner validator-engine-console gener json2tlo dht-server http-proxy rldp-http-proxy adnl-proxy create-state create-hardfork emulator ^ test-ed25519 test-ed25519-crypto test-bigint test-vm test-fift test-cells test-smartcont test-net ^ test-tdactor test-tdutils test-tonlib-offline test-adnl test-dht test-rldp test-rldp2 test-catchain ^ -test-fec test-tddb test-db test-validator-session-state +test-fec test-tddb test-db test-validator-session-state test-emulator IF %errorlevel% NEQ 0 ( echo Can't compile TON exit /b %errorlevel% diff --git a/emulator/test/emulator-tests.cpp b/emulator/test/emulator-tests.cpp index 310fe2f1..0780be61 100644 --- a/emulator/test/emulator-tests.cpp +++ b/emulator/test/emulator-tests.cpp @@ -16,7 +16,150 @@ #include "emulator/emulator-extern.h" // testnet config as of 27.06.24 -const char *config_boc = "te6cckICAl8AAQAANecAAAIBIAABAAICAtgAAwAEAgL1AA0ADgIBIAAFAAYCAUgCPgI/AgEgAAcACAIBSAAJAAoCASAAHgAfAgEgAGUAZgIBSAALAAwCAWoA0gDTAQFIAJIBAUgAsgEDpDMADwIBbgAQABEAQDPAueB1cC0DTaIjG28I/scJsoxoIScEE9LNtuiQoYa2AgOuIAASABMBA7LwABoBASAAFAEBIAAYAQHAABUCAWoAFgAXAIm/VzGVo387z8N7BhdH91LBHMMhBLu7nv21jwo9wtTSXQIBABvI0aFLnw2QbZgjMPCLRdtRHxhUyinQudg6sdiohIwgwCAAQ79oJ47o6vzJDO5wV60LQESEyBcI3zuSSKtFQIlzhk86tAMBg+mbgbrrZVY0qEWL8HxF+gYzy9t5jLO50+QkJ2DWbWFHj0Qaw5TPlNDYOnY0A2VNeAnS9bZ98W8X7FTvgVqStlmABAAZAIOgCYiOTH0TnIIa0oSKjkT3CsgHNUU1Iy/5E472ortANeCAAAAAAAAAAAAAAAAROiXXYZuWf8AAi5Oy+xV/i+2JL9ABA6BgABsCASAAHAAdAFur4AAAAAAHGv1JjQAAEeDul1fav9HZ8+939/IsLGZ46E5h3qjR13yIrB8mcfbBAFur/////8AHGv1JjQAAEeDul1fav9HZ8+939/IsLGZ46E5h3qjR13yIrB8mcfbBAgEgACAAIQIBIAAzADQCASAAIgAjAgEgACkAKgIBIAAkACUBAUgAKAEBIAAmAQEgACcAQFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAEAzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMwBAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQECASAAKwAsAQFYAC8BASAALQEBIAAuAEDv5x0Thgr6pq6ur2NvkWhIf4DxAxsL+Nk5rknT6n99oABTAf//////////////////////////////////////////gAAAAIAAAAFAAQHAADACASAAMQAyABW+AAADvLNnDcFVUAAVv////7y9GpSiABACASAANQA2AgEgADcAOAIBIABCAEMCASAATgBPAgEgADkAOgIBIAA+AD8BASAAOwEBIAA9AQHAADwAt9BTLudOzwABAnAA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"; +const char *config_boc = "te6cckICAl8AAQAANecAAAIBIAABAAICAtgAAwAEAgL1AA0ADgIBIAAFAAYCAUgCPgI/AgEgAAcACAIBSAAJAAoCASAAHgAfAgEgAGUAZgIBSAALAAwCAWoA0gDTAQFI" + "AJIBAUgAsgEDpDMADwIBbgAQABEAQDPAueB1cC0DTaIjG28I/scJsoxoIScEE9LNtuiQoYa2AgOuIAASABMBA7LwABoBASAAFAEBIAAYAQHAABUCAWoAFgAXAIm/VzGV" + "o387z8N7BhdH91LBHMMhBLu7nv21jwo9wtTSXQIBABvI0aFLnw2QbZgjMPCLRdtRHxhUyinQudg6sdiohIwgwCAAQ79oJ47o6vzJDO5wV60LQESEyBcI3zuSSKtFQIlz" + "hk86tAMBg+mbgbrrZVY0qEWL8HxF+gYzy9t5jLO50+QkJ2DWbWFHj0Qaw5TPlNDYOnY0A2VNeAnS9bZ98W8X7FTvgVqStlmABAAZAIOgCYiOTH0TnIIa0oSKjkT3CsgH" + "NUU1Iy/5E472ortANeCAAAAAAAAAAAAAAAAROiXXYZuWf8AAi5Oy+xV/i+2JL9ABA6BgABsCASAAHAAdAFur4AAAAAAHGv1JjQAAEeDul1fav9HZ8+939/IsLGZ46E5h" + "3qjR13yIrB8mcfbBAFur/////8AHGv1JjQAAEeDul1fav9HZ8+939/IsLGZ46E5h3qjR13yIrB8mcfbBAgEgACAAIQIBIAAzADQCASAAIgAjAgEgACkAKgIBIAAkACUB" + "AUgAKAEBIAAmAQEgACcAQFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVAEAzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMwBAAQEBAQEBAQEBAQEB" + "AQEBAQEBAQEBAQEBAQEBAQEBAQECASAAKwAsAQFYAC8BASAALQEBIAAuAEDv5x0Thgr6pq6ur2NvkWhIf4DxAxsL+Nk5rknT6n99oABTAf//////////////////////" + "////////////////////gAAAAIAAAAFAAQHAADACASAAMQAyABW+AAADvLNnDcFVUAAVv////7y9GpSiABACASAANQA2AgEgADcAOAIBIABCAEMCASAATgBPAgEgADkA" + "OgIBIAA+AD8BASAAOwEBIAA9AQHAADwAt9BTLudOzwABAnAAKtiftocOhhpk4QsHt8jHSWwV/O7nxvFyZKUf75zoqiN3Bfb/JZk7D9mvTw7EDHU5BlaNBz2ml2s54kRz" + "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"; constexpr td::int64 Ton = 1000000000; @@ -117,7 +260,7 @@ TEST(Emulator, wallet_int_and_ext_msg) { CHECK(transaction_boc.is_ok()); auto trans_cell = vm::std_boc_deserialize(transaction_boc.move_as_ok()); CHECK(trans_cell.is_ok()); - auto trans_hash = trans_cell.ok()->get_hash().bits(); + td::Bits256 trans_hash = trans_cell.ok()->get_hash().bits(); block::gen::Transaction::Record trans; block::gen::TransactionDescr::Record_trans_ord trans_descr; CHECK(tlb::unpack_cell(trans_cell.move_as_ok(), trans) && tlb::unpack_cell(trans.description, trans_descr)); @@ -172,7 +315,7 @@ TEST(Emulator, wallet_int_and_ext_msg) { CHECK(ext_transaction_boc.is_ok()); auto ext_trans_cell = vm::std_boc_deserialize(ext_transaction_boc.move_as_ok()); CHECK(ext_trans_cell.is_ok()); - auto ext_trans_hash = ext_trans_cell.ok()->get_hash().bits(); + td::Bits256 ext_trans_hash = ext_trans_cell.ok()->get_hash().bits(); block::gen::Transaction::Record ext_trans; block::gen::TransactionDescr::Record_trans_ord ext_trans_descr; CHECK(tlb::unpack_cell(ext_trans_cell.move_as_ok(), ext_trans) && tlb::unpack_cell(ext_trans.description, ext_trans_descr));