Raw File
forbidden_delegates_storage.ml
(*****************************************************************************)
(*                                                                           *)
(* SPDX-License-Identifier: MIT                                              *)
(* Copyright (c) 2023 Nomadic Labs, <contact@nomadic-labs.com>               *)
(*                                                                           *)
(*****************************************************************************)

let is_forbidden ctxt delegate =
  let forbidden_delegates = Raw_context.Consensus.forbidden_delegates ctxt in
  Signature.Public_key_hash.Set.mem delegate forbidden_delegates

let forbid ctxt delegate =
  let ctxt = Raw_context.Consensus.forbid_delegate ctxt delegate in
  let new_forbidden_delegates =
    Raw_context.Consensus.forbidden_delegates ctxt
  in
  Storage.Tenderbake.Forbidden_delegates.add ctxt new_forbidden_delegates

let load ctxt =
  let open Lwt_result_syntax in
  let* forbidden_delegates_opt =
    Storage.Tenderbake.Forbidden_delegates.find ctxt
  in
  let ctxt =
    match forbidden_delegates_opt with
    | Some forbidden_delegates ->
        Raw_context.Consensus.set_forbidden_delegates ctxt forbidden_delegates
    | None ->
        Raw_context.Consensus.set_forbidden_delegates
          ctxt
          Signature.Public_key_hash.Set.empty
  in
  return ctxt

let set_forbidden_delegates ctxt forbidden_delegates =
  let open Lwt_syntax in
  let* ctxt =
    Storage.Tenderbake.Forbidden_delegates.add ctxt forbidden_delegates
  in
  let ctxt =
    Raw_context.Consensus.set_forbidden_delegates ctxt forbidden_delegates
  in
  return ctxt

let should_unforbid ctxt delegate ~selection_for_new_cycle =
  let open Lwt_result_syntax in
  (* A delegate who has pending denunciations for which slashing has
     not been applied yet should stay forbidden, because their frozen
     deposits are going to decrease by a yet unknown amount. *)
  let*! has_pending_denunciations =
    Pending_denunciations_storage.has_pending_denunciations ctxt delegate
  in
  if has_pending_denunciations then return_false
  else
    (* To get unforbidden, a delegate's current frozen deposits must
       be high enough to insure the next cycle's baking rights. More
       precisely, their [current_frozen_deposits] must at least match
       [frozen], where:

       - [current_frozen_deposits] is the sum of the delegate's own
       frozen funds and their stakers'; it doesn't necessarily observe
       overstaking limits.

       - [frozen] is the frozen stake that was used in the past to
       compute the baking rights for the new cycle. It includes past
       frozen balances from the delegate and their stakers, but
       excludes any overstaked funds (as enforced by
       {!Stake_context.apply_limits}). *)
    match
      Signature.Public_key_hash.Map.find delegate selection_for_new_cycle
    with
    | None -> return_true
    | Some {Stake_repr.frozen; _} ->
        let* current_frozen_deposits =
          Delegate_storage.current_frozen_deposits ctxt delegate
        in
        return Tez_repr.(current_frozen_deposits >= frozen)

let update_at_cycle_end_after_slashing ctxt ~new_cycle =
  let open Lwt_result_syntax in
  let forbidden_delegates = Raw_context.Consensus.forbidden_delegates ctxt in
  if Signature.Public_key_hash.Set.is_empty forbidden_delegates then return ctxt
  else
    let* selection_for_new_cycle =
      Stake_storage.get_selected_distribution_as_map ctxt new_cycle
    in
    let* forbidden_delegates =
      Signature.Public_key_hash.Set.fold_es
        (fun delegate acc ->
          let* should_unforbid =
            should_unforbid ctxt delegate ~selection_for_new_cycle
          in
          if should_unforbid then
            let old_forbidden =
              match acc with
              | `Unchanged -> forbidden_delegates
              | `Changed forbidden_delegates -> forbidden_delegates
            in
            let new_forbidden =
              Signature.Public_key_hash.Set.remove delegate old_forbidden
            in
            return (`Changed new_forbidden)
          else return acc)
        forbidden_delegates
        `Unchanged
    in
    match forbidden_delegates with
    | `Unchanged -> return ctxt
    | `Changed forbidden_delegates ->
        let*! ctxt = set_forbidden_delegates ctxt forbidden_delegates in
        return ctxt

let init_for_genesis ctxt =
  Storage.Tenderbake.Forbidden_delegates.init
    ctxt
    Signature.Public_key_hash.Set.empty
back to top