diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index 7b9e6a80001625db43cb3e9b0945717d7b64bc39..f2ad386e912e5fae3ff75b153ff949885742f5b3 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -269,6 +269,11 @@ Minor Changes - New function `compare_operations` which defines a total ordering relation. (MR :gl:`!6092`) +- Removed conflict between proposals/ballots operations and testnet + dictator proposals. Ballots and proposals become noops + when applying the block after a testnet dictator enacted a protocol + change. (MR :gl:`!6313`) + Internal -------- diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 92decb272643ab93e20339653d7bb7419ba11163..0b4d291eb75aacfe96babdf8829f08a0ed801ef1 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -585,6 +585,10 @@ let record_non_consensus_operation_hash = let non_consensus_operations = Raw_context.non_consensus_operations +let record_dictator_proposal_seen = Raw_context.record_dictator_proposal_seen + +let dictator_proposal_seen = Raw_context.dictator_proposal_seen + let activate = Raw_context.activate let reset_internal_nonce = Raw_context.reset_internal_nonce diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index a15e7045571c559e12ada98fc93f2c86393c31f6..e298a64b0f0889702664506ab8f2473c22c66462 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -4648,6 +4648,10 @@ val record_non_consensus_operation_hash : context -> Operation_hash.t -> context val non_consensus_operations : context -> Operation_hash.t list +val record_dictator_proposal_seen : t -> t + +val dictator_proposal_seen : t -> bool + (** Finalize an {{!t} [Alpha_context.t]}, producing a [validation_result]. *) val finalize : diff --git a/src/proto_alpha/lib_protocol/amendment.ml b/src/proto_alpha/lib_protocol/amendment.ml index 08cb2a151633678ddf59f1443b6a286df4f21dcd..d59452cbbd22cb3680dd0bd4f2c0f378bf79da03 100644 --- a/src/proto_alpha/lib_protocol/amendment.ml +++ b/src/proto_alpha/lib_protocol/amendment.ml @@ -173,6 +173,7 @@ module Testnet_dictator = struct let*! ctxt = Vote.clear_ballots ctxt in let*! ctxt = Vote.clear_proposals ctxt in let*! ctxt = Vote.clear_current_proposal ctxt in + let ctxt = record_dictator_proposal_seen ctxt in match proposals with | [] -> Voting_period.Testnet_dictator.overwrite_current_kind @@ -196,6 +197,9 @@ let apply_proposals ctxt chain_id (Proposals {source; period = _; proposals}) = let* ctxt = if is_testnet_dictator ctxt chain_id source then Testnet_dictator.record_proposals ctxt chain_id proposals + else if dictator_proposal_seen ctxt then + (* Noop if dictator voted *) + return ctxt else let* count = Vote.get_delegate_proposal_count ctxt source in let new_count = count + List.length proposals in @@ -212,5 +216,8 @@ let apply_proposals ctxt chain_id (Proposals {source; period = _; proposals}) = let apply_ballot ctxt (Ballot {source; period = _; proposal = _; ballot}) = let open Lwt_tzresult_syntax in - let* ctxt = Vote.record_ballot ctxt source ballot in + let* ctxt = + if dictator_proposal_seen ctxt then (* Noop if dictator voted *) return ctxt + else Vote.record_ballot ctxt source ballot + in return (ctxt, Apply_results.Single_result Ballot_result) diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index 50a9d86e20fecb9346eca6064a30680ee30e37ef..e66e2a17f33cb6324bea6fe2ff8a04d43d564d22 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -1509,7 +1509,11 @@ module Encoding = struct obj3 (req "source" Signature.Public_key_hash.encoding) (req "period" int32) - (req "proposals" (list Protocol_hash.encoding)); + (req + "proposals" + (list + ~max_length:Constants_repr.max_proposals_per_delegate + Protocol_hash.encoding)); select = (function Contents (Proposals _ as op) -> Some op | _ -> None); proj = diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index d1d4786e14a9ca3b78bcd4ebdc1d50f1edec2468..e7d4f737d20d3197632f4690cb9a735ba9a2752c 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -257,6 +257,7 @@ type back = { unlimited_operation_gas : bool; consensus : Raw_consensus.t; non_consensus_operations_rev : Operation_hash.t list; + dictator_proposal_seen : bool; sampler_state : (Seed_repr.seed * consensus_pk Sampler.t) Cycle_repr.Map.t; stake_distribution_for_current_cycle : Tez_repr.t Signature.Public_key_hash.Map.t option; @@ -342,6 +343,8 @@ let[@inline] remaining_operation_gas ctxt = ctxt.remaining_operation_gas let[@inline] non_consensus_operations_rev ctxt = ctxt.back.non_consensus_operations_rev +let[@inline] dictator_proposal_seen ctxt = ctxt.back.dictator_proposal_seen + let[@inline] sampler_state ctxt = ctxt.back.sampler_state let[@inline] update_back ctxt back = {ctxt with back} @@ -379,6 +382,9 @@ let[@inline] update_non_consensus_operations_rev ctxt non_consensus_operations_rev = update_back ctxt {ctxt.back with non_consensus_operations_rev} +let[@inline] update_dictator_proposal_seen ctxt dictator_proposal_seen = + update_back ctxt {ctxt.back with dictator_proposal_seen} + let[@inline] update_sampler_state ctxt sampler_state = update_back ctxt {ctxt.back with sampler_state} @@ -832,6 +838,7 @@ let prepare ~level ~predecessor_timestamp ~timestamp ctxt = unlimited_operation_gas = true; consensus = Raw_consensus.empty; non_consensus_operations_rev = []; + dictator_proposal_seen = false; sampler_state = Cycle_repr.Map.empty; stake_distribution_for_current_cycle = None; tx_rollup_current_messages = Tx_rollup_repr.Map.empty; @@ -1302,6 +1309,10 @@ let record_non_consensus_operation_hash ctxt operation_hash = let non_consensus_operations ctxt = List.rev (non_consensus_operations_rev ctxt) +let record_dictator_proposal_seen ctxt = update_dictator_proposal_seen ctxt true + +let dictator_proposal_seen ctxt = dictator_proposal_seen ctxt + module Migration_from_Kathmandu = struct let reset_samplers ctxt = let ctxt = update_sampler_state ctxt Cycle_repr.Map.empty in diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index b656afe0c09aba4644071c5abf7e532cac0c0070..cd353fa5cfcb206b59c03e7cf662066bcb52f44c 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -242,6 +242,12 @@ type consensus_pk = { val consensus_pk_encoding : consensus_pk Data_encoding.t +(** Record that the dictator already voted in this block. *) +val record_dictator_proposal_seen : t -> t + +(** Checks whether the dictator voted in this block. *) +val dictator_proposal_seen : t -> bool + (** [init_sampler_for_cycle ctxt cycle seed state] caches the seeded stake sampler (a.k.a. [seed, state]) for [cycle] in memory for quick access. *) val init_sampler_for_cycle : diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_voting.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_voting.ml index 1ca7ec09ae115c80a9fc2aecd9c01ec1e1695c90..9981dd0f803ee6915da761656bbb4335ecb34555 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_voting.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_voting.ml @@ -1393,22 +1393,6 @@ let test_proposals_contain_duplicate () = block __LOC__ -(** Test that a Proposals operation fails when its proposal list is - longer than the [max_proposals_per_delegate] protocol constant. *) -let test_operation_has_too_many_proposals () = - let open Lwt_result_syntax in - let* block, proposer = context_init1 () in - assert (Array.length protos >= Constants.max_proposals_per_delegate + 1) ; - let proposals = - List.map (Array.get protos) (0 -- Constants.max_proposals_per_delegate) - in - assert_validate_proposals_fails - ~expected_error:too_many_proposals - ~proposer - ~proposals - block - __LOC__ - (** Test that a Proposals operation fails when it would make the total count of proposals submitted by the proposer exceed the [max_proposals_per_delegate] protocol constant. *) @@ -1647,6 +1631,19 @@ let observe_proposals pre_state post_state op caller_loc = (Int64.add weight_pre source_power)) proposals +let test_too_many_proposals_in_one_operation () = + let open Lwt_result_syntax in + let* b0, proposer0 = context_init1 () in + let protos = Array.to_list protos in + let* _ = + try + let* _ = Op.proposals (B b0) proposer0 protos in + failwith + "Encoding of proposals operation with too many proposal should fail" + with Data_encoding.Binary.(Write_error List_invalid_length) -> return_unit + in + return_unit + (* Bake blocks with various valid Proposals operations, and observe that their effects are correctly applied. *) let test_valid_proposals () = @@ -2047,9 +2044,9 @@ let tests = `Quick test_proposals_contain_duplicate; Tztest.tztest - "Operation has too many proposals" + "Too many proposals (over one operation)" `Quick - test_operation_has_too_many_proposals; + test_too_many_proposals_in_one_operation; Tztest.tztest "Too many proposals (over two operations)" `Quick