diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index e06b2cfd..d80518ce 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -119,6 +119,36 @@ builder pack_complaint(int validator_pubkey, cell description, int severity, int return (cs~load_int(32), cs~load_int(32), cs~load_int(32), cs.preload_int(32)); } +;; next three functions return information about current validator set (config param #34) +;; they are borrowed from config-code.fc +(cell, int, cell) get_current_vset() inline_ref { + var vset = config_param(34); + var cs = begin_parse(vset); + ;; validators_ext#12 utime_since:uint32 utime_until:uint32 + ;; total:(## 16) main:(## 16) { main <= total } { main >= 1 } + ;; total_weight:uint64 + throw_unless(40, cs~load_uint(8) == 0x12); + cs~skip_bits(32 + 32 + 16 + 16); + var (total_weight, dict) = (cs~load_uint(64), cs~load_dict()); + cs.end_parse(); + return (vset, total_weight, dict); +} + +(slice, int) get_validator_descr(int idx) inline_ref { + var (vset, total_weight, dict) = get_current_vset(); + var (value, _) = dict.udict_get?(16, idx); + return (value, total_weight); +} + +(int, int) unpack_validator_descr(slice cs) inline { + ;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; + ;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr; + ;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; + throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53); + throw_unless(41, cs~load_uint(32) == 0x8e81278a); + return (cs~load_uint(256), cs~load_uint(64)); +} + () send_message_back(addr, ans_tag, query_id, body, grams, mode) impure inline_ref { ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool src:MsgAddress -> 011000 var msg = begin_cell() @@ -446,6 +476,7 @@ int register_complaint(s_addr, complaint, msg_value) { ifnot (f) { ;; no such validator, cannot complain return -6; } + fs~skip_bits(256 + 64); ;; addr weight var validator_stake = fs~load_grams(); int fine = suggested_fine + muldiv(validator_stake, suggested_fine_part, 1 << 32); if (fine > validator_stake) { ;; validator's stake is less than suggested fine @@ -467,7 +498,84 @@ int register_complaint(s_addr, complaint, msg_value) { ;; next line can be commented, but it saves a lot of stack manipulations var (elect, credits, _, grams, active_id, active_hash) = load_data(); store_data(elect, credits, past_elections, grams, active_id, active_hash); - return 0; + return paid; +} + +(cell, cell, int) punish(credits, frozen, complaint) inline_ref { + var (validator_pubkey, description, severity, reward_addr, paid, suggested_fine, suggested_fine_part) = complaint.begin_parse().unpack_complaint(); + var (cs, f) = frozen.udict_get?(256, validator_pubkey); + ifnot (f) { + ;; no validator to punish + return (credits, frozen, 0); + } + var (addr, weight, stake, banned) = (cs~load_uint(256), cs~load_uint(64), cs~load_grams(), cs~load_int(1)); + cs.end_parse(); + int fine = min(stake, suggested_fine + muldiv(stake, suggested_fine_part, 1 << 32)); + stake -= fine; + frozen~udict_set_builder(256, validator_pubkey, begin_cell() + .store_uint(addr, 256) + .store_uint(weight, 64) + .store_grams(stake) + .store_int(banned, 1)); + int reward = min(fine >> 3, paid * 8); + fine -= reward; + credits~credit_to(reward_addr, reward); + return (credits, frozen, fine); +} + +(cell, cell, int) register_vote(complaints, chash, idx, weight) inline_ref { + var (cstatus, found?) = complaints.udict_get?(256, chash); + ifnot (found?) { + ;; complaint not found + return (complaints, null(), -1); + } + var (cur_vset, total_weight, _) = get_current_vset(); + int cur_vset_id = cur_vset.cell_hash(); + var (complaint, voters, vset_id, weight_remaining) = unpack_complaint_status(cstatus); + if (vset_id != cur_vset_id) { + ;; complaint votes belong to a previous validator set, reset voting + vset_id = cur_vset_id; + voters = null(); + weight_remaining = muldiv(total_weight, 2, 3); + } + var (_, found?) = voters.udict_get?(16, idx); + if (found?) { + ;; already voted for this proposal, ignore vote + return (complaints, null(), 0); + } + ;; register vote + voters~udict_set_builder(16, idx, begin_cell().store_uint(now(), 32)); + int old_wr = weight_remaining; + weight_remaining -= weight; + old_wr ^= weight_remaining; + ;; save voters and weight_remaining + complaints~udict_set_builder(256, chash, pack_complaint_status(complaint, voters, vset_id, weight_remaining)); + if (old_wr >= 0) { + ;; not enough votes or already accepted + return (complaints, null(), 1); + } + ;; complaint wins, prepare punishment + return (complaints, complaint, 2); +} + +int proceed_register_vote(election_id, chash, idx, weight) impure inline_ref { + var (elect, credits, past_elections, grams, active_id, active_hash) = load_data(); + var (fs, f) = past_elections.udict_get?(32, election_id); + ifnot (f) { ;; election not found + return -2; + } + var (unfreeze_at, stake_held, vset_hash, frozen_dict, total_stake, bonuses, complaints) = unpack_past_election(fs); + (complaints, var accepted_complaint, var status) = register_vote(complaints, chash, idx, weight); + if (status <= 0) { + return status; + } + ifnot (accepted_complaint.null?()) { + (credits, frozen_dict, int fine) = punish(credits, frozen_dict, accepted_complaint); + grams += fine; + } + past_elections.udict_set_builder(32, election_id, pack_past_election(unfreeze_at, stake_held, vset_hash, frozen_dict, total_stake, bonuses, complaints)); + store_data(elect, credits, past_elections, grams, active_id, active_hash); + return status; } () recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { @@ -520,6 +628,20 @@ int register_complaint(s_addr, complaint, msg_value) { } return send_message_back(s_addr, ans_tag + 0xf2676350, query_id, op, 0, mode); } + if (op == 0x56744370) { + ;; vote for a complaint + var signature = in_msg~load_bits(512); + var msg_body = in_msg; + var (sign_tag, idx, elect_id, chash) = (in_msg~load_uint(32), in_msg~load_uint(16), in_msg~load_uint(32), in_msg~load_uint(256)); + in_msg.end_parse(); + throw_unless(37, sign_tag == 0x56744350); + var (vdescr, total_weight) = get_validator_descr(idx); + var (val_pubkey, weight) = unpack_validator_descr(vdescr); + throw_unless(34, check_data_signature(msg_body, signature, val_pubkey)); + int res = proceed_register_vote(elect_id, chash, idx, weight); + return send_message_back(s_addr, res + 0xd6745240, query_id, op, 0, 64); + } + ifnot (op & (1 << 31)) { ;; unknown query, return error return send_message_back(s_addr, 0xffffffff, query_id, op, 0, 64); diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 23e27451..c3d739de 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -1171,6 +1171,15 @@ td::Status ValidatorEngine::load_global_config() { } validator_options_ = ton::validator::ValidatorManagerOptions::create(zero_state, init_block); + validator_options_.write().set_shard_check_function( + [](ton::ShardIdFull shard, ton::validator::ValidatorManagerOptions::ShardCheckMode mode) -> bool { + if (mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_monitor) { + return true; + } + CHECK(mode == ton::validator::ValidatorManagerOptions::ShardCheckMode::m_validate); + //return shard.is_masterchain(); + return true; + }); if (state_ttl_ != 0) { validator_options_.write().set_state_ttl(state_ttl_); } diff --git a/validator-session/validator-session-description.hpp b/validator-session/validator-session-description.hpp index a7b42c9f..d7b430bd 100644 --- a/validator-session/validator-session-description.hpp +++ b/validator-session/validator-session-description.hpp @@ -143,7 +143,7 @@ class ValidatorSessionDescriptionImpl : public ValidatorSessionDescription { td::uint32 src_idx, td::Slice signature) const override; double get_delay(td::uint32 priority) const override; double get_empty_block_delay() const override { - return get_delay(get_max_priority() + 1); + return std::max(get_delay(get_max_priority() + 1), 1.0); } td::uint32 get_vote_for_author(td::uint32 attempt_seqno) const override; std::vector export_nodes() const; diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 1bf1e535..80f3eb72 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -1250,8 +1250,10 @@ void ValidateQuery::got_neighbor_out_queue(int i, td::Result> descr.set_queue_root(qinfo.out_queue->prefetch_ref(0)); // TODO: comment the next two lines in the future when the output queues become huge // (do this carefully) - CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell())); - CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell())); + if (debug_checks_) { + CHECK(block::gen::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell())); + CHECK(block::tlb::t_OutMsgQueueInfo.validate_ref(1000000, outq_descr->root_cell())); + } // unpack ProcessedUpto LOG(DEBUG) << "unpacking ProcessedUpto of neighbor " << descr.blk_.to_str(); if (verbosity >= 2) { diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index 031e3204..c75b5877 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -143,6 +143,7 @@ class ValidateQuery : public td::actor::Actor { bool update_shard_cc_{false}; bool is_fake_{false}; bool prev_key_block_exists_{false}; + bool debug_checks_{false}; BlockSeqno prev_key_seqno_{~0u}; int stage_{0}; td::BitArray<64> shard_pfx_;