diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 50654cba8..d2e59ba38 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -68,6 +68,7 @@ struct NewOutMsg { Ref trans; unsigned msg_idx; td::optional metadata; + td::Ref msg_env_from_dispatch_queue; // Not null if from dispatch queue; in this case lt is deferred_lt NewOutMsg(ton::LogicalTime _lt, Ref _msg, Ref _trans, unsigned _msg_idx) : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)), msg_idx(_msg_idx) { } diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index 7877f1a58..3a6d22405 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -193,6 +193,7 @@ class Collator final : public td::actor::Actor { std::priority_queue, std::greater> new_msgs; std::pair last_proc_int_msg_, first_unproc_int_msg_; 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; bool have_out_msg_queue_size_in_state_ = false; std::unique_ptr ihr_pending; @@ -208,7 +209,7 @@ class Collator final : public td::actor::Actor { std::unique_ptr dispatch_queue_; std::map sender_generated_messages_count_; unsigned dispatch_queue_ops_{0}; - std::map last_enqueued_deferred_lt_; + std::map last_deferred_lt_; bool have_unprocessed_account_dispatch_queue_ = true; bool msg_metadata_enabled_ = false; @@ -301,8 +302,8 @@ class Collator final : public td::actor::Actor { int process_external_message(Ref msg); bool process_dispatch_queue(); bool process_deferred_message(Ref enq_msg, StdSmcAddress src_addr, LogicalTime lt); - bool enqueue_message(block::NewOutMsg msg, td::RefInt256 fwd_fees_remaining, ton::LogicalTime enqueued_lt, - StdSmcAddress src_addr, bool defer = false); + bool enqueue_message(block::NewOutMsg msg, td::RefInt256 fwd_fees_remaining, StdSmcAddress src_addr, + bool defer = false); bool enqueue_transit_message(Ref msg, Ref old_msg_env, ton::AccountIdPrefixFull prev_prefix, ton::AccountIdPrefixFull cur_prefix, ton::AccountIdPrefixFull dest_prefix, td::RefInt256 fwd_fee_remaining, td::optional msg_metadata, diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 5b47ccf87..2df8c86a1 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -1,7 +1,7 @@ /* - This file is part of TON Blockchain Library. + This file is part of TON Blockchain Library. - TON Blockchain Library is free software: you can redistribute it and/or modify + TON Blockchain Library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. @@ -2100,6 +2100,11 @@ bool Collator::do_collate() { if (!init_value_create()) { return fatal_error("cannot compute the value to be created / minted / recovered"); } + // 2-. take messages from dispatch queue + LOG(INFO) << "process dispatch queue"; + if (!process_dispatch_queue()) { + return fatal_error("cannot process dispatch queue"); + } // 2. tick transactions LOG(INFO) << "create tick transactions"; if (!create_ticktock_transactions(2)) { @@ -2114,11 +2119,6 @@ bool Collator::do_collate() { // TODO: implement merge prepare/install transactions for "large" smart contracts // ... } - // 4-. take messages from dispatch queue - LOG(INFO) << "process dispatch queue"; - if (!process_dispatch_queue()) { - return fatal_error("cannot process dispatch queue"); - } // 4. import inbound internal messages, process or transit LOG(INFO) << "process inbound internal messages"; if (!process_inbound_internal_messages()) { @@ -2654,8 +2654,8 @@ bool Collator::create_ticktock_transaction(const ton::StdSmcAddress& smc_addr, t return true; } req_start_lt = std::max(req_start_lt, start_lt + 1); - auto it = last_enqueued_deferred_lt_.find(acc->addr); - if (it != last_enqueued_deferred_lt_.end()) { + auto it = last_deferred_lt_.find(acc->addr); + if (it != last_deferred_lt_.end()) { req_start_lt = std::max(req_start_lt, it->second + 1); } if (acc->last_trans_end_lt_ >= start_lt && acc->transactions.empty()) { @@ -2755,10 +2755,13 @@ Ref Collator::create_ordinary_transaction(Ref msg_root, block::Account* acc = acc_res.move_as_ok(); assert(acc); - LogicalTime after_lt = last_proc_int_msg_.first; - auto it = last_enqueued_deferred_lt_.find(acc->addr); - if (it != last_enqueued_deferred_lt_.end()) { - after_lt = std::max(after_lt, it->second); + LogicalTime after_lt = 0; + if (external) { + after_lt = last_proc_int_msg_.first; + } + auto it = last_deferred_lt_.find(acc->addr); + if (it != last_deferred_lt_.end()) { + after_lt = it->second; } auto res = impl_create_ordinary_transaction(msg_root, acc, now_, start_lt, &storage_phase_cfg_, &compute_phase_cfg_, &action_phase_cfg_, external, after_lt); @@ -2828,13 +2831,12 @@ td::Result> Collator::impl_crea << ":" << acc->addr.to_hex() << " is too large"); } auto trans_min_lt = lt; - if (external) { - // transactions processing external messages must have lt larger than all processed internal messages - trans_min_lt = std::max(trans_min_lt, after_lt); - } + // transactions processing external messages must have lt larger than all processed internal messages + // if account has deferred message processed in this block, the next transaction should have lt > deferred_lt + trans_min_lt = std::max(trans_min_lt, after_lt); - std::unique_ptr trans = - std::make_unique(*acc, block::transaction::Transaction::tr_ord, trans_min_lt + 1, utime, msg_root); + std::unique_ptr trans = std::make_unique( + *acc, block::transaction::Transaction::tr_ord, trans_min_lt + 1, utime, msg_root); bool ihr_delivered = false; // FIXME if (!trans->unpack_input_msg(ihr_delivered, action_phase_cfg)) { if (external) { @@ -2998,6 +3000,7 @@ bool Collator::is_our_address(const ton::StdSmcAddress& addr) const { * -1 - error occured. */ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, Ref* is_special) { + bool from_dispatch_queue = msg.msg_env_from_dispatch_queue.not_null(); Ref src, dest; bool enqueue, external; auto cs = load_cell_slice(msg.msg); @@ -3009,7 +3012,7 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R if (!tlb::unpack(cs, info)) { return -1; } - CHECK(info.created_lt == msg.lt && info.created_at == now_); + CHECK(info.created_lt == msg.lt && info.created_at == now_ && !from_dispatch_queue); src = std::move(info.src); enqueue = external = true; break; @@ -3019,7 +3022,7 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R if (!tlb::unpack(cs, info)) { return -1; } - CHECK(info.created_lt == msg.lt && info.created_at == now_); + CHECK(from_dispatch_queue || (info.created_lt == msg.lt && info.created_at == now_)); src = std::move(info.src); dest = std::move(info.dest); fwd_fees = block::tlb::t_Grams.as_integer(info.fwd_fee); @@ -3046,22 +3049,41 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R WorkchainId src_wc; StdSmcAddress src_addr; - CHECK(block::tlb::t_MsgAddressInt.extract_std_address(std::move(src), src_wc, src_addr)); + CHECK(block::tlb::t_MsgAddressInt.extract_std_address(src, src_wc, src_addr)); CHECK(src_wc == workchain()); bool is_special_account = is_masterchain() && config_->is_special_smartcontract(src_addr); bool defer = false; - if (deferring_messages_enabled_ && !is_special && !is_special_account && msg.msg_idx != 0) { - if (++sender_generated_messages_count_[src_addr] >= DEFER_MESSAGES_AFTER) { + 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) { + defer = true; + } + } + if (dispatch_queue_->lookup(src_addr).not_null() || unprocessed_deferred_messages_.count(src_addr)) { defer = true; } - } - if (dispatch_queue_->lookup(src_addr).not_null()) { - defer = true; + } else { + auto &x = unprocessed_deferred_messages_[src_addr]; + CHECK(x > 0); + if (--x == 0) { + unprocessed_deferred_messages_.erase(src_addr); + } } if (enqueue || defer) { - auto lt = msg.lt; - bool ok = enqueue_message(std::move(msg), std::move(fwd_fees), lt, src_addr, defer); + bool ok; + if (from_dispatch_queue) { + auto msg_env = msg.msg_env_from_dispatch_queue; + block::tlb::MsgEnvelope::Record_std env; + CHECK(block::tlb::unpack_cell(msg_env, env)); + auto src_prefix = block::tlb::MsgAddressInt::get_prefix(src); + auto dest_prefix = block::tlb::MsgAddressInt::get_prefix(dest); + CHECK(env.deferred_lt && env.deferred_lt.value() == msg.lt); + ok = enqueue_transit_message(std::move(msg.msg), std::move(msg_env), src_prefix, src_prefix, dest_prefix, + std::move(env.fwd_fee_remaining), std::move(env.metadata), msg.lt); + } else { + ok = enqueue_message(std::move(msg), std::move(fwd_fees), src_addr, defer); + } return ok ? 0 : -1; } // process message by a transaction in this block: @@ -3092,10 +3114,21 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R } // 3. create InMsg, referring to this MsgEnvelope and this Transaction vm::CellBuilder cb; - CHECK(cb.store_long_bool(3, 3) // msg_import_imm$011 - && cb.store_ref_bool(msg_env) // in_msg:^MsgEnvelope - && cb.store_ref_bool(trans_root) // transaction:^Transaction - && block::tlb::t_Grams.store_integer_ref(cb, fwd_fees)); // fwd_fee:Grams + if (from_dispatch_queue) { + auto msg_env = msg.msg_env_from_dispatch_queue; + block::tlb::MsgEnvelope::Record_std env; + CHECK(block::tlb::unpack_cell(msg_env, env)); + CHECK(env.deferred_lt && env.deferred_lt.value() == msg.lt); + CHECK(cb.store_long_bool(0b00100, 5) // msg_import_deferred_fin$00100 + && cb.store_ref_bool(msg_env) // in_msg:^MsgEnvelope + && cb.store_ref_bool(trans_root) // transaction:^Transaction + && block::tlb::t_Grams.store_integer_ref(cb, env.fwd_fee_remaining)); // fwd_fee:Grams + } else { + CHECK(cb.store_long_bool(3, 3) // msg_import_imm$011 + && cb.store_ref_bool(msg_env) // in_msg:^MsgEnvelope + && cb.store_ref_bool(trans_root) // transaction:^Transaction + && block::tlb::t_Grams.store_integer_ref(cb, fwd_fees)); // fwd_fee:Grams + } // 4. insert InMsg into InMsgDescr Ref in_msg = cb.finalize(); if (!insert_in_msg(in_msg)) { @@ -3106,14 +3139,16 @@ int Collator::process_one_new_message(block::NewOutMsg msg, bool enqueue_only, R *is_special = in_msg; return 1; } - // 5. create OutMsg, referring to this MsgEnvelope and InMsg - CHECK(cb.store_long_bool(2, 3) // msg_export_imm$010 - && cb.store_ref_bool(msg_env) // out_msg:^MsgEnvelope - && cb.store_ref_bool(msg.trans) // transaction:^Transaction - && cb.store_ref_bool(in_msg)); // reimport:^InMsg - // 6. insert OutMsg into OutMsgDescr - if (!insert_out_msg(cb.finalize())) { - return -1; + if (!from_dispatch_queue) { + // 5. create OutMsg, referring to this MsgEnvelope and InMsg + CHECK(cb.store_long_bool(2, 3) // msg_export_imm$010 + && cb.store_ref_bool(msg_env) // out_msg:^MsgEnvelope + && cb.store_ref_bool(msg.trans) // transaction:^Transaction + && cb.store_ref_bool(in_msg)); // reimport:^InMsg + // 6. insert OutMsg into OutMsgDescr + if (!insert_out_msg(cb.finalize())) { + return -1; + } } // 7. check whether the block is full now if (!block_limit_status_->fits(block::ParamLimits::cl_normal)) { @@ -3642,7 +3677,6 @@ bool Collator::process_dispatch_queue() { << src_addr.to_hex() << ", lt=" << lt); } - LOG(INFO) << "delivering deferred message from account " << src_addr.to_hex() << ", lt=" << lt; if (!process_deferred_message(std::move(enqueued_msg), src_addr, lt)) { return fatal_error(PSTRING() << "error processing internal message from dispatch queue: account=" << src_addr.to_hex() << ", lt=" << lt); @@ -3681,8 +3715,7 @@ bool Collator::process_deferred_message(Ref enq_msg, StdSmcAddres ++sender_generated_messages_count_[src_addr]; LogicalTime enqueued_lt = 0; - if (enq_msg.is_null() || enq_msg->size_ext() != 0x10040 || - (enqueued_lt = enq_msg->prefetch_ulong(64)) < /* 0 */ 1 * lt) { + if (enq_msg.is_null() || enq_msg->size_ext() != 0x10040 || (enqueued_lt = enq_msg->prefetch_ulong(64)) != lt) { if (enq_msg.not_null()) { block::gen::t_EnqueuedMsg.print(std::cerr, *enq_msg); } @@ -3750,44 +3783,28 @@ bool Collator::process_deferred_message(Ref enq_msg, StdSmcAddres LOG(ERROR) << "internal message in DispatchQueue is expected to have zero cur_addr and next_addr"; return false; } - // 5. decide what to do with the message - bool to_us = ton::shard_contains(shard_, dest_prefix); - if (!to_us) { - LogicalTime deferred_lt = std::max(start_lt, last_enqueued_deferred_lt_[src_addr] + 1); - auto it = accounts.find(src_addr); - if (it != accounts.end()) { - deferred_lt = std::max(deferred_lt, it->second->last_trans_end_lt_ + 1); - } - // destination is outside our shard, relay transit message - // (very similar to enqueue_message()) - if (!enqueue_transit_message(std::move(env.msg), std::move(msg_env), src_prefix, src_prefix, dest_prefix, - std::move(env.fwd_fee_remaining), std::move(env.metadata), deferred_lt)) { - return fatal_error(PSTRING() << "cannot enqueue transit internal message from dispatchqueue from address " - << src_addr.to_hex() << ", lt=" << lt); - } - last_enqueued_deferred_lt_[src_addr] = deferred_lt; - update_max_lt(deferred_lt + 1); - return true; + // 5. calculate deferred_lt + LogicalTime deferred_lt = std::max(start_lt, last_deferred_lt_[src_addr] + 1); + auto it = accounts.find(src_addr); + if (it != accounts.end()) { + deferred_lt = std::max(deferred_lt, it->second->last_trans_end_lt_ + 1); } - // destination is in our shard - // process the message by an ordinary transaction similarly to process_one_new_message() - // - // 6. create a transaction processing this message - auto trans_root = create_ordinary_transaction(env.msg, env.metadata); - if (trans_root.is_null()) { - return fatal_error("cannot create transaction for processing inbound message"); - } - // 7. create InMsg, referring to this MsgEnvelope and this Transaction - vm::CellBuilder cb; - CHECK(cb.store_long_bool(0b00100, 5) // msg_import_deferred_fin$00100 - && cb.store_ref_bool(msg_env) // in_msg:^MsgEnvelope - && cb.store_ref_bool(trans_root) // transaction:^Transaction - && block::tlb::t_Grams.store_integer_ref(cb, env.fwd_fee_remaining)); // fwd_fee:Grams - Ref in_msg = cb.finalize(); - // 8. insert InMsg into InMsgDescr - if (!insert_in_msg(std::move(in_msg))) { - return fatal_error("cannot insert InMsg into InMsgDescr"); + last_deferred_lt_[src_addr] = deferred_lt; + update_max_lt(deferred_lt + 1); + + env.deferred_lt = deferred_lt; + if (!block::tlb::pack_cell(msg_env, env)) { + return fatal_error("cannot pack msg envelope"); } + + // 6. create NewOutMsg + block::NewOutMsg new_msg{deferred_lt, env.msg, {}, 0}; + new_msg.metadata = env.metadata; + new_msg.msg_env_from_dispatch_queue = msg_env; + ++unprocessed_deferred_messages_[src_addr]; + LOG(INFO) << "delivering deferred message from account " << src_addr.to_hex() << ", lt=" << lt + << ", deferred_lt=" << deferred_lt; + register_new_msg(std::move(new_msg)); return true; } @@ -3891,14 +3908,15 @@ bool Collator::insert_out_msg(Ref out_msg, td::ConstBitPtr msg_hash) { * * @param msg The new outbound message to enqueue. * @param fwd_fees_remaining The remaining forward fees for the message. - * @param enqueued_lt The logical time at which the message is enqueued. * @param src_addr 256-bit address of the sender * @param defer Put the message to DispatchQueue * * @returns True if the message was successfully enqueued, false otherwise. */ -bool Collator::enqueue_message(block::NewOutMsg msg, td::RefInt256 fwd_fees_remaining, ton::LogicalTime enqueued_lt, - StdSmcAddress src_addr, bool defer) { +bool Collator::enqueue_message(block::NewOutMsg msg, td::RefInt256 fwd_fees_remaining, StdSmcAddress src_addr, + bool defer) { + LogicalTime enqueued_lt = msg.lt; + CHECK(msg.msg_env_from_dispatch_queue.is_null()); // 0. unpack src_addr and dest_addr block::gen::CommonMsgInfo::Record_int_msg_info info; if (!tlb::unpack_cell_inexact(msg.msg, info)) { diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 39ee8c33e..360924328 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -3752,8 +3752,10 @@ bool ValidateQuery::check_in_msg(td::ConstBitPtr key, Ref in_msg) return reject_query("inbound internal message with hash "s + key.to_hex(256) + " has destination address " + dest_prefix.to_str() + "... not in this shard, but it is processed nonetheless"); } - // if a message is not processed by a transaction, its final destination must be outside this shard - if (transaction.is_null() && ton::shard_contains(shard_, dest_prefix)) { + // if a message is not processed by a transaction, its final destination must be outside this shard, + // or it is a deferred message (dispatch queue -> out msg queue) + if (tag != block::gen::InMsg::msg_import_deferred_tr && transaction.is_null() && + ton::shard_contains(shard_, dest_prefix)) { return reject_query("inbound internal message with hash "s + key.to_hex(256) + " has destination address " + dest_prefix.to_str() + "... in this shard, but it is not processed by a transaction"); } @@ -3786,13 +3788,30 @@ bool ValidateQuery::check_in_msg(td::ConstBitPtr key, Ref in_msg) return reject_query(PSTRING() << "deferred InMsg with src_addr=" << src_addr.to_hex() << ", lt=" << lt << " was not removed from the dispatch queue"); } - Ref expexted_msg_env = it->second; - if (expexted_msg_env->get_hash() != msg_env->get_hash()) { + // InMsg msg_import_deferred_* has deferred_lt in MessageEnv, but this deferred_lt is not present in DispatchQueue + Ref dispatched_msg_env = it->second; + td::Ref expected_msg_env; + if (!env.deferred_lt) { + return reject_query(PSTRING() << "no dispatch_lt in deferred InMsg with src_addr=" << src_addr.to_hex() + << ", lt=" << lt); + } + auto deferred_lt = env.deferred_lt.value(); + if (deferred_lt < start_lt_ || deferred_lt > end_lt_) { + return reject_query(PSTRING() << "dispatch_lt in deferred InMsg with src_addr=" << src_addr.to_hex() + << ", lt=" << lt << " is not between start and end of the block"); + } + auto env2 = env; + env2.deferred_lt = {}; + CHECK(block::tlb::pack_cell(expected_msg_env, env2)); + if (dispatched_msg_env->get_hash() != expected_msg_env->get_hash()) { return reject_query(PSTRING() << "deferred InMsg with src_addr=" << src_addr.to_hex() << ", lt=" << lt - << " msg envelope hasg mismatch: " << msg_env->get_hash().to_hex() << " in InMsg, " - << expexted_msg_env->get_hash().to_hex() << " in DispatchQueue"); + << " msg envelope hasg mismatch: " << dispatched_msg_env->get_hash().to_hex() + << " in DispatchQueue, " << expected_msg_env->get_hash().to_hex() << " expected"); } removed_dispatch_queue_messages_.erase(it); + if (tag == block::gen::InMsg::msg_import_deferred_fin) { + msg_deferred_lt_.emplace_back(src_addr, lt, env.deferred_lt.value()); + } } if (transaction.not_null()) { @@ -4012,6 +4031,12 @@ bool ValidateQuery::check_in_msg(td::ConstBitPtr key, Ref in_msg) << (env.metadata ? env.metadata.value().to_str() : "") << " in in_msg, but " << (tr_env.metadata ? tr_env.metadata.value().to_str() : "") << " in out_msg"); } + if (tr_env.deferred_lt != env.deferred_lt) { + return reject_query( + PSTRING() << "InMsg for transit message with hash " << key.to_hex(256) << " contains invalid deferred_lt: " + << (env.deferred_lt ? td::to_string(env.deferred_lt.value()) : "") << " in in_msg, but " + << (tr_env.deferred_lt ? td::to_string(tr_env.deferred_lt.value()) : "") << " in out_msg"); + } if (tr_msg_env->get_hash() != out_msg_env->get_hash()) { return reject_query( "InMsg for transit message with hash "s + key.to_hex(256) + @@ -4094,7 +4119,7 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms int tag = block::gen::t_OutMsg.get_tag(*out_msg); CHECK(tag >= 0); // NB: the block has been already checked to be valid TL-B in try_validate() ton::StdSmcAddress src_addr; - ton::WorkchainId wc; + ton::WorkchainId src_wc; Ref src, dest; Ref transaction; Ref msg, msg_env, tr_msg_env, reimport; @@ -4138,7 +4163,7 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms src_prefix.to_str() + "... not in this shard"); } src = std::move(info_ext.src); - if (!block::tlb::t_MsgAddressInt.extract_std_address(src, wc, src_addr)) { + if (!block::tlb::t_MsgAddressInt.extract_std_address(src, src_wc, src_addr)) { return reject_query("cannot unpack source address of outbound external message with hash "s + key.to_hex(256)); } break; @@ -4238,6 +4263,14 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms reimport = std::move(out.imported); in_tag = block::gen::InMsg::msg_import_deferred_tr; mode = 2; // added to OutMsgQueue + if (!env.deferred_lt) { + return reject_query(PSTRING() << "msg_export_deferred_tr deferred lt for OutMsg with key " << key.to_hex(256) + << " does not have deferred lt"); + } + if (env.deferred_lt.value() < start_lt_ || env.deferred_lt.value() > end_lt_) { + return reject_query(PSTRING() << "deferred lt for msg_export_deferred_tr with key " << key.to_hex(256) + << " is not between start and end lt of the block"); + } // ... break; } @@ -4315,7 +4348,7 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms src = std::move(info.src); dest = std::move(info.dest); // unpack complete source address if it is inside this shard - if (transaction.not_null() && !block::tlb::t_MsgAddressInt.extract_std_address(src, wc, src_addr)) { + if (!block::tlb::t_MsgAddressInt.extract_std_address(src, src_wc, src_addr)) { return reject_query("cannot unpack source address of outbound internal message with hash "s + key.to_hex(256) + " created in this shard"); } @@ -4377,11 +4410,11 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms return reject_query(PSTRING() << "new deferred OutMsg with src_addr=" << src_addr.to_hex() << ", lt=" << created_lt << " was not added to the dispatch queue"); } - Ref expexted_msg_env = it->second; - if (expexted_msg_env->get_hash() != msg_env->get_hash()) { + Ref expected_msg_env = it->second; + if (expected_msg_env->get_hash() != msg_env->get_hash()) { return reject_query(PSTRING() << "new deferred OutMsg with src_addr=" << src_addr.to_hex() << ", lt=" << created_lt << " msg envelope hasg mismatch: " << msg_env->get_hash().to_hex() - << " in OutMsg, " << expexted_msg_env->get_hash().to_hex() << " in DispatchQueue"); + << " in OutMsg, " << expected_msg_env->get_hash().to_hex() << " in DispatchQueue"); } new_dispatch_queue_messages_.erase(it); } else { @@ -4671,6 +4704,24 @@ bool ValidateQuery::check_out_msg(td::ConstBitPtr key, Ref out_ms return fatal_error(PSTRING() << "unknown OutMsg tag " << tag); } + if (tag == block::gen::OutMsg::msg_export_imm || tag == block::gen::OutMsg::msg_export_deq_imm || + tag == block::gen::OutMsg::msg_export_new || tag == block::gen::OutMsg::msg_export_deferred_tr) { + if (src_wc != workchain()) { + return true; + } + if (tag == block::gen::OutMsg::msg_export_imm && is_special_in_msg(vm::load_cell_slice(reimport))) { + return true; + } + unsigned long long created_lt; + auto cs = vm::load_cell_slice(env.msg); + if (!block::tlb::t_Message.get_created_lt(cs, created_lt)) { + return reject_query(PSTRING() << "cannot get created_lt for OutMsg with key " << key.to_hex(256) + << ", tag=" << tag); + } + auto created_or_deferred_lt = env.deferred_lt ? env.deferred_lt.value() : created_lt; + msg_deferred_lt_.emplace_back(src_addr, created_lt, created_or_deferred_lt); + } + return true; } @@ -5129,8 +5180,22 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT << " processed inbound message created later at logical time " << info.created_lt); } + LogicalTime created_or_deferred_lt = info.created_lt; // See ValidateQuery::check_message_processing_order + if (in_msg_tag == block::gen::InMsg::msg_import_imm || in_msg_tag == block::gen::InMsg::msg_import_fin || + in_msg_tag == block::gen::InMsg::msg_import_deferred_fin) { + block::tlb::MsgEnvelope::Record_std msg_env; + if (!block::tlb::unpack_cell(in_descr_cs->prefetch_ref(), msg_env)) { + return reject_query(PSTRING() << "InMsg record for inbound message with hash " + << in_msg_root->get_hash().to_hex() << " of transaction " << lt + << " of account " << addr.to_hex() << " does not have a valid MsgEnvelope"); + } + in_msg_metadata = std::move(msg_env.metadata); + if (msg_env.deferred_lt) { + created_or_deferred_lt = msg_env.deferred_lt.value(); + } + } if (info.created_lt != start_lt_ || !is_special_tx) { - msg_proc_lt_.emplace_back(addr, lt, info.created_lt); + msg_proc_lt_.emplace_back(addr, lt, created_or_deferred_lt); } dest = std::move(info.dest); CHECK(money_imported.validate_unpack(info.value)); @@ -5154,16 +5219,6 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT << " of transaction " << lt << " of account " << addr.to_hex() << " refers to a different processing transaction"); } - if (in_msg_tag == block::gen::InMsg::msg_import_imm || in_msg_tag == block::gen::InMsg::msg_import_fin || - in_msg_tag == block::gen::InMsg::msg_import_deferred_fin) { - block::tlb::MsgEnvelope::Record_std msg_env; - if (!block::tlb::unpack_cell(in_descr_cs->prefetch_ref(), msg_env)) { - return reject_query(PSTRING() << "InMsg record for inbound message with hash " - << in_msg_root->get_hash().to_hex() << " of transaction " << lt << " of account " - << addr.to_hex() << " does not have a valid MsgEnvelope"); - } - in_msg_metadata = std::move(msg_env.metadata); - } } // check output messages td::optional new_msg_metadata; @@ -5731,12 +5786,9 @@ bool ValidateQuery::check_all_ticktock_processed() { bool ValidateQuery::check_message_processing_order() { // Old rule: if messages m1 and m2 with the same destination generate transactions t1 and t2, // then (m1.created_lt < m2.created_lt) => (t1.lt < t2.lt). - // This no longer holds when DispatchQueue is added. // New rule: - // If an account sends two messages m1, m2, m1.created_lt < m2.created_lt - // then they are processed (enqueued in OutMsgQueue or generate transaction) in the same order. - - /* std::sort(msg_proc_lt_.begin(), msg_proc_lt_.end()); + // If message was deferred, instead of created_lt use deferred_lt + std::sort(msg_proc_lt_.begin(), msg_proc_lt_.end()); for (std::size_t i = 1; i < msg_proc_lt_.size(); i++) { auto &a = msg_proc_lt_[i - 1], &b = msg_proc_lt_[i]; if (std::get<0>(a) == std::get<0>(b) && std::get<2>(a) > std::get<2>(b)) { @@ -5746,7 +5798,21 @@ bool ValidateQuery::check_message_processing_order() { << std::get<0>(a).to_hex() << ") processes an earlier message created at logical time " << std::get<2>(b)); } - } */ + } + + // Check that if messages m1 and m2 with the same source have m1.created_lt < m2.created_lt then + // m1.created_or_deferred_lt < m2.created_or_deferred_lt + std::sort(msg_deferred_lt_.begin(), msg_deferred_lt_.end()); + for (std::size_t i = 1; i < msg_deferred_lt_.size(); i++) { + auto &a = msg_deferred_lt_[i - 1], &b = msg_deferred_lt_[i]; + if (std::get<0>(a) == std::get<0>(b) && std::get<2>(a) >= std::get<2>(b)) { + return reject_query(PSTRING() << "incorrect deferred message processing order for sender " + << std::get<0>(a).to_hex() << ": message with created_lt " << std::get<1>(a) + << " has created_or_deferred_lt " << std::get<2>(a) + << ", but message with created_lt " << std::get<1>(b) + << " has created_or_deferred_lt " << std::get<2>(b)); + } + } return true; } diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index 4339fcce0..9679a1899 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -228,6 +228,7 @@ class ValidateQuery : public td::actor::Actor { bool inbound_queues_empty_{false}; std::vector> msg_proc_lt_; + std::vector> msg_deferred_lt_; std::vector> lib_publishers_, lib_publishers2_;