From 83daefc854326e651c899d59a6bcc17d425c8e71 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 14 Jan 2025 16:41:16 +0100 Subject: [PATCH 1/6] Alpha/DAL: add misbehavior and denounciation representations --- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 2 ++ .../lib_protocol/dal_denunciations_repr.ml | 31 +++++++++++++++++ .../lib_protocol/dal_denunciations_repr.mli | 34 +++++++++++++++++++ .../lib_protocol/dal_misbehaviour_repr.ml | 21 ++++++++++++ .../lib_protocol/dal_misbehaviour_repr.mli | 21 ++++++++++++ src/proto_alpha/lib_protocol/dune | 8 +++++ 6 files changed, 117 insertions(+) create mode 100644 src/proto_alpha/lib_protocol/dal_denunciations_repr.ml create mode 100644 src/proto_alpha/lib_protocol/dal_denunciations_repr.mli create mode 100644 src/proto_alpha/lib_protocol/dal_misbehaviour_repr.ml create mode 100644 src/proto_alpha/lib_protocol/dal_misbehaviour_repr.mli diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index b304bdd315b7..d2ebc0e78809 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -129,6 +129,8 @@ "Staking_parameters_repr", "Misbehaviour_repr", "Denunciations_repr", + "Dal_misbehaviour_repr", + "Dal_denunciations_repr", "Raw_context_intf", "Raw_context", diff --git a/src/proto_alpha/lib_protocol/dal_denunciations_repr.ml b/src/proto_alpha/lib_protocol/dal_denunciations_repr.ml new file mode 100644 index 000000000000..39e896bf710f --- /dev/null +++ b/src/proto_alpha/lib_protocol/dal_denunciations_repr.ml @@ -0,0 +1,31 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +type item = { + operation_hash : Operation_hash.t; + rewarded : Signature.public_key_hash; + misbehaviour : Dal_misbehaviour_repr.t; +} + +let item_encoding = + let open Data_encoding in + conv + (fun {operation_hash; rewarded; misbehaviour} -> + (operation_hash, rewarded, misbehaviour)) + (fun (operation_hash, rewarded, misbehaviour) -> + {operation_hash; rewarded; misbehaviour}) + (obj3 + (req "operation_hash" Operation_hash.encoding) + (req "rewarded" Signature.Public_key_hash.encoding) + (req "misbehaviour" Dal_misbehaviour_repr.encoding)) + +type t = item list + +let encoding = Data_encoding.list item_encoding + +let add operation_hash rewarded misbehaviour list = + list @ [{operation_hash; rewarded; misbehaviour}] diff --git a/src/proto_alpha/lib_protocol/dal_denunciations_repr.mli b/src/proto_alpha/lib_protocol/dal_denunciations_repr.mli new file mode 100644 index 000000000000..46ee4ce76d84 --- /dev/null +++ b/src/proto_alpha/lib_protocol/dal_denunciations_repr.mli @@ -0,0 +1,34 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +(** Internal representation of a pending DAL denunciation, meaning that a + denunciation operation has been observed in an applied block, but + the corresponding slashing has not happened yet. + + Note: the public key hash of the culprit doesn't appear in this + type because it is used as key to store the list of a culprit's + items (see type [t] below) in the context. *) +type item = { + operation_hash : Operation_hash.t; + rewarded : Signature.public_key_hash; + misbehaviour : Dal_misbehaviour_repr.t; +} + +(** List of all pending denunciations about the same culprit. *) +type t = item list + +val item_encoding : item Data_encoding.t + +val encoding : t Data_encoding.t + +(** Append a new pending denunciation to the end of the given list. *) +val add : + Operation_hash.t -> + Signature.public_key_hash -> + Dal_misbehaviour_repr.t -> + t -> + t diff --git a/src/proto_alpha/lib_protocol/dal_misbehaviour_repr.ml b/src/proto_alpha/lib_protocol/dal_misbehaviour_repr.ml new file mode 100644 index 000000000000..fd595aebcadc --- /dev/null +++ b/src/proto_alpha/lib_protocol/dal_misbehaviour_repr.ml @@ -0,0 +1,21 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +type t = {level : Raw_level_repr.t; slot_index : Dal_slot_index_repr.t} + +let compare a b = + Compare.or_else (Raw_level_repr.compare a.level b.level) @@ fun () -> + Dal_slot_index_repr.compare a.slot_index b.slot_index + +let encoding = + let open Data_encoding in + conv + (fun {level; slot_index} -> (level, slot_index)) + (fun (level, slot_index) -> {level; slot_index}) + (obj2 + (req "level" Raw_level_repr.encoding) + (req "slot_index" Dal_slot_index_repr.encoding)) diff --git a/src/proto_alpha/lib_protocol/dal_misbehaviour_repr.mli b/src/proto_alpha/lib_protocol/dal_misbehaviour_repr.mli new file mode 100644 index 000000000000..18fca9f9a860 --- /dev/null +++ b/src/proto_alpha/lib_protocol/dal_misbehaviour_repr.mli @@ -0,0 +1,21 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +(** Internal representation of a DAL misbehavior event used in + {!Dal_denunciations_repr.item}. + + Note: the culprit pkh doesn't appear as a field here because it is + typically used as a key when storing denunciation items in the + context. *) +type t = {level : Raw_level_repr.t; slot_index : Dal_slot_index_repr.t} + +val encoding : t Data_encoding.t + +(** Comparison function for DAL misbehaviours. + + Misbehaviours are ordered by increasing level, then increasing slot_index. *) +val compare : t -> t -> int diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index e1c9e1ae8d76..0a88d927e985 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -151,6 +151,8 @@ Staking_parameters_repr Misbehaviour_repr Denunciations_repr + Dal_misbehaviour_repr + Dal_denunciations_repr Raw_context_intf Raw_context Storage_costs_generated @@ -448,6 +450,8 @@ staking_parameters_repr.ml staking_parameters_repr.mli misbehaviour_repr.ml misbehaviour_repr.mli denunciations_repr.ml denunciations_repr.mli + dal_misbehaviour_repr.ml dal_misbehaviour_repr.mli + dal_denunciations_repr.ml dal_denunciations_repr.mli raw_context_intf.ml raw_context.ml raw_context.mli storage_costs_generated.ml @@ -747,6 +751,8 @@ staking_parameters_repr.ml staking_parameters_repr.mli misbehaviour_repr.ml misbehaviour_repr.mli denunciations_repr.ml denunciations_repr.mli + dal_misbehaviour_repr.ml dal_misbehaviour_repr.mli + dal_denunciations_repr.ml dal_denunciations_repr.mli raw_context_intf.ml raw_context.ml raw_context.mli storage_costs_generated.ml @@ -1030,6 +1036,8 @@ staking_parameters_repr.ml staking_parameters_repr.mli misbehaviour_repr.ml misbehaviour_repr.mli denunciations_repr.ml denunciations_repr.mli + dal_misbehaviour_repr.ml dal_misbehaviour_repr.mli + dal_denunciations_repr.ml dal_denunciations_repr.mli raw_context_intf.ml raw_context.ml raw_context.mli storage_costs_generated.ml -- GitLab From a2799603a512ea55adb63324cf4699a16905f0d1 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 14 Jan 2025 16:53:45 +0100 Subject: [PATCH 2/6] Alpha/DAL: add Dal_pending_denunciations_storage module --- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 1 + .../dal_pending_denunciations_storage.ml | 52 ++++++++++++++ .../dal_pending_denunciations_storage.mli | 68 +++++++++++++++++++ src/proto_alpha/lib_protocol/dune | 4 ++ 4 files changed, 125 insertions(+) create mode 100644 src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.ml create mode 100644 src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.mli diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index d2ebc0e78809..92b5efaca5ac 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -167,6 +167,7 @@ "Stake_storage", "Unstaked_frozen_deposits_storage", "Pending_denunciations_storage", + "Dal_pending_denunciations_storage", "Unstake_requests_storage", "Staking_pseudotokens_storage", diff --git a/src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.ml b/src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.ml new file mode 100644 index 000000000000..e6caa308bf5f --- /dev/null +++ b/src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.ml @@ -0,0 +1,52 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs. *) +(* *) +(*****************************************************************************) + +let find ctxt delegate = + let open Lwt_result_syntax in + let* denunciations_opt = + Storage.Dal_pending_denunciations.find ctxt delegate + in + return @@ Option.value denunciations_opt ~default:[] + +let add_denunciation ctxt ~misbehaving_delegate operation_hash + ~rewarded_delegate misbehaviour = + let open Lwt_result_syntax in + let* denunciations = find ctxt misbehaving_delegate in + let denunciations = + Dal_denunciations_repr.add + operation_hash + rewarded_delegate + misbehaviour + denunciations + in + let*! ctxt = + Storage.Dal_pending_denunciations.add + ctxt + misbehaving_delegate + denunciations + in + return ctxt + +let set_denunciations ctxt delegate denunciations = + match denunciations with + | [] -> Storage.Dal_pending_denunciations.remove ctxt delegate + | _ -> Storage.Dal_pending_denunciations.add ctxt delegate denunciations + +let has_pending_denunciations ctxt delegate = + (* we rely here on the fact that we never insert an empty list in the table *) + Storage.Dal_pending_denunciations.mem ctxt delegate + +let fold = Storage.Dal_pending_denunciations.fold + +let clear ctxt = Storage.Dal_pending_denunciations.clear ctxt + +module For_RPC = struct + let pending_denunciations_list ctxt = + let open Lwt_syntax in + let+ r = Storage.Dal_pending_denunciations.bindings ctxt in + List.map (fun (x, l) -> List.map (fun y -> (x, y)) l) r |> List.flatten +end diff --git a/src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.mli b/src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.mli new file mode 100644 index 000000000000..9463f394979e --- /dev/null +++ b/src/proto_alpha/lib_protocol/dal_pending_denunciations_storage.mli @@ -0,0 +1,68 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* Copyright (c) 2025 Nomadic Labs. *) +(* *) +(*****************************************************************************) + +(** This module deals with pending DAL denunciations before they are used to slash + delegates. + + This module is responsible for maintaining the table + {!Storage.Dal_pending_denunciations} + + In particular, it maintains the invariant that no key is pointing to an + empty denunciation list. +*) + +(** Returns the pending denunciations list of the given delegate. + It returns an empty list if none are registered. + *) +val find : + Raw_context.t -> + Signature.public_key_hash -> + Dal_denunciations_repr.item list tzresult Lwt.t + +(** Add a denunciation in the list of the given delegate *) +val add_denunciation : + Raw_context.t -> + misbehaving_delegate:Signature.public_key_hash -> + Operation_hash.t -> + rewarded_delegate:Signature.public_key_hash -> + Dal_misbehaviour_repr.t -> + Raw_context.t tzresult Lwt.t + +(** Set the denunciation list of the given delegate. + Previously set denunciations would be erased. +*) +val set_denunciations : + Raw_context.t -> + Signature.public_key_hash -> + Dal_denunciations_repr.t -> + Raw_context.t Lwt.t + +(** Tells if the given delegate has some pending denunciations *) +val has_pending_denunciations : + Raw_context.t -> Signature.public_key_hash -> bool Lwt.t + +(** See {!Storage.Dal_pending_denunciations.fold} *) +val fold : + Raw_context.t -> + order:[`Sorted | `Undefined] -> + init:'a -> + f: + (Signature.public_key_hash -> + Dal_denunciations_repr.item list -> + 'a -> + 'a Lwt.t) -> + 'a Lwt.t + +(** See {!Storage.Dal_pending_denunciations.clear} *) +val clear : Raw_context.t -> Raw_context.t Lwt.t + +module For_RPC : sig + (** Returns a list of all denunciations paired with the offending delegate pkh. *) + val pending_denunciations_list : + Raw_context.t -> + (Signature.public_key_hash * Dal_denunciations_repr.item) list Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 0a88d927e985..3f43a8f89ea5 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -184,6 +184,7 @@ Stake_storage Unstaked_frozen_deposits_storage Pending_denunciations_storage + Dal_pending_denunciations_storage Unstake_requests_storage Staking_pseudotokens_storage Contract_storage @@ -483,6 +484,7 @@ stake_storage.ml stake_storage.mli unstaked_frozen_deposits_storage.ml unstaked_frozen_deposits_storage.mli pending_denunciations_storage.ml pending_denunciations_storage.mli + dal_pending_denunciations_storage.ml dal_pending_denunciations_storage.mli unstake_requests_storage.ml unstake_requests_storage.mli staking_pseudotokens_storage.ml staking_pseudotokens_storage.mli contract_storage.ml contract_storage.mli @@ -784,6 +786,7 @@ stake_storage.ml stake_storage.mli unstaked_frozen_deposits_storage.ml unstaked_frozen_deposits_storage.mli pending_denunciations_storage.ml pending_denunciations_storage.mli + dal_pending_denunciations_storage.ml dal_pending_denunciations_storage.mli unstake_requests_storage.ml unstake_requests_storage.mli staking_pseudotokens_storage.ml staking_pseudotokens_storage.mli contract_storage.ml contract_storage.mli @@ -1069,6 +1072,7 @@ stake_storage.ml stake_storage.mli unstaked_frozen_deposits_storage.ml unstaked_frozen_deposits_storage.mli pending_denunciations_storage.ml pending_denunciations_storage.mli + dal_pending_denunciations_storage.ml dal_pending_denunciations_storage.mli unstake_requests_storage.ml unstake_requests_storage.mli staking_pseudotokens_storage.ml staking_pseudotokens_storage.mli contract_storage.ml contract_storage.mli -- GitLab From b28edd534c01763144139266c7b2f26b58ce2cc1 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 14 Jan 2025 16:54:36 +0100 Subject: [PATCH 3/6] Alpha/DAL: allow to store pending DAL denunciations --- src/proto_alpha/lib_protocol/storage.ml | 9 +++++++++ src/proto_alpha/lib_protocol/storage.mli | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 5d35d827f747..f16fe041284d 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -1118,6 +1118,15 @@ module Pending_denunciations = (Public_key_hash_index) (Denunciations_repr) +module Dal_pending_denunciations = + Make_indexed_data_storage + (Make_subcontext (Registered) (Raw_context) + (struct + let name = ["dal_denunciations"] + end)) + (Public_key_hash_index) + (Dal_denunciations_repr) + module Slashed_deposits = Make_indexed_data_storage (Make_subcontext (Registered) (Raw_context) diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 0e7b60543e8a..90db6c57d76e 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -473,6 +473,15 @@ module Pending_denunciations : and type key = Signature.public_key_hash and type value = Denunciations_repr.t +(** All denunciations of the current and previous cycles that will have an effect + (slashing, reward), i.e. all below 100%, deferred to the end of their + slashing period. *) +module Dal_pending_denunciations : + Indexed_data_storage + with type t := Raw_context.t + and type key = Signature.public_key_hash + and type value = Dal_denunciations_repr.t + (** History of slashed deposits: an associative list of cycles to slashed percentages. -- GitLab From 463366610497f09c941111cb13ffe166ae6022ce Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 14 Jan 2025 21:29:39 +0100 Subject: [PATCH 4/6] Alpha/DAL: record pending denounciations --- src/proto_alpha/lib_protocol/alpha_context.ml | 9 ++++++++- src/proto_alpha/lib_protocol/alpha_context.mli | 14 ++++++++++++++ src/proto_alpha/lib_protocol/apply.ml | 9 +++++++++ .../delegate_slashed_deposits_storage.ml | 10 ++++++++++ .../delegate_slashed_deposits_storage.mli | 10 ++++++++++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 1574be3a8b20..e28881c43455 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -168,7 +168,14 @@ module Dal = struct module Slots_history = Dal_slot_repr.History module Slots_storage = Dal_slot_storage - module Delegate = Dal_already_denounced_storage + module Misbehaviour = Dal_misbehaviour_repr + + module Delegate = struct + include Dal_already_denounced_storage + + let record_denunciation_as_pending = + Delegate_slashed_deposits_storage.record_dal_denunciation_as_pending + end end module Dal_errors = Dal_errors_repr diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index c106bc21fe74..bf585f5f573a 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -3033,6 +3033,11 @@ module Dal : sig Lwt.t end + (** This module re-exports definitions from {!Dal_misbehaviour_repr}. *) + module Misbehaviour : sig + type t = {level : Raw_level.t; slot_index : Slot_index.t} + end + (* This would normally be a part of {!Delegate}, but it is not because {!Dal.Slot_index} is defined after {!Delegate}. *) module Delegate : sig @@ -3047,6 +3052,15 @@ module Dal : sig (** See {!Dal_already_denounced_storage.is_already_denounced}. *) val is_already_denounced : context -> public_key_hash -> Level.t -> Slot_index.t -> bool Lwt.t + + (** See {!Delegate_slashed_deposits_storage.record_dal_denunciation_as_pending}. *) + val record_denunciation_as_pending : + context -> + operation_hash:Operation_hash.t -> + Misbehaviour.t -> + public_key_hash -> + rewarded:public_key_hash -> + context tzresult Lwt.t end end diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 7439bf1f11e1..6e7e0bcb9924 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2495,6 +2495,15 @@ let apply_contents_list (type kind) ctxt chain_id (mode : mode) in (* We could assert that the delegate has not been already denounced, given that the operation has already been validated. *) + let misbehaviour = Dal.Misbehaviour.{level = raw_level; slot_index} in + let* ctxt = + Dal.Delegate.record_denunciation_as_pending + ctxt + ~operation_hash + misbehaviour + delegate + ~rewarded:payload_producer.delegate + in return ( ctxt, Single_result (Dal_entrapment_evidence_result {balance_updates = []}) 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..a8f85ace8383 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -43,6 +43,16 @@ let record_denunciation ctxt ~operation_hash ~rewarded_delegate:rewarded misbehaviour +let record_dal_denunciation_as_pending ctxt ~operation_hash + (dal_misbehaviour : Dal_misbehaviour_repr.t) delegate ~rewarded = + (* do not forbid the delegate *) + Dal_pending_denunciations_storage.add_denunciation + ctxt + ~misbehaving_delegate:delegate + operation_hash + ~rewarded_delegate:rewarded + dal_misbehaviour + let punish_double_signing ctxt ~operation_hash misbehaviour delegate (level : Level_repr.t) ~rewarded = let open Lwt_result_syntax in diff --git a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli index 02e0705ade68..880d6c932436 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli @@ -68,6 +68,16 @@ val punish_double_signing : rewarded:Signature.public_key_hash -> Raw_context.t tzresult Lwt.t +(* TODO: similar to {punish_double_signing} just use a different name, because + we don't punish here, but at the end of the cycle. *) +val record_dal_denunciation_as_pending : + Raw_context.t -> + operation_hash:Operation_hash.t -> + Dal_misbehaviour_repr.t -> + Signature.Public_key_hash.t -> + rewarded:Signature.public_key_hash -> + Raw_context.t tzresult Lwt.t + (** Applies pending denunciations in {!Storage.Pending_denunciations} at the end of a cycle. The applicable denunciations are those that point to a misbehavior whose max slashable period is ending. -- GitLab From 55dc388c589e5c862936bcb9a7f544b46498f58e Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Sat, 11 Jan 2025 06:26:50 +0100 Subject: [PATCH 5/6] Alpha/DAL: add Dal_entrapment_punishment receipt kind --- src/proto_alpha/lib_client/operation_result.ml | 1 + src/proto_alpha/lib_protocol/alpha_context.mli | 2 ++ src/proto_alpha/lib_protocol/receipt_repr.ml | 3 +++ src/proto_alpha/lib_protocol/receipt_repr.mli | 1 + src/proto_alpha/lib_protocol/token.ml | 2 ++ src/proto_alpha/lib_protocol/token.mli | 1 + 6 files changed, 10 insertions(+) diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index d918565a9af6..f08ef4e7123a 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -424,6 +424,7 @@ let pp_balance_updates ppf balance_updates = | Dal_attesting_rewards -> "DAL attesting rewards" | Storage_fees -> "storage fees" | Double_signing_punishments -> "double signing punishments" + | Dal_entrapment_punishment -> "DAL entrapment punishment" | Lost_attesting_rewards (pkh, p, r) -> let reason = match (p, r) with diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index bf585f5f573a..3d8d907444f0 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2158,6 +2158,7 @@ module Receipt : sig | Dal_attesting_rewards : Tez.t balance | Storage_fees : Tez.t balance | Double_signing_punishments : Tez.t balance + | Dal_entrapment_punishment : Tez.t balance | Lost_attesting_rewards : public_key_hash * bool * bool -> Tez.t balance | Lost_dal_attesting_rewards : public_key_hash -> Tez.t balance | Liquidity_baking_subsidies : Tez.t balance @@ -5397,6 +5398,7 @@ module Token : sig type receiver = [ `Storage_fees | `Double_signing_punishments + | `Dal_entrapment_punishment | `Lost_attesting_rewards of public_key_hash * bool * bool | `Lost_dal_attesting_rewards of public_key_hash | `Burned diff --git a/src/proto_alpha/lib_protocol/receipt_repr.ml b/src/proto_alpha/lib_protocol/receipt_repr.ml index 93fa0f50cb40..9c185bd8a5b2 100644 --- a/src/proto_alpha/lib_protocol/receipt_repr.ml +++ b/src/proto_alpha/lib_protocol/receipt_repr.ml @@ -83,6 +83,7 @@ type 'token balance = | Dal_attesting_rewards : Tez_repr.t balance | Storage_fees : Tez_repr.t balance | Double_signing_punishments : Tez_repr.t balance + | Dal_entrapment_punishment : Tez_repr.t balance | Lost_attesting_rewards : Signature.Public_key_hash.t * bool * bool -> Tez_repr.t balance @@ -120,6 +121,7 @@ let token_of_balance : type token. token balance -> token Token.t = function | Dal_attesting_rewards -> Token.Tez | Storage_fees -> Token.Tez | Double_signing_punishments -> Token.Tez + | Dal_entrapment_punishment -> Token.Tez | Lost_attesting_rewards _ -> Token.Tez | Lost_dal_attesting_rewards _ -> Token.Tez | Liquidity_baking_subsidies -> Token.Tez @@ -193,6 +195,7 @@ let compare_balance : | Staking_delegate_denominator _ -> 22 | Dal_attesting_rewards -> 23 | Lost_dal_attesting_rewards _ -> 24 + | Dal_entrapment_punishment -> 25 (* don't forget to add parameterized cases in the first part of the function *) in Compare.Int.compare (index ba) (index bb) diff --git a/src/proto_alpha/lib_protocol/receipt_repr.mli b/src/proto_alpha/lib_protocol/receipt_repr.mli index 34bb154e0ab3..41668a3ad532 100644 --- a/src/proto_alpha/lib_protocol/receipt_repr.mli +++ b/src/proto_alpha/lib_protocol/receipt_repr.mli @@ -56,6 +56,7 @@ type 'token balance = | Dal_attesting_rewards : Tez_repr.t balance | Storage_fees : Tez_repr.t balance | Double_signing_punishments : Tez_repr.t balance + | Dal_entrapment_punishment : Tez_repr.t balance | Lost_attesting_rewards : Signature.Public_key_hash.t * bool * bool -> Tez_repr.t balance diff --git a/src/proto_alpha/lib_protocol/token.ml b/src/proto_alpha/lib_protocol/token.ml index 2c31121bed8a..b246d43fe141 100644 --- a/src/proto_alpha/lib_protocol/token.ml +++ b/src/proto_alpha/lib_protocol/token.ml @@ -49,6 +49,7 @@ type giver = [infinite_source | container] type infinite_sink = [ `Storage_fees | `Double_signing_punishments + | `Dal_entrapment_punishment | `Lost_attesting_rewards of Signature.Public_key_hash.t * bool * bool | `Lost_dal_attesting_rewards of Signature.Public_key_hash.t | `Sc_rollup_refutation_punishments @@ -74,6 +75,7 @@ let credit ctxt receiver amount origin = match infinite_sink with | `Storage_fees -> Storage_fees | `Double_signing_punishments -> Double_signing_punishments + | `Dal_entrapment_punishment -> Dal_entrapment_punishment | `Lost_attesting_rewards (d, p, r) -> Lost_attesting_rewards (d, p, r) | `Lost_dal_attesting_rewards d -> Lost_dal_attesting_rewards d | `Sc_rollup_refutation_punishments -> diff --git a/src/proto_alpha/lib_protocol/token.mli b/src/proto_alpha/lib_protocol/token.mli index a4b4a653257f..671715f6cc64 100644 --- a/src/proto_alpha/lib_protocol/token.mli +++ b/src/proto_alpha/lib_protocol/token.mli @@ -96,6 +96,7 @@ type giver = [infinite_source | container] type infinite_sink = [ `Storage_fees (** Fees burnt to compensate storage usage *) | `Double_signing_punishments (** Consensus slashing *) + | `Dal_entrapment_punishment (** DAL slashing *) | `Lost_attesting_rewards of Signature.Public_key_hash.t * bool * bool (** Consensus rewards not distributed because the participation of the delegate was too low. *) | `Lost_dal_attesting_rewards of Signature.Public_key_hash.t -- GitLab From a158889d853f18fc2a9f710d391abee746fcf2ac Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Wed, 15 Jan 2025 11:01:23 +0100 Subject: [PATCH 6/6] Alpha/DAL: slash a fixed percetage --- .../delegate_slashed_deposits_storage.ml | 313 +++++++++++++++++- 1 file changed, 307 insertions(+), 6 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 a8f85ace8383..d0b94bde9ad9 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -96,6 +96,7 @@ let punish_double_signing ctxt ~operation_hash misbehaviour delegate - by increasing level, then increasing round, then kind, ignoring the slot - for the kind: double baking > double attesting > double preattesting *) module MisMap = Map.Make (Misbehaviour_repr) +module DalMisMap = Map.Make (Dal_misbehaviour_repr) let compute_punishing_amount slashing_percentage frozen_deposits = let punish_value = @@ -162,6 +163,28 @@ let update_block_denunciations_map_with delegate denunciations initial_block_map initial_block_map denunciations +(* Same as the previous function, but for the DAL. TODO: we need a way to use the same code *) +let update_block_dal_denunciations_map_with delegate denunciations + initial_block_map = + List.fold_left + (fun block_map denunciation -> + DalMisMap.update + denunciation.Dal_denunciations_repr.misbehaviour + (function + | None -> + Some + (Signature.Public_key_hash.Map.singleton delegate denunciation) + | Some map -> + Some + (Signature.Public_key_hash.Map.update + delegate + (function + | None -> Some denunciation | Some old_d -> Some old_d) + map)) + block_map) + initial_block_map + denunciations + (* Split denunciations into two groups: those to be applied, and those to be delayed. *) let get_applicable_and_remaining_denunciations ctxt current_cycle = Storage.Pending_denunciations.fold @@ -199,6 +222,45 @@ let get_applicable_and_remaining_denunciations ctxt current_cycle = in Lwt.return (new_block_map, new_remaining_denunciations)) +(* Same as the previous function, but for the DAL. TODO: we need a way to use the same code *) +let get_applicable_and_remaining_dal_denunciations ctxt current_cycle = + Storage.Dal_pending_denunciations.fold + ctxt + ~order:`Undefined + ~init:(DalMisMap.empty, []) + ~f:(fun delegate denunciations acc -> + let block_map, remaining_denunciations = acc in + (* Since the [max_slashing_period] is 2, and we want to apply denunciations at the + end of this period, we "delay" the current cycle's misbehaviour's denunciations, + while we apply the older denunciations. + Indeed, we apply denunciations in the cycle following the misbehaviour, so that + the time between the misbehaviour and the slashing is at most + [max_slashing_period = 2] cycles. *) + let denunciations_to_apply, denunciations_to_delay = + List.partition + (fun denunciation -> + let level = + denunciation.Dal_denunciations_repr.misbehaviour.level + in + let misb_cycle = + Level_repr.cycle_from_raw + ~cycle_eras:(Raw_context.cycle_eras ctxt) + level + in + Cycle_repr.(misb_cycle < current_cycle)) + denunciations + in + let new_block_map = + update_block_dal_denunciations_map_with + delegate + denunciations_to_apply + block_map + in + let new_remaining_denunciations = + (delegate, denunciations_to_delay) :: remaining_denunciations + in + Lwt.return (new_block_map, new_remaining_denunciations)) + let apply_block_denunciations ctxt current_cycle block_denunciations_map = let slashable_deposits_period = Constants_storage.slashable_deposits_period ctxt @@ -457,20 +519,245 @@ let apply_block_denunciations ctxt current_cycle block_denunciations_map = block_denunciations_map (ctxt, []) +let apply_block_dal_denunciations ctxt current_cycle block_dal_denunciations_map + slashing_percentage = + let slashable_deposits_period = + Constants_storage.slashable_deposits_period ctxt + in + let open Lwt_result_syntax in + let global_limit_of_staking_over_baking = + Constants_storage.adaptive_issuance_global_limit_of_staking_over_baking ctxt + in + DalMisMap.fold_es + (fun ({Dal_misbehaviour_repr.level = raw_level; _} as miskey) + denunciations_map + acc -> + let ctxt, balance_updates = acc in + let level = + Level_repr.level_from_raw + ~cycle_eras:(Raw_context.cycle_eras ctxt) + raw_level + in + let misbehaviour_cycle = level.cycle in + let denunciations = + Signature.Public_key_hash.Map.bindings denunciations_map + in + let+ ctxt, balance_updates = + List.fold_left_es + (fun (ctxt, balance_updates) + ( delegate, + Dal_denunciations_repr.{operation_hash; rewarded; misbehaviour} + ) -> + assert ( + Compare.Int.equal + (* This compare ignores the slot *) + (Dal_misbehaviour_repr.compare miskey misbehaviour) + 0) ; + (* Validate ensures that [denunciations] contains [delegate] at most once *) + let* frozen_deposits = + let* initial_amount = + get_initial_frozen_deposits_of_misbehaviour_cycle + ~current_cycle + ~misbehaviour_cycle + ctxt + delegate + in + let* current_amount = + Delegate_storage.current_frozen_deposits ctxt delegate + in + return Deposits_repr.{initial_amount; current_amount} + in + let punishing_amount = + compute_punishing_amount slashing_percentage frozen_deposits + (* Ensures: [punishing_amount <= current_amount] + + where [current_amount = frozen_deposits.current_amount + = own_frozen + staked_frozen] + *) + in + let* {baker_part; stakers_part = _} = + Shared_stake.share + ~rounding:`Towards_baker + ctxt + delegate + punishing_amount + (* Ensures: + + - [baker_part + stakers_part = punishing_amount] + + - [baker_part / punishing_amount = own_frozen / (own_frozen + allowed_staked_frozen)] + + where [allowed_staked_frozen] is [staked_frozen] + capped by the delegate's [limit_of_staking_over_baking], + which notably means that [allowed_staked_frozen <= staked_frozen] + i.e. [own_frozen + allowed_staked_frozen <= own_frozen + staked_frozen = current_amount] + + Combining all of the above: + + [baker_part / punishing_amount >= own_frozen / current_amount] + + [(punishing_amount - stakers_part) / punishing_amount >= (current_amount - staked_frozen) / current_amount] + + [1 - (stakers_part / punishing_amount) >= 1 - (staked_frozen / current_amount)] + + [stakers_part / punishing_amount <= staked_frozen / current_amount] + + [stakers_part <= staked_frozen * punishing_amount / current_amount] + + Moreover, we know from above that [punishing_amount <= current_amount] so: + + [stakers_part <= staked_frozen] + *) + in + let* full_staking_balance = + Stake_storage.get_full_staking_balance ctxt delegate + in + let own_frozen = + Full_staking_balance_repr.own_frozen full_staking_balance + in + let actual_baker_part = Tez_repr.min baker_part own_frozen in + let*? actual_stakers_part = + Tez_repr.(punishing_amount -? actual_baker_part) + in + (* To avoid underflows, we need to guarantee that: + - [actual_baker_part <= own_frozen] and + - [actual_stakers_part <= staked_frozen] + + The [min] ensures that [actual_baker_part <= own_frozen]. + + For [actual_stakers_part], let's examine two cases + based on the [min]: + + - Case 1: [actual_baker_part = baker_part] + + [actual_stakers_part = punishing_amount - actual_baker_part + = punishing_amount - baker_part + = stakers_part + <= staked_frozen] as proven above + + - Case 2: [actual_baker_part = own_frozen] + + [actual_stakers_part = punishing_amount - actual_baker_part + = punishing_amount - own_frozen + <= current_amount - own_frozen + = own_frozen + staked_frozen - own_frozen + = staked_frozen] + *) + let*? {amount_to_burn = to_burn_baker; reward = to_reward_baker} = + split_reward_and_burn + actual_baker_part + global_limit_of_staking_over_baking + in + let*? {amount_to_burn = to_burn_stakers; reward = to_reward_stakers} + = + split_reward_and_burn + actual_stakers_part + global_limit_of_staking_over_baking + in + let giver_baker = + `Frozen_deposits (Frozen_staker_repr.baker delegate) + in + let giver_stakers = + `Frozen_deposits + (Frozen_staker_repr.shared_between_stakers ~delegate) + in + let init_to_burn = + [(giver_baker, to_burn_baker); (giver_stakers, to_burn_stakers)] + in + let init_to_reward = + [ + (giver_baker, to_reward_baker); + (giver_stakers, to_reward_stakers); + ] + in + let* to_burn, to_reward = + let oldest_slashable_cycle = + Cycle_repr.sub misbehaviour_cycle slashable_deposits_period + |> Option.value ~default:Cycle_repr.root + in + let slashable_cycles = + Cycle_repr.(oldest_slashable_cycle ---> misbehaviour_cycle) + in + List.fold_left_es + (fun (to_burn, to_reward) cycle -> + let* frozen_deposits = + Unstaked_frozen_deposits_storage.get ctxt delegate cycle + in + let*? {amount_to_burn; reward} = + compute_reward_and_burn + slashing_percentage + frozen_deposits + global_limit_of_staking_over_baking + in + let giver = + `Unstaked_frozen_deposits + (Unstaked_frozen_staker_repr.Shared delegate, cycle) + in + return + ( (giver, amount_to_burn) :: to_burn, + (giver, reward) :: to_reward )) + (init_to_burn, init_to_reward) + slashable_cycles + in + let origin = Receipt_repr.Delayed_operation {operation_hash} in + let* ctxt, punish_balance_updates = + Token.transfer_n ctxt ~origin to_burn `Dal_entrapment_punishment + in + let+ ctxt, reward_balance_updates = + Token.transfer_n + ctxt + ~origin + to_reward + (`Contract (Contract_repr.Implicit rewarded)) + in + ( ctxt, + punish_balance_updates @ reward_balance_updates @ balance_updates + )) + (ctxt, balance_updates) + denunciations + in + (ctxt, balance_updates)) + block_dal_denunciations_map + (ctxt, []) + let apply_denunciations ctxt = let open Lwt_result_syntax in + let dal_slashing_percentage = + Percentage.of_q_bounded ~round:`Down Q.(1 // 1000) + in let current_cycle = (Raw_context.current_level ctxt).cycle in - let*! applicable_denunciations_map, remaining_denunciations = + let*! applicable_dal_denunciations_map, remaining_dal_denunciations = + get_applicable_and_remaining_dal_denunciations ctxt current_cycle + in + let* ctxt, dal_balance_updates = + apply_block_dal_denunciations + ctxt + current_cycle + applicable_dal_denunciations_map + dal_slashing_percentage + in + let*! ( applicable_consensus_denunciations_map, + remaining_consensus_denunciations ) = get_applicable_and_remaining_denunciations ctxt current_cycle in - let* ctxt, balance_updates = - apply_block_denunciations ctxt current_cycle applicable_denunciations_map + let* ctxt, consensus_balance_updates = + apply_block_denunciations + ctxt + current_cycle + applicable_consensus_denunciations_map in - return (ctxt, balance_updates, remaining_denunciations) + return + ( ctxt, + dal_balance_updates @ consensus_balance_updates, + remaining_consensus_denunciations, + remaining_dal_denunciations ) let apply_and_clear_denunciations ctxt = let open Lwt_result_syntax in - let* ctxt, balance_updates, remaining_denunciations = + let* ( ctxt, + balance_updates, + remaining_consensus_denunciations, + remaining_dal_denunciations ) = apply_denunciations ctxt in (* Updates the storage to only contain the remaining denunciations *) @@ -486,7 +773,21 @@ let apply_and_clear_denunciations ctxt = delegate current_cycle_denunciations) ctxt - remaining_denunciations + remaining_consensus_denunciations + in + let*! ctxt = Dal_pending_denunciations_storage.clear ctxt in + let*! ctxt = + List.fold_left_s + (fun ctxt (delegate, current_cycle_denunciations) -> + match current_cycle_denunciations with + | [] -> Lwt.return ctxt + | _ -> + Dal_pending_denunciations_storage.set_denunciations + ctxt + delegate + current_cycle_denunciations) + ctxt + remaining_dal_denunciations in return (ctxt, balance_updates) -- GitLab