From e610c180681087f4b8f41f690c8f56d29fdff1c0 Mon Sep 17 00:00:00 2001 From: Diane Gallois-Wong Date: Fri, 13 Dec 2024 11:16:46 +0100 Subject: [PATCH 1/6] Proto: add unstake_finalization_delay pseudo-constant and factorize greatest_unstake_finalizable_cycle --- .../lib_plugin/delegate_services.ml | 7 ++----- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 1 + src/proto_alpha/lib_protocol/alpha_context.ml | 6 +++++- src/proto_alpha/lib_protocol/alpha_context.mli | 12 +++++++++++- .../lib_protocol/constants_storage.ml | 3 +++ .../lib_protocol/constants_storage.mli | 15 ++++++++++++++- src/proto_alpha/lib_protocol/cycle_storage.ml | 13 +++++++++++++ src/proto_alpha/lib_protocol/cycle_storage.mli | 18 ++++++++++++++++++ src/proto_alpha/lib_protocol/dune | 4 ++++ src/proto_alpha/lib_protocol/seed_storage.ml | 2 +- .../lib_protocol/unstake_requests_storage.ml | 7 +------ .../unstaked_frozen_deposits_storage.ml | 12 +++--------- 12 files changed, 76 insertions(+), 24 deletions(-) create mode 100644 src/proto_alpha/lib_protocol/cycle_storage.ml create mode 100644 src/proto_alpha/lib_protocol/cycle_storage.mli diff --git a/src/proto_alpha/lib_plugin/delegate_services.ml b/src/proto_alpha/lib_plugin/delegate_services.ml index a12bfb59e7a0..b107de23dc0d 100644 --- a/src/proto_alpha/lib_plugin/delegate_services.ml +++ b/src/proto_alpha/lib_plugin/delegate_services.ml @@ -1141,13 +1141,10 @@ let f_external_delegated ctxt pkh () () = let total_unstaked_per_cycle ctxt pkh = let open Lwt_result_syntax in - let ctxt_cycle = (Alpha_context.Level.current ctxt).cycle in + let ctxt_cycle = Cycle.current ctxt in let last_unslashable_cycle = Option.value ~default:Cycle.root - @@ Cycle.sub - ctxt_cycle - (Constants.slashable_deposits_period ctxt - + Constants_repr.max_slashing_period) + @@ Cycle.greatest_unstake_finalizable_cycle ctxt in let cycles = Cycle.(last_unslashable_cycle ---> ctxt_cycle) in List.map_es diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index bed70e8c3409..59c0291b4d8a 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -144,6 +144,7 @@ "Ticket_hash_builder", "Constants_storage", "Level_storage", + "Cycle_storage", "Nonce_storage", "Seed_storage", "Contract_manager_storage", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 5628c9371603..22acc6aad520 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -243,7 +243,11 @@ module Raw_level = struct end end -module Cycle = Cycle_repr +module Cycle = struct + include Cycle_repr + include Cycle_storage +end + module Fees = Fees_storage type public_key = Signature.Public_key.t diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 8fc9a2064be7..86500a920c66 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -276,7 +276,8 @@ module Raw_level : sig end end -(** This module re-exports definitions from {!Cycle_repr}. *) +(** This module re-exports definitions from {!Cycle_repr} and + {!Cycle_storage}. *) module Cycle : sig include BASIC_DATA @@ -299,6 +300,12 @@ module Cycle : sig val ( ---> ) : cycle -> cycle -> cycle list module Map : Map.S with type key = cycle + + (** See {!Cycle_storage.current}. *) + val current : context -> cycle + + (** See {!Cycle_storage.greatest_unstake_finalizable_cycle}. *) + val greatest_unstake_finalizable_cycle : context -> cycle option end (** This module re-exports definitions from {!Round_repr}. *) @@ -983,6 +990,9 @@ module Constants : sig val slashable_deposits_period : context -> int + (** See {!Constants_storage.unstake_finalization_delay}. *) + val unstake_finalization_delay : context -> int + val issuance_modification_delay : context -> int val blocks_per_cycle : context -> int32 diff --git a/src/proto_alpha/lib_protocol/constants_storage.ml b/src/proto_alpha/lib_protocol/constants_storage.ml index 779b4e796b62..9f738d950223 100644 --- a/src/proto_alpha/lib_protocol/constants_storage.ml +++ b/src/proto_alpha/lib_protocol/constants_storage.ml @@ -73,6 +73,9 @@ let slashable_deposits_period c = let constants = Raw_context.constants c in constants.consensus_rights_delay +let unstake_finalization_delay c = + slashable_deposits_period c + (Constants_repr.max_slashing_period - 1) + let blocks_per_cycle c = let constants = Raw_context.constants c in constants.blocks_per_cycle diff --git a/src/proto_alpha/lib_protocol/constants_storage.mli b/src/proto_alpha/lib_protocol/constants_storage.mli index 47069d4c63bd..d86e07b64047 100644 --- a/src/proto_alpha/lib_protocol/constants_storage.mli +++ b/src/proto_alpha/lib_protocol/constants_storage.mli @@ -177,9 +177,22 @@ val consensus_key_activation_delay : Raw_context.t -> int (** Number of cycles during which a misbehavior of a delegate will induce a slashing of the funds that are currently in its frozen deposit. *) - val slashable_deposits_period : Raw_context.t -> int +(** Number of **full cycles** to wait for an unstake request to become + finalizable. + + In other words, if the unstake is requested during cycle [n], then + it becomes finalizable after the end of cycle [n + + unstake_finalization_delay], at the beginning of cycle [n + + unstake_finalization_delay + 1]. + + The exact waiting time depends on when the unstake operation + happened inside cycle [n], but it is always at least [n + + unstake_finalization_delay] cycles and less than [n + + unstake_finalization_delay + 1] cycles. *) +val unstake_finalization_delay : Raw_context.t -> int + (* attestation aggregation feature flag *) val aggregate_attestation : Raw_context.t -> bool diff --git a/src/proto_alpha/lib_protocol/cycle_storage.ml b/src/proto_alpha/lib_protocol/cycle_storage.ml new file mode 100644 index 000000000000..c5d9c6e16fe9 --- /dev/null +++ b/src/proto_alpha/lib_protocol/cycle_storage.ml @@ -0,0 +1,13 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +let current ctxt = (Raw_context.current_level ctxt).cycle + +let greatest_unstake_finalizable_cycle ctxt = + Cycle_repr.sub + (current ctxt) + (Constants_storage.unstake_finalization_delay ctxt + 1) diff --git a/src/proto_alpha/lib_protocol/cycle_storage.mli b/src/proto_alpha/lib_protocol/cycle_storage.mli new file mode 100644 index 000000000000..33aa4ba361b0 --- /dev/null +++ b/src/proto_alpha/lib_protocol/cycle_storage.mli @@ -0,0 +1,18 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2024 Nomadic Labs *) +(* *) +(*****************************************************************************) + +(** This module hosts cycle functions which depend on the context. *) + +(** Current cycle, that is, the cycle of the current level. *) +val current : Raw_context.t -> Cycle_repr.t + +(** Highest cycle whose unstake requests are currently finalizable. + + Returns [None] when the computed cycle would be negative: this + means there are no cycles yet whose unstake request can be + finalized. *) +val greatest_unstake_finalizable_cycle : Raw_context.t -> Cycle_repr.t option diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index cfb3f8ebb60b..15a062600551 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -163,6 +163,7 @@ Ticket_hash_builder Constants_storage Level_storage + Cycle_storage Nonce_storage Seed_storage Contract_manager_storage @@ -460,6 +461,7 @@ ticket_hash_builder.ml ticket_hash_builder.mli constants_storage.ml constants_storage.mli level_storage.ml level_storage.mli + cycle_storage.ml cycle_storage.mli nonce_storage.ml nonce_storage.mli seed_storage.ml seed_storage.mli contract_manager_storage.ml contract_manager_storage.mli @@ -759,6 +761,7 @@ ticket_hash_builder.ml ticket_hash_builder.mli constants_storage.ml constants_storage.mli level_storage.ml level_storage.mli + cycle_storage.ml cycle_storage.mli nonce_storage.ml nonce_storage.mli seed_storage.ml seed_storage.mli contract_manager_storage.ml contract_manager_storage.mli @@ -1042,6 +1045,7 @@ ticket_hash_builder.ml ticket_hash_builder.mli constants_storage.ml constants_storage.mli level_storage.ml level_storage.mli + cycle_storage.ml cycle_storage.mli nonce_storage.ml nonce_storage.mli seed_storage.ml seed_storage.mli contract_manager_storage.ml contract_manager_storage.mli diff --git a/src/proto_alpha/lib_protocol/seed_storage.ml b/src/proto_alpha/lib_protocol/seed_storage.ml index 9409d20883cd..bbea45563261 100644 --- a/src/proto_alpha/lib_protocol/seed_storage.ml +++ b/src/proto_alpha/lib_protocol/seed_storage.ml @@ -219,7 +219,7 @@ let for_cycle ctxt cycle = let open Lwt_result_syntax in let delay = Constants_storage.consensus_rights_delay ctxt in let max_slashing_period = Constants_repr.max_slashing_period in - let current_cycle = (Level_storage.current ctxt).cycle in + let current_cycle = Cycle_storage.current ctxt in let latest = if Cycle_repr.(current_cycle = root) then Cycle_repr.add current_cycle (delay + 1) diff --git a/src/proto_alpha/lib_protocol/unstake_requests_storage.ml b/src/proto_alpha/lib_protocol/unstake_requests_storage.ml index 4ff1186700e6..f12c85ce92ed 100644 --- a/src/proto_alpha/lib_protocol/unstake_requests_storage.ml +++ b/src/proto_alpha/lib_protocol/unstake_requests_storage.ml @@ -120,16 +120,11 @@ let prepare_finalize_unstake_uncarbonated ctxt let slashable_deposits_period = Constants_storage.slashable_deposits_period ctxt in - let max_slashing_period = Constants_repr.max_slashing_period in - let slashable_plus_denunciation_delay = - slashable_deposits_period + max_slashing_period - in - let current_cycle = (Raw_context.current_level ctxt).cycle in let* requests_opt = Storage.Contract.Unstake_requests.find ctxt contract in match requests_opt with | None | Some {delegate = _; requests = []} -> return_none | Some {delegate; requests} -> ( - match Cycle_repr.sub current_cycle slashable_plus_denunciation_delay with + match Cycle_storage.greatest_unstake_finalizable_cycle ctxt with | None (* no finalizable cycle *) -> return_some {finalizable = []; unfinalizable = {delegate; requests}} | Some greatest_finalizable_cycle -> diff --git a/src/proto_alpha/lib_protocol/unstaked_frozen_deposits_storage.ml b/src/proto_alpha/lib_protocol/unstaked_frozen_deposits_storage.ml index 782d27216ade..b167acba6330 100644 --- a/src/proto_alpha/lib_protocol/unstaked_frozen_deposits_storage.ml +++ b/src/proto_alpha/lib_protocol/unstaked_frozen_deposits_storage.ml @@ -23,20 +23,14 @@ (* *) (*****************************************************************************) -let current_unslashable_cycle ctxt = - let cycle = (Raw_context.current_level ctxt).cycle in - let slashable_deposits_period = - Constants_storage.slashable_deposits_period ctxt - in - let max_slashing_period = Constants_repr.max_slashing_period in - Cycle_repr.sub cycle (slashable_deposits_period + max_slashing_period) - let get_all ctxt contract = let open Lwt_result_syntax in let* unstaked_frozen_deposits_opt = Storage.Contract.Unstaked_frozen_deposits.find ctxt contract in - let unslashable_cycle = current_unslashable_cycle ctxt in + let unslashable_cycle = + Cycle_storage.greatest_unstake_finalizable_cycle ctxt + in match unstaked_frozen_deposits_opt with | None -> return (Unstaked_frozen_deposits_repr.empty ~unslashable_cycle) | Some unstaked_frozen_deposits -> -- GitLab From b702d1521518a1f05727c92fb3faf1db110b74e1 Mon Sep 17 00:00:00 2001 From: Diane Gallois-Wong Date: Fri, 13 Dec 2024 16:54:04 +0100 Subject: [PATCH 2/6] Proto: factorize and comment last cycle with sampling data and cycle to be cleared when a new cycle starts --- src/proto_alpha/lib_protocol/cycle_storage.ml | 20 +++++++++++++++++++ .../lib_protocol/cycle_storage.mli | 12 +++++++++++ .../lib_protocol/delegate_sampler.ml | 2 +- src/proto_alpha/lib_protocol/seed_storage.ml | 7 +------ src/proto_alpha/lib_protocol/stake_storage.ml | 3 +-- 5 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/proto_alpha/lib_protocol/cycle_storage.ml b/src/proto_alpha/lib_protocol/cycle_storage.ml index c5d9c6e16fe9..07147dadc0f2 100644 --- a/src/proto_alpha/lib_protocol/cycle_storage.ml +++ b/src/proto_alpha/lib_protocol/cycle_storage.ml @@ -7,6 +7,26 @@ let current ctxt = (Raw_context.current_level ctxt).cycle +(* The context needs to keep data on [current_cycle - + (max_slashing_period - 1)], because potential misbehaviours from + that cycle are slashed at the end of [current_cycle], and the + slashing depends on baking rights at the misbehaviour level. Older + cycles than that are no longer needed. *) +let oldest_cycle_with_sampling_data ctxt = + match + Cycle_repr.sub (current ctxt) (Constants_repr.max_slashing_period - 1) + with + | None -> (* The context has data on all past cycles. *) Cycle_repr.root + | Some cycle -> cycle + +(* At the dawn of [new_cycle], sampling data can be cleared on cycles + that are strictly older than [new_cycle - (max_slashing_period - + 1)]. In practice, we only need to clear cycle [new_cycle - + max_slashing_period], because prior cycles have already been clean + up earlier. *) +let cycle_to_clear_of_sampling_data ~new_cycle = + Cycle_repr.sub new_cycle Constants_repr.max_slashing_period + let greatest_unstake_finalizable_cycle ctxt = Cycle_repr.sub (current ctxt) diff --git a/src/proto_alpha/lib_protocol/cycle_storage.mli b/src/proto_alpha/lib_protocol/cycle_storage.mli index 33aa4ba361b0..63a47e0b4fa0 100644 --- a/src/proto_alpha/lib_protocol/cycle_storage.mli +++ b/src/proto_alpha/lib_protocol/cycle_storage.mli @@ -10,6 +10,18 @@ (** Current cycle, that is, the cycle of the current level. *) val current : Raw_context.t -> Cycle_repr.t +(** The oldest cycle on which the context keeps sampling data. *) +val oldest_cycle_with_sampling_data : Raw_context.t -> Cycle_repr.t + +(** The cycle whose sampling data should be cleared when [new_cycle] + starts. + + Returns [None] when the computed cycle would be negative: this + means there are no cycles that should be cleared when [new_cycle] + starts. *) +val cycle_to_clear_of_sampling_data : + new_cycle:Cycle_repr.t -> Cycle_repr.t option + (** Highest cycle whose unstake requests are currently finalizable. Returns [None] when the computed cycle would be negative: this diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml index 9b7da835fedd..c1bbd6fe150b 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.ml +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -238,7 +238,7 @@ let select_new_distribution_at_cycle_end ctxt ~new_cycle = let clear_outdated_sampling_data ctxt ~new_cycle = let open Lwt_result_syntax in - match Cycle_repr.sub new_cycle Constants_repr.max_slashing_period with + match Cycle_storage.cycle_to_clear_of_sampling_data ~new_cycle with | None -> return ctxt | Some outdated_cycle -> let* ctxt = Delegate_sampler_state.remove_existing ctxt outdated_cycle in diff --git a/src/proto_alpha/lib_protocol/seed_storage.ml b/src/proto_alpha/lib_protocol/seed_storage.ml index bbea45563261..d0629de48746 100644 --- a/src/proto_alpha/lib_protocol/seed_storage.ml +++ b/src/proto_alpha/lib_protocol/seed_storage.ml @@ -218,18 +218,13 @@ let raw_for_cycle = Storage.Seed.For_cycle.get let for_cycle ctxt cycle = let open Lwt_result_syntax in let delay = Constants_storage.consensus_rights_delay ctxt in - let max_slashing_period = Constants_repr.max_slashing_period in let current_cycle = Cycle_storage.current ctxt in let latest = if Cycle_repr.(current_cycle = root) then Cycle_repr.add current_cycle (delay + 1) else Cycle_repr.add current_cycle delay in - let oldest = - match Cycle_repr.sub current_cycle (max_slashing_period - 1) with - | None -> Cycle_repr.root - | Some oldest -> oldest - in + let oldest = Cycle_storage.oldest_cycle_with_sampling_data ctxt in let*? () = error_unless Cycle_repr.(oldest <= cycle && cycle <= latest) diff --git a/src/proto_alpha/lib_protocol/stake_storage.ml b/src/proto_alpha/lib_protocol/stake_storage.ml index 2b88e615f67c..ae0b66580201 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.ml +++ b/src/proto_alpha/lib_protocol/stake_storage.ml @@ -232,8 +232,7 @@ let fold_on_active_delegates_with_minimal_stake_es ctxt ~f ~order ~init = let clear_at_cycle_end ctxt ~new_cycle = let open Lwt_result_syntax in - let max_slashing_period = Constants_repr.max_slashing_period in - match Cycle_repr.sub new_cycle max_slashing_period with + match Cycle_storage.cycle_to_clear_of_sampling_data ~new_cycle with | None -> return ctxt | Some cycle_to_clear -> let* ctxt = -- GitLab From 0d4f58a4e4b92916108d9654ee1f10bc673ce041 Mon Sep 17 00:00:00 2001 From: Diane Gallois-Wong Date: Fri, 13 Dec 2024 11:52:53 +0100 Subject: [PATCH 3/6] Proto: replace max_slashing_period with two constants --- .../lib_protocol/alpha_context.mli | 4 +- .../lib_protocol/already_denounced_storage.ml | 8 ++- .../already_denounced_storage.mli | 5 +- .../lib_protocol/constants_repr.ml | 26 +++++--- .../lib_protocol/constants_repr.mli | 63 +++++++++++++++---- .../lib_protocol/constants_storage.ml | 4 +- src/proto_alpha/lib_protocol/cycle_storage.ml | 6 +- .../dal_already_denounced_storage.ml | 8 ++- .../dal_already_denounced_storage.mli | 5 +- .../lib_protocol/dal_slot_storage.ml | 2 +- src/proto_alpha/lib_protocol/raw_context.ml | 4 +- .../lib_protocol/test/helpers/block.ml | 4 +- .../test/helpers/error_helpers.ml | 2 +- .../lib_protocol/test/helpers/scenario_op.ml | 4 +- .../test/helpers/slashing_helpers.ml | 2 +- .../lib_protocol/test/helpers/state.ml | 5 +- .../test/helpers/tez_staking_helpers.ml | 7 +-- .../consensus/test_double_attestation.ml | 13 ++-- .../consensus/test_double_baking.ml | 5 +- .../consensus/test_double_preattestation.ml | 3 +- .../consensus/test_frozen_deposits.ml | 10 +-- .../test/integration/consensus/test_seed.ml | 2 +- .../integration/test_scenario_slashing.ml | 18 +++--- .../test/integration/test_scenario_stake.ml | 5 +- .../validate/generator_descriptors.ml | 2 +- .../test/unit/test_staking_operations.ml | 3 +- src/proto_alpha/lib_protocol/validate.ml | 11 ++-- 27 files changed, 139 insertions(+), 92 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 86500a920c66..763383aa81d3 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -824,7 +824,9 @@ module Constants : sig val michelson_maximum_type_size : int - val max_slashing_period : int + val denunciation_period : int + + val slashing_delay : int val sc_rollup_message_size_limit : int diff --git a/src/proto_alpha/lib_protocol/already_denounced_storage.ml b/src/proto_alpha/lib_protocol/already_denounced_storage.ml index 685aa851449f..ccafdb900b8f 100644 --- a/src/proto_alpha/lib_protocol/already_denounced_storage.ml +++ b/src/proto_alpha/lib_protocol/already_denounced_storage.ml @@ -78,6 +78,12 @@ let add_denunciation ctxt delegate (level : Level_repr.t) round kind = return (ctxt, already_denounced) let clear_outdated_cycle ctxt ~new_cycle = - match Cycle_repr.(sub new_cycle Constants_repr.max_slashing_period) with + (* Misbehaviours from cycles [new_cycle - denunciation_period] and + higher might still be denounced during [new_cycle], so we need to + keep known denunciations on them. Anything older than that can be + discarded. In practice, we only need to clear cycle [new_cycle - + denunciation_period - 1], because prior cycles have already been + cleaned up earlier. *) + match Cycle_repr.(sub new_cycle (Constants_repr.denunciation_period + 1)) with | None -> Lwt.return ctxt | Some outdated_cycle -> Storage.Already_denounced.clear (ctxt, outdated_cycle) diff --git a/src/proto_alpha/lib_protocol/already_denounced_storage.mli b/src/proto_alpha/lib_protocol/already_denounced_storage.mli index a7adfb5b049d..ca133b01ae5a 100644 --- a/src/proto_alpha/lib_protocol/already_denounced_storage.mli +++ b/src/proto_alpha/lib_protocol/already_denounced_storage.mli @@ -55,8 +55,7 @@ val add_denunciation : Misbehaviour_repr.kind -> (Raw_context.t * bool) tzresult Lwt.t -(** Clear {!Storage.Already_denounced} for the cycle [new_cycle - - max_slashing_period]. Indeed, denunciations on events which - happened during this cycle are no longer allowed anyway. *) +(** Clear {!Storage.Already_denounced} for old cycles that we no + longer need denunciations for. *) val clear_outdated_cycle : Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t Lwt.t diff --git a/src/proto_alpha/lib_protocol/constants_repr.ml b/src/proto_alpha/lib_protocol/constants_repr.ml index 2f7846f66157..40cd5f9b8c5f 100644 --- a/src/proto_alpha/lib_protocol/constants_repr.ml +++ b/src/proto_alpha/lib_protocol/constants_repr.ml @@ -64,7 +64,14 @@ let michelson_maximum_type_size = 2001 mechanism (see {Context.Cache}). *) let cache_layout_size = 3 -let max_slashing_period = 2 +(* /!\ Several parts of the codebase may assume that + [denunciation_period = 1] and [slashing_delay = 1] **without being + parametrized using these constants**. So if they are ever modified, + the codebase needs to be examined extensively; searching for places + that use these constants is not enough. *) +let denunciation_period = 1 + +let slashing_delay = denunciation_period (* The {!Sc_rollups.wrapped_proof_encoding} uses unbounded sub-encodings. To avoid attacks through too large proofs and long decoding times on public @@ -119,7 +126,8 @@ let fixed_encoding = max_allowed_global_constant_depth, cache_layout_size, michelson_maximum_type_size ), - ( max_slashing_period, + ( denunciation_period, + slashing_delay, sc_max_wrapped_proof_binary_size, sc_rollup_message_size_limit, sc_rollup_max_number_of_messages_per_level ) )) @@ -133,7 +141,8 @@ let fixed_encoding = _max_allowed_global_constant_depth, _cache_layout_size, _michelson_maximum_type_size ), - ( _max_slashing_period, + ( _denunciation_period, + _slashing_delay, _sc_max_wrapped_proof_binary_size, _sc_rollup_message_size_limit, _sc_rollup_number_of_messages_per_level ) ) -> ()) @@ -149,8 +158,9 @@ let fixed_encoding = (req "max_allowed_global_constants_depth" int31) (req "cache_layout_size" uint8) (req "michelson_maximum_type_size" uint16)) - (obj4 - (req "max_slashing_period" uint8) + (obj5 + (req "denunciation_period" uint8) + (req "slashing_delay" uint8) (req "smart_rollup_max_wrapped_proof_binary_size" int31) (req "smart_rollup_message_size_limit" int31) (req "smart_rollup_max_number_of_messages_per_level" n))) @@ -358,14 +368,14 @@ let check_constants constants = error_unless Compare.Int.( constants.cache_stake_distribution_cycles - = constants.consensus_rights_delay + max_slashing_period + 1) + = constants.consensus_rights_delay + slashing_delay + 2) (Invalid_protocol_constants (Format.sprintf "We should have cache_stake_distribution_cycles (%d) = \ - consensus_rights_delay (%d) + max_slashing_period (%d) + 1." + consensus_rights_delay (%d) + slashing_delay (%d) + 2." constants.cache_stake_distribution_cycles constants.consensus_rights_delay - max_slashing_period)) + slashing_delay)) in let* () = error_unless diff --git a/src/proto_alpha/lib_protocol/constants_repr.mli b/src/proto_alpha/lib_protocol/constants_repr.mli index 363b3aa205e4..66e1990fccfe 100644 --- a/src/proto_alpha/lib_protocol/constants_repr.mli +++ b/src/proto_alpha/lib_protocol/constants_repr.mli @@ -77,17 +77,58 @@ val max_allowed_global_constant_depth : int *) val michelson_maximum_type_size : int -(** The max slashing period is the maximum number of cycles after which a - misbehaviour can be denounced, i.e. if a misbehaviour happened at cycle [c], - it will be rejected if it is denounced at cycle [c + max_slashing_period]. - Having [max_slashing_period] strictly smaller than 2 doesn't make sense. - Indeed, if a misbehaviour happens at the very last block of a cycle, it - couldn't be denounced. - [max_slashing_period = 2] leaves one cycle to denounce a misbehaviour in - the worst case, which is deemed enough. - Several parts of the codebase may use the fact that - [max_slashing_period = 2], so let's ensure it cannot be different. *) -val max_slashing_period : int +(** The number of **full cycles** after a misbehaviour during which it + can still be denounced. + + More precisely, the window for denouncing a misbehaviour that + happens during cycle [n] spans from the misbehaviour itself until + the end of cycle [n + denunciation_period]. So it indeed lasts + [denunciation_period] full cycles plus part of cycle [n]. + + (Technically, denunciations of the misbehaviour are actually + allowed from the beginning of cycle [n], even on earlier levels + than the misbehaviour's. But this is only relevant if someone + crafts double blocks or consensus operations in the future for some + reason. In practice, we expect misbehaviours to only happen around + the time when the chain reaches their level, which is why we don't + count cycle [n] itself as a full cycle for denouncing.) + + Note that we must have [denunciation_period >= 1], otherwise if a + misbehaviour happens at the very last block of a cycle, there is no + time frame to denounce it. + + Currently [denunciation_period = 1] which is considered enough + time for all misbehaviours to be denounced. + + /!\ Several parts of the codebase may assume that + [denunciation_period = 1] **without being parametrized using this + constant**. So if it is ever modified, the codebase needs to be + examined extensively; searching for places that use this constant + is not enough. *) +val denunciation_period : int + +(** The number of **full cycles** between a misbehaviour and its + slashing (assuming that it has been denounced). + + That is, a misbehaviour that happens during cycle [n] is always + slashed at the end of cycle [n + slashing_delay], regardless of + when it has been denounced (and if it has not been denounced by + then, then it is too late and it will never be slashed). + + Note that we must have [slashing_delay >= denunciation_period], + otherwise the chain would accept denunciations for which the + slashing can no longer happen because it is too late. + + Also note that to make the slashing possible, information on + baking rights for cycle [n] needs to be kept in the context until + the end of cycle [n + slashing_delay]. + + /!\ Several parts of the codebase may assume that + [slashing_delay = 1] **without being parametrized using this + constant**. So if it is ever modified, the codebase needs to be + examined extensively; searching for places that use this constant + is not enough. *) +val slashing_delay : int (** A size limit for {!Sc_rollups.wrapped_proof} binary encoding. *) val sc_max_wrapped_proof_binary_size : int diff --git a/src/proto_alpha/lib_protocol/constants_storage.ml b/src/proto_alpha/lib_protocol/constants_storage.ml index 9f738d950223..03396bf5bbcd 100644 --- a/src/proto_alpha/lib_protocol/constants_storage.ml +++ b/src/proto_alpha/lib_protocol/constants_storage.ml @@ -54,7 +54,7 @@ let issuance_modification_delay c = *) let adaptive_issuance_activation_delay c = let constants = Raw_context.constants c in - 1 + constants.consensus_rights_delay + Constants_repr.max_slashing_period + 1 + constants.consensus_rights_delay + Constants_repr.slashing_delay + 1 (** Tolerated inactivity period for delegates before being deactivated. *) let tolerated_inactivity_period c = @@ -74,7 +74,7 @@ let slashable_deposits_period c = constants.consensus_rights_delay let unstake_finalization_delay c = - slashable_deposits_period c + (Constants_repr.max_slashing_period - 1) + slashable_deposits_period c + Constants_repr.slashing_delay let blocks_per_cycle c = let constants = Raw_context.constants c in diff --git a/src/proto_alpha/lib_protocol/cycle_storage.ml b/src/proto_alpha/lib_protocol/cycle_storage.ml index 07147dadc0f2..7b5a5ed20916 100644 --- a/src/proto_alpha/lib_protocol/cycle_storage.ml +++ b/src/proto_alpha/lib_protocol/cycle_storage.ml @@ -13,9 +13,7 @@ let current ctxt = (Raw_context.current_level ctxt).cycle slashing depends on baking rights at the misbehaviour level. Older cycles than that are no longer needed. *) let oldest_cycle_with_sampling_data ctxt = - match - Cycle_repr.sub (current ctxt) (Constants_repr.max_slashing_period - 1) - with + match Cycle_repr.sub (current ctxt) Constants_repr.slashing_delay with | None -> (* The context has data on all past cycles. *) Cycle_repr.root | Some cycle -> cycle @@ -25,7 +23,7 @@ let oldest_cycle_with_sampling_data ctxt = max_slashing_period], because prior cycles have already been clean up earlier. *) let cycle_to_clear_of_sampling_data ~new_cycle = - Cycle_repr.sub new_cycle Constants_repr.max_slashing_period + Cycle_repr.sub new_cycle (Constants_repr.slashing_delay + 1) let greatest_unstake_finalizable_cycle ctxt = Cycle_repr.sub diff --git a/src/proto_alpha/lib_protocol/dal_already_denounced_storage.ml b/src/proto_alpha/lib_protocol/dal_already_denounced_storage.ml index bbfac05f8644..f0bad6f6d7a6 100644 --- a/src/proto_alpha/lib_protocol/dal_already_denounced_storage.ml +++ b/src/proto_alpha/lib_protocol/dal_already_denounced_storage.ml @@ -28,7 +28,13 @@ let add_denunciation ctxt delegate (level : Level_repr.t) slot_index = return (ctxt, already_denounced) let clear_outdated_cycle ctxt ~new_cycle = - match Cycle_repr.(sub new_cycle Constants_repr.max_slashing_period) with + (* Misbehaviours from cycles [new_cycle - denunciation_period] and + higher might still be denounced during [new_cycle], so we need to + keep known denunciations on them. Anything older than that can be + discarded. In practice, we only need to clear cycle [new_cycle - + denunciation_period - 1], because prior cycles have already been + cleaned up earlier. *) + match Cycle_repr.(sub new_cycle (Constants_repr.denunciation_period + 1)) with | None -> Lwt.return ctxt | Some outdated_cycle -> Storage.Dal_already_denounced.clear (ctxt, outdated_cycle) diff --git a/src/proto_alpha/lib_protocol/dal_already_denounced_storage.mli b/src/proto_alpha/lib_protocol/dal_already_denounced_storage.mli index ee66e8e57285..27ae5476ae54 100644 --- a/src/proto_alpha/lib_protocol/dal_already_denounced_storage.mli +++ b/src/proto_alpha/lib_protocol/dal_already_denounced_storage.mli @@ -51,8 +51,7 @@ val add_denunciation : Dal_slot_index_repr.t -> (Raw_context.t * bool) Lwt.t -(** Clear {!Storage.Dal_already_denounced} for the cycle [new_cycle - - max_slashing_period]. Indeed, denunciations on events which - happened during this cycle are no longer allowed anyway. *) +(** Clear {!Storage.Dal_already_denounced} for old cycles that we no + longer need denunciations for. *) val clear_outdated_cycle : Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t Lwt.t diff --git a/src/proto_alpha/lib_protocol/dal_slot_storage.ml b/src/proto_alpha/lib_protocol/dal_slot_storage.ml index 4c3f70fc2f05..98082dd1954a 100644 --- a/src/proto_alpha/lib_protocol/dal_slot_storage.ml +++ b/src/proto_alpha/lib_protocol/dal_slot_storage.ml @@ -123,7 +123,7 @@ let update_number_of_attested_slots ctxt num_attested_slots = let remove_old_headers ctxt ~published_level = let open Lwt_syntax in let denunciation_period = - Constants_repr.max_slashing_period + (Constants_repr.denunciation_period + 1) * (Int32.to_int @@ Constants_storage.blocks_per_cycle ctxt) in match Raw_level_repr.(sub published_level denunciation_period) with diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index fc1477ff1a2c..974aedf28499 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -1564,9 +1564,9 @@ let prepare_first_block ~level ~timestamp chain_id ctxt = initial_seed; cache_script_size; cache_stake_distribution_cycles = - consensus_rights_delay + Constants_repr.max_slashing_period + 1; + consensus_rights_delay + Constants_repr.slashing_delay + 2; cache_sampler_state_cycles = - consensus_rights_delay + Constants_repr.max_slashing_period + 1; + consensus_rights_delay + Constants_repr.slashing_delay + 2; dal; sc_rollup; zk_rollup; diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 3c1726dee472..5f99ea6e4ee4 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -662,9 +662,9 @@ let prepare_initial_context_params ?consensus_committee_size Option.value ~default:constants.aggregate_attestation aggregate_attestation in let cache_sampler_state_cycles = - consensus_rights_delay + Constants_repr.max_slashing_period + 1 + consensus_rights_delay + Constants_repr.slashing_delay + 2 and cache_stake_distribution_cycles = - consensus_rights_delay + Constants_repr.max_slashing_period + 1 + consensus_rights_delay + Constants_repr.slashing_delay + 2 in let constants = { diff --git a/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml index daf4515c5604..2599f87b5ee3 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/error_helpers.ml @@ -111,7 +111,7 @@ let expect_outdated_denunciation_state ~loc ~state errs = (Block.current_cycle_of_level ~blocks_per_cycle:state.State.constants.blocks_per_cycle ~current_level:(Protocol.Raw_level_repr.to_int32 ds.misbehaviour.level)) - (Protocol.Constants_repr.max_slashing_period - 1) + Protocol.Constants_repr.denunciation_period in let (kind : Protocol.Alpha_context.Misbehaviour.kind) = (* This conversion would not be needed if diff --git a/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml b/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml index 26295574d735..be59fc79a83f 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/scenario_op.ml @@ -465,8 +465,8 @@ let update_state_denunciation (block, state) return (state, denounced)) else if Cycle.( - add ds_cycle Protocol.Constants_repr.max_slashing_period - <= inclusion_cycle) + add ds_cycle Protocol.Constants_repr.denunciation_period + < inclusion_cycle) then ( (* The denunciation is too late and gets refused. *) Log.info ~color:event_color "Denunciation too late" ; diff --git a/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml index 6f8f9488a2b6..32933208f0dc 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/slashing_helpers.ml @@ -282,7 +282,7 @@ let apply_all_slashes_at_cycle_end current_cycle (block_before_slash : Block.t) in let open Protocol.Alpha_context in let misb_slashing_cycle = - Cycle.add misb_cycle (Constants.max_slashing_period - 1) + Cycle.add misb_cycle Constants.slashing_delay in if Cycle.(equal misb_slashing_cycle current_cycle) then (* Slash now *) diff --git a/src/proto_alpha/lib_protocol/test/helpers/state.ml b/src/proto_alpha/lib_protocol/test/helpers/state.ml index aa78f5f87a47..6d2ca3c6d1e1 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/state.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/state.ml @@ -40,9 +40,8 @@ let param_wait state = state.constants.delegate_parameters_activation_delay + 1 (** Expected number of cycles before staking unstaked funds get unfrozen *) let unstake_wait state = - let pc = state.constants.consensus_rights_delay in - let msp = Protocol.Constants_repr.max_slashing_period in - pc + msp + state.constants.consensus_rights_delay + + Protocol.Constants_repr.slashing_delay + 1 (** From a name, returns the corresponding account *) let find_account (account_name : string) (state : t) : account_state = diff --git a/src/proto_alpha/lib_protocol/test/helpers/tez_staking_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/tez_staking_helpers.ml index 52d2b3b80e1a..c8f5d6853aa9 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/tez_staking_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/tez_staking_helpers.ml @@ -463,11 +463,8 @@ module Unstaked_frozen = struct ({cycle; requests = _; initial; current; slash_pct = old_slash_pct} as r) -> - if - Cycle.( - cycle > slashed_cycle - || add cycle slashable_deposits_period < slashed_cycle) - then (r, (Tez.zero, Tez.zero)) + if Cycle.(add cycle slashable_deposits_period < slashed_cycle) then + (r, (Tez.zero, Tez.zero)) else let new_current, burnt, rewarded = apply_slash_to_current cst pct initial current diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml index 41013b9227db..8b5c7f2834aa 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_attestation.ml @@ -355,11 +355,11 @@ let test_two_double_attestation_evidences_leadsto_no_bake () = | Validate_errors.Consensus.Forbidden_delegate _ -> true | _ -> false) in - (* Check that all frozen deposits have been slashed at the end of the cycle. *) + (* Bake until the slashing and check the frozen deposits. *) let* b = Block.bake_until_n_cycle_end ~policy:(By_account baker) - Constants_repr.max_slashing_period + (Constants.slashing_delay + 1) blk_with_evidence2 in let* slashing_pct1 = @@ -604,20 +604,15 @@ let test_too_early_double_attestation_evidence () = let test_too_late_double_attestation_evidence () = let open Lwt_result_syntax in let* genesis, _contracts = Context.init2 ~consensus_threshold:0 () in - let max_slashing_period = Constants.max_slashing_period in - let* Constants.{parametric = {blocks_per_cycle; _}; _} = - Context.get_constants (B genesis) - in let* blk_1, blk_2 = block_fork genesis in let* blk_a = Block.bake blk_1 in let* blk_b = Block.bake blk_2 in let* attestation_a = Op.raw_attestation blk_a in let* attestation_b = Op.raw_attestation blk_b in let* blk = - Block.bake_n - ((max_slashing_period * Int32.to_int blocks_per_cycle) + 1) - blk_a + Block.bake_until_n_cycle_end (Constants.denunciation_period + 1) blk_a in + let* blk = Block.bake blk in double_attestation (B blk) attestation_a attestation_b |> fun operation -> let*! res = Block.bake ~operation blk in Assert.proto_error ~loc:__LOC__ res (function diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml index fe0fa484b1bb..0504b044b0d1 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml @@ -518,10 +518,11 @@ let test_too_early_double_baking_evidence () = --, it is not possible to create a double baking operation anymore. *) let test_too_late_double_baking_evidence () = let open Lwt_result_syntax in - let max_slashing_period = Constants.max_slashing_period in let* b, contracts = Context.init2 ~consensus_threshold:0 () in let* blk_a, blk_b = block_fork ~policy:(By_round 0) contracts b in - let* blk = Block.bake_until_n_cycle_end max_slashing_period blk_a in + let* blk = + Block.bake_until_n_cycle_end (Constants.denunciation_period + 1) blk_a + in double_baking (B blk) blk_a.header blk_b.header |> fun operation -> let*! res = Block.bake ~operation blk in Assert.proto_error ~loc:__LOC__ res (function diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preattestation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preattestation.ml index eb407ddb4705..12675911009b 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preattestation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preattestation.ml @@ -93,11 +93,10 @@ end = struct let max_slashing_period () = let open Lwt_result_syntax in let* genesis, _contract = Context.init1 ~consensus_threshold:0 () in - let max_slashing_period = Constants.max_slashing_period in let* {parametric = {blocks_per_cycle; _}; _} = Context.get_constants (B genesis) in - return (max_slashing_period * Int32.to_int blocks_per_cycle) + return ((Constants.slashing_delay + 1) * Int32.to_int blocks_per_cycle) let already_denounced loc res = Assert.proto_error ~loc res (function diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_frozen_deposits.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_frozen_deposits.ml index 4598da5bb42e..fb73b608d54f 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_frozen_deposits.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_frozen_deposits.ml @@ -232,7 +232,7 @@ let test_limit_with_overdelegation () = Assert.equal_tez ~loc:__LOC__ frozen_deposits expected_new_frozen_deposits in let cycles_to_bake = - 2 * (constants.consensus_rights_delay + Constants.max_slashing_period) + 2 * (constants.consensus_rights_delay + Constants.slashing_delay + 1) in let rec loop b n = if n = 0 then return b @@ -424,7 +424,7 @@ let test_deposits_after_stake_removal () = (* the frozen deposits for account1 do not change until [preserved cycles + max_slashing_period] are baked (-1 because we already baked a cycle) *) let* b = - loop b (constants.consensus_rights_delay + Constants.max_slashing_period - 1) + loop b (constants.consensus_rights_delay + Constants.slashing_delay) in (* and still after preserved cycles + max_slashing_period, the frozen_deposits for account1 won't reflect the decrease in account1's active stake @@ -459,7 +459,7 @@ let test_frozen_deposits_with_deactivation () = in let last_cycle_with_deposits = last_active_cycle + constants.consensus_rights_delay - + Constants.max_slashing_period + + Constants.slashing_delay + 1 (* according to [Delegate_storage.freeze_deposits] *) in let cycles_to_bake = @@ -545,7 +545,7 @@ let test_frozen_deposits_with_delegation () = Assert.equal_tez ~loc:__LOC__ new_frozen_deposits initial_frozen_deposits in let cycles_to_bake = - 2 * (constants.consensus_rights_delay + Constants.max_slashing_period) + 2 * (constants.consensus_rights_delay + Constants.slashing_delay + 1) in let rec loop b n = if n = 0 then return b @@ -644,7 +644,7 @@ let test_frozen_deposits_with_overdelegation () = expected_new_frozen_deposits in let cycles_to_bake = - 2 * (constants.consensus_rights_delay + Constants.max_slashing_period) + 2 * (constants.consensus_rights_delay + Constants.slashing_delay + 1) in let* frozen_deposits = Context.Delegate.current_frozen_deposits (B b) account1 diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml index 80b9145f9a05..c98082f2f986 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml @@ -599,7 +599,7 @@ let test_cycle_bounds () = let open Lwt_result_syntax in let* b, _accounts = Context.init1 ~consensus_threshold:0 () in let* csts = Context.get_constants (B b) in - let past_offset = Constants_repr.max_slashing_period - 1 in + let past_offset = Constants_repr.slashing_delay in let future_offset = csts.parametric.consensus_rights_delay in let open Alpha_context.Cycle in let expected_error_message direction current_cycle = diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml index 34da0b0be23a..f6376f38348c 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_slashing.ml @@ -114,6 +114,9 @@ let test_multiple_misbehaviors = Empty [1; 3] +let wait_for_slashing = + wait_n_cycles (Protocol.Constants_repr.slashing_delay + 1) + let check_is_forbidden ~loc baker = assert_failure ~expected_error:(fun (_, state) errs -> @@ -220,8 +223,7 @@ let test_delegate_forbidden = --> wait_n_cycles_f crd --> check_is_not_forbidden "delegate" |+ Tag "Is not forbidden after a denunciation is outdated" - --> double_attest "delegate" - --> wait_n_cycles Protocol.Constants_repr.max_slashing_period + --> double_attest "delegate" --> wait_for_slashing --> assert_failure ~expected_error:(fun (_block, state) errs -> Error_helpers.expect_outdated_denunciation_state @@ -324,7 +326,7 @@ let test_slash_timing = --> List.fold_left (fun acc i -> acc |+ Tag (string_of_int i ^ " cycles lag") --> wait_n_cycles i) - (wait_n_cycles Protocol.Constants_repr.max_slashing_period) + wait_for_slashing [3; 4; 5; 6] --> double_bake "delegate" --> exclude_bakers ["delegate"] @@ -340,7 +342,7 @@ let test_no_shortcut_for_cheaters = --> stake "delegate" (Amount (Tez.of_mutez 1_800_000_000_000L)) --> next_cycle --> double_bake "delegate" --> make_denunciations () --> set_baker "bootstrap1" (* exclude_bakers ["delegate"] *) - --> wait_n_cycles Protocol.Constants_repr.max_slashing_period + --> wait_for_slashing --> snapshot_balances "init" ["delegate"] --> unstake "delegate" amount --> (List.fold_left @@ -412,7 +414,7 @@ let test_slash_correct_amount_after_stake_from_unstake = ~unstaked_finalizable:Tez.zero --> exclude_bakers ["delegate"] --> double_bake "delegate" --> make_denunciations () - --> wait_n_cycles Protocol.Constants_repr.max_slashing_period + --> wait_for_slashing --> if cycles_to_wait > consensus_rights_delay then (* The misbehaviour happens at least two full cycles after @@ -544,8 +546,7 @@ let test_mega_slash = --> (* The "incident" *) let incident = - double_attest "delegate" --> make_denunciations () - --> wait_n_cycles Protocol.Constants_repr.max_slashing_period + double_attest "delegate" --> make_denunciations () --> wait_for_slashing (* We check stakers can still unstake and change delegates *) --> assert_success (unstake "staker1" Half) --> assert_success (unstake "staker1" All) @@ -597,8 +598,7 @@ let test_mega_slash = loop 3 incident (* but 4 will. *) --> double_attest "delegate" - --> make_denunciations () - --> wait_n_cycles Protocol.Constants_repr.max_slashing_period + --> make_denunciations () --> wait_for_slashing (* We check stakers can still unstake and finalize, despite everything *) --> assert_success (unstake "staker1" Half diff --git a/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml b/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml index 5b6eafccd953..2fab13c36ab1 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_scenario_stake.ml @@ -82,10 +82,7 @@ let wait_for_unfreeze_and_check wait = --> next_cycle --> assert_failure_in_check_snapshot_balances ~loc:__LOC__ "wait snap" -let unstake_wait (_, state) = - let crd = state.State.constants.consensus_rights_delay in - let msp = Protocol.Constants_repr.max_slashing_period in - crd + msp +let unstake_wait (_, state) = State.unstake_wait state let finalize staker = assert_failure_in_check_balance_field diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml index 0d94f68465ca..3fff4b2463db 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml @@ -428,7 +428,7 @@ let entrapment_descriptor = let double_baking_descriptor = { parameters = Fun.id; - required_cycle = (fun _params -> Constants_repr.max_slashing_period); + required_cycle = (fun _params -> Constants.slashing_delay + 1); required_block = (fun _ -> 0); prelude = ( From 2, diff --git a/src/proto_alpha/lib_protocol/test/unit/test_staking_operations.ml b/src/proto_alpha/lib_protocol/test/unit/test_staking_operations.ml index 0e535f923cf9..37df676de8c3 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_staking_operations.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_staking_operations.ml @@ -359,8 +359,7 @@ let fee_low_spendable_balance_non_zero_finalizable_unstake ~self_delegate_staker let* b = Block.bake ~operation:unstake b in let* b = Block.bake_until_n_cycle_end - (constants.consensus_rights_delay - + Protocol.Constants_repr.max_slashing_period) + (constants.consensus_rights_delay + Constants.slashing_delay + 1) b in let* () = diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index d36bec58a721..0bf8be54a354 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -1303,9 +1303,8 @@ module Anonymous = struct let open Result_syntax in let current_cycle = vi.current_level.cycle in let given_cycle = (Level.from_raw vi.ctxt given_level).cycle in - let max_slashing_period = Constants.max_slashing_period in - let last_slashable_cycle = - Cycle.add given_cycle (max_slashing_period - 1) + let last_denunciable_cycle = + Cycle.add given_cycle Constants.denunciation_period in let* () = error_unless @@ -1319,14 +1318,14 @@ module Anonymous = struct {level = given_level; current = vi.current_level.level}) in error_unless - Cycle.(last_slashable_cycle >= current_cycle) + Cycle.(last_denunciable_cycle >= current_cycle) (match kind with | `Consensus_denounciation kind -> Outdated_denunciation - {kind; level = given_level; last_cycle = last_slashable_cycle} + {kind; level = given_level; last_cycle = last_denunciable_cycle} | `Dal_denounciation -> Outdated_dal_denunciation - {level = given_level; last_cycle = last_slashable_cycle}) + {level = given_level; last_cycle = last_denunciable_cycle}) let check_double_attesting_evidence (type kind) vi (op1 : kind Kind.consensus Operation.t) -- GitLab From 90636d002e578f6c9ed81e130a9cd41c1243bf4e Mon Sep 17 00:00:00 2001 From: Diane Gallois-Wong Date: Fri, 13 Dec 2024 17:48:55 +0100 Subject: [PATCH 4/6] Proto: better variable names and comments --- .../delegate_slashed_deposits_storage.ml | 18 ++++--- .../lib_protocol/unstake_requests_storage.ml | 53 +++++++++++-------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml index 9d6deb9d05e7..2de3199c8acd 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -202,18 +202,18 @@ let apply_block_denunciations ctxt current_cycle block_denunciations_map = denunciations_map acc -> let ctxt, balance_updates = acc in - let level = + let misbehaviour_level = Level_repr.level_from_raw ~cycle_eras:(Raw_context.cycle_eras ctxt) raw_level in - let misbehaviour_cycle = level.cycle in + let misbehaviour_cycle = misbehaviour_level.cycle in let denunciations = Signature.Public_key_hash.Map.bindings denunciations_map in let denounced = List.map fst denunciations in let* ctxt, slashing_percentage = - Slash_percentage.get ctxt ~kind ~level denounced + Slash_percentage.get ctxt ~kind ~level:misbehaviour_level denounced in let+ ctxt, balance_updates = List.fold_left_es @@ -258,11 +258,13 @@ let apply_block_denunciations ctxt current_cycle block_denunciations_map = in let previous_total_slashing_percentage = - Storage.Slashed_deposits_history.get level.cycle slash_history + Storage.Slashed_deposits_history.get + misbehaviour_cycle + slash_history in let slash_history = Storage.Slashed_deposits_history.add - level.cycle + misbehaviour_cycle slashing_percentage slash_history in @@ -270,7 +272,9 @@ let apply_block_denunciations ctxt current_cycle block_denunciations_map = Storage.Slashed_deposits.add ctxt delegate slash_history in let new_total_slashing_percentage = - Storage.Slashed_deposits_history.get level.cycle slash_history + Storage.Slashed_deposits_history.get + misbehaviour_cycle + slash_history in (* We do not slash above 100%: if the slashing percentage would make the total sum of the slashing history above 100%, we rectify @@ -403,7 +407,7 @@ let apply_block_denunciations ctxt current_cycle block_denunciations_map = |> Option.value ~default:Cycle_repr.root in let slashable_cycles = - Cycle_repr.(oldest_slashable_cycle ---> misbehaviour_cycle) + Cycle_repr.(oldest_slashable_cycle ---> current_cycle) in List.fold_left_es (fun (to_burn, to_reward) cycle -> diff --git a/src/proto_alpha/lib_protocol/unstake_requests_storage.ml b/src/proto_alpha/lib_protocol/unstake_requests_storage.ml index f12c85ce92ed..765a978a1ae1 100644 --- a/src/proto_alpha/lib_protocol/unstake_requests_storage.ml +++ b/src/proto_alpha/lib_protocol/unstake_requests_storage.ml @@ -91,19 +91,29 @@ let prepared_finalize_unstake_encoding : (req "finalizable" finalizable_encoding) (req "unfinalizable" stored_requests_encoding)) -let apply_slashes ~slashable_deposits_period slashing_history ~from_cycle amount - = - let first_cycle_to_apply_slash = from_cycle in - let last_cycle_to_apply_slash = - Cycle_repr.add from_cycle slashable_deposits_period +let apply_slashes ctxt slashing_history ~unstake_request_cycle amount = + let slashable_deposits_period = + Constants_storage.slashable_deposits_period ctxt in (* [slashing_history] is sorted so slashings always happen in the same order. *) List.fold_left - (fun remain (slashing_cycle, slashing_percentage) -> + (fun remain (misbehaviour_cycle, slashing_percentage) -> if Cycle_repr.( - slashing_cycle >= first_cycle_to_apply_slash - && slashing_cycle <= last_cycle_to_apply_slash) + (* The unstake request is recent enough to be slashed for + the misbehaviour, because its funds were still staked at + the time the baking rights for the misbehaviour level + were computed. *) + misbehaviour_cycle + <= add unstake_request_cycle slashable_deposits_period + && (* The unstake request is old enough that it already + existed when the misbehaviour was slashed (or will be + slashed if it hasn't happened yet). Indeed, unstake + requests created after the slashing contain funds that + have already been slashed while they were still staked + so they should not be slashed again. *) + unstake_request_cycle + <= add misbehaviour_cycle Constants_repr.slashing_delay) then Tez_repr.( sub_opt @@ -117,9 +127,6 @@ let apply_slashes ~slashable_deposits_period slashing_history ~from_cycle amount let prepare_finalize_unstake_uncarbonated ctxt ~check_delegate_of_unfinalizable_requests contract = let open Lwt_result_syntax in - let slashable_deposits_period = - Constants_storage.slashable_deposits_period ctxt - in let* requests_opt = Storage.Contract.Unstake_requests.find ctxt contract in match requests_opt with | None | Some {delegate = _; requests = []} -> return_none @@ -155,16 +162,19 @@ let prepare_finalize_unstake_uncarbonated ctxt let finalizable, unfinalizable_requests = List.partition_map (fun request -> - let request_cycle, request_amount = request in - if Cycle_repr.(request_cycle <= greatest_finalizable_cycle) then + let unstake_request_cycle, request_amount = request in + if + Cycle_repr.( + unstake_request_cycle <= greatest_finalizable_cycle) + then let new_amount = apply_slashes - ~slashable_deposits_period + ctxt slashing_history - ~from_cycle:request_cycle + ~unstake_request_cycle request_amount in - Left (delegate, request_cycle, new_amount) + Left (delegate, unstake_request_cycle, new_amount) else Right request) requests in @@ -440,9 +450,6 @@ module For_RPC = struct let apply_slash_to_unstaked_unfinalizable ctxt {requests; delegate} = let open Lwt_result_syntax in - let slashable_deposits_period = - Constants_storage.slashable_deposits_period ctxt - in let* slashing_history_opt = Storage.Slashed_deposits.find ctxt delegate in let slashing_history = Option.value slashing_history_opt ~default:[] in @@ -466,14 +473,14 @@ module For_RPC = struct in List.map_es - (fun (request_cycle, request_amount) -> + (fun (unstake_request_cycle, request_amount) -> let new_amount = apply_slashes - ~slashable_deposits_period + ctxt slashing_history - ~from_cycle:request_cycle + ~unstake_request_cycle request_amount in - return (request_cycle, new_amount)) + return (unstake_request_cycle, new_amount)) requests end -- GitLab From 56c66d09498c29657079e4fe455aaa1de339438c Mon Sep 17 00:00:00 2001 From: Marina Polubelova Date: Mon, 16 Dec 2024 11:29:00 +0100 Subject: [PATCH 5/6] Proto/kaitai: update structs --- client-libs/kaitai-struct-files/files/alpha__constants.ksy | 4 +++- .../kaitai-struct-files/files/alpha__constants__fixed.ksy | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/client-libs/kaitai-struct-files/files/alpha__constants.ksy b/client-libs/kaitai-struct-files/files/alpha__constants.ksy index 1540d0e6cae1..61e57943e453 100644 --- a/client-libs/kaitai-struct-files/files/alpha__constants.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__constants.ksy @@ -236,7 +236,9 @@ seq: type: u1 - id: michelson_maximum_type_size type: u2be -- id: max_slashing_period +- id: denunciation_period + type: u1 +- id: slashing_delay type: u1 - id: smart_rollup_max_wrapped_proof_binary_size type: int31 diff --git a/client-libs/kaitai-struct-files/files/alpha__constants__fixed.ksy b/client-libs/kaitai-struct-files/files/alpha__constants__fixed.ksy index 1ccd49c5892d..4b21a14a28f0 100644 --- a/client-libs/kaitai-struct-files/files/alpha__constants__fixed.ksy +++ b/client-libs/kaitai-struct-files/files/alpha__constants__fixed.ksy @@ -43,7 +43,9 @@ seq: type: u1 - id: michelson_maximum_type_size type: u2be -- id: max_slashing_period +- id: denunciation_period + type: u1 +- id: slashing_delay type: u1 - id: smart_rollup_max_wrapped_proof_binary_size type: int31 -- GitLab From 89a8a8293422e8f6a11de87dbc2f9189d658e8f3 Mon Sep 17 00:00:00 2001 From: Marina Polubelova Date: Mon, 16 Dec 2024 11:29:49 +0100 Subject: [PATCH 6/6] Tezt/Tests: reset regressions --- ...pha- (mode client) RPC regression tests- misc_protocol.out | 4 ++-- ...lpha- (mode light) RPC regression tests- misc_protocol.out | 4 ++-- ...lpha- (mode proxy) RPC regression tests- misc_protocol.out | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out index 8cca5af35264..916f7c38bd47 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out @@ -5,8 +5,8 @@ "max_proposals_per_delegate": 20, "max_micheline_node_count": 50000, "max_micheline_bytes_limit": 50000, "max_allowed_global_constants_depth": 10000, "cache_layout_size": 3, - "michelson_maximum_type_size": 2001, "max_slashing_period": 2, - "smart_rollup_max_wrapped_proof_binary_size": 30000, + "michelson_maximum_type_size": 2001, "denunciation_period": 1, + "slashing_delay": 1, "smart_rollup_max_wrapped_proof_binary_size": 30000, "smart_rollup_message_size_limit": 4096, "smart_rollup_max_number_of_messages_per_level": "1000000", "consensus_rights_delay": 2, "blocks_preservation_cycles": 1, diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out index 00a9cf5522d7..b51062be9891 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out @@ -5,8 +5,8 @@ "max_proposals_per_delegate": 20, "max_micheline_node_count": 50000, "max_micheline_bytes_limit": 50000, "max_allowed_global_constants_depth": 10000, "cache_layout_size": 3, - "michelson_maximum_type_size": 2001, "max_slashing_period": 2, - "smart_rollup_max_wrapped_proof_binary_size": 30000, + "michelson_maximum_type_size": 2001, "denunciation_period": 1, + "slashing_delay": 1, "smart_rollup_max_wrapped_proof_binary_size": 30000, "smart_rollup_message_size_limit": 4096, "smart_rollup_max_number_of_messages_per_level": "1000000", "consensus_rights_delay": 2, "blocks_preservation_cycles": 1, diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out index 85a30aecd80e..f1b791e57393 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out @@ -5,8 +5,8 @@ "max_proposals_per_delegate": 20, "max_micheline_node_count": 50000, "max_micheline_bytes_limit": 50000, "max_allowed_global_constants_depth": 10000, "cache_layout_size": 3, - "michelson_maximum_type_size": 2001, "max_slashing_period": 2, - "smart_rollup_max_wrapped_proof_binary_size": 30000, + "michelson_maximum_type_size": 2001, "denunciation_period": 1, + "slashing_delay": 1, "smart_rollup_max_wrapped_proof_binary_size": 30000, "smart_rollup_message_size_limit": 4096, "smart_rollup_max_number_of_messages_per_level": "1000000", "consensus_rights_delay": 2, "blocks_preservation_cycles": 1, -- GitLab