From 61f186157cbdf4eba2f85b5c087e5288332d1771 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Thu, 14 Apr 2022 23:37:43 +0200 Subject: [PATCH 01/35] Proto: split `Delegate_storage` in multiple files --- .../test/tenderbrute/lib/tenderbrute.ml | 2 +- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 4 + src/proto_alpha/lib_protocol/alpha_context.ml | 15 +- .../lib_protocol/alpha_context.mli | 10 +- src/proto_alpha/lib_protocol/apply.ml | 11 +- .../lib_protocol/delegate_cycles.ml | 225 +++++ .../lib_protocol/delegate_cycles.mli | 52 ++ .../delegate_missed_endorsements_storage.ml | 266 ++++++ .../delegate_missed_endorsements_storage.mli | 86 ++ .../lib_protocol/delegate_sampler.ml | 242 +++++ .../lib_protocol/delegate_sampler.mli | 65 ++ .../delegate_slashed_deposits_storage.ml | 118 +++ .../delegate_slashed_deposits_storage.mli | 61 ++ .../lib_protocol/delegate_storage.ml | 840 ++---------------- .../lib_protocol/delegate_storage.mli | 209 +---- src/proto_alpha/lib_protocol/dune | 24 + src/proto_alpha/lib_protocol/init_storage.ml | 4 +- 17 files changed, 1250 insertions(+), 984 deletions(-) create mode 100644 src/proto_alpha/lib_protocol/delegate_cycles.ml create mode 100644 src/proto_alpha/lib_protocol/delegate_cycles.mli create mode 100644 src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml create mode 100644 src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli create mode 100644 src/proto_alpha/lib_protocol/delegate_sampler.ml create mode 100644 src/proto_alpha/lib_protocol/delegate_sampler.mli create mode 100644 src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml create mode 100644 src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli diff --git a/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml b/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml index a27b9381426c..fa99d708a0a1 100644 --- a/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml +++ b/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml @@ -100,7 +100,7 @@ let check ctxt ~selection = (fun () -> LevelRoundMap.fold_es (fun (level, round) delegate ctxt -> - Delegate_storage.baking_rights_owner ctxt level ~round + Delegate_sampler.baking_rights_owner ctxt level ~round >|= Environment.wrap_tzresult >>=? fun (ctxt, _, (_, pkh)) -> if not (Signature.Public_key_hash.equal delegate pkh) then raise Exit diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 0a8af4e50aaf..e8c49f63ca92 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -112,6 +112,10 @@ "Contract_storage", "Token", "Delegate_storage", + "Delegate_missed_endorsements_storage", + "Delegate_slashed_deposits_storage", + "Delegate_sampler", + "Delegate_cycles", "Bootstrap_storage", "Vote_storage", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index a8171c397beb..b522a619e883 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -398,6 +398,11 @@ module Receipt = Receipt_repr module Delegate = struct include Delegate_storage + include Delegate_missed_endorsements_storage + include Delegate_slashed_deposits_storage + include Delegate_cycles + + let deactivated = Delegate_activation_storage.is_inactive type deposits = Storage.deposits = { initial_amount : Tez.t; @@ -419,15 +424,15 @@ end module Stake_distribution = struct let snapshot = Stake_storage.snapshot - let compute_snapshot_index = Delegate_storage.compute_snapshot_index + let compute_snapshot_index = Delegate_sampler.compute_snapshot_index - let baking_rights_owner = Delegate.baking_rights_owner + let baking_rights_owner = Delegate_sampler.baking_rights_owner - let slot_owner = Delegate.slot_owner + let slot_owner = Delegate_sampler.slot_owner - let delegate_pubkey = Delegate.pubkey + let delegate_pubkey = Delegate_storage.pubkey - let get_staking_balance = Delegate.staking_balance + let get_staking_balance = Delegate_storage.staking_balance end module Nonce = Nonce_storage diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index d32e6023936b..5676d5d37e50 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2236,22 +2236,20 @@ module Delegate : sig tzresult Lwt.t - val already_slashed_for_double_endorsing : - context -> public_key_hash -> Level.t -> bool tzresult Lwt.t + val check_and_record_already_slashed_for_double_endorsing : + context -> public_key_hash -> Level.t -> (context * bool) tzresult Lwt.t - val already_slashed_for_double_baking : - context -> public_key_hash -> Level.t -> bool tzresult Lwt.t + val check_and_record_already_slashed_for_double_baking : + context -> public_key_hash -> Level.t -> (context * bool) tzresult Lwt.t val punish_double_endorsing : context -> public_key_hash -> - Level.t -> (context * Tez.t * Receipt.balance_updates) tzresult Lwt.t val punish_double_baking : context -> public_key_hash -> - Level.t -> (context * Tez.t * Receipt.balance_updates) tzresult Lwt.t val full_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 183dfb0524c7..8ebb49accef8 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2903,18 +2903,19 @@ let check_denunciation_age ctxt kind given_level = {kind; level = given_level; last_cycle = last_slashable_cycle}) let punish_delegate ctxt delegate level mistake mk_result ~payload_producer = - let already_slashed, punish = + let check_and_record_already_slashed, punish = match mistake with | `Double_baking -> - ( Delegate.already_slashed_for_double_baking, + ( Delegate.check_and_record_already_slashed_for_double_baking, Delegate.punish_double_baking ) | `Double_endorsing -> - ( Delegate.already_slashed_for_double_endorsing, + ( Delegate.check_and_record_already_slashed_for_double_endorsing, Delegate.punish_double_endorsing ) in - already_slashed ctxt delegate level >>=? fun slashed -> + check_and_record_already_slashed ctxt delegate level + >>=? fun (ctxt, slashed) -> fail_when slashed Unrequired_denunciation >>=? fun () -> - punish ctxt delegate level >>=? fun (ctxt, burned, punish_balance_updates) -> + punish ctxt delegate >>=? fun (ctxt, burned, punish_balance_updates) -> (match Tez.(burned /? 2L) with | Ok reward -> Token.transfer diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml new file mode 100644 index 000000000000..a866f2727f6d --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -0,0 +1,225 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +let update_activity ctxt last_cycle = + let preserved = Constants_storage.preserved_cycles ctxt in + match Cycle_repr.sub last_cycle preserved with + | None -> return (ctxt, []) + | Some _unfrozen_cycle -> + Stake_storage.fold_on_active_delegates_with_rolls + ctxt + ~order:`Sorted + ~init:(Ok (ctxt, [])) + ~f:(fun delegate () acc -> + acc >>?= fun (ctxt, deactivated) -> + Delegate_activation_storage.last_cycle_before_deactivation + ctxt + delegate + >>=? fun cycle -> + if Cycle_repr.(cycle <= last_cycle) then + Delegate_storage.set_inactive ctxt delegate >>=? fun ctxt -> + return (ctxt, delegate :: deactivated) + else return (ctxt, deactivated)) + >|=? fun (ctxt, deactivated) -> (ctxt, deactivated) + +(* Return a map from delegates (with active stake at some cycle + in the cycle window [from_cycle, to_cycle]) to the maximum + of the stake to be deposited for each such cycle (which is just the + [frozen_deposits_percentage] of the active stake at that cycle). Also + return the delegates that have fallen out of the sliding window. *) +let max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle = + let frozen_deposits_percentage = + Constants_storage.frozen_deposits_percentage ctxt + in + let cycles = Cycle_repr.(from_cycle ---> to_cycle) in + (match Cycle_repr.pred from_cycle with + | None -> return Signature.Public_key_hash.Set.empty + | Some cleared_cycle -> ( + Stake_storage.find_selected_distribution ctxt cleared_cycle + >|=? fun cleared_cycle_delegates -> + match cleared_cycle_delegates with + | None -> Signature.Public_key_hash.Set.empty + | Some delegates -> + List.fold_left + (fun set (d, _) -> Signature.Public_key_hash.Set.add d set) + Signature.Public_key_hash.Set.empty + delegates)) + >>=? fun cleared_cycle_delegates -> + List.fold_left_es + (fun (maxima, delegates_to_remove) (cycle : Cycle_repr.t) -> + Stake_storage.get_selected_distribution ctxt cycle + >|=? fun active_stakes -> + List.fold_left + (fun (maxima, delegates_to_remove) (delegate, stake) -> + let stake_to_be_deposited = + Tez_repr.(div_exn (mul_exn stake frozen_deposits_percentage) 100) + in + let maxima = + Signature.Public_key_hash.Map.update + delegate + (function + | None -> Some stake_to_be_deposited + | Some maximum -> + Some (Tez_repr.max maximum stake_to_be_deposited)) + maxima + in + let delegates_to_remove = + Signature.Public_key_hash.Set.remove delegate delegates_to_remove + in + (maxima, delegates_to_remove)) + (maxima, delegates_to_remove) + active_stakes) + (Signature.Public_key_hash.Map.empty, cleared_cycle_delegates) + cycles + +let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle + ~balance_updates = + let max_slashable_period = Constants_storage.max_slashing_period ctxt in + (* We want to be able to slash for at most [max_slashable_period] *) + (match Cycle_repr.(sub new_cycle (max_slashable_period - 1)) with + | None -> + Storage.Tenderbake.First_level_of_protocol.get ctxt + >>=? fun first_level_of_protocol -> + let cycle_eras = Raw_context.cycle_eras ctxt in + let level = + Level_repr.level_from_raw ~cycle_eras first_level_of_protocol + in + return level.cycle + | Some cycle -> return cycle) + >>=? fun from_cycle -> + let preserved_cycles = Constants_storage.preserved_cycles ctxt in + let to_cycle = Cycle_repr.(add new_cycle preserved_cycles) in + max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle + >>=? fun (maxima, delegates_to_remove) -> + Signature.Public_key_hash.Map.fold_es + (fun delegate maximum_stake_to_be_deposited (ctxt, balance_updates) -> + (* Here we make sure to preserve the following invariant : + maximum_stake_to_be_deposited <= frozen_deposits + balance + See select_distribution_for_cycle *) + let delegate_contract = Contract_repr.Implicit delegate in + Frozen_deposits_storage.update_initial_amount + ctxt + delegate_contract + maximum_stake_to_be_deposited + >>=? fun ctxt -> + Frozen_deposits_storage.get ctxt delegate_contract >>=? fun deposits -> + let current_amount = deposits.current_amount in + if Tez_repr.(current_amount > maximum_stake_to_be_deposited) then + Tez_repr.(current_amount -? maximum_stake_to_be_deposited) + >>?= fun to_reimburse -> + Token.transfer + ~origin + ctxt + (`Frozen_deposits delegate) + (`Delegate_balance delegate) + to_reimburse + >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) + else if Tez_repr.(current_amount < maximum_stake_to_be_deposited) then + Tez_repr.(maximum_stake_to_be_deposited -? current_amount) + >>?= fun desired_to_freeze -> + Contract_storage.get_balance ctxt delegate_contract >>=? fun balance -> + (* In case the delegate hasn't been slashed in this cycle, + the following invariant holds: + maximum_stake_to_be_deposited <= frozen_deposits + balance + See select_distribution_for_cycle + + If the delegate has been slashed during the cycle, the invariant + above doesn't necessarily hold. In this case, we freeze the max + we can for the delegate. *) + let to_freeze = Tez_repr.(min balance desired_to_freeze) in + Token.transfer + ~origin + ctxt + (`Delegate_balance delegate) + (`Frozen_deposits delegate) + to_freeze + >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) + else return (ctxt, balance_updates)) + maxima + (ctxt, balance_updates) + >>=? fun (ctxt, balance_updates) -> + (* Unfreeze deposits (that is, set them to zero) for delegates that + were previously in the relevant window (and therefore had some + frozen deposits) but are not in the new window; because that means + that such a delegate had no active stake in the relevant cycles, + and therefore it should have no frozen deposits. *) + Signature.Public_key_hash.Set.fold_es + (fun delegate (ctxt, balance_updates) -> + let delegate_contract = Contract_repr.Implicit delegate in + Frozen_deposits_storage.update_initial_amount + ctxt + delegate_contract + Tez_repr.zero + >>=? fun ctxt -> + Frozen_deposits_storage.get ctxt delegate_contract + >>=? fun frozen_deposits -> + if Tez_repr.(frozen_deposits.current_amount > zero) then + Token.transfer + ~origin + ctxt + (`Frozen_deposits delegate) + (`Delegate_balance delegate) + frozen_deposits.current_amount + >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) + else return (ctxt, balance_updates)) + delegates_to_remove + (ctxt, balance_updates) + +let freeze_deposits_do_not_call_except_for_migration = + freeze_deposits ~origin:Protocol_migration + +let cycle_end ctxt last_cycle unrevealed_nonces = + let new_cycle = Cycle_repr.add last_cycle 1 in + Delegate_sampler.select_new_distribution_at_cycle_end ctxt ~new_cycle + >>=? fun ctxt -> + Delegate_slashed_deposits_storage.clear_outdated_slashed_deposits + ctxt + ~new_cycle + >>= fun ctxt -> + Delegate_missed_endorsements_storage.distribute_endorsing_rewards + ctxt + last_cycle + unrevealed_nonces + >>=? fun (ctxt, balance_updates) -> + freeze_deposits ctxt ~new_cycle ~balance_updates + >>=? fun (ctxt, balance_updates) -> + Stake_storage.clear_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> + Delegate_sampler.clear_outdated_sampling_data ctxt ~new_cycle >>=? fun ctxt -> + update_activity ctxt last_cycle >>=? fun (ctxt, deactivated_delagates) -> + return (ctxt, balance_updates, deactivated_delagates) + +let init_first_cycles ctxt = + let preserved = Constants_storage.preserved_cycles ctxt in + List.fold_left_es + (fun ctxt c -> + let cycle = Cycle_repr.of_int32_exn (Int32.of_int c) in + Stake_storage.snapshot ctxt >>=? fun ctxt -> + (* NB: we need to take several snapshots because + select_distribution_for_cycle deletes the snapshots *) + Delegate_sampler.select_distribution_for_cycle ctxt cycle) + ctxt + Misc.(0 --> preserved) diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.mli b/src/proto_alpha/lib_protocol/delegate_cycles.mli new file mode 100644 index 000000000000..f45fd05bed59 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_cycles.mli @@ -0,0 +1,52 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Trigger the context maintenance at the end of cycle 'n', i.e.: + unfreeze the endorsing rewards, potentially deactivate delegates. + Return the corresponding balances updates and the list of + deactivated delegates. *) +val cycle_end : + Raw_context.t -> + Cycle_repr.t -> + Storage.Seed.unrevealed_nonce list -> + (Raw_context.t + * Receipt_repr.balance_updates + * Signature.Public_key_hash.t list) + tzresult + Lwt.t + +(** [init_first_cycles ctxt] computes and records the distribution of the total + active stake among active delegates. This concerns the total active stake + involved in the calculation of baking rights for all cycles in the range + [0, preserved_cycles]. *) +val init_first_cycles : Raw_context.t -> Raw_context.t tzresult Lwt.t + +val freeze_deposits_do_not_call_except_for_migration : + Raw_context.t -> + new_cycle:Cycle_repr.t -> + balance_updates:Receipt_repr.balance_updates -> + (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml new file mode 100644 index 000000000000..b776c0eb8171 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -0,0 +1,266 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +let expected_slots_for_given_active_stake ctxt ~total_active_stake ~active_stake + = + let blocks_per_cycle = + Int32.to_int (Constants_storage.blocks_per_cycle ctxt) + in + let consensus_committee_size = + Constants_storage.consensus_committee_size ctxt + in + let number_of_endorsements_per_cycle = + blocks_per_cycle * consensus_committee_size + in + Result.return + (Z.to_int + (Z.div + (Z.mul + (Z.of_int64 (Tez_repr.to_mutez active_stake)) + (Z.of_int number_of_endorsements_per_cycle)) + (Z.of_int64 (Tez_repr.to_mutez total_active_stake)))) + +type level_participation = Participated | Didn't_participate + +(* Note that the participation for the last block of a cycle is + recorded in the next cycle. *) +let record_endorsing_participation ctxt ~delegate ~participation + ~endorsing_power = + match participation with + | Participated -> Delegate_storage.set_active ctxt delegate + | Didn't_participate -> ( + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Missed_endorsements.find ctxt contract >>=? function + | Some {remaining_slots; missed_levels} -> + let remaining_slots = remaining_slots - endorsing_power in + Storage.Contract.Missed_endorsements.update + ctxt + contract + {remaining_slots; missed_levels = missed_levels + 1} + | None -> ( + let level = Level_storage.current ctxt in + Raw_context.stake_distribution_for_current_cycle ctxt + >>?= fun stake_distribution -> + match + Signature.Public_key_hash.Map.find delegate stake_distribution + with + | None -> + (* This happens when the block is the first one in a + cycle, and therefore the endorsements are for the last + block of the previous cycle, and when the delegate does + not have an active stake at the current cycle; in this + case its participation is simply ignored. *) + assert (Compare.Int32.(level.cycle_position = 0l)) ; + return ctxt + | Some active_stake -> + Stake_storage.get_total_active_stake ctxt level.cycle + >>=? fun total_active_stake -> + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + >>?= fun expected_slots -> + let Ratio_repr.{numerator; denominator} = + Constants_storage.minimal_participation_ratio ctxt + in + let minimal_activity = expected_slots * numerator / denominator in + let maximal_inactivity = expected_slots - minimal_activity in + let remaining_slots = maximal_inactivity - endorsing_power in + Storage.Contract.Missed_endorsements.init + ctxt + contract + {remaining_slots; missed_levels = 1})) + +let record_baking_activity_and_pay_rewards_and_fees ctxt ~payload_producer + ~block_producer ~baking_reward ~reward_bonus = + Delegate_storage.set_active ctxt payload_producer >>=? fun ctxt -> + (if not (Signature.Public_key_hash.equal payload_producer block_producer) then + Delegate_storage.set_active ctxt block_producer + else return ctxt) + >>=? fun ctxt -> + let pay_payload_producer ctxt delegate = + let contract = Contract_repr.Implicit delegate in + Token.balance ctxt `Block_fees >>=? fun (ctxt, block_fees) -> + Token.transfer_n + ctxt + [(`Block_fees, block_fees); (`Baking_rewards, baking_reward)] + (`Contract contract) + in + let pay_block_producer ctxt delegate bonus = + let contract = Contract_repr.Implicit delegate in + Token.transfer ctxt `Baking_bonuses (`Contract contract) bonus + in + pay_payload_producer ctxt payload_producer + >>=? fun (ctxt, balance_updates_payload_producer) -> + (match reward_bonus with + | Some bonus -> pay_block_producer ctxt block_producer bonus + | None -> return (ctxt, [])) + >>=? fun (ctxt, balance_updates_block_producer) -> + return + (ctxt, balance_updates_payload_producer @ balance_updates_block_producer) + +type participation_info = { + expected_cycle_activity : int; + minimal_cycle_activity : int; + missed_slots : int; + missed_levels : int; + remaining_allowed_missed_slots : int; + expected_endorsing_rewards : Tez_repr.t; +} + +(* Inefficient, only for RPC *) +let delegate_participation_info ctxt delegate = + let level = Level_storage.current ctxt in + Stake_storage.get_selected_distribution ctxt level.cycle + >>=? fun stake_distribution -> + match + List.assoc_opt + ~equal:Signature.Public_key_hash.equal + delegate + stake_distribution + with + | None -> + (* delegate does not have an active stake at the current cycle *) + return + { + expected_cycle_activity = 0; + minimal_cycle_activity = 0; + missed_slots = 0; + missed_levels = 0; + remaining_allowed_missed_slots = 0; + expected_endorsing_rewards = Tez_repr.zero; + } + | Some active_stake -> + Stake_storage.get_total_active_stake ctxt level.cycle + >>=? fun total_active_stake -> + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + >>?= fun expected_cycle_activity -> + let Ratio_repr.{numerator; denominator} = + Constants_storage.minimal_participation_ratio ctxt + in + let endorsing_reward_per_slot = + Constants_storage.endorsing_reward_per_slot ctxt + in + let minimal_cycle_activity = + expected_cycle_activity * numerator / denominator + in + let maximal_cycle_inactivity = + expected_cycle_activity - minimal_cycle_activity + in + let expected_endorsing_rewards = + Tez_repr.mul_exn endorsing_reward_per_slot expected_cycle_activity + in + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Missed_endorsements.find ctxt contract + >>=? fun missed_endorsements -> + let missed_slots, missed_levels, remaining_allowed_missed_slots = + match missed_endorsements with + | None -> (0, 0, maximal_cycle_inactivity) + | Some {remaining_slots; missed_levels} -> + ( maximal_cycle_inactivity - remaining_slots, + missed_levels, + Compare.Int.max 0 remaining_slots ) + in + let expected_endorsing_rewards = + match missed_endorsements with + | Some r when Compare.Int.(r.remaining_slots < 0) -> Tez_repr.zero + | _ -> expected_endorsing_rewards + in + return + { + expected_cycle_activity; + minimal_cycle_activity; + missed_slots; + missed_levels; + remaining_allowed_missed_slots; + expected_endorsing_rewards; + } + +let delegate_participated_enough ctxt delegate = + Storage.Contract.Missed_endorsements.find ctxt delegate >>=? function + | None -> return_true + | Some missed_endorsements -> + return Compare.Int.(missed_endorsements.remaining_slots >= 0) + +let delegate_has_revealed_nonces delegate unrevelead_nonces_set = + not (Signature.Public_key_hash.Set.mem delegate unrevelead_nonces_set) + +let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = + let endorsing_reward_per_slot = + Constants_storage.endorsing_reward_per_slot ctxt + in + let unrevealed_nonces_set = + List.fold_left + (fun set {Storage.Seed.nonce_hash = _; delegate} -> + Signature.Public_key_hash.Set.add delegate set) + Signature.Public_key_hash.Set.empty + unrevealed_nonces + in + Stake_storage.get_total_active_stake ctxt last_cycle + >>=? fun total_active_stake -> + Stake_storage.get_selected_distribution ctxt last_cycle >>=? fun delegates -> + List.fold_left_es + (fun (ctxt, balance_updates) (delegate, active_stake) -> + let delegate_contract = Contract_repr.Implicit delegate in + delegate_participated_enough ctxt delegate_contract + >>=? fun sufficient_participation -> + let has_revealed_nonces = + delegate_has_revealed_nonces delegate unrevealed_nonces_set + in + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + >>?= fun expected_slots -> + let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in + (if sufficient_participation && has_revealed_nonces then + (* Sufficient participation: we pay the rewards *) + Token.transfer + ctxt + `Endorsing_rewards + (`Contract delegate_contract) + rewards + >|=? fun (ctxt, payed_rewards_receipts) -> + (ctxt, payed_rewards_receipts @ balance_updates) + else + (* Insufficient participation or unrevealed nonce: no rewards *) + Token.transfer + ctxt + `Endorsing_rewards + (`Lost_endorsing_rewards + (delegate, not sufficient_participation, not has_revealed_nonces)) + rewards + >|=? fun (ctxt, payed_rewards_receipts) -> + (ctxt, payed_rewards_receipts @ balance_updates)) + >>=? fun (ctxt, balance_updates) -> + Storage.Contract.Missed_endorsements.remove ctxt delegate_contract + >>= fun ctxt -> return (ctxt, balance_updates)) + (ctxt, []) + delegates diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli new file mode 100644 index 000000000000..3ac64a03ae96 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli @@ -0,0 +1,86 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type level_participation = Participated | Didn't_participate + +(** Record the participation of a delegate as a validator. *) +val record_endorsing_participation : + Raw_context.t -> + delegate:Signature.Public_key_hash.t -> + participation:level_participation -> + endorsing_power:int -> + Raw_context.t tzresult Lwt.t + +(** Participation information. We denote by: + - "static" information that does not change during the cycle + - "dynamic" information that may change during the cycle *) +type participation_info = { + expected_cycle_activity : int; + (** The total expected slots to be endorsed in the cycle. (static) *) + minimal_cycle_activity : int; + (** The minimal endorsing slots in the cycle to get endorsing + rewards. (static) *) + missed_slots : int; + (** The number of missed endorsing slots in the cycle. (dynamic) *) + missed_levels : int; + (** The number of missed endorsing levels in the cycle. (dynamic) *) + remaining_allowed_missed_slots : int; + (** Remaining amount of endorsing slots that can be missed in the + cycle before forfeiting the rewards. (dynamic) *) + expected_endorsing_rewards : Tez_repr.t; + (** Endorsing rewards that will be distributed at the end of the + cycle if activity at that point will be greater than the minimal + required. If the activity is already known to be below the + required minimum, then the rewards are zero. (dynamic) *) +} + +(** Only use this function for RPC: this is expensive. + + [delegate_participation_info] and [!val:check_delegate] forms the + implementation of RPC call "/context/delegates//participation". + *) +val delegate_participation_info : + Raw_context.t -> + Signature.Public_key_hash.t -> + participation_info tzresult Lwt.t + +(** Sets the payload and block producer as active. Pays the baking + reward and the fees to the payload producer and the reward bonus to + the payload producer (if the reward_bonus is not None).*) +val record_baking_activity_and_pay_rewards_and_fees : + Raw_context.t -> + payload_producer:Signature.Public_key_hash.t -> + block_producer:Signature.Public_key_hash.t -> + baking_reward:Tez_repr.t -> + reward_bonus:Tez_repr.t option -> + (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t + +val distribute_endorsing_rewards : + Raw_context.t -> + Cycle_repr.t -> + Storage.Seed.unrevealed_nonce list -> + (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml new file mode 100644 index 000000000000..71b837b7ef60 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -0,0 +1,242 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +module Delegate_sampler_state = struct + module Cache_client = struct + type cached_value = + (Signature.public_key * Signature.public_key_hash) Sampler.t + + let namespace = Cache_repr.create_namespace "sampler_state" + + let cache_index = 2 + + let value_of_identifier ctxt identifier = + let cycle = Cycle_repr.of_string_exn identifier in + Storage.Delegate_sampler_state.get ctxt cycle + end + + module Cache = (val Cache_repr.register_exn (module Cache_client)) + + let identifier_of_cycle cycle = Format.asprintf "%a" Cycle_repr.pp cycle + + let init ctxt cycle sampler_state = + let id = identifier_of_cycle cycle in + Storage.Delegate_sampler_state.init ctxt cycle sampler_state + >>=? fun ctxt -> + let size = 1 (* that's symbolic: 1 cycle = 1 entry *) in + Cache.update ctxt id (Some (sampler_state, size)) >>?= fun ctxt -> + return ctxt + + let get ctxt cycle = + let id = identifier_of_cycle cycle in + Cache.find ctxt id >>=? function + | None -> Storage.Delegate_sampler_state.get ctxt cycle + | Some v -> return v + + let remove_existing ctxt cycle = + let id = identifier_of_cycle cycle in + Cache.update ctxt id None >>?= fun ctxt -> + Storage.Delegate_sampler_state.remove_existing ctxt cycle +end + +module Random = struct + (* [init_random_state] initialize a random sequence drawing state + that's unique for a given (seed, level, index) triple. Elements + from this sequence are drawn using [take_int64], updating the + state for the next draw. The initial state is the Blake2b hash of + the three randomness sources, and an offset set to zero + (indicating that zero bits of randomness have been + consumed). When drawing random elements, bits are extracted from + the state until exhaustion (256 bits), at which point the state + is rehashed and the offset reset to 0. *) + + let init_random_state seed level index = + ( Raw_hashes.blake2b + (Data_encoding.Binary.to_bytes_exn + Data_encoding.(tup3 Seed_repr.seed_encoding int32 int32) + (seed, level.Level_repr.cycle_position, Int32.of_int index)), + 0 ) + + let take_int64 bound state = + let drop_if_over = + (* This function draws random values in [0-(bound-1)] by drawing + in [0-(2^63-1)] (64-bit) and computing the value modulo + [bound]. For the application of [mod bound] to preserve + uniformity, the input space must be of the form + [0-(n*bound-1)]. We enforce this by rejecting 64-bit samples + above this limit (in which case, we draw a new 64-sample from + the sequence and try again). *) + Int64.sub Int64.max_int (Int64.rem Int64.max_int bound) + in + let rec loop (bytes, n) = + let consumed_bytes = 8 in + let state_size = Bytes.length bytes in + if Compare.Int.(n > state_size - consumed_bytes) then + loop (Raw_hashes.blake2b bytes, 0) + else + let r = TzEndian.get_int64 bytes n in + (* The absolute value of min_int is min_int. Also, every + positive integer is represented twice (positive and negative), + but zero is only represented once. We fix both problems at + once. *) + let r = if Compare.Int64.(r = Int64.min_int) then 0L else Int64.abs r in + if Compare.Int64.(r >= drop_if_over) then + loop (bytes, n + consumed_bytes) + else + let v = Int64.rem r bound in + (v, (bytes, n + consumed_bytes)) + in + loop state + + (** [sampler_for_cycle ctxt cycle] reads the sampler for [cycle] from + [ctxt] if it has been previously inited. Otherwise it initializes + the sampler and caches it in [ctxt] with + [Raw_context.set_sampler_for_cycle]. *) + let sampler_for_cycle ctxt cycle = + let read ctxt = + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + Delegate_sampler_state.get ctxt cycle >>=? fun state -> + return (seed, state) + in + Raw_context.sampler_for_cycle ~read ctxt cycle + + let owner c (level : Level_repr.t) offset = + let cycle = level.Level_repr.cycle in + sampler_for_cycle c cycle >>=? fun (c, seed, state) -> + let sample ~int_bound ~mass_bound = + let state = init_random_state seed level offset in + let i, state = take_int64 (Int64.of_int int_bound) state in + let elt, _ = take_int64 mass_bound state in + (Int64.to_int i, elt) + in + let pk, pkh = Sampler.sample state sample in + return (c, (pk, pkh)) +end + +let slot_owner c level slot = Random.owner c level (Slot_repr.to_int slot) + +let baking_rights_owner c (level : Level_repr.t) ~round = + Round_repr.to_int round >>?= fun round -> + let consensus_committee_size = Constants_storage.consensus_committee_size c in + Slot_repr.of_int (round mod consensus_committee_size) >>?= fun slot -> + slot_owner c level slot >>=? fun (ctxt, pk) -> return (ctxt, slot, pk) + +let get_stakes_for_selected_index ctxt index = + Stake_storage.fold_snapshot + ctxt + ~index + ~f:(fun (delegate, staking_balance) (acc, total_stake) -> + let delegate_contract = Contract_repr.Implicit delegate in + let open Tez_repr in + let open Lwt_result_syntax in + let* frozen_deposits_limit = + Delegate_storage.frozen_deposits_limit ctxt delegate + in + + let* balance_and_frozen_bonds = + Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract + in + let* frozen_deposits = + Frozen_deposits_storage.get ctxt delegate_contract + in + let*? total_balance = + balance_and_frozen_bonds +? frozen_deposits.current_amount + in + let* stake_for_cycle = + let frozen_deposits_percentage = + Int64.of_int @@ Constants_storage.frozen_deposits_percentage ctxt + in + let max_mutez = of_mutez_exn Int64.max_int in + let frozen_deposits_limit = + match frozen_deposits_limit with Some fdp -> fdp | None -> max_mutez + in + let aux = min total_balance frozen_deposits_limit in + let*? overflow_bound = max_mutez /? 100L in + if aux <= overflow_bound then + let*? aux = aux *? 100L in + let*? v = aux /? frozen_deposits_percentage in + return (min v staking_balance) + else + let*? sbal = staking_balance /? 100L in + let*? a = aux /? frozen_deposits_percentage in + if sbal <= a then return staking_balance + else + let*? r = max_mutez /? frozen_deposits_percentage in + return r + in + let*? total_stake = Tez_repr.(total_stake +? stake_for_cycle) in + return ((delegate, stake_for_cycle) :: acc, total_stake)) + ~init:([], Tez_repr.zero) + +let compute_snapshot_index_for_seed ~max_snapshot_index seed = + let rd = Seed_repr.initialize_new seed [Bytes.of_string "stake_snapshot"] in + let seq = Seed_repr.sequence rd 0l in + Seed_repr.take_int32 seq (Int32.of_int max_snapshot_index) + |> fst |> Int32.to_int |> return + +let compute_snapshot_index ctxt cycle ~max_snapshot_index = + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + compute_snapshot_index_for_seed ~max_snapshot_index seed + +let select_distribution_for_cycle ctxt cycle = + Stake_storage.max_snapshot_index ctxt >>=? fun max_snapshot_index -> + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + compute_snapshot_index_for_seed ~max_snapshot_index seed + >>=? fun selected_index -> + get_stakes_for_selected_index ctxt selected_index + >>=? fun (stakes, total_stake) -> + Stake_storage.set_selected_distribution_for_cycle + ctxt + cycle + stakes + total_stake + >>=? fun ctxt -> + List.fold_left_es + (fun acc (pkh, stake) -> + Delegate_storage.pubkey ctxt pkh >|=? fun pk -> + ((pk, pkh), Tez_repr.to_mutez stake) :: acc) + [] + stakes + >>=? fun stakes_pk -> + let state = Sampler.create stakes_pk in + Delegate_sampler_state.init ctxt cycle state >>=? fun ctxt -> + (* pre-allocate the sampler *) + Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) + +let select_new_distribution_at_cycle_end ctxt ~new_cycle = + let preserved = Constants_storage.preserved_cycles ctxt in + let for_cycle = Cycle_repr.add new_cycle preserved in + select_distribution_for_cycle ctxt for_cycle + +let clear_outdated_sampling_data ctxt ~new_cycle = + let max_slashing_period = Constants_storage.max_slashing_period ctxt in + match Cycle_repr.sub new_cycle max_slashing_period with + | None -> return ctxt + | Some outdated_cycle -> + Delegate_sampler_state.remove_existing ctxt outdated_cycle + >>=? fun ctxt -> + Storage.Seed.For_cycle.remove_existing ctxt outdated_cycle diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.mli b/src/proto_alpha/lib_protocol/delegate_sampler.mli new file mode 100644 index 000000000000..194c5a50468b --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_sampler.mli @@ -0,0 +1,65 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Participation slots potentially associated to accounts. The + accounts that didn't place a deposit will be excluded from this + list. This function should only be used to compute the deposits to + freeze or initialize the protocol while stitching. RPCs can use this + function to predict an approximation of long term future slot + allocations. It shouldn't be used in the baker. *) +val slot_owner : + Raw_context.t -> + Level_repr.t -> + Slot_repr.t -> + (Raw_context.t * (Signature.public_key * Signature.public_key_hash)) tzresult + Lwt.t + +val baking_rights_owner : + Raw_context.t -> + Level_repr.t -> + round:Round_repr.round -> + (Raw_context.t + * Slot_repr.t + * (Signature.public_key * Signature.public_key_hash)) + tzresult + Lwt.t + +(** [compute_snapshot_index ctxt cycle max_snapshot_index] Returns the index of + the selected snapshot for the [cycle] passed as argument, and for the max + index of snapshots taken so far, [max_snapshot_index] (see + [Stake_storage.max_snapshot_index]. *) +val compute_snapshot_index : + Raw_context.t -> Cycle_repr.t -> max_snapshot_index:int -> int tzresult Lwt.t + +val select_new_distribution_at_cycle_end : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +val clear_outdated_sampling_data : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +val select_distribution_for_cycle : + Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml new file mode 100644 index 000000000000..043f8c117346 --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -0,0 +1,118 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +let check_and_record_already_slashed_for_double_baking ctxt delegate + (level : Level_repr.t) = + let open Lwt_tzresult_syntax in + let* slashed = + Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) + in + let already_slashed, updated_slashed = + match slashed with + | None -> + (false, {Storage.for_double_baking = true; for_double_endorsing = false}) + | Some slashed -> + (slashed.for_double_baking, {slashed with for_double_baking = true}) + in + let*! ctxt = + Storage.Slashed_deposits.add + (ctxt, level.cycle) + (level.level, delegate) + updated_slashed + in + return (ctxt, already_slashed) + +let check_and_record_already_slashed_for_double_endorsing ctxt delegate + (level : Level_repr.t) = + let open Lwt_tzresult_syntax in + let* slashed = + Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) + in + let already_slashed, updated_slashed = + match slashed with + | None -> + (false, {Storage.for_double_endorsing = true; for_double_baking = false}) + | Some slashed -> + ( slashed.for_double_endorsing, + {slashed with for_double_endorsing = true} ) + in + let*! ctxt = + Storage.Slashed_deposits.add + (ctxt, level.cycle) + (level.level, delegate) + updated_slashed + in + return (ctxt, already_slashed) + +let clear_outdated_slashed_deposits ctxt ~new_cycle = + let max_slashable_period = Constants_storage.max_slashing_period ctxt in + match Cycle_repr.(sub new_cycle max_slashable_period) with + | None -> Lwt.return ctxt + | Some outdated_cycle -> Storage.Slashed_deposits.clear (ctxt, outdated_cycle) + +let punish_double_endorsing ctxt delegate = + let delegate_contract = Contract_repr.Implicit delegate in + Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> + let slashing_ratio : Ratio_repr.t = + Constants_storage.ratio_of_frozen_deposits_slashed_per_double_endorsement + ctxt + in + let punish_value = + Tez_repr.( + div_exn + (mul_exn frozen_deposits.initial_amount slashing_ratio.numerator) + slashing_ratio.denominator) + in + let amount_to_burn = + Tez_repr.(min frozen_deposits.current_amount punish_value) + in + Token.transfer + ctxt + (`Frozen_deposits delegate) + `Double_signing_punishments + amount_to_burn + >>=? fun (ctxt, balance_updates) -> + Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> + return (ctxt, amount_to_burn, balance_updates) + +let punish_double_baking ctxt delegate = + let delegate_contract = Contract_repr.Implicit delegate in + Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> + let slashing_for_one_block = + Constants_storage.double_baking_punishment ctxt + in + let amount_to_burn = + Tez_repr.(min frozen_deposits.current_amount slashing_for_one_block) + in + Token.transfer + ctxt + (`Frozen_deposits delegate) + `Double_signing_punishments + amount_to_burn + >>=? fun (ctxt, balance_updates) -> + Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> + return (ctxt, amount_to_burn, balance_updates) diff --git a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli new file mode 100644 index 000000000000..474456f6c49d --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli @@ -0,0 +1,61 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) +(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Burn some frozen deposit for a delegate at a given level. Returns + the burned amount. *) +val punish_double_endorsing : + Raw_context.t -> + Signature.Public_key_hash.t -> + (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + +val punish_double_baking : + Raw_context.t -> + Signature.Public_key_hash.t -> + (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + +(** Returns true if the given delegate has already been slashed + for double baking for the given level, and record in the context + that the given delegate has now been slashed for double baking + for the given level. *) +val check_and_record_already_slashed_for_double_baking : + Raw_context.t -> + Signature.Public_key_hash.t -> + Level_repr.t -> + (Raw_context.t * bool) tzresult Lwt.t + +(** Returns true if the given delegate has already been slashed for + double preendorsing or double endorsing for the given level, and + record in the context that the given delegate has now been slashed + for double preendorsing or double endorsing for the given level. *) +val check_and_record_already_slashed_for_double_endorsing : + Raw_context.t -> + Signature.Public_key_hash.t -> + Level_repr.t -> + (Raw_context.t * bool) tzresult Lwt.t + +val clear_outdated_slashed_deposits : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index d2f0fa7c7656..8d10d9fad5c4 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -170,6 +171,37 @@ let () = (function Not_registered pkh -> Some pkh | _ -> None) (fun pkh -> Not_registered pkh) +(* The fact that this succeeds iff [registered ctxt pkh] returns true is an + invariant of the [set] function. *) +let check_delegate ctxt pkh = + Storage.Delegates.mem ctxt pkh >>= function + | true -> return_unit + | false -> fail (Not_registered pkh) + +let fold = Storage.Delegates.fold + +let list = Storage.Delegates.elements + +let pubkey ctxt pkh = + Contract_manager_storage.get_manager_key + ctxt + pkh + ~error:(Unregistered_delegate pkh) + +let frozen_deposits_limit ctxt delegate = + Storage.Contract.Frozen_deposits_limit.find + ctxt + (Contract_repr.Implicit delegate) + +let set_frozen_deposits_limit ctxt delegate limit = + Storage.Contract.Frozen_deposits_limit.add_or_remove + ctxt + (Contract_repr.Implicit delegate) + limit + +let frozen_deposits ctxt delegate = + Frozen_deposits_storage.get ctxt (Contract_repr.Implicit delegate) + let set_inactive ctxt delegate = Delegate_activation_storage.set_inactive ctxt delegate >>= fun ctxt -> Stake_storage.deactivate_only_call_from_delegate_storage ctxt delegate >|= ok @@ -180,16 +212,30 @@ let set_active ctxt delegate = if not inactive then return ctxt else Stake_storage.activate_only_call_from_delegate_storage ctxt delegate +let balance ctxt delegate = + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Spendable_balance.get ctxt contract + let staking_balance ctxt delegate = Contract_delegate_storage.registered ctxt delegate >>=? fun is_registered -> if is_registered then Stake_storage.get_staking_balance ctxt delegate else return Tez_repr.zero -let pubkey ctxt delegate = - Contract_manager_storage.get_manager_key - ctxt - delegate - ~error:(Unregistered_delegate delegate) +let delegated_balance ctxt delegate = + staking_balance ctxt delegate >>=? fun staking_balance -> + balance ctxt delegate >>=? fun balance -> + frozen_deposits ctxt delegate >>=? fun frozen_deposits -> + Tez_repr.(balance +? frozen_deposits.current_amount) + >>?= fun self_staking_balance -> + Lwt.return Tez_repr.(staking_balance -? self_staking_balance) + +let full_balance ctxt delegate = + frozen_deposits ctxt delegate >>=? fun frozen_deposits -> + let delegate_contract = Contract_repr.Implicit delegate in + Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract + >>=? fun balance_and_frozen_bonds -> + Lwt.return + Tez_repr.(frozen_deposits.current_amount +? balance_and_frozen_bonds) let init ctxt contract delegate = Contract_manager_storage.is_manager_key_revealed ctxt delegate @@ -254,7 +300,7 @@ let set c contract delegate = else return_unit | Originated _ -> return_unit) >>=? fun () -> - Storage.Contract.Spendable_balance.mem c contract >>= fun exists -> + Contract_storage.allocated c contract >>= fun exists -> error_when (self_delegation && not exists) (Empty_delegate_account delegate) @@ -269,785 +315,3 @@ let set c contract delegate = if self_delegation then Storage.Delegates.add c delegate >>= fun c -> set_active c delegate else return c - -let frozen_deposits_limit ctxt delegate = - Storage.Contract.Frozen_deposits_limit.find - ctxt - (Contract_repr.Implicit delegate) - -let set_frozen_deposits_limit ctxt delegate limit = - Storage.Contract.Frozen_deposits_limit.add_or_remove - ctxt - (Contract_repr.Implicit delegate) - limit - -let update_activity ctxt last_cycle = - let preserved = Constants_storage.preserved_cycles ctxt in - match Cycle_repr.sub last_cycle preserved with - | None -> return (ctxt, []) - | Some _unfrozen_cycle -> - Stake_storage.fold_on_active_delegates_with_rolls - ctxt - ~order:`Sorted - ~init:(Ok (ctxt, [])) - ~f:(fun delegate () acc -> - acc >>?= fun (ctxt, deactivated) -> - Delegate_activation_storage.last_cycle_before_deactivation - ctxt - delegate - >>=? fun cycle -> - if Cycle_repr.(cycle <= last_cycle) then - set_inactive ctxt delegate >|=? fun ctxt -> - (ctxt, delegate :: deactivated) - else return (ctxt, deactivated)) - >|=? fun (ctxt, deactivated) -> (ctxt, deactivated) - -let expected_slots_for_given_active_stake ctxt ~total_active_stake ~active_stake - = - let blocks_per_cycle = - Int32.to_int (Constants_storage.blocks_per_cycle ctxt) - in - let consensus_committee_size = - Constants_storage.consensus_committee_size ctxt - in - let number_of_endorsements_per_cycle = - blocks_per_cycle * consensus_committee_size - in - Result.return - (Z.to_int - (Z.div - (Z.mul - (Z.of_int64 (Tez_repr.to_mutez active_stake)) - (Z.of_int number_of_endorsements_per_cycle)) - (Z.of_int64 (Tez_repr.to_mutez total_active_stake)))) - -let delegate_participated_enough ctxt delegate = - Storage.Contract.Missed_endorsements.find ctxt delegate >>=? function - | None -> return_true - | Some missed_endorsements -> - return Compare.Int.(missed_endorsements.remaining_slots >= 0) - -let delegate_has_revealed_nonces delegate unrevelead_nonces_set = - not (Signature.Public_key_hash.Set.mem delegate unrevelead_nonces_set) - -let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = - let endorsing_reward_per_slot = - Constants_storage.endorsing_reward_per_slot ctxt - in - let unrevealed_nonces_set = - List.fold_left - (fun set {Storage.Seed.nonce_hash = _; delegate} -> - Signature.Public_key_hash.Set.add delegate set) - Signature.Public_key_hash.Set.empty - unrevealed_nonces - in - Stake_storage.get_total_active_stake ctxt last_cycle - >>=? fun total_active_stake -> - Stake_storage.get_selected_distribution ctxt last_cycle >>=? fun delegates -> - List.fold_left_es - (fun (ctxt, balance_updates) (delegate, active_stake) -> - let delegate_contract = Contract_repr.Implicit delegate in - delegate_participated_enough ctxt delegate_contract - >>=? fun sufficient_participation -> - let has_revealed_nonces = - delegate_has_revealed_nonces delegate unrevealed_nonces_set - in - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_slots -> - let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in - (if sufficient_participation && has_revealed_nonces then - (* Sufficient participation: we pay the rewards *) - Token.transfer - ctxt - `Endorsing_rewards - (`Contract delegate_contract) - rewards - >|=? fun (ctxt, payed_rewards_receipts) -> - (ctxt, payed_rewards_receipts @ balance_updates) - else - (* Insufficient participation or unrevealed nonce: no rewards *) - Token.transfer - ctxt - `Endorsing_rewards - (`Lost_endorsing_rewards - (delegate, not sufficient_participation, not has_revealed_nonces)) - rewards - >|=? fun (ctxt, payed_rewards_receipts) -> - (ctxt, payed_rewards_receipts @ balance_updates)) - >>=? fun (ctxt, balance_updates) -> - Storage.Contract.Missed_endorsements.remove ctxt delegate_contract - >>= fun ctxt -> return (ctxt, balance_updates)) - (ctxt, []) - delegates - -let clear_outdated_slashed_deposits ctxt ~new_cycle = - let max_slashable_period = Constants_storage.max_slashing_period ctxt in - match Cycle_repr.(sub new_cycle max_slashable_period) with - | None -> Lwt.return ctxt - | Some outdated_cycle -> Storage.Slashed_deposits.clear (ctxt, outdated_cycle) - -(* Return a map from delegates (with active stake at some cycle - in the cycle window [from_cycle, to_cycle]) to the maximum - of the stake to be deposited for each such cycle (which is just the - [frozen_deposits_percentage] of the active stake at that cycle). Also - return the delegates that have fallen out of the sliding window. *) -let max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle = - let frozen_deposits_percentage = - Constants_storage.frozen_deposits_percentage ctxt - in - let cycles = Cycle_repr.(from_cycle ---> to_cycle) in - (match Cycle_repr.pred from_cycle with - | None -> return Signature.Public_key_hash.Set.empty - | Some cleared_cycle -> ( - Stake_storage.find_selected_distribution ctxt cleared_cycle - >|=? fun cleared_cycle_delegates -> - match cleared_cycle_delegates with - | None -> Signature.Public_key_hash.Set.empty - | Some delegates -> - List.fold_left - (fun set (d, _) -> Signature.Public_key_hash.Set.add d set) - Signature.Public_key_hash.Set.empty - delegates)) - >>=? fun cleared_cycle_delegates -> - List.fold_left_es - (fun (maxima, delegates_to_remove) (cycle : Cycle_repr.t) -> - Stake_storage.get_selected_distribution ctxt cycle - >|=? fun active_stakes -> - List.fold_left - (fun (maxima, delegates_to_remove) (delegate, stake) -> - let stake_to_be_deposited = - Tez_repr.(div_exn (mul_exn stake frozen_deposits_percentage) 100) - in - let maxima = - Signature.Public_key_hash.Map.update - delegate - (function - | None -> Some stake_to_be_deposited - | Some maximum -> - Some (Tez_repr.max maximum stake_to_be_deposited)) - maxima - in - let delegates_to_remove = - Signature.Public_key_hash.Set.remove delegate delegates_to_remove - in - (maxima, delegates_to_remove)) - (maxima, delegates_to_remove) - active_stakes) - (Signature.Public_key_hash.Map.empty, cleared_cycle_delegates) - cycles - -let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle - ~balance_updates = - let max_slashable_period = Constants_storage.max_slashing_period ctxt in - (* We want to be able to slash for at most [max_slashable_period] *) - (match Cycle_repr.(sub new_cycle (max_slashable_period - 1)) with - | None -> - Storage.Tenderbake.First_level_of_protocol.get ctxt - >>=? fun first_level_of_protocol -> - let cycle_eras = Raw_context.cycle_eras ctxt in - let level = - Level_repr.level_from_raw ~cycle_eras first_level_of_protocol - in - return level.cycle - | Some cycle -> return cycle) - >>=? fun from_cycle -> - let preserved_cycles = Constants_storage.preserved_cycles ctxt in - let to_cycle = Cycle_repr.(add new_cycle preserved_cycles) in - max_frozen_deposits_and_delegates_to_remove ctxt ~from_cycle ~to_cycle - >>=? fun (maxima, delegates_to_remove) -> - Signature.Public_key_hash.Map.fold_es - (fun delegate maximum_stake_to_be_deposited (ctxt, balance_updates) -> - (* Here we make sure to preserve the following invariant : - maximum_stake_to_be_deposited <= frozen_deposits + balance - See select_distribution_for_cycle *) - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.update_initial_amount - ctxt - delegate_contract - maximum_stake_to_be_deposited - >>=? fun ctxt -> - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun deposits -> - let current_amount = deposits.current_amount in - if Tez_repr.(current_amount > maximum_stake_to_be_deposited) then - Tez_repr.(current_amount -? maximum_stake_to_be_deposited) - >>?= fun to_reimburse -> - Token.transfer - ~origin - ctxt - (`Frozen_deposits delegate) - (`Delegate_balance delegate) - to_reimburse - >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) - else if Tez_repr.(current_amount < maximum_stake_to_be_deposited) then - Tez_repr.(maximum_stake_to_be_deposited -? current_amount) - >>?= fun desired_to_freeze -> - Storage.Contract.Spendable_balance.get ctxt delegate_contract - >>=? fun balance -> - (* In case the delegate hasn't been slashed in this cycle, - the following invariant holds: - maximum_stake_to_be_deposited <= frozen_deposits + balance - See select_distribution_for_cycle - - If the delegate has been slashed during the cycle, the invariant - above doesn't necessarily hold. In this case, we freeze the max - we can for the delegate. *) - let to_freeze = Tez_repr.(min balance desired_to_freeze) in - Token.transfer - ~origin - ctxt - (`Delegate_balance delegate) - (`Frozen_deposits delegate) - to_freeze - >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) - else return (ctxt, balance_updates)) - maxima - (ctxt, balance_updates) - >>=? fun (ctxt, balance_updates) -> - (* Unfreeze deposits (that is, set them to zero) for delegates that - were previously in the relevant window (and therefore had some - frozen deposits) but are not in the new window; because that means - that such a delegate had no active stake in the relevant cycles, - and therefore it should have no frozen deposits. *) - Signature.Public_key_hash.Set.fold_es - (fun delegate (ctxt, balance_updates) -> - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.update_initial_amount - ctxt - delegate_contract - Tez_repr.zero - >>=? fun ctxt -> - Frozen_deposits_storage.get ctxt delegate_contract - >>=? fun frozen_deposits -> - if Tez_repr.(frozen_deposits.current_amount > zero) then - Token.transfer - ~origin - ctxt - (`Frozen_deposits delegate) - (`Delegate_balance delegate) - frozen_deposits.current_amount - >|=? fun (ctxt, bupds) -> (ctxt, bupds @ balance_updates) - else return (ctxt, balance_updates)) - delegates_to_remove - (ctxt, balance_updates) - -let freeze_deposits_do_not_call_except_for_migration = - freeze_deposits ~origin:Protocol_migration - -module Delegate_sampler_state = struct - module Cache_client = struct - type cached_value = - (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t - - let namespace = Cache_repr.create_namespace "sampler_state" - - let cache_index = 2 - - let value_of_identifier ctxt identifier = - let cycle = Cycle_repr.of_string_exn identifier in - Storage.Delegate_sampler_state.get ctxt cycle - end - - module Cache = (val Cache_repr.register_exn (module Cache_client)) - - let identifier_of_cycle cycle = Format.asprintf "%a" Cycle_repr.pp cycle - - let init ctxt cycle sampler_state = - let id = identifier_of_cycle cycle in - Storage.Delegate_sampler_state.init ctxt cycle sampler_state - >>=? fun ctxt -> - let size = 1 (* that's symbolic: 1 cycle = 1 entry *) in - Cache.update ctxt id (Some (sampler_state, size)) >>?= fun ctxt -> - return ctxt - - let get ctxt cycle = - let id = identifier_of_cycle cycle in - Cache.find ctxt id >>=? function - | None -> Storage.Delegate_sampler_state.get ctxt cycle - | Some v -> return v - - let remove_existing ctxt cycle = - let id = identifier_of_cycle cycle in - Cache.update ctxt id None >>?= fun ctxt -> - Storage.Delegate_sampler_state.remove_existing ctxt cycle -end - -let get_stakes_for_selected_index ctxt index = - Stake_storage.fold_snapshot - ctxt - ~index - ~f:(fun (delegate, staking_balance) (acc, total_stake) -> - let delegate_contract = Contract_repr.Implicit delegate in - let open Tez_repr in - let open Lwt_result_syntax in - let* frozen_deposits_limit = - Storage.Contract.Frozen_deposits_limit.find ctxt delegate_contract - in - - let* balance_and_frozen_bonds = - Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract - in - let* frozen_deposits = - Frozen_deposits_storage.get ctxt delegate_contract - in - let*? total_balance = - balance_and_frozen_bonds +? frozen_deposits.current_amount - in - let* stake_for_cycle = - let frozen_deposits_percentage = - Int64.of_int @@ Constants_storage.frozen_deposits_percentage ctxt - in - let max_mutez = of_mutez_exn Int64.max_int in - let frozen_deposits_limit = - match frozen_deposits_limit with Some fdp -> fdp | None -> max_mutez - in - let aux = min total_balance frozen_deposits_limit in - let*? overflow_bound = max_mutez /? 100L in - if aux <= overflow_bound then - let*? aux = aux *? 100L in - let*? v = aux /? frozen_deposits_percentage in - return (min v staking_balance) - else - let*? sbal = staking_balance /? 100L in - let*? a = aux /? frozen_deposits_percentage in - if sbal <= a then return staking_balance - else - let*? r = max_mutez /? frozen_deposits_percentage in - return r - in - let*? total_stake = Tez_repr.(total_stake +? stake_for_cycle) in - return ((delegate, stake_for_cycle) :: acc, total_stake)) - ~init:([], Tez_repr.zero) - -let compute_snapshot_index_for_seed ~max_snapshot_index seed = - let rd = Seed_repr.initialize_new seed [Bytes.of_string "stake_snapshot"] in - let seq = Seed_repr.sequence rd 0l in - Seed_repr.take_int32 seq (Int32.of_int max_snapshot_index) - |> fst |> Int32.to_int |> return - -let compute_snapshot_index ctxt cycle ~max_snapshot_index = - Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> - compute_snapshot_index_for_seed ~max_snapshot_index seed - -let select_distribution_for_cycle ctxt cycle = - Stake_storage.max_snapshot_index ctxt >>=? fun max_snapshot_index -> - Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> - compute_snapshot_index_for_seed ~max_snapshot_index seed - >>=? fun selected_index -> - get_stakes_for_selected_index ctxt selected_index - >>=? fun (stakes, total_stake) -> - Stake_storage.set_selected_distribution_for_cycle - ctxt - cycle - stakes - total_stake - >>=? fun ctxt -> - List.fold_left_es - (fun acc (pkh, stake) -> - pubkey ctxt pkh >|=? fun pk -> ((pk, pkh), Tez_repr.to_mutez stake) :: acc) - [] - stakes - >>=? fun stakes_pk -> - let state = Sampler.create stakes_pk in - Delegate_sampler_state.init ctxt cycle state >>=? fun ctxt -> - (* pre-allocate the sampler *) - Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) - -let select_new_distribution_at_cycle_end ctxt ~new_cycle = - let preserved = Constants_storage.preserved_cycles ctxt in - let for_cycle = Cycle_repr.add new_cycle preserved in - select_distribution_for_cycle ctxt for_cycle - -let clear_outdated_sampling_data ctxt ~new_cycle = - let max_slashing_period = Constants_storage.max_slashing_period ctxt in - match Cycle_repr.sub new_cycle max_slashing_period with - | None -> return ctxt - | Some outdated_cycle -> - Delegate_sampler_state.remove_existing ctxt outdated_cycle - >>=? fun ctxt -> - Storage.Seed.For_cycle.remove_existing ctxt outdated_cycle - -let cycle_end ctxt last_cycle unrevealed_nonces = - let new_cycle = Cycle_repr.add last_cycle 1 in - select_new_distribution_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> - clear_outdated_slashed_deposits ctxt ~new_cycle >>= fun ctxt -> - distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces - >>=? fun (ctxt, balance_updates) -> - freeze_deposits ctxt ~new_cycle ~balance_updates - >>=? fun (ctxt, balance_updates) -> - Stake_storage.clear_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> - clear_outdated_sampling_data ctxt ~new_cycle >>=? fun ctxt -> - update_activity ctxt last_cycle >>=? fun (ctxt, deactivated_delagates) -> - return (ctxt, balance_updates, deactivated_delagates) - -let balance ctxt delegate = - let contract = Contract_repr.Implicit delegate in - Storage.Contract.Spendable_balance.get ctxt contract - -let frozen_deposits ctxt delegate = - Frozen_deposits_storage.get ctxt (Contract_repr.Implicit delegate) - -let full_balance ctxt delegate = - frozen_deposits ctxt delegate >>=? fun frozen_deposits -> - let delegate_contract = Contract_repr.Implicit delegate in - Contract_storage.get_balance_and_frozen_bonds ctxt delegate_contract - >>=? fun balance_and_frozen_bonds -> - Lwt.return - Tez_repr.(frozen_deposits.current_amount +? balance_and_frozen_bonds) - -let deactivated = Delegate_activation_storage.is_inactive - -let delegated_balance ctxt delegate = - staking_balance ctxt delegate >>=? fun staking_balance -> - balance ctxt delegate >>=? fun balance -> - frozen_deposits ctxt delegate >>=? fun frozen_deposits -> - Tez_repr.(balance +? frozen_deposits.current_amount) - >>?= fun self_staking_balance -> - Lwt.return Tez_repr.(staking_balance -? self_staking_balance) - -let fold = Storage.Delegates.fold - -let list = Storage.Delegates.elements - -(* The fact that this succeeds iff [registered ctxt pkh] returns true is an - invariant of the [set] function. *) -let check_delegate ctxt pkh = - Storage.Delegates.mem ctxt pkh >>= function - | true -> return_unit - | false -> fail (Not_registered pkh) - -module Random = struct - (* [init_random_state] initialize a random sequence drawing state - that's unique for a given (seed, level, index) triple. Elements - from this sequence are drawn using [take_int64], updating the - state for the next draw. The initial state is the Blake2b hash of - the three randomness sources, and an offset set to zero - (indicating that zero bits of randomness have been - consumed). When drawing random elements, bits are extracted from - the state until exhaustion (256 bits), at which point the state - is rehashed and the offset reset to 0. *) - - let init_random_state seed level index = - ( Raw_hashes.blake2b - (Data_encoding.Binary.to_bytes_exn - Data_encoding.(tup3 Seed_repr.seed_encoding int32 int32) - (seed, level.Level_repr.cycle_position, Int32.of_int index)), - 0 ) - - let take_int64 bound state = - let drop_if_over = - (* This function draws random values in [0-(bound-1)] by drawing - in [0-(2^63-1)] (64-bit) and computing the value modulo - [bound]. For the application of [mod bound] to preserve - uniformity, the input space must be of the form - [0-(n*bound-1)]. We enforce this by rejecting 64-bit samples - above this limit (in which case, we draw a new 64-sample from - the sequence and try again). *) - Int64.sub Int64.max_int (Int64.rem Int64.max_int bound) - in - let rec loop (bytes, n) = - let consumed_bytes = 8 in - let state_size = Bytes.length bytes in - if Compare.Int.(n > state_size - consumed_bytes) then - loop (Raw_hashes.blake2b bytes, 0) - else - let r = TzEndian.get_int64 bytes n in - (* The absolute value of min_int is min_int. Also, every - positive integer is represented twice (positive and negative), - but zero is only represented once. We fix both problems at - once. *) - let r = if Compare.Int64.(r = Int64.min_int) then 0L else Int64.abs r in - if Compare.Int64.(r >= drop_if_over) then - loop (bytes, n + consumed_bytes) - else - let v = Int64.rem r bound in - (v, (bytes, n + consumed_bytes)) - in - loop state - - (** [sampler_for_cycle ctxt cycle] reads the sampler for [cycle] from - [ctxt] if it has been previously inited. Otherwise it initializes - the sampler and caches it in [ctxt] with - [Raw_context.set_sampler_for_cycle]. *) - let sampler_for_cycle ctxt cycle = - let read ctxt = - Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> - Delegate_sampler_state.get ctxt cycle >>=? fun state -> - return (seed, state) - in - Raw_context.sampler_for_cycle ~read ctxt cycle - - let owner c (level : Level_repr.t) offset = - let cycle = level.Level_repr.cycle in - sampler_for_cycle c cycle >>=? fun (c, seed, state) -> - let sample ~int_bound ~mass_bound = - let state = init_random_state seed level offset in - let i, state = take_int64 (Int64.of_int int_bound) state in - let elt, _ = take_int64 mass_bound state in - (Int64.to_int i, elt) - in - let pk, pkh = Sampler.sample state sample in - return (c, (pk, pkh)) -end - -let slot_owner c level slot = Random.owner c level (Slot_repr.to_int slot) - -let baking_rights_owner c (level : Level_repr.t) ~round = - Round_repr.to_int round >>?= fun round -> - let consensus_committee_size = Constants_storage.consensus_committee_size c in - Slot_repr.of_int (round mod consensus_committee_size) >>?= fun slot -> - slot_owner c level slot >>=? fun (ctxt, pk) -> return (ctxt, slot, pk) - -let already_slashed_for_double_endorsing ctxt delegate (level : Level_repr.t) = - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? function - | None -> return_false - | Some slashed -> return slashed.for_double_endorsing - -let already_slashed_for_double_baking ctxt delegate (level : Level_repr.t) = - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? function - | None -> return_false - | Some slashed -> return slashed.for_double_baking - -let punish_double_endorsing ctxt delegate (level : Level_repr.t) = - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> - let slashing_ratio : Ratio_repr.t = - Constants_storage.ratio_of_frozen_deposits_slashed_per_double_endorsement - ctxt - in - let punish_value = - Tez_repr.( - div_exn - (mul_exn frozen_deposits.initial_amount slashing_ratio.numerator) - slashing_ratio.denominator) - in - let amount_to_burn = - Tez_repr.(min frozen_deposits.current_amount punish_value) - in - Token.transfer - ctxt - (`Frozen_deposits delegate) - `Double_signing_punishments - amount_to_burn - >>=? fun (ctxt, balance_updates) -> - Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? fun slashed -> - let slashed : Storage.slashed_level = - match slashed with - | None -> {for_double_endorsing = true; for_double_baking = false} - | Some slashed -> - assert (Compare.Bool.(slashed.for_double_endorsing = false)) ; - {slashed with for_double_endorsing = true} - in - Storage.Slashed_deposits.add - (ctxt, level.cycle) - (level.level, delegate) - slashed - >>= fun ctxt -> return (ctxt, amount_to_burn, balance_updates) - -let punish_double_baking ctxt delegate (level : Level_repr.t) = - let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> - let slashing_for_one_block = - Constants_storage.double_baking_punishment ctxt - in - let amount_to_burn = - Tez_repr.(min frozen_deposits.current_amount slashing_for_one_block) - in - Token.transfer - ctxt - (`Frozen_deposits delegate) - `Double_signing_punishments - amount_to_burn - >>=? fun (ctxt, balance_updates) -> - Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - >>=? fun slashed -> - let slashed : Storage.slashed_level = - match slashed with - | None -> {for_double_endorsing = false; for_double_baking = true} - | Some slashed -> - assert (Compare.Bool.(slashed.for_double_baking = false)) ; - {slashed with for_double_baking = true} - in - Storage.Slashed_deposits.add - (ctxt, level.cycle) - (level.level, delegate) - slashed - >>= fun ctxt -> return (ctxt, amount_to_burn, balance_updates) - -type level_participation = Participated | Didn't_participate - -(* Note that the participation for the last block of a cycle is - recorded in the next cycle. *) -let record_endorsing_participation ctxt ~delegate ~participation - ~endorsing_power = - match participation with - | Participated -> set_active ctxt delegate - | Didn't_participate -> ( - let contract = Contract_repr.Implicit delegate in - Storage.Contract.Missed_endorsements.find ctxt contract >>=? function - | Some {remaining_slots; missed_levels} -> - let remaining_slots = remaining_slots - endorsing_power in - Storage.Contract.Missed_endorsements.update - ctxt - contract - {remaining_slots; missed_levels = missed_levels + 1} - | None -> ( - let level = Level_storage.current ctxt in - Raw_context.stake_distribution_for_current_cycle ctxt - >>?= fun stake_distribution -> - match - Signature.Public_key_hash.Map.find delegate stake_distribution - with - | None -> - (* This happens when the block is the first one in a - cycle, and therefore the endorsements are for the last - block of the previous cycle, and when the delegate does - not have an active stake at the current cycle; in this - case its participation is simply ignored. *) - assert (Compare.Int32.(level.cycle_position = 0l)) ; - return ctxt - | Some active_stake -> - Stake_storage.get_total_active_stake ctxt level.cycle - >>=? fun total_active_stake -> - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_slots -> - let Ratio_repr.{numerator; denominator} = - Constants_storage.minimal_participation_ratio ctxt - in - let minimal_activity = expected_slots * numerator / denominator in - let maximal_inactivity = expected_slots - minimal_activity in - let remaining_slots = maximal_inactivity - endorsing_power in - Storage.Contract.Missed_endorsements.init - ctxt - contract - {remaining_slots; missed_levels = 1})) - -let record_baking_activity_and_pay_rewards_and_fees ctxt ~payload_producer - ~block_producer ~baking_reward ~reward_bonus = - set_active ctxt payload_producer >>=? fun ctxt -> - (if not (Signature.Public_key_hash.equal payload_producer block_producer) then - set_active ctxt block_producer - else return ctxt) - >>=? fun ctxt -> - let pay_payload_producer ctxt delegate = - let contract = Contract_repr.Implicit delegate in - Token.balance ctxt `Block_fees >>=? fun (ctxt, block_fees) -> - Token.transfer_n - ctxt - [(`Block_fees, block_fees); (`Baking_rewards, baking_reward)] - (`Contract contract) - in - let pay_block_producer ctxt delegate bonus = - let contract = Contract_repr.Implicit delegate in - Token.transfer ctxt `Baking_bonuses (`Contract contract) bonus - in - pay_payload_producer ctxt payload_producer - >>=? fun (ctxt, balance_updates_payload_producer) -> - (match reward_bonus with - | Some bonus -> pay_block_producer ctxt block_producer bonus - | None -> return (ctxt, [])) - >>=? fun (ctxt, balance_updates_block_producer) -> - return - (ctxt, balance_updates_payload_producer @ balance_updates_block_producer) - -type participation_info = { - expected_cycle_activity : int; - minimal_cycle_activity : int; - missed_slots : int; - missed_levels : int; - remaining_allowed_missed_slots : int; - expected_endorsing_rewards : Tez_repr.t; -} - -(* Inefficient, only for RPC *) -let delegate_participation_info ctxt delegate = - let level = Level_storage.current ctxt in - Stake_storage.get_selected_distribution ctxt level.cycle - >>=? fun stake_distribution -> - match - List.assoc_opt - ~equal:Signature.Public_key_hash.equal - delegate - stake_distribution - with - | None -> - (* delegate does not have an active stake at the current cycle *) - return - { - expected_cycle_activity = 0; - minimal_cycle_activity = 0; - missed_slots = 0; - missed_levels = 0; - remaining_allowed_missed_slots = 0; - expected_endorsing_rewards = Tez_repr.zero; - } - | Some active_stake -> - Stake_storage.get_total_active_stake ctxt level.cycle - >>=? fun total_active_stake -> - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_cycle_activity -> - let Ratio_repr.{numerator; denominator} = - Constants_storage.minimal_participation_ratio ctxt - in - let endorsing_reward_per_slot = - Constants_storage.endorsing_reward_per_slot ctxt - in - let minimal_cycle_activity = - expected_cycle_activity * numerator / denominator - in - let maximal_cycle_inactivity = - expected_cycle_activity - minimal_cycle_activity - in - let expected_endorsing_rewards = - Tez_repr.mul_exn endorsing_reward_per_slot expected_cycle_activity - in - let contract = Contract_repr.Implicit delegate in - Storage.Contract.Missed_endorsements.find ctxt contract - >>=? fun missed_endorsements -> - let missed_slots, missed_levels, remaining_allowed_missed_slots = - match missed_endorsements with - | None -> (0, 0, maximal_cycle_inactivity) - | Some {remaining_slots; missed_levels} -> - ( maximal_cycle_inactivity - remaining_slots, - missed_levels, - Compare.Int.max 0 remaining_slots ) - in - let expected_endorsing_rewards = - match missed_endorsements with - | Some r when Compare.Int.(r.remaining_slots < 0) -> Tez_repr.zero - | _ -> expected_endorsing_rewards - in - return - { - expected_cycle_activity; - minimal_cycle_activity; - missed_slots; - missed_levels; - remaining_allowed_missed_slots; - expected_endorsing_rewards; - } - -let init_first_cycles ctxt = - let preserved = Constants_storage.preserved_cycles ctxt in - List.fold_left_es - (fun ctxt c -> - let cycle = Cycle_repr.of_int32_exn (Int32.of_int c) in - Stake_storage.snapshot ctxt >>=? fun ctxt -> - (* NB: we need to take several snapshots because - select_distribution_for_cycle deletes the snapshots *) - select_distribution_for_cycle ctxt cycle) - ctxt - Misc.(0 --> preserved) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index cba3a98d9746..ce6f830baf59 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. *) (* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2022 G.B. Fefe, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -24,41 +25,6 @@ (* *) (*****************************************************************************) -(** Allow to register a delegate when creating an account. *) -val init : - Raw_context.t -> - Contract_repr.t -> - Signature.Public_key_hash.t -> - Raw_context.t tzresult Lwt.t - -val pubkey : - Raw_context.t -> - Signature.Public_key_hash.t -> - Signature.Public_key.t tzresult Lwt.t - -(** Updating the delegate of a contract. - - When calling this function on an "implicit contract" and setting - the delegate to the contract manager registers it as a delegate. One - cannot unregister a delegate for now. The associate contract is now - 'undeletable'. *) -val set : - Raw_context.t -> - Contract_repr.t -> - Signature.Public_key_hash.t option -> - Raw_context.t tzresult Lwt.t - -val frozen_deposits_limit : - Raw_context.t -> - Signature.Public_key_hash.t -> - Tez_repr.t option tzresult Lwt.t - -val set_frozen_deposits_limit : - Raw_context.t -> - Signature.Public_key_hash.t -> - Tez_repr.t option -> - Raw_context.t Lwt.t - type error += | (* `Permanent *) No_deletion of Signature.Public_key_hash.t | (* `Temporary *) Active_delegate @@ -77,39 +43,6 @@ type error += val check_delegate : Raw_context.t -> Signature.Public_key_hash.t -> unit tzresult Lwt.t -(** Participation information. We denote by: - - "static" information that does not change during the cycle - - "dynamic" information that may change during the cycle *) -type participation_info = { - expected_cycle_activity : int; - (** The total expected slots to be endorsed in the cycle. (static) *) - minimal_cycle_activity : int; - (** The minimal endorsing slots in the cycle to get endorsing - rewards. (static) *) - missed_slots : int; - (** The number of missed endorsing slots in the cycle. (dynamic) *) - missed_levels : int; - (** The number of missed endorsing levels in the cycle. (dynamic) *) - remaining_allowed_missed_slots : int; - (** Remaining amount of endorsing slots that can be missed in the - cycle before forfeiting the rewards. (dynamic) *) - expected_endorsing_rewards : Tez_repr.t; - (** Endorsing rewards that will be distributed at the end of the - cycle if activity at that point will be greater than the minimal - required. If the activity is already known to be below the - required minimum, then the rewards are zero. (dynamic) *) -} - -(** Only use this function for RPC: this is expensive. - - [delegate_participation_info] and [!val:check_delegate] forms the - implementation of RPC call "/context/delegates//participation". - *) -val delegate_participation_info : - Raw_context.t -> - Signature.Public_key_hash.t -> - participation_info tzresult Lwt.t - (** Iterate on all registered delegates. *) val fold : Raw_context.t -> @@ -121,85 +54,35 @@ val fold : (** List all registered delegates. *) val list : Raw_context.t -> Signature.Public_key_hash.t list Lwt.t -val balance : - Raw_context.t -> Signature.public_key_hash -> Tez_repr.tez tzresult Lwt.t - -type level_participation = Participated | Didn't_participate - -(** Record the participation of a delegate as a validator. *) -val record_endorsing_participation : - Raw_context.t -> - delegate:Signature.Public_key_hash.t -> - participation:level_participation -> - endorsing_power:int -> - Raw_context.t tzresult Lwt.t - -(** Sets the payload and block producer as active. Pays the baking - reward and the fees to the payload producer and the reward bonus to - the payload producer (if the reward_bonus is not None).*) -val record_baking_activity_and_pay_rewards_and_fees : - Raw_context.t -> - payload_producer:Signature.Public_key_hash.t -> - block_producer:Signature.Public_key_hash.t -> - baking_reward:Tez_repr.t -> - reward_bonus:Tez_repr.t option -> - (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t - -(** Trigger the context maintenance at the end of cycle 'n', i.e.: - unfreeze the endorsing rewards, potentially deactivate delegates. - Return the corresponding balances updates and the list of - deactivated delegates. *) -val cycle_end : - Raw_context.t -> - Cycle_repr.t -> - Storage.Seed.unrevealed_nonce list -> - (Raw_context.t - * Receipt_repr.balance_updates - * Signature.Public_key_hash.t list) - tzresult - Lwt.t - -(** Returns true if the given delegate has already been slashed - for double baking for the given level. *) -val already_slashed_for_double_baking : - Raw_context.t -> - Signature.Public_key_hash.t -> - Level_repr.t -> - bool tzresult Lwt.t - -(** Returns true if the given delegate has already been slashed - for double preendorsing or double endorsing for the given level. *) -val already_slashed_for_double_endorsing : - Raw_context.t -> - Signature.Public_key_hash.t -> - Level_repr.t -> - bool tzresult Lwt.t - -(** Burn some frozen deposit for a delegate at a given level. Returns - the burned amount. *) -val punish_double_endorsing : +val frozen_deposits_limit : Raw_context.t -> Signature.Public_key_hash.t -> - Level_repr.t -> - (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + Tez_repr.t option tzresult Lwt.t -val punish_double_baking : +val set_frozen_deposits_limit : Raw_context.t -> Signature.Public_key_hash.t -> - Level_repr.t -> - (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + Tez_repr.t option -> + Raw_context.t Lwt.t (** Returns a delegate's frozen deposits, both the current amount and the initial freezed amount. A delegate's frozen balance is only composed of frozen deposits; - rewards and fees are not frozen, but simply credited at the right - moment. *) + rewards and fees are not frozen, but simply credited at the right + moment. *) val frozen_deposits : Raw_context.t -> Signature.Public_key_hash.t -> Storage.deposits tzresult Lwt.t +val staking_balance : + Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t + +(** Only use this function for RPCs: this is expensive. *) +val delegated_balance : + Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t + (** Returns the full 'balance' of the implicit contract associated to a given key, i.e. the sum of the spendable balance (given by [balance] or [Contract_storage.get_balance]) and of the frozen balance. The frozen @@ -211,55 +94,27 @@ val frozen_deposits : val full_balance : Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t -val staking_balance : - Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t - -(** Only use this function for RPCs: this is expensive. *) -val delegated_balance : - Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t - -val deactivated : - Raw_context.t -> Signature.Public_key_hash.t -> bool tzresult Lwt.t - -(** Participation slots potentially associated to accounts. The - accounts that didn't place a deposit will be excluded from this - list. This function should only be used to compute the deposits to - freeze or initialize the protocol while stitching. RPCs can use this - function to predict an approximation of long term future slot - allocations. It shouldn't be used in the baker. *) -val slot_owner : +(** Allow to register a delegate when creating an account. *) +val init : Raw_context.t -> - Level_repr.t -> - Slot_repr.t -> - (Raw_context.t * (Signature.Public_key.t * Signature.Public_key_hash.t)) - tzresult - Lwt.t + Contract_repr.t -> + Signature.Public_key_hash.t -> + Raw_context.t tzresult Lwt.t -val baking_rights_owner : +(** Allow to set the delegate of an account. *) +val set : Raw_context.t -> - Level_repr.t -> - round:Round_repr.round -> - (Raw_context.t - * Slot_repr.t - * (Signature.public_key * Signature.public_key_hash)) - tzresult - Lwt.t + Contract_repr.t -> + Signature.Public_key_hash.t option -> + Raw_context.t tzresult Lwt.t -val freeze_deposits_do_not_call_except_for_migration : +val pubkey : Raw_context.t -> - new_cycle:Cycle_repr.t -> - balance_updates:Receipt_repr.balance_updates -> - (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t + Signature.Public_key_hash.t -> + Signature.Public_key.t tzresult Lwt.t -(** [init_first_cycles ctxt] computes and records the distribution of the total - active stake among active delegates. This concerns the total active stake - involved in the calculation of baking rights for all cycles in the range - [0, preserved_cycles]. *) -val init_first_cycles : Raw_context.t -> Raw_context.t tzresult Lwt.t +val set_active : + Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t -(** [compute_snapshot_index ctxt cycle max_snapshot_index] Returns the index of - the selected snapshot for the [cycle] passed as argument, and for the max - index of snapshots taken so far, [max_snapshot_index] (see - [Stake_storage.max_snapshot_index]. *) -val compute_snapshot_index : - Raw_context.t -> Cycle_repr.t -> max_snapshot_index:int -> int tzresult Lwt.t +val set_inactive : + Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 73761c300e8d..7b7cc9980b21 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -139,6 +139,10 @@ Contract_storage Token Delegate_storage + Delegate_missed_endorsements_storage + Delegate_slashed_deposits_storage + Delegate_sampler + Delegate_cycles Bootstrap_storage Vote_storage Fees_storage @@ -352,6 +356,11 @@ contract_storage.ml contract_storage.mli token.ml token.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli fees_storage.ml fees_storage.mli @@ -551,6 +560,11 @@ contract_storage.ml contract_storage.mli token.ml token.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli fees_storage.ml fees_storage.mli @@ -770,6 +784,11 @@ contract_storage.ml contract_storage.mli token.ml token.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli fees_storage.ml fees_storage.mli @@ -985,6 +1004,11 @@ contract_storage.ml contract_storage.mli token.ml token.mli delegate_storage.ml delegate_storage.mli + delegate_missed_endorsements_storage.ml + delegate_missed_endorsements_storage.mli + delegate_slashed_deposits_storage.ml delegate_slashed_deposits_storage.mli + delegate_sampler.ml delegate_sampler.mli + delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli fees_storage.ml fees_storage.mli diff --git a/src/proto_alpha/lib_protocol/init_storage.ml b/src/proto_alpha/lib_protocol/init_storage.ml index 258702c6e1f0..d5b1b058a73f 100644 --- a/src/proto_alpha/lib_protocol/init_storage.ml +++ b/src/proto_alpha/lib_protocol/init_storage.ml @@ -97,9 +97,9 @@ let prepare_first_block _chain_id ctxt ~typecheck ~level ~timestamp = param.bootstrap_accounts param.bootstrap_contracts >>=? fun (ctxt, bootstrap_balance_updates) -> - Delegate_storage.init_first_cycles ctxt >>=? fun ctxt -> + Delegate_cycles.init_first_cycles ctxt >>=? fun ctxt -> let cycle = (Raw_context.current_level ctxt).cycle in - Delegate_storage.freeze_deposits_do_not_call_except_for_migration + Delegate_cycles.freeze_deposits_do_not_call_except_for_migration ~new_cycle:cycle ~balance_updates:[] ctxt -- GitLab From 9301a568bc654400ad305af075dcea8d4a9176fb Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Thu, 21 Apr 2022 10:38:31 +0200 Subject: [PATCH 02/35] Proto: move `set_active` into `Stake_storage` --- .../lib_protocol/delegate_cycles.ml | 2 +- .../delegate_missed_endorsements_storage.ml | 6 +++--- .../lib_protocol/delegate_storage.ml | 13 ++---------- .../lib_protocol/delegate_storage.mli | 6 ------ src/proto_alpha/lib_protocol/stake_storage.ml | 21 ++++++++++++------- .../lib_protocol/stake_storage.mli | 4 ++-- 6 files changed, 21 insertions(+), 31 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index a866f2727f6d..7d5d6b0b11d3 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -41,7 +41,7 @@ let update_activity ctxt last_cycle = delegate >>=? fun cycle -> if Cycle_repr.(cycle <= last_cycle) then - Delegate_storage.set_inactive ctxt delegate >>=? fun ctxt -> + Stake_storage.set_inactive ctxt delegate >>= fun ctxt -> return (ctxt, delegate :: deactivated) else return (ctxt, deactivated)) >|=? fun (ctxt, deactivated) -> (ctxt, deactivated) diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml index b776c0eb8171..508d2852e8a3 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -51,7 +51,7 @@ type level_participation = Participated | Didn't_participate let record_endorsing_participation ctxt ~delegate ~participation ~endorsing_power = match participation with - | Participated -> Delegate_storage.set_active ctxt delegate + | Participated -> Stake_storage.set_active ctxt delegate | Didn't_participate -> ( let contract = Contract_repr.Implicit delegate in Storage.Contract.Missed_endorsements.find ctxt contract >>=? function @@ -97,9 +97,9 @@ let record_endorsing_participation ctxt ~delegate ~participation let record_baking_activity_and_pay_rewards_and_fees ctxt ~payload_producer ~block_producer ~baking_reward ~reward_bonus = - Delegate_storage.set_active ctxt payload_producer >>=? fun ctxt -> + Stake_storage.set_active ctxt payload_producer >>=? fun ctxt -> (if not (Signature.Public_key_hash.equal payload_producer block_producer) then - Delegate_storage.set_active ctxt block_producer + Stake_storage.set_active ctxt block_producer else return ctxt) >>=? fun ctxt -> let pay_payload_producer ctxt delegate = diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 8d10d9fad5c4..8935aed71ec7 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -202,16 +202,6 @@ let set_frozen_deposits_limit ctxt delegate limit = let frozen_deposits ctxt delegate = Frozen_deposits_storage.get ctxt (Contract_repr.Implicit delegate) -let set_inactive ctxt delegate = - Delegate_activation_storage.set_inactive ctxt delegate >>= fun ctxt -> - Stake_storage.deactivate_only_call_from_delegate_storage ctxt delegate >|= ok - -let set_active ctxt delegate = - Delegate_activation_storage.set_active ctxt delegate - >>=? fun (ctxt, inactive) -> - if not inactive then return ctxt - else Stake_storage.activate_only_call_from_delegate_storage ctxt delegate - let balance ctxt delegate = let contract = Contract_repr.Implicit delegate in Storage.Contract.Spendable_balance.get ctxt contract @@ -313,5 +303,6 @@ let set c contract delegate = Stake_storage.add_stake c delegate balance_and_frozen_bonds >>=? fun c -> if self_delegation then - Storage.Delegates.add c delegate >>= fun c -> set_active c delegate + Storage.Delegates.add c delegate >>= fun c -> + Stake_storage.set_active c delegate else return c diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index ce6f830baf59..089cb829199d 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -112,9 +112,3 @@ val pubkey : Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t - -val set_active : - Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t - -val set_inactive : - Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/stake_storage.ml b/src/proto_alpha/lib_protocol/stake_storage.ml index 9c34808d628e..c668430b87bd 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.ml +++ b/src/proto_alpha/lib_protocol/stake_storage.ml @@ -107,16 +107,21 @@ let add_stake ctxt delegate amount = roll now). *) return ctxt -let deactivate_only_call_from_delegate_storage ctxt delegate = +let set_inactive ctxt delegate = + Delegate_activation_storage.set_inactive ctxt delegate >>= fun ctxt -> Storage.Stake.Active_delegate_with_one_roll.remove ctxt delegate -let activate_only_call_from_delegate_storage ctxt delegate = - get_initialized_stake ctxt delegate >>=? fun (staking_balance, ctxt) -> - let tokens_per_roll = Constants_storage.tokens_per_roll ctxt in - if Tez_repr.(staking_balance >= tokens_per_roll) then - Storage.Stake.Active_delegate_with_one_roll.add ctxt delegate () - >>= fun ctxt -> return ctxt - else return ctxt +let set_active ctxt delegate = + Delegate_activation_storage.set_active ctxt delegate + >>=? fun (ctxt, inactive) -> + if not inactive then return ctxt + else + get_initialized_stake ctxt delegate >>=? fun (staking_balance, ctxt) -> + let tokens_per_roll = Constants_storage.tokens_per_roll ctxt in + if Tez_repr.(staking_balance >= tokens_per_roll) then + Storage.Stake.Active_delegate_with_one_roll.add ctxt delegate () + >>= fun ctxt -> return ctxt + else return ctxt let snapshot ctxt = Storage.Stake.Last_snapshot.get ctxt >>=? fun index -> diff --git a/src/proto_alpha/lib_protocol/stake_storage.mli b/src/proto_alpha/lib_protocol/stake_storage.mli index 894f683cd579..e41693973461 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.mli +++ b/src/proto_alpha/lib_protocol/stake_storage.mli @@ -38,10 +38,10 @@ val add_stake : Tez_repr.t -> Raw_context.t tzresult Lwt.t -val deactivate_only_call_from_delegate_storage : +val set_inactive : Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t Lwt.t -val activate_only_call_from_delegate_storage : +val set_active : Raw_context.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t val get_staking_balance : -- GitLab From eab4a563ee5a6380ddd7dd9389c52e3781b69ba5 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Thu, 21 Apr 2022 11:55:00 +0200 Subject: [PATCH 03/35] Proto: Rename `Delegate.check_delegate` into `Delegate.check` --- .../lib_protocol/alpha_context.mli | 2 +- .../lib_protocol/delegate_services.ml | 26 +++++++++---------- .../lib_protocol/delegate_storage.ml | 2 +- .../lib_protocol/delegate_storage.mli | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 5676d5d37e50..3e4e4e39c0e5 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2214,7 +2214,7 @@ module Delegate : sig val list : context -> public_key_hash list Lwt.t - val check_delegate : context -> public_key_hash -> unit tzresult Lwt.t + val check_registered : context -> public_key_hash -> unit tzresult Lwt.t type participation_info = { expected_cycle_activity : int; diff --git a/src/proto_alpha/lib_protocol/delegate_services.ml b/src/proto_alpha/lib_protocol/delegate_services.ml index b3daf7ff85bc..544711c5ad83 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.ml +++ b/src/proto_alpha/lib_protocol/delegate_services.ml @@ -365,7 +365,7 @@ let register () = | {with_minimal_stake = false; without_minimal_stake = false; _} -> return delegates) ; register1 ~chunked:false S.info (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.full_balance ctxt pkh >>=? fun full_balance -> Delegate.frozen_deposits ctxt pkh >>=? fun frozen_deposits -> Delegate.staking_balance ctxt pkh >>=? fun staking_balance -> @@ -388,42 +388,42 @@ let register () = voting_info; }) ; register1 ~chunked:false S.full_balance (fun ctxt pkh () () -> - trace (Balance_rpc_non_delegate pkh) (Delegate.check_delegate ctxt pkh) + trace (Balance_rpc_non_delegate pkh) (Delegate.check_registered ctxt pkh) >>=? fun () -> Delegate.full_balance ctxt pkh) ; register1 ~chunked:false S.current_frozen_deposits (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.frozen_deposits ctxt pkh >>=? fun deposits -> return deposits.current_amount) ; register1 ~chunked:false S.frozen_deposits (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.frozen_deposits ctxt pkh >>=? fun deposits -> return deposits.initial_amount) ; register1 ~chunked:false S.staking_balance (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.staking_balance ctxt pkh) ; register1 ~chunked:false S.frozen_deposits_limit (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.frozen_deposits_limit ctxt pkh) ; register1 ~chunked:true S.delegated_contracts (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.delegated_contracts ctxt pkh >|= ok) ; register1 ~chunked:false S.delegated_balance (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.delegated_balance ctxt pkh) ; register1 ~chunked:false S.deactivated (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.deactivated ctxt pkh) ; register1 ~chunked:false S.grace_period (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.last_cycle_before_deactivation ctxt pkh) ; register1 ~chunked:false S.voting_power (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Vote.get_voting_power_free ctxt pkh) ; register1 ~chunked:false S.voting_info (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Vote.get_delegate_info ctxt pkh) ; register1 ~chunked:false S.participation (fun ctxt pkh () () -> - Delegate.check_delegate ctxt pkh >>=? fun () -> + Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.delegate_participation_info ctxt pkh) let list ctxt block ?(active = true) ?(inactive = false) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 8935aed71ec7..39b2a8050110 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -173,7 +173,7 @@ let () = (* The fact that this succeeds iff [registered ctxt pkh] returns true is an invariant of the [set] function. *) -let check_delegate ctxt pkh = +let check_registered ctxt pkh = Storage.Delegates.mem ctxt pkh >>= function | true -> return_unit | false -> fail (Not_registered pkh) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 089cb829199d..05cda508fdfd 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -40,7 +40,7 @@ type error += | (* `Temporary *) Not_registered of Signature.Public_key_hash.t (** Check that a given implicit account is a registered delegate. *) -val check_delegate : +val check_registered : Raw_context.t -> Signature.Public_key_hash.t -> unit tzresult Lwt.t (** Iterate on all registered delegates. *) -- GitLab From 5febbae3d85567f3fde8296bc6e1204facc9c6eb Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 16:32:16 +0200 Subject: [PATCH 04/35] Proto: simplify `Delegate.registered` --- src/proto_alpha/lib_protocol/alpha_context.ml | 2 -- src/proto_alpha/lib_protocol/alpha_context.mli | 2 +- src/proto_alpha/lib_protocol/apply.ml | 2 +- .../lib_protocol/contract_delegate_storage.ml | 8 -------- .../lib_protocol/contract_delegate_storage.mli | 5 ----- .../lib_protocol/delegate_storage.ml | 17 ++++++++--------- .../lib_protocol/delegate_storage.mli | 3 +++ 7 files changed, 13 insertions(+), 26 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index b522a619e883..507c555c80f7 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -414,8 +414,6 @@ module Delegate = struct let prepare_stake_distribution = Stake_storage.prepare_stake_distribution - let registered = Contract_delegate_storage.registered - let find = Contract_delegate_storage.find let delegated_contracts = Contract_delegate_storage.delegated_contracts diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 3e4e4e39c0e5..40c34ddb07fa 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2284,7 +2284,7 @@ module Delegate : sig val delegated_balance : context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t - val registered : context -> Signature.Public_key_hash.t -> bool tzresult Lwt.t + val registered : context -> Signature.public_key_hash -> bool Lwt.t val deactivated : context -> Signature.Public_key_hash.t -> bool tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 8ebb49accef8..c1d24d86d620 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -1501,7 +1501,7 @@ let apply_external_manager_operation_content : Tez.(limit > max_limit) (Set_deposits_limit_too_high {limit; max_limit})) >>?= fun () -> - Delegate.registered ctxt source >>=? fun is_registered -> + Delegate.registered ctxt source >>= fun is_registered -> error_unless is_registered (Set_deposits_limit_on_unregistered_delegate source) diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml index 8dbb1063250d..75efd48243da 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml @@ -25,14 +25,6 @@ let find = Storage.Contract.Delegate.find -(* A delegate is registered if its "implicit account" delegates to itself. *) -let registered c delegate = - Storage.Contract.Delegate.find c (Contract_repr.Implicit delegate) - >|=? function - | Some current_delegate -> - Signature.Public_key_hash.equal delegate current_delegate - | None -> false - let init ctxt contract delegate = Storage.Contract.Delegate.init ctxt contract delegate >>=? fun ctxt -> let delegate_contract = Contract_repr.Implicit delegate in diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli index 45a05ca408ca..2f9b8e201c4f 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli @@ -30,11 +30,6 @@ val find : Contract_repr.t -> Signature.Public_key_hash.t option tzresult Lwt.t -(** [registered ctxt delegate] returns true iff delegate is an implicit contract - that delegates to itself. *) -val registered : - Raw_context.t -> Signature.Public_key_hash.t -> bool tzresult Lwt.t - (** [init ctxt contract delegate] sets the [delegate] associated to [contract]. This function is undefined if [contract] is not allocated, or if [contract] diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 39b2a8050110..7d5719d1415d 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -171,10 +171,10 @@ let () = (function Not_registered pkh -> Some pkh | _ -> None) (fun pkh -> Not_registered pkh) -(* The fact that this succeeds iff [registered ctxt pkh] returns true is an - invariant of the [set] function. *) +let registered = Storage.Delegates.mem + let check_registered ctxt pkh = - Storage.Delegates.mem ctxt pkh >>= function + registered ctxt pkh >>= function | true -> return_unit | false -> fail (Not_registered pkh) @@ -207,7 +207,7 @@ let balance ctxt delegate = Storage.Contract.Spendable_balance.get ctxt contract let staking_balance ctxt delegate = - Contract_delegate_storage.registered ctxt delegate >>=? fun is_registered -> + registered ctxt delegate >>= fun is_registered -> if is_registered then Stake_storage.get_staking_balance ctxt delegate else return Tez_repr.zero @@ -231,7 +231,7 @@ let init ctxt contract delegate = Contract_manager_storage.is_manager_key_revealed ctxt delegate >>=? fun known_delegate -> error_unless known_delegate (Unregistered_delegate delegate) >>?= fun () -> - Contract_delegate_storage.registered ctxt delegate >>=? fun is_registered -> + registered ctxt delegate >>= fun is_registered -> error_unless is_registered (Unregistered_delegate delegate) >>?= fun () -> Contract_delegate_storage.init ctxt contract delegate >>=? fun ctxt -> Contract_storage.get_balance_and_frozen_bonds ctxt contract @@ -244,7 +244,7 @@ let set c contract delegate = (* check if contract is a registered delegate *) (match contract with | Contract_repr.Implicit pkh -> - Contract_delegate_storage.registered c pkh >>=? fun is_registered -> + registered c pkh >>= fun is_registered -> fail_when is_registered (No_deletion pkh) | Originated _ -> return_unit) >>=? fun () -> @@ -259,8 +259,7 @@ let set c contract delegate = | Some delegate -> Contract_manager_storage.is_manager_key_revealed c delegate >>=? fun known_delegate -> - Contract_delegate_storage.registered c delegate - >>=? fun registered_delegate -> + registered c delegate >>= fun registered_delegate -> let self_delegation = match contract with | Implicit pkh -> Signature.Public_key_hash.equal pkh delegate @@ -283,7 +282,7 @@ let set c contract delegate = (* check if contract is a registered delegate *) (match contract with | Contract_repr.Implicit pkh -> - Contract_delegate_storage.registered c pkh >>=? fun is_registered -> + registered c pkh >>= fun is_registered -> (* allow self-delegation to re-activate *) if (not self_delegation) && is_registered then fail (No_deletion pkh) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 05cda508fdfd..afa43e5df789 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -39,6 +39,9 @@ type error += } | (* `Temporary *) Not_registered of Signature.Public_key_hash.t +(** Has a delegate been registered in the delegate table? *) +val registered : Raw_context.t -> Signature.Public_key_hash.t -> bool Lwt.t + (** Check that a given implicit account is a registered delegate. *) val check_registered : Raw_context.t -> Signature.Public_key_hash.t -> unit tzresult Lwt.t -- GitLab From b51897528ad7f8d2d280783d9444b2f365b70389 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Thu, 14 Apr 2022 23:37:43 +0200 Subject: [PATCH 05/35] Proto: remove `freeze_deposits_do_not_call_except_for_migration` --- src/proto_alpha/lib_protocol/delegate_cycles.ml | 8 ++++---- src/proto_alpha/lib_protocol/delegate_cycles.mli | 16 +++++++--------- src/proto_alpha/lib_protocol/init_storage.ml | 7 +------ 3 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index 7d5d6b0b11d3..57cf944e033a 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -189,9 +189,6 @@ let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle delegates_to_remove (ctxt, balance_updates) -let freeze_deposits_do_not_call_except_for_migration = - freeze_deposits ~origin:Protocol_migration - let cycle_end ctxt last_cycle unrevealed_nonces = let new_cycle = Cycle_repr.add last_cycle 1 in Delegate_sampler.select_new_distribution_at_cycle_end ctxt ~new_cycle @@ -212,7 +209,7 @@ let cycle_end ctxt last_cycle unrevealed_nonces = update_activity ctxt last_cycle >>=? fun (ctxt, deactivated_delagates) -> return (ctxt, balance_updates, deactivated_delagates) -let init_first_cycles ctxt = +let init_first_cycles ctxt ~origin = let preserved = Constants_storage.preserved_cycles ctxt in List.fold_left_es (fun ctxt c -> @@ -223,3 +220,6 @@ let init_first_cycles ctxt = Delegate_sampler.select_distribution_for_cycle ctxt cycle) ctxt Misc.(0 --> preserved) + >>=? fun ctxt -> + let cycle = (Raw_context.current_level ctxt).cycle in + freeze_deposits ~origin ~new_cycle:cycle ~balance_updates:[] ctxt diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.mli b/src/proto_alpha/lib_protocol/delegate_cycles.mli index f45fd05bed59..0814d32d45b8 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.mli +++ b/src/proto_alpha/lib_protocol/delegate_cycles.mli @@ -39,14 +39,12 @@ val cycle_end : tzresult Lwt.t -(** [init_first_cycles ctxt] computes and records the distribution of the total - active stake among active delegates. This concerns the total active stake - involved in the calculation of baking rights for all cycles in the range - [0, preserved_cycles]. *) -val init_first_cycles : Raw_context.t -> Raw_context.t tzresult Lwt.t - -val freeze_deposits_do_not_call_except_for_migration : +(** [init_first_cycles ctxt ~origin] computes and records the distribution of + the total active stake among active delegates. This concerns the total + active stake involved in the calculation of baking rights for all cycles + in the range [0, preserved_cycles]. It also freezes the deposits for all + the active delegates. *) +val init_first_cycles : Raw_context.t -> - new_cycle:Cycle_repr.t -> - balance_updates:Receipt_repr.balance_updates -> + origin:Receipt_repr.update_origin -> (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/init_storage.ml b/src/proto_alpha/lib_protocol/init_storage.ml index d5b1b058a73f..e5195fe38233 100644 --- a/src/proto_alpha/lib_protocol/init_storage.ml +++ b/src/proto_alpha/lib_protocol/init_storage.ml @@ -97,12 +97,7 @@ let prepare_first_block _chain_id ctxt ~typecheck ~level ~timestamp = param.bootstrap_accounts param.bootstrap_contracts >>=? fun (ctxt, bootstrap_balance_updates) -> - Delegate_cycles.init_first_cycles ctxt >>=? fun ctxt -> - let cycle = (Raw_context.current_level ctxt).cycle in - Delegate_cycles.freeze_deposits_do_not_call_except_for_migration - ~new_cycle:cycle - ~balance_updates:[] - ctxt + Delegate_cycles.init_first_cycles ctxt ~origin:Protocol_migration >>=? fun (ctxt, deposits_balance_updates) -> Vote_storage.init ctxt -- GitLab From 997cbc82f38da5e45905090d317d6efc568ff3cd Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Sat, 7 May 2022 13:41:44 +0200 Subject: [PATCH 06/35] Proto: introduce `Alpha_context.Contract.Delegate` --- src/proto_alpha/lib_protocol/alpha_context.ml | 10 ++++++-- .../lib_protocol/alpha_context.mli | 24 ++++++++++--------- src/proto_alpha/lib_protocol/apply.ml | 4 ++-- .../lib_protocol/contract_services.ml | 4 ++-- .../test/integration/test_frozen_bonds.ml | 17 ++++++------- .../test/integration/test_token.ml | 6 ++--- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 507c555c80f7..136f4eb95ea4 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -238,6 +238,14 @@ module Contract = struct let get_manager_key = Contract_manager_storage.get_manager_key + module Delegate = struct + let find = Contract_delegate_storage.find + + let init = Delegate_storage.init + + let set = Delegate_storage.set + end + module Internal_for_tests = struct include Contract_repr include Contract_storage @@ -414,8 +422,6 @@ module Delegate = struct let prepare_stake_distribution = Stake_storage.prepare_stake_distribution - let find = Contract_delegate_storage.find - let delegated_contracts = Contract_delegate_storage.delegated_contracts end diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 40c34ddb07fa..06d0192345e7 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -1586,6 +1586,19 @@ module Contract : sig script:Script.t * Lazy_storage.diffs option -> context tzresult Lwt.t + module Delegate : sig + val find : context -> t -> Signature.Public_key_hash.t option tzresult Lwt.t + + val init : + context -> t -> Signature.Public_key_hash.t -> context tzresult Lwt.t + + val set : + context -> + t -> + Signature.Public_key_hash.t option -> + context tzresult Lwt.t + end + module Internal_for_tests : sig (** see [Contract_repr.originated_contract] for documentation *) val originated_contract : Origination_nonce.Internal_for_tests.t -> t @@ -2188,17 +2201,6 @@ module Receipt : sig end module Delegate : sig - val init : - context -> - Contract.t -> - Signature.Public_key_hash.t -> - context tzresult Lwt.t - - val find : context -> Contract.t -> public_key_hash option tzresult Lwt.t - - val set : - context -> Contract.t -> public_key_hash option -> context tzresult Lwt.t - val frozen_deposits_limit : context -> Signature.Public_key_hash.t -> Tez.t option tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index c1d24d86d620..05840c044a4f 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -794,7 +794,7 @@ let update_script_storage_and_ticket_balances ctxt ~self storage Ticket_accounting.update_ticket_balances ctxt ~self ~ticket_diffs operations let apply_delegation ~ctxt ~source ~delegate ~before_operation = - Delegate.set ctxt source delegate >|=? fun ctxt -> + Contract.Delegate.set ctxt source delegate >|=? fun ctxt -> (ctxt, Gas.consumed ~since:before_operation ~until:ctxt, []) type execution_arg = @@ -1063,7 +1063,7 @@ let apply_origination ~ctxt ~storage_type ~storage ~unparsed_code let contract = Contract.Originated contract_hash in (match delegate with | None -> return ctxt - | Some delegate -> Delegate.init ctxt contract delegate) + | Some delegate -> Contract.Delegate.init ctxt contract delegate) >>=? fun ctxt -> Token.transfer ctxt (`Contract source) (`Contract contract) credit >>=? fun (ctxt, balance_updates) -> diff --git a/src/proto_alpha/lib_protocol/contract_services.ml b/src/proto_alpha/lib_protocol/contract_services.ml index ee2a3a66a40e..bb0115cc0942 100644 --- a/src/proto_alpha/lib_protocol/contract_services.ml +++ b/src/proto_alpha/lib_protocol/contract_services.ml @@ -370,7 +370,7 @@ let[@coq_axiom_with_reason "gadt"] register () = | false -> return_some None | true -> Contract.get_manager_key ctxt mgr >|=? fun key -> Some (Some key))) ; - register_opt_field ~chunked:false S.delegate Delegate.find ; + register_opt_field ~chunked:false S.delegate Contract.Delegate.find ; opt_register1 ~chunked:false S.counter (fun ctxt contract () () -> match contract with | Originated _ -> return_none @@ -501,7 +501,7 @@ let[@coq_axiom_with_reason "gadt"] register () = S.info (fun ctxt contract {normalize_types} -> Contract.get_balance ctxt contract >>=? fun balance -> - Delegate.find ctxt contract >>=? fun delegate -> + Contract.Delegate.find ctxt contract >>=? fun delegate -> (match contract with | Implicit manager -> Contract.get_counter ctxt manager >>=? fun counter -> diff --git a/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml b/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml index 27f5394c700a..2237adcb1407 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_frozen_bonds.ml @@ -93,7 +93,7 @@ let init_test ~user_is_delegate = (* Configure delegate, as a delegate by self-delegation, for which revealing its manager key is a prerequisite. *) Contract.reveal_manager_key ctxt delegate delegate_pk >>>=? fun ctxt -> - Delegate.set ctxt delegate_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt delegate_contract (Some delegate) >>>=? fun ctxt -> return (ctxt, user_contract, user_account, delegate) (** Tested scenario : @@ -111,7 +111,7 @@ let test_delegate_then_freeze_deposit () = (* Fetch user's initial balance before freeze. *) Token.balance ctxt user_account >>>=? fun (ctxt, user_balance) -> (* Let user delegate to "delegate". *) - Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> (* Fetch staking balance after delegation and before freeze. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance -> (* Freeze a tx-rollup deposit. *) @@ -126,7 +126,7 @@ let test_delegate_then_freeze_deposit () = (* Ensure staking balance did not change. *) Assert.equal_tez ~loc:__LOC__ staking_balance' staking_balance >>=? fun () -> (* Remove delegation. *) - Delegate.set ctxt user_contract None >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract None >>>=? fun ctxt -> (* Fetch staking balance after delegation removal. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance'' -> (* Ensure staking balance decreased by user's initial balance. *) @@ -173,7 +173,7 @@ let test_freeze_deposit_then_delegate () = Now, fetch staking balance before delegation and after freeze. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance -> (* Let user delegate to "delegate". *) - Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> (* Fetch staking balance after delegation. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> (* ensure staking balance increased by the user's balance. *) @@ -191,7 +191,7 @@ let test_freeze_deposit_then_delegate () = Assert.equal_tez ~loc:__LOC__ staking_balance'' staking_balance' >>=? fun () -> (* Remove delegation. *) - Delegate.set ctxt user_contract None >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract None >>>=? fun ctxt -> (* Fetch staking balance. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance''' -> (* Check that staking balance has decreased by the user's initial balance. *) @@ -329,7 +329,8 @@ let test_scenario scenario = (* Configure delegate, as a delegate by self-delegation, for which revealing its manager key is a prerequisite. *) Contract.reveal_manager_key ctxt delegate2 delegate_pk2 >>>=? fun ctxt -> - Delegate.set ctxt delegate_contract2 (Some delegate2) >>>=? fun ctxt -> + Contract.Delegate.set ctxt delegate_contract2 (Some delegate2) + >>>=? fun ctxt -> let tx_rollup1, nonce = mk_tx_rollup () in let tx_rollup2, _ = mk_tx_rollup ~nonce () in let bond_id1 = Bond_id.Tx_rollup_bond_id tx_rollup1 in @@ -344,7 +345,7 @@ let test_scenario scenario = Contract.get_balance_and_frozen_bonds ctxt user_contract >>>=? fun user_balance -> (* Let user delegate to "delegate". *) - Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract (Some delegate) >>>=? fun ctxt -> (* Fetch staking balance after delegation *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> Assert.equal_tez @@ -418,7 +419,7 @@ let test_scenario scenario = (* Fetch user's initial balance before undelegate. *) Token.balance ctxt user_account >>>=? fun (_, user_balance) -> (* Remove delegation. *) - Delegate.set ctxt user_contract None >>>=? fun ctxt -> + Contract.Delegate.set ctxt user_contract None >>>=? fun ctxt -> (* Fetch staking balance after delegation removal. *) Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> (* Ensure staking balance decreased by delegation amount *) diff --git a/src/proto_alpha/lib_protocol/test/integration/test_token.ml b/src/proto_alpha/lib_protocol/test/integration/test_token.ml index d12451168919..20a147c187b1 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_token.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_token.ml @@ -505,13 +505,13 @@ let build_test_cases () = (* Configure baker1, and baker2 as delegates by self-delegation, for which revealing their manager key is a prerequisite. *) wrap (Contract.reveal_manager_key ctxt baker1 baker1_pk) >>=? fun ctxt -> - wrap (Delegate.set ctxt (Contract.Implicit baker1) (Some baker1)) + wrap (Contract.Delegate.set ctxt (Contract.Implicit baker1) (Some baker1)) >>=? fun ctxt -> wrap (Contract.reveal_manager_key ctxt baker2 baker2_pk) >>=? fun ctxt -> - wrap (Delegate.set ctxt (Contract.Implicit baker2) (Some baker2)) + wrap (Contract.Delegate.set ctxt (Contract.Implicit baker2) (Some baker2)) (* Let user1 delegate to baker2. *) >>=? fun ctxt -> - wrap (Delegate.set ctxt (Contract.Implicit user1) (Some baker2)) + wrap (Contract.Delegate.set ctxt (Contract.Implicit user1) (Some baker2)) >>=? fun ctxt -> let tx_rollup1 = mk_rollup () in let bond_id1 = Bond_id.Tx_rollup_bond_id tx_rollup1 in -- GitLab From e923c18a22e3c24cd1163f012a97f9a415eb81df Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 16:39:20 +0200 Subject: [PATCH 07/35] Proto: introduce `Delegate_table_storage.Contract` --- src/proto_alpha/lib_protocol/alpha_context.ml | 4 +- .../lib_protocol/alpha_context.mli | 15 +- .../lib_protocol/bootstrap_storage.ml | 4 +- .../contract_delegate_storage.mli | 8 +- .../lib_protocol/delegate_storage.ml | 147 +++++++++--------- .../lib_protocol/delegate_storage.mli | 39 +++-- 6 files changed, 114 insertions(+), 103 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 136f4eb95ea4..8649672c9df2 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -241,9 +241,7 @@ module Contract = struct module Delegate = struct let find = Contract_delegate_storage.find - let init = Delegate_storage.init - - let set = Delegate_storage.set + include Delegate_storage.Contract end module Internal_for_tests = struct diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 06d0192345e7..9c5e8cbde009 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -1586,17 +1586,16 @@ module Contract : sig script:Script.t * Lazy_storage.diffs option -> context tzresult Lwt.t + (** Functions for handling the delegate of a contract.*) module Delegate : sig - val find : context -> t -> Signature.Public_key_hash.t option tzresult Lwt.t + (** see {!Contract_delegate_storage.find} for documentation *) + val init : context -> t -> public_key_hash -> context tzresult Lwt.t - val init : - context -> t -> Signature.Public_key_hash.t -> context tzresult Lwt.t + (** see {!Delegate_table_storage.set} for documentation *) + val set : context -> t -> public_key_hash option -> context tzresult Lwt.t - val set : - context -> - t -> - Signature.Public_key_hash.t option -> - context tzresult Lwt.t + (** see {!Delegate_table_storage.find} for documentation *) + val find : context -> t -> public_key_hash option tzresult Lwt.t end module Internal_for_tests : sig diff --git a/src/proto_alpha/lib_protocol/bootstrap_storage.ml b/src/proto_alpha/lib_protocol/bootstrap_storage.ml index ce7a64777383..d37f5c3c699e 100644 --- a/src/proto_alpha/lib_protocol/bootstrap_storage.ml +++ b/src/proto_alpha/lib_protocol/bootstrap_storage.ml @@ -41,7 +41,7 @@ let init_account (ctxt, balance_updates) public_key_hash public_key >>=? fun ctxt -> - Delegate_storage.set + Delegate_storage.Contract.set ctxt contract (Some (Option.value ~default:public_key_hash delegate_to)) @@ -62,7 +62,7 @@ let init_contract ~typecheck (ctxt, balance_updates) let contract = Contract_repr.Originated contract_hash in (match delegate with | None -> return ctxt - | Some delegate -> Delegate_storage.init ctxt contract delegate) + | Some delegate -> Delegate_storage.Contract.init ctxt contract delegate) >>=? fun ctxt -> let origin = Receipt_repr.Protocol_migration in Token.transfer ~origin ctxt `Bootstrap (`Contract contract) amount diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli index 2f9b8e201c4f..1bdd5d5d1895 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli @@ -32,8 +32,7 @@ val find : (** [init ctxt contract delegate] sets the [delegate] associated to [contract]. - This function is undefined if [contract] is not allocated, or if [contract] - has already a delegate. *) + This function is undefined if [contract] has already a delegate. *) val init : Raw_context.t -> Contract_repr.t -> @@ -55,10 +54,7 @@ val unlink : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t This function is undefined if [contract] is not allocated. *) val delete : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t -(** [set ctxt contract delegate] updates the [delegate] associated to [contract]. - - This function is undefined if [contract] is not allocated, or if [contract] - does not have a delegate. *) +(** [set ctxt contract delegate] updates the [delegate] associated to [contract]. *) val set : Raw_context.t -> Contract_repr.t -> diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 7d5719d1415d..4f9fda6a3b3b 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -227,81 +227,86 @@ let full_balance ctxt delegate = Lwt.return Tez_repr.(frozen_deposits.current_amount +? balance_and_frozen_bonds) -let init ctxt contract delegate = - Contract_manager_storage.is_manager_key_revealed ctxt delegate - >>=? fun known_delegate -> - error_unless known_delegate (Unregistered_delegate delegate) >>?= fun () -> - registered ctxt delegate >>= fun is_registered -> - error_unless is_registered (Unregistered_delegate delegate) >>?= fun () -> - Contract_delegate_storage.init ctxt contract delegate >>=? fun ctxt -> - Contract_storage.get_balance_and_frozen_bonds ctxt contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.add_stake ctxt delegate balance_and_frozen_bonds +module Contract = struct + let init ctxt contract delegate = + Contract_manager_storage.is_manager_key_revealed ctxt delegate + >>=? fun known_delegate -> + error_unless known_delegate (Unregistered_delegate delegate) >>?= fun () -> + registered ctxt delegate >>= fun is_registered -> + error_unless is_registered (Unregistered_delegate delegate) >>?= fun () -> + Contract_delegate_storage.init ctxt contract delegate >>=? fun ctxt -> + Contract_storage.get_balance_and_frozen_bonds ctxt contract + >>=? fun balance_and_frozen_bonds -> + Stake_storage.add_stake ctxt delegate balance_and_frozen_bonds -let set c contract delegate = - match delegate with - | None -> ( - (* check if contract is a registered delegate *) - (match contract with - | Contract_repr.Implicit pkh -> - registered c pkh >>= fun is_registered -> - fail_when is_registered (No_deletion pkh) - | Originated _ -> return_unit) - >>=? fun () -> - Contract_delegate_storage.find c contract >>=? function - | None -> return c - | Some delegate -> - (* Removes the balance of the contract from the delegate *) - Contract_storage.get_balance_and_frozen_bonds c contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.remove_stake c delegate balance_and_frozen_bonds - >>=? fun c -> Contract_delegate_storage.delete c contract) - | Some delegate -> - Contract_manager_storage.is_manager_key_revealed c delegate - >>=? fun known_delegate -> - registered c delegate >>= fun registered_delegate -> - let self_delegation = - match contract with - | Implicit pkh -> Signature.Public_key_hash.equal pkh delegate - | Originated _ -> false - in - if (not known_delegate) || not (registered_delegate || self_delegation) - then fail (Unregistered_delegate delegate) - else - Contract_delegate_storage.find c contract >>=? fun current_delegate -> - (match current_delegate with - | Some current_delegate - when Signature.Public_key_hash.equal delegate current_delegate -> - if self_delegation then - Delegate_activation_storage.is_inactive c delegate >>=? function - | true -> return_unit - | false -> fail Active_delegate - else fail Current_delegate - | None | Some _ -> return_unit) - >>=? fun () -> + let set c contract delegate = + match delegate with + | None -> ( (* check if contract is a registered delegate *) (match contract with | Contract_repr.Implicit pkh -> registered c pkh >>= fun is_registered -> - (* allow self-delegation to re-activate *) - if (not self_delegation) && is_registered then - fail (No_deletion pkh) - else return_unit + fail_when is_registered (No_deletion pkh) | Originated _ -> return_unit) >>=? fun () -> - Contract_storage.allocated c contract >>= fun exists -> - error_when - (self_delegation && not exists) - (Empty_delegate_account delegate) - >>?= fun () -> - Contract_storage.get_balance_and_frozen_bonds c contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.remove_contract_stake c contract balance_and_frozen_bonds - >>=? fun c -> - Contract_delegate_storage.set c contract delegate >>=? fun c -> - Stake_storage.add_stake c delegate balance_and_frozen_bonds - >>=? fun c -> - if self_delegation then - Storage.Delegates.add c delegate >>= fun c -> - Stake_storage.set_active c delegate - else return c + Contract_delegate_storage.find c contract >>=? function + | None -> return c + | Some delegate -> + (* Removes the balance of the contract from the delegate *) + Contract_storage.get_balance_and_frozen_bonds c contract + >>=? fun balance_and_frozen_bonds -> + Stake_storage.remove_stake c delegate balance_and_frozen_bonds + >>=? fun c -> Contract_delegate_storage.delete c contract) + | Some delegate -> + Contract_manager_storage.is_manager_key_revealed c delegate + >>=? fun known_delegate -> + registered c delegate >>= fun registered_delegate -> + let self_delegation = + match contract with + | Implicit pkh -> Signature.Public_key_hash.equal pkh delegate + | Originated _ -> false + in + if (not known_delegate) || not (registered_delegate || self_delegation) + then fail (Unregistered_delegate delegate) + else + Contract_delegate_storage.find c contract >>=? fun current_delegate -> + (match current_delegate with + | Some current_delegate + when Signature.Public_key_hash.equal delegate current_delegate -> + if self_delegation then + Delegate_activation_storage.is_inactive c delegate >>=? function + | true -> return_unit + | false -> fail Active_delegate + else fail Current_delegate + | None | Some _ -> return_unit) + >>=? fun () -> + (* check if contract is a registered delegate *) + (match contract with + | Implicit pkh -> + registered c pkh >>= fun is_registered -> + (* allow self-delegation to re-activate *) + if (not self_delegation) && is_registered then + fail (No_deletion pkh) + else return_unit + | Originated _ -> return_unit) + >>=? fun () -> + Contract_storage.allocated c contract >>= fun exists -> + error_when + (self_delegation && not exists) + (Empty_delegate_account delegate) + >>?= fun () -> + Contract_storage.get_balance_and_frozen_bonds c contract + >>=? fun balance_and_frozen_bonds -> + Stake_storage.remove_contract_stake + c + contract + balance_and_frozen_bonds + >>=? fun c -> + Contract_delegate_storage.set c contract delegate >>=? fun c -> + Stake_storage.add_stake c delegate balance_and_frozen_bonds + >>=? fun c -> + if self_delegation then + Storage.Delegates.add c delegate >>= fun c -> + Stake_storage.set_active c delegate + else return c +end diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index afa43e5df789..3c747f8d89c5 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -97,19 +97,32 @@ val delegated_balance : val full_balance : Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t -(** Allow to register a delegate when creating an account. *) -val init : - Raw_context.t -> - Contract_repr.t -> - Signature.Public_key_hash.t -> - Raw_context.t tzresult Lwt.t - -(** Allow to set the delegate of an account. *) -val set : - Raw_context.t -> - Contract_repr.t -> - Signature.Public_key_hash.t option -> - Raw_context.t tzresult Lwt.t +module Contract : sig + (** [init ctxt contract delegate] allows to register a delegate when + creating a contract. + + This function is undefined if [contract] is not allocated, or if + [contract] has already a delegate, or if [delegate] is not a registered + delegate. *) + val init : + Raw_context.t -> + Contract_repr.t -> + Signature.Public_key_hash.t -> + Raw_context.t tzresult Lwt.t + + (** [set ctxt contract delegate_opt] allows to set or unsetthe + delegate of a contract. + + This function is undefined if [contract] is not allocated. When + [delegate = contract], the function also register the contract + as a delegate. Otherwide, the function is undefined if + [delegate] is not a registered delegate *) + val set : + Raw_context.t -> + Contract_repr.t -> + Signature.Public_key_hash.t option -> + Raw_context.t tzresult Lwt.t +end val pubkey : Raw_context.t -> -- GitLab From c2f697d06eeb74875fd0d4415f2120f2fb87960b Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Thu, 14 Apr 2022 23:37:43 +0200 Subject: [PATCH 08/35] Proto: merge `Seed.cycle_end` into `Delegate.cycle_end` --- src/proto_alpha/lib_protocol/alpha_context.mli | 4 ---- src/proto_alpha/lib_protocol/apply.ml | 3 +-- src/proto_alpha/lib_protocol/delegate_cycles.ml | 3 ++- src/proto_alpha/lib_protocol/delegate_cycles.mli | 1 - 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 9c5e8cbde009..450db04c630c 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -1202,9 +1202,6 @@ module Seed : sig val for_cycle : context -> Cycle.t -> seed tzresult Lwt.t - val cycle_end : - context -> Cycle.t -> (context * Nonce.unrevealed list) tzresult Lwt.t - val seed_encoding : seed Data_encoding.t end @@ -2232,7 +2229,6 @@ module Delegate : sig val cycle_end : context -> Cycle.t -> - Nonce.unrevealed list -> (context * Receipt.balance_updates * Signature.Public_key_hash.t list) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 05840c044a4f..5529711fa4b1 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -3240,8 +3240,7 @@ let may_start_new_cycle ctxt = match Level.dawn_of_a_new_cycle ctxt with | None -> return (ctxt, [], []) | Some last_cycle -> - Seed.cycle_end ctxt last_cycle >>=? fun (ctxt, unrevealed) -> - Delegate.cycle_end ctxt last_cycle unrevealed + Delegate.cycle_end ctxt last_cycle >>=? fun (ctxt, balance_updates, deactivated) -> Bootstrap.cycle_end ctxt last_cycle >|=? fun ctxt -> (ctxt, balance_updates, deactivated) diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index 57cf944e033a..1fec466d8e38 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -189,7 +189,8 @@ let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle delegates_to_remove (ctxt, balance_updates) -let cycle_end ctxt last_cycle unrevealed_nonces = +let cycle_end ctxt last_cycle = + Seed_storage.cycle_end ctxt last_cycle >>=? fun (ctxt, unrevealed_nonces) -> let new_cycle = Cycle_repr.add last_cycle 1 in Delegate_sampler.select_new_distribution_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.mli b/src/proto_alpha/lib_protocol/delegate_cycles.mli index 0814d32d45b8..ebcecb05d925 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.mli +++ b/src/proto_alpha/lib_protocol/delegate_cycles.mli @@ -32,7 +32,6 @@ val cycle_end : Raw_context.t -> Cycle_repr.t -> - Storage.Seed.unrevealed_nonce list -> (Raw_context.t * Receipt_repr.balance_updates * Signature.Public_key_hash.t list) -- GitLab From 2001a1bc7e887dcbb9cdf62daf8cdf4966f6b21d Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Sat, 23 Apr 2022 13:14:16 +0200 Subject: [PATCH 09/35] Proto/Alpha_context: always use `public_key` and `public_key_hash` --- .../lib_protocol/alpha_context.mli | 112 +++++++----------- 1 file changed, 43 insertions(+), 69 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 450db04c630c..7df05c1db2fe 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -1654,7 +1654,7 @@ end module Tx_rollup_withdraw : sig type order = { - claimer : Signature.Public_key_hash.t; + claimer : public_key_hash; ticket_hash : Ticket_hash.t; amount : Tx_rollup_l2_qty.t; } @@ -1777,7 +1777,7 @@ module Tx_rollup_reveal : sig ty : Script.lazy_expr; ticketer : Contract.t; amount : Tx_rollup_l2_qty.t; - claimer : Signature.Public_key_hash.t; + claimer : public_key_hash; } val encoding : t Data_encoding.t @@ -1931,7 +1931,7 @@ module Tx_rollup_commitment : sig type nonrec t = { commitment : Compact.t; commitment_hash : Tx_rollup_commitment_hash.t; - committer : Signature.Public_key_hash.t; + committer : public_key_hash; submitted_at : Raw_level.t; finalized_at : Raw_level.t option; } @@ -1962,10 +1962,9 @@ module Tx_rollup_commitment : sig context -> Tx_rollup.t -> Tx_rollup_state.t -> - Signature.public_key_hash -> + public_key_hash -> Full.t -> - (context * Tx_rollup_state.t * Signature.public_key_hash option) tzresult - Lwt.t + (context * Tx_rollup_state.t * public_key_hash option) tzresult Lwt.t val find : context -> @@ -2001,16 +2000,10 @@ module Tx_rollup_commitment : sig (context * Submitted_commitment.t) tzresult Lwt.t val pending_bonded_commitments : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - (context * int) tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> (context * int) tzresult Lwt.t val has_bond : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - (context * bool) tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> (context * bool) tzresult Lwt.t val finalize_commitment : context -> @@ -2025,16 +2018,10 @@ module Tx_rollup_commitment : sig (context * Tx_rollup_state.t * Tx_rollup_level.t) tzresult Lwt.t val remove_bond : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - context tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> context tzresult Lwt.t val slash_bond : - context -> - Tx_rollup.t -> - Signature.public_key_hash -> - (context * bool) tzresult Lwt.t + context -> Tx_rollup.t -> public_key_hash -> (context * bool) tzresult Lwt.t val reject_commitment : context -> @@ -2080,8 +2067,8 @@ module Tx_rollup_errors : sig } | Level_already_has_commitment of Tx_rollup_level.t | Wrong_inbox_hash - | Bond_does_not_exist of Signature.public_key_hash - | Bond_in_use of Signature.public_key_hash + | Bond_does_not_exist of public_key_hash + | Bond_in_use of public_key_hash | No_uncommitted_inbox | No_commitment_to_finalize | No_commitment_to_remove @@ -2165,7 +2152,7 @@ module Receipt : sig | Baking_bonuses | Storage_fees | Double_signing_punishments - | Lost_endorsing_rewards of Signature.Public_key_hash.t * bool * bool + | Lost_endorsing_rewards of public_key_hash * bool * bool | Liquidity_baking_subsidies | Burned | Commitments of Blinded_public_key_hash.t @@ -2198,10 +2185,10 @@ end module Delegate : sig val frozen_deposits_limit : - context -> Signature.Public_key_hash.t -> Tez.t option tzresult Lwt.t + context -> public_key_hash -> Tez.t option tzresult Lwt.t val set_frozen_deposits_limit : - context -> Signature.Public_key_hash.t -> Tez.t option -> context Lwt.t + context -> public_key_hash -> Tez.t option -> context Lwt.t val fold : context -> @@ -2229,9 +2216,7 @@ module Delegate : sig val cycle_end : context -> Cycle.t -> - (context * Receipt.balance_updates * Signature.Public_key_hash.t list) - tzresult - Lwt.t + (context * Receipt.balance_updates * public_key_hash list) tzresult Lwt.t val check_and_record_already_slashed_for_double_endorsing : context -> public_key_hash -> Level.t -> (context * bool) tzresult Lwt.t @@ -2255,15 +2240,15 @@ module Delegate : sig val record_baking_activity_and_pay_rewards_and_fees : context -> - payload_producer:Signature.Public_key_hash.t -> - block_producer:Signature.Public_key_hash.t -> + payload_producer:public_key_hash -> + block_producer:public_key_hash -> baking_reward:Tez.t -> reward_bonus:Tez.t option -> (context * Receipt.balance_updates) tzresult Lwt.t val record_endorsing_participation : context -> - delegate:Signature.Public_key_hash.t -> + delegate:public_key_hash -> participation:level_participation -> endorsing_power:int -> context tzresult Lwt.t @@ -2272,22 +2257,18 @@ module Delegate : sig val frozen_deposits : context -> public_key_hash -> deposits tzresult Lwt.t - val staking_balance : - context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t + val staking_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t - val delegated_contracts : - context -> Signature.Public_key_hash.t -> Contract.t list Lwt.t + val delegated_contracts : context -> public_key_hash -> Contract.t list Lwt.t - val delegated_balance : - context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t + val delegated_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t - val registered : context -> Signature.public_key_hash -> bool Lwt.t + val registered : context -> public_key_hash -> bool Lwt.t - val deactivated : - context -> Signature.Public_key_hash.t -> bool tzresult Lwt.t + val deactivated : context -> public_key_hash -> bool tzresult Lwt.t val last_cycle_before_deactivation : - context -> Signature.Public_key_hash.t -> Cycle.t tzresult Lwt.t + context -> public_key_hash -> Cycle.t tzresult Lwt.t val pubkey : context -> public_key_hash -> public_key tzresult Lwt.t @@ -2350,8 +2331,7 @@ module Vote : sig val recorded_proposal_count_for_delegate : context -> public_key_hash -> int tzresult Lwt.t - val listings_encoding : - (Signature.Public_key_hash.t * int64) list Data_encoding.t + val listings_encoding : (public_key_hash * int64) list Data_encoding.t val update_listings : context -> context tzresult Lwt.t @@ -2375,13 +2355,12 @@ module Vote : sig val delegate_info_encoding : delegate_info Data_encoding.t val get_delegate_info : - context -> Signature.Public_key_hash.t -> delegate_info tzresult Lwt.t + context -> public_key_hash -> delegate_info tzresult Lwt.t - val get_voting_power_free : - context -> Signature.Public_key_hash.t -> int64 tzresult Lwt.t + val get_voting_power_free : context -> public_key_hash -> int64 tzresult Lwt.t val get_voting_power : - context -> Signature.Public_key_hash.t -> (context * int64) tzresult Lwt.t + context -> public_key_hash -> (context * int64) tzresult Lwt.t val get_total_voting_power_free : context -> int64 tzresult Lwt.t @@ -2456,8 +2435,7 @@ module Sc_rollup : sig val encoding : t Data_encoding.t end - module Staker : - S.SIGNATURE_PUBLIC_KEY_HASH with type t = Signature.Public_key_hash.t + module Staker : S.SIGNATURE_PUBLIC_KEY_HASH with type t = public_key_hash module State_hash : S.HASH @@ -2747,7 +2725,7 @@ module Block_header : sig Liquidity_baking_repr.liquidity_baking_toggle_vote; } - type protocol_data = {contents : contents; signature : Signature.t} + type protocol_data = {contents : contents; signature : signature} type t = {shell : Block_header.shell_header; protocol_data : protocol_data} @@ -2804,8 +2782,7 @@ module Block_header : sig predecessor_round:Round.t -> unit tzresult - val check_signature : - t -> Chain_id.t -> Signature.Public_key.t -> unit tzresult + val check_signature : t -> Chain_id.t -> public_key -> unit tzresult val begin_validate_block_header : block_header:t -> @@ -2814,7 +2791,7 @@ module Block_header : sig predecessor_round:Round.t -> fitness:Fitness.t -> timestamp:Time.t -> - delegate_pk:Signature.public_key -> + delegate_pk:public_key -> round_durations:Round.round_durations -> proof_of_work_threshold:int64 -> expected_commitment:bool -> @@ -2967,7 +2944,7 @@ val consensus_content_encoding : consensus_content Data_encoding.t val pp_consensus_content : Format.formatter -> consensus_content -> unit type origination = { - delegate : Signature.Public_key_hash.t option; + delegate : public_key_hash option; script : Script.t; credit : Tez.tez; } @@ -2979,7 +2956,7 @@ type 'kind operation = { and 'kind protocol_data = { contents : 'kind contents_list; - signature : Signature.t option; + signature : signature option; } and _ contents_list = @@ -3017,13 +2994,13 @@ and _ contents = } -> Kind.activate_account contents | Proposals : { - source : Signature.Public_key_hash.t; + source : public_key_hash; period : int32; proposals : Protocol_hash.t list; } -> Kind.proposals contents | Ballot : { - source : Signature.Public_key_hash.t; + source : public_key_hash; period : int32; proposal : Protocol_hash.t; ballot : Vote.ballot; @@ -3031,7 +3008,7 @@ and _ contents = -> Kind.ballot contents | Failing_noop : string -> Kind.failing_noop contents | Manager_operation : { - source : Signature.Public_key_hash.t; + source : public_key_hash; fee : Tez.tez; counter : counter; operation : 'kind manager_operation; @@ -3041,7 +3018,7 @@ and _ contents = -> 'kind Kind.manager contents and _ manager_operation = - | Reveal : Signature.Public_key.t -> Kind.reveal manager_operation + | Reveal : public_key -> Kind.reveal manager_operation | Transaction : { amount : Tez.tez; parameters : Script.lazy_expr; @@ -3050,9 +3027,7 @@ and _ manager_operation = } -> Kind.transaction manager_operation | Origination : origination -> Kind.origination manager_operation - | Delegation : - Signature.Public_key_hash.t option - -> Kind.delegation manager_operation + | Delegation : public_key_hash option -> Kind.delegation manager_operation | Register_global_constant : { value : Script.lazy_expr; } @@ -3409,8 +3384,7 @@ module Stake_distribution : sig val delegate_pubkey : context -> public_key_hash -> public_key tzresult Lwt.t - val get_staking_balance : - context -> Signature.Public_key_hash.t -> Tez.t tzresult Lwt.t + val get_staking_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t end module Commitment : sig @@ -3585,8 +3559,8 @@ module Token : sig type container = [ `Contract of Contract.t | `Collected_commitments of Blinded_public_key_hash.t - | `Delegate_balance of Signature.Public_key_hash.t - | `Frozen_deposits of Signature.Public_key_hash.t + | `Delegate_balance of public_key_hash + | `Frozen_deposits of public_key_hash | `Block_fees | `Frozen_bonds of Contract.t * Bond_id.t ] @@ -3607,7 +3581,7 @@ module Token : sig type sink = [ `Storage_fees | `Double_signing_punishments - | `Lost_endorsing_rewards of Signature.Public_key_hash.t * bool * bool + | `Lost_endorsing_rewards of public_key_hash * bool * bool | `Burned | `Tx_rollup_rejection_punishments | container ] -- GitLab From 3b87a36093f3eb0d9b41962318ccc7b2b463507f Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 10/35] Proto/Delegate: remove dead code --- src/proto_alpha/lib_protocol/alpha_context.ml | 2 -- src/proto_alpha/lib_protocol/alpha_context.mli | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 8649672c9df2..8b291cc38354 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -432,8 +432,6 @@ module Stake_distribution = struct let slot_owner = Delegate_sampler.slot_owner - let delegate_pubkey = Delegate_storage.pubkey - let get_staking_balance = Delegate_storage.staking_balance end diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 7df05c1db2fe..11ec5d1fd525 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -3382,8 +3382,6 @@ module Stake_distribution : sig Slot.t -> (context * (public_key * public_key_hash)) tzresult Lwt.t - val delegate_pubkey : context -> public_key_hash -> public_key tzresult Lwt.t - val get_staking_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t end -- GitLab From 5fc671d4c683675e7d1e791eb8fd7ebeea668b1f Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:43:07 +0200 Subject: [PATCH 11/35] Proto: rename `Delegate.delegate_participation_info` --- src/proto_alpha/lib_protocol/alpha_context.mli | 2 +- .../lib_protocol/delegate_missed_endorsements_storage.ml | 2 +- .../lib_protocol/delegate_missed_endorsements_storage.mli | 2 +- src/proto_alpha/lib_protocol/delegate_services.ml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 11ec5d1fd525..c8a56aa4c2b7 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2210,7 +2210,7 @@ module Delegate : sig expected_endorsing_rewards : Tez.t; } - val delegate_participation_info : + val participation_info : context -> public_key_hash -> participation_info tzresult Lwt.t val cycle_end : diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml index 508d2852e8a3..6d28a0beacfa 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -133,7 +133,7 @@ type participation_info = { } (* Inefficient, only for RPC *) -let delegate_participation_info ctxt delegate = +let participation_info ctxt delegate = let level = Level_storage.current ctxt in Stake_storage.get_selected_distribution ctxt level.cycle >>=? fun stake_distribution -> diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli index 3ac64a03ae96..7f8f7b1a4df7 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli @@ -63,7 +63,7 @@ type participation_info = { [delegate_participation_info] and [!val:check_delegate] forms the implementation of RPC call "/context/delegates//participation". *) -val delegate_participation_info : +val participation_info : Raw_context.t -> Signature.Public_key_hash.t -> participation_info tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_services.ml b/src/proto_alpha/lib_protocol/delegate_services.ml index 544711c5ad83..26b149982247 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.ml +++ b/src/proto_alpha/lib_protocol/delegate_services.ml @@ -424,7 +424,7 @@ let register () = Vote.get_delegate_info ctxt pkh) ; register1 ~chunked:false S.participation (fun ctxt pkh () () -> Delegate.check_registered ctxt pkh >>=? fun () -> - Delegate.delegate_participation_info ctxt pkh) + Delegate.participation_info ctxt pkh) let list ctxt block ?(active = true) ?(inactive = false) ?(with_minimal_stake = true) ?(without_minimal_stake = false) () = -- GitLab From c8dc03ddb44476e944923bf956fc2ed410345e0b Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 26 Apr 2022 10:29:20 +0000 Subject: [PATCH 12/35] Proto: improve doc in `Delegate_table_storage`. --- .../contract_delegate_storage.mli | 2 +- .../lib_protocol/delegate_storage.mli | 26 +++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli index 1bdd5d5d1895..7fe992903bc0 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli @@ -32,7 +32,7 @@ val find : (** [init ctxt contract delegate] sets the [delegate] associated to [contract]. - This function is undefined if [contract] has already a delegate. *) + This function returns an error if [contract] has already a delegate. *) val init : Raw_context.t -> Contract_repr.t -> diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 3c747f8d89c5..885f3e097ea8 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -98,25 +98,29 @@ val full_balance : Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t module Contract : sig - (** [init ctxt contract delegate] allows to register a delegate when + (** [init ctxt contract delegate] registers a delegate when creating a contract. - This function is undefined if [contract] is not allocated, or if - [contract] has already a delegate, or if [delegate] is not a registered - delegate. *) + This functions assumes that [contract] is allocated. + This function returns an error if [contract] already has a delegate or + if [delegate] is not a registered delegate. *) val init : Raw_context.t -> Contract_repr.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t - (** [set ctxt contract delegate_opt] allows to set or unsetthe - delegate of a contract. - - This function is undefined if [contract] is not allocated. When - [delegate = contract], the function also register the contract - as a delegate. Otherwide, the function is undefined if - [delegate] is not a registered delegate *) + (** [set ctxt contract delegate_opt] allows to set the + delegate of a contract to [delegate] when [delegate_opt = Some delegate] + or to unset the delegate when [delegate_opt = None]. + When [delegate_opt = Some contract] (aka self-delegation), + the function also registers the contract as a delegate and + sets the delegate as {{!module:Delegate_activation_storage}active}. + + It returns an error when trying to set the delegate to an unregistered delegate. + It returns an error when trying to unset or change the delegate of a registered delegate. + It returns an error when self-delegating and the delegate is not {{!Contract_storage.allocated}allocated}. + It returns an error when self-delegating and the delegate is already active. *) val set : Raw_context.t -> Contract_repr.t -> -- GitLab From 11bd76e297bf144a7a32292e4a7aa6a994feee19 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Wed, 27 Apr 2022 11:08:02 +0200 Subject: [PATCH 13/35] Proto: refactor `Delegate_table_storage.Contract.set` --- .../lib_protocol/delegate_storage.ml | 150 ++++++++++-------- 1 file changed, 83 insertions(+), 67 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 4f9fda6a3b3b..454f849ea20a 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -227,6 +227,11 @@ let full_balance ctxt delegate = Lwt.return Tez_repr.(frozen_deposits.current_amount +? balance_and_frozen_bonds) +(** This module ensures the following invariants: + - registered delegates are self-delegated and appears in `Storage.Delegate`, + i.e. registered delegate cannot change their delegation + - stake is properly moved when changing delegation. +*) module Contract = struct let init ctxt contract delegate = Contract_manager_storage.is_manager_key_revealed ctxt delegate @@ -239,74 +244,85 @@ module Contract = struct >>=? fun balance_and_frozen_bonds -> Stake_storage.add_stake ctxt delegate balance_and_frozen_bonds - let set c contract delegate = + let set_self_delegate c delegate = + let open Lwt_tzresult_syntax in + let*! is_registered = registered c delegate in + if is_registered then + let* () = + let* is_inactive = Delegate_activation_storage.is_inactive c delegate in + fail_unless is_inactive Active_delegate + in + Stake_storage.set_active c delegate + else + let contract = Contract_repr.Implicit delegate in + let* () = + let* is_pk_revealed = + Contract_manager_storage.is_manager_key_revealed c delegate + in + fail_unless is_pk_revealed (Unregistered_delegate delegate) + in + let* () = + let*! is_allocated = Contract_storage.allocated c contract in + fail_unless is_allocated (Empty_delegate_account delegate) + in + let* balance_and_frozen_bonds = + Contract_storage.get_balance_and_frozen_bonds c contract + in + let* c = + Stake_storage.remove_contract_stake c contract balance_and_frozen_bonds + in + let* c = Contract_delegate_storage.set c contract delegate in + let* c = Stake_storage.add_stake c delegate balance_and_frozen_bonds in + let*! c = Storage.Delegates.add c delegate in + let* c = Stake_storage.set_active c delegate in + return c + + let set_delegate c contract delegate = + let open Lwt_tzresult_syntax in + let* () = + match contract with + | Contract_repr.Originated _ -> return_unit + | Implicit pkh -> + let*! is_registered = registered c pkh in + fail_when is_registered (No_deletion pkh) + in + let* () = + let* current_delegate = Contract_delegate_storage.find c contract in + match (delegate, current_delegate) with + | None, None -> + (* we don't fail in this case in order not to risk breaking + existing smart contracts. *) + return_unit + | Some delegate, Some current_delegate + when Signature.Public_key_hash.equal delegate current_delegate -> + fail Current_delegate + | _ -> return_unit + in + let* balance_and_frozen_bonds = + Contract_storage.get_balance_and_frozen_bonds c contract + in + let* c = + Stake_storage.remove_contract_stake c contract balance_and_frozen_bonds + in match delegate with - | None -> ( - (* check if contract is a registered delegate *) - (match contract with - | Contract_repr.Implicit pkh -> - registered c pkh >>= fun is_registered -> - fail_when is_registered (No_deletion pkh) - | Originated _ -> return_unit) - >>=? fun () -> - Contract_delegate_storage.find c contract >>=? function - | None -> return c - | Some delegate -> - (* Removes the balance of the contract from the delegate *) - Contract_storage.get_balance_and_frozen_bonds c contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.remove_stake c delegate balance_and_frozen_bonds - >>=? fun c -> Contract_delegate_storage.delete c contract) + | None -> + let* c = Contract_delegate_storage.delete c contract in + return c | Some delegate -> - Contract_manager_storage.is_manager_key_revealed c delegate - >>=? fun known_delegate -> - registered c delegate >>= fun registered_delegate -> - let self_delegation = - match contract with - | Implicit pkh -> Signature.Public_key_hash.equal pkh delegate - | Originated _ -> false + let* () = + let*! is_delegate_registered = registered c delegate in + fail_when + (not is_delegate_registered) + (Unregistered_delegate delegate) in - if (not known_delegate) || not (registered_delegate || self_delegation) - then fail (Unregistered_delegate delegate) - else - Contract_delegate_storage.find c contract >>=? fun current_delegate -> - (match current_delegate with - | Some current_delegate - when Signature.Public_key_hash.equal delegate current_delegate -> - if self_delegation then - Delegate_activation_storage.is_inactive c delegate >>=? function - | true -> return_unit - | false -> fail Active_delegate - else fail Current_delegate - | None | Some _ -> return_unit) - >>=? fun () -> - (* check if contract is a registered delegate *) - (match contract with - | Implicit pkh -> - registered c pkh >>= fun is_registered -> - (* allow self-delegation to re-activate *) - if (not self_delegation) && is_registered then - fail (No_deletion pkh) - else return_unit - | Originated _ -> return_unit) - >>=? fun () -> - Contract_storage.allocated c contract >>= fun exists -> - error_when - (self_delegation && not exists) - (Empty_delegate_account delegate) - >>?= fun () -> - Contract_storage.get_balance_and_frozen_bonds c contract - >>=? fun balance_and_frozen_bonds -> - Stake_storage.remove_contract_stake - c - contract - balance_and_frozen_bonds - >>=? fun c -> - Contract_delegate_storage.set c contract delegate >>=? fun c -> - Stake_storage.add_stake c delegate balance_and_frozen_bonds - >>=? fun c -> - if self_delegation then - Storage.Delegates.add c delegate >>= fun c -> - Stake_storage.set_active c delegate - else return c + let* c = Contract_delegate_storage.set c contract delegate in + let* c = Stake_storage.add_stake c delegate balance_and_frozen_bonds in + return c + + let set c contract delegate = + match (delegate, contract) with + | Some delegate, Contract_repr.Implicit source + when Signature.Public_key_hash.equal source delegate -> + set_self_delegate c delegate + | _ -> set_delegate c contract delegate end -- GitLab From 222bf2c2d882833c3e8aa41cc8aa4e0c47be5171 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Sat, 7 May 2022 13:08:21 +0200 Subject: [PATCH 14/35] Proto: no error for `expected_slots_for_given_active_stake` --- .../delegate_missed_endorsements_storage.ml | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml index 6d28a0beacfa..6517bf97ad9c 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -36,13 +36,12 @@ let expected_slots_for_given_active_stake ctxt ~total_active_stake ~active_stake let number_of_endorsements_per_cycle = blocks_per_cycle * consensus_committee_size in - Result.return - (Z.to_int - (Z.div - (Z.mul - (Z.of_int64 (Tez_repr.to_mutez active_stake)) - (Z.of_int number_of_endorsements_per_cycle)) - (Z.of_int64 (Tez_repr.to_mutez total_active_stake)))) + Z.to_int + (Z.div + (Z.mul + (Z.of_int64 (Tez_repr.to_mutez active_stake)) + (Z.of_int number_of_endorsements_per_cycle)) + (Z.of_int64 (Tez_repr.to_mutez total_active_stake))) type level_participation = Participated | Didn't_participate @@ -79,11 +78,12 @@ let record_endorsing_participation ctxt ~delegate ~participation | Some active_stake -> Stake_storage.get_total_active_stake ctxt level.cycle >>=? fun total_active_stake -> - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_slots -> + let expected_slots = + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in let Ratio_repr.{numerator; denominator} = Constants_storage.minimal_participation_ratio ctxt in @@ -157,11 +157,12 @@ let participation_info ctxt delegate = | Some active_stake -> Stake_storage.get_total_active_stake ctxt level.cycle >>=? fun total_active_stake -> - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_cycle_activity -> + let expected_cycle_activity = + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in let Ratio_repr.{numerator; denominator} = Constants_storage.minimal_participation_ratio ctxt in @@ -234,11 +235,12 @@ let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = let has_revealed_nonces = delegate_has_revealed_nonces delegate unrevealed_nonces_set in - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - >>?= fun expected_slots -> + let expected_slots = + expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in (if sufficient_participation && has_revealed_nonces then (* Sufficient participation: we pay the rewards *) -- GitLab From ab6aa935044ffad8c3e65c9c712859fbe5fc1d8d Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Sat, 7 May 2022 13:08:38 +0200 Subject: [PATCH 15/35] Proto: move `distribute_endorsing_rewards` into `Delegate_cycles` --- .../lib_protocol/delegate_cycles.ml | 62 +++++++++++++++-- .../delegate_missed_endorsements_storage.ml | 68 ++----------------- .../delegate_missed_endorsements_storage.mli | 19 ++++-- 3 files changed, 78 insertions(+), 71 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index 1fec466d8e38..53a0c38c2146 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -189,6 +189,63 @@ let freeze_deposits ?(origin = Receipt_repr.Block_application) ctxt ~new_cycle delegates_to_remove (ctxt, balance_updates) +let delegate_has_revealed_nonces delegate unrevelead_nonces_set = + not (Signature.Public_key_hash.Set.mem delegate unrevelead_nonces_set) + +let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = + let endorsing_reward_per_slot = + Constants_storage.endorsing_reward_per_slot ctxt + in + let unrevealed_nonces_set = + List.fold_left + (fun set {Storage.Seed.nonce_hash = _; delegate} -> + Signature.Public_key_hash.Set.add delegate set) + Signature.Public_key_hash.Set.empty + unrevealed_nonces + in + Stake_storage.get_total_active_stake ctxt last_cycle + >>=? fun total_active_stake -> + Stake_storage.get_selected_distribution ctxt last_cycle >>=? fun delegates -> + List.fold_left_es + (fun (ctxt, balance_updates) (delegate, active_stake) -> + let delegate_contract = Contract_repr.Implicit delegate in + Delegate_missed_endorsements_storage.reset_delegate_participation + ctxt + delegate + >>=? fun (ctxt, sufficient_participation) -> + let has_revealed_nonces = + delegate_has_revealed_nonces delegate unrevealed_nonces_set + in + let expected_slots = + Delegate_missed_endorsements_storage + .expected_slots_for_given_active_stake + ctxt + ~total_active_stake + ~active_stake + in + let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in + if sufficient_participation && has_revealed_nonces then + (* Sufficient participation: we pay the rewards *) + Token.transfer + ctxt + `Endorsing_rewards + (`Contract delegate_contract) + rewards + >|=? fun (ctxt, payed_rewards_receipts) -> + (ctxt, payed_rewards_receipts @ balance_updates) + else + (* Insufficient participation or unrevealed nonce: no rewards *) + Token.transfer + ctxt + `Endorsing_rewards + (`Lost_endorsing_rewards + (delegate, not sufficient_participation, not has_revealed_nonces)) + rewards + >|=? fun (ctxt, payed_rewards_receipts) -> + (ctxt, payed_rewards_receipts @ balance_updates)) + (ctxt, []) + delegates + let cycle_end ctxt last_cycle = Seed_storage.cycle_end ctxt last_cycle >>=? fun (ctxt, unrevealed_nonces) -> let new_cycle = Cycle_repr.add last_cycle 1 in @@ -198,10 +255,7 @@ let cycle_end ctxt last_cycle = ctxt ~new_cycle >>= fun ctxt -> - Delegate_missed_endorsements_storage.distribute_endorsing_rewards - ctxt - last_cycle - unrevealed_nonces + distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces >>=? fun (ctxt, balance_updates) -> freeze_deposits ctxt ~new_cycle ~balance_updates >>=? fun (ctxt, balance_updates) -> diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml index 6517bf97ad9c..96d22c2888cd 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -204,65 +204,11 @@ let participation_info ctxt delegate = expected_endorsing_rewards; } -let delegate_participated_enough ctxt delegate = - Storage.Contract.Missed_endorsements.find ctxt delegate >>=? function - | None -> return_true +let reset_delegate_participation ctxt delegate = + let contract = Contract_repr.Implicit delegate in + Storage.Contract.Missed_endorsements.find ctxt contract >>=? fun missed -> + match missed with + | None -> return (ctxt, true) | Some missed_endorsements -> - return Compare.Int.(missed_endorsements.remaining_slots >= 0) - -let delegate_has_revealed_nonces delegate unrevelead_nonces_set = - not (Signature.Public_key_hash.Set.mem delegate unrevelead_nonces_set) - -let distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces = - let endorsing_reward_per_slot = - Constants_storage.endorsing_reward_per_slot ctxt - in - let unrevealed_nonces_set = - List.fold_left - (fun set {Storage.Seed.nonce_hash = _; delegate} -> - Signature.Public_key_hash.Set.add delegate set) - Signature.Public_key_hash.Set.empty - unrevealed_nonces - in - Stake_storage.get_total_active_stake ctxt last_cycle - >>=? fun total_active_stake -> - Stake_storage.get_selected_distribution ctxt last_cycle >>=? fun delegates -> - List.fold_left_es - (fun (ctxt, balance_updates) (delegate, active_stake) -> - let delegate_contract = Contract_repr.Implicit delegate in - delegate_participated_enough ctxt delegate_contract - >>=? fun sufficient_participation -> - let has_revealed_nonces = - delegate_has_revealed_nonces delegate unrevealed_nonces_set - in - let expected_slots = - expected_slots_for_given_active_stake - ctxt - ~total_active_stake - ~active_stake - in - let rewards = Tez_repr.mul_exn endorsing_reward_per_slot expected_slots in - (if sufficient_participation && has_revealed_nonces then - (* Sufficient participation: we pay the rewards *) - Token.transfer - ctxt - `Endorsing_rewards - (`Contract delegate_contract) - rewards - >|=? fun (ctxt, payed_rewards_receipts) -> - (ctxt, payed_rewards_receipts @ balance_updates) - else - (* Insufficient participation or unrevealed nonce: no rewards *) - Token.transfer - ctxt - `Endorsing_rewards - (`Lost_endorsing_rewards - (delegate, not sufficient_participation, not has_revealed_nonces)) - rewards - >|=? fun (ctxt, payed_rewards_receipts) -> - (ctxt, payed_rewards_receipts @ balance_updates)) - >>=? fun (ctxt, balance_updates) -> - Storage.Contract.Missed_endorsements.remove ctxt delegate_contract - >>= fun ctxt -> return (ctxt, balance_updates)) - (ctxt, []) - delegates + Storage.Contract.Missed_endorsements.remove ctxt contract >>= fun ctxt -> + return (ctxt, Compare.Int.(missed_endorsements.remaining_slots >= 0)) diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli index 7f8f7b1a4df7..d2267d33a6a3 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.mli @@ -25,6 +25,12 @@ (* *) (*****************************************************************************) +val expected_slots_for_given_active_stake : + Raw_context.t -> + total_active_stake:Tez_repr.tez -> + active_stake:Tez_repr.tez -> + int + type level_participation = Participated | Didn't_participate (** Record the participation of a delegate as a validator. *) @@ -35,6 +41,13 @@ val record_endorsing_participation : endorsing_power:int -> Raw_context.t tzresult Lwt.t +(** Check that a delegate participated enough in the last cycle, + and then reset the participation for preparing the next cycle. *) +val reset_delegate_participation : + Raw_context.t -> + Signature.Public_key_hash.t -> + (Raw_context.t * bool) tzresult Lwt.t + (** Participation information. We denote by: - "static" information that does not change during the cycle - "dynamic" information that may change during the cycle *) @@ -78,9 +91,3 @@ val record_baking_activity_and_pay_rewards_and_fees : baking_reward:Tez_repr.t -> reward_bonus:Tez_repr.t option -> (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t - -val distribute_endorsing_rewards : - Raw_context.t -> - Cycle_repr.t -> - Storage.Seed.unrevealed_nonce list -> - (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t -- GitLab From c48a03d4a55aeff239688d6be99609378ac3b228 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Sat, 7 May 2022 14:31:00 +0200 Subject: [PATCH 16/35] Proto: remove unused error in `Delegate_storage` --- .../client_proto_context_commands.ml | 6 +- .../lib_protocol/delegate_storage.ml | 215 +++++++----------- .../lib_protocol/delegate_storage.mli | 21 +- .../integration/consensus/test_delegation.ml | 8 +- 4 files changed, 105 insertions(+), 145 deletions(-) diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index 7ace7e77ed5c..0fff31de6b21 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -1482,7 +1482,11 @@ let commands_rw () = src_pk >>= function | Ok _ -> return_unit - | Error [Environment.Ecoproto_error Delegate_storage.Active_delegate] -> + | Error + [ + Environment.Ecoproto_error + Delegate_storage.Contract.Active_delegate; + ] -> cctxt#message "Delegate already activated." >>= fun () -> return_unit | Error el -> Lwt.return_error el); diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 454f849ea20a..997e86f304ea 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -25,132 +25,9 @@ (* *) (*****************************************************************************) -type error += - | (* `Permanent *) No_deletion of Signature.Public_key_hash.t - | (* `Temporary *) Active_delegate - | (* `Temporary *) Current_delegate - | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t - | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t - | (* `Permanent *) Unassigned_validation_slot_for_level of Level_repr.t * int - | (* `Permanent *) - Cannot_find_active_stake of { - cycle : Cycle_repr.t; - delegate : Signature.Public_key_hash.t; - } - | (* `Temporary *) Not_registered of Signature.Public_key_hash.t +type error += (* `Temporary *) Not_registered of Signature.Public_key_hash.t let () = - register_error_kind - `Permanent - ~id:"delegate.no_deletion" - ~title:"Forbidden delegate deletion" - ~description:"Tried to unregister a delegate" - ~pp:(fun ppf delegate -> - Format.fprintf - ppf - "Delegate deletion is forbidden (%a)" - Signature.Public_key_hash.pp - delegate) - Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) - (function No_deletion c -> Some c | _ -> None) - (fun c -> No_deletion c) ; - register_error_kind - `Temporary - ~id:"delegate.already_active" - ~title:"Delegate already active" - ~description:"Useless delegate reactivation" - ~pp:(fun ppf () -> - Format.fprintf ppf "The delegate is still active, no need to refresh it") - Data_encoding.empty - (function Active_delegate -> Some () | _ -> None) - (fun () -> Active_delegate) ; - register_error_kind - `Temporary - ~id:"delegate.unchanged" - ~title:"Unchanged delegated" - ~description:"Contract already delegated to the given delegate" - ~pp:(fun ppf () -> - Format.fprintf - ppf - "The contract is already delegated to the same delegate") - Data_encoding.empty - (function Current_delegate -> Some () | _ -> None) - (fun () -> Current_delegate) ; - register_error_kind - `Permanent - ~id:"delegate.empty_delegate_account" - ~title:"Empty delegate account" - ~description:"Cannot register a delegate when its implicit account is empty" - ~pp:(fun ppf delegate -> - Format.fprintf - ppf - "Delegate registration is forbidden when the delegate\n\ - \ implicit account is empty (%a)" - Signature.Public_key_hash.pp - delegate) - Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) - (function Empty_delegate_account c -> Some c | _ -> None) - (fun c -> Empty_delegate_account c) ; - (* Unregistered delegate *) - register_error_kind - `Permanent - ~id:"contract.manager.unregistered_delegate" - ~title:"Unregistered delegate" - ~description:"A contract cannot be delegated to an unregistered delegate" - ~pp:(fun ppf k -> - Format.fprintf - ppf - "The provided public key (with hash %a) is not registered as valid \ - delegate key." - Signature.Public_key_hash.pp - k) - Data_encoding.(obj1 (req "hash" Signature.Public_key_hash.encoding)) - (function Unregistered_delegate k -> Some k | _ -> None) - (fun k -> Unregistered_delegate k) ; - (* Unassigned_validation_slot_for_level *) - register_error_kind - `Permanent - ~id:"delegate.unassigned_validation_slot_for_level" - ~title:"Unassigned validation slot for level" - ~description: - "The validation slot for the given level is not assigned. Nobody payed \ - for that slot, or the level is either in the past or too far in the \ - future (further than the validatiors_selection_offset constant)" - ~pp:(fun ppf (l, slot) -> - Format.fprintf - ppf - "The validation slot %i for the level %a is not assigned. Nobody payed \ - for that slot, or the level is either in the past or too far in the \ - future (further than the validatiors_selection_offset constant)" - slot - Level_repr.pp - l) - Data_encoding.(obj2 (req "level" Level_repr.encoding) (req "slot" int31)) - (function - | Unassigned_validation_slot_for_level (l, s) -> Some (l, s) | _ -> None) - (fun (l, s) -> Unassigned_validation_slot_for_level (l, s)) ; - register_error_kind - `Permanent - ~id:"delegate.cannot_find_active_stake" - ~title:"Cannot find active stake" - ~description: - "The active stake of a delegate cannot be found for the given cycle." - ~pp:(fun ppf (cycle, delegate) -> - Format.fprintf - ppf - "The active stake of the delegate %a cannot be found for the cycle %a." - Cycle_repr.pp - cycle - Signature.Public_key_hash.pp - delegate) - Data_encoding.( - obj2 - (req "cycle" Cycle_repr.encoding) - (req "delegate" Signature.Public_key_hash.encoding)) - (function - | Cannot_find_active_stake {cycle; delegate} -> Some (cycle, delegate) - | _ -> None) - (fun (cycle, delegate) -> Cannot_find_active_stake {cycle; delegate}) ; register_error_kind `Temporary ~id:"delegate.not_registered" @@ -183,10 +60,7 @@ let fold = Storage.Delegates.fold let list = Storage.Delegates.elements let pubkey ctxt pkh = - Contract_manager_storage.get_manager_key - ctxt - pkh - ~error:(Unregistered_delegate pkh) + Contract_manager_storage.get_manager_key ctxt pkh ~error:(Not_registered pkh) let frozen_deposits_limit ctxt delegate = Storage.Contract.Frozen_deposits_limit.find @@ -233,6 +107,27 @@ let full_balance ctxt delegate = - stake is properly moved when changing delegation. *) module Contract = struct + type error += + | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t + + let () = + (* Unregistered delegate *) + register_error_kind + `Permanent + ~id:"contract.manager.unregistered_delegate" + ~title:"Unregistered delegate" + ~description:"A contract cannot be delegated to an unregistered delegate" + ~pp:(fun ppf k -> + Format.fprintf + ppf + "The provided public key (with hash %a) is not registered as valid \ + delegate key." + Signature.Public_key_hash.pp + k) + Data_encoding.(obj1 (req "hash" Signature.Public_key_hash.encoding)) + (function Unregistered_delegate k -> Some k | _ -> None) + (fun k -> Unregistered_delegate k) + let init ctxt contract delegate = Contract_manager_storage.is_manager_key_revealed ctxt delegate >>=? fun known_delegate -> @@ -244,6 +139,38 @@ module Contract = struct >>=? fun balance_and_frozen_bonds -> Stake_storage.add_stake ctxt delegate balance_and_frozen_bonds + type error += + | (* `Temporary *) Active_delegate + | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t + + let () = + register_error_kind + `Temporary + ~id:"delegate.already_active" + ~title:"Delegate already active" + ~description:"Useless delegate reactivation" + ~pp:(fun ppf () -> + Format.fprintf ppf "The delegate is still active, no need to refresh it") + Data_encoding.empty + (function Active_delegate -> Some () | _ -> None) + (fun () -> Active_delegate) ; + register_error_kind + `Permanent + ~id:"delegate.empty_delegate_account" + ~title:"Empty delegate account" + ~description: + "Cannot register a delegate when its implicit account is empty" + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "Delegate registration is forbidden when the delegate\n\ + \ implicit account is empty (%a)" + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Empty_delegate_account c -> Some c | _ -> None) + (fun c -> Empty_delegate_account c) + let set_self_delegate c delegate = let open Lwt_tzresult_syntax in let*! is_registered = registered c delegate in @@ -277,6 +204,38 @@ module Contract = struct let* c = Stake_storage.set_active c delegate in return c + type error += + | (* `Permanent *) No_deletion of Signature.Public_key_hash.t + | (* `Temporary *) Current_delegate + + let () = + register_error_kind + `Permanent + ~id:"delegate.no_deletion" + ~title:"Forbidden delegate deletion" + ~description:"Tried to unregister a delegate" + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "Delegate deletion is forbidden (%a)" + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function No_deletion c -> Some c | _ -> None) + (fun c -> No_deletion c) ; + register_error_kind + `Temporary + ~id:"delegate.unchanged" + ~title:"Unchanged delegated" + ~description:"Contract already delegated to the given delegate" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "The contract is already delegated to the same delegate") + Data_encoding.empty + (function Current_delegate -> Some () | _ -> None) + (fun () -> Current_delegate) + let set_delegate c contract delegate = let open Lwt_tzresult_syntax in let* () = diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 885f3e097ea8..8096594f8fab 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -25,19 +25,7 @@ (* *) (*****************************************************************************) -type error += - | (* `Permanent *) No_deletion of Signature.Public_key_hash.t - | (* `Temporary *) Active_delegate - | (* `Temporary *) Current_delegate - | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t - | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t - | (* `Permanent *) Unassigned_validation_slot_for_level of Level_repr.t * int - | (* `Permanent *) - Cannot_find_active_stake of { - cycle : Cycle_repr.t; - delegate : Signature.Public_key_hash.t; - } - | (* `Temporary *) Not_registered of Signature.Public_key_hash.t +type error += (* `Temporary *) Not_registered of Signature.Public_key_hash.t (** Has a delegate been registered in the delegate table? *) val registered : Raw_context.t -> Signature.Public_key_hash.t -> bool Lwt.t @@ -98,6 +86,13 @@ val full_balance : Raw_context.t -> Signature.Public_key_hash.t -> Tez_repr.t tzresult Lwt.t module Contract : sig + type error += + | (* `Permanent *) Unregistered_delegate of Signature.Public_key_hash.t + | (* `Temporary *) Active_delegate + | (* `Permanent *) Empty_delegate_account of Signature.Public_key_hash.t + | (* `Permanent *) No_deletion of Signature.Public_key_hash.t + | (* `Temporary *) Current_delegate + (** [init ctxt contract delegate] registers a delegate when creating a contract. diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml index 41299b62d68a..1aa2a163f01f 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_delegation.ml @@ -56,7 +56,7 @@ let expect_error err = function let expect_alpha_error err = expect_error (Environment.Ecoproto_error err) let expect_no_change_registered_delegate_pkh pkh = function - | Environment.Ecoproto_error (Delegate_storage.No_deletion pkh0) :: _ + | Environment.Ecoproto_error (Delegate_storage.Contract.No_deletion pkh0) :: _ when pkh0 = pkh -> return_unit | _ -> failwith "Delegate can not be deleted and operation should fail." @@ -203,7 +203,8 @@ let bootstrap_manager_already_registered_delegate ~fee () = else Incremental.add_operation ~expect_failure:(function - | Environment.Ecoproto_error Delegate_storage.Active_delegate :: _ -> + | Environment.Ecoproto_error Delegate_storage.Contract.Active_delegate + :: _ -> return_unit | _ -> failwith "Delegate is already active and operation should fail.") i @@ -440,7 +441,8 @@ let tests_bootstrap_contracts = two possibilities of 1a for non-credited contracts. *) let expect_unregistered_key pkh = function - | Environment.Ecoproto_error (Delegate_storage.Unregistered_delegate pkh0) + | Environment.Ecoproto_error + (Delegate_storage.Contract.Unregistered_delegate pkh0) :: _ when pkh = pkh0 -> return_unit -- GitLab From 13223856121a00222f5a3b0548448c388f030166 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Tue, 10 May 2022 02:57:07 +0200 Subject: [PATCH 17/35] Proto: add some doc for `Delegate_storage` --- .../delegate_missed_endorsements_storage.ml | 4 ++++ src/proto_alpha/lib_protocol/delegate_sampler.ml | 7 +++++++ .../lib_protocol/delegate_slashed_deposits_storage.ml | 4 ++++ src/proto_alpha/lib_protocol/delegate_storage.ml | 11 +++++++++++ 4 files changed, 26 insertions(+) diff --git a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml index 96d22c2888cd..7869cfca16ec 100644 --- a/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_missed_endorsements_storage.ml @@ -25,6 +25,10 @@ (* *) (*****************************************************************************) +(** This module is responsible for: + - [Storage.Contract.Missed_endorsements] +*) + let expected_slots_for_given_active_stake ctxt ~total_active_stake ~active_stake = let blocks_per_cycle = diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml index 71b837b7ef60..9d5af86363fd 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.ml +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -25,6 +25,13 @@ (* *) (*****************************************************************************) +(** This module is responsible for: + - [Storage.Seed.For_cycle] + + And the submodule `Delegate_sampler_state` is responsible for: + - [Storage.Delegate_sampler_state] +*) + module Delegate_sampler_state = struct module Cache_client = struct type cached_value = 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 043f8c117346..c3975dc709c0 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -25,6 +25,10 @@ (* *) (*****************************************************************************) +(** This module is responsible for: + - [Storage.Slashed_deposits] +*) + let check_and_record_already_slashed_for_double_baking ctxt delegate (level : Level_repr.t) = let open Lwt_tzresult_syntax in diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 997e86f304ea..b4e7deaf3ddc 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -25,6 +25,17 @@ (* *) (*****************************************************************************) +(** This is module regroups everything related to delegate registration + (see the invariant maintained by the submodule `Contract`. + + It also regroups "trivial" getter/setter related to delegates. + + It is responsible for: + - [Storage.Contract.Frozen_deposits_limit] + - [Storage.Delegates] + +*) + type error += (* `Temporary *) Not_registered of Signature.Public_key_hash.t let () = -- GitLab From 5ccfe823be948781a7995a7969eff5feb6150292 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 16 May 2022 11:16:32 +0200 Subject: [PATCH 18/35] Proto: merge `Delegate.check_and_record_...` into `punish` --- .../lib_protocol/alpha_context.mli | 8 +- src/proto_alpha/lib_protocol/apply.ml | 29 +--- .../delegate_slashed_deposits_storage.ml | 130 ++++++++++-------- .../delegate_slashed_deposits_storage.mli | 40 +++--- 4 files changed, 97 insertions(+), 110 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index c8a56aa4c2b7..43c150907559 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2218,20 +2218,16 @@ module Delegate : sig Cycle.t -> (context * Receipt.balance_updates * public_key_hash list) tzresult Lwt.t - val check_and_record_already_slashed_for_double_endorsing : - context -> public_key_hash -> Level.t -> (context * bool) tzresult Lwt.t - - val check_and_record_already_slashed_for_double_baking : - context -> public_key_hash -> Level.t -> (context * bool) tzresult Lwt.t - val punish_double_endorsing : context -> public_key_hash -> + Level.t -> (context * Tez.t * Receipt.balance_updates) tzresult Lwt.t val punish_double_baking : context -> public_key_hash -> + Level.t -> (context * Tez.t * Receipt.balance_updates) tzresult Lwt.t val full_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 5529711fa4b1..7cee1882adfd 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -107,7 +107,6 @@ type error += delegate1 : Signature.Public_key_hash.t; delegate2 : Signature.Public_key_hash.t; } - | Unrequired_denunciation | Too_early_denunciation of { kind : denunciation_kind; level : Raw_level.t; @@ -617,19 +616,6 @@ let () = | _ -> None) (fun (kind, delegate1, delegate2) -> Inconsistent_denunciation {kind; delegate1; delegate2}) ; - register_error_kind - `Branch - ~id:"block.unrequired_denunciation" - ~title:"Unrequired denunciation" - ~description:"A denunciation is unrequired" - ~pp:(fun ppf _ -> - Format.fprintf - ppf - "A valid denunciation cannot be applied: the associated delegate has \ - already been denounced for this level.") - Data_encoding.unit - (function Unrequired_denunciation -> Some () | _ -> None) - (fun () -> Unrequired_denunciation) ; register_error_kind `Temporary ~id:"block.too_early_denunciation" @@ -2903,19 +2889,12 @@ let check_denunciation_age ctxt kind given_level = {kind; level = given_level; last_cycle = last_slashable_cycle}) let punish_delegate ctxt delegate level mistake mk_result ~payload_producer = - let check_and_record_already_slashed, punish = + let punish = match mistake with - | `Double_baking -> - ( Delegate.check_and_record_already_slashed_for_double_baking, - Delegate.punish_double_baking ) - | `Double_endorsing -> - ( Delegate.check_and_record_already_slashed_for_double_endorsing, - Delegate.punish_double_endorsing ) + | `Double_baking -> Delegate.punish_double_baking + | `Double_endorsing -> Delegate.punish_double_endorsing in - check_and_record_already_slashed ctxt delegate level - >>=? fun (ctxt, slashed) -> - fail_when slashed Unrequired_denunciation >>=? fun () -> - punish ctxt delegate >>=? fun (ctxt, burned, punish_balance_updates) -> + punish ctxt delegate level >>=? fun (ctxt, burned, punish_balance_updates) -> (match Tez.(burned /? 2L) with | Ok reward -> Token.transfer 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 c3975dc709c0..a67b302e57ba 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.ml @@ -29,58 +29,46 @@ - [Storage.Slashed_deposits] *) -let check_and_record_already_slashed_for_double_baking ctxt delegate - (level : Level_repr.t) = - let open Lwt_tzresult_syntax in - let* slashed = - Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) - in - let already_slashed, updated_slashed = - match slashed with - | None -> - (false, {Storage.for_double_baking = true; for_double_endorsing = false}) - | Some slashed -> - (slashed.for_double_baking, {slashed with for_double_baking = true}) - in - let*! ctxt = - Storage.Slashed_deposits.add - (ctxt, level.cycle) - (level.level, delegate) - updated_slashed - in - return (ctxt, already_slashed) +type error += Unrequired_denunciation + +let () = + register_error_kind + `Branch + ~id:"block.unrequired_denunciation" + ~title:"Unrequired denunciation" + ~description:"A denunciation is unrequired" + ~pp:(fun ppf _ -> + Format.fprintf + ppf + "A valid denunciation cannot be applied: the associated delegate has \ + already been denounced for this level.") + Data_encoding.unit + (function Unrequired_denunciation -> Some () | _ -> None) + (fun () -> Unrequired_denunciation) -let check_and_record_already_slashed_for_double_endorsing ctxt delegate - (level : Level_repr.t) = +let clear_outdated_slashed_deposits ctxt ~new_cycle = + let max_slashable_period = Constants_storage.max_slashing_period ctxt in + match Cycle_repr.(sub new_cycle max_slashable_period) with + | None -> Lwt.return ctxt + | Some outdated_cycle -> Storage.Slashed_deposits.clear (ctxt, outdated_cycle) + +let punish_double_endorsing ctxt delegate (level : Level_repr.t) = let open Lwt_tzresult_syntax in let* slashed = Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) in - let already_slashed, updated_slashed = + let* updated_slashed = match slashed with | None -> - (false, {Storage.for_double_endorsing = true; for_double_baking = false}) + return {Storage.for_double_endorsing = true; for_double_baking = false} | Some slashed -> - ( slashed.for_double_endorsing, - {slashed with for_double_endorsing = true} ) - in - let*! ctxt = - Storage.Slashed_deposits.add - (ctxt, level.cycle) - (level.level, delegate) - updated_slashed + let* () = + fail_when slashed.for_double_endorsing Unrequired_denunciation + in + return {slashed with for_double_endorsing = true} in - return (ctxt, already_slashed) - -let clear_outdated_slashed_deposits ctxt ~new_cycle = - let max_slashable_period = Constants_storage.max_slashing_period ctxt in - match Cycle_repr.(sub new_cycle max_slashable_period) with - | None -> Lwt.return ctxt - | Some outdated_cycle -> Storage.Slashed_deposits.clear (ctxt, outdated_cycle) - -let punish_double_endorsing ctxt delegate = let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> + let* frozen_deposits = Frozen_deposits_storage.get ctxt delegate_contract in let slashing_ratio : Ratio_repr.t = Constants_storage.ratio_of_frozen_deposits_slashed_per_double_endorsement ctxt @@ -94,29 +82,55 @@ let punish_double_endorsing ctxt delegate = let amount_to_burn = Tez_repr.(min frozen_deposits.current_amount punish_value) in - Token.transfer - ctxt - (`Frozen_deposits delegate) - `Double_signing_punishments - amount_to_burn - >>=? fun (ctxt, balance_updates) -> - Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> + let* ctxt, balance_updates = + Token.transfer + ctxt + (`Frozen_deposits delegate) + `Double_signing_punishments + amount_to_burn + in + let* ctxt = Stake_storage.remove_stake ctxt delegate amount_to_burn in + let*! ctxt = + Storage.Slashed_deposits.add + (ctxt, level.cycle) + (level.level, delegate) + updated_slashed + in return (ctxt, amount_to_burn, balance_updates) -let punish_double_baking ctxt delegate = +let punish_double_baking ctxt delegate (level : Level_repr.t) = + let open Lwt_tzresult_syntax in + let* slashed = + Storage.Slashed_deposits.find (ctxt, level.cycle) (level.level, delegate) + in + let* updated_slashed = + match slashed with + | None -> + return {Storage.for_double_baking = true; for_double_endorsing = false} + | Some slashed -> + let* () = fail_when slashed.for_double_baking Unrequired_denunciation in + return {slashed with for_double_baking = true} + in let delegate_contract = Contract_repr.Implicit delegate in - Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> + let* frozen_deposits = Frozen_deposits_storage.get ctxt delegate_contract in let slashing_for_one_block = Constants_storage.double_baking_punishment ctxt in let amount_to_burn = Tez_repr.(min frozen_deposits.current_amount slashing_for_one_block) in - Token.transfer - ctxt - (`Frozen_deposits delegate) - `Double_signing_punishments - amount_to_burn - >>=? fun (ctxt, balance_updates) -> - Stake_storage.remove_stake ctxt delegate amount_to_burn >>=? fun ctxt -> + let* ctxt, balance_updates = + Token.transfer + ctxt + (`Frozen_deposits delegate) + `Double_signing_punishments + amount_to_burn + in + let* ctxt = Stake_storage.remove_stake ctxt delegate amount_to_burn in + let*! ctxt = + Storage.Slashed_deposits.add + (ctxt, level.cycle) + (level.level, delegate) + updated_slashed + in return (ctxt, amount_to_burn, balance_updates) 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 474456f6c49d..189a37d29011 100644 --- a/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_slashed_deposits_storage.mli @@ -25,37 +25,35 @@ (* *) (*****************************************************************************) -(** Burn some frozen deposit for a delegate at a given level. Returns - the burned amount. *) -val punish_double_endorsing : - Raw_context.t -> - Signature.Public_key_hash.t -> - (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t +type error += Unrequired_denunciation -val punish_double_baking : - Raw_context.t -> - Signature.Public_key_hash.t -> - (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t +(** Burn some frozen deposit for a delegate at a given level and + record in the context that the given delegate has now been slashed + for double endorsing for the given level. + + Returns the burned amount. -(** Returns true if the given delegate has already been slashed - for double baking for the given level, and record in the context - that the given delegate has now been slashed for double baking - for the given level. *) -val check_and_record_already_slashed_for_double_baking : + Fails with [Unrequired_denunciation] if the given delegate has + already been slashed for double endorsing for the given level. *) +val punish_double_endorsing : Raw_context.t -> Signature.Public_key_hash.t -> Level_repr.t -> - (Raw_context.t * bool) tzresult Lwt.t + (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t -(** Returns true if the given delegate has already been slashed for - double preendorsing or double endorsing for the given level, and +(** Burn some frozen deposit for a delegate at a given level and record in the context that the given delegate has now been slashed - for double preendorsing or double endorsing for the given level. *) -val check_and_record_already_slashed_for_double_endorsing : + for double baking for the given level. + + Returns the burned amount. + + Fails with [Unrequired_denunciation] if the given delegate has + already been slashed for double baking for the given level. *) +val punish_double_baking : Raw_context.t -> Signature.Public_key_hash.t -> Level_repr.t -> - (Raw_context.t * bool) tzresult Lwt.t + (Raw_context.t * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t val clear_outdated_slashed_deposits : Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t Lwt.t -- GitLab From bbdb9f0c350dd35474077ca598a879492a7af59d Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 19/35] Proto: Make room for more ghosts in `Storage_functors`. --- src/proto_alpha/lib_protocol/storage.ml | 36 +++++++++++++++++-- .../lib_protocol/storage_functors.ml | 16 ++++++--- src/proto_alpha/lib_protocol/storage_sigs.ml | 4 +-- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 46c941431516..f314b79f273a 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -190,6 +190,7 @@ module Contract = struct module Spendable_balance = Indexed_context.Make_map + (Registered) (struct let name = ["balance"] end) @@ -197,6 +198,7 @@ module Contract = struct module Missed_endorsements = Indexed_context.Make_map + (Registered) (struct let name = ["missed_endorsements"] end) @@ -204,6 +206,7 @@ module Contract = struct module Manager = Indexed_context.Make_map + (Registered) (struct let name = ["manager"] end) @@ -211,6 +214,7 @@ module Contract = struct module Delegate = Indexed_context.Make_map + (Registered) (struct let name = ["delegate"] end) @@ -225,6 +229,7 @@ module Contract = struct module Delegate_last_cycle_before_deactivation = Indexed_context.Make_map + (Registered) (struct (* FIXME? Change the key name to reflect the functor's name *) let name = ["delegate_desactivation"] @@ -241,6 +246,7 @@ module Contract = struct module Counter = Indexed_context.Make_map + (Registered) (struct let name = ["counter"] end) @@ -254,8 +260,7 @@ module Contract = struct and type value = Script_repr.lazy_expr and type t := Raw_context.t = struct module I = - Indexed_context.Make_carbonated_map - (N) + Indexed_context.Make_carbonated_map (Registered) (N) (struct type t = Script_repr.lazy_expr @@ -324,6 +329,7 @@ module Contract = struct module Paid_storage_space = Indexed_context.Make_map + (Registered) (struct let name = ["paid_bytes"] end) @@ -331,6 +337,7 @@ module Contract = struct module Used_storage_space = Indexed_context.Make_map + (Registered) (struct let name = ["used_bytes"] end) @@ -338,6 +345,7 @@ module Contract = struct module Frozen_deposits = Indexed_context.Make_map + (Registered) (struct let name = ["frozen_deposits"] end) @@ -345,6 +353,7 @@ module Contract = struct module Frozen_deposits_limit = Indexed_context.Make_map + (Registered) (struct let name = ["frozen_deposits_limit"] end) @@ -360,6 +369,7 @@ module Contract = struct module Frozen_bonds = Bond_id_index.Make_carbonated_map + (Registered) (struct let name = ["frozen_bonds"] end) @@ -369,6 +379,7 @@ module Contract = struct module Total_frozen_bonds = Indexed_context.Make_map + (Registered) (struct let name = ["total_frozen_bonds"] end) @@ -453,6 +464,7 @@ module Big_map = struct module Total_bytes = Indexed_context.Make_map + (Registered) (struct let name = ["total_bytes"] end) @@ -460,6 +472,7 @@ module Big_map = struct module Key_type = Indexed_context.Make_map + (Registered) (struct let name = ["key_type"] end) @@ -471,6 +484,7 @@ module Big_map = struct module Value_type = Indexed_context.Make_map + (Registered) (struct let name = ["value_type"] end) @@ -582,6 +596,7 @@ module Sapling = struct module Total_bytes = Indexed_context.Make_map + (Registered) (struct let name = ["total_bytes"] end) @@ -942,6 +957,7 @@ module Cycle = struct module Selected_stake_distribution = Indexed_context.Make_map + (Registered) (struct let name = ["selected_stake_distribution"] end) @@ -958,6 +974,7 @@ module Cycle = struct module Total_active_stake = Indexed_context.Make_map + (Registered) (struct let name = ["total_active_stake"] end) @@ -971,6 +988,7 @@ module Cycle = struct module Delegate_sampler_state = Indexed_context.Make_map + (Registered) (struct let name = ["delegate_sampler_state"] end) @@ -1025,6 +1043,7 @@ module Cycle = struct module Seed = Indexed_context.Make_map + (Registered) (struct let name = ["random_seed"] end) @@ -1417,6 +1436,7 @@ module Tx_rollup = struct module State = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["state"] end) @@ -1432,6 +1452,7 @@ module Tx_rollup = struct module Inbox = Level_context.Make_carbonated_map + (Registered) (struct let name = ["inbox"] end) @@ -1443,6 +1464,7 @@ module Tx_rollup = struct module Revealed_withdrawals = Level_context.Make_carbonated_map + (Registered) (struct let name = ["withdrawals"] end) @@ -1450,6 +1472,7 @@ module Tx_rollup = struct module Commitment = Level_context.Make_carbonated_map + (Registered) (struct let name = ["commitment"] end) @@ -1465,6 +1488,7 @@ module Tx_rollup = struct module Commitment_bond = Bond_indexed_context.Make_carbonated_map + (Registered) (struct let name = ["commitment"] end) @@ -1492,6 +1516,7 @@ module Sc_rollup = struct module PVM_kind = Indexed_context.Make_map + (Registered) (struct let name = ["kind"] end) @@ -1503,6 +1528,7 @@ module Sc_rollup = struct module Boot_sector = Indexed_context.Make_map + (Registered) (struct let name = ["boot_sector"] end) @@ -1514,6 +1540,7 @@ module Sc_rollup = struct module Parameters_type = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["parameters_type"] end) @@ -1525,6 +1552,7 @@ module Sc_rollup = struct module Initial_level = Indexed_context.Make_map + (Registered) (struct let name = ["initial_level"] end) @@ -1536,6 +1564,7 @@ module Sc_rollup = struct module Inbox = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["inbox"] end) @@ -1547,6 +1576,7 @@ module Sc_rollup = struct module Last_cemented_commitment = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["last_cemented_commitment"] end) @@ -1571,6 +1601,7 @@ module Sc_rollup = struct module Staker_count = Indexed_context.Make_carbonated_map + (Registered) (struct let name = ["staker_count"] end) @@ -1714,6 +1745,7 @@ module Sc_rollup = struct module Applied_outbox_messages = Level_index_context.Make_carbonated_map + (Registered) (struct let name = ["applied_outbox_messages"] end) diff --git a/src/proto_alpha/lib_protocol/storage_functors.ml b/src/proto_alpha/lib_protocol/storage_functors.ml index d32d3f00c456..8652c9598192 100644 --- a/src/proto_alpha/lib_protocol/storage_functors.ml +++ b/src/proto_alpha/lib_protocol/storage_functors.ml @@ -882,7 +882,7 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : [@@coq_axiom_with_reason "stack overflow in Coq"] end - module Make_map (N : NAME) (V : VALUE) : + module Make_map (R : REGISTER) (N : NAME) (V : VALUE) : Indexed_data_storage with type t = t and type key = key and type value = V.t = struct type t = C.t @@ -966,16 +966,20 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : let () = let open Storage_description in let unpack = unpack I.args in + let description = + if R.ghost then Storage_description.create () + else Raw_context.description + in register_value ~get:(fun c -> let c, k = unpack c in find c k) - (register_named_subcontext Raw_context.description N.name) + (register_named_subcontext description N.name) V.encoding [@@coq_axiom_with_reason "stack overflow in Coq"] end - module Make_carbonated_map (N : NAME) (V : VALUE) : + module Make_carbonated_map (R : REGISTER) (N : NAME) (V : VALUE) : Non_iterable_indexed_carbonated_data_storage with type t = t and type key = key @@ -1082,11 +1086,15 @@ module Make_indexed_subcontext (C : Raw_context.T) (I : INDEX) : let () = let open Storage_description in let unpack = unpack I.args in + let description = + if R.ghost then Storage_description.create () + else Raw_context.description + in register_value ~get:(fun c -> let c, k = unpack c in find c k >|=? fun (_, v) -> v) - (register_named_subcontext Raw_context.description N.name) + (register_named_subcontext description N.name) V.encoding [@@coq_axiom_with_reason "stack overflow in Coq"] end diff --git a/src/proto_alpha/lib_protocol/storage_sigs.ml b/src/proto_alpha/lib_protocol/storage_sigs.ml index ad16d90af855..b38b18ae7c08 100644 --- a/src/proto_alpha/lib_protocol/storage_sigs.ml +++ b/src/proto_alpha/lib_protocol/storage_sigs.ml @@ -410,10 +410,10 @@ module type Indexed_raw_context = sig module Make_set (_ : REGISTER) (_ : NAME) : Data_set_storage with type t = t and type elt = key - module Make_map (_ : NAME) (V : VALUE) : + module Make_map (_ : REGISTER) (_ : NAME) (V : VALUE) : Indexed_data_storage with type t = t and type key = key and type value = V.t - module Make_carbonated_map (_ : NAME) (V : VALUE) : + module Make_carbonated_map (_ : REGISTER) (_ : NAME) (V : VALUE) : Non_iterable_indexed_carbonated_data_storage with type t = t and type key = key -- GitLab From a145a5c2fbb5b1777f2b6a9a814bfd281cdc8677 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 20/35] Proto/Delegate: Add a `Storage.Contracts.Consensus_key` --- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 1 + src/proto_alpha/lib_protocol/alpha_context.ml | 2 + .../lib_protocol/alpha_context.mli | 16 ++ .../lib_protocol/delegate_consensus_key.ml | 155 +++++++++++++ .../lib_protocol/delegate_consensus_key.mli | 73 ++++++ .../lib_protocol/delegate_cycles.ml | 1 + .../lib_protocol/delegate_sampler.ml | 4 +- .../lib_protocol/delegate_storage.ml | 26 ++- .../lib_protocol/delegate_storage.mli | 4 + src/proto_alpha/lib_protocol/dune | 5 + src/proto_alpha/lib_protocol/init_storage.ml | 4 +- src/proto_alpha/lib_protocol/storage.ml | 17 ++ src/proto_alpha/lib_protocol/storage.mli | 14 ++ .../lib_protocol/test/unit/main.ml | 1 + .../test/unit/test_consensus_key.ml | 210 ++++++++++++++++++ 15 files changed, 525 insertions(+), 8 deletions(-) create mode 100644 src/proto_alpha/lib_protocol/delegate_consensus_key.ml create mode 100644 src/proto_alpha/lib_protocol/delegate_consensus_key.mli create mode 100644 src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index e8c49f63ca92..07845e66f02d 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -111,6 +111,7 @@ "Stake_storage", "Contract_storage", "Token", + "Delegate_consensus_key", "Delegate_storage", "Delegate_missed_endorsements_storage", "Delegate_slashed_deposits_storage", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 8b291cc38354..242c1d1fe8c9 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -421,6 +421,8 @@ module Delegate = struct let prepare_stake_distribution = Stake_storage.prepare_stake_distribution let delegated_contracts = Contract_delegate_storage.delegated_contracts + + module Consensus_key = Delegate_consensus_key end module Stake_distribution = struct diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 43c150907559..45a2f2784fb5 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2268,6 +2268,22 @@ module Delegate : sig val pubkey : context -> public_key_hash -> public_key tzresult Lwt.t + module Consensus_key : sig + val active_pubkey : context -> public_key_hash -> public_key tzresult Lwt.t + + val pending_updates : + context -> + public_key_hash -> + (Cycle.t * public_key_hash) list tzresult Lwt.t + + val register_update : + context -> + public_key_hash -> + Cycle.t -> + public_key -> + context tzresult Lwt.t + end + val prepare_stake_distribution : context -> context tzresult Lwt.t end diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml new file mode 100644 index 000000000000..2303e0287a1b --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -0,0 +1,155 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type error += + | Invalid_consensus_key_update_cycle of Cycle_repr.t + | Invalid_consensus_key_update_noop of Cycle_repr.t + +let () = + register_error_kind + `Permanent + ~id:"delegate.consensus_key.invalid_cycle" + ~title:"Invalid cycle for consensus key update" + ~description:"Tried to update the consensus key for an invalid cycle" + ~pp:(fun ppf cycle -> + Format.fprintf + ppf + "Invalid cycle while updating a consensus key (%a)." + Cycle_repr.pp + cycle) + Data_encoding.(obj1 (req "cycle" Cycle_repr.encoding)) + (function Invalid_consensus_key_update_cycle c -> Some c | _ -> None) + (fun c -> Invalid_consensus_key_update_cycle c) ; + register_error_kind + `Permanent + ~id:"delegate.consensus_key.invalid_noop" + ~title:"Invalid key for consensus key update" + ~description:"Tried to update the consensus key with an active key" + ~pp:(fun ppf cycle -> + Format.fprintf + ppf + "Invalid key while updating a consensus key (active since %a)." + Cycle_repr.pp + cycle) + Data_encoding.(obj1 (req "cycle" Cycle_repr.encoding)) + (function Invalid_consensus_key_update_noop c -> Some c | _ -> None) + (fun c -> Invalid_consensus_key_update_noop c) + +let init ctxt delegate pk = + Storage.Contract.Consensus_key.init ctxt (Contract_repr.Implicit delegate) pk + +let active_pubkey ctxt delegate = + let open Lwt_tzresult_syntax in + let* pk = + Storage.Contract.Consensus_key.get ctxt (Contract_repr.Implicit delegate) + in + return pk + +let active_key ctxt delegate = + let open Lwt_tzresult_syntax in + let* pk = active_pubkey ctxt delegate in + return (Signature.Public_key.hash pk) + +let raw_pending_updates ctxt delegate = + let open Lwt_tzresult_syntax in + let*! pendings = + Storage.Contract.Pending_consensus_keys.bindings + (ctxt, Contract_repr.Implicit delegate) + in + return pendings + +let pending_updates ctxt delegate = + let open Lwt_tzresult_syntax in + let* updates = raw_pending_updates ctxt delegate in + let updates = + List.sort (fun (c1, _) (c2, _) -> Cycle_repr.compare c1 c2) updates + in + return (List.map (fun (c, pk) -> (c, Signature.Public_key.hash pk)) updates) + +let raw_active_pubkey_for_cycle ctxt delegate cycle = + let open Lwt_tzresult_syntax in + let* pendings = raw_pending_updates ctxt delegate in + let* active = active_pubkey ctxt delegate in + let current_level = Raw_context.current_level ctxt in + let active_for_cycle = + List.fold_left + (fun (c1, active) (c2, pk) -> + if Cycle_repr.(c1 < c2 && c2 <= cycle) then (c2, pk) else (c1, active)) + (current_level.cycle, active) + pendings + in + return active_for_cycle + +let active_pubkey_for_cycle ctxt delegate cycle = + let open Lwt_tzresult_syntax in + let* _, active = raw_active_pubkey_for_cycle ctxt delegate cycle in + return active + +let register_update ctxt delegate cycle pk = + let open Lwt_tzresult_syntax in + let* () = + let current_level = Raw_context.current_level ctxt in + fail_when + Cycle_repr.(cycle <= current_level.cycle) + (Invalid_consensus_key_update_cycle cycle) + in + let* () = + let* cycle, active_pubkey = + raw_active_pubkey_for_cycle ctxt delegate cycle + in + fail_when + Signature.Public_key.(pk = active_pubkey) + (Invalid_consensus_key_update_noop cycle) + in + let*! ctxt = + Storage.Contract.Pending_consensus_keys.add + (ctxt, Contract_repr.Implicit delegate) + cycle + pk + in + return ctxt + +let activate ctxt ~new_cycle = + let open Lwt_tzresult_syntax in + Storage.Delegates.fold + ctxt + ~order:`Undefined + ~init:(ok ctxt) + ~f:(fun delegate ctxt -> + let*? ctxt = ctxt in + let delegate = Contract_repr.Implicit delegate in + let* update = + Storage.Contract.Pending_consensus_keys.find (ctxt, delegate) new_cycle + in + match update with + | None -> return ctxt + | Some pk -> + let*! ctxt = Storage.Contract.Consensus_key.add ctxt delegate pk in + let*! ctxt = + Storage.Contract.Pending_consensus_keys.remove + (ctxt, delegate) + new_cycle + in + return ctxt) diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli new file mode 100644 index 000000000000..8ca12e0beb8b --- /dev/null +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -0,0 +1,73 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type error += + | Invalid_consensus_key_update_cycle of Cycle_repr.t + | Invalid_consensus_key_update_noop of Cycle_repr.t + +(** Initialize the consensus key when registering a delegate. *) +val init : + Raw_context.t -> + Signature.Public_key_hash.t -> + Signature.Public_key.t -> + Raw_context.t tzresult Lwt.t + +(** Returns the active consensus key for the current cycle. *) +val active_pubkey : + Raw_context.t -> + Signature.Public_key_hash.t -> + Signature.Public_key.t tzresult Lwt.t + +(** Returns the active consensus key for the current cycle. *) +val active_key : + Raw_context.t -> + Signature.Public_key_hash.t -> + Signature.Public_key_hash.t tzresult Lwt.t + +(** Returns the active consensus key for the given cycle. *) +val active_pubkey_for_cycle : + Raw_context.t -> + Signature.Public_key_hash.t -> + Cycle_repr.t -> + Signature.Public_key.t tzresult Lwt.t + +(** Returns the list of pending consensus-key updates in upcoming cycles. *) +val pending_updates : + Raw_context.t -> + Signature.Public_key_hash.t -> + (Cycle_repr.t * Signature.Public_key_hash.t) list tzresult Lwt.t + +(** Register a consensus-key update in a given cycles. *) +val register_update : + Raw_context.t -> + Signature.Public_key_hash.t -> + Cycle_repr.t -> + Signature.Public_key.t -> + Raw_context.t tzresult Lwt.t + +(** Activate consensus keys at the beggining of cycle [new_cycle]. + This function iterates on all registered delegates. *) +val activate : + Raw_context.t -> new_cycle:Cycle_repr.t -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index 53a0c38c2146..e4cf195b7a21 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -251,6 +251,7 @@ let cycle_end ctxt last_cycle = let new_cycle = Cycle_repr.add last_cycle 1 in Delegate_sampler.select_new_distribution_at_cycle_end ctxt ~new_cycle >>=? fun ctxt -> + Delegate_consensus_key.activate ctxt ~new_cycle >>=? fun ctxt -> Delegate_slashed_deposits_storage.clear_outdated_slashed_deposits ctxt ~new_cycle diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml index 9d5af86363fd..b2611be04dfd 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.ml +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -224,8 +224,8 @@ let select_distribution_for_cycle ctxt cycle = >>=? fun ctxt -> List.fold_left_es (fun acc (pkh, stake) -> - Delegate_storage.pubkey ctxt pkh >|=? fun pk -> - ((pk, pkh), Tez_repr.to_mutez stake) :: acc) + Delegate_consensus_key.active_pubkey_for_cycle ctxt pkh cycle + >|=? fun pk -> ((pk, pkh), Tez_repr.to_mutez stake) :: acc) [] stakes >>=? fun stakes_pk -> diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index b4e7deaf3ddc..d5765cc8fb47 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -193,11 +193,11 @@ module Contract = struct Stake_storage.set_active c delegate else let contract = Contract_repr.Implicit delegate in - let* () = - let* is_pk_revealed = - Contract_manager_storage.is_manager_key_revealed c delegate - in - fail_unless is_pk_revealed (Unregistered_delegate delegate) + let* pk = + Contract_manager_storage.get_manager_key + c + ~error:(Unregistered_delegate delegate) + delegate in let* () = let*! is_allocated = Contract_storage.allocated c contract in @@ -212,6 +212,7 @@ module Contract = struct let* c = Contract_delegate_storage.set c contract delegate in let* c = Stake_storage.add_stake c delegate balance_and_frozen_bonds in let*! c = Storage.Delegates.add c delegate in + let* c = Delegate_consensus_key.init c delegate pk in let* c = Stake_storage.set_active c delegate in return c @@ -296,3 +297,18 @@ module Contract = struct set_self_delegate c delegate | _ -> set_delegate c contract delegate end + +module Migration_from_Jakarta = struct + let update_delegate ctxt pkh = + let open Lwt_tzresult_syntax in + (* Consensus key*) + let* pk = Contract_manager_storage.get_manager_key ctxt pkh in + let* ctxt = Delegate_consensus_key.init ctxt pkh pk in + return ctxt + + let update ctxt = + let open Lwt_tzresult_syntax in + let*! delegates = Storage.Delegates.elements ctxt in + let* ctxt = List.fold_left_es update_delegate ctxt delegates in + return ctxt +end diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 8096594f8fab..29e649efdefd 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -127,3 +127,7 @@ val pubkey : Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t + +module Migration_from_Jakarta : sig + val update : Raw_context.t -> Raw_context.t tzresult Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 7b7cc9980b21..5f889852a9dd 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -138,6 +138,7 @@ Stake_storage Contract_storage Token + Delegate_consensus_key Delegate_storage Delegate_missed_endorsements_storage Delegate_slashed_deposits_storage @@ -355,6 +356,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml delegate_missed_endorsements_storage.mli @@ -559,6 +561,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml delegate_missed_endorsements_storage.mli @@ -783,6 +786,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml delegate_missed_endorsements_storage.mli @@ -1003,6 +1007,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml delegate_missed_endorsements_storage.mli diff --git a/src/proto_alpha/lib_protocol/init_storage.ml b/src/proto_alpha/lib_protocol/init_storage.ml index e5195fe38233..ea7eb7858dbb 100644 --- a/src/proto_alpha/lib_protocol/init_storage.ml +++ b/src/proto_alpha/lib_protocol/init_storage.ml @@ -118,7 +118,9 @@ let prepare_first_block _chain_id ctxt ~typecheck ~level ~timestamp = if that is done, do not set Storage.Tenderbake.First_level_of_protocol. *) Raw_level_repr.of_int32 level >>?= fun level -> Storage.Tenderbake.First_level_of_protocol.update ctxt level - >>=? fun ctxt -> return (ctxt, [])) + >>=? fun ctxt -> + Delegate_storage.Migration_from_Jakarta.update ctxt >>=? fun ctxt -> + return (ctxt, [])) >>=? fun (ctxt, balance_updates) -> Receipt_repr.group_balance_updates balance_updates >>?= fun balance_updates -> Storage.Pending_migration.Balance_updates.add ctxt balance_updates diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index f314b79f273a..3ffcddec2f46 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -212,6 +212,23 @@ module Contract = struct end) (Manager_repr) + module Consensus_key = + Indexed_context.Make_map + (Registered) + (struct + let name = ["consensus_key"; "active"] + end) + (Signature.Public_key) + + module Pending_consensus_keys = + Make_indexed_data_storage + (Make_subcontext (Registered) (Indexed_context.Raw_context) + (struct + let name = ["consensus_key"; "pendings"] + end)) + (Make_index (Cycle_repr.Index)) + (Signature.Public_key) + module Delegate = Indexed_context.Make_map (Registered) diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 667740f8fe60..27eec55eb3df 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -104,6 +104,20 @@ module Contract : sig and type value = Manager_repr.t and type t := Raw_context.t + (** The active consensus key of a delegate *) + module Consensus_key : + Indexed_data_storage + with type key = Contract_repr.t + and type value = Signature.Public_key.t + and type t := Raw_context.t + + (** The pending consensus key of a delegate *) + module Pending_consensus_keys : + Indexed_data_storage + with type key = Cycle_repr.t + and type value = Signature.Public_key.t + and type t := Raw_context.t * Contract_repr.t + (** The delegate of a contract, if any. *) module Delegate : Indexed_data_storage diff --git a/src/proto_alpha/lib_protocol/test/unit/main.ml b/src/proto_alpha/lib_protocol/test/unit/main.ml index 8e1f03660c12..42bc5a90aa75 100644 --- a/src/proto_alpha/lib_protocol/test/unit/main.ml +++ b/src/proto_alpha/lib_protocol/test/unit/main.ml @@ -79,5 +79,6 @@ let () = Unit_test.spec "sc rollup management protocol" Test_sc_rollup_management_protocol.tests; + Unit_test.spec "Delegate_consensus_key.ml" Test_consensus_key.tests; ] |> Lwt_main.run diff --git a/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml new file mode 100644 index 000000000000..2b66644bcdd0 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml @@ -0,0 +1,210 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Protocol (delegate_consensus_key) + Invocation: dune exec src/proto_alpha/lib_protocol/test/unit/main.exe \ + -- test "^\[Unit\] Delegate_consensus_key.ml" + Subject: Functions from the module `Delegate_consensus_key` +*) + +open Protocol + +let create () = + let open Lwt_result_syntax in + let accounts = Account.generate_accounts 1 in + let account = + match accounts with [(account, _, _)] -> account | _ -> assert false + in + let* ctxt = Block.alpha_context accounts in + return (Alpha_context.Internal_for_tests.to_raw ctxt, account) + +module Consensus_key = struct + let active_key ctxt pkh = + Delegate_consensus_key.active_key ctxt pkh >|= Environment.wrap_tzresult + + let active_pubkey ctxt pkh = + Delegate_consensus_key.active_pubkey ctxt pkh >|= Environment.wrap_tzresult + + let active_pubkey_for_cycle ctxt pkh cycle = + Delegate_consensus_key.active_pubkey_for_cycle + ctxt + pkh + (Cycle_repr.of_int32_exn (Int32.of_int cycle)) + >|= Environment.wrap_tzresult + + let pending_updates ctxt pkh = + Delegate_consensus_key.pending_updates ctxt pkh + >|= Environment.wrap_tzresult + + let register_update ctxt pkh cycle pk = + Delegate_consensus_key.register_update + ctxt + pkh + (Cycle_repr.of_int32_exn (Int32.of_int cycle)) + pk + >|= Environment.wrap_tzresult + + let activate ctxt ~new_cycle = + Delegate_consensus_key.activate + ctxt + ~new_cycle:(Cycle_repr.of_int32_exn (Int32.of_int new_cycle)) + >|= Environment.wrap_tzresult +end + +module Assert = struct + include Assert + + let equal_pkh ~__LOC__ a b = + Assert.equal + ~loc:__LOC__ + Signature.Public_key_hash.equal + "pkh" + Signature.Public_key_hash.pp + a + b + + let equal_pk ~__LOC__ a b = + Assert.equal + ~loc:__LOC__ + Signature.Public_key.equal + "pk" + Signature.Public_key.pp + a + b + + let active_keys ~__LOC__ ctxt delegate l = + List.iter_es + (fun (c, pk) -> + let open Lwt_result_syntax in + let* active_pk = + Consensus_key.active_pubkey_for_cycle ctxt delegate c + in + equal_pk ~__LOC__ active_pk pk) + l +end + +let test_consensus_key_storage () = + let open Lwt_result_syntax in + let* ctxt, delegate = create () in + let a1 = Account.new_account () in + let a2 = Account.new_account () in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh delegate.pkh + in + let* () = + let* active_pk = Consensus_key.active_pubkey ctxt delegate.pkh in + Assert.equal_pk ~__LOC__ active_pk delegate.pk + in + let* () = + let* active_pk = + Consensus_key.active_pubkey_for_cycle ctxt delegate.pkh 5 + in + Assert.equal_pk ~__LOC__ active_pk delegate.pk + in + let* ctxt = Consensus_key.register_update ctxt delegate.pkh 2 a1.pk in + let* ctxt = Consensus_key.register_update ctxt delegate.pkh 5 a2.pk in + let* () = + Assert.active_keys + ~__LOC__ + ctxt + delegate.pkh + [ + (0, delegate.pk); + (1, delegate.pk); + (2, a1.pk); + (3, a1.pk); + (4, a1.pk); + (5, a2.pk); + (6, a2.pk); + ] + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 2 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 2l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 3 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 2l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 2l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 5 a2.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 5l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 6 a2.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 5l + | _ -> false) + in + let* ctxt = Consensus_key.activate ctxt ~new_cycle:1 in + let* ctxt = Consensus_key.activate ctxt ~new_cycle:2 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh a1.pkh + in + let* ctxt = Consensus_key.activate ctxt ~new_cycle:3 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh a1.pkh + in + let* ctxt = Consensus_key.activate ctxt ~new_cycle:4 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh a1.pkh + in + let* ctxt = Consensus_key.activate ctxt ~new_cycle:5 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh a2.pkh + in + let* ctxt = Consensus_key.activate ctxt ~new_cycle:6 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh a2.pkh + in + return () + +let tests = + [Tztest.tztest "consensus_key_storage" `Quick test_consensus_key_storage] -- GitLab From 3f6ddd3e4df4b73a0ff0667196e9fddeeeb68290 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 21/35] Proto/Delegate: rename `pubkey` into `manager_pubkey` --- scripts/yes-wallet/yes_wallet_lib.ml | 2 +- src/proto_alpha/lib_protocol/alpha_context.mli | 5 ++++- src/proto_alpha/lib_protocol/apply.ml | 4 ++-- src/proto_alpha/lib_protocol/delegate_storage.ml | 2 +- src/proto_alpha/lib_protocol/delegate_storage.mli | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/yes-wallet/yes_wallet_lib.ml b/scripts/yes-wallet/yes_wallet_lib.ml index b49196122b25..9c3359aa2f0c 100644 --- a/scripts/yes-wallet/yes_wallet_lib.ml +++ b/scripts/yes-wallet/yes_wallet_lib.ml @@ -449,7 +449,7 @@ let get_delegates (proto : protocol) context ~init:(Ok ([], Alpha_context.Tez.zero)) ~f:(fun pkh acc -> let* pk = - let*! r = Alpha_context.Delegate.pubkey ctxt pkh in + let*! r = Alpha_context.Delegate.manager_pubkey ctxt pkh in Lwt.return @@ Environment.wrap_tzresult r in let*? key_list_acc, staking_balance_acc = acc in diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 45a2f2784fb5..4586aec9670e 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2266,7 +2266,10 @@ module Delegate : sig val last_cycle_before_deactivation : context -> public_key_hash -> Cycle.t tzresult Lwt.t - val pubkey : context -> public_key_hash -> public_key tzresult Lwt.t + val manager_pubkey : + context -> + Signature.public_key_hash -> + Signature.Public_key.t tzresult Lwt.t module Consensus_key : sig val active_pubkey : context -> public_key_hash -> public_key tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 7cee1882adfd..0511dc1c0c36 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -3147,7 +3147,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode >>=? fun (ctxt, bupds) -> return (ctxt, Single_result (Activate_account_result bupds)) | Single (Proposals {source; period; proposals}) -> - Delegate.pubkey ctxt source >>=? fun delegate -> + Delegate.manager_pubkey ctxt source >>=? fun delegate -> Operation.check_signature delegate chain_id operation >>?= fun () -> Voting_period.get_current ctxt >>=? fun {index = current_period; _} -> error_unless @@ -3157,7 +3157,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode Amendment.record_proposals ctxt source proposals >|=? fun ctxt -> (ctxt, Single_result Proposals_result) | Single (Ballot {source; period; proposal; ballot}) -> - Delegate.pubkey ctxt source >>=? fun delegate -> + Delegate.manager_pubkey ctxt source >>=? fun delegate -> Operation.check_signature delegate chain_id operation >>?= fun () -> Voting_period.get_current ctxt >>=? fun {index = current_period; _} -> error_unless diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index d5765cc8fb47..29268f00b595 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -70,7 +70,7 @@ let fold = Storage.Delegates.fold let list = Storage.Delegates.elements -let pubkey ctxt pkh = +let manager_pubkey ctxt pkh = Contract_manager_storage.get_manager_key ctxt pkh ~error:(Not_registered pkh) let frozen_deposits_limit ctxt delegate = diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 29e649efdefd..bf603fdd3806 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -123,7 +123,7 @@ module Contract : sig Raw_context.t tzresult Lwt.t end -val pubkey : +val manager_pubkey : Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t -- GitLab From 9d6283d2fd8e38d83ec61ac9a8a8cb4d3e8b1ea9 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 22/35] Proto/RPC: add an RPC for querying a delegate's consensus key --- .../lib_protocol/delegate_services.ml | 55 +++++++++++++++++-- .../lib_protocol/delegate_services.mli | 10 ++++ .../lib_protocol/test/helpers/context.ml | 4 ++ .../lib_protocol/test/helpers/context.mli | 7 +++ .../rpc/alpha.client.delegates.out | 3 +- .../rpc/alpha.light.delegates.out | 3 +- .../rpc/alpha.proxy.delegates.out | 3 +- .../alpha.proxy_server_data_dir.delegates.out | 3 +- .../rpc/alpha.proxy_server_rpc.delegates.out | 3 +- 9 files changed, 82 insertions(+), 9 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_services.ml b/src/proto_alpha/lib_protocol/delegate_services.ml index 26b149982247..ebbc02a7152c 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.ml +++ b/src/proto_alpha/lib_protocol/delegate_services.ml @@ -60,6 +60,8 @@ type info = { deactivated : bool; grace_period : Cycle.t; voting_info : Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } let info_encoding = @@ -76,6 +78,8 @@ let info_encoding = deactivated; grace_period; voting_info; + active_consensus_key; + pending_consensus_keys; } -> ( ( full_balance, current_frozen_deposits, @@ -86,7 +90,7 @@ let info_encoding = delegated_balance, deactivated, grace_period ), - voting_info )) + (voting_info, (active_consensus_key, pending_consensus_keys)) )) (fun ( ( full_balance, current_frozen_deposits, frozen_deposits, @@ -96,7 +100,7 @@ let info_encoding = delegated_balance, deactivated, grace_period ), - voting_info ) -> + (voting_info, (active_consensus_key, pending_consensus_keys)) ) -> { full_balance; current_frozen_deposits; @@ -108,6 +112,8 @@ let info_encoding = deactivated; grace_period; voting_info; + active_consensus_key; + pending_consensus_keys; }) (merge_objs (obj9 @@ -120,7 +126,17 @@ let info_encoding = (req "delegated_balance" Tez.encoding) (req "deactivated" bool) (req "grace_period" Cycle.encoding)) - Vote.delegate_info_encoding) + (merge_objs + Vote.delegate_info_encoding + (obj2 + (req "active_consensus_key" Signature.Public_key_hash.encoding) + (dft + "pending_consensus_keys" + (list + (obj2 + (req "cycle" Cycle.encoding) + (req "pkh" Signature.Public_key_hash.encoding))) + [])))) let participation_info_encoding = let open Data_encoding in @@ -307,6 +323,25 @@ module S = struct ~output:Vote.delegate_info_encoding RPC_path.(path / "voting_info") + let consensus_key = + RPC_service.get_service + ~description: + "The active consensus key for a given delegate and the pending \ + consensus keys." + ~query:RPC_query.empty + ~output: + Data_encoding.( + obj2 + (req "active" Signature.Public_key_hash.encoding) + (dft + "pendings" + (list + (obj2 + (req "cycle" Cycle.encoding) + (req "pkh" Signature.Public_key_hash.encoding))) + [])) + RPC_path.(path / "consensus_key") + let participation = RPC_service.get_service ~description: @@ -374,7 +409,9 @@ let register () = Delegate.delegated_balance ctxt pkh >>=? fun delegated_balance -> Delegate.deactivated ctxt pkh >>=? fun deactivated -> Delegate.last_cycle_before_deactivation ctxt pkh >>=? fun grace_period -> - Vote.get_delegate_info ctxt pkh >|=? fun voting_info -> + Vote.get_delegate_info ctxt pkh >>=? fun voting_info -> + Delegate.Consensus_key.active_pubkey ctxt pkh >>=? fun consensus_key -> + Delegate.Consensus_key.pending_updates ctxt pkh >|=? fun pendings -> { full_balance; current_frozen_deposits = frozen_deposits.current_amount; @@ -386,6 +423,8 @@ let register () = deactivated; grace_period; voting_info; + active_consensus_key = Signature.Public_key.hash consensus_key; + pending_consensus_keys = pendings; }) ; register1 ~chunked:false S.full_balance (fun ctxt pkh () () -> trace (Balance_rpc_non_delegate pkh) (Delegate.check_registered ctxt pkh) @@ -422,6 +461,11 @@ let register () = register1 ~chunked:false S.voting_info (fun ctxt pkh () () -> Delegate.check_registered ctxt pkh >>=? fun () -> Vote.get_delegate_info ctxt pkh) ; + register1 ~chunked:false S.consensus_key (fun ctxt pkh () () -> + Delegate.check_registered ctxt pkh >>=? fun () -> + Delegate.Consensus_key.active_pubkey ctxt pkh >>=? fun pk -> + Delegate.Consensus_key.pending_updates ctxt pkh >>=? fun pendings -> + return (Signature.Public_key.hash pk, pendings)) ; register1 ~chunked:false S.participation (fun ctxt pkh () () -> Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.participation_info ctxt pkh) @@ -470,5 +514,8 @@ let voting_power ctxt block pkh = let voting_info ctxt block pkh = RPC_context.make_call1 S.voting_info ctxt block pkh () () +let consensus_key ctxt block pkh = + RPC_context.make_call1 S.consensus_key ctxt block pkh () () + let participation ctxt block pkh = RPC_context.make_call1 S.participation ctxt block pkh () () diff --git a/src/proto_alpha/lib_protocol/delegate_services.mli b/src/proto_alpha/lib_protocol/delegate_services.mli index 8092f2c368cd..e53ac412ba16 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.mli +++ b/src/proto_alpha/lib_protocol/delegate_services.mli @@ -52,6 +52,8 @@ type info = { deactivated : bool; grace_period : Cycle.t; voting_info : Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } val info_encoding : info Data_encoding.t @@ -125,6 +127,14 @@ val voting_info : public_key_hash -> Vote.delegate_info shell_tzresult Lwt.t +val consensus_key : + 'a #RPC_context.simple -> + 'a -> + Signature.Public_key_hash.t -> + (Signature.Public_key_hash.t * (Cycle.t * Signature.Public_key_hash.t) list) + shell_tzresult + Lwt.t + val participation : 'a #RPC_context.simple -> 'a -> diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index c5f7874c415c..c2af8dd2d839 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -330,6 +330,8 @@ module Delegate = struct deactivated : bool; grace_period : Cycle.t; voting_info : Alpha_context.Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } let info ctxt pkh = Delegate_services.info rpc_ctxt ctxt pkh @@ -352,6 +354,8 @@ module Delegate = struct let voting_info ctxt d = Alpha_services.Delegate.voting_info rpc_ctxt ctxt d + let consensus_key ctxt pkh = Delegate_services.consensus_key rpc_ctxt ctxt pkh + let participation ctxt pkh = Delegate_services.participation rpc_ctxt ctxt pkh end diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index 38f49f94fe3d..9378ee1c0b3e 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -164,6 +164,8 @@ module Delegate : sig deactivated : bool; grace_period : Cycle.t; voting_info : Vote.delegate_info; + active_consensus_key : Signature.Public_key_hash.t; + pending_consensus_keys : (Cycle.t * Signature.Public_key_hash.t) list; } val info : t -> public_key_hash -> Delegate_services.info tzresult Lwt.t @@ -185,6 +187,11 @@ module Delegate : sig val voting_info : t -> public_key_hash -> Vote.delegate_info tzresult Lwt.t + val consensus_key : + t -> + public_key_hash -> + (public_key_hash * (Cycle.t * public_key_hash) list) tzresult Lwt.t + val participation : t -> public_key_hash -> Delegate.participation_info tzresult Lwt.t end diff --git a/tezt/_regressions/rpc/alpha.client.delegates.out b/tezt/_regressions/rpc/alpha.client.delegates.out index 5b87aa0650a6..648f09344be0 100644 --- a/tezt/_regressions/rpc/alpha.client.delegates.out +++ b/tezt/_regressions/rpc/alpha.client.delegates.out @@ -22,7 +22,8 @@ rpc/alpha.client.delegates.out "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } ./tezos-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' "4000000000000" diff --git a/tezt/_regressions/rpc/alpha.light.delegates.out b/tezt/_regressions/rpc/alpha.light.delegates.out index 86f0c849ccff..87ef3f752b0f 100644 --- a/tezt/_regressions/rpc/alpha.light.delegates.out +++ b/tezt/_regressions/rpc/alpha.light.delegates.out @@ -24,7 +24,8 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode light rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' diff --git a/tezt/_regressions/rpc/alpha.proxy.delegates.out b/tezt/_regressions/rpc/alpha.proxy.delegates.out index 172a65ed7b8f..f484c5fe3315 100644 --- a/tezt/_regressions/rpc/alpha.proxy.delegates.out +++ b/tezt/_regressions/rpc/alpha.proxy.delegates.out @@ -24,7 +24,8 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode proxy rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' diff --git a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out index 82f40f30c09c..8bd81a4eb05a 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.delegates.out @@ -22,7 +22,8 @@ rpc/alpha.proxy_server_data_dir.delegates.out "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } ./tezos-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' "4000000000000" diff --git a/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out b/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out index bf6ef936d9ad..f2537562625c 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_rpc.delegates.out @@ -22,7 +22,8 @@ rpc/alpha.proxy_server_rpc.delegates.out "frozen_deposits": "200000000000", "staking_balance": "4000000000000", "delegated_contracts": [ "[PUBLIC_KEY_HASH]" ], "delegated_balance": "0", "deactivated": false, "grace_period": 5, - "voting_power": "4000000000000", "remaining_proposals": 20 } + "voting_power": "4000000000000", "remaining_proposals": 20, + "active_consensus_key": "[PUBLIC_KEY_HASH]" } ./tezos-client rpc get '/chains/main/blocks/head/context/delegates/[PUBLIC_KEY_HASH]/full_balance' "4000000000000" -- GitLab From d03a9a54dc33c442bdf6fe13f2ce067ba036c9f2 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 23/35] Proto/Bootstrap: allow to register a consensus key for bootstrap accounts --- src/lib_store/test/alpha_utils.ml | 8 +- src/proto_alpha/lib_client/mockup.ml | 66 ++++------------ .../lib_parameters/default_parameters.ml | 12 ++- .../lib_parameters/default_parameters.mli | 3 +- .../lib_protocol/alpha_context.mli | 3 + .../lib_protocol/bootstrap_storage.ml | 8 +- .../lib_protocol/parameters_repr.ml | 75 +++++++++++++++++-- .../lib_protocol/parameters_repr.mli | 3 + .../lib_protocol/test/helpers/block.ml | 3 +- .../lib_protocol/test/helpers/context.ml | 5 +- .../test/integration/consensus/test_baking.ml | 2 +- 11 files changed, 121 insertions(+), 67 deletions(-) diff --git a/src/lib_store/test/alpha_utils.ml b/src/lib_store/test/alpha_utils.ml index 6369a58503c4..31a14e77165b 100644 --- a/src/lib_store/test/alpha_utils.ml +++ b/src/lib_store/test/alpha_utils.ml @@ -110,7 +110,13 @@ module Account = struct let account_to_bootstrap ({pkh; pk; _}, amount, delegate_to) = let open Parameters in - ({public_key_hash = pkh; public_key = Some pk; amount; delegate_to} + ({ + public_key_hash = pkh; + public_key = Some pk; + amount; + delegate_to; + consensus_key = None; + } : bootstrap_account) let commitment_secret = diff --git a/src/proto_alpha/lib_client/mockup.ml b/src/proto_alpha/lib_client/mockup.ml index 646a1db0cb79..24ff91f2262e 100644 --- a/src/proto_alpha/lib_client/mockup.ml +++ b/src/proto_alpha/lib_client/mockup.ml @@ -1107,6 +1107,7 @@ module Parsed_account = struct public_key = Some public_key; amount = repr.amount; delegate_to = None; + consensus_key = None; } let default_to_json (cctxt : Tezos_client_base.Client_context.full) : @@ -1158,37 +1159,17 @@ module Bootstrap_account = struct let encoding : Parameters.bootstrap_account Data_encoding.t = let open Data_encoding in let open Parameters in - union - [ - case - ~title:"No delegate" - (Tag 0) - (obj3 - (req "public_key_hash" Signature.Public_key_hash.encoding) - (opt "public_key" Signature.Public_key.encoding) - (req "amount" Tez.encoding)) - (function - | {public_key_hash; public_key; amount; delegate_to = None} -> - Some (public_key_hash, public_key, amount) - | _ -> None) - (fun (public_key_hash, public_key, amount) -> - {public_key_hash; public_key; amount; delegate_to = None}); - case - ~title:"With delegate" - (Tag 1) - (obj4 - (req "public_key_hash" Signature.Public_key_hash.encoding) - (opt "public_key" Signature.Public_key.encoding) - (req "amount" Tez.encoding) - (req "delegate_to" Signature.Public_key_hash.encoding)) - (function - | {public_key_hash; public_key; amount; delegate_to = Some delegate} - -> - Some (public_key_hash, public_key, amount, delegate) - | _ -> None) - (fun (public_key_hash, public_key, amount, delegate) -> - {public_key_hash; public_key; amount; delegate_to = Some delegate}); - ] + conv + (fun {public_key_hash; public_key; amount; delegate_to; consensus_key} -> + (public_key_hash, public_key, amount, delegate_to, consensus_key)) + (fun (public_key_hash, public_key, amount, delegate_to, consensus_key) -> + {public_key_hash; public_key; amount; delegate_to; consensus_key}) + (obj5 + (req "public_key_hash" Signature.Public_key_hash.encoding) + (opt "public_key" Signature.Public_key.encoding) + (req "amount" Tez.encoding) + (opt "delegate_to" Signature.Public_key_hash.encoding) + (opt "consensus_key" Signature.Public_key.encoding)) end module Bootstrap_contract = struct @@ -1245,31 +1226,12 @@ module Protocol_parameters = struct end (* This encoding extends [Protocol_constants_overrides.encoding] to allow - reading json files as produced by lib_parameters. Sadly, this require - copying partially [bootstrap_account_encoding], which is not exposed - in parameters_repr.ml. *) + reading json files as produced by lib_parameters. *) let lib_parameters_json_encoding = - let bootstrap_account_encoding = - let open Data_encoding in - conv - (function - | {Parameters.public_key; amount; _} -> ( - match public_key with - | None -> assert false - | Some pk -> (pk, amount))) - (fun (pk, amount) -> - { - Parameters.public_key = Some pk; - public_key_hash = Signature.Public_key.hash pk; - amount; - delegate_to = None; - }) - (tup2 Signature.Public_key.encoding Tez.encoding) - in Data_encoding.( merge_objs (obj2 - (opt "bootstrap_accounts" (list bootstrap_account_encoding)) + (opt "bootstrap_accounts" (list Parameters.bootstrap_account_encoding)) (opt "commitments" (list Commitment.encoding))) Protocol_constants_overrides.encoding) diff --git a/src/proto_alpha/lib_parameters/default_parameters.ml b/src/proto_alpha/lib_parameters/default_parameters.ml index 9ae091ee04f7..334302db0e33 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.ml +++ b/src/proto_alpha/lib_parameters/default_parameters.ml @@ -296,12 +296,20 @@ let compute_accounts = public_key = Some public_key; amount = bootstrap_balance; delegate_to = None; + consensus_key = None; }) let bootstrap_accounts = compute_accounts bootstrap_accounts_strings -let make_bootstrap_account (pkh, pk, amount, delegate_to) = - Parameters.{public_key_hash = pkh; public_key = Some pk; amount; delegate_to} +let make_bootstrap_account (pkh, pk, amount, delegate_to, consensus_key) = + Parameters. + { + public_key_hash = pkh; + public_key = Some pk; + amount; + delegate_to; + consensus_key; + } let parameters_of_constants ?(bootstrap_accounts = bootstrap_accounts) ?(bootstrap_contracts = []) ?(commitments = []) constants = diff --git a/src/proto_alpha/lib_parameters/default_parameters.mli b/src/proto_alpha/lib_parameters/default_parameters.mli index 8aeb40866526..eff7da674972 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.mli +++ b/src/proto_alpha/lib_parameters/default_parameters.mli @@ -38,7 +38,8 @@ val make_bootstrap_account : Signature.public_key_hash * Signature.public_key * Tez.t - * Signature.public_key_hash option -> + * Signature.public_key_hash option + * Signature.public_key option -> Parameters.bootstrap_account val parameters_of_constants : diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 4586aec9670e..2f325bb00673 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -3474,6 +3474,7 @@ module Parameters : sig public_key : public_key option; amount : Tez.t; delegate_to : public_key_hash option; + consensus_key : public_key option; } type bootstrap_contract = { @@ -3491,6 +3492,8 @@ module Parameters : sig no_reward_cycles : int option; } + val bootstrap_account_encoding : bootstrap_account Data_encoding.t + val encoding : t Data_encoding.t end diff --git a/src/proto_alpha/lib_protocol/bootstrap_storage.ml b/src/proto_alpha/lib_protocol/bootstrap_storage.ml index d37f5c3c699e..af0f71358bb6 100644 --- a/src/proto_alpha/lib_protocol/bootstrap_storage.ml +++ b/src/proto_alpha/lib_protocol/bootstrap_storage.ml @@ -24,7 +24,7 @@ (*****************************************************************************) let init_account (ctxt, balance_updates) - ({public_key_hash; public_key; amount; delegate_to} : + ({public_key_hash; public_key; amount; delegate_to; consensus_key} : Parameters_repr.bootstrap_account) = let contract = Contract_repr.Implicit public_key_hash in Token.transfer @@ -45,6 +45,12 @@ let init_account (ctxt, balance_updates) ctxt contract (Some (Option.value ~default:public_key_hash delegate_to)) + >>=? fun ctxt -> + (match consensus_key with + | None -> return ctxt + | Some consensus_key -> + Delegate_consensus_key.init ctxt public_key_hash consensus_key) + >>=? fun ctxt -> return ctxt | None -> return ctxt) >|=? fun ctxt -> (ctxt, new_balance_updates @ balance_updates) diff --git a/src/proto_alpha/lib_protocol/parameters_repr.ml b/src/proto_alpha/lib_protocol/parameters_repr.ml index 3ab238b23d48..13604ffdc600 100644 --- a/src/proto_alpha/lib_protocol/parameters_repr.ml +++ b/src/proto_alpha/lib_protocol/parameters_repr.ml @@ -29,6 +29,7 @@ type bootstrap_account = { public_key : Signature.Public_key.t option; amount : Tez_repr.t; delegate_to : Signature.Public_key_hash.t option; + consensus_key : Signature.Public_key.t option; } type bootstrap_contract = { @@ -60,30 +61,50 @@ let bootstrap_account_encoding = public_key = Some public_key; amount; delegate_to = None; + consensus_key = None; } -> assert ( Signature.Public_key_hash.equal (Signature.Public_key.hash public_key) public_key_hash) ; Some (public_key, amount) - | {public_key = None; _} | {delegate_to = Some _; _} -> None) + | {public_key = None; _} + | {delegate_to = Some _; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key, amount) -> { public_key = Some public_key; public_key_hash = Signature.Public_key.hash public_key; amount; delegate_to = None; + consensus_key = None; }); case (Tag 1) ~title:"Public_key_unknown" (tup2 Signature.Public_key_hash.encoding Tez_repr.encoding) (function - | {public_key_hash; public_key = None; amount; delegate_to = None} -> + | { + public_key_hash; + public_key = None; + amount; + delegate_to = None; + consensus_key = None; + } -> Some (public_key_hash, amount) - | {public_key = Some _; _} | {delegate_to = Some _; _} -> None) + | {public_key = Some _; _} + | {delegate_to = Some _; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key_hash, amount) -> - {public_key = None; public_key_hash; amount; delegate_to = None}); + { + public_key = None; + public_key_hash; + amount; + delegate_to = None; + consensus_key = None; + }); case (Tag 2) ~title:"Public_key_known_with_delegate" @@ -97,19 +118,24 @@ let bootstrap_account_encoding = public_key = Some public_key; amount; delegate_to = Some delegate; + consensus_key = None; } -> assert ( Signature.Public_key_hash.equal (Signature.Public_key.hash public_key) public_key_hash) ; Some (public_key, amount, delegate) - | {public_key = None; _} | {delegate_to = None; _} -> None) + | {public_key = None; _} + | {delegate_to = None; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key, amount, delegate) -> { public_key = Some public_key; public_key_hash = Signature.Public_key.hash public_key; amount; delegate_to = Some delegate; + consensus_key = None; }); case (Tag 3) @@ -124,15 +150,52 @@ let bootstrap_account_encoding = public_key = None; amount; delegate_to = Some delegate; + consensus_key = None; } -> Some (public_key_hash, amount, delegate) - | {public_key = Some _; _} | {delegate_to = None; _} -> None) + | {public_key = Some _; _} + | {delegate_to = None; _} + | {consensus_key = Some _; _} -> + None) (fun (public_key_hash, amount, delegate) -> { public_key = None; public_key_hash; amount; delegate_to = Some delegate; + consensus_key = None; + }); + case + (Tag 4) + ~title:"Public_key_known_with_consensus_key" + (tup3 + Signature.Public_key.encoding + Tez_repr.encoding + Signature.Public_key.encoding) + (function + | { + public_key_hash; + public_key = Some public_key; + amount; + delegate_to = None; + consensus_key = Some consensus_key; + } -> + assert ( + Signature.Public_key_hash.equal + (Signature.Public_key.hash public_key) + public_key_hash) ; + Some (public_key, amount, consensus_key) + | {public_key = None; _} + | {delegate_to = Some _; _} + | {consensus_key = None; _} -> + None) + (fun (public_key, amount, consensus_key) -> + { + public_key = Some public_key; + public_key_hash = Signature.Public_key.hash public_key; + amount; + delegate_to = None; + consensus_key = Some consensus_key; }); ] diff --git a/src/proto_alpha/lib_protocol/parameters_repr.mli b/src/proto_alpha/lib_protocol/parameters_repr.mli index 844e54ba3753..97bad09f1ccb 100644 --- a/src/proto_alpha/lib_protocol/parameters_repr.mli +++ b/src/proto_alpha/lib_protocol/parameters_repr.mli @@ -33,6 +33,7 @@ type bootstrap_account = { public_key : Signature.Public_key.t option; amount : Tez_repr.t; delegate_to : Signature.Public_key_hash.t option; + consensus_key : Signature.Public_key.t option; } (** An originated contract initially existing on a chain since genesis. *) @@ -53,6 +54,8 @@ type t = { no_reward_cycles : int option; } +val bootstrap_account_encoding : bootstrap_account Data_encoding.t + val encoding : t Data_encoding.t val check_params : t -> unit tzresult diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 21b2a26c38b0..87c8782f4519 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -276,7 +276,8 @@ let prepare_main_init_params ?bootstrap_contracts commitments constants let bootstrap_accounts = List.map (fun (Account.{pk; pkh; _}, amount, delegate_to) -> - Default_parameters.make_bootstrap_account (pkh, pk, amount, delegate_to)) + Default_parameters.make_bootstrap_account + (pkh, pk, amount, delegate_to, None)) initial_accounts in let parameters = diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.ml b/src/proto_alpha/lib_protocol/test/helpers/context.ml index c2af8dd2d839..b99d8cf04053 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/context.ml @@ -460,7 +460,7 @@ let init_with_constants_gen tup constants = List.map (fun (acc, tez, delegate_to) -> Default_parameters.make_bootstrap_account - (acc.Account.pkh, acc.Account.pk, tez, delegate_to)) + (acc.Account.pkh, acc.Account.pk, tez, delegate_to, None)) accounts in let parameters = @@ -483,7 +483,8 @@ let default_raw_context () = let bootstrap_accounts = List.map (fun (Account.{pk; pkh; _}, amount, delegate_to) -> - Default_parameters.make_bootstrap_account (pkh, pk, amount, delegate_to)) + Default_parameters.make_bootstrap_account + (pkh, pk, amount, delegate_to, None)) initial_accounts in Block.prepare_initial_context_params initial_accounts diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml index 6a6bd357fc3c..f7179edab1b3 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_baking.ml @@ -344,7 +344,7 @@ let test_committee_sampling () = List.map (fun (acc, tez, delegate_to) -> Default_parameters.make_bootstrap_account - (acc.Account.pkh, acc.Account.pk, tez, delegate_to)) + (acc.Account.pkh, acc.Account.pk, tez, delegate_to, None)) accounts in let consensus_committee_size = max_round in -- GitLab From b7b31721a1cb7acdf8eaf32ea0b4726175ca451e Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 24/35] Proto/Delegate: Capture the consensus key in snapshot --- scripts/yes-wallet/yes_wallet_lib.ml | 7 +- .../lib_client/operation_result.ml | 15 +- src/proto_alpha/lib_delegate/baking_state.ml | 2 +- .../client_baking_denunciation.ml | 8 +- .../test/tenderbrute/lib/tenderbrute.ml | 5 +- src/proto_alpha/lib_plugin/plugin.ml | 258 ++++++++++++------ src/proto_alpha/lib_protocol/alpha_context.ml | 1 + .../lib_protocol/alpha_context.mli | 30 +- src/proto_alpha/lib_protocol/apply.ml | 133 +++++---- src/proto_alpha/lib_protocol/apply.mli | 18 +- src/proto_alpha/lib_protocol/apply_results.ml | 57 ++-- .../lib_protocol/apply_results.mli | 10 +- src/proto_alpha/lib_protocol/baking.ml | 42 ++- src/proto_alpha/lib_protocol/baking.mli | 12 +- .../lib_protocol/delegate_consensus_key.ml | 55 +++- .../lib_protocol/delegate_consensus_key.mli | 29 +- .../lib_protocol/delegate_cycles.ml | 28 ++ .../lib_protocol/delegate_cycles.mli | 4 + .../lib_protocol/delegate_sampler.ml | 27 +- .../lib_protocol/delegate_sampler.mli | 14 +- .../lib_protocol/delegate_services.ml | 4 +- .../lib_protocol/delegate_storage.ml | 15 - .../lib_protocol/delegate_storage.mli | 4 - src/proto_alpha/lib_protocol/init_storage.ml | 2 +- src/proto_alpha/lib_protocol/level_repr.ml | 9 + src/proto_alpha/lib_protocol/level_repr.mli | 2 + src/proto_alpha/lib_protocol/main.ml | 26 +- src/proto_alpha/lib_protocol/main.mli | 12 +- src/proto_alpha/lib_protocol/raw_context.ml | 70 +++-- src/proto_alpha/lib_protocol/raw_context.mli | 48 ++-- src/proto_alpha/lib_protocol/storage.ml | 35 ++- src/proto_alpha/lib_protocol/storage.mli | 12 +- .../lib_protocol/test/helpers/block.ml | 12 +- .../test/unit/test_consensus_key.ml | 135 ++++++--- tezt/_regressions/rpc/alpha.client.others.out | 30 +- tezt/_regressions/rpc/alpha.light.others.out | 30 +- tezt/_regressions/rpc/alpha.proxy.others.out | 30 +- .../alpha.proxy_server_data_dir.others.out | 30 +- .../rpc/alpha.proxy_server_rpc.others.out | 30 +- 39 files changed, 870 insertions(+), 421 deletions(-) diff --git a/scripts/yes-wallet/yes_wallet_lib.ml b/scripts/yes-wallet/yes_wallet_lib.ml index 9c3359aa2f0c..fc2e61059980 100644 --- a/scripts/yes-wallet/yes_wallet_lib.ml +++ b/scripts/yes-wallet/yes_wallet_lib.ml @@ -449,7 +449,12 @@ let get_delegates (proto : protocol) context ~init:(Ok ([], Alpha_context.Tez.zero)) ~f:(fun pkh acc -> let* pk = - let*! r = Alpha_context.Delegate.manager_pubkey ctxt pkh in + let*! r = + let* pk = + Alpha_context.Delegate.Consensus_key.active_pubkey ctxt pkh + in + return pk.consensus_pk + in Lwt.return @@ Environment.wrap_tzresult r in let*? key_list_acc, staking_balance_acc = acc in diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index ad2d57872ff3..5da8026f2140 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -1066,8 +1066,8 @@ let rec pp_contents_and_result_list : bus | Single_and_result ( Preendorsement {level; _}, - Preendorsement_result {balance_updates; delegate; preendorsement_power} - ) -> + Preendorsement_result + {balance_updates; delegate; consensus_key; preendorsement_power} ) -> Format.fprintf ppf "@[Preendorsement:@,\ @@ -1079,12 +1079,13 @@ let rec pp_contents_and_result_list : level pp_balance_updates balance_updates - Signature.Public_key_hash.pp - delegate + Consensus_key.pp + {delegate; consensus_pkh = consensus_key} preendorsement_power | Single_and_result ( Endorsement {level; _}, - Endorsement_result {balance_updates; delegate; endorsement_power} ) -> + Endorsement_result + {balance_updates; delegate; consensus_key; endorsement_power} ) -> Format.fprintf ppf "@[Endorsement:@,\ @@ -1096,8 +1097,8 @@ let rec pp_contents_and_result_list : level pp_balance_updates balance_updates - Signature.Public_key_hash.pp - delegate + Consensus_key.pp + {delegate; consensus_pkh = consensus_key} endorsement_power | Single_and_result ( Double_endorsement_evidence {op1; op2}, diff --git a/src/proto_alpha/lib_delegate/baking_state.ml b/src/proto_alpha/lib_delegate/baking_state.ml index c7370341ff79..3753157f3395 100644 --- a/src/proto_alpha/lib_delegate/baking_state.ml +++ b/src/proto_alpha/lib_delegate/baking_state.ml @@ -605,7 +605,7 @@ let compute_delegate_slots (cctxt : Protocol_client_context.full) let own_delegate_slots, all_delegate_slots = List.fold_left (fun (own_map, all_map) slot -> - let {Plugin.RPC.Validators.delegate; slots; _} = slot in + let {Plugin.RPC.Validators.consensus_key = delegate; slots; _} = slot in let endorsing_slot = {endorsing_power = List.length slots; delegate; slots} in diff --git a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml index 4f3638131307..2ebc4af5961e 100644 --- a/src/proto_alpha/lib_delegate/client_baking_denunciation.ml +++ b/src/proto_alpha/lib_delegate/client_baking_denunciation.ml @@ -264,13 +264,13 @@ let process_block (cctxt : #Protocol_client_context.full) state Option.value ~default:Delegate_Map.empty @@ HLevel.find state.blocks_table (chain_id, level, round) in - match Delegate_Map.find baker map with + match Delegate_Map.find baker.delegate map with | None -> return @@ HLevel.add state.blocks_table (chain_id, level, round) - (Delegate_Map.add baker new_hash map) + (Delegate_Map.add baker.delegate new_hash map) | Some existing_hash when Block_hash.(existing_hash = new_hash) -> (* This case should never happen *) Events.(emit double_baking_but_not) () >>= fun () -> @@ -278,7 +278,7 @@ let process_block (cctxt : #Protocol_client_context.full) state @@ HLevel.replace state.blocks_table (chain_id, level, round) - (Delegate_Map.add baker new_hash map) + (Delegate_Map.add baker.delegate new_hash map) | Some existing_hash -> (* If a previous block made by this pkh is found for the same (level, round) we inject a double_baking_evidence *) @@ -310,7 +310,7 @@ let process_block (cctxt : #Protocol_client_context.full) state @@ HLevel.replace state.blocks_table (chain_id, level, round) - (Delegate_Map.add baker new_hash map)) + (Delegate_Map.add baker.delegate new_hash map)) (* Remove levels that are lower than the [highest_level_encountered] minus [preserved_levels] *) diff --git a/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml b/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml index fa99d708a0a1..ce3f8bba6380 100644 --- a/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml +++ b/src/proto_alpha/lib_delegate/test/tenderbrute/lib/tenderbrute.ml @@ -102,8 +102,9 @@ let check ctxt ~selection = (fun (level, round) delegate ctxt -> Delegate_sampler.baking_rights_owner ctxt level ~round >|= Environment.wrap_tzresult - >>=? fun (ctxt, _, (_, pkh)) -> - if not (Signature.Public_key_hash.equal delegate pkh) then raise Exit + >>=? fun (ctxt, _, pk) -> + if not (Signature.Public_key_hash.equal delegate pk.delegate) then + raise Exit else return ctxt) selection ctxt diff --git a/src/proto_alpha/lib_plugin/plugin.ml b/src/proto_alpha/lib_plugin/plugin.ml index a4e87f1dd557..19c540d81296 100644 --- a/src/proto_alpha/lib_plugin/plugin.ml +++ b/src/proto_alpha/lib_plugin/plugin.ml @@ -2360,7 +2360,7 @@ module RPC = struct let operation : _ operation = {shell; protocol_data} in let hash = Operation.hash {shell; protocol_data} in let ctxt = Origination_nonce.init ctxt hash in - let payload_producer = Signature.Public_key_hash.zero in + let payload_producer = Consensus_key.zero in match protocol_data.contents with | Single (Manager_operation _) as op -> Apply.precheck_manager_contents_list ctxt op ~mempool_mode:true @@ -3983,7 +3983,8 @@ module RPC = struct module Baking_rights = struct type t = { level : Raw_level.t; - delegate : Signature.Public_key_hash.t; + delegate : public_key_hash; + consensus_key : public_key_hash; round : int; timestamp : Timestamp.t option; } @@ -3991,15 +3992,16 @@ module RPC = struct let encoding = let open Data_encoding in conv - (fun {level; delegate; round; timestamp} -> - (level, delegate, round, timestamp)) - (fun (level, delegate, round, timestamp) -> - {level; delegate; round; timestamp}) - (obj4 + (fun {level; delegate; consensus_key; round; timestamp} -> + (level, delegate, round, timestamp, consensus_key)) + (fun (level, delegate, round, timestamp, consensus_key) -> + {level; delegate; consensus_key; round; timestamp}) + (obj5 (req "level" Raw_level.encoding) (req "delegate" Signature.Public_key_hash.encoding) (req "round" uint16) - (opt "estimated_time" Timestamp.encoding)) + (opt "estimated_time" Timestamp.encoding) + (req "consensus_key" Signature.Public_key_hash.encoding)) let default_max_round = 64 @@ -4012,18 +4014,23 @@ module RPC = struct levels : Raw_level.t list; cycle : Cycle.t option; delegates : Signature.Public_key_hash.t list; + consensus_keys : Signature.Public_key_hash.t list; max_round : int option; all : bool; } let baking_rights_query = let open RPC_query in - query (fun levels cycle delegates max_round all -> - {levels; cycle; delegates; max_round; all}) + query (fun levels cycle delegates consensus_keys max_round all -> + {levels; cycle; delegates; consensus_keys; max_round; all}) |+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels) |+ opt_field "cycle" Cycle.rpc_arg (fun t -> t.cycle) |+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t -> t.delegates) + |+ multi_field + "consensus_key" + Signature.Public_key_hash.rpc_arg + (fun t -> t.consensus_keys) |+ opt_field "max_round" RPC_arg.uint (fun t -> t.max_round) |+ flag "all" (fun t -> t.all) |> seal @@ -4040,9 +4047,10 @@ module RPC = struct (valid) level(s) in the past or future at which the baking \ rights have to be returned.\n\ Parameter `delegate` can be used to restrict the results to \ - the given delegates. If parameter `all` is set, all the baking \ - opportunities for each baker at each level are returned, \ - instead of just the first one.\n\ + the given delegates. Parameter `consensus_key` can be used to \ + restrict the results to the given consensus_keys. If parameter \ + `all` is set, all the baking opportunities for each baker at \ + each level are returned, instead of just the first one.\n\ Returns the list of baking opportunities up to round %d. Also \ returns the minimal timestamps that correspond to these \ opportunities. The timestamps are omitted for levels in the \ @@ -4065,8 +4073,9 @@ module RPC = struct let rec loop l acc round = if Compare.Int.(round > max_round) then return (List.rev acc) else - let (Misc.LCons (pk, next)) = l in - let delegate = Signature.Public_key.hash pk in + let (Misc.LCons ({Consensus_key.consensus_pkh; delegate}, next)) = + l + in estimated_time round_durations ~current_level @@ -4075,7 +4084,16 @@ module RPC = struct ~level ~round >>?= fun timestamp -> - let acc = {level = level.level; delegate; round; timestamp} :: acc in + let acc = + { + level = level.level; + delegate; + consensus_key = consensus_pkh; + round; + timestamp; + } + :: acc + in next () >>=? fun l -> loop l acc (round + 1) in loop delegates [] 0 @@ -4120,29 +4138,44 @@ module RPC = struct if q.all then List.concat rights else List.concat_map remove_duplicated_delegates rights in - match q.delegates with - | [] -> rights - | _ :: _ as delegates -> - let is_requested p = - List.exists - (Signature.Public_key_hash.equal p.delegate) - delegates - in - List.filter is_requested rights) + let rights = + match q.delegates with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.delegate) + delegates + in + List.filter is_requested rights + in + let rights = + match q.consensus_keys with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.consensus_key) + delegates + in + List.filter is_requested rights + in + rights) - let get ctxt ?(levels = []) ?cycle ?(delegates = []) ?(all = false) - ?max_round block = + let get ctxt ?(levels = []) ?cycle ?(delegates = []) ?(consensus_keys = []) + ?(all = false) ?max_round block = RPC_context.make_call0 S.baking_rights ctxt block - {levels; cycle; delegates; max_round; all} + {levels; cycle; delegates; consensus_keys; max_round; all} () end module Endorsing_rights = struct type delegate_rights = { delegate : Signature.Public_key_hash.t; + consensus_key : Signature.Public_key_hash.t; first_slot : Slot.t; endorsing_power : int; } @@ -4156,14 +4189,15 @@ module RPC = struct let delegate_rights_encoding = let open Data_encoding in conv - (fun {delegate; first_slot; endorsing_power} -> - (delegate, first_slot, endorsing_power)) - (fun (delegate, first_slot, endorsing_power) -> - {delegate; first_slot; endorsing_power}) - (obj3 + (fun {delegate; consensus_key; first_slot; endorsing_power} -> + (delegate, first_slot, endorsing_power, consensus_key)) + (fun (delegate, first_slot, endorsing_power, consensus_key) -> + {delegate; first_slot; endorsing_power; consensus_key}) + (obj4 (req "delegate" Signature.Public_key_hash.encoding) (req "first_slot" Slot.encoding) - (req "endorsing_power" uint16)) + (req "endorsing_power" uint16) + (req "consensus_key" Signature.Public_key_hash.encoding)) let encoding = let open Data_encoding in @@ -4186,15 +4220,21 @@ module RPC = struct levels : Raw_level.t list; cycle : Cycle.t option; delegates : Signature.Public_key_hash.t list; + consensus_keys : Signature.Public_key_hash.t list; } let endorsing_rights_query = let open RPC_query in - query (fun levels cycle delegates -> {levels; cycle; delegates}) + query (fun levels cycle delegates consensus_keys -> + {levels; cycle; delegates; consensus_keys}) |+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels) |+ opt_field "cycle" Cycle.rpc_arg (fun t -> t.cycle) |+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t -> t.delegates) + |+ multi_field + "consensus_key" + Signature.Public_key_hash.rpc_arg + (fun t -> t.consensus_keys) |> seal let endorsing_rights = @@ -4210,6 +4250,8 @@ module RPC = struct deprecated because in this case the RPC takes a long time and \ returns an unreasonable amount of data. Parameter `delegate` can \ be used to restrict the results to the given delegates.\n\ + Parameter `consensus_key` can be used to restrict the results to \ + the given consensus_keys. \n\ Returns the smallest endorsing slots and the endorsing power. \ Also returns the minimal timestamp that corresponds to endorsing \ at the given level. The timestamps are omitted for levels in the \ @@ -4238,8 +4280,15 @@ module RPC = struct >>?= fun estimated_time -> let rights = Slot.Map.fold - (fun first_slot (_pk, delegate, endorsing_power) acc -> - {delegate; first_slot; endorsing_power} :: acc) + (fun first_slot + ( { + Consensus_key.delegate; + consensus_pk = _; + consensus_pkh = consensus_key; + }, + endorsing_power ) + acc -> + {delegate; consensus_key; first_slot; endorsing_power} :: acc) rights [] in @@ -4259,49 +4308,76 @@ module RPC = struct in List.map_es (endorsing_rights_at_level ctxt) levels >|=? fun rights_per_level -> - match q.delegates with - | [] -> rights_per_level - | _ :: _ as delegates -> - List.filter_map - (fun rights_at_level -> - let is_requested p = - List.exists - (Signature.Public_key_hash.equal p.delegate) - delegates - in - match - List.filter is_requested rights_at_level.delegates_rights - with - | [] -> None - | delegates_rights -> - Some {rights_at_level with delegates_rights}) - rights_per_level) - - let get ctxt ?(levels = []) ?cycle ?(delegates = []) block = + let rights_per_level = + match q.delegates with + | [] -> rights_per_level + | _ :: _ as delegates -> + List.filter_map + (fun rights_at_level -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.delegate) + delegates + in + match + List.filter is_requested rights_at_level.delegates_rights + with + | [] -> None + | delegates_rights -> + Some {rights_at_level with delegates_rights}) + rights_per_level + in + let rights_per_level = + match q.consensus_keys with + | [] -> rights_per_level + | _ :: _ as consensus_keys -> + List.filter_map + (fun rights_at_level -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.consensus_key) + consensus_keys + in + match + List.filter is_requested rights_at_level.delegates_rights + with + | [] -> None + | delegates_rights -> + Some {rights_at_level with delegates_rights}) + rights_per_level + in + rights_per_level) + + let get ctxt ?(levels = []) ?cycle ?(delegates = []) ?(consensus_keys = []) + block = RPC_context.make_call0 S.endorsing_rights ctxt block - {levels; cycle; delegates} + {levels; cycle; delegates; consensus_keys} () end module Validators = struct type t = { level : Raw_level.t; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; slots : Slot.t list; } let encoding = let open Data_encoding in conv - (fun {level; delegate; slots} -> (level, delegate, slots)) - (fun (level, delegate, slots) -> {level; delegate; slots}) - (obj3 + (fun {level; delegate; consensus_key; slots} -> + (level, delegate, slots, consensus_key)) + (fun (level, delegate, slots, consensus_key) -> + {level; delegate; consensus_key; slots}) + (obj4 (req "level" Raw_level.encoding) (req "delegate" Signature.Public_key_hash.encoding) - (req "slots" (list Slot.encoding))) + (req "slots" (list Slot.encoding)) + (req "consensus_key" Signature.Public_key_hash.encoding)) module S = struct open Data_encoding @@ -4311,14 +4387,20 @@ module RPC = struct type validators_query = { levels : Raw_level.t list; delegates : Signature.Public_key_hash.t list; + consensus_keys : Signature.Public_key_hash.t list; } let validators_query = let open RPC_query in - query (fun levels delegates -> {levels; delegates}) + query (fun levels delegates consensus_keys -> + {levels; delegates; consensus_keys}) |+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels) |+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t -> t.delegates) + |+ multi_field + "consensus_key" + Signature.Public_key_hash.rpc_arg + (fun t -> t.consensus_keys) |> seal let validators = @@ -4330,7 +4412,8 @@ module RPC = struct Parameter `level` can be used to specify the (valid) level(s) in \ the past or future at which the endorsement rights have to be \ returned. Parameter `delegate` can be used to restrict the \ - results to the given delegates.\n" + results to the given delegates. Parameter `consensus_key` can be \ + used to restrict the results to the given consensus_keys.\n" ~query:validators_query ~output:(list encoding) path @@ -4339,9 +4422,9 @@ module RPC = struct let endorsing_slots_at_level ctxt level = Baking.endorsing_rights ctxt level >|=? fun (_, rights) -> Signature.Public_key_hash.Map.fold - (fun delegate slots acc -> - {level = level.level; delegate; slots} :: acc) - (rights :> Slot.t list Signature.Public_key_hash.Map.t) + (fun _pkh {Baking.delegate; consensus_key; slots} acc -> + {level = level.level; delegate; consensus_key; slots} :: acc) + rights [] let register () = @@ -4355,18 +4438,37 @@ module RPC = struct in List.concat_map_es (endorsing_slots_at_level ctxt) levels >|=? fun rights -> - match q.delegates with - | [] -> rights - | _ :: _ as delegates -> - let is_requested p = - List.exists - (Signature.Public_key_hash.equal p.delegate) - delegates - in - List.filter is_requested rights) + let rights = + match q.delegates with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.delegate) + delegates + in + List.filter is_requested rights + in + let rights = + match q.consensus_keys with + | [] -> rights + | _ :: _ as delegates -> + let is_requested p = + List.exists + (Signature.Public_key_hash.equal p.consensus_key) + delegates + in + List.filter is_requested rights + in + rights) - let get ctxt ?(levels = []) ?(delegates = []) block = - RPC_context.make_call0 S.validators ctxt block {levels; delegates} () + let get ctxt ?(levels = []) ?(delegates = []) ?(consensus_keys = []) block = + RPC_context.make_call0 + S.validators + ctxt + block + {levels; delegates; consensus_keys} + () end module S = struct diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 242c1d1fe8c9..2292460c7489 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -401,6 +401,7 @@ module Bond_id = struct end module Receipt = Receipt_repr +module Consensus_key = Delegate_consensus_key module Delegate = struct include Delegate_storage diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 2f325bb00673..a454c626e0b7 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2183,6 +2183,25 @@ module Receipt : sig val group_balance_updates : balance_updates -> balance_updates tzresult end +module Consensus_key : sig + type pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; + } + + type t = { + delegate : Signature.Public_key_hash.t; + consensus_pkh : Signature.Public_key_hash.t; + } + + val zero : t + + val pp : Format.formatter -> t -> unit + + val pkh : pk -> t +end + module Delegate : sig val frozen_deposits_limit : context -> public_key_hash -> Tez.t option tzresult Lwt.t @@ -2272,7 +2291,8 @@ module Delegate : sig Signature.Public_key.t tzresult Lwt.t module Consensus_key : sig - val active_pubkey : context -> public_key_hash -> public_key tzresult Lwt.t + val active_pubkey : + context -> public_key_hash -> Consensus_key.pk tzresult Lwt.t val pending_updates : context -> @@ -3389,13 +3409,10 @@ module Stake_distribution : sig context -> Level.t -> round:Round.t -> - (context * Slot.t * (public_key * public_key_hash)) tzresult Lwt.t + (context * Slot.t * Consensus_key.pk) tzresult Lwt.t val slot_owner : - context -> - Level.t -> - Slot.t -> - (context * (public_key * public_key_hash)) tzresult Lwt.t + context -> Level.t -> Slot.t -> (context * Consensus_key.pk) tzresult Lwt.t val get_staking_balance : context -> public_key_hash -> Tez.t tzresult Lwt.t end @@ -3562,6 +3579,7 @@ module Consensus : sig and type 'a slot_map := 'a Slot.Map.t and type slot_set := Slot.Set.t and type round := Round.t + and type consensus_pk := Consensus_key.pk val store_endorsement_branch : context -> Block_hash.t * Block_payload_hash.t -> context Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 0511dc1c0c36..aa380ccf8883 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2372,7 +2372,7 @@ let skipped_operation_result : let rec mark_skipped : type kind. - payload_producer:Signature.Public_key_hash.t -> + payload_producer:Consensus_key.t -> Level.t -> kind Kind.manager prechecked_contents_list -> kind Kind.manager contents_result_list = @@ -2505,7 +2505,7 @@ let rec apply_manager_contents_list_rec : type kind. context -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> Chain_id.t -> kind Kind.manager prechecked_contents_list -> (success_or_failure * kind Kind.manager contents_result_list) Lwt.t = @@ -2824,7 +2824,7 @@ let validate_consensus_contents (type kind) ctxt chain_id (operation_kind : kind consensus_operation_type) (operation : kind operation) (apply_mode : apply_mode) (content : consensus_content) : - (context * public_key_hash * int) tzresult Lwt.t = + (context * Consensus_key.t * int) tzresult Lwt.t = let current_level = Level.current ctxt in let proposal_level = get_predecessor_level apply_mode in let slot_map = @@ -2850,14 +2850,15 @@ let validate_consensus_contents (type kind) ctxt chain_id >>?= fun () -> match Slot.Map.find content.slot slot_map with | None -> fail Wrong_slot_used_for_consensus_operation - | Some (delegate_pk, delegate_pkh, voting_power) -> - Delegate.frozen_deposits ctxt delegate_pkh >>=? fun frozen_deposits -> + | Some (consensus_pk, voting_power) -> + Delegate.frozen_deposits ctxt consensus_pk.delegate + >>=? fun frozen_deposits -> fail_unless Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits delegate_pkh) + (Zero_frozen_deposits consensus_pk.delegate) >>=? fun () -> - Operation.check_signature delegate_pk chain_id operation >>?= fun () -> - return (ctxt, delegate_pkh, voting_power) + Operation.check_signature consensus_pk.consensus_pk chain_id operation + >>?= fun () -> return (ctxt, Consensus_key.pkh consensus_pk, voting_power) let apply_manager_contents_list ctxt mode ~payload_producer chain_id prechecked_contents_list = @@ -2900,7 +2901,7 @@ let punish_delegate ctxt delegate level mistake mk_result ~payload_producer = Token.transfer ctxt `Double_signing_evidence_rewards - (`Contract (Contract.Implicit payload_producer)) + (`Contract (Contract.Implicit payload_producer.Consensus_key.delegate)) reward | Error _ -> (* reward is Tez.zero *) return (ctxt, [])) >|=? fun (ctxt, reward_balance_updates) -> @@ -2944,19 +2945,27 @@ let punish_double_endorsement_or_preendorsement (type kind) ctxt ~chain_id let level = Level.from_raw ctxt e1.level in check_denunciation_age ctxt kind level.level >>=? fun () -> Stake_distribution.slot_owner ctxt level e1.slot - >>=? fun (ctxt, (delegate1_pk, delegate1)) -> + >>=? fun (ctxt, consensus_pk1) -> Stake_distribution.slot_owner ctxt level e2.slot - >>=? fun (ctxt, (_delegate2_pk, delegate2)) -> + >>=? fun (ctxt, consensus_pk2) -> fail_unless - (Signature.Public_key_hash.equal delegate1 delegate2) - (Inconsistent_denunciation {kind; delegate1; delegate2}) + (Signature.Public_key_hash.equal + consensus_pk1.delegate + consensus_pk2.delegate) + (Inconsistent_denunciation + { + kind; + delegate1 = consensus_pk1.delegate; + delegate2 = consensus_pk2.delegate; + }) >>=? fun () -> - let delegate_pk, delegate = (delegate1_pk, delegate1) in - Operation.check_signature delegate_pk chain_id op1 >>?= fun () -> - Operation.check_signature delegate_pk chain_id op2 >>?= fun () -> + Operation.check_signature consensus_pk1.consensus_pk chain_id op1 + >>?= fun () -> + Operation.check_signature consensus_pk2.consensus_pk chain_id op2 + >>?= fun () -> punish_delegate ctxt - delegate + consensus_pk1.delegate level `Double_endorsing mk_result @@ -2986,20 +2995,26 @@ let punish_double_baking ctxt chain_id bh1 bh2 ~payload_producer = let committee_size = Constants.consensus_committee_size ctxt in Round.to_slot round1 ~committee_size >>?= fun slot1 -> Stake_distribution.slot_owner ctxt level slot1 - >>=? fun (ctxt, (delegate1_pk, delegate1)) -> + >>=? fun (ctxt, consensus_pk1) -> Round.to_slot round2 ~committee_size >>?= fun slot2 -> Stake_distribution.slot_owner ctxt level slot2 - >>=? fun (ctxt, (_delegate2_pk, delegate2)) -> + >>=? fun (ctxt, consensus_pk2) -> fail_unless - Signature.Public_key_hash.(delegate1 = delegate2) - (Inconsistent_denunciation {kind = Block; delegate1; delegate2}) + Signature.Public_key_hash.(consensus_pk1.delegate = consensus_pk2.delegate) + (Inconsistent_denunciation + { + kind = Block; + delegate1 = consensus_pk1.delegate; + delegate2 = consensus_pk2.delegate; + }) >>=? fun () -> - let delegate_pk, delegate = (delegate1_pk, delegate1) in - Block_header.check_signature bh1 chain_id delegate_pk >>?= fun () -> - Block_header.check_signature bh2 chain_id delegate_pk >>?= fun () -> + Block_header.check_signature bh1 chain_id consensus_pk1.consensus_pk + >>?= fun () -> + Block_header.check_signature bh2 chain_id consensus_pk2.consensus_pk + >>?= fun () -> punish_delegate ctxt - delegate + consensus_pk1.delegate level `Double_baking ~payload_producer @@ -3026,16 +3041,19 @@ let validate_grand_parent_endorsement ctxt chain_id | Single (Endorsement e) -> let level = Level.from_raw ctxt e.level in Stake_distribution.slot_owner ctxt level e.slot - >>=? fun (ctxt, (delegate_pk, pkh)) -> - Operation.check_signature delegate_pk chain_id op >>?= fun () -> - Consensus.record_grand_parent_endorsement ctxt pkh >>?= fun ctxt -> + >>=? fun (ctxt, consensus_pk) -> + Operation.check_signature consensus_pk.consensus_pk chain_id op + >>?= fun () -> + Consensus.record_grand_parent_endorsement ctxt consensus_pk.delegate + >>?= fun ctxt -> return ( ctxt, Single_result (Endorsement_result { balance_updates = []; - delegate = pkh; + delegate = consensus_pk.delegate; + consensus_key = consensus_pk.consensus_pkh; endorsement_power = 0 (* dummy endorsement power: this will never be used *); }) ) @@ -3058,7 +3076,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode operation apply_mode consensus_content - >>=? fun (ctxt, delegate, voting_power) -> + >>=? fun (ctxt, {delegate; consensus_pkh}, voting_power) -> Consensus.record_preendorsement ctxt ~initial_slot:consensus_content.slot @@ -3072,6 +3090,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode { balance_updates = []; delegate; + consensus_key = consensus_pkh; preendorsement_power = voting_power; }) ) | Single (Endorsement consensus_content) -> ( @@ -3093,7 +3112,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode operation apply_mode consensus_content - >>=? fun (ctxt, delegate, voting_power) -> + >>=? fun (ctxt, {delegate; consensus_pkh}, voting_power) -> Consensus.record_endorsement ctxt ~initial_slot:consensus_content.slot @@ -3106,13 +3125,16 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode { balance_updates = []; delegate; + consensus_key = consensus_pkh; endorsement_power = voting_power; }) )) | Single (Seed_nonce_revelation {level; nonce}) -> let level = Level.from_raw ctxt level in Nonce.reveal ctxt level nonce >>=? fun ctxt -> let tip = Constants.seed_nonce_revelation_tip ctxt in - let contract = Contract.Implicit payload_producer in + let contract = + Contract.Implicit payload_producer.Consensus_key.delegate + in Token.transfer ctxt `Revelation_rewards (`Contract contract) tip >|=? fun (ctxt, balance_updates) -> (ctxt, Single_result (Seed_nonce_revelation_result balance_updates)) @@ -3406,8 +3428,8 @@ let apply_liquidity_baking_subsidy ctxt ~toggle_vote = type 'a full_construction = { ctxt : t; protocol_data : 'a; - payload_producer : Signature.public_key_hash; - block_producer : Signature.public_key_hash; + payload_producer : Consensus_key.t; + block_producer : Consensus_key.t; round : Round.t; implicit_operations_results : packed_successful_manager_operation_result list; liquidity_baking_toggle_ema : Liquidity_baking.Toggle_EMA.t; @@ -3426,17 +3448,18 @@ let begin_full_construction ctxt ~predecessor_timestamp ~predecessor_level >>?= fun () -> let current_level = Level.current ctxt in Stake_distribution.baking_rights_owner ctxt current_level ~round - >>=? fun (ctxt, _slot, (_block_producer_pk, block_producer)) -> - Delegate.frozen_deposits ctxt block_producer >>=? fun frozen_deposits -> + >>=? fun (ctxt, _slot, block_producer) -> + Delegate.frozen_deposits ctxt block_producer.delegate + >>=? fun frozen_deposits -> fail_unless Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits block_producer) + (Zero_frozen_deposits block_producer.delegate) >>=? fun () -> Stake_distribution.baking_rights_owner ctxt current_level ~round:protocol_data.Block_header.payload_round - >>=? fun (ctxt, _slot, (_payload_producer_pk, payload_producer)) -> + >>=? fun (ctxt, _slot, payload_producer) -> init_allowed_consensus_operations ctxt ~endorsement_level:predecessor_level @@ -3450,8 +3473,8 @@ let begin_full_construction ctxt ~predecessor_timestamp ~predecessor_level { ctxt; protocol_data; - payload_producer; - block_producer; + payload_producer = Consensus_key.pkh payload_producer; + block_producer = Consensus_key.pkh block_producer; round; implicit_operations_results = liquidity_baking_operations_results; liquidity_baking_toggle_ema; @@ -3473,7 +3496,7 @@ let begin_application ctxt chain_id (block_header : Block_header.t) fitness let round = Fitness.round fitness in let current_level = Level.current ctxt in Stake_distribution.baking_rights_owner ctxt current_level ~round - >>=? fun (ctxt, _slot, (block_producer_pk, block_producer)) -> + >>=? fun (ctxt, _slot, block_producer) -> let timestamp = block_header.shell.timestamp in Block_header.begin_validate_block_header ~block_header @@ -3482,21 +3505,22 @@ let begin_application ctxt chain_id (block_header : Block_header.t) fitness ~predecessor_round ~fitness ~timestamp - ~delegate_pk:block_producer_pk + ~delegate_pk:block_producer.consensus_pk ~round_durations:(Constants.round_durations ctxt) ~proof_of_work_threshold:(Constants.proof_of_work_threshold ctxt) ~expected_commitment:current_level.expected_commitment >>?= fun () -> - Delegate.frozen_deposits ctxt block_producer >>=? fun frozen_deposits -> + Delegate.frozen_deposits ctxt block_producer.delegate + >>=? fun frozen_deposits -> fail_unless Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits block_producer) + (Zero_frozen_deposits block_producer.delegate) >>=? fun () -> Stake_distribution.baking_rights_owner ctxt current_level ~round:block_header.protocol_data.contents.payload_round - >>=? fun (ctxt, _slot, (payload_producer_pk, _payload_producer)) -> + >>=? fun (ctxt, _slot, payload_producer) -> init_allowed_consensus_operations ctxt ~endorsement_level:predecessor_level @@ -3511,8 +3535,8 @@ let begin_application ctxt chain_id (block_header : Block_header.t) fitness liquidity_baking_operations_results, liquidity_baking_toggle_ema ) -> ( ctxt, - payload_producer_pk, - block_producer, + Consensus_key.pkh payload_producer, + Consensus_key.pkh block_producer, liquidity_baking_operations_results, liquidity_baking_toggle_ema ) @@ -3599,7 +3623,7 @@ let finalize_application_check_validity ctxt (mode : finalize_application_mode) let record_endorsing_participation ctxt = let validators = Consensus.allowed_endorsements ctxt in Slot.Map.fold_es - (fun initial_slot (_delegate_pk, delegate, power) ctxt -> + (fun initial_slot ((consensus_pk : Consensus_key.pk), power) ctxt -> let participation = if Slot.Set.mem initial_slot (Consensus.endorsements_seen ctxt) then Delegate.Participated @@ -3607,15 +3631,16 @@ let record_endorsing_participation ctxt = in Delegate.record_endorsing_participation ctxt - ~delegate + ~delegate:consensus_pk.delegate ~participation ~endorsing_power:power) validators ctxt let finalize_application ctxt (mode : finalize_application_mode) protocol_data - ~payload_producer ~block_producer liquidity_baking_toggle_ema - implicit_operations_results ~round ~predecessor ~migration_balance_updates = + ~(payload_producer : Consensus_key.t) ~(block_producer : Consensus_key.t) + liquidity_baking_toggle_ema implicit_operations_results ~round ~predecessor + ~migration_balance_updates = (* Then we finalize the consensus. *) let level = Level.current ctxt in let block_endorsing_power = Consensus.current_endorsement_power ctxt in @@ -3650,7 +3675,7 @@ let finalize_application ctxt (mode : finalize_application_mode) protocol_data (match protocol_data.Block_header.seed_nonce_hash with | None -> return ctxt | Some nonce_hash -> - Nonce.record_hash ctxt {nonce_hash; delegate = block_producer}) + Nonce.record_hash ctxt {nonce_hash; delegate = block_producer.delegate}) >>=? fun ctxt -> (if required_endorsements then record_endorsing_participation ctxt >>=? fun ctxt -> @@ -3661,8 +3686,8 @@ let finalize_application ctxt (mode : finalize_application_mode) protocol_data let baking_reward = Constants.baking_reward_fixed_portion ctxt in Delegate.record_baking_activity_and_pay_rewards_and_fees ctxt - ~payload_producer - ~block_producer + ~payload_producer:payload_producer.delegate + ~block_producer:block_producer.delegate ~baking_reward ~reward_bonus >>=? fun (ctxt, baking_receipts) -> diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index 3e3a0688450a..c7a85192d5b3 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -58,8 +58,8 @@ val begin_partial_construction : type 'a full_construction = { ctxt : context; protocol_data : 'a; - payload_producer : Signature.public_key_hash; - block_producer : Signature.public_key_hash; + payload_producer : Consensus_key.t; + block_producer : Consensus_key.t; round : Round.t; implicit_operations_results : packed_successful_manager_operation_result list; liquidity_baking_toggle_ema : Liquidity_baking.Toggle_EMA.t; @@ -83,8 +83,8 @@ val begin_application : predecessor_level:Level.t -> predecessor_round:Round.t -> (t - * Signature.public_key - * Signature.public_key_hash + * Consensus_key.t + * Consensus_key.t * packed_successful_manager_operation_result list * Liquidity_baking.Toggle_EMA.t) tzresult @@ -118,7 +118,7 @@ val apply_operation : Chain_id.t -> apply_mode -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> Operation_list_hash.elt -> 'a operation -> (context * 'a operation_metadata, error trace) result Lwt.t @@ -134,8 +134,8 @@ val finalize_application : context -> finalize_application_mode -> Block_header.contents -> - payload_producer:public_key_hash -> - block_producer:public_key_hash -> + payload_producer:Consensus_key.t -> + block_producer:Consensus_key.t -> Liquidity_baking.Toggle_EMA.t -> packed_successful_manager_operation_result list -> round:Round.t -> @@ -146,7 +146,7 @@ val finalize_application : val apply_manager_contents_list : context -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> Chain_id.t -> 'a Kind.manager prechecked_contents_list -> (context * 'a Kind.manager contents_result_list) Lwt.t @@ -156,7 +156,7 @@ val apply_contents_list : Chain_id.t -> apply_mode -> Script_ir_translator.unparsing_mode -> - payload_producer:public_key_hash -> + payload_producer:Consensus_key.t -> 'kind operation -> 'kind contents_list -> (context * 'kind contents_result_list) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index 0eeb36942f7d..d592d0973886 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -1436,13 +1436,15 @@ let successful_manager_operation_result_encoding : type 'kind contents_result = | Preendorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; preendorsement_power : int; } -> Kind.preendorsement contents_result | Endorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; endorsement_power : int; } -> Kind.endorsement contents_result @@ -1587,10 +1589,11 @@ module Encoding = struct { op_case = Operation.Encoding.preendorsement_case; encoding = - obj3 + obj4 (dft "balance_updates" Receipt.balance_updates_encoding []) (req "delegate" Signature.Public_key_hash.encoding) - (req "preendorsement_power" int31); + (req "preendorsement_power" int31) + (req "consensus_key" Signature.Public_key_hash.encoding); select = (function | Contents_result (Preendorsement_result _ as op) -> Some op @@ -1602,12 +1605,13 @@ module Encoding = struct proj = (function | Preendorsement_result - {balance_updates; delegate; preendorsement_power} -> - (balance_updates, delegate, preendorsement_power)); + {balance_updates; delegate; consensus_key; preendorsement_power} + -> + (balance_updates, delegate, preendorsement_power, consensus_key)); inj = - (fun (balance_updates, delegate, preendorsement_power) -> + (fun (balance_updates, delegate, preendorsement_power, consensus_key) -> Preendorsement_result - {balance_updates; delegate; preendorsement_power}); + {balance_updates; delegate; consensus_key; preendorsement_power}); } let[@coq_axiom_with_reason "gadt"] endorsement_case = @@ -1615,10 +1619,11 @@ module Encoding = struct { op_case = Operation.Encoding.endorsement_case; encoding = - obj3 + obj4 (dft "balance_updates" Receipt.balance_updates_encoding []) (req "delegate" Signature.Public_key_hash.encoding) - (req "endorsement_power" int31); + (req "endorsement_power" int31) + (req "consensus_key" Signature.Public_key_hash.encoding); select = (function | Contents_result (Endorsement_result _ as op) -> Some op | _ -> None); @@ -1628,11 +1633,13 @@ module Encoding = struct | _ -> None); proj = (function - | Endorsement_result {balance_updates; delegate; endorsement_power} -> - (balance_updates, delegate, endorsement_power)); + | Endorsement_result + {balance_updates; delegate; consensus_key; endorsement_power} -> + (balance_updates, delegate, endorsement_power, consensus_key)); inj = - (fun (balance_updates, delegate, endorsement_power) -> - Endorsement_result {balance_updates; delegate; endorsement_power}); + (fun (balance_updates, delegate, endorsement_power, consensus_key) -> + Endorsement_result + {balance_updates; delegate; consensus_key; endorsement_power}); } let[@coq_axiom_with_reason "gadt"] seed_nonce_revelation_case = @@ -3001,8 +3008,8 @@ let operation_data_and_metadata_encoding = ] type block_metadata = { - proposer : Signature.Public_key_hash.t; - baker : Signature.Public_key_hash.t; + proposer : Consensus_key.t; + baker : Consensus_key.t; level_info : Level.t; voting_period_info : Voting_period.info; nonce_hash : Nonce_hash.t option; @@ -3018,8 +3025,9 @@ let block_metadata_encoding = def "block_header.alpha.metadata" @@ conv (fun { - proposer; - baker; + proposer = + {delegate = proposer; consensus_pkh = proposer_active_key}; + baker = {delegate = baker; consensus_pkh = baker_active_key}; level_info; voting_period_info; nonce_hash; @@ -3039,7 +3047,7 @@ let block_metadata_encoding = balance_updates, liquidity_baking_toggle_ema, implicit_operations_results ), - consumed_gas )) + (proposer_active_key, baker_active_key, consumed_gas) )) (fun ( ( proposer, baker, level_info, @@ -3050,10 +3058,10 @@ let block_metadata_encoding = balance_updates, liquidity_baking_toggle_ema, implicit_operations_results ), - _consumed_millgas ) -> + (proposer_active_key, baker_active_key, _consumed_millgas) ) -> { - proposer; - baker; + proposer = {delegate = proposer; consensus_pkh = proposer_active_key}; + baker = {delegate = baker; consensus_pkh = baker_active_key}; level_info; voting_period_info; nonce_hash; @@ -3079,7 +3087,10 @@ let block_metadata_encoding = (req "implicit_operations_results" (list successful_manager_operation_result_encoding))) - (obj1 (req "consumed_milligas" Gas.Arith.n_fp_encoding))) + (obj3 + (req "proposer_consensus_key" Signature.Public_key_hash.encoding) + (req "baker_consensus_key" Signature.Public_key_hash.encoding) + (req "consumed_milligas" Gas.Arith.n_fp_encoding))) type precheck_result = { consumed_gas : Gas.Arith.fp; diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index 69822f79d6dd..cdb2c2211103 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -89,13 +89,15 @@ and packed_contents_result_list = and 'kind contents_result = | Preendorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; preendorsement_power : int; } -> Kind.preendorsement contents_result | Endorsement_result : { balance_updates : Receipt.balance_updates; - delegate : Signature.Public_key_hash.t; + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; endorsement_power : int; } -> Kind.endorsement contents_result @@ -362,8 +364,8 @@ val kind_equal_list : ('kind, 'kind2) eq option type block_metadata = { - proposer : Signature.Public_key_hash.t; - baker : Signature.Public_key_hash.t; + proposer : Consensus_key.t; + baker : Consensus_key.t; level_info : Level.t; voting_period_info : Voting_period.info; nonce_hash : Nonce_hash.t option; diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index 00bc864d9102..4cf700d66586 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -72,12 +72,17 @@ let bonus_baking_reward ctxt ~endorsing_power = let baking_rights c level = let rec f c round = Stake_distribution.baking_rights_owner c level ~round - >>=? fun (c, _slot, (delegate, _)) -> - return (LCons (delegate, fun () -> f c (Round.succ round))) + >>=? fun (c, _slot, consensus_pk) -> + return + (LCons (Consensus_key.pkh consensus_pk, fun () -> f c (Round.succ round))) in f c Round.zero -type ordered_slots = Slot.t list +type ordered_slots = { + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; + slots : Slot.t list; +} (* Slots returned by this function are assumed by consumers to be in increasing order, hence the use of [Slot.Range.rev_fold_es]. *) @@ -86,11 +91,20 @@ let endorsing_rights (ctxt : t) level = Slot.Range.create ~min:0 ~count:consensus_committee_size >>?= fun slots -> Slot.Range.rev_fold_es (fun (ctxt, map) slot -> - Stake_distribution.slot_owner ctxt level slot >>=? fun (ctxt, (_, pkh)) -> + Stake_distribution.slot_owner ctxt level slot + >>=? fun (ctxt, consensus_pk) -> let map = Signature.Public_key_hash.Map.update - pkh - (function None -> Some [slot] | Some slots -> Some (slot :: slots)) + consensus_pk.delegate + (function + | None -> + Some + { + delegate = consensus_pk.delegate; + consensus_key = consensus_pk.consensus_pkh; + slots = [slot]; + } + | Some slots -> Some {slots with slots = slot :: slots.slots}) map in return (ctxt, map)) @@ -103,11 +117,17 @@ let endorsing_rights_by_first_slot ctxt level = Slot.Range.fold_es (fun (ctxt, (delegates_map, slots_map)) slot -> Stake_distribution.slot_owner ctxt level slot - >|=? fun (ctxt, (pk, pkh)) -> + >|=? fun (ctxt, consensus_pk) -> let initial_slot, delegates_map = - match Signature.Public_key_hash.Map.find pkh delegates_map with + match + Signature.Public_key_hash.Map.find consensus_pk.delegate delegates_map + with | None -> - (slot, Signature.Public_key_hash.Map.add pkh slot delegates_map) + ( slot, + Signature.Public_key_hash.Map.add + consensus_pk.delegate + slot + delegates_map ) | Some initial_slot -> (initial_slot, delegates_map) in (* [slots_map]'keys are the minimal slots of delegates because @@ -116,8 +136,8 @@ let endorsing_rights_by_first_slot ctxt level = Slot.Map.update initial_slot (function - | None -> Some (pk, pkh, 1) - | Some (pk, pkh, count) -> Some (pk, pkh, count + 1)) + | None -> Some (consensus_pk, 1) + | Some (consensus_pk, count) -> Some (consensus_pk, count + 1)) slots_map in (ctxt, (delegates_map, slots_map))) diff --git a/src/proto_alpha/lib_protocol/baking.mli b/src/proto_alpha/lib_protocol/baking.mli index 9d9281b735a3..9e90eb4ea479 100644 --- a/src/proto_alpha/lib_protocol/baking.mli +++ b/src/proto_alpha/lib_protocol/baking.mli @@ -34,7 +34,11 @@ type error += consensus_threshold : int; } -type ordered_slots = private Slot.t list +type ordered_slots = private { + delegate : Signature.public_key_hash; + consensus_key : Signature.public_key_hash; + slots : Slot.t list; +} (** For a given level computes who has the right to include an endorsement in the next block. @@ -48,14 +52,14 @@ val endorsing_rights : Level.t -> (context * ordered_slots Signature.Public_key_hash.Map.t) tzresult Lwt.t -(** Computes endorsing rights for a given level. +(** Computes endorsing rights for a given level. @return map from allocated first slots to their owner's public key, public key hash, and endorsing power. *) val endorsing_rights_by_first_slot : context -> Level.t -> - (context * (public_key * public_key_hash * int) Slot.Map.t) tzresult Lwt.t + (context * (Consensus_key.pk * int) Slot.Map.t) tzresult Lwt.t (** Computes the bonus baking reward depending on the endorsing power. *) val bonus_baking_reward : context -> endorsing_power:int -> Tez.t tzresult @@ -63,4 +67,4 @@ val bonus_baking_reward : context -> endorsing_power:int -> Tez.t tzresult (** [baking_rights ctxt level] is the lazy list of contract's public key hashes that are allowed to propose for [level] at each round. *) -val baking_rights : context -> Level.t -> public_key lazy_list +val baking_rights : context -> Level.t -> Consensus_key.t lazy_list diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml index 2303e0287a1b..37c7e48af185 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -36,7 +36,7 @@ let () = ~pp:(fun ppf cycle -> Format.fprintf ppf - "Invalid cycle while updating a consensus key (%a)." + "Invalid cycle while updating a consensus key (expected: %a)." Cycle_repr.pp cycle) Data_encoding.(obj1 (req "cycle" Cycle_repr.encoding)) @@ -57,6 +57,35 @@ let () = (function Invalid_consensus_key_update_noop c -> Some c | _ -> None) (fun c -> Invalid_consensus_key_update_noop c) +type pk = Raw_context.consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +type t = { + delegate : Signature.Public_key_hash.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +let pkh {delegate; consensus_pkh; consensus_pk = _} = {delegate; consensus_pkh} + +let zero = + { + consensus_pkh = Signature.Public_key_hash.zero; + delegate = Signature.Public_key_hash.zero; + } + +let pp ppf {delegate; consensus_pkh} = + Format.fprintf ppf "@[%a" Signature.Public_key_hash.pp delegate ; + if not (Signature.Public_key_hash.equal delegate consensus_pkh) then + Format.fprintf + ppf + "@,Active key: %a" + Signature.Public_key_hash.pp + consensus_pkh ; + Format.fprintf ppf "@]" + let init ctxt delegate pk = Storage.Contract.Consensus_key.init ctxt (Contract_repr.Implicit delegate) pk @@ -65,12 +94,13 @@ let active_pubkey ctxt delegate = let* pk = Storage.Contract.Consensus_key.get ctxt (Contract_repr.Implicit delegate) in - return pk + let pkh = Signature.Public_key.hash pk in + return {consensus_pk = pk; consensus_pkh = pkh; delegate} let active_key ctxt delegate = let open Lwt_tzresult_syntax in let* pk = active_pubkey ctxt delegate in - return (Signature.Public_key.hash pk) + return (pkh pk) let raw_pending_updates ctxt delegate = let open Lwt_tzresult_syntax in @@ -97,23 +127,32 @@ let raw_active_pubkey_for_cycle ctxt delegate cycle = List.fold_left (fun (c1, active) (c2, pk) -> if Cycle_repr.(c1 < c2 && c2 <= cycle) then (c2, pk) else (c1, active)) - (current_level.cycle, active) + (current_level.cycle, active.consensus_pk) pendings in return active_for_cycle let active_pubkey_for_cycle ctxt delegate cycle = let open Lwt_tzresult_syntax in - let* _, active = raw_active_pubkey_for_cycle ctxt delegate cycle in - return active + let* _, consensus_pk = raw_active_pubkey_for_cycle ctxt delegate cycle in + return + { + consensus_pk; + consensus_pkh = Signature.Public_key.hash consensus_pk; + delegate; + } let register_update ctxt delegate cycle pk = let open Lwt_tzresult_syntax in let* () = let current_level = Raw_context.current_level ctxt in + let preserved_cycles = Constants_storage.preserved_cycles ctxt in + let required_cycle = + Cycle_repr.add current_level.cycle (preserved_cycles + 1) + in fail_when - Cycle_repr.(cycle <= current_level.cycle) - (Invalid_consensus_key_update_cycle cycle) + Cycle_repr.(cycle <> required_cycle) + (Invalid_consensus_key_update_cycle required_cycle) in let* () = let* cycle, active_pubkey = diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli index 8ca12e0beb8b..a8d4ea02aafc 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -27,6 +27,25 @@ type error += | Invalid_consensus_key_update_cycle of Cycle_repr.t | Invalid_consensus_key_update_noop of Cycle_repr.t +(** The public key of a consensus key and the associated delegate. *) +type pk = Raw_context.consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +(** The public key hash of a consensus key and the associated delegate. *) +type t = { + delegate : Signature.Public_key_hash.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +val zero : t + +val pp : Format.formatter -> t -> unit + +val pkh : pk -> t + (** Initialize the consensus key when registering a delegate. *) val init : Raw_context.t -> @@ -36,22 +55,18 @@ val init : (** Returns the active consensus key for the current cycle. *) val active_pubkey : - Raw_context.t -> - Signature.Public_key_hash.t -> - Signature.Public_key.t tzresult Lwt.t + Raw_context.t -> Signature.Public_key_hash.t -> pk tzresult Lwt.t (** Returns the active consensus key for the current cycle. *) val active_key : - Raw_context.t -> - Signature.Public_key_hash.t -> - Signature.Public_key_hash.t tzresult Lwt.t + Raw_context.t -> Signature.Public_key_hash.t -> t tzresult Lwt.t (** Returns the active consensus key for the given cycle. *) val active_pubkey_for_cycle : Raw_context.t -> Signature.Public_key_hash.t -> Cycle_repr.t -> - Signature.Public_key.t tzresult Lwt.t + pk tzresult Lwt.t (** Returns the list of pending consensus-key updates in upcoming cycles. *) val pending_updates : diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.ml b/src/proto_alpha/lib_protocol/delegate_cycles.ml index e4cf195b7a21..438d57d7aece 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.ml +++ b/src/proto_alpha/lib_protocol/delegate_cycles.ml @@ -279,3 +279,31 @@ let init_first_cycles ctxt ~origin = >>=? fun ctxt -> let cycle = (Raw_context.current_level ctxt).cycle in freeze_deposits ~origin ~new_cycle:cycle ~balance_updates:[] ctxt + +module Migration_from_Jakarta = struct + let update_delegate ctxt pkh = + let open Lwt_tzresult_syntax in + (* Consensus key*) + let* pk = Contract_manager_storage.get_manager_key ctxt pkh in + let* ctxt = Delegate_consensus_key.init ctxt pkh pk in + return ctxt + + let update ctxt = + let open Lwt_tzresult_syntax in + let*! delegates = Storage.Delegates.elements ctxt in + let* ctxt = List.fold_left_es update_delegate ctxt delegates in + let*! cycles = + Storage.Migration_from_Jakarta.Delegate_sampler_state.keys ctxt + in + let*! ctxt = + Storage.Migration_from_Jakarta.Delegate_sampler_state.clear ctxt + in + let*? ctxt = Raw_context.Migration_from_Jakarta.reset_samplers ctxt in + let* ctxt = + List.fold_left_es + Delegate_sampler.Migration_from_Jakarta.update_sampler + ctxt + cycles + in + return ctxt +end diff --git a/src/proto_alpha/lib_protocol/delegate_cycles.mli b/src/proto_alpha/lib_protocol/delegate_cycles.mli index ebcecb05d925..a51e8a9a16e2 100644 --- a/src/proto_alpha/lib_protocol/delegate_cycles.mli +++ b/src/proto_alpha/lib_protocol/delegate_cycles.mli @@ -47,3 +47,7 @@ val init_first_cycles : Raw_context.t -> origin:Receipt_repr.update_origin -> (Raw_context.t * Receipt_repr.balance_updates) tzresult Lwt.t + +module Migration_from_Jakarta : sig + val update : Raw_context.t -> Raw_context.t tzresult Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.ml b/src/proto_alpha/lib_protocol/delegate_sampler.ml index b2611be04dfd..85240f203e3a 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.ml +++ b/src/proto_alpha/lib_protocol/delegate_sampler.ml @@ -34,8 +34,7 @@ module Delegate_sampler_state = struct module Cache_client = struct - type cached_value = - (Signature.public_key * Signature.public_key_hash) Sampler.t + type cached_value = Delegate_consensus_key.pk Sampler.t let namespace = Cache_repr.create_namespace "sampler_state" @@ -140,8 +139,8 @@ module Random = struct let elt, _ = take_int64 mass_bound state in (Int64.to_int i, elt) in - let pk, pkh = Sampler.sample state sample in - return (c, (pk, pkh)) + let pk = Sampler.sample state sample in + return (c, pk) end let slot_owner c level slot = Random.owner c level (Slot_repr.to_int slot) @@ -225,7 +224,7 @@ let select_distribution_for_cycle ctxt cycle = List.fold_left_es (fun acc (pkh, stake) -> Delegate_consensus_key.active_pubkey_for_cycle ctxt pkh cycle - >|=? fun pk -> ((pk, pkh), Tez_repr.to_mutez stake) :: acc) + >|=? fun pk -> (pk, Tez_repr.to_mutez stake) :: acc) [] stakes >>=? fun stakes_pk -> @@ -247,3 +246,21 @@ let clear_outdated_sampling_data ctxt ~new_cycle = Delegate_sampler_state.remove_existing ctxt outdated_cycle >>=? fun ctxt -> Storage.Seed.For_cycle.remove_existing ctxt outdated_cycle + +module Migration_from_Jakarta = struct + let update_sampler ctxt cycle = + let open Lwt_tzresult_syntax in + let* stakes = Stake_storage.get_selected_distribution ctxt cycle in + let* stakes_pk = + List.fold_left_es + (fun acc (delegate, stake) -> + Delegate_consensus_key.active_pubkey ctxt delegate >>=? fun pk -> + return ((pk, Tez_repr.to_mutez stake) :: acc)) + [] + stakes + in + let state = Sampler.create stakes_pk in + Delegate_sampler_state.init ctxt cycle state >>=? fun ctxt -> + Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> + Lwt.return (Raw_context.init_sampler_for_cycle ctxt cycle seed state) +end diff --git a/src/proto_alpha/lib_protocol/delegate_sampler.mli b/src/proto_alpha/lib_protocol/delegate_sampler.mli index 194c5a50468b..ae3132041a18 100644 --- a/src/proto_alpha/lib_protocol/delegate_sampler.mli +++ b/src/proto_alpha/lib_protocol/delegate_sampler.mli @@ -35,18 +35,13 @@ val slot_owner : Raw_context.t -> Level_repr.t -> Slot_repr.t -> - (Raw_context.t * (Signature.public_key * Signature.public_key_hash)) tzresult - Lwt.t + (Raw_context.t * Delegate_consensus_key.pk) tzresult Lwt.t val baking_rights_owner : Raw_context.t -> Level_repr.t -> round:Round_repr.round -> - (Raw_context.t - * Slot_repr.t - * (Signature.public_key * Signature.public_key_hash)) - tzresult - Lwt.t + (Raw_context.t * Slot_repr.t * Delegate_consensus_key.pk) tzresult Lwt.t (** [compute_snapshot_index ctxt cycle max_snapshot_index] Returns the index of the selected snapshot for the [cycle] passed as argument, and for the max @@ -63,3 +58,8 @@ val clear_outdated_sampling_data : val select_distribution_for_cycle : Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t + +module Migration_from_Jakarta : sig + val update_sampler : + Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t +end diff --git a/src/proto_alpha/lib_protocol/delegate_services.ml b/src/proto_alpha/lib_protocol/delegate_services.ml index ebbc02a7152c..d87b9610b4f8 100644 --- a/src/proto_alpha/lib_protocol/delegate_services.ml +++ b/src/proto_alpha/lib_protocol/delegate_services.ml @@ -423,7 +423,7 @@ let register () = deactivated; grace_period; voting_info; - active_consensus_key = Signature.Public_key.hash consensus_key; + active_consensus_key = consensus_key.consensus_pkh; pending_consensus_keys = pendings; }) ; register1 ~chunked:false S.full_balance (fun ctxt pkh () () -> @@ -465,7 +465,7 @@ let register () = Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.Consensus_key.active_pubkey ctxt pkh >>=? fun pk -> Delegate.Consensus_key.pending_updates ctxt pkh >>=? fun pendings -> - return (Signature.Public_key.hash pk, pendings)) ; + return (pk.consensus_pkh, pendings)) ; register1 ~chunked:false S.participation (fun ctxt pkh () () -> Delegate.check_registered ctxt pkh >>=? fun () -> Delegate.participation_info ctxt pkh) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 29268f00b595..96aac84dde1e 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -297,18 +297,3 @@ module Contract = struct set_self_delegate c delegate | _ -> set_delegate c contract delegate end - -module Migration_from_Jakarta = struct - let update_delegate ctxt pkh = - let open Lwt_tzresult_syntax in - (* Consensus key*) - let* pk = Contract_manager_storage.get_manager_key ctxt pkh in - let* ctxt = Delegate_consensus_key.init ctxt pkh pk in - return ctxt - - let update ctxt = - let open Lwt_tzresult_syntax in - let*! delegates = Storage.Delegates.elements ctxt in - let* ctxt = List.fold_left_es update_delegate ctxt delegates in - return ctxt -end diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index bf603fdd3806..4bb421f8a644 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -127,7 +127,3 @@ val manager_pubkey : Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t - -module Migration_from_Jakarta : sig - val update : Raw_context.t -> Raw_context.t tzresult Lwt.t -end diff --git a/src/proto_alpha/lib_protocol/init_storage.ml b/src/proto_alpha/lib_protocol/init_storage.ml index ea7eb7858dbb..81ccb3ba85e6 100644 --- a/src/proto_alpha/lib_protocol/init_storage.ml +++ b/src/proto_alpha/lib_protocol/init_storage.ml @@ -119,7 +119,7 @@ let prepare_first_block _chain_id ctxt ~typecheck ~level ~timestamp = Raw_level_repr.of_int32 level >>?= fun level -> Storage.Tenderbake.First_level_of_protocol.update ctxt level >>=? fun ctxt -> - Delegate_storage.Migration_from_Jakarta.update ctxt >>=? fun ctxt -> + Delegate_cycles.Migration_from_Jakarta.update ctxt >>=? fun ctxt -> return (ctxt, [])) >>=? fun (ctxt, balance_updates) -> Receipt_repr.group_balance_updates balance_updates >>?= fun balance_updates -> diff --git a/src/proto_alpha/lib_protocol/level_repr.ml b/src/proto_alpha/lib_protocol/level_repr.ml index 0b5926f387f5..f3100a55a188 100644 --- a/src/proto_alpha/lib_protocol/level_repr.ml +++ b/src/proto_alpha/lib_protocol/level_repr.ml @@ -340,4 +340,13 @@ module Internal_for_tests = struct let raw_level = level.level in let new_raw_level = Raw_level_repr.add raw_level n in {level with level = new_raw_level} + + let add_cycles ~blocks_per_cycle level n = + { + level with + cycle = Cycle_repr.add level.cycle n; + level = Raw_level_repr.add level.level (n * blocks_per_cycle); + level_position = + Int32.add level.level_position (Int32.of_int (n * blocks_per_cycle)); + } end diff --git a/src/proto_alpha/lib_protocol/level_repr.mli b/src/proto_alpha/lib_protocol/level_repr.mli index b595ce324e69..d6ab68499ba8 100644 --- a/src/proto_alpha/lib_protocol/level_repr.mli +++ b/src/proto_alpha/lib_protocol/level_repr.mli @@ -111,6 +111,8 @@ val last_of_cycle : cycle_eras:cycle_eras -> level -> bool module Internal_for_tests : sig val add_level : t -> int -> t + + val add_cycles : blocks_per_cycle:int -> t -> int -> t end (**/**) diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 3a1dfce12939..547dae479a32 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -94,16 +94,16 @@ type validation_mode = | Application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_round : Alpha_context.Round.t; predecessor_level : Alpha_context.Level.t; } | Partial_application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_level : Alpha_context.Level.t; predecessor_round : Alpha_context.Round.t; } @@ -117,8 +117,8 @@ type validation_mode = (* Baker only *) | Full_construction of { predecessor : Block_hash.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; protocol_data_contents : Alpha_context.Block_header.contents; level : Int32.t; round : Alpha_context.Round.t; @@ -162,7 +162,7 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt ~predecessor_level ~predecessor_round >>=? fun ( ctxt, - payload_producer_pk, + payload_producer, block_producer, liquidity_baking_operations_results, liquidity_baking_toggle_ema ) -> @@ -173,7 +173,7 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt fitness; predecessor_level; predecessor_round; - payload_producer = Signature.Public_key.hash payload_producer_pk; + payload_producer; block_producer; } in @@ -229,7 +229,7 @@ let begin_application ~chain_id ~predecessor_context:ctxt ~predecessor_timestamp fitness; predecessor_round; predecessor_level; - payload_producer = Signature.Public_key.hash payload_producer; + payload_producer; block_producer; } in @@ -443,7 +443,7 @@ let apply_operation ({mode; chain_id; ctxt; op_count; _} as data) data op_count operation - ~payload_producer:Signature.Public_key_hash.zero + ~payload_producer:Alpha_context.Consensus_key.zero | Full_construction { payload_producer; @@ -573,8 +573,8 @@ let finalize_block ( ctxt, Apply_results. { - proposer = Signature.Public_key_hash.zero; - baker = Signature.Public_key_hash.zero; + proposer = Alpha_context.Consensus_key.zero; + baker = Alpha_context.Consensus_key.zero; level_info; voting_period_info; nonce_hash = None; @@ -609,7 +609,7 @@ let finalize_block ( ctxt, Apply_results. { - proposer = Signature.Public_key_hash.zero; + proposer = Alpha_context.Consensus_key.zero; (* We cannot retrieve the proposer as it requires the frozen deposit that might not be available depending on the context given to the partial application. *) diff --git a/src/proto_alpha/lib_protocol/main.mli b/src/proto_alpha/lib_protocol/main.mli index a6e20a489ec4..ad796cee7971 100644 --- a/src/proto_alpha/lib_protocol/main.mli +++ b/src/proto_alpha/lib_protocol/main.mli @@ -56,8 +56,8 @@ type validation_mode = | Application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_round : Alpha_context.Round.t; predecessor_level : Alpha_context.Level.t; } @@ -66,8 +66,8 @@ type validation_mode = | Partial_application of { block_header : Alpha_context.Block_header.t; fitness : Alpha_context.Fitness.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; predecessor_level : Alpha_context.Level.t; predecessor_round : Alpha_context.Round.t; } @@ -86,8 +86,8 @@ type validation_mode = {!val:Tezos_protocol_environment_sigs.V5.T.Updater.PROTOCOL.begin_construction} *) | Full_construction of { predecessor : Block_hash.t; - payload_producer : Alpha_context.public_key_hash; - block_producer : Alpha_context.public_key_hash; + payload_producer : Alpha_context.Consensus_key.t; + block_producer : Alpha_context.Consensus_key.t; protocol_data_contents : Alpha_context.Block_header.contents; level : Int32.t; round : Alpha_context.Round.t; diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index f1341fb2d2d8..4c0712fd9a92 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -73,6 +73,29 @@ module Sc_rollup_address_map_builder = *) +type consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +let consensus_pk_encoding = + let open Data_encoding in + conv + (fun {delegate; consensus_pk; consensus_pkh} -> + if Signature.Public_key_hash.equal consensus_pkh delegate then + (consensus_pk, None) + else (consensus_pk, Some delegate)) + (fun (consensus_pk, delegate) -> + let consensus_pkh = Signature.Public_key.hash consensus_pk in + let delegate = + match delegate with None -> consensus_pkh | Some del -> del + in + {delegate; consensus_pk; consensus_pkh}) + (obj2 + (req "consensus_pk" Signature.Public_key.encoding) + (opt "delegate" Signature.Public_key_hash.encoding)) + module Raw_consensus = struct (** Consensus operations are indexed by their [initial slots]. Given a delegate, the [initial slot] is the lowest slot assigned to @@ -81,16 +104,12 @@ module Raw_consensus = struct type t = { current_endorsement_power : int; (** Number of endorsement slots recorded for the current block. *) - allowed_endorsements : - (Signature.Public_key.t * Signature.Public_key_hash.t * int) - Slot_repr.Map.t; + allowed_endorsements : (consensus_pk * int) Slot_repr.Map.t; (** Endorsements rights for the current block. Only an endorsement for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this slot with its power. *) - allowed_preendorsements : - (Signature.Public_key.t * Signature.Public_key_hash.t * int) - Slot_repr.Map.t; + allowed_preendorsements : (consensus_pk * int) Slot_repr.Map.t; (** Preendorsements rights for the current block. Only a preendorsement for the lowest slot in the block can be recorded. The map associates to each initial slot the [pkh] associated to this @@ -238,10 +257,7 @@ type back = { unlimited_operation_gas : bool; consensus : Raw_consensus.t; non_consensus_operations_rev : Operation_hash.t list; - sampler_state : - (Seed_repr.seed - * (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t) - Cycle_repr.Map.t; + sampler_state : (Seed_repr.seed * consensus_pk Sampler.t) Cycle_repr.Map.t; stake_distribution_for_current_cycle : Tez_repr.t Signature.Public_key_hash.Map.t option; tx_rollup_current_messages : @@ -1216,6 +1232,12 @@ let record_non_consensus_operation_hash ctxt operation_hash = let non_consensus_operations ctxt = List.rev (non_consensus_operations_rev ctxt) +module Migration_from_Jakarta = struct + let reset_samplers ctxt = + let ctxt = update_sampler_state ctxt Cycle_repr.Map.empty in + ok ctxt +end + let init_sampler_for_cycle ctxt cycle seed state = let map = sampler_state ctxt in if Cycle_repr.Map.mem cycle map then error (Sampler_already_set cycle) @@ -1254,6 +1276,17 @@ module Internal_for_tests = struct let new_level = Level_repr.Internal_for_tests.add_level ctxt.back.level l in let new_back = {ctxt.back with level = new_level} in {ctxt with back = new_back} + + let add_cycles ctxt l = + let blocks_per_cycle = Int32.to_int (constants ctxt).blocks_per_cycle in + let new_level = + Level_repr.Internal_for_tests.add_cycles + ~blocks_per_cycle + ctxt.back.level + l + in + let new_back = {ctxt.back with level = new_level} in + {ctxt with back = new_back} end module type CONSENSUS = sig @@ -1267,20 +1300,18 @@ module type CONSENSUS = sig type round - val allowed_endorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + type consensus_pk + + val allowed_endorsements : t -> (consensus_pk * int) slot_map - val allowed_preendorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + val allowed_preendorsements : t -> (consensus_pk * int) slot_map val current_endorsement_power : t -> int val initialize_consensus_operation : t -> - allowed_endorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> - allowed_preendorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> + allowed_endorsements:(consensus_pk * int) slot_map -> + allowed_preendorsements:(consensus_pk * int) slot_map -> t val record_grand_parent_endorsement : @@ -1314,7 +1345,8 @@ module Consensus : and type slot := Slot_repr.t and type 'a slot_map := 'a Slot_repr.Map.t and type slot_set := Slot_repr.Set.t - and type round := Round_repr.t = struct + and type round := Round_repr.t + and type consensus_pk := consensus_pk = struct let[@inline] allowed_endorsements ctxt = ctxt.back.consensus.allowed_endorsements diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index 913ccadb75d1..2fd0c99bab0d 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -217,14 +217,18 @@ val record_non_consensus_operation_hash : t -> Operation_hash.t -> t val non_consensus_operations : t -> Operation_hash.t list +type consensus_pk = { + delegate : Signature.Public_key_hash.t; + consensus_pk : Signature.Public_key.t; + consensus_pkh : Signature.Public_key_hash.t; +} + +val consensus_pk_encoding : consensus_pk Data_encoding.t + (** [init_sampler_for_cycle ctxt cycle seed state] caches the seeded stake sampler (a.k.a. [seed, state]) for [cycle] in memory for quick access. *) val init_sampler_for_cycle : - t -> - Cycle_repr.t -> - Seed_repr.seed -> - (Signature.public_key * Signature.public_key_hash) Sampler.t -> - t tzresult + t -> Cycle_repr.t -> Seed_repr.seed -> consensus_pk Sampler.t -> t tzresult (** [sampler_for_cycle ~read ctxt cycle] returns the seeded stake sampler for [cycle]. The sampler is read in memory if @@ -233,19 +237,10 @@ val init_sampler_for_cycle : the [read] function and then cached in [ctxt] like [init_sampler_for_cycle]. *) val sampler_for_cycle : - read: - (t -> - (Seed_repr.seed - * (Signature.public_key * Signature.public_key_hash) Sampler.t) - tzresult - Lwt.t) -> + read:(t -> (Seed_repr.seed * consensus_pk Sampler.t) tzresult Lwt.t) -> t -> Cycle_repr.t -> - (t - * Seed_repr.seed - * (Signature.public_key * Signature.public_key_hash) Sampler.t) - tzresult - Lwt.t + (t * Seed_repr.seed * consensus_pk Sampler.t) tzresult Lwt.t (* The stake distribution is stored both in [t] and in the cache. It may be sufficient to only store it in the cache. *) @@ -257,6 +252,8 @@ val init_stake_distribution_for_current_cycle : module Internal_for_tests : sig val add_level : t -> int -> t + + val add_cycles : t -> int -> t end module type CONSENSUS = sig @@ -270,17 +267,17 @@ module type CONSENSUS = sig type round + type consensus_pk + (** Returns a map where each endorser's pkh is associated to the list of its endorsing slots (in decreasing order) for a given level. *) - val allowed_endorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + val allowed_endorsements : t -> (consensus_pk * int) slot_map (** Returns a map where each endorser's pkh is associated to the list of its endorsing slots (in decreasing order) for a given level. *) - val allowed_preendorsements : - t -> (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map + val allowed_preendorsements : t -> (consensus_pk * int) slot_map (** [endorsement power ctx] returns the endorsement power of the current block. *) @@ -291,10 +288,8 @@ module type CONSENSUS = sig any consensus operation. *) val initialize_consensus_operation : t -> - allowed_endorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> - allowed_preendorsements: - (Signature.Public_key.t * Signature.Public_key_hash.t * int) slot_map -> + allowed_endorsements:(consensus_pk * int) slot_map -> + allowed_preendorsements:(consensus_pk * int) slot_map -> t (** [record_grand_parent_endorsement ctx pkh] records an @@ -356,6 +351,7 @@ module Consensus : and type 'a slot_map := 'a Slot_repr.Map.t and type slot_set := Slot_repr.Set.t and type round := Round_repr.t + and type consensus_pk := consensus_pk module Tx_rollup : sig val add_message : @@ -370,3 +366,7 @@ module Sc_rollup_in_memory_inbox : sig val set_current_messages : t -> Sc_rollup_repr.t -> Context.tree -> t tzresult end + +module Migration_from_Jakarta : sig + val reset_samplers : t -> t tzresult +end diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 3ffcddec2f46..6913f64762be 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -997,11 +997,26 @@ module Cycle = struct end) (Tez_repr) - let public_key_with_ghost_hash_encoding = - Data_encoding.conv - fst - (fun x -> (x, Signature.Public_key.hash x)) - Signature.Public_key.encoding + module Migration_from_Jakarta = struct + let public_key_with_ghost_hash_encoding = + Data_encoding.conv + fst + (fun x -> (x, Signature.Public_key.hash x)) + Signature.Public_key.encoding + + module Delegate_sampler_state = + Indexed_context.Make_map + (Ghost) + (struct + let name = ["delegate_sampler_state"] + end) + (struct + type t = + (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + + let encoding = Sampler.encoding public_key_with_ghost_hash_encoding + end) + end module Delegate_sampler_state = Indexed_context.Make_map @@ -1010,10 +1025,9 @@ module Cycle = struct let name = ["delegate_sampler_state"] end) (struct - type t = - (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + type t = Raw_context.consensus_pk Sampler.t - let encoding = Sampler.encoding public_key_with_ghost_hash_encoding + let encoding = Sampler.encoding Raw_context.consensus_pk_encoding end) type unrevealed_nonce = { @@ -1768,3 +1782,8 @@ module Sc_rollup = struct end) (Bitset_and_level) end + +module Migration_from_Jakarta = struct + module Delegate_sampler_state = + Cycle.Migration_from_Jakarta.Delegate_sampler_state +end diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 27eec55eb3df..f9310a667565 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -411,8 +411,7 @@ module Total_active_stake : module Delegate_sampler_state : Indexed_data_storage with type key = Cycle_repr.t - and type value = - (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + and type value = Raw_context.consensus_pk Sampler.t and type t := Raw_context.t (** Votes *) @@ -838,3 +837,12 @@ module Sc_rollup : sig and type key = int32 and type value = Raw_level_repr.t * Bitset.t end + +module Migration_from_Jakarta : sig + module Delegate_sampler_state : + Indexed_data_storage + with type key = Cycle_repr.t + and type value = + (Signature.Public_key.t * Signature.Public_key_hash.t) Sampler.t + and type t := Raw_context.t +end diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 87c8782f4519..51d835047099 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -90,9 +90,12 @@ let get_next_baker_excluding excludes block = let {Plugin.RPC.Baking_rights.delegate = pkh; timestamp; round; _} = WithExceptions.Option.get ~loc:__LOC__ @@ List.find - (fun {Plugin.RPC.Baking_rights.delegate; _} -> + (fun {Plugin.RPC.Baking_rights.consensus_key; _} -> not - (List.mem ~equal:Signature.Public_key_hash.equal delegate excludes)) + (List.mem + ~equal:Signature.Public_key_hash.equal + consensus_key + excludes)) bakers in (pkh, round, WithExceptions.Option.to_exn ~none:(Failure "") timestamp) @@ -187,7 +190,8 @@ module Forge = struct | _ -> assert false in let predecessor_round = Fitness.round pred_fitness in - dispatch_policy policy pred >>=? fun (pkh, round, expected_timestamp) -> + dispatch_policy policy pred + >>=? fun (delegate, round, expected_timestamp) -> let timestamp = Option.value ~default:expected_timestamp timestamp in let level = Int32.succ pred.header.shell.level in Raw_level.of_int32 level |> Environment.wrap_tzresult >>?= fun raw_level -> @@ -234,7 +238,7 @@ module Forge = struct ~payload_round () in - {baker = pkh; shell; contents} + {baker = delegate; shell; contents} (* compatibility only, needed by incremental *) let contents ?(proof_of_work_nonce = default_proof_of_work_nonce) diff --git a/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml index 2b66644bcdd0..d1d0de0c7b5f 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml @@ -69,9 +69,7 @@ module Consensus_key = struct >|= Environment.wrap_tzresult let activate ctxt ~new_cycle = - Delegate_consensus_key.activate - ctxt - ~new_cycle:(Cycle_repr.of_int32_exn (Int32.of_int new_cycle)) + Delegate_consensus_key.activate ctxt ~new_cycle >|= Environment.wrap_tzresult end @@ -103,31 +101,63 @@ module Assert = struct let* active_pk = Consensus_key.active_pubkey_for_cycle ctxt delegate c in - equal_pk ~__LOC__ active_pk pk) + equal_pk ~__LOC__ active_pk.consensus_pk pk) l end +let rec add_cycles ctxt n = + if n <= 0 then return ctxt + else + let open Lwt_result_syntax in + let current_level = Raw_context.current_level ctxt in + let new_cycle = Cycle_repr.succ current_level.cycle in + let* ctxt = Consensus_key.activate ctxt ~new_cycle in + let ctxt = Raw_context.Internal_for_tests.add_cycles ctxt 1 in + add_cycles ctxt (n - 1) + let test_consensus_key_storage () = let open Lwt_result_syntax in let* ctxt, delegate = create () in let a1 = Account.new_account () in let a2 = Account.new_account () in + let preserved_cycles = Constants_storage.preserved_cycles ctxt in + let* () = Assert.equal_int ~loc:__LOC__ preserved_cycles 3 in let* () = let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh delegate.pkh + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh delegate.pkh in let* () = let* active_pk = Consensus_key.active_pubkey ctxt delegate.pkh in - Assert.equal_pk ~__LOC__ active_pk delegate.pk + Assert.equal_pk ~__LOC__ active_pk.consensus_pk delegate.pk in let* () = let* active_pk = - Consensus_key.active_pubkey_for_cycle ctxt delegate.pkh 5 + Consensus_key.active_pubkey_for_cycle ctxt delegate.pkh 3 in - Assert.equal_pk ~__LOC__ active_pk delegate.pk + Assert.equal_pk ~__LOC__ active_pk.consensus_pk delegate.pk + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 3 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) + in + let* ctxt = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt delegate.pkh 5 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> + c = Cycle_repr.of_int32_exn 4l + | _ -> false) in - let* ctxt = Consensus_key.register_update ctxt delegate.pkh 2 a1.pk in - let* ctxt = Consensus_key.register_update ctxt delegate.pkh 5 a2.pk in let* () = Assert.active_keys ~__LOC__ @@ -136,73 +166,94 @@ let test_consensus_key_storage () = [ (0, delegate.pk); (1, delegate.pk); - (2, a1.pk); - (3, a1.pk); + (2, delegate.pk); + (2, delegate.pk); + (3, delegate.pk); (4, a1.pk); - (5, a2.pk); - (6, a2.pk); + (5, a1.pk); ] in + let* ctxt = add_cycles ctxt 1 in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 2 a1.pk in - Assert.proto_error ~loc:__LOC__ err (function - | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> - c = Cycle_repr.of_int32_exn 2l - | _ -> false) + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh delegate.pkh in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 3 a1.pk in + let*! err = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in Assert.proto_error ~loc:__LOC__ err (function - | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> - c = Cycle_repr.of_int32_exn 2l + | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> + c = Cycle_repr.of_int32_exn 5l | _ -> false) in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + let*! err = Consensus_key.register_update ctxt delegate.pkh 5 a1.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> - c = Cycle_repr.of_int32_exn 2l + c = Cycle_repr.of_int32_exn 4l | _ -> false) in + let* ctxt = Consensus_key.register_update ctxt delegate.pkh 5 a2.pk in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 5 a2.pk in - Assert.proto_error ~loc:__LOC__ err (function - | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> - c = Cycle_repr.of_int32_exn 5l - | _ -> false) + Assert.active_keys + ~__LOC__ + ctxt + delegate.pkh + [ + (1, delegate.pk); + (2, delegate.pk); + (2, delegate.pk); + (3, delegate.pk); + (4, a1.pk); + (5, a2.pk); + (6, a2.pk); + ] + in + let* ctxt = add_cycles ctxt 2 in + let* () = + let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh delegate.pkh in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 6 a2.pk in + let*! err = Consensus_key.register_update ctxt delegate.pkh 7 a2.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> c = Cycle_repr.of_int32_exn 5l | _ -> false) in - let* ctxt = Consensus_key.activate ctxt ~new_cycle:1 in - let* ctxt = Consensus_key.activate ctxt ~new_cycle:2 in + let* ctxt = Consensus_key.register_update ctxt delegate.pkh 7 a1.pk in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh a1.pkh + Assert.active_keys + ~__LOC__ + ctxt + delegate.pkh + [ + (3, delegate.pk); + (4, a1.pk); + (5, a2.pk); + (6, a2.pk); + (7, a1.pk); + (8, a1.pk); + ] in - let* ctxt = Consensus_key.activate ctxt ~new_cycle:3 in + let* ctxt = add_cycles ctxt 1 in let* () = let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh a1.pkh + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a1.pkh in - let* ctxt = Consensus_key.activate ctxt ~new_cycle:4 in + let* ctxt = add_cycles ctxt 1 in let* () = let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh a1.pkh + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a2.pkh in - let* ctxt = Consensus_key.activate ctxt ~new_cycle:5 in + let* ctxt = add_cycles ctxt 1 in let* () = let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh a2.pkh + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a2.pkh in - let* ctxt = Consensus_key.activate ctxt ~new_cycle:6 in + let* ctxt = add_cycles ctxt 1 in let* () = let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh a2.pkh + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a1.pkh in return () diff --git a/tezt/_regressions/rpc/alpha.client.others.out b/tezt/_regressions/rpc/alpha.client.others.out index 6e692cbcd10a..867b16dcca1a 100644 --- a/tezt/_regressions/rpc/alpha.client.others.out +++ b/tezt/_regressions/rpc/alpha.client.others.out @@ -54,15 +54,20 @@ rpc/alpha.client.others.out ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 10, "estimated_time": "[TIMESTAMP]" } ] + "round": 10, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] ./tezos-client rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' { "level": 1, "level_position": 0, "cycle": 0, "cycle_position": 0, @@ -72,15 +77,20 @@ rpc/alpha.client.others.out [ { "level": 1, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 11, "endorsing_power": 50 }, + "first_slot": 11, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 4, "endorsing_power": 47 }, + "first_slot": 4, "endorsing_power": 47, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 46 }, + "first_slot": 2, "endorsing_power": 46, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 55 }, + "first_slot": 1, "endorsing_power": 55, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 58 } ] } ] + "first_slot": 0, "endorsing_power": 58, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] ./tezos-client rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle { "first": 1, "last": 8 } diff --git a/tezt/_regressions/rpc/alpha.light.others.out b/tezt/_regressions/rpc/alpha.light.others.out index 61402074c195..208d97cc3102 100644 --- a/tezt/_regressions/rpc/alpha.light.others.out +++ b/tezt/_regressions/rpc/alpha.light.others.out @@ -55,15 +55,20 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes ./tezos-client --mode light rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 10, "estimated_time": "[TIMESTAMP]" } ] + "round": 10, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode light rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' @@ -75,15 +80,20 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes [ { "level": 1, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 11, "endorsing_power": 50 }, + "first_slot": 11, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 4, "endorsing_power": 47 }, + "first_slot": 4, "endorsing_power": 47, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 46 }, + "first_slot": 2, "endorsing_power": 46, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 55 }, + "first_slot": 1, "endorsing_power": 55, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 58 } ] } ] + "first_slot": 0, "endorsing_power": 58, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode light rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle diff --git a/tezt/_regressions/rpc/alpha.proxy.others.out b/tezt/_regressions/rpc/alpha.proxy.others.out index 7d26b7089f03..82d17b80a52e 100644 --- a/tezt/_regressions/rpc/alpha.proxy.others.out +++ b/tezt/_regressions/rpc/alpha.proxy.others.out @@ -55,15 +55,20 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen ./tezos-client --mode proxy rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", - "round": 10, "estimated_time": "[TIMESTAMP]" } ] + "round": 10, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode proxy rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' @@ -75,15 +80,20 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen [ { "level": 1, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 11, "endorsing_power": 50 }, + "first_slot": 11, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 4, "endorsing_power": 47 }, + "first_slot": 4, "endorsing_power": 47, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 46 }, + "first_slot": 2, "endorsing_power": 46, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 55 }, + "first_slot": 1, "endorsing_power": 55, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 58 } ] } ] + "first_slot": 0, "endorsing_power": 58, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im ./tezos-client --mode proxy rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle diff --git a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out index 12f77d24e987..1f8608a42e78 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_data_dir.others.out @@ -54,15 +54,20 @@ rpc/alpha.proxy_server_data_dir.others.out ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 4, "estimated_time": "[TIMESTAMP]" } ] + "round": 4, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] ./tezos-client rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' { "level": 2, "level_position": 1, "cycle": 0, "cycle_position": 1, @@ -72,15 +77,20 @@ rpc/alpha.proxy_server_data_dir.others.out [ { "level": 2, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 10, "endorsing_power": 50 }, + "first_slot": 10, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 3, "endorsing_power": 50 }, + "first_slot": 3, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 65 }, + "first_slot": 2, "endorsing_power": 65, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 50 }, + "first_slot": 1, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 41 } ] } ] + "first_slot": 0, "endorsing_power": 41, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] ./tezos-client rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle { "first": 1, "last": 8 } diff --git a/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out b/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out index 869343b796cc..f660bc2c4f30 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out +++ b/tezt/_regressions/rpc/alpha.proxy_server_rpc.others.out @@ -54,15 +54,20 @@ rpc/alpha.proxy_server_rpc.others.out ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 0, "estimated_time": "[TIMESTAMP]" }, + "round": 0, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 1, "estimated_time": "[TIMESTAMP]" }, + "round": 1, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 2, "estimated_time": "[TIMESTAMP]" }, + "round": 2, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 3, "estimated_time": "[TIMESTAMP]" }, + "round": 3, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", - "round": 4, "estimated_time": "[TIMESTAMP]" } ] + "round": 4, "estimated_time": "[TIMESTAMP]", + "consensus_key": "[PUBLIC_KEY_HASH]" } ] ./tezos-client rpc get '/chains/main/blocks/head/helpers/current_level?offset=0' { "level": 2, "level_position": 1, "cycle": 0, "cycle_position": 1, @@ -72,15 +77,20 @@ rpc/alpha.proxy_server_rpc.others.out [ { "level": 2, "delegates": [ { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 10, "endorsing_power": 50 }, + "first_slot": 10, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 3, "endorsing_power": 50 }, + "first_slot": 3, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 2, "endorsing_power": 65 }, + "first_slot": 2, "endorsing_power": 65, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 1, "endorsing_power": 50 }, + "first_slot": 1, "endorsing_power": 50, + "consensus_key": "[PUBLIC_KEY_HASH]" }, { "delegate": "[PUBLIC_KEY_HASH]", - "first_slot": 0, "endorsing_power": 41 } ] } ] + "first_slot": 0, "endorsing_power": 41, + "consensus_key": "[PUBLIC_KEY_HASH]" } ] } ] ./tezos-client rpc get /chains/main/blocks/head/helpers/levels_in_current_cycle { "first": 1, "last": 8 } -- GitLab From 3a7f41ace7af6e22657706eb0cf971427a83cc00 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 25/35] Proto: do not allow two delegates to use the same consensus key --- .../lib_protocol/delegate_consensus_key.ml | 36 +++++- .../lib_protocol/delegate_consensus_key.mli | 1 + src/proto_alpha/lib_protocol/storage.ml | 8 ++ src/proto_alpha/lib_protocol/storage.mli | 6 + .../test/unit/test_consensus_key.ml | 108 ++++++++++-------- 5 files changed, 108 insertions(+), 51 deletions(-) diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml index 37c7e48af185..53646ff4e493 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.ml @@ -26,6 +26,7 @@ type error += | Invalid_consensus_key_update_cycle of Cycle_repr.t | Invalid_consensus_key_update_noop of Cycle_repr.t + | Invalid_consensus_key_update_active let () = register_error_kind @@ -55,7 +56,20 @@ let () = cycle) Data_encoding.(obj1 (req "cycle" Cycle_repr.encoding)) (function Invalid_consensus_key_update_noop c -> Some c | _ -> None) - (fun c -> Invalid_consensus_key_update_noop c) + (fun c -> Invalid_consensus_key_update_noop c) ; + register_error_kind + `Permanent + ~id:"delegate.consensus_key.active" + ~title:"Active consensus key" + ~description: + "The delegate consensus key is already used by another delegate" + ~pp:(fun ppf () -> + Format.fprintf + ppf + "The delegate consensus key is already used by another delegate") + Data_encoding.empty + (function Invalid_consensus_key_update_active -> Some () | _ -> None) + (fun () -> Invalid_consensus_key_update_active) type pk = Raw_context.consensus_pk = { delegate : Signature.Public_key_hash.t; @@ -86,7 +100,20 @@ let pp ppf {delegate; consensus_pkh} = consensus_pkh ; Format.fprintf ppf "@]" +let check_inactive ctxt pkh = + let open Lwt_tzresult_syntax in + let*! is_active = Storage.Consensus_keys.mem ctxt pkh in + fail_when is_active Invalid_consensus_key_update_active + +let set_inactive = Storage.Consensus_keys.remove + +let set_active = Storage.Consensus_keys.add + let init ctxt delegate pk = + let open Lwt_tzresult_syntax in + let pkh = Signature.Public_key.hash pk in + let* () = check_inactive ctxt pkh in + let*! ctxt = set_active ctxt pkh in Storage.Contract.Consensus_key.init ctxt (Contract_repr.Implicit delegate) pk let active_pubkey ctxt delegate = @@ -162,6 +189,13 @@ let register_update ctxt delegate cycle pk = Signature.Public_key.(pk = active_pubkey) (Invalid_consensus_key_update_noop cycle) in + let pkh = Signature.Public_key.hash pk in + let* () = check_inactive ctxt pkh in + let*! ctxt = set_active ctxt pkh in + let* {consensus_pkh = old_pkh; _} = + active_pubkey_for_cycle ctxt delegate cycle + in + let*! ctxt = set_inactive ctxt old_pkh in let*! ctxt = Storage.Contract.Pending_consensus_keys.add (ctxt, Contract_repr.Implicit delegate) diff --git a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli index a8d4ea02aafc..f9e89bbae48c 100644 --- a/src/proto_alpha/lib_protocol/delegate_consensus_key.mli +++ b/src/proto_alpha/lib_protocol/delegate_consensus_key.mli @@ -26,6 +26,7 @@ type error += | Invalid_consensus_key_update_cycle of Cycle_repr.t | Invalid_consensus_key_update_noop of Cycle_repr.t + | Invalid_consensus_key_update_active (** The public key of a consensus key and the associated delegate. *) type pk = Raw_context.consensus_pk = { diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 6913f64762be..66292f67b43f 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -937,6 +937,14 @@ module Delegates = end)) (Public_key_hash_index) +module Consensus_keys = + Make_data_set_storage + (Make_subcontext (Registered) (Raw_context) + (struct + let name = ["consensus_keys"] + end)) + (Public_key_hash_index) + (** Per cycle storage *) type slashed_level = {for_double_endorsing : bool; for_double_baking : bool} diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index f9310a667565..6f216f99a6ca 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -359,6 +359,12 @@ module Delegates : with type t := Raw_context.t and type elt = Signature.Public_key_hash.t +(** Set of all active consensus keys in cycle `current + preserved_cycles + 1` *) +module Consensus_keys : + Data_set_storage + with type t := Raw_context.t + and type elt = Signature.Public_key_hash.t + type slashed_level = {for_double_endorsing : bool; for_double_baking : bool} (** Set used to avoid slashing multiple times the same event *) diff --git a/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml index d1d0de0c7b5f..8df0ca5fabe3 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_consensus_key.ml @@ -35,12 +35,14 @@ open Protocol let create () = let open Lwt_result_syntax in - let accounts = Account.generate_accounts 1 in - let account = - match accounts with [(account, _, _)] -> account | _ -> assert false + let accounts = Account.generate_accounts 2 in + let a1, a2 = + match accounts with + | [(a1, _, _); (a2, _, _)] -> (a1, a2) + | _ -> assert false in let* ctxt = Block.alpha_context accounts in - return (Alpha_context.Internal_for_tests.to_raw ctxt, account) + return (Alpha_context.Internal_for_tests.to_raw ctxt, a1, a2) module Consensus_key = struct let active_key ctxt pkh = @@ -117,42 +119,53 @@ let rec add_cycles ctxt n = let test_consensus_key_storage () = let open Lwt_result_syntax in - let* ctxt, delegate = create () in + let* ctxt, del1, del2 = create () in let a1 = Account.new_account () in let a2 = Account.new_account () in let preserved_cycles = Constants_storage.preserved_cycles ctxt in let* () = Assert.equal_int ~loc:__LOC__ preserved_cycles 3 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh delegate.pkh + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh del1.pkh in let* () = - let* active_pk = Consensus_key.active_pubkey ctxt delegate.pkh in - Assert.equal_pk ~__LOC__ active_pk.consensus_pk delegate.pk + let* active_pk = Consensus_key.active_pubkey ctxt del1.pkh in + Assert.equal_pk ~__LOC__ active_pk.consensus_pk del1.pk in let* () = - let* active_pk = - Consensus_key.active_pubkey_for_cycle ctxt delegate.pkh 3 - in - Assert.equal_pk ~__LOC__ active_pk.consensus_pk delegate.pk + let* active_pk = Consensus_key.active_pubkey_for_cycle ctxt del1.pkh 3 in + Assert.equal_pk ~__LOC__ active_pk.consensus_pk del1.pk in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 3 a1.pk in + let*! err = Consensus_key.register_update ctxt del1.pkh 4 del2.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_active -> true + | _ -> false) + in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 3 a1.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> c = Cycle_repr.of_int32_exn 4l | _ -> false) in - let* ctxt = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + let* ctxt = Consensus_key.register_update ctxt del1.pkh 4 a1.pk in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + let*! err = Consensus_key.register_update ctxt del1.pkh 4 a1.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> c = Cycle_repr.of_int32_exn 4l | _ -> false) in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 5 a1.pk in + let*! err = Consensus_key.register_update ctxt del2.pkh 4 a1.pk in + Assert.proto_error ~loc:__LOC__ err (function + | Delegate_consensus_key.Invalid_consensus_key_update_active -> true + | _ -> false) + in + let* ctxt = Consensus_key.register_update ctxt del2.pkh 4 del1.pk in + let* () = + let*! err = Consensus_key.register_update ctxt del1.pkh 5 a1.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> c = Cycle_repr.of_int32_exn 4l @@ -162,47 +175,49 @@ let test_consensus_key_storage () = Assert.active_keys ~__LOC__ ctxt - delegate.pkh + del1.pkh [ - (0, delegate.pk); - (1, delegate.pk); - (2, delegate.pk); - (2, delegate.pk); - (3, delegate.pk); + (0, del1.pk); + (1, del1.pk); + (2, del1.pk); + (2, del1.pk); + (3, del1.pk); (4, a1.pk); (5, a1.pk); ] in let* ctxt = add_cycles ctxt 1 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh delegate.pkh + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh del1.pkh in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 4 a1.pk in + let*! err = Consensus_key.register_update ctxt del1.pkh 4 a1.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_cycle c -> c = Cycle_repr.of_int32_exn 5l | _ -> false) in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 5 a1.pk in + let*! err = Consensus_key.register_update ctxt del1.pkh 5 a1.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> c = Cycle_repr.of_int32_exn 4l | _ -> false) in - let* ctxt = Consensus_key.register_update ctxt delegate.pkh 5 a2.pk in + let* ctxt = Consensus_key.register_update ctxt del1.pkh 5 a2.pk in + let* ctxt = Consensus_key.register_update ctxt del2.pkh 5 a1.pk in + let* ctxt = Consensus_key.register_update ctxt del2.pkh 5 del2.pk in let* () = Assert.active_keys ~__LOC__ ctxt - delegate.pkh + del1.pkh [ - (1, delegate.pk); - (2, delegate.pk); - (2, delegate.pk); - (3, delegate.pk); + (1, del1.pk); + (2, del1.pk); + (2, del1.pk); + (3, del1.pk); (4, a1.pk); (5, a2.pk); (6, a2.pk); @@ -210,49 +225,42 @@ let test_consensus_key_storage () = in let* ctxt = add_cycles ctxt 2 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in - Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh delegate.pkh + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in + Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh del1.pkh in let* () = - let*! err = Consensus_key.register_update ctxt delegate.pkh 7 a2.pk in + let*! err = Consensus_key.register_update ctxt del1.pkh 7 a2.pk in Assert.proto_error ~loc:__LOC__ err (function | Delegate_consensus_key.Invalid_consensus_key_update_noop c -> c = Cycle_repr.of_int32_exn 5l | _ -> false) in - let* ctxt = Consensus_key.register_update ctxt delegate.pkh 7 a1.pk in + let* ctxt = Consensus_key.register_update ctxt del1.pkh 7 a1.pk in let* () = Assert.active_keys ~__LOC__ ctxt - delegate.pkh - [ - (3, delegate.pk); - (4, a1.pk); - (5, a2.pk); - (6, a2.pk); - (7, a1.pk); - (8, a1.pk); - ] + del1.pkh + [(3, del1.pk); (4, a1.pk); (5, a2.pk); (6, a2.pk); (7, a1.pk); (8, a1.pk)] in let* ctxt = add_cycles ctxt 1 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a1.pkh in let* ctxt = add_cycles ctxt 1 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a2.pkh in let* ctxt = add_cycles ctxt 1 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a2.pkh in let* ctxt = add_cycles ctxt 1 in let* () = - let* active_pkh = Consensus_key.active_key ctxt delegate.pkh in + let* active_pkh = Consensus_key.active_key ctxt del1.pkh in Assert.equal_pkh ~__LOC__ active_pkh.consensus_pkh a1.pkh in return () -- GitLab From f083c77306d6901b29290e7b1940280b8c342d9b Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 26/35] Proto/Baker: display consensus key and delegate in logs --- .../lib_delegate/baking_actions.ml | 33 +++++----- .../lib_delegate/baking_actions.mli | 16 ++--- .../lib_delegate/baking_commands.ml | 4 +- src/proto_alpha/lib_delegate/baking_events.ml | 62 ++++++++--------- src/proto_alpha/lib_delegate/baking_lib.ml | 10 ++- src/proto_alpha/lib_delegate/baking_lib.mli | 8 +-- src/proto_alpha/lib_delegate/baking_nonces.ml | 2 +- .../lib_delegate/baking_nonces.mli | 2 +- .../lib_delegate/baking_scheduling.mli | 8 ++- src/proto_alpha/lib_delegate/baking_state.ml | 66 ++++++++++++------- src/proto_alpha/lib_delegate/baking_state.mli | 31 +++++---- .../lib_delegate/client_daemon.mli | 2 +- .../lib_delegate/state_transitions.ml | 8 +-- .../lib_delegate/state_transitions.mli | 10 +-- .../test/mockup_simulator/mockup_simulator.ml | 8 +-- .../mockup_simulator/mockup_simulator.mli | 2 +- 16 files changed, 152 insertions(+), 120 deletions(-) diff --git a/src/proto_alpha/lib_delegate/baking_actions.ml b/src/proto_alpha/lib_delegate/baking_actions.ml index 81dfefc9f74b..5ac946621647 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.ml +++ b/src/proto_alpha/lib_delegate/baking_actions.ml @@ -117,7 +117,7 @@ type block_kind = type block_to_bake = { predecessor : block_info; round : Round.t; - delegate : Baking_state.delegate; + delegate : Baking_state.consensus_key_and_delegate; kind : block_kind; } @@ -125,11 +125,11 @@ type action = | Do_nothing | Inject_block of {block_to_bake : block_to_bake; updated_state : state} | Inject_preendorsements of { - preendorsements : (delegate * consensus_content) list; + preendorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Inject_endorsements of { - endorsements : (delegate * consensus_content) list; + endorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Update_to_level of level_update @@ -214,7 +214,9 @@ let sign_block_header state proposer unsigned_block_header = return {Block_header.shell; protocol_data = {contents; signature}} let inject_block ~state_recorder state block_to_bake ~updated_state = - let {predecessor; round; delegate; kind} = block_to_bake in + let {predecessor; round; delegate = (consensus_key, _) as delegate; kind} = + block_to_bake + in let cctxt = state.global_state.cctxt in let chain_id = state.global_state.chain_id in let simulation_mode = state.global_state.validation_mode in @@ -260,7 +262,7 @@ let inject_block ~state_recorder state block_to_bake ~updated_state = >>=? fun injection_level -> generate_seed_nonce_hash state.global_state.config.Baking_configuration.nonce - delegate + consensus_key injection_level >>=? fun seed_nonce_opt -> let seed_nonce_hash = Option.map fst seed_nonce_opt in @@ -292,7 +294,7 @@ let inject_block ~state_recorder state block_to_bake ~updated_state = simulation_kind state.global_state.constants.parametric >>=? fun {unsigned_block_header; operations} -> - sign_block_header state delegate unsigned_block_header + sign_block_header state consensus_key unsigned_block_header >>=? fun signed_block_header -> (match seed_nonce_opt with | None -> @@ -325,7 +327,7 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state (* Don't parallelize signatures: the signer might not be able to handle concurrent requests *) List.filter_map_es - (fun (delegate, consensus_content) -> + (fun (((consensus_key, _) as delegate), consensus_content) -> Events.(emit signing_preendorsement delegate) >>= fun () -> let shell = { @@ -340,11 +342,10 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state let block_location = Baking_files.resolve_location ~chain_id `Highwatermarks in - let delegate = delegate.public_key_hash in Baking_highwatermarks.may_sign_preendorsement cctxt block_location - ~delegate + ~delegate:consensus_key.public_key_hash ~level ~round >>=? function @@ -352,7 +353,7 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state Baking_highwatermarks.record_preendorsement cctxt block_location - ~delegate + ~delegate:consensus_key.public_key_hash ~level ~round >>=? fun () -> return_true @@ -367,7 +368,8 @@ let inject_preendorsements ~state_recorder state ~preendorsements ~updated_state unsigned_operation in (* TODO: do we want to reload the sk uri or not ? *) - Client_keys.get_key cctxt delegate.public_key_hash >>=? fun (_, _, sk) -> + Client_keys.get_key cctxt consensus_key.public_key_hash + >>=? fun (_, _, sk) -> Client_keys.sign cctxt ~watermark sk unsigned_operation_bytes else fail (Baking_highwatermarks.Block_previously_preendorsed {round; level})) @@ -412,7 +414,7 @@ let sign_endorsements state endorsements = (* Don't parallelize signatures: the signer might not be able to handle concurrent requests *) List.filter_map_es - (fun (delegate, consensus_content) -> + (fun (((consensus_key, _) as delegate), consensus_content) -> Events.(emit signing_endorsement delegate) >>= fun () -> let shell = { @@ -433,7 +435,7 @@ let sign_endorsements state endorsements = Baking_highwatermarks.may_sign_endorsement cctxt block_location - ~delegate:delegate.public_key_hash + ~delegate:consensus_key.public_key_hash ~level ~round >>=? function @@ -441,7 +443,7 @@ let sign_endorsements state endorsements = Baking_highwatermarks.record_endorsement cctxt block_location - ~delegate:delegate.public_key_hash + ~delegate:consensus_key.public_key_hash ~level ~round >>=? fun () -> return_true @@ -456,7 +458,8 @@ let sign_endorsements state endorsements = unsigned_operation in (* TODO: do we want to reload the sk uri or not ? *) - Client_keys.get_key cctxt delegate.public_key_hash >>=? fun (_, _, sk) -> + Client_keys.get_key cctxt consensus_key.public_key_hash + >>=? fun (_, _, sk) -> Client_keys.sign cctxt ~watermark sk unsigned_operation_bytes else fail (Baking_highwatermarks.Block_previously_preendorsed {round; level})) diff --git a/src/proto_alpha/lib_delegate/baking_actions.mli b/src/proto_alpha/lib_delegate/baking_actions.mli index 83789e84892f..da181cda8c32 100644 --- a/src/proto_alpha/lib_delegate/baking_actions.mli +++ b/src/proto_alpha/lib_delegate/baking_actions.mli @@ -39,7 +39,7 @@ type block_kind = type block_to_bake = { predecessor : block_info; round : Round.t; - delegate : delegate; + delegate : consensus_key_and_delegate; kind : block_kind; } @@ -47,11 +47,11 @@ type action = | Do_nothing | Inject_block of {block_to_bake : block_to_bake; updated_state : state} | Inject_preendorsements of { - preendorsements : (delegate * consensus_content) list; + preendorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Inject_endorsements of { - endorsements : (delegate * consensus_content) list; + endorsements : (consensus_key_and_delegate * consensus_content) list; updated_state : state; } | Update_to_level of level_update @@ -75,7 +75,7 @@ type t = action val generate_seed_nonce_hash : Baking_configuration.nonce_config -> - delegate -> + consensus_key -> Level.t -> (Nonce_hash.t * Nonce.t) option tzresult Lwt.t @@ -89,19 +89,19 @@ val inject_block : val inject_preendorsements : state_recorder:(new_state:state -> unit tzresult Lwt.t) -> state -> - preendorsements:(delegate * consensus_content) list -> + preendorsements:(consensus_key_and_delegate * consensus_content) list -> updated_state:state -> state tzresult Lwt.t val sign_endorsements : state -> - (delegate * consensus_content) list -> - (delegate * packed_operation) list tzresult Lwt.t + (consensus_key_and_delegate * consensus_content) list -> + (consensus_key_and_delegate * packed_operation) list tzresult Lwt.t val inject_endorsements : state_recorder:(new_state:state -> unit tzresult Lwt.t) -> state -> - endorsements:(delegate * consensus_content) list -> + endorsements:(consensus_key_and_delegate * consensus_content) list -> updated_state:state -> state tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_commands.ml b/src/proto_alpha/lib_delegate/baking_commands.ml index 78d6ec9e61a1..eae0f2d4beda 100644 --- a/src/proto_alpha/lib_delegate/baking_commands.ml +++ b/src/proto_alpha/lib_delegate/baking_commands.ml @@ -202,7 +202,9 @@ let sources_param = Clic.seq_of_param (Client_keys.Public_key_hash.source_param ~name:"baker" - ~desc:"name of the delegate owning the endorsement right") + ~desc: + "name of the delegate owning the endorsement/baking right or name of \ + the consensus key signing on the delegate's behalf") let delegate_commands () : Protocol_client_context.full Clic.command list = let open Clic in diff --git a/src/proto_alpha/lib_delegate/baking_events.ml b/src/proto_alpha/lib_delegate/baking_events.ml index d0e2f9a35f2e..dd8506d8c7fc 100644 --- a/src/proto_alpha/lib_delegate/baking_events.ml +++ b/src/proto_alpha/lib_delegate/baking_events.ml @@ -87,8 +87,8 @@ module State_transitions = struct ("level", Data_encoding.int32) ~pp3:Round.pp ("next_round", Round.encoding) - ~pp4:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp4:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let new_head_while_waiting_for_qc = declare_0 @@ -241,8 +241,8 @@ module State_transitions = struct ~name:"proposing_fresh_block" ~level:Info ~msg:"proposing fresh block for {delegate} at round {round}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Round.pp ("round", Round.encoding) @@ -384,8 +384,8 @@ module Scheduling = struct ("round", Round.encoding) ~pp3:Timestamp.pp ("timestamp", Timestamp.encoding) - ~pp4:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp4:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let waiting_end_of_round = declare_3 @@ -499,8 +499,8 @@ module Actions = struct ~name:"skipping_preendorsement" ~level:Error ~msg:"skipping preendorsement for {delegate} -- {trace}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Error_monad.pp_print_trace ("trace", Error_monad.trace_encoding) @@ -510,8 +510,8 @@ module Actions = struct ~name:"skipping_endorsement" ~level:Error ~msg:"skipping endorsement for {delegate} -- {trace}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Error_monad.pp_print_trace ("trace", Error_monad.trace_encoding) @@ -521,8 +521,8 @@ module Actions = struct ~name:"failed_to_inject_preendorsement" ~level:Error ~msg:"failed to inject preendorsement for {delegate} -- {trace}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) ~pp2:Error_monad.pp_print_trace ("trace", Error_monad.trace_encoding) @@ -545,8 +545,8 @@ module Actions = struct ~msg:"injected preendorsement {ophash} for {delegate}" ~pp1:Operation_hash.pp ("ophash", Operation_hash.encoding) - ~pp2:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp2:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let endorsement_injected = declare_2 @@ -556,8 +556,8 @@ module Actions = struct ~msg:"injected endorsement {ophash} for {delegate}" ~pp1:Operation_hash.pp ("ophash", Operation_hash.encoding) - ~pp2:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp2:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let synchronizing_round = declare_1 @@ -573,29 +573,26 @@ module Actions = struct ~section ~name:"forging_block" ~level:Info - ~msg: - "forging block at level {level}, round {round} for delegate {delegate}" + ~msg:"forging block at level {level}, round {round} for {delegate}" ~pp1:pp_int32 ~pp2:Round.pp - ~pp3:Baking_state.pp_delegate + ~pp3:Baking_state.pp_consensus_key_and_delegate ("level", Data_encoding.int32) ("round", Round.encoding) - ("delegate", Baking_state.delegate_encoding) + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let injecting_block = declare_3 ~section ~name:"injecting_block" ~level:Debug - ~msg: - "injecting block at level {level}, round {round} for delegate \ - {delegate}" + ~msg:"injecting block at level {level}, round {round} for {delegate}" ~pp1:pp_int32 ~pp2:Round.pp - ~pp3:Baking_state.pp_delegate + ~pp3:Baking_state.pp_consensus_key_and_delegate ("level", Data_encoding.int32) ("round", Round.encoding) - ("delegate", Baking_state.delegate_encoding) + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let block_injected = declare_4 @@ -603,16 +600,15 @@ module Actions = struct ~name:"block_injected" ~level:Notice ~msg: - "block {block} at level {level}, round {round} injected for delegate \ - {delegate}" + "block {block} at level {level}, round {round} injected for {delegate}" ~pp1:Block_hash.pp ~pp2:pp_int32 ~pp3:Round.pp - ~pp4:Baking_state.pp_delegate + ~pp4:Baking_state.pp_consensus_key_and_delegate ("block", Block_hash.encoding) ("level", Data_encoding.int32) ("round", Round.encoding) - ("delegate", Baking_state.delegate_encoding) + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let signing_preendorsement = declare_1 @@ -620,8 +616,8 @@ module Actions = struct ~name:"signing_preendorsement" ~level:Info ~msg:"signing preendorsement for {delegate}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let signing_endorsement = declare_1 @@ -629,8 +625,8 @@ module Actions = struct ~name:"signing_endorsement" ~level:Info ~msg:"signing endorsement for {delegate}" - ~pp1:Baking_state.pp_delegate - ("delegate", Baking_state.delegate_encoding) + ~pp1:Baking_state.pp_consensus_key_and_delegate + ("delegate", Baking_state.consensus_key_and_delegate_encoding) let invalid_json_file = declare_1 diff --git a/src/proto_alpha/lib_delegate/baking_lib.ml b/src/proto_alpha/lib_delegate/baking_lib.ml index 7ef044310a5f..7ec3ae000cd5 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.ml +++ b/src/proto_alpha/lib_delegate/baking_lib.ml @@ -80,7 +80,10 @@ let preendorse (cctxt : Protocol_client_context.full) ?(force = false) delegates let*! () = cctxt#message "@[Preendorsing for:@ %a@]" - Format.(pp_print_list ~pp_sep:pp_print_space Baking_state.pp_delegate) + Format.( + pp_print_list + ~pp_sep:pp_print_space + Baking_state.pp_consensus_key_and_delegate) (List.map fst consensus_list) in let state_recorder ~new_state = @@ -117,7 +120,10 @@ let endorse (cctxt : Protocol_client_context.full) ?(force = false) delegates = let*! () = cctxt#message "@[Endorsing for:@ %a@]" - Format.(pp_print_list ~pp_sep:pp_print_space Baking_state.pp_delegate) + Format.( + pp_print_list + ~pp_sep:pp_print_space + Baking_state.pp_consensus_key_and_delegate) (List.map fst consensus_list) in let state_recorder ~new_state = diff --git a/src/proto_alpha/lib_delegate/baking_lib.mli b/src/proto_alpha/lib_delegate/baking_lib.mli index 547822f25d1a..e37f01ac7bb5 100644 --- a/src/proto_alpha/lib_delegate/baking_lib.mli +++ b/src/proto_alpha/lib_delegate/baking_lib.mli @@ -37,19 +37,19 @@ val bake : ?extra_operations:Baking_configuration.Operations_source.t -> ?monitor_node_mempool:bool -> ?context_path:string -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t val preendorse : Protocol_client_context.full -> ?force:bool -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t val endorse : Protocol_client_context.full -> ?force:bool -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t val propose : @@ -61,5 +61,5 @@ val propose : ?minimal_timestamp:bool -> ?extra_operations:Baking_configuration.Operations_source.t -> ?context_path:string -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_nonces.ml b/src/proto_alpha/lib_delegate/baking_nonces.ml index 170bedfdb697..ce893c0a8b1c 100644 --- a/src/proto_alpha/lib_delegate/baking_nonces.ml +++ b/src/proto_alpha/lib_delegate/baking_nonces.ml @@ -199,7 +199,7 @@ let get_unrevealed_nonces ({cctxt; chain; _} as state) nonces = (* Nonce creation *) let generate_seed_nonce (nonce_config : Baking_configuration.nonce_config) - (delegate : Baking_state.delegate) level = + (delegate : Baking_state.consensus_key) level = (match nonce_config with | Deterministic -> let data = Data_encoding.Binary.to_bytes_exn Raw_level.encoding level in diff --git a/src/proto_alpha/lib_delegate/baking_nonces.mli b/src/proto_alpha/lib_delegate/baking_nonces.mli index baa6ebf23f18..5151c8909f24 100644 --- a/src/proto_alpha/lib_delegate/baking_nonces.mli +++ b/src/proto_alpha/lib_delegate/baking_nonces.mli @@ -84,7 +84,7 @@ val get_unrevealed_nonces : val generate_seed_nonce : Baking_configuration.nonce_config -> - Baking_state.delegate -> + Baking_state.consensus_key -> Raw_level.t -> (Nonce_hash.t * Nonce.t) tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_scheduling.mli b/src/proto_alpha/lib_delegate/baking_scheduling.mli index a641b7f892fc..83e167cfcf34 100644 --- a/src/proto_alpha/lib_delegate/baking_scheduling.mli +++ b/src/proto_alpha/lib_delegate/baking_scheduling.mli @@ -44,7 +44,9 @@ val wait_next_event : val compute_next_round_time : state -> (Time.Protocol.t * Round.t) option val first_potential_round_at_next_level : - state -> earliest_round:Round.t -> (Round.t * delegate) option + state -> + earliest_round:Round.t -> + (Round.t * consensus_key_and_delegate) option val compute_next_potential_baking_time_at_next_level : state -> (Time.Protocol.t * Round.t) option Lwt.t @@ -58,7 +60,7 @@ val create_initial_state : Baking_configuration.t -> Operation_worker.t -> current_proposal:proposal -> - delegate trace -> + consensus_key list -> state tzresult Lwt.t val compute_bootstrap_event : state -> event tzresult @@ -79,5 +81,5 @@ val run : ?on_error:(tztrace -> unit tzresult Lwt.t) -> chain:Chain_services.chain -> Baking_configuration.t -> - delegate trace -> + consensus_key list -> unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_delegate/baking_state.ml b/src/proto_alpha/lib_delegate/baking_state.ml index 3753157f3395..0d07cba1b012 100644 --- a/src/proto_alpha/lib_delegate/baking_state.ml +++ b/src/proto_alpha/lib_delegate/baking_state.ml @@ -26,16 +26,16 @@ open Protocol open Alpha_context -(** A delegate (aka, a validator) is identified by its alias name, its +(** A consenssus key (aka, a validator) is identified by its alias name, its public key, its public key hash, and its secret key. *) -type delegate = { +type consensus_key = { alias : string option; public_key : Signature.Public_key.t; public_key_hash : Signature.Public_key_hash.t; secret_key_uri : Client_keys.sk_uri; } -let delegate_encoding = +let consensus_key_encoding = let open Data_encoding in conv (fun {alias; public_key; public_key_hash; secret_key_uri} -> @@ -59,7 +59,7 @@ let delegate_encoding = (req "public_key_hash" Signature.Public_key_hash.encoding) (req "secret_key_uri" string)) -let pp_delegate fmt {alias; public_key_hash; _} = +let pp_consensus_key fmt {alias; public_key_hash; _} = match alias with | None -> Format.fprintf fmt "%a" Signature.Public_key_hash.pp public_key_hash | Some alias -> @@ -70,6 +70,26 @@ let pp_delegate fmt {alias; public_key_hash; _} = Signature.Public_key_hash.pp public_key_hash +type consensus_key_and_delegate = consensus_key * Signature.Public_key_hash.t + +let consensus_key_and_delegate_encoding = + let open Data_encoding in + merge_objs + consensus_key_encoding + (obj1 (req "delegate" Signature.Public_key_hash.encoding)) + +let pp_consensus_key_and_delegate fmt (consensus_key, delegate) = + if Signature.Public_key_hash.equal consensus_key.public_key_hash delegate then + pp_consensus_key fmt consensus_key + else + Format.fprintf + fmt + "%a@,on behalf of %a" + pp_consensus_key + consensus_key + Signature.Public_key_hash.pp + delegate + type validation_mode = Node | Local of Abstract_context_index.t type prequorum = { @@ -96,7 +116,7 @@ type block_info = { type cache = { known_timestamps : Timestamp.time Baking_cache.Timestamp_of_round_cache.t; round_timestamps : - (Timestamp.time * Round.t * delegate) + (Timestamp.time * Round.t * consensus_key_and_delegate) Baking_cache.Round_timestamp_interval_cache.t; } @@ -116,7 +136,7 @@ type global_state = { (* the validation mode used by the baker*) validation_mode : validation_mode; (* the delegates on behalf of which the baker is running *) - delegates : delegate list; + delegates : consensus_key list; cache : cache; } @@ -215,11 +235,7 @@ module SlotMap : Map.S with type key = Slot.t = Map.Make (Slot) list of slots (i.e., a list of position indexes in the slot map, in other words the list of rounds when it will be the proposer), and its endorsing power. *) -type endorsing_slot = { - delegate : Signature.Public_key_hash.t; - slots : Slot.t list; - endorsing_power : int; -} +type endorsing_slot = {slots : Slot.t list; endorsing_power : int} (* FIXME: determine if the slot map should contain all slots or just the first one *) @@ -228,7 +244,7 @@ type endorsing_slot = { type delegate_slots = { (* be careful not to duplicate endorsing slots with different slots keys: always use the first slot in the slots list *) - own_delegate_slots : (delegate * endorsing_slot) SlotMap.t; + own_delegate_slots : (consensus_key_and_delegate * endorsing_slot) SlotMap.t; all_delegate_slots : endorsing_slot SlotMap.t; all_slots_by_round : Slot.t array; } @@ -575,7 +591,7 @@ let may_load_endorsable_data state = module DelegateSet = struct include Set.Make (struct - type t = delegate + type t = consensus_key let compare {public_key_hash = pkh; _} {public_key_hash = pkh'; _} = Signature.Public_key_hash.compare pkh pkh' @@ -605,10 +621,8 @@ let compute_delegate_slots (cctxt : Protocol_client_context.full) let own_delegate_slots, all_delegate_slots = List.fold_left (fun (own_map, all_map) slot -> - let {Plugin.RPC.Validators.consensus_key = delegate; slots; _} = slot in - let endorsing_slot = - {endorsing_power = List.length slots; delegate; slots} - in + let {Plugin.RPC.Validators.consensus_key; delegate; slots; _} = slot in + let endorsing_slot = {endorsing_power = List.length slots; slots} in let all_map = List.fold_left (fun all_map slot -> SlotMap.add slot endorsing_slot all_map) @@ -616,11 +630,14 @@ let compute_delegate_slots (cctxt : Protocol_client_context.full) slots in let own_map = - match DelegateSet.find_pkh delegate own_delegates with - | Some delegate -> + match DelegateSet.find_pkh consensus_key own_delegates with + | Some consensus_key -> List.fold_left (fun own_map slot -> - SlotMap.add slot (delegate, endorsing_slot) own_map) + SlotMap.add + slot + ((consensus_key, delegate), endorsing_slot) + own_map) own_map slots | None -> own_map @@ -658,7 +675,7 @@ let pp_global_state fmt {chain_id; config; validation_mode; delegates; _} = config pp_validation_mode validation_mode - Format.(pp_print_list pp_delegate) + Format.(pp_print_list pp_consensus_key) delegates let pp_option pp fmt = function @@ -739,14 +756,15 @@ let pp_elected_block fmt {proposal; endorsement_qc} = proposal.block (List.length endorsement_qc) -let pp_endorsing_slot fmt (delegate, {delegate = _; slots; endorsing_power}) = +let pp_endorsing_slot fmt (consensus_key_and_delegate, {slots; endorsing_power}) + = Format.fprintf fmt "slots: @[[%a]@],@ delegate: %a,@ endorsing_power: %d" Format.(pp_print_list ~pp_sep:pp_print_space Slot.pp) slots - pp_delegate - delegate + pp_consensus_key_and_delegate + consensus_key_and_delegate endorsing_power let pp_delegate_slots fmt {own_delegate_slots; _} = diff --git a/src/proto_alpha/lib_delegate/baking_state.mli b/src/proto_alpha/lib_delegate/baking_state.mli index 45f325e509d1..3650fc322708 100644 --- a/src/proto_alpha/lib_delegate/baking_state.mli +++ b/src/proto_alpha/lib_delegate/baking_state.mli @@ -26,16 +26,24 @@ open Protocol open Alpha_context -type delegate = { +type consensus_key = { alias : string option; public_key : Signature.public_key; public_key_hash : Signature.public_key_hash; secret_key_uri : Client_keys.sk_uri; } -val delegate_encoding : delegate Data_encoding.t +val consensus_key_encoding : consensus_key Data_encoding.t -val pp_delegate : Format.formatter -> delegate -> unit +val pp_consensus_key : Format.formatter -> consensus_key -> unit + +type consensus_key_and_delegate = consensus_key * Signature.Public_key_hash.t + +val consensus_key_and_delegate_encoding : + consensus_key_and_delegate Data_encoding.t + +val pp_consensus_key_and_delegate : + Format.formatter -> consensus_key_and_delegate -> unit type validation_mode = Node | Local of Abstract_context_index.t @@ -65,7 +73,7 @@ type block_info = { type cache = { known_timestamps : Timestamp.time Baking_cache.Timestamp_of_round_cache.t; round_timestamps : - (Timestamp.time * Round.t * delegate) + (Timestamp.time * Round.t * consensus_key_and_delegate) Baking_cache.Round_timestamp_interval_cache.t; } @@ -77,7 +85,7 @@ type global_state = { round_durations : Round.round_durations; operation_worker : Operation_worker.t; validation_mode : validation_mode; - delegates : delegate list; + delegates : consensus_key list; cache : cache; } @@ -87,14 +95,10 @@ val round_of_shell_header : Block_header.shell_header -> Round.t tzresult module SlotMap : Map.S with type key = Slot.t -type endorsing_slot = { - delegate : Signature.public_key_hash; - slots : Slot.t trace; - endorsing_power : int; -} +type endorsing_slot = {slots : Slot.t list; endorsing_power : int} type delegate_slots = { - own_delegate_slots : (delegate * endorsing_slot) SlotMap.t; + own_delegate_slots : (consensus_key_and_delegate * endorsing_slot) SlotMap.t; all_delegate_slots : endorsing_slot SlotMap.t; all_slots_by_round : Slot.t array; } @@ -189,7 +193,7 @@ val compute_delegate_slots : ?block:Block_services.block -> level:int32 -> chain:Shell_services.chain -> - delegate list -> + consensus_key list -> delegate_slots tzresult Lwt.t val create_cache : unit -> cache @@ -211,7 +215,8 @@ val pp_endorsable_payload : Format.formatter -> endorsable_payload -> unit val pp_elected_block : Format.formatter -> elected_block -> unit -val pp_endorsing_slot : Format.formatter -> delegate * endorsing_slot -> unit +val pp_endorsing_slot : + Format.formatter -> consensus_key_and_delegate * endorsing_slot -> unit val pp_delegate_slots : Format.formatter -> delegate_slots -> unit diff --git a/src/proto_alpha/lib_delegate/client_daemon.mli b/src/proto_alpha/lib_delegate/client_daemon.mli index 0e6f79986105..790d03cee1c2 100644 --- a/src/proto_alpha/lib_delegate/client_daemon.mli +++ b/src/proto_alpha/lib_delegate/client_daemon.mli @@ -39,7 +39,7 @@ module Baker : sig chain:Shell_services.chain -> context_path:string -> keep_alive:bool -> - Baking_state.delegate list -> + Baking_state.consensus_key list -> unit tzresult Lwt.t end diff --git a/src/proto_alpha/lib_delegate/state_transitions.ml b/src/proto_alpha/lib_delegate/state_transitions.ml index dd978072e535..6e8dd27e6284 100644 --- a/src/proto_alpha/lib_delegate/state_transitions.ml +++ b/src/proto_alpha/lib_delegate/state_transitions.ml @@ -79,8 +79,8 @@ let make_consensus_list state proposal = let round = proposal.block.round in let block_payload_hash = proposal.block.payload_hash in SlotMap.fold - (fun _slot (delegate, slots) acc -> - ( delegate, + (fun _slot (consensus_key_and_delegate, slots) acc -> + ( consensus_key_and_delegate, {slot = Stdlib.List.hd slots.slots; level; round; block_payload_hash} ) :: acc) state.level_state.delegate_slots.own_delegate_slots @@ -96,7 +96,7 @@ let make_preendorse_action state proposal = in {state with round_state} in - let preendorsements : (delegate * consensus_content) list = + let preendorsements : (consensus_key_and_delegate * consensus_content) list = make_consensus_list state proposal in Inject_preendorsements {preendorsements; updated_state} @@ -598,7 +598,7 @@ let make_endorse_action state proposal = proposal.block.round proposal.block.payload_hash in - let endorsements : (delegate * consensus_content) list = + let endorsements : (consensus_key_and_delegate * consensus_content) list = make_consensus_list state proposal in Inject_endorsements {endorsements; updated_state} diff --git a/src/proto_alpha/lib_delegate/state_transitions.mli b/src/proto_alpha/lib_delegate/state_transitions.mli index 4b14864cb1de..9dde0efaf02b 100644 --- a/src/proto_alpha/lib_delegate/state_transitions.mli +++ b/src/proto_alpha/lib_delegate/state_transitions.mli @@ -36,7 +36,7 @@ val is_acceptable_proposal_for_current_level : state -> proposal -> proposal_acceptance Lwt.t val make_consensus_list : - state -> proposal -> (delegate * consensus_content) list + state -> proposal -> (consensus_key_and_delegate * consensus_content) list val make_preendorse_action : state -> proposal -> action @@ -51,21 +51,21 @@ val handle_new_proposal : state -> proposal -> (state * action) Lwt.t val round_proposer : state -> - (delegate * endorsing_slot) SlotMap.t -> + (consensus_key_and_delegate * endorsing_slot) SlotMap.t -> Round.t -> - (delegate * endorsing_slot) option + (consensus_key_and_delegate * endorsing_slot) option val propose_fresh_block_action : endorsements:Kind.endorsement Operation.t list -> ?last_proposal:block_info -> predecessor:block_info -> state -> - delegate -> + consensus_key_and_delegate -> Round.t -> action Lwt.t val propose_block_action : - state -> delegate -> Round.t -> proposal -> action Lwt.t + state -> consensus_key_and_delegate -> Round.t -> proposal -> action Lwt.t (** Increase the current round and propose at the new round (same level), if the baker has a proposer slot. *) diff --git a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml index 5194f4dd2cea..001336b30a94 100644 --- a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml +++ b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.ml @@ -135,7 +135,7 @@ module type Hooks = sig val on_start_baker : baker_position:int -> - delegates:Baking_state.delegate list -> + delegates:Baking_state.consensus_key list -> cctxt:Protocol_client_context.full -> unit Lwt.t @@ -738,7 +738,7 @@ let create_fake_node_state ~i ~live_depth } (** Start baker process. *) -let baker_process ~(delegates : Baking_state.delegate list) ~base_dir +let baker_process ~(delegates : Baking_state.consensus_key list) ~base_dir ~(genesis_block : Block_header.t * Environment_context.rpc_context) ~i ~global_chain_table ~broadcast_pipes ~(user_hooks : (module Hooks)) = let broadcast_pipe = @@ -766,7 +766,7 @@ let baker_process ~(delegates : Baking_state.delegate list) ~base_dir User_hooks.on_start_baker ~baker_position:i ~delegates ~cctxt >>= fun () -> List.iter_es (fun ({alias; public_key; public_key_hash; secret_key_uri} : - Baking_state.delegate) -> + Baking_state.consensus_key) -> let open Tezos_client_base in let name = alias |> WithExceptions.Option.get ~loc:__LOC__ in Client_keys.neuterize secret_key_uri >>=? fun public_key_uri -> @@ -1064,7 +1064,7 @@ let default_config = let make_baking_delegate ( (account : Alpha_context.Parameters.bootstrap_account), (secret : Tezos_mockup_commands.Mockup_wallet.bootstrap_secret) ) : - Baking_state.delegate = + Baking_state.consensus_key = Baking_state. { alias = Some secret.name; diff --git a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli index 25b73e0c6182..9704b16d57be 100644 --- a/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli +++ b/src/proto_alpha/lib_delegate/test/mockup_simulator/mockup_simulator.mli @@ -116,7 +116,7 @@ module type Hooks = sig bakers that were started for this run. *) val on_start_baker : baker_position:int -> - delegates:Baking_state.delegate list -> + delegates:Baking_state.consensus_key list -> cctxt:Protocol_client_context.full -> unit Lwt.t -- GitLab From 7a06c292a8ff41b0653fb0da5940016287622012 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 27/35] Proto: add operation `Update_consensus_key` --- src/proto_alpha/lib_client/injection.ml | 3 + .../lib_client/operation_result.ml | 20 ++ src/proto_alpha/lib_injector/l1_operation.ml | 1 + .../lib_protocol/alpha_context.mli | 12 ++ src/proto_alpha/lib_protocol/apply.ml | 37 +++- src/proto_alpha/lib_protocol/apply_results.ml | 68 +++++++ .../lib_protocol/apply_results.mli | 4 + .../lib_protocol/operation_repr.ml | 35 ++++ .../lib_protocol/operation_repr.mli | 14 ++ .../lib_protocol/test/helpers/block.ml | 7 +- .../_regressions/rpc/alpha.client.mempool.out | 189 ++++++++++++++++++ tezt/_regressions/rpc/alpha.proxy.mempool.out | 189 ++++++++++++++++++ 12 files changed, 573 insertions(+), 6 deletions(-) diff --git a/src/proto_alpha/lib_client/injection.ml b/src/proto_alpha/lib_client/injection.ml index 11e8eb18027b..81b87bc7d983 100644 --- a/src/proto_alpha/lib_client/injection.ml +++ b/src/proto_alpha/lib_client/injection.ml @@ -315,6 +315,7 @@ let estimated_gas_single (type kind) | Applied (Register_global_constant_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Set_deposits_limit_result {consumed_gas}) -> Ok consumed_gas + | Applied (Update_consensus_key_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Tx_rollup_origination_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Tx_rollup_submit_batch_result {consumed_gas; _}) -> @@ -379,6 +380,7 @@ let estimated_storage_single (type kind) ~tx_rollup_origination_size | Applied (Register_global_constant_result {size_of_constant; _}) -> Ok size_of_constant | Applied (Set_deposits_limit_result _) -> Ok Z.zero + | Applied (Update_consensus_key_result _) -> Ok Z.zero | Applied (Tx_rollup_origination_result _) -> Ok tx_rollup_origination_size | Applied (Tx_rollup_submit_batch_result {paid_storage_size_diff; _}) | Applied (Sc_rollup_atomic_batch_result {paid_storage_size_diff; _}) -> @@ -454,6 +456,7 @@ let originated_contracts_single (type kind) | Applied (Reveal_result _) -> Ok [] | Applied (Delegation_result _) -> Ok [] | Applied (Set_deposits_limit_result _) -> Ok [] + | Applied (Update_consensus_key_result _) -> Ok [] | Applied (Tx_rollup_origination_result _) -> Ok [] | Applied (Tx_rollup_submit_batch_result _) -> Ok [] | Applied (Tx_rollup_commit_result _) -> Ok [] diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index 5da8026f2140..b5e1d13e44d5 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -205,6 +205,14 @@ let pp_manager_operation_content (type kind) source pp_result ppf match limit_opt with | None -> Format.pp_print_string ppf "Unlimited deposits" | Some limit -> Format.fprintf ppf "Limit: %a" Tez.pp limit) + | Update_consensus_key (cycle, pk) -> + Format.fprintf + ppf + "Update_consensus_key:@,Cycle: %a@,Public key hash: %a" + Cycle.pp + cycle + Signature.Public_key_hash.pp + (Signature.Public_key.hash pk) | Tx_rollup_origination -> Format.fprintf ppf "Tx rollup origination:@,From: %a" Contract.pp source | Tx_rollup_submit_batch {tx_rollup; content; burn_limit = _} -> @@ -744,6 +752,9 @@ let pp_manager_operation_contents_and_result ppf Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas ; pp_balance_updates_opt ppf balance_updates in + let pp_update_consensus_key_op (Update_consensus_key_result {consumed_gas}) = + Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas + in let pp_result (type kind) ppf (result : kind manager_operation_result) = Format.fprintf ppf "@," ; match result with @@ -773,6 +784,15 @@ let pp_manager_operation_contents_and_result ppf ppf "@[This deposits limit modification was BACKTRACKED, its \ expected effects were NOT applied.@]" + | Applied (Update_consensus_key_result _ as op) -> + Format.fprintf ppf "The consensus key was successfully udpated" ; + pp_update_consensus_key_op op + | Backtracked ((Update_consensus_key_result _ as op), _) -> + Format.fprintf + ppf + "@[This consensus key update was BACKTRACKED, its expected \ + effects were NOT applied.@]" ; + pp_update_consensus_key_op op | Applied (Transaction_result tx) -> Format.fprintf ppf "This transaction was successfully applied" ; pp_transaction_result tx diff --git a/src/proto_alpha/lib_injector/l1_operation.ml b/src/proto_alpha/lib_injector/l1_operation.ml index b0aef6cc2142..c4af4a525f21 100644 --- a/src/proto_alpha/lib_injector/l1_operation.ml +++ b/src/proto_alpha/lib_injector/l1_operation.ml @@ -80,6 +80,7 @@ module Manager_operation = struct | Delegation _ -> delegation_case | Register_global_constant _ -> register_global_constant_case | Set_deposits_limit _ -> set_deposits_limit_case + | Update_consensus_key _ -> update_consensus_key_case | Tx_rollup_origination -> tx_rollup_origination_case | Tx_rollup_submit_batch _ -> tx_rollup_submit_batch_case | Tx_rollup_commit _ -> tx_rollup_commit_case diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index a454c626e0b7..2025e650dc5f 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2893,6 +2893,8 @@ module Kind : sig type set_deposits_limit = Set_deposits_limit_kind + type update_consensus_key = Update_consensus_key_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -2936,6 +2938,7 @@ module Kind : sig | Delegation_manager_kind : delegation manager | Register_global_constant_manager_kind : register_global_constant manager | Set_deposits_limit_manager_kind : set_deposits_limit manager + | Update_consensus_key_manager_kind : update_consensus_key manager | Tx_rollup_origination_manager_kind : tx_rollup_origination manager | Tx_rollup_submit_batch_manager_kind : tx_rollup_submit_batch manager | Tx_rollup_commit_manager_kind : tx_rollup_commit manager @@ -3070,6 +3073,9 @@ and _ manager_operation = | Set_deposits_limit : Tez.t option -> Kind.set_deposits_limit manager_operation + | Update_consensus_key : + Cycle.t * Signature.Public_key.t + -> Kind.update_consensus_key manager_operation | Tx_rollup_origination : Kind.tx_rollup_origination manager_operation | Tx_rollup_submit_batch : { tx_rollup : Tx_rollup.t; @@ -3289,6 +3295,8 @@ module Operation : sig val delegation_case : Kind.delegation Kind.manager case + val update_consensus_key_case : Kind.update_consensus_key Kind.manager case + val tx_rollup_origination_case : Kind.tx_rollup_origination Kind.manager case @@ -3354,6 +3362,10 @@ module Operation : sig val delegation_case : Kind.delegation case + val update_consensus_key_tag : int + + val update_consensus_key_case : Kind.update_consensus_key case + val register_global_constant_case : Kind.register_global_constant case val set_deposits_limit_case : Kind.set_deposits_limit case diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index aa380ccf8883..4f9eabd3160d 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -93,6 +93,7 @@ type error += } | Set_deposits_limit_on_unregistered_delegate of Signature.Public_key_hash.t | Set_deposits_limit_too_high of {limit : Tez.t; max_limit : Tez.t} + | Update_consensus_key_on_unregistered_delegate of Signature.Public_key_hash.t | Empty_transaction of Contract.t | Tx_rollup_feature_disabled | Tx_rollup_invalid_transaction_ticket_amount @@ -468,6 +469,21 @@ let () = | Set_deposits_limit_too_high {limit; max_limit} -> Some (limit, max_limit) | _ -> None) (fun (limit, max_limit) -> Set_deposits_limit_too_high {limit; max_limit}) ; + register_error_kind + `Temporary + ~id:"operation.update_consensus_key_on_unregistered_delegate" + ~title:"Update consensus key on an unregistered delegate" + ~description:"Cannot update consensus key an unregistered delegate." + ~pp:(fun ppf c -> + Format.fprintf + ppf + "Cannot update the consensus key on the unregistered delegate %a." + Signature.Public_key_hash.pp + c) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function + | Update_consensus_key_on_unregistered_delegate c -> Some c | _ -> None) + (fun c -> Update_consensus_key_on_unregistered_delegate c) ; register_error_kind `Branch ~id:"contract.empty_transaction" @@ -1498,6 +1514,19 @@ let apply_external_manager_operation_content : Set_deposits_limit_result {consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt}, [] ) + | Update_consensus_key (cycle, pk) -> + Delegate.registered ctxt source >>= fun is_registered -> + error_unless + is_registered + (Update_consensus_key_on_unregistered_delegate source) + >>?= fun () -> + Delegate.Consensus_key.register_update ctxt source cycle pk + >>=? fun ctxt -> + return + ( ctxt, + Update_consensus_key_result + {consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt}, + [] ) | Tx_rollup_origination -> Tx_rollup.originate ctxt >>=? fun (ctxt, originated_tx_rollup) -> let result = @@ -1911,7 +1940,7 @@ let precheck_manager_contents (type kind) ctxt (op : kind Kind.manager contents) @@ (* See comment in the Transaction branch *) ( Script.force_decode_in_context ~consume_deserialization_gas ctxt value >|? fun (_value, ctxt) -> ctxt ) - | Delegation _ | Set_deposits_limit _ -> return ctxt + | Delegation _ | Set_deposits_limit _ | Update_consensus_key _ -> return ctxt | Tx_rollup_origination -> assert_tx_rollup_feature_enabled ctxt >|=? fun () -> ctxt | Tx_rollup_submit_batch {content; _} -> @@ -2117,7 +2146,8 @@ let burn_manager_storage_fees : size_of_constant = payload.size_of_constant; global_address = payload.global_address; } ) - | Set_deposits_limit_result _ -> return (ctxt, storage_limit, smopr) + | Set_deposits_limit_result _ | Update_consensus_key_result _ -> + return (ctxt, storage_limit, smopr) | Tx_rollup_origination_result payload -> Fees.burn_tx_rollup_origination_fees ctxt ~storage_limit ~payer >|=? fun (ctxt, storage_limit, origination_bus) -> @@ -2218,7 +2248,8 @@ let burn_internal_storage_fees : ( ctxt, storage_limit, Register_global_constant_result {payload with balance_updates} ) - | Set_deposits_limit_result _ -> return (ctxt, storage_limit, smopr) + | Set_deposits_limit_result _ | Update_consensus_key_result _ -> + return (ctxt, storage_limit, smopr) | Tx_rollup_origination_result ({balance_updates; _} as payload) -> Fees.burn_tx_rollup_origination_fees ctxt ~storage_limit ~payer >>=? fun (ctxt, storage_limit, origination_bus) -> diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index d592d0973886..cd8ee1e4dae9 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -162,6 +162,10 @@ type _ successful_manager_operation_result = consumed_gas : Gas.Arith.fp; } -> Kind.set_deposits_limit successful_manager_operation_result + | Update_consensus_key_result : { + consumed_gas : Gas.Arith.fp; + } + -> Kind.update_consensus_key successful_manager_operation_result | Tx_rollup_origination_result : { balance_updates : Receipt.balance_updates; consumed_gas : Gas.Arith.fp; @@ -613,6 +617,26 @@ module Manager_result = struct assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; Delegation_result {consumed_gas = consumed_milligas}) + let update_consensus_key_case = + make + ~op_case:Operation.Encoding.Manager_operations.update_consensus_key_case + ~encoding: + Data_encoding.( + obj2 + (dft "consumed_gas" Gas.Arith.n_integral_encoding Gas.Arith.zero) + (dft "consumed_milligas" Gas.Arith.n_fp_encoding Gas.Arith.zero)) + ~select:(function + | Successful_manager_result (Update_consensus_key_result _ as op) -> + Some op + | _ -> None) + ~kind:Kind.Update_consensus_key_manager_kind + ~proj:(function[@coq_match_with_default] + | Update_consensus_key_result {consumed_gas} -> + (Gas.Arith.ceil consumed_gas, consumed_gas)) + ~inj:(fun (consumed_gas, consumed_milligas) -> + assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; + Update_consensus_key_result {consumed_gas = consumed_milligas}) + let set_deposits_limit_case = make ~op_case:Operation.Encoding.Manager_operations.set_deposits_limit_case @@ -1429,6 +1453,7 @@ let successful_manager_operation_result_encoding : make Manager_result.transaction_case; make Manager_result.origination_case; make Manager_result.delegation_case; + make Manager_result.update_consensus_key_case; make Manager_result.set_deposits_limit_case; make Manager_result.sc_rollup_originate_case; ] @@ -1494,6 +1519,10 @@ let equal_manager_kind : | Kind.Origination_manager_kind, _ -> None | Kind.Delegation_manager_kind, Kind.Delegation_manager_kind -> Some Eq | Kind.Delegation_manager_kind, _ -> None + | ( Kind.Update_consensus_key_manager_kind, + Kind.Update_consensus_key_manager_kind ) -> + Some Eq + | Kind.Update_consensus_key_manager_kind, _ -> None | ( Kind.Register_global_constant_manager_kind, Kind.Register_global_constant_manager_kind ) -> Some Eq @@ -1893,6 +1922,17 @@ module Encoding = struct Some (op, res) | _ -> None) + let[@coq_axiom_with_reason "gadt"] update_consensus_key_case = + make_manager_case + Operation.Encoding.update_consensus_key_case + Manager_result.update_consensus_key_case + (function + | Contents_and_result + ( (Manager_operation {operation = Update_consensus_key _; _} as op), + res ) -> + Some (op, res) + | _ -> None) + let[@coq_axiom_with_reason "gadt"] register_global_constant_case = make_manager_case Operation.Encoding.register_global_constant_case @@ -2130,6 +2170,7 @@ let contents_result_encoding = make delegation_case; make register_global_constant_case; make set_deposits_limit_case; + make update_consensus_key_case; make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; @@ -2187,6 +2228,7 @@ let contents_and_result_encoding = make delegation_case; make register_global_constant_case; make set_deposits_limit_case; + make update_consensus_key_case; make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; @@ -2424,6 +2466,32 @@ let kind_equal : } ) -> Some Eq | Manager_operation {operation = Delegation _; _}, _ -> None + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + {operation_result = Applied (Update_consensus_key_result _); _} ) -> + Some Eq + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + {operation_result = Backtracked (Update_consensus_key_result _, _); _} ) + -> + Some Eq + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + { + operation_result = + Failed (Alpha_context.Kind.Update_consensus_key_manager_kind, _); + _; + } ) -> + Some Eq + | ( Manager_operation {operation = Update_consensus_key _; _}, + Manager_operation_result + { + operation_result = + Skipped Alpha_context.Kind.Update_consensus_key_manager_kind; + _; + } ) -> + Some Eq + | Manager_operation {operation = Update_consensus_key _; _}, _ -> None | ( Manager_operation {operation = Register_global_constant _; _}, Manager_operation_result {operation_result = Applied (Register_global_constant_result _); _} ) -> diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index cdb2c2211103..d39f0cc35f29 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -207,6 +207,10 @@ and _ successful_manager_operation_result = consumed_gas : Gas.Arith.fp; } -> Kind.set_deposits_limit successful_manager_operation_result + | Update_consensus_key_result : { + consumed_gas : Gas.Arith.fp; + } + -> Kind.update_consensus_key successful_manager_operation_result | Tx_rollup_origination_result : { balance_updates : Receipt.balance_updates; consumed_gas : Gas.Arith.fp; diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index a45d1e4a03ad..6ce9b2221580 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -68,6 +68,8 @@ module Kind = struct type set_deposits_limit = Set_deposits_limit_kind + type update_consensus_key = Update_consensus_key_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -111,6 +113,7 @@ module Kind = struct | Delegation_manager_kind : delegation manager | Register_global_constant_manager_kind : register_global_constant manager | Set_deposits_limit_manager_kind : set_deposits_limit manager + | Update_consensus_key_manager_kind : update_consensus_key manager | Tx_rollup_origination_manager_kind : tx_rollup_origination manager | Tx_rollup_submit_batch_manager_kind : tx_rollup_submit_batch manager | Tx_rollup_commit_manager_kind : tx_rollup_commit manager @@ -307,6 +310,9 @@ and _ manager_operation = | Set_deposits_limit : Tez_repr.t option -> Kind.set_deposits_limit manager_operation + | Update_consensus_key : + Cycle_repr.t * Signature.Public_key.t + -> Kind.update_consensus_key manager_operation | Tx_rollup_origination : Kind.tx_rollup_origination manager_operation | Tx_rollup_submit_batch : { tx_rollup : Tx_rollup_repr.t; @@ -414,6 +420,7 @@ let manager_kind : type kind. kind manager_operation -> kind Kind.manager = | Delegation _ -> Kind.Delegation_manager_kind | Register_global_constant _ -> Kind.Register_global_constant_manager_kind | Set_deposits_limit _ -> Kind.Set_deposits_limit_manager_kind + | Update_consensus_key _ -> Kind.Update_consensus_key_manager_kind | Tx_rollup_origination -> Kind.Tx_rollup_origination_manager_kind | Tx_rollup_submit_batch _ -> Kind.Tx_rollup_submit_batch_manager_kind | Tx_rollup_commit _ -> Kind.Tx_rollup_commit_manager_kind @@ -652,6 +659,28 @@ module Encoding = struct inj = (fun key -> Set_deposits_limit key); } + let update_consensus_key_tag = 6 + + let[@coq_axiom_with_reason "gadt"] update_consensus_key_case = + MCase + { + tag = update_consensus_key_tag; + name = "update_consensus_key"; + encoding = + obj2 + (req "cycle" Cycle_repr.encoding) + (req "pk" Signature.Public_key.encoding); + select = + (function + | Manager (Update_consensus_key _ as op) -> Some op | _ -> None); + proj = + (function + | Update_consensus_key (cycle, consensus_pk) -> (cycle, consensus_pk)); + inj = + (fun (cycle, consensus_pk) -> + Update_consensus_key (cycle, consensus_pk)); + } + let[@coq_axiom_with_reason "gadt"] tx_rollup_origination_case = MCase { @@ -1389,6 +1418,9 @@ module Encoding = struct let set_deposits_limit_case = make_manager_case 112 Manager_operations.set_deposits_limit_case + let update_consensus_key_case = + make_manager_case 113 Manager_operations.update_consensus_key_case + let tx_rollup_origination_case = make_manager_case tx_rollup_operation_tag_offset @@ -1495,6 +1527,7 @@ module Encoding = struct make origination_case; make delegation_case; make set_deposits_limit_case; + make update_consensus_key_case; make failing_noop_case; make register_global_constant_case; make tx_rollup_origination_case; @@ -1699,6 +1732,8 @@ let equal_manager_operation_kind : | Register_global_constant _, _ -> None | Set_deposits_limit _, Set_deposits_limit _ -> Some Eq | Set_deposits_limit _, _ -> None + | Update_consensus_key _, Update_consensus_key _ -> Some Eq + | Update_consensus_key _, _ -> None | Tx_rollup_origination, Tx_rollup_origination -> Some Eq | Tx_rollup_origination, _ -> None | Tx_rollup_submit_batch _, Tx_rollup_submit_batch _ -> Some Eq diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index 63a179cf3944..14ce04ae9add 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -97,6 +97,8 @@ module Kind : sig type set_deposits_limit = Set_deposits_limit_kind + type update_consensus_key = Update_consensus_key_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -140,6 +142,7 @@ module Kind : sig | Delegation_manager_kind : delegation manager | Register_global_constant_manager_kind : register_global_constant manager | Set_deposits_limit_manager_kind : set_deposits_limit manager + | Update_consensus_key_manager_kind : update_consensus_key manager | Tx_rollup_origination_manager_kind : tx_rollup_origination manager | Tx_rollup_submit_batch_manager_kind : tx_rollup_submit_batch manager | Tx_rollup_commit_manager_kind : tx_rollup_commit manager @@ -348,6 +351,11 @@ and _ manager_operation = | Set_deposits_limit : Tez_repr.t option -> Kind.set_deposits_limit manager_operation + (* [Update_consensus_key (cycle, pk)] updates the consensus key of + the signing delegate to [pk] starting at [cycle]. *) + | Update_consensus_key : + Cycle_repr.t * Signature.Public_key.t + -> Kind.update_consensus_key manager_operation (* [Tx_rollup_origination] allows an implicit contract to originate a new transactional rollup. *) | Tx_rollup_origination : Kind.tx_rollup_origination manager_operation @@ -586,6 +594,8 @@ module Encoding : sig val delegation_case : Kind.delegation Kind.manager case + val update_consensus_key_case : Kind.update_consensus_key Kind.manager case + val register_global_constant_case : Kind.register_global_constant Kind.manager case @@ -649,6 +659,10 @@ module Encoding : sig val delegation_case : Kind.delegation case + val update_consensus_key_tag : int + + val update_consensus_key_case : Kind.update_consensus_key case + val register_global_constant_case : Kind.register_global_constant case val set_deposits_limit_case : Kind.set_deposits_limit case diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 51d835047099..7502ba0197d5 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -747,9 +747,9 @@ let bake_n_with_all_balance_updates ?(baking_mode = Application) ?policy fun (Successful_manager_result r) -> match r with | Reveal_result _ | Delegation_result _ - | Set_deposits_limit_result _ | Tx_rollup_origination_result _ - | Tx_rollup_submit_batch_result _ | Tx_rollup_commit_result _ - | Tx_rollup_return_bond_result _ + | Update_consensus_key_result _ | Set_deposits_limit_result _ + | Tx_rollup_origination_result _ | Tx_rollup_submit_batch_result _ + | Tx_rollup_commit_result _ | Tx_rollup_return_bond_result _ | Tx_rollup_finalize_commitment_result _ | Tx_rollup_remove_commitment_result _ | Tx_rollup_rejection_result _ | Transfer_ticket_result _ @@ -785,6 +785,7 @@ let bake_n_with_origination_results ?(baking_mode = Application) ?policy n b = function | Successful_manager_result (Reveal_result _) | Successful_manager_result (Delegation_result _) + | Successful_manager_result (Update_consensus_key_result _) | Successful_manager_result (Transaction_result _) | Successful_manager_result (Register_global_constant_result _) | Successful_manager_result (Set_deposits_limit_result _) diff --git a/tezt/_regressions/rpc/alpha.client.mempool.out b/tezt/_regressions/rpc/alpha.client.mempool.out index 252bdc0ec972..3686a04f6374 100644 --- a/tezt/_regressions/rpc/alpha.client.mempool.out +++ b/tezt/_regressions/rpc/alpha.client.mempool.out @@ -1683,6 +1683,52 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", @@ -5075,6 +5121,103 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Set_deposits_limit" }, + { + "tag": 113, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "source", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "fee", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "counter", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "gas_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "cycle", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "pk", + "layout": { + "name": "public_key", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Update_consensus_key" + }, { "tag": 150, "fields": [ @@ -12150,6 +12293,52 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", diff --git a/tezt/_regressions/rpc/alpha.proxy.mempool.out b/tezt/_regressions/rpc/alpha.proxy.mempool.out index c0f381b6079f..e549a5e6107c 100644 --- a/tezt/_regressions/rpc/alpha.proxy.mempool.out +++ b/tezt/_regressions/rpc/alpha.proxy.mempool.out @@ -1704,6 +1704,52 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", @@ -5096,6 +5142,103 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Set_deposits_limit" }, + { + "tag": 113, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "source", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "fee", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "counter", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "gas_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "cycle", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "pk", + "layout": { + "name": "public_key", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Update_consensus_key" + }, { "tag": 150, "fields": [ @@ -12171,6 +12314,52 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Update_consensus_key", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "update_consensus_key" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "cycle": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "pk": { + "$ref": "#/definitions/Signature.Public_key" + } + }, + "required": [ + "pk", + "cycle", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", -- GitLab From 47f5ce55684474c289692033b852e11d012d2d73 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 28/35] Proto/Client: add `tezos-client set consensus key for to ` --- .../lib_client/client_proto_context.ml | 35 ++++++++++++++++ .../lib_client/client_proto_context.mli | 16 ++++++++ .../client_proto_context_commands.ml | 40 +++++++++++++++++++ 3 files changed, 91 insertions(+) diff --git a/src/proto_alpha/lib_client/client_proto_context.ml b/src/proto_alpha/lib_client/client_proto_context.ml index 49111fcc74e3..c319d7aa0573 100644 --- a/src/proto_alpha/lib_client/client_proto_context.ml +++ b/src/proto_alpha/lib_client/client_proto_context.ml @@ -316,6 +316,41 @@ let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run ~fee_parameter (Some source) +let update_consensus_key cctxt ~chain ~block ?confirmations ?dry_run + ?verbose_signing ?simulation ?fee ~consensus_pk ~manager_sk ~fee_parameter + cycle src_pk = + let source = Signature.Public_key.hash src_pk in + let operation = Update_consensus_key (cycle, consensus_pk) in + let operation = + Injection.prepare_manager_operation + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown + operation + in + let operation = Annotated_manager_operation.Single_manager operation in + Injection.inject_manager_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ?simulation + ~successor_level:true + ~source + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown + ~src_pk + ~src_sk:manager_sk + ~fee_parameter + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result ((Manager_operation _ as op), result) -> + return (oph, op, result) + let set_deposits_limit cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ?simulation ?fee contract ~src_pk ~manager_sk ~fee_parameter limit_opt = diff --git a/src/proto_alpha/lib_client/client_proto_context.mli b/src/proto_alpha/lib_client/client_proto_context.mli index 97951466bf00..dc99612f702d 100644 --- a/src/proto_alpha/lib_client/client_proto_context.mli +++ b/src/proto_alpha/lib_client/client_proto_context.mli @@ -131,6 +131,22 @@ val set_delegate : public_key_hash option -> Kind.delegation Kind.manager Injection.result tzresult Lwt.t +val update_consensus_key : + #Protocol_client_context.full -> + chain:Shell_services.chain -> + block:Shell_services.block -> + ?confirmations:int -> + ?dry_run:bool -> + ?verbose_signing:bool -> + ?simulation:bool -> + ?fee:Tez.tez -> + consensus_pk:Signature.public_key -> + manager_sk:Client_keys.sk_uri -> + fee_parameter:Injection.fee_parameter -> + Cycle.t -> + Signature.public_key -> + Kind.update_consensus_key Kind.manager Injection.result tzresult Lwt.t + val set_deposits_limit : #Protocol_client_context.full -> chain:Shell_services.chain -> diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index 0fff31de6b21..01a4c4df8df9 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -1490,6 +1490,46 @@ let commands_rw () = cctxt#message "Delegate already activated." >>= fun () -> return_unit | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Update the consensus key of a delegate." + (args4 fee_arg dry_run_switch verbose_signing_switch fee_parameter_args) + (prefixes ["set"; "consensus"; "key"; "for"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["to"] + @@ Public_key.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (fee, dry_run, verbose_signing, fee_parameter) + delegate_pkh + (name_pk, consensus_pk) + cctxt -> + (match consensus_pk with + | Some pk -> return pk + | None -> Client_keys.public_key name_pk) + >>=? fun consensus_pk -> + Client_keys.get_key cctxt delegate_pkh + >>=? fun (_, delegate_pk, delegate_sk) -> + Alpha_services.Constants.all cctxt (cctxt#chain, cctxt#block) + >>=? fun {parametric = {preserved_cycles; _}; _} -> + Plugin.RPC.current_level cctxt ~offset:1l (cctxt#chain, cctxt#block) + >>=? fun l -> + let cycle = Cycle.(succ (add l.cycle preserved_cycles)) in + update_consensus_key + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~fee_parameter + ~verbose_signing + ?fee + ~consensus_pk + ~manager_sk:delegate_sk + cycle + delegate_pk + >>= function + | Ok _ -> return_unit + | Error el -> Lwt.return_error el); command ~desc:"Wait until an operation is included in a block" (args3 -- GitLab From 8eb4d46156a707b50dfc9aae874cace61f48d791 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 29/35] Proto/Client: add `tezos-client register key as delegate with consensus key ` --- .../lib_client/client_proto_context.ml | 93 ++++++++++++++----- .../lib_client/client_proto_context.mli | 8 +- .../client_proto_context_commands.ml | 40 ++++++++ 3 files changed, 118 insertions(+), 23 deletions(-) diff --git a/src/proto_alpha/lib_client/client_proto_context.ml b/src/proto_alpha/lib_client/client_proto_context.ml index c319d7aa0573..42afa109f1ff 100644 --- a/src/proto_alpha/lib_client/client_proto_context.ml +++ b/src/proto_alpha/lib_client/client_proto_context.ml @@ -299,35 +299,84 @@ let set_delegate cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ~fee_parameter opt_delegate +let build_update_consensus_key ?fee ?gas_limit ?storage_limit cycle consensus_pk + = + let operation = Update_consensus_key (cycle, consensus_pk) in + Injection.prepare_manager_operation + ~fee:(Limit.of_option fee) + ~gas_limit:(Limit.of_option gas_limit) + ~storage_limit:(Limit.of_option storage_limit) + operation + let register_as_delegate cctxt ~chain ~block ?confirmations ?dry_run - ?verbose_signing ?fee ~manager_sk ~fee_parameter src_pk = + ?verbose_signing ?fee ~manager_sk ~fee_parameter ?consensus_pk src_pk = let source = Signature.Public_key.hash src_pk in - delegate_contract - cctxt - ~chain - ~block - ?confirmations - ?dry_run - ?verbose_signing - ~source - ~src_pk - ~src_sk:manager_sk - ?fee - ~fee_parameter - (Some source) + let delegate_op = build_delegate_operation ?fee (Some source) in + match consensus_pk with + | None -> ( + let operation = Annotated_manager_operation.Single_manager delegate_op in + Injection.inject_manager_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ~source + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown + ~src_pk + ~src_sk:manager_sk + ~fee_parameter + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result ((Manager_operation _ as op), result) -> + return ((oph, op, result), None)) + | Some consensus_pk -> ( + Alpha_services.Constants.all cctxt (chain, block) + >>=? fun {parametric = {preserved_cycles; _}; _} -> + Plugin.RPC.current_level cctxt ~offset:1l (chain, block) >>=? fun l -> + let cycle = Cycle.(succ (add l.cycle preserved_cycles)) in + let operation = + Annotated_manager_operation.Cons_manager + ( delegate_op, + Annotated_manager_operation.Single_manager + (build_update_consensus_key ?fee cycle consensus_pk) ) + in + Injection.inject_manager_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ~successor_level:true + ~source + ~fee:(Limit.of_option fee) + ~gas_limit:Limit.unknown + ~storage_limit:Limit.unknown + ~src_pk + ~src_sk:manager_sk + ~fee_parameter + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result + (Manager_operation _, Manager_operation_result _) -> + . + | Apply_results.Cons_and_result + ( (Manager_operation _ as op1), + res1, + Single_and_result ((Manager_operation _ as op2), res2) ) -> + return ((oph, op1, res1), Some (op2, res2))) let update_consensus_key cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ?simulation ?fee ~consensus_pk ~manager_sk ~fee_parameter cycle src_pk = let source = Signature.Public_key.hash src_pk in - let operation = Update_consensus_key (cycle, consensus_pk) in - let operation = - Injection.prepare_manager_operation - ~fee:(Limit.of_option fee) - ~gas_limit:Limit.unknown - ~storage_limit:Limit.unknown - operation - in + let operation = build_update_consensus_key ?fee cycle consensus_pk in let operation = Annotated_manager_operation.Single_manager operation in Injection.inject_manager_operation cctxt diff --git a/src/proto_alpha/lib_client/client_proto_context.mli b/src/proto_alpha/lib_client/client_proto_context.mli index dc99612f702d..73d6cb1e798d 100644 --- a/src/proto_alpha/lib_client/client_proto_context.mli +++ b/src/proto_alpha/lib_client/client_proto_context.mli @@ -173,8 +173,14 @@ val register_as_delegate : ?fee:Tez.tez -> manager_sk:Client_keys.sk_uri -> fee_parameter:Injection.fee_parameter -> + ?consensus_pk:public_key -> public_key -> - Kind.delegation Kind.manager Injection.result tzresult Lwt.t + (Kind.delegation Kind.manager Injection.result + * (Kind.update_consensus_key Kind.manager contents + * Kind.update_consensus_key Kind.manager Apply_results.contents_result) + option) + tzresult + Lwt.t val save_contract : force:bool -> diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index 01a4c4df8df9..c0c36b3eff9a 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -1490,6 +1490,46 @@ let commands_rw () = cctxt#message "Delegate already activated." >>= fun () -> return_unit | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Register the public key hash as a delegate." + (args4 fee_arg dry_run_switch verbose_signing_switch fee_parameter_args) + (prefixes ["register"; "key"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["as"; "delegate"; "with"; "consensus"; "key"] + @@ Public_key.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (fee, dry_run, verbose_signing, fee_parameter) + src_pkh + (name_pk, consensus_pk) + cctxt -> + Client_keys.get_key cctxt src_pkh >>=? fun (_, src_pk, src_sk) -> + (match consensus_pk with + | Some pk -> return pk + | None -> Client_keys.public_key name_pk) + >>=? fun consensus_pk -> + register_as_delegate + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~fee_parameter + ~verbose_signing + ?fee + ~manager_sk:src_sk + ~consensus_pk + src_pk + >>= function + | Ok _ -> return_unit + | Error + [ + Environment.Ecoproto_error + Delegate_storage.Contract.Active_delegate; + ] -> + cctxt#message "Delegate already activated." >>= fun () -> + return_unit + | Error el -> Lwt.return_error el); command ~group ~desc:"Update the consensus key of a delegate." -- GitLab From fcd3a70f1f2667c603ed703ff62c1d7f8426e269 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 30/35] Proto: add a minimal `tezt` scenario for consensus key --- .../client_proto_context_commands.ml | 2 +- tezt/lib_tezos/client.ml | 37 ++- tezt/lib_tezos/client.mli | 25 +- tezt/tests/consensus_key.ml | 314 ++++++++++++++++++ tezt/tests/main.ml | 1 + 5 files changed, 365 insertions(+), 14 deletions(-) create mode 100644 tezt/tests/consensus_key.ml diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index c0c36b3eff9a..00e024799965 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -1021,7 +1021,7 @@ let commands_rw () = ?fee ~src_pk ~manager_sk - >>= fun _ -> return_unit); + >>=? fun _ -> return_unit); command ~group ~desc:"Launch a smart contract on the blockchain." diff --git a/tezt/lib_tezos/client.ml b/tezt/lib_tezos/client.ml index 2d3a8a43fb30..97ef5d74c31b 100644 --- a/tezt/lib_tezos/client.ml +++ b/tezt/lib_tezos/client.ml @@ -492,7 +492,7 @@ let spawn_bake_for ?endpoint ?protocol ?(keys = [Constant.bootstrap1.alias]) let bake_for ?endpoint ?protocol ?keys ?minimal_fees ?minimal_nanotez_per_gas_unit ?minimal_nanotez_per_byte ?minimal_timestamp - ?mempool ?ignore_node_mempool ?force ?context_path client = + ?mempool ?ignore_node_mempool ?force ?context_path ?expect_failure client = spawn_bake_for ?endpoint ?keys @@ -506,7 +506,7 @@ let bake_for ?endpoint ?protocol ?keys ?minimal_fees ?context_path ?protocol client - |> Process.check + |> Process.check ?expect_failure let node_of_endpoint = function Node n -> Some n | Proxy_server _ -> None @@ -795,7 +795,7 @@ let get_delegate ?endpoint ~src client = Lwt.return (output =~* rex "(tz[a-zA-Z0-9]+) \\(.*\\)") let set_delegate ?endpoint ?(wait = "none") ?fee ?fee_cap - ?(force_low_fee = false) ~src ~delegate client = + ?(force_low_fee = false) ?expect_failure ~src ~delegate client = let value = spawn_command ?endpoint @@ -806,7 +806,7 @@ let set_delegate ?endpoint ?(wait = "none") ?fee ?fee_cap @ optional_arg "fee-cap" Tez.to_string fee_cap @ if force_low_fee then ["--force-low-fee"] else []) in - {value; run = Process.check} + {value; run = Process.check ?expect_failure} let reveal ?endpoint ?(wait = "none") ?fee ?fee_cap ?(force_low_fee = false) ~src client = @@ -826,10 +826,11 @@ let spawn_withdraw_delegate ?endpoint ?(wait = "none") ~src client = spawn_command ?endpoint client - (["--wait"; wait] @ ["withdraw"; "delegate"; "for"; src]) + (["--wait"; wait] @ ["withdraw"; "delegate"; "from"; src]) -let withdraw_delegate ?endpoint ?wait ~src client = - spawn_withdraw_delegate ?endpoint ?wait ~src client |> Process.check +let withdraw_delegate ?endpoint ?wait ?expect_failure ~src client = + spawn_withdraw_delegate ?endpoint ?wait ~src client + |> Process.check ?expect_failure let spawn_get_balance_for ?endpoint ~account client = spawn_command ?endpoint client ["get"; "balance"; "for"; account] @@ -901,6 +902,17 @@ let unset_deposits_limit ?hooks ?endpoint ?(wait = "none") ~src client = (["--wait"; wait] @ ["unset"; "deposits"; "limit"; "for"; src]) |> Process.check_and_read_stdout +let update_consensus_key ?hooks ?endpoint ?(wait = "none") ?burn_cap + ?expect_failure ~src ~pk client = + spawn_command + ?hooks + ?endpoint + client + (["--wait"; wait] + @ ["set"; "consensus"; "key"; "for"; src; "to"; pk] + @ optional_arg "burn-cap" Tez.to_string burn_cap) + |> Process.check ?expect_failure + let spawn_originate_contract ?hooks ?log_output ?endpoint ?(wait = "none") ?init ?burn_cap ~alias ~amount ~src ~prg client = spawn_command @@ -1836,12 +1848,17 @@ let init_with_protocol ?path ?admin_path ?name ?color ?base_dir ?event_level let* _ = Node.wait_for_level node 1 in return (node, client) -let spawn_register_key owner client = +let spawn_register_key ?consensus owner client = spawn_command client - ["--wait"; "none"; "register"; "key"; owner; "as"; "delegate"] + (["--wait"; "none"; "register"; "key"; owner; "as"; "delegate"] + @ + match consensus with + | None -> [] + | Some pk -> ["with"; "consensus"; "key"; pk]) -let register_key owner client = spawn_register_key owner client |> Process.check +let register_key ?consensus owner client = + spawn_register_key ?consensus owner client |> Process.check let contract_storage ?unparsing_mode address client = spawn_command diff --git a/tezt/lib_tezos/client.mli b/tezt/lib_tezos/client.mli index 236d049fed5f..bd9c6679b847 100644 --- a/tezt/lib_tezos/client.mli +++ b/tezt/lib_tezos/client.mli @@ -363,6 +363,7 @@ val bake_for : ?ignore_node_mempool:bool -> ?force:bool -> ?context_path:string -> + ?expect_failure:bool -> t -> unit Lwt.t @@ -628,6 +629,7 @@ val set_delegate : ?fee:Tez.t -> ?fee_cap:Tez.t -> ?force_low_fee:bool -> + ?expect_failure:bool -> src:string -> delegate:string -> t -> @@ -646,7 +648,12 @@ val reveal : (** Run [tezos-client withdraw delegate from ]. *) val withdraw_delegate : - ?endpoint:endpoint -> ?wait:string -> src:string -> t -> unit Lwt.t + ?endpoint:endpoint -> + ?wait:string -> + ?expect_failure:bool -> + src:string -> + t -> + unit Lwt.t (** Same as [withdraw_delegate], but do not wait for the process to exit. *) val spawn_withdraw_delegate : @@ -729,6 +736,18 @@ val unset_deposits_limit : t -> string Lwt.t +(** Run [tezos-client use as consensus key for delegate ] *) +val update_consensus_key : + ?hooks:Process.hooks -> + ?endpoint:endpoint -> + ?wait:string -> + ?burn_cap:Tez.t -> + ?expect_failure:bool -> + src:string -> + pk:string -> + t -> + unit Lwt.t + (* TODO: https://gitlab.com/tezos/tezos/-/issues/2336 [amount] should be named [transferring] *) (* TODO: https://gitlab.com/tezos/tezos/-/issues/2336 @@ -1380,10 +1399,10 @@ val spawn_command : Process.t (** Register public key for given account with given client. *) -val spawn_register_key : string -> t -> Process.t +val spawn_register_key : ?consensus:string -> string -> t -> Process.t (** Register public key for given account with given client. *) -val register_key : string -> t -> unit Lwt.t +val register_key : ?consensus:string -> string -> t -> unit Lwt.t (** Get contract storage for a contract. Returns a Micheline expression representing the storage as a string. *) diff --git a/tezt/tests/consensus_key.ml b/tezt/tests/consensus_key.ml new file mode 100644 index 000000000000..34576df6a454 --- /dev/null +++ b/tezt/tests/consensus_key.ml @@ -0,0 +1,314 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 G.B. Fefe *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(* Testing + ------- + Component: Baker + Invocation: dune exec tezt/tests/main.exe -- --file consensus_key.ml + Subject: Test the operation `Update_consensus_key` and its effects + on the baker. +*) + +module Helpers = struct + type level = { + level : int; + level_position : int; + cycle : int; + cycle_position : int; + expected_commitment : bool; + } + + let level_type : level Check.typ = + Check.convert + (fun {level; level_position; cycle; cycle_position; expected_commitment} -> + (level, level_position, cycle, cycle_position, expected_commitment)) + Check.(tuple5 int int int int bool) + + let decode_level json = + let level = JSON.(json |-> "level" |> as_int) in + let level_position = JSON.(json |-> "level_position" |> as_int) in + let cycle = JSON.(json |-> "cycle" |> as_int) in + let cycle_position = JSON.(json |-> "cycle_position" |> as_int) in + let expected_commitment = + JSON.(json |-> "expected_commitment" |> as_bool) + in + {level; level_position; cycle; cycle_position; expected_commitment} + + let get_current_level client = + let* json = RPC.get_current_level client in + return (decode_level json) + + let check_current_level client expected_level = + let* level = get_current_level client in + Check.((level = expected_level) level_type) + ~error_msg:"expected current_period = %R, got %L" ; + unit + + let bake_and_wait_block node client = + let* level_json = RPC.get_current_level client in + let level = JSON.(level_json |-> "level" |> as_int) in + let* () = + Client.bake_for ~context_path:(Node.data_dir node // "context") client + in + let* _i = Node.wait_for_level node (level + 1) in + Lwt.return_unit +end + +open Helpers + +let test_update_consensus_key = + Protocol.register_test + ~__FILE__ + ~title:"update consensus key" + ~tags:["consensus_key"] + @@ fun protocol -> + let parameters = + (* we update paramaters for faster testing: no need to wait + 5 cycles for the consensus key to activate. *) + [(["blocks_per_cycle"], Some "4"); (["preserved_cycles"], Some "0")] + in + let* parameter_file = + Protocol.write_parameter_file ~base:(Right (protocol, None)) parameters + in + let* _, client = + Client.init_with_protocol ~parameter_file ~protocol `Client () + in + let* key_a = Client.gen_and_show_keys client in + let* key_b = Client.gen_and_show_keys client in + let* key_c = Client.gen_and_show_keys client in + + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1_000_000) + ~giver:Constant.bootstrap1.alias + ~receiver:key_b.alias + client + in + let* () = Client.bake_for_and_wait client in + + (* Trying multiple invalid updates: + - changing the consensus key of an unregistered delegate + - changing the consensus key to its actual value + - changing the consensus key to an active consensus key (bootstrap) + *) + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:key_b.alias + ~pk:Constant.bootstrap1.alias + client + in + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap1.alias + ~pk:Constant.bootstrap1.alias + client + in + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap2.alias + ~pk:Constant.bootstrap1.alias + client + in + + (* Trying a valid consensus key update. *) + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap1.alias + ~pk:key_a.alias + client + in + let* () = Client.bake_for_and_wait client in + + (* Invalid update: + - changing the consensus key to an active consensus key (set) *) + let* () = + Client.update_consensus_key + ~expect_failure:true + ~src:Constant.bootstrap2.alias + ~pk:key_a.alias + client + in + + (* Register a delegate with a consensus key. *) + let* () = Client.register_key ~consensus:key_c.alias key_b.alias client in + + (* Bake until the end of the cycle... *) + let* () = Client.bake_for_and_wait client in + let* () = + check_current_level + client + { + level = 4; + level_position = 3; + cycle = 0; + cycle_position = 3; + expected_commitment = true; + } + in + + (* Bootstrap1 should not be able to bake anymore... *) + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap1.alias] + client + in + + (* ... meanwhile `key_a` and `key_c` are able to bake. *) + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_c.alias] client in + + (* Switch back to the initial consensus key. *) + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap1.alias + ~pk:Constant.bootstrap1.alias + client + in + let* () = + Client.update_consensus_key ~src:key_b.alias ~pk:key_b.alias client + in + + (* Bake until the end of the cycle. *) + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = + check_current_level + client + { + level = 8; + level_position = 7; + cycle = 1; + cycle_position = 3; + expected_commitment = true; + } + in + + (* We are not able to bake with `key_a` anymore... *) + let* () = Client.bake_for ~expect_failure:true ~keys:[key_a.alias] client in + + (* ... but are able to bake again with `bootstrap1` and `key_b`... *) + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_b.alias] client in + + (* Set the consensus key for all delegates... *) + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap1.alias + ~pk:key_a.alias + client + in + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap2.alias + ~pk:key_a.alias + client + in + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap3.alias + ~pk:key_a.alias + client + in + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap4.alias + ~pk:key_a.alias + client + in + let* () = + Client.update_consensus_key + ~src:Constant.bootstrap5.alias + ~pk:key_a.alias + client + in + (* Bake until the end of the cycle. *) + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + let* () = + check_current_level + client + { + level = 12; + level_position = 11; + cycle = 2; + cycle_position = 3; + expected_commitment = true; + } + in + + (* Bootstrap's accounts should not be able to bake anymore... *) + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap1.alias] + client + in + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap2.alias] + client + in + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap3.alias] + client + in + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap4.alias] + client + in + let* () = + Client.bake_for + ~expect_failure:true + ~keys:[Constant.bootstrap5.alias] + client + in + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = + check_current_level + client + { + level = 16; + level_position = 15; + cycle = 3; + cycle_position = 3; + expected_commitment = true; + } + in + unit + +let register ~protocols = test_update_consensus_key protocols diff --git a/tezt/tests/main.ml b/tezt/tests/main.ml index 16b77d632e1e..eed1452d76c3 100644 --- a/tezt/tests/main.ml +++ b/tezt/tests/main.ml @@ -126,6 +126,7 @@ let () = Sapling.register ~protocols:[Alpha] ; Client_run_view.register ~protocols:[Alpha] ; Multinode_snapshot.register ~protocols:[Alpha] ; + Consensus_key.register ~protocols:[Alpha] ; Config.register () ; (* Test.run () should be the last statement, don't register afterwards! *) Test.run () -- GitLab From f7ac6e284851bfac876e16f3b767f80a7ff96b06 Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 31/35] Proto: add operation `Drain_delegate` --- .../lib_client/operation_result.ml | 19 ++ src/proto_alpha/lib_plugin/plugin.ml | 6 +- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 2 +- .../lib_protocol/alpha_context.mli | 16 ++ src/proto_alpha/lib_protocol/apply.ml | 193 +++++++++++++++++- src/proto_alpha/lib_protocol/apply.mli | 12 +- src/proto_alpha/lib_protocol/apply_results.ml | 41 +++- .../lib_protocol/apply_results.mli | 5 + .../lib_protocol/delegate_storage.ml | 47 +++++ .../lib_protocol/delegate_storage.mli | 7 + src/proto_alpha/lib_protocol/dune | 10 +- src/proto_alpha/lib_protocol/main.ml | 8 + src/proto_alpha/lib_protocol/main.mli | 11 + .../lib_protocol/operation_repr.ml | 37 +++- .../lib_protocol/operation_repr.mli | 13 ++ .../_regressions/rpc/alpha.client.mempool.out | 110 ++++++++++ tezt/_regressions/rpc/alpha.proxy.mempool.out | 110 ++++++++++ 17 files changed, 631 insertions(+), 16 deletions(-) diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index b5e1d13e44d5..dee2e1b59e9b 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -1185,6 +1185,25 @@ let rec pp_contents_and_result_list : proposal Data_encoding.Json.pp (Data_encoding.Json.construct Vote.ballot_encoding ballot) + | Single_and_result + ( Drain_delegate {consensus_key; delegate; destination}, + Drain_delegate_result {balance_updates; allocated_destination_contract} + ) -> + Format.fprintf + ppf + "@[Drain delegate:@,\ + Consensus key hash: %a@,\ + Delegate: %a@,\ + Destination: %a%s%a@]" + Signature.Public_key_hash.pp + consensus_key + Signature.Public_key_hash.pp + delegate + Signature.Public_key_hash.pp + destination + (if allocated_destination_contract then " (allocated)" else "") + pp_balance_updates_opt + balance_updates | Single_and_result (Failing_noop _arbitrary, _) -> (* the Failing_noop operation always fails and can't have result *) . diff --git a/src/proto_alpha/lib_plugin/plugin.ml b/src/proto_alpha/lib_plugin/plugin.ml index 19c540d81296..de9dca51a667 100644 --- a/src/proto_alpha/lib_plugin/plugin.ml +++ b/src/proto_alpha/lib_plugin/plugin.ml @@ -1009,7 +1009,8 @@ module Mempool = struct | Single (Double_baking_evidence _) | Single (Activate_account _) | Single (Proposals _) - | Single (Ballot _) -> + | Single (Ballot _) + | Single (Drain_delegate _) -> Lwt.return @@ `Passed_prefilter other_prio | Single (Manager_operation {source; _}) as op -> prefilter_manager_op source op @@ -1288,7 +1289,8 @@ module Mempool = struct | Single_result (Double_baking_evidence_result _) | Single_result (Activate_account_result _) | Single_result Proposals_result - | Single_result Ballot_result -> + | Single_result Ballot_result + | Single_result (Drain_delegate_result _) -> Lwt.return (`Passed_postfilter filter_state) | Single_result (Manager_operation_result _) as result -> Lwt.return (post_filter_manager ctxt filter_state result config) diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 07845e66f02d..104ed3a178a0 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -111,6 +111,7 @@ "Stake_storage", "Contract_storage", "Token", + "Fees_storage", "Delegate_consensus_key", "Delegate_storage", "Delegate_missed_endorsements_storage", @@ -120,7 +121,6 @@ "Bootstrap_storage", "Vote_storage", - "Fees_storage", "Ticket_storage", "Liquidity_baking_storage", "Liquidity_baking_cpmm", diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 2025e650dc5f..3871697f2768 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2220,6 +2220,12 @@ module Delegate : sig val check_registered : context -> public_key_hash -> unit tzresult Lwt.t + val drain : + context -> + delegate:public_key_hash -> + destination:public_key_hash -> + (context * bool * Tez.t * Receipt.balance_updates) tzresult Lwt.t + type participation_info = { expected_cycle_activity : int; minimal_cycle_activity : int; @@ -2895,6 +2901,8 @@ module Kind : sig type update_consensus_key = Update_consensus_key_kind + type drain_delegate = Drain_delegate_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -3044,6 +3052,12 @@ and _ contents = ballot : Vote.ballot; } -> Kind.ballot contents + | Drain_delegate : { + consensus_key : Signature.Public_key_hash.t; + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + -> Kind.drain_delegate contents | Failing_noop : string -> Kind.failing_noop contents | Manager_operation : { source : public_key_hash; @@ -3285,6 +3299,8 @@ module Operation : sig val ballot_case : Kind.ballot case + val drain_delegate_case : Kind.drain_delegate case + val failing_noop_case : Kind.failing_noop case val reveal_case : Kind.reveal Kind.manager case diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 4f9eabd3160d..ba302df68c1e 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -94,6 +94,19 @@ type error += | Set_deposits_limit_on_unregistered_delegate of Signature.Public_key_hash.t | Set_deposits_limit_too_high of {limit : Tez.t; max_limit : Tez.t} | Update_consensus_key_on_unregistered_delegate of Signature.Public_key_hash.t + | Drain_delegate_on_unregistered_delegate of Signature.Public_key_hash.t + | Invalid_drain_delegate_inactive_key of { + delegate : Signature.Public_key_hash.t; + consensus_key : Signature.Public_key_hash.t; + active_consensus_key : Signature.Public_key_hash.t; + } + | Invalid_drain_delegate_no_consensus_key of Signature.Public_key_hash.t + | Invalid_drain_delegate_noop of Signature.Public_key_hash.t + | Invalid_drain_delegate_no_allocation_burn of { + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + | Unauthorized_key_for_drain of Signature.Public_key_hash.t | Empty_transaction of Contract.t | Tx_rollup_feature_disabled | Tx_rollup_invalid_transaction_ticket_amount @@ -484,6 +497,118 @@ let () = (function | Update_consensus_key_on_unregistered_delegate c -> Some c | _ -> None) (fun c -> Update_consensus_key_on_unregistered_delegate c) ; + register_error_kind + `Temporary + ~id:"operation.drain_delegate_key_on_unregistered_delegate" + ~title:"Drain delegate key on an unregistered delegate" + ~description:"Cannot drain an unregistered delegate." + ~pp:(fun ppf c -> + Format.fprintf + ppf + "Cannot drain an unregistered delegate %a." + Signature.Public_key_hash.pp + c) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Drain_delegate_on_unregistered_delegate c -> Some c | _ -> None) + (fun c -> Drain_delegate_on_unregistered_delegate c) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.inactive_key" + ~title:"Drain delegate with an inactive consensus key" + ~description:"Cannot drain with an inactive consensus key." + ~pp:(fun ppf (delegate, consensus_key, active_consensus_key) -> + Format.fprintf + ppf + "Consensus key %a is not active consensus key for delegate %a. The \ + active consensus key is %a." + Signature.Public_key_hash.pp + consensus_key + Signature.Public_key_hash.pp + delegate + Signature.Public_key_hash.pp + active_consensus_key) + Data_encoding.( + obj3 + (req "delegate" Signature.Public_key_hash.encoding) + (req "consensus_key" Signature.Public_key_hash.encoding) + (req "active_consensus_key" Signature.Public_key_hash.encoding)) + (function + | Invalid_drain_delegate_inactive_key + {delegate; consensus_key; active_consensus_key} -> + Some (delegate, consensus_key, active_consensus_key) + | _ -> None) + (fun (delegate, consensus_key, active_consensus_key) -> + Invalid_drain_delegate_inactive_key + {delegate; consensus_key; active_consensus_key}) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.no_consensus_key" + ~title:"Drain a delegate without consensus key" + ~description:"Cannot drain a delegate without consensus key." + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "There is no active consensus key for delegate %a." + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Invalid_drain_delegate_no_consensus_key c -> Some c | _ -> None) + (fun c -> Invalid_drain_delegate_no_consensus_key c) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.noop" + ~title:"Invalid drain delegate: noop" + ~description:"Cannot drain a delegate to itself." + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "The destination of a drain operation cannot be the delegate itself \ + (%a)." + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Invalid_drain_delegate_noop c -> Some c | _ -> None) + (fun c -> Invalid_drain_delegate_noop c) ; + register_error_kind + `Temporary + ~id:"operation.unauthorized_key_for_drain" + ~title:"Unauthorized key for draining a delegate" + ~description:"Unauthorized key for draining a delegate." + ~pp:(fun ppf c -> + Format.fprintf + ppf + "The source key (%a) is not an active consensus key. It cannot be used \ + for draining a delegate." + Signature.Public_key_hash.pp + c) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Unauthorized_key_for_drain c -> Some c | _ -> None) + (fun c -> Unauthorized_key_for_drain c) ; + register_error_kind + `Temporary + ~id:"operation.invalid_drain.no_allocation_burn" + ~title:"Drain delegate without enough balance for allocation burn" + ~description:"Cannot drain without enough allocation burn." + ~pp:(fun ppf (delegate, destination) -> + Format.fprintf + ppf + "Cannot drain delegate %a to %a. The destination is not allocated and \ + there is not enough funds in the delegate account to pay for the \ + allocation burn." + Signature.Public_key_hash.pp + delegate + Signature.Public_key_hash.pp + destination) + Data_encoding.( + obj2 + (req "delegate" Signature.Public_key_hash.encoding) + (req "destination" Signature.Public_key_hash.encoding)) + (function + | Invalid_drain_delegate_no_allocation_burn {delegate; destination} -> + Some (delegate, destination) + | _ -> None) + (fun (delegate, destination) -> + Invalid_drain_delegate_no_allocation_burn {delegate; destination}) ; register_error_kind `Branch ~id:"contract.empty_transaction" @@ -1940,7 +2065,7 @@ let precheck_manager_contents (type kind) ctxt (op : kind Kind.manager contents) @@ (* See comment in the Transaction branch *) ( Script.force_decode_in_context ~consume_deserialization_gas ctxt value >|? fun (_value, ctxt) -> ctxt ) - | Delegation _ | Set_deposits_limit _ | Update_consensus_key _ -> return ctxt + | Delegation _ | Set_deposits_limit _ | Update_consensus_key _ | Tx_rollup_origination -> assert_tx_rollup_feature_enabled ctxt >|=? fun () -> ctxt | Tx_rollup_submit_batch {content; _} -> @@ -2532,6 +2657,49 @@ let check_manager_signature ctxt chain_id (op : _ Kind.manager contents_list) find_manager_public_key ctxt op >>=? fun public_key -> Lwt.return (Operation.check_signature public_key chain_id raw_operation) +let check_drain ctxt + (Single (Drain_delegate {delegate; destination; consensus_key})) = + Delegate.registered ctxt delegate >>= fun is_registered -> + error_unless is_registered (Drain_delegate_on_unregistered_delegate delegate) + >>?= fun () -> + Delegate.Consensus_key.active_pubkey ctxt delegate >>=? fun active_pk -> + if not (Signature.Public_key_hash.equal active_pk.consensus_pkh consensus_key) + then + fail + (Invalid_drain_delegate_inactive_key + { + delegate; + consensus_key; + active_consensus_key = active_pk.consensus_pkh; + }) + else if Signature.Public_key_hash.equal active_pk.consensus_pkh delegate then + fail (Invalid_drain_delegate_no_consensus_key delegate) + else if Signature.Public_key_hash.equal destination delegate then + fail (Invalid_drain_delegate_noop delegate) + else return active_pk.consensus_pk + +let precheck_drain ctxt + (Single (Drain_delegate {delegate; destination; _}) as op) = + check_drain ctxt op >>=? fun _active_pk -> + Contract.allocated ctxt (Contract.Implicit destination) + >>= fun is_destination_allocated -> + (if is_destination_allocated then ok Tez.zero + else + let cost_per_byte = Constants.cost_per_byte ctxt in + let origination_size = Constants.origination_size ctxt in + Tez.(cost_per_byte *? Int64.of_int origination_size)) + >>?= fun min_balance -> + Contract.get_balance ctxt (Contract.Implicit delegate) >>=? fun balance -> + if Tez.(balance < min_balance) then + fail (Invalid_drain_delegate_no_allocation_burn {delegate; destination}) + else return_unit + +let check_drain_signature ctxt chain_id (Single (Drain_delegate {delegate; _})) + raw_operation = + Delegate.Consensus_key.active_pubkey ctxt delegate >>=? fun active_pk -> + Lwt.return + (Operation.check_signature active_pk.consensus_pk chain_id raw_operation) + let rec apply_manager_contents_list_rec : type kind. context -> @@ -2675,7 +2843,8 @@ let record_operation (type kind) ctxt (operation : kind operation) : context = | Single ( Failing_noop _ | Proposals _ | Ballot _ | Seed_nonce_revelation _ | Double_endorsement_evidence _ | Double_preendorsement_evidence _ - | Double_baking_evidence _ | Activate_account _ | Manager_operation _ ) + | Double_baking_evidence _ | Activate_account _ | Drain_delegate _ + | Manager_operation _ ) | Cons (Manager_operation _, _) -> let hash = Operation.hash operation in record_non_consensus_operation_hash ctxt hash @@ -3219,6 +3388,26 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) mode >>?= fun () -> Amendment.record_ballot ctxt source proposal ballot >|=? fun ctxt -> (ctxt, Single_result Ballot_result) + | Single (Drain_delegate {delegate; destination; consensus_key = _}) as op -> + check_drain ctxt op >>=? fun active_pk -> + Operation.check_signature active_pk chain_id operation >>?= fun () -> + Delegate.drain ctxt ~delegate ~destination + >>=? fun ( ctxt, + allocated_destination_contract, + fees, + drain_balance_updates ) -> + Token.transfer + ctxt + (`Delegate_balance delegate) + (`Contract (Contract.Implicit payload_producer.Consensus_key.delegate)) + fees + >>=? fun (ctxt, fees_balance_updates) -> + let balance_updates = drain_balance_updates @ fees_balance_updates in + return + ( ctxt, + Single_result + (Drain_delegate_result + {balance_updates; allocated_destination_contract}) ) | Single (Failing_noop _) -> (* Failing_noop _ always fails *) fail Failing_noop_error diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index c7a85192d5b3..beacfae230e7 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -210,4 +210,14 @@ val check_manager_signature : Chain_id.t -> 'a Kind.manager contents_list -> 'b operation -> - (unit, error trace) result Lwt.t + unit tzresult Lwt.t + +val precheck_drain : + context -> Kind.drain_delegate contents_list -> unit tzresult Lwt.t + +val check_drain_signature : + context -> + Chain_id.t -> + Kind.drain_delegate contents_list -> + Kind.drain_delegate operation -> + unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index cd8ee1e4dae9..fe097547cd6f 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -1490,6 +1490,11 @@ type 'kind contents_result = -> Kind.activate_account contents_result | Proposals_result : Kind.proposals contents_result | Ballot_result : Kind.ballot contents_result + | Drain_delegate_result : { + balance_updates : Receipt.balance_updates; + allocated_destination_contract : bool; + } + -> Kind.drain_delegate contents_result | Manager_operation_result : { balance_updates : Receipt.balance_updates; operation_result : 'kind manager_operation_result; @@ -1523,9 +1528,6 @@ let equal_manager_kind : Kind.Update_consensus_key_manager_kind ) -> Some Eq | Kind.Update_consensus_key_manager_kind, _ -> None - | ( Kind.Register_global_constant_manager_kind, - Kind.Register_global_constant_manager_kind ) -> - Some Eq | Kind.Register_global_constant_manager_kind, _ -> None | Kind.Set_deposits_limit_manager_kind, Kind.Set_deposits_limit_manager_kind -> @@ -1801,6 +1803,34 @@ module Encoding = struct inj = (fun () -> Ballot_result); } + let drain_delegate_case = + Case + { + op_case = Operation.Encoding.drain_delegate_case; + encoding = + Data_encoding.( + obj2 + (dft "balance_updates" Receipt.balance_updates_encoding []) + (dft "allocated_destination_contract" bool false)); + select = + (function + | Contents_result (Drain_delegate_result _ as op) -> Some op + | _ -> None); + mselect = + (function + | Contents_and_result ((Drain_delegate _ as op), res) -> Some (op, res) + | _ -> None); + proj = + (function + | Drain_delegate_result + {balance_updates; allocated_destination_contract} -> + (balance_updates, allocated_destination_contract)); + inj = + (fun (balance_updates, allocated_destination_contract) -> + Drain_delegate_result + {balance_updates; allocated_destination_contract}); + } + let[@coq_axiom_with_reason "gadt"] make_manager_case (type kind) (Operation.Encoding.Case op_case : kind Kind.manager Operation.Encoding.case) @@ -1862,6 +1892,7 @@ module Encoding = struct | Contents_result (Double_preendorsement_evidence_result _) -> None | Contents_result (Double_baking_evidence_result _) -> None | Contents_result (Activate_account_result _) -> None + | Contents_result (Drain_delegate_result _) -> None | Contents_result Proposals_result -> None); mselect; proj = @@ -2164,6 +2195,7 @@ let contents_result_encoding = make activate_account_case; make proposals_case; make ballot_case; + make drain_delegate_case; make reveal_case; make transaction_case; make origination_case; @@ -2229,6 +2261,7 @@ let contents_and_result_encoding = make register_global_constant_case; make set_deposits_limit_case; make update_consensus_key_case; + make drain_delegate_case; make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; @@ -2370,6 +2403,8 @@ let kind_equal : | Proposals _, _ -> None | Ballot _, Ballot_result -> Some Eq | Ballot _, _ -> None + | Drain_delegate _, Drain_delegate_result _ -> Some Eq + | Drain_delegate _, _ -> None | Failing_noop _, _ -> (* the Failing_noop operation always fails and can't have result *) None diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index d39f0cc35f29..71a57aabb3f3 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -118,6 +118,11 @@ and 'kind contents_result = -> Kind.activate_account contents_result | Proposals_result : Kind.proposals contents_result | Ballot_result : Kind.ballot contents_result + | Drain_delegate_result : { + balance_updates : Receipt.balance_updates; + allocated_destination_contract : bool; + } + -> Kind.drain_delegate contents_result | Manager_operation_result : { balance_updates : Receipt.balance_updates; operation_result : 'kind manager_operation_result; diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 96aac84dde1e..38c2270529a0 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -297,3 +297,50 @@ module Contract = struct set_self_delegate c delegate | _ -> set_delegate c contract delegate end + +type error += (* `Temporary *) Invalid_drain + +let () = + register_error_kind + `Temporary + ~id:"delegate.invalid_drain" + ~title:"Invalid Drain operation" + ~description: + "The drain operation is not signed by the active consensus key." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "The drain operation is invalid: not signed by the active consensus key") + Data_encoding.empty + (function Invalid_drain -> Some () | _ -> None) + (fun () -> Invalid_drain) + +let drain ctxt ~delegate ~destination = + let open Lwt_tzresult_syntax in + let*! is_destination_allocated = + Contract_storage.allocated ctxt (Contract_repr.Implicit destination) + in + let* ctxt, _, balance_updates1 = + if not is_destination_allocated then + Fees_storage.burn_origination_fees + ctxt + ~storage_limit:(Z.of_int (Constants_storage.origination_size ctxt)) + ~payer:(`Delegate_balance delegate) + else return (ctxt, Z.zero, []) + in + let* manager_balance = balance ctxt delegate in + let*? one_percent = Tez_repr.(manager_balance /? 100L) in + let fees = Tez_repr.(min one one_percent) in + let*? transfered = Tez_repr.(manager_balance -? fees) in + let* ctxt, balance_updates2 = + Token.transfer + ctxt + (`Delegate_balance delegate) + (`Contract (Contract_repr.Implicit destination)) + transfered + in + return + ( ctxt, + not is_destination_allocated, + fees, + balance_updates1 @ balance_updates2 ) diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 4bb421f8a644..3478986218b4 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -127,3 +127,10 @@ val manager_pubkey : Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t + +val drain : + Raw_context.t -> + delegate:Signature.Public_key_hash.t -> + destination:Signature.Public_key_hash.t -> + (Raw_context.t * bool * Tez_repr.t * Receipt_repr.balance_updates) tzresult + Lwt.t diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 5f889852a9dd..81a2f61ddd78 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -138,6 +138,7 @@ Stake_storage Contract_storage Token + Fees_storage Delegate_consensus_key Delegate_storage Delegate_missed_endorsements_storage @@ -146,7 +147,6 @@ Delegate_cycles Bootstrap_storage Vote_storage - Fees_storage Ticket_storage Liquidity_baking_storage Liquidity_baking_cpmm @@ -356,6 +356,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml @@ -365,7 +366,6 @@ delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml @@ -561,6 +561,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml @@ -570,7 +571,6 @@ delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml @@ -786,6 +786,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml @@ -795,7 +796,6 @@ delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml @@ -1007,6 +1007,7 @@ stake_storage.ml stake_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli + fees_storage.ml fees_storage.mli delegate_consensus_key.ml delegate_consensus_key.mli delegate_storage.ml delegate_storage.mli delegate_missed_endorsements_storage.ml @@ -1016,7 +1017,6 @@ delegate_cycles.ml delegate_cycles.mli bootstrap_storage.ml bootstrap_storage.mli vote_storage.ml vote_storage.mli - fees_storage.ml fees_storage.mli ticket_storage.ml ticket_storage.mli liquidity_baking_storage.ml liquidity_baking_storage.mli liquidity_baking_cpmm.ml diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 547dae479a32..d763dab346c2 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -715,6 +715,9 @@ let relative_position_within_block op1 op2 = | Single (Ballot _), Single (Ballot _) -> 0 | _, Single (Ballot _) -> 1 | Single (Ballot _), _ -> -1 + | Single (Drain_delegate _), Single (Drain_delegate _) -> 0 + | _, Single (Drain_delegate _) -> 1 + | Single (Drain_delegate _), _ -> -1 | Single (Failing_noop _), Single (Failing_noop _) -> 0 | _, Single (Failing_noop _) -> 1 | Single (Failing_noop _), _ -> -1 @@ -812,4 +815,9 @@ let precheck_manager {ctxt; _} op = * 'kind Alpha_context.Kind.manager Apply_results.prechecked_contents_list) -> () +let precheck_drain {ctxt; _} op = Apply.precheck_drain ctxt op + +let check_drain_signature {chain_id; ctxt; _} op raw_op = + Apply.check_drain_signature ctxt chain_id op raw_op + (* Vanity nonce: TBD *) diff --git a/src/proto_alpha/lib_protocol/main.mli b/src/proto_alpha/lib_protocol/main.mli index ad796cee7971..2cbb0aff5b23 100644 --- a/src/proto_alpha/lib_protocol/main.mli +++ b/src/proto_alpha/lib_protocol/main.mli @@ -148,6 +148,17 @@ val precheck_manager : 'a Alpha_context.Kind.manager Alpha_context.contents_list -> unit tzresult Lwt.t +val check_drain_signature : + validation_state -> + Alpha_context.Kind.drain_delegate Alpha_context.contents_list -> + Alpha_context.Kind.drain_delegate Alpha_context.operation -> + unit tzresult Lwt.t + +val precheck_drain : + validation_state -> + Alpha_context.Kind.drain_delegate Alpha_context.contents_list -> + unit tzresult Lwt.t + include Updater.PROTOCOL with type block_header_data = Alpha_context.Block_header.protocol_data diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index 6ce9b2221580..d761d7caf7b6 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -70,6 +70,8 @@ module Kind = struct type update_consensus_key = Update_consensus_key_kind + type drain_delegate = Drain_delegate_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -279,6 +281,12 @@ and _ contents = ballot : Vote_repr.ballot; } -> Kind.ballot contents + | Drain_delegate : { + consensus_key : Signature.Public_key_hash.t; + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + -> Kind.drain_delegate contents | Failing_noop : string -> Kind.failing_noop contents | Manager_operation : { source : Signature.public_key_hash; @@ -1350,6 +1358,27 @@ module Encoding = struct Ballot {source; period; proposal; ballot}); } + let[@coq_axiom_with_reason "gadt"] drain_delegate_case = + Case + { + tag = 8; + name = "drain_delegate"; + encoding = + obj3 + (req "consensus_key" Signature.Public_key_hash.encoding) + (req "delegate" Signature.Public_key_hash.encoding) + (req "destination" Signature.Public_key_hash.encoding); + select = + (function Contents (Drain_delegate _ as op) -> Some op | _ -> None); + proj = + (function + | Drain_delegate {consensus_key; delegate; destination} -> + (consensus_key, delegate, destination)); + inj = + (fun (consensus_key, delegate, destination) -> + Drain_delegate {consensus_key; delegate; destination}); + } + let failing_noop_case = Case { @@ -1528,6 +1557,7 @@ module Encoding = struct make delegation_case; make set_deposits_limit_case; make update_consensus_key_case; + make drain_delegate_case; make failing_noop_case; make register_global_constant_case; make tx_rollup_origination_case; @@ -1612,6 +1642,7 @@ let acceptable_passes (op : packed_operation) = | Single (Double_preendorsement_evidence _) -> [2] | Single (Double_baking_evidence _) -> [2] | Single (Activate_account _) -> [2] + | Single (Drain_delegate _) -> [2] | Single (Manager_operation _) -> [3] | Cons (Manager_operation _, _ops) -> [3] @@ -1686,8 +1717,8 @@ let check_signature (type kind) key chain_id | Single ( Failing_noop _ | Proposals _ | Ballot _ | Seed_nonce_revelation _ | Double_endorsement_evidence _ | Double_preendorsement_evidence _ - | Double_baking_evidence _ | Activate_account _ | Manager_operation _ - ) -> + | Double_baking_evidence _ | Activate_account _ | Drain_delegate _ + | Manager_operation _ ) -> check ~watermark:Generic_operation (Contents_list protocol_data.contents) @@ -1790,6 +1821,8 @@ let equal_contents_kind : type a b. a contents -> b contents -> (a, b) eq option | Proposals _, _ -> None | Ballot _, Ballot _ -> Some Eq | Ballot _, _ -> None + | Drain_delegate _, Drain_delegate _ -> Some Eq + | Drain_delegate _, _ -> None | Failing_noop _, Failing_noop _ -> Some Eq | Failing_noop _, _ -> None | Manager_operation op1, Manager_operation op2 -> ( diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index 14ce04ae9add..33177956f4e2 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -99,6 +99,8 @@ module Kind : sig type update_consensus_key = Update_consensus_key_kind + type drain_delegate = Drain_delegate_kind + type failing_noop = Failing_noop_kind type register_global_constant = Register_global_constant_kind @@ -295,6 +297,15 @@ and _ contents = ballot : Vote_repr.ballot; } -> Kind.ballot contents + (* [Drain_delegate { consensus_key ; delegate ; destination }] + transfers the spendable balance of the [delegate] to [destination] + when [consensus_key] is the active consensus key of [delegate].. *) + | Drain_delegate : { + consensus_key : Signature.Public_key_hash.t; + delegate : Signature.Public_key_hash.t; + destination : Signature.Public_key_hash.t; + } + -> Kind.drain_delegate contents (* Failing_noop: An operation never considered by the state machine and which will always fail at [apply]. This allows end-users to sign arbitrary messages which have no computational semantics. *) @@ -584,6 +595,8 @@ module Encoding : sig val ballot_case : Kind.ballot case + val drain_delegate_case : Kind.drain_delegate case + val failing_noop_case : Kind.failing_noop case val reveal_case : Kind.reveal Kind.manager case diff --git a/tezt/_regressions/rpc/alpha.client.mempool.out b/tezt/_regressions/rpc/alpha.client.mempool.out index 3686a04f6374..934e1b034237 100644 --- a/tezt/_regressions/rpc/alpha.client.mempool.out +++ b/tezt/_regressions/rpc/alpha.client.mempool.out @@ -1729,6 +1729,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", @@ -4381,6 +4409,60 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Double_preendorsement_evidence" }, + { + "tag": 8, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "consensus_key", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "delegate", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "destination", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Drain_delegate" + }, { "tag": 17, "fields": [ @@ -12339,6 +12421,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", diff --git a/tezt/_regressions/rpc/alpha.proxy.mempool.out b/tezt/_regressions/rpc/alpha.proxy.mempool.out index e549a5e6107c..fd3289f9b28f 100644 --- a/tezt/_regressions/rpc/alpha.proxy.mempool.out +++ b/tezt/_regressions/rpc/alpha.proxy.mempool.out @@ -1750,6 +1750,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", @@ -4402,6 +4430,60 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "name": "Double_preendorsement_evidence" }, + { + "tag": 8, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "consensus_key", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "delegate", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "destination", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Drain_delegate" + }, { "tag": 17, "fields": [ @@ -12360,6 +12442,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Drain_delegate", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "drain_delegate" + ] + }, + "consensus_key": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "delegate": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "destination": { + "$ref": "#/definitions/Signature.Public_key_hash" + } + }, + "required": [ + "destination", + "delegate", + "consensus_key", + "kind" + ], + "additionalProperties": false + }, { "title": "Failing_noop", "type": "object", -- GitLab From 1eb52a1f7ee24aecd0b1be2cf6a2cf6f40ee1a0a Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 32/35] Proto/Client: add `tezos-client drain delegate to ` --- .../lib_client/client_proto_context.ml | 22 +++++++ .../lib_client/client_proto_context.mli | 15 +++++ .../client_proto_context_commands.ml | 59 +++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/src/proto_alpha/lib_client/client_proto_context.ml b/src/proto_alpha/lib_client/client_proto_context.ml index 42afa109f1ff..0ad62e66205c 100644 --- a/src/proto_alpha/lib_client/client_proto_context.ml +++ b/src/proto_alpha/lib_client/client_proto_context.ml @@ -400,6 +400,28 @@ let update_consensus_key cctxt ~chain ~block ?confirmations ?dry_run | Apply_results.Single_and_result ((Manager_operation _ as op), result) -> return (oph, op, result) +let drain_delegate cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing + ?simulation ~consensus_sk ~consensus_pkh ?(destination = consensus_pkh) + ~delegate () = + let operation = + Single + (Drain_delegate {consensus_key = consensus_pkh; delegate; destination}) + in + Injection.inject_operation + cctxt + ~chain + ~block + ?confirmations + ?dry_run + ?verbose_signing + ?simulation + ~src_sk:consensus_sk + operation + >>=? fun (oph, op, result) -> + match Apply_results.pack_contents_list op result with + | Apply_results.Single_and_result ((Drain_delegate _ as op), result) -> + return (oph, op, result) + let set_deposits_limit cctxt ~chain ~block ?confirmations ?dry_run ?verbose_signing ?simulation ?fee contract ~src_pk ~manager_sk ~fee_parameter limit_opt = diff --git a/src/proto_alpha/lib_client/client_proto_context.mli b/src/proto_alpha/lib_client/client_proto_context.mli index 73d6cb1e798d..4c13216cd894 100644 --- a/src/proto_alpha/lib_client/client_proto_context.mli +++ b/src/proto_alpha/lib_client/client_proto_context.mli @@ -147,6 +147,21 @@ val update_consensus_key : Signature.public_key -> Kind.update_consensus_key Kind.manager Injection.result tzresult Lwt.t +val drain_delegate : + #Protocol_client_context.full -> + chain:Shell_services.chain -> + block:Shell_services.block -> + ?confirmations:int -> + ?dry_run:bool -> + ?verbose_signing:bool -> + ?simulation:bool -> + consensus_sk:Client_keys.sk_uri -> + consensus_pkh:Signature.public_key_hash -> + ?destination:Signature.public_key_hash -> + delegate:Signature.public_key_hash -> + unit -> + Kind.drain_delegate Injection.result tzresult Lwt.t + val set_deposits_limit : #Protocol_client_context.full -> chain:Shell_services.chain -> diff --git a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml index 00e024799965..14fdbc765d87 100644 --- a/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml +++ b/src/proto_alpha/lib_client_commands/client_proto_context_commands.ml @@ -1570,6 +1570,65 @@ let commands_rw () = >>= function | Ok _ -> return_unit | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Drain all funds from a delegate." + (args2 dry_run_switch verbose_signing_switch) + (prefixes ["drain"; "delegate"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["to"] + @@ Public_key_hash.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (dry_run, verbose_signing) delegate_pkh consensus_pkh cctxt -> + Client_keys.get_key cctxt consensus_pkh + >>=? fun (_, _consensus_pk, consensus_sk) -> + drain_delegate + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~verbose_signing + ~consensus_pkh + ~consensus_sk + ~delegate:delegate_pkh + () + >>= function + | Ok _ -> return_unit + | Error el -> Lwt.return_error el); + command + ~group + ~desc:"Drain all funds from a delegate." + (args2 dry_run_switch verbose_signing_switch) + (prefixes ["drain"; "delegate"] + @@ Public_key_hash.source_param ~name:"mgr" ~desc:"the delegate key" + @@ prefixes ["to"] + @@ Public_key_hash.source_param ~name:"key" ~desc:"the destination key" + @@ prefixes ["with"] + @@ Public_key_hash.source_param ~name:"key" ~desc:"the consensus key" + @@ stop) + (fun (dry_run, verbose_signing) + delegate_pkh + destination_pkh + consensus_pkh + cctxt -> + Client_keys.get_key cctxt consensus_pkh + >>=? fun (_, _consensus_pk, consensus_sk) -> + drain_delegate + cctxt + ~chain:cctxt#chain + ~block:cctxt#block + ?confirmations:cctxt#confirmations + ~dry_run + ~verbose_signing + ~consensus_pkh + ~consensus_sk + ~destination:destination_pkh + ~delegate:delegate_pkh + () + >>= function + | Ok _ -> return_unit + | Error el -> Lwt.return_error el); command ~desc:"Wait until an operation is included in a block" (args3 -- GitLab From e42bae60aedf9434bae6b94a120690d2b06933ef Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Sun, 8 May 2022 19:46:31 +0200 Subject: [PATCH 33/35] Proto/Plugin: prioritize `Drain_delegate` over any manager op --- src/proto_alpha/lib_plugin/plugin.ml | 139 ++++++++++++++++-- src/proto_alpha/lib_plugin/test/generators.ml | 2 +- .../lib_plugin/test/test_filter_state.ml | 4 +- src/proto_alpha/lib_plugin/test/test_utils.ml | 57 ++++--- 4 files changed, 170 insertions(+), 32 deletions(-) diff --git a/src/proto_alpha/lib_plugin/plugin.ml b/src/proto_alpha/lib_plugin/plugin.ml index de9dca51a667..613a05a152b1 100644 --- a/src/proto_alpha/lib_plugin/plugin.ml +++ b/src/proto_alpha/lib_plugin/plugin.ml @@ -225,6 +225,8 @@ module Mempool = struct weight : Q.t; } + type op_info = Manager_op of manager_op_info | Drained of Operation_hash.t + type manager_op_weight = {operation_hash : Operation_hash.t; weight : Q.t} let op_weight_of_info (info : manager_op_info) : manager_op_weight = @@ -243,7 +245,7 @@ module Mempool = struct type state = { grandparent_level_start : Timestamp.t option; round_zero_duration : Period.t option; - op_prechecked_managers : manager_op_info Signature.Public_key_hash.Map.t; + op_prechecked_managers : op_info Signature.Public_key_hash.Map.t; (** All managers that are the source of manager operations prechecked in the mempool. Each manager in the map is associated to a record of type [manager_op_info] (See for record details above). @@ -363,7 +365,8 @@ module Mempool = struct let ops_prechecked = match !removed_op with | None -> filter_state.ops_prechecked - | Some op -> + | Some (Drained _) -> filter_state.ops_prechecked + | Some (Manager_op op) -> ManagerOpWeightSet.remove (op_weight_of_info op) filter_state.ops_prechecked @@ -417,6 +420,7 @@ module Mempool = struct type Environment.Error_monad.error += | Manager_restriction of {oph : Operation_hash.t; fee : Tez.t} + | Manager_drained of {oph : Operation_hash.t} let () = Environment.Error_monad.register_error_kind @@ -439,7 +443,24 @@ module Mempool = struct (req "operation_hash" Operation_hash.encoding) (req "operation_fee" Tez.encoding)) (function Manager_restriction {oph; fee} -> Some (oph, fee) | _ -> None) - (fun (oph, fee) -> Manager_restriction {oph; fee}) + (fun (oph, fee) -> Manager_restriction {oph; fee}) ; + Environment.Error_monad.register_error_kind + `Temporary + ~id:"prefilter.manager_drained" + ~title: + "Only one manager operation per manager per block allowed (drained)" + ~description: + "Only one manager operation per manager per block allowed (drained)" + ~pp:(fun ppf oph -> + Format.fprintf + ppf + "Only one manager operation per manager per block allowed (found \ + Drain_delegate operation %a which takes precedence)" + Operation_hash.pp + oph) + Data_encoding.(obj1 (req "operation_hash" Operation_hash.encoding)) + (function Manager_drained {oph} -> Some oph | _ -> None) + (fun oph -> Manager_drained {oph}) type Environment.Error_monad.error += | Manager_operation_replaced of { @@ -543,13 +564,18 @@ module Mempool = struct filter_state.op_prechecked_managers with | None -> `Fresh + | Some (Drained old_hash) -> + `Fail + (`Branch_delayed + [Environment.wrap_tzerror (Manager_drained {oph = old_hash})]) | Some - { - operation_hash = old_hash; - gas_limit = old_gas; - fee = old_fee; - weight = _; - } -> + (Manager_op + { + operation_hash = old_hash; + gas_limit = old_gas; + fee = old_fee; + weight = _; + }) -> (* Manager already seen: one manager per block limitation triggered. Can replace old operation if new operation's fees are better *) if @@ -1133,7 +1159,7 @@ module Mempool = struct (* Manager not seen yet, record it for next ops *) Signature.Public_key_hash.Map.add source - info + (Manager_op info) filter_state.op_prechecked_managers; operation_hash_to_manager = Operation_hash.Map.add oph source filter_state.operation_hash_to_manager @@ -1144,6 +1170,78 @@ module Mempool = struct min_prechecked_op_weight; } + let precheck_drain filter_state validation_state oph shell + ({contents; _} as protocol_data : Kind.drain_delegate protocol_data) + delegate ~nb_successful_prechecks = + let precheck_drain_and_check_signature ~on_success = + ( Main.precheck_drain validation_state contents >>=? fun () -> + let (raw_operation : Kind.drain_delegate operation) = + Alpha_context.{shell; protocol_data} + in + if Compare.Int.(nb_successful_prechecks > 0) then + (* Signature succesfully checked at least once. *) + return_unit + else + (* Signature probably never checked. *) + Main.check_drain_signature validation_state contents raw_operation ) + >|= function + | Ok () -> on_success + | Error err -> ( + let err = Environment.wrap_tztrace err in + match classify_trace err with + | Branch -> `Branch_refused err + | Permanent -> `Refused err + | Temporary -> `Branch_delayed err + | Outdated -> `Outdated err) + in + match + Signature.Public_key_hash.Map.find + delegate + filter_state.op_prechecked_managers + with + | None -> + let on_success = `Prechecked_drain `No_replace in + precheck_drain_and_check_signature ~on_success + | Some (Drained old_hash) -> + Lwt.return + (`Branch_delayed + [Environment.wrap_tzerror (Manager_drained {oph = old_hash})]) + | Some (Manager_op {operation_hash = old_oph; _}) -> + let err = + Environment.wrap_tzerror + @@ Manager_operation_replaced {old_hash = old_oph; new_hash = oph} + in + precheck_drain_and_check_signature + ~on_success:(`Prechecked_drain (`Replace (old_oph, `Outdated [err]))) + + let add_drain_restriction filter_state oph delegate replacement = + let filter_state = + match replacement with + | `No_replace -> filter_state + | `Replace (oph, _class) -> remove ~filter_state oph + in + let prechecked_operations_count = + if Operation_hash.Map.mem oph filter_state.operation_hash_to_manager then + filter_state.prechecked_operations_count + else filter_state.prechecked_operations_count + 1 + in + { + filter_state with + op_prechecked_managers = + (* Manager not seen yet, record it for next ops *) + Signature.Public_key_hash.Map.add + delegate + (Drained oph) + filter_state.op_prechecked_managers; + operation_hash_to_manager = + Operation_hash.Map.add + oph + delegate + filter_state.operation_hash_to_manager + (* Record which manager is used for the operation hash. *); + prechecked_operations_count; + } + let precheck : config -> filter_state:state -> @@ -1163,6 +1261,25 @@ module Mempool = struct oph {shell = shell_header; protocol_data = Operation_data protocol_data} ~nb_successful_prechecks -> + let precheck_drain protocol_data delegate = + precheck_drain + filter_state + validation_state + oph + shell_header + protocol_data + delegate + ~nb_successful_prechecks + >|= function + | `Prechecked_drain replacement -> + let filter_state = + add_drain_restriction filter_state oph delegate replacement + in + `Passed_precheck (filter_state, replacement) + | (`Refused _ | `Branch_delayed _ | `Branch_refused _ | `Outdated _) as + errs -> + errs + in let precheck_manager protocol_data source op = match get_manager_operation_gas_and_fee op with | Error err -> Lwt.return (`Refused (Environment.wrap_tztrace err)) @@ -1198,6 +1315,8 @@ module Mempool = struct errs) in match protocol_data.contents with + | Single (Drain_delegate {delegate; _}) -> + precheck_drain protocol_data delegate | Single (Manager_operation {source; _}) as op -> precheck_manager protocol_data source op | Cons (Manager_operation {source; _}, _) as op -> diff --git a/src/proto_alpha/lib_plugin/test/generators.ml b/src/proto_alpha/lib_plugin/test/generators.ml index 38d6e4e13509..367822582621 100644 --- a/src/proto_alpha/lib_plugin/test/generators.ml +++ b/src/proto_alpha/lib_plugin/test/generators.ml @@ -80,7 +80,7 @@ let filter_state_gen : Plugin.Mempool.state QCheck2.Gen.t = op_prechecked_managers = Signature.Public_key_hash.Map.add pkh - info + (Manager_op info) state.op_prechecked_managers; operation_hash_to_manager = Operation_hash.Map.add oph pkh state.operation_hash_to_manager; diff --git a/src/proto_alpha/lib_plugin/test/test_filter_state.ml b/src/proto_alpha/lib_plugin/test/test_filter_state.ml index 1a06a6762e87..f28f180505ee 100644 --- a/src/proto_alpha/lib_plugin/test/test_filter_state.ml +++ b/src/proto_alpha/lib_plugin/test/test_filter_state.ml @@ -95,7 +95,7 @@ let test_check_manager_restriction_fail = op_prechecked_managers = Signature.Public_key_hash.Map.add pkh - {op_info with fee = Alpha_context.Tez.one} + (Manager_op {op_info with fee = Alpha_context.Tez.one}) (* We force higher fee than below: [one > zero]. *) filter_state.op_prechecked_managers; } @@ -150,7 +150,7 @@ let test_check_manager_restriction_replace = op_prechecked_managers = Signature.Public_key_hash.Map.add pkh - op_info + (Manager_op op_info) filter_state.op_prechecked_managers; } in diff --git a/src/proto_alpha/lib_plugin/test/test_utils.ml b/src/proto_alpha/lib_plugin/test/test_utils.ml index cf25d367381e..d26e76f16cc6 100644 --- a/src/proto_alpha/lib_plugin/test/test_utils.ml +++ b/src/proto_alpha/lib_plugin/test/test_utils.ml @@ -40,18 +40,28 @@ let pp_prechecked_managers fmt set = Format.fprintf fmt "[%a]" - (Format.pp_print_list (fun ppf (pkh, (op_info : manager_op_info)) -> - Format.fprintf - ppf - "(%a -> (hash:%a,gas:%a,fee:%a))" - Signature.Public_key_hash.pp - pkh - Operation_hash.pp - op_info.operation_hash - Alpha_context.Gas.Arith.pp - op_info.gas_limit - Alpha_context.Tez.pp - op_info.fee)) + (Format.pp_print_list (fun ppf (pkh, info) -> + match info with + | Drained oph -> + Format.fprintf + ppf + "(%a -> (hash:%a,drained))" + Signature.Public_key_hash.pp + pkh + Operation_hash.pp + oph + | Manager_op op_info -> + Format.fprintf + ppf + "(%a -> (hash:%a,gas:%a,fee:%a))" + Signature.Public_key_hash.pp + pkh + Operation_hash.pp + op_info.operation_hash + Alpha_context.Gas.Arith.pp + op_info.gas_limit + Alpha_context.Tez.pp + op_info.fee)) (Signature.Public_key_hash.Map.bindings set) let pp_operation_hash_manager fmt map = @@ -113,13 +123,22 @@ let pp_state fmt state = state.min_prechecked_op_weight let eq_prechecked_managers = - Signature.Public_key_hash.Map.equal - (fun - ({operation_hash = oph1; gas_limit = _; fee = _; weight = _} : - manager_op_info) - ({operation_hash = oph2; gas_limit = _; fee = _; weight = _} : - manager_op_info) - -> Operation_hash.equal oph1 oph2) + Signature.Public_key_hash.Map.equal (fun op1 op2 -> + let oph1 = + match op1 with + | Drained oph1 + | Manager_op {operation_hash = oph1; gas_limit = _; fee = _; weight = _} + -> + oph1 + in + let oph2 = + match op2 with + | Drained oph2 + | Manager_op {operation_hash = oph2; gas_limit = _; fee = _; weight = _} + -> + oph2 + in + Operation_hash.equal oph1 oph2) (* This function needs to be updated if the filter state is extended *) let eq_state s1 s2 = -- GitLab From 6f8019376b1524d533dea6c9a56701a0e2e1d7ba Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Fri, 20 May 2022 17:46:00 +0200 Subject: [PATCH 34/35] Proto/Plugin: filter out `Drain_delegate` without enough fees --- src/proto_alpha/lib_plugin/plugin.ml | 24 ++++++++++++-- .../lib_protocol/alpha_context.mli | 2 ++ src/proto_alpha/lib_protocol/apply.ml | 9 ++--- src/proto_alpha/lib_protocol/apply.mli | 4 ++- .../lib_protocol/delegate_storage.ml | 8 +++-- .../lib_protocol/delegate_storage.mli | 2 ++ src/proto_alpha/lib_protocol/main.mli | 13 +++++++- .../_regressions/rpc/alpha.client.mempool.out | 33 ++++++++++++------- tezt/_regressions/rpc/alpha.proxy.mempool.out | 33 ++++++++++++------- 9 files changed, 95 insertions(+), 33 deletions(-) diff --git a/src/proto_alpha/lib_plugin/plugin.ml b/src/proto_alpha/lib_plugin/plugin.ml index 613a05a152b1..671409179316 100644 --- a/src/proto_alpha/lib_plugin/plugin.ml +++ b/src/proto_alpha/lib_plugin/plugin.ml @@ -104,6 +104,7 @@ module Mempool = struct minimal_fees : Tez.t; minimal_nanotez_per_gas_unit : nanotez; minimal_nanotez_per_byte : nanotez; + minimal_drain_fees : Tez.t; allow_script_failure : bool; (** If [true], this makes [post_filter_manager] unconditionally return [`Passed_postfilter filter_state], no matter the operation's @@ -125,6 +126,9 @@ module Mempool = struct let default_minimal_fees = match Tez.of_mutez 100L with None -> assert false | Some t -> t + let default_minimal_drain_fees = + match Tez.of_mutez 100L with None -> assert false | Some t -> t + let default_minimal_nanotez_per_gas_unit = Q.of_int 100 let default_minimal_nanotez_per_byte = Q.of_int 1000 @@ -146,6 +150,7 @@ module Mempool = struct minimal_fees = default_minimal_fees; minimal_nanotez_per_gas_unit = default_minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte = default_minimal_nanotez_per_byte; + minimal_drain_fees = default_minimal_drain_fees; allow_script_failure = true; clock_drift = None; replace_by_fee_factor = @@ -159,6 +164,7 @@ module Mempool = struct conv (fun { minimal_fees; + minimal_drain_fees; minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte; allow_script_failure; @@ -167,6 +173,7 @@ module Mempool = struct max_prechecked_manager_operations; } -> ( minimal_fees, + minimal_drain_fees, minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, allow_script_failure, @@ -174,6 +181,7 @@ module Mempool = struct replace_by_fee_factor, max_prechecked_manager_operations )) (fun ( minimal_fees, + minimal_drain_fees, minimal_nanotez_per_gas_unit, minimal_nanotez_per_byte, allow_script_failure, @@ -182,6 +190,7 @@ module Mempool = struct max_prechecked_manager_operations ) -> { minimal_fees; + minimal_drain_fees; minimal_nanotez_per_gas_unit; minimal_nanotez_per_byte; allow_script_failure; @@ -189,8 +198,12 @@ module Mempool = struct replace_by_fee_factor; max_prechecked_manager_operations; }) - (obj7 + (obj8 (dft "minimal_fees" Tez.encoding default_config.minimal_fees) + (dft + "minimal_drain_fees" + Tez.encoding + default_config.minimal_drain_fees) (dft "minimal_nanotez_per_gas_unit" nanotez_enc @@ -1170,11 +1183,15 @@ module Mempool = struct min_prechecked_op_weight; } - let precheck_drain filter_state validation_state oph shell + let precheck_drain config filter_state validation_state oph shell ({contents; _} as protocol_data : Kind.drain_delegate protocol_data) delegate ~nb_successful_prechecks = let precheck_drain_and_check_signature ~on_success = - ( Main.precheck_drain validation_state contents >>=? fun () -> + ( Main.precheck_drain validation_state contents >>=? fun fees -> + Environment.Error_monad.fail_when + Tez.(fees < config.minimal_drain_fees) + Fees_too_low + >>=? fun () -> let (raw_operation : Kind.drain_delegate operation) = Alpha_context.{shell; protocol_data} in @@ -1263,6 +1280,7 @@ module Mempool = struct ~nb_successful_prechecks -> let precheck_drain protocol_data delegate = precheck_drain + config filter_state validation_state oph diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 3871697f2768..a419c7d4a53c 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2226,6 +2226,8 @@ module Delegate : sig destination:public_key_hash -> (context * bool * Tez.t * Receipt.balance_updates) tzresult Lwt.t + val drain_fees : Tez.t -> Tez.t tzresult + type participation_info = { expected_cycle_activity : int; minimal_cycle_activity : int; diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index ba302df68c1e..2fd5a2d8eaf8 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2688,11 +2688,12 @@ let precheck_drain ctxt let cost_per_byte = Constants.cost_per_byte ctxt in let origination_size = Constants.origination_size ctxt in Tez.(cost_per_byte *? Int64.of_int origination_size)) - >>?= fun min_balance -> + >>?= fun allocation_burn -> Contract.get_balance ctxt (Contract.Implicit delegate) >>=? fun balance -> - if Tez.(balance < min_balance) then - fail (Invalid_drain_delegate_no_allocation_burn {delegate; destination}) - else return_unit + match Tez.(balance -? allocation_burn) with + | Error _ -> + fail (Invalid_drain_delegate_no_allocation_burn {delegate; destination}) + | Ok balance -> Lwt.return (Delegate.drain_fees balance) let check_drain_signature ctxt chain_id (Single (Drain_delegate {delegate; _})) raw_operation = diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index beacfae230e7..43403c38a898 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -212,9 +212,11 @@ val check_manager_signature : 'b operation -> unit tzresult Lwt.t +(** See [Main.precheck_drain] *) val precheck_drain : - context -> Kind.drain_delegate contents_list -> unit tzresult Lwt.t + context -> Kind.drain_delegate contents_list -> Tez.t tzresult Lwt.t +(** See [Main.check_drain_signature] *) val check_drain_signature : context -> Chain_id.t -> diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 38c2270529a0..c323433c11ca 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -315,6 +315,11 @@ let () = (function Invalid_drain -> Some () | _ -> None) (fun () -> Invalid_drain) +let drain_fees manager_balance = + let open Result_syntax in + let* one_percent = Tez_repr.(manager_balance /? 100L) in + return Tez_repr.(min one one_percent) + let drain ctxt ~delegate ~destination = let open Lwt_tzresult_syntax in let*! is_destination_allocated = @@ -329,8 +334,7 @@ let drain ctxt ~delegate ~destination = else return (ctxt, Z.zero, []) in let* manager_balance = balance ctxt delegate in - let*? one_percent = Tez_repr.(manager_balance /? 100L) in - let fees = Tez_repr.(min one one_percent) in + let*? fees = drain_fees manager_balance in let*? transfered = Tez_repr.(manager_balance -? fees) in let* ctxt, balance_updates2 = Token.transfer diff --git a/src/proto_alpha/lib_protocol/delegate_storage.mli b/src/proto_alpha/lib_protocol/delegate_storage.mli index 3478986218b4..2b84244ce947 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/delegate_storage.mli @@ -134,3 +134,5 @@ val drain : destination:Signature.Public_key_hash.t -> (Raw_context.t * bool * Tez_repr.t * Receipt_repr.balance_updates) tzresult Lwt.t + +val drain_fees : Tez_repr.t -> Tez_repr.t tzresult diff --git a/src/proto_alpha/lib_protocol/main.mli b/src/proto_alpha/lib_protocol/main.mli index 2cbb0aff5b23..59a051e9b23b 100644 --- a/src/proto_alpha/lib_protocol/main.mli +++ b/src/proto_alpha/lib_protocol/main.mli @@ -148,16 +148,27 @@ val precheck_manager : 'a Alpha_context.Kind.manager Alpha_context.contents_list -> unit tzresult Lwt.t +(** [check_drain_signature validation_state op raw_operation] + The function starts by retrieving the active consensus public key [pk] + of the delegate. + + @return [Error Invalid_signature] if the signature check fails +*) val check_drain_signature : validation_state -> Alpha_context.Kind.drain_delegate Alpha_context.contents_list -> Alpha_context.Kind.drain_delegate Alpha_context.operation -> unit tzresult Lwt.t +(** [precheck_drain validation_state op] returns the fees that will be + credited to the baker if the operation [op] is solveable, returns + an error otherwise. A drain operation is solveable if it is + well-formed and can pay for the potential allocation burn of the + destination account. This function uses [Apply.precheck_drain]. *) val precheck_drain : validation_state -> Alpha_context.Kind.drain_delegate Alpha_context.contents_list -> - unit tzresult Lwt.t + Alpha_context.Tez.t tzresult Lwt.t include Updater.PROTOCOL diff --git a/tezt/_regressions/rpc/alpha.client.mempool.out b/tezt/_regressions/rpc/alpha.client.mempool.out index 934e1b034237..e7c7756202cf 100644 --- a/tezt/_regressions/rpc/alpha.client.mempool.out +++ b/tezt/_regressions/rpc/alpha.client.mempool.out @@ -154,13 +154,15 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= [{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]"},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"0","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"permanent","id":"proto.alpha.prefilter.fees_too_low"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"branch","id":"proto.alpha.contract.counter_in_the_past","contract":"[PUBLIC_KEY_HASH]","expected":"2","found":"1"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"5","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"temporary","id":"proto.alpha.contract.counter_in_the_future","contract":"[PUBLIC_KEY_HASH]","expected":"1","found":"5"}]}] ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -180,19 +182,22 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= ], "allow_script_failure": false }' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -205,19 +210,22 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= "minimal_fees": "200", "allow_script_failure": true }' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -226,19 +234,22 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= { "minimal_fees": "200" } ./tezos-client rpc post /chains/main/mempool/filter with '{}' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } ./tezos-client rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } diff --git a/tezt/_regressions/rpc/alpha.proxy.mempool.out b/tezt/_regressions/rpc/alpha.proxy.mempool.out index fd3289f9b28f..6ebc9200d13f 100644 --- a/tezt/_regressions/rpc/alpha.proxy.mempool.out +++ b/tezt/_regressions/rpc/alpha.proxy.mempool.out @@ -160,14 +160,16 @@ curl -s 'http://localhost:[PORT]/chains/main/mempool/monitor_operations?applied= [{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]"},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"0","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"permanent","id":"proto.alpha.prefilter.fees_too_low"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"1","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"branch","id":"proto.alpha.contract.counter_in_the_past","contract":"[PUBLIC_KEY_HASH]","expected":"2","found":"1"}]},{"hash":"[OPERATION_HASH]","protocol":"ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK","branch":"[BRANCH_HASH]","contents":[{"kind":"transaction","source":"[PUBLIC_KEY_HASH]","fee":"1000","counter":"5","gas_limit":"1040","storage_limit":"257","amount":"1000000","destination":"[PUBLIC_KEY_HASH]"}],"signature":"[SIGNATURE]","error":[{"kind":"temporary","id":"proto.alpha.contract.counter_in_the_future","contract":"[PUBLIC_KEY_HASH]","expected":"1","found":"5"}]}] ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -189,21 +191,24 @@ protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaAL ], "allow_script_failure": false }' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "50", "minimal_nanotez_per_gas_unit": [ "201", "5" ], +{ "minimal_fees": "50", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "201", "5" ], "minimal_nanotez_per_byte": [ "56", "3" ], "allow_script_failure": false, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -218,21 +223,24 @@ protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaAL "minimal_fees": "200", "allow_script_failure": true }' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "200", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "200", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } @@ -243,21 +251,24 @@ protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaAL protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc post /chains/main/mempool/filter with '{}' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get /chains/main/mempool/filter -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } protocol of proxy unspecified, using the node's protocol: ProtoALphaALphaALphaALphaALphaALphaALphaALphaDdp3zK ./tezos-client --mode proxy rpc get '/chains/main/mempool/filter?include_default=true' -{ "minimal_fees": "100", "minimal_nanotez_per_gas_unit": [ "100", "1" ], +{ "minimal_fees": "100", "minimal_drain_fees": "100", + "minimal_nanotez_per_gas_unit": [ "100", "1" ], "minimal_nanotez_per_byte": [ "1000", "1" ], "allow_script_failure": true, "replace_by_fee_factor": [ "21", "20" ], "max_prechecked_manager_operations": 5000 } -- GitLab From f60da9c4c7d9b8494f0f23997873a31035f2679d Mon Sep 17 00:00:00 2001 From: "G.B. Fefe" Date: Mon, 25 Apr 2022 09:26:31 +0200 Subject: [PATCH 35/35] Proto: add a minimal `tezt` scenario for `Drain_delegate` --- tezt/lib_tezos/client.ml | 16 +++++ tezt/lib_tezos/client.mli | 12 ++++ tezt/tests/consensus_key.ml | 136 ++++++++++++++++++++++++------------ 3 files changed, 120 insertions(+), 44 deletions(-) diff --git a/tezt/lib_tezos/client.ml b/tezt/lib_tezos/client.ml index 97ef5d74c31b..d045a6348f7f 100644 --- a/tezt/lib_tezos/client.ml +++ b/tezt/lib_tezos/client.ml @@ -913,6 +913,22 @@ let update_consensus_key ?hooks ?endpoint ?(wait = "none") ?burn_cap @ optional_arg "burn-cap" Tez.to_string burn_cap) |> Process.check ?expect_failure +let drain_delegate ?hooks ?endpoint ?(wait = "none") ?expect_failure ~delegate + ~consensus_key ?destination client = + let destination = + match destination with + | None -> [] + | Some destination -> [destination; "with"] + in + spawn_command + ?hooks + ?endpoint + client + (["--wait"; wait] + @ ["drain"; "delegate"; delegate; "to"] + @ destination @ [consensus_key]) + |> Process.check ?expect_failure + let spawn_originate_contract ?hooks ?log_output ?endpoint ?(wait = "none") ?init ?burn_cap ~alias ~amount ~src ~prg client = spawn_command diff --git a/tezt/lib_tezos/client.mli b/tezt/lib_tezos/client.mli index bd9c6679b847..7abaf1ec7bc1 100644 --- a/tezt/lib_tezos/client.mli +++ b/tezt/lib_tezos/client.mli @@ -748,6 +748,18 @@ val update_consensus_key : t -> unit Lwt.t +(** Run [tezos-client drain delegate with consensus key ] *) +val drain_delegate : + ?hooks:Process.hooks -> + ?endpoint:endpoint -> + ?wait:string -> + ?expect_failure:bool -> + delegate:string -> + consensus_key:string -> + ?destination:string -> + t -> + unit Lwt.t + (* TODO: https://gitlab.com/tezos/tezos/-/issues/2336 [amount] should be named [transferring] *) (* TODO: https://gitlab.com/tezos/tezos/-/issues/2336 diff --git a/tezt/tests/consensus_key.ml b/tezt/tests/consensus_key.ml index 34576df6a454..6ee1ffc51045 100644 --- a/tezt/tests/consensus_key.ml +++ b/tezt/tests/consensus_key.ml @@ -98,6 +98,7 @@ let test_update_consensus_key = let* key_a = Client.gen_and_show_keys client in let* key_b = Client.gen_and_show_keys client in let* key_c = Client.gen_and_show_keys client in + let* destination = Client.gen_and_show_keys client in let* () = Client.transfer @@ -107,6 +108,22 @@ let test_update_consensus_key = ~receiver:key_b.alias client in + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1_000_000) + ~giver:Constant.bootstrap2.alias + ~receiver:key_c.alias + client + in + let* () = + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1) + ~giver:Constant.bootstrap4.alias + ~receiver:destination.alias + client + in let* () = Client.bake_for_and_wait client in (* Trying multiple invalid updates: @@ -217,19 +234,6 @@ let test_update_consensus_key = let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in let* () = Client.bake_for_and_wait ~keys:[key_b.alias] client in - (* Set the consensus key for all delegates... *) - let* () = - Client.update_consensus_key - ~src:Constant.bootstrap1.alias - ~pk:key_a.alias - client - in - let* () = - Client.update_consensus_key - ~src:Constant.bootstrap2.alias - ~pk:key_a.alias - client - in let* () = Client.update_consensus_key ~src:Constant.bootstrap3.alias @@ -239,15 +243,10 @@ let test_update_consensus_key = let* () = Client.update_consensus_key ~src:Constant.bootstrap4.alias - ~pk:key_a.alias - client - in - let* () = - Client.update_consensus_key - ~src:Constant.bootstrap5.alias - ~pk:key_a.alias + ~pk:key_c.alias client in + (* Bake until the end of the cycle. *) let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in @@ -263,52 +262,101 @@ let test_update_consensus_key = } in - (* Bootstrap's accounts should not be able to bake anymore... *) + (* Invalid drain: unregistered delegate. *) let* () = - Client.bake_for + Client.drain_delegate ~expect_failure:true - ~keys:[Constant.bootstrap1.alias] + ~delegate:destination.alias + ~consensus_key:destination.alias + ~destination:destination.alias client in + + (* Invalid drain: bootstrap2 is has no custom consensus key. *) let* () = - Client.bake_for + Client.drain_delegate ~expect_failure:true - ~keys:[Constant.bootstrap2.alias] + ~delegate:Constant.bootstrap2.alias + ~consensus_key:Constant.bootstrap2.alias + ~destination:destination.alias client in + + (* Invalid drain: bootstrap2 is not the consensus key for bootstrap1. *) let* () = - Client.bake_for + Client.drain_delegate ~expect_failure:true - ~keys:[Constant.bootstrap3.alias] + ~delegate:Constant.bootstrap1.alias + ~consensus_key:Constant.bootstrap2.alias + ~destination:destination.alias client in + + (* Invalid drain: cannot drain to itself. *) let* () = - Client.bake_for + Client.drain_delegate ~expect_failure:true - ~keys:[Constant.bootstrap4.alias] + ~delegate:Constant.bootstrap4.alias + ~consensus_key:key_c.alias + ~destination:Constant.bootstrap4.alias client in + + let* old_balance = Client.get_balance_for ~account:destination.alias client in let* () = - Client.bake_for - ~expect_failure:true - ~keys:[Constant.bootstrap5.alias] + Client.drain_delegate + ~delegate:Constant.bootstrap4.alias + ~consensus_key:key_c.alias + ~destination:destination.alias client in - let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in - let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in - let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in - let* () = Client.bake_for_and_wait ~keys:[key_a.alias] client in + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + + (* The manager account has been drained... *) + let* b = Client.get_balance_for ~account:Constant.bootstrap4.alias client in + Check.((Tez.to_mutez b = 0) int) ~error_msg:"Manager balance is not empty" ; + + let* new_balance = Client.get_balance_for ~account:destination.alias client in + Check.((Tez.to_mutez old_balance < Tez.to_mutez new_balance) int) + ~error_msg:"Destination account has not been credited" ; + + (* Check if drain is prefered over a operation from the delegate... *) + let* old_balance5 = + Client.get_balance_for ~account:Constant.bootstrap5.alias client + in let* () = - check_current_level + Client.transfer + ~burn_cap:Tez.one + ~amount:(Tez.of_int 1) + ~giver:Constant.bootstrap3.alias + ~receiver:Constant.bootstrap5.alias client - { - level = 16; - level_position = 15; - cycle = 3; - cycle_position = 3; - expected_commitment = true; - } in + + let* old_balance = Client.get_balance_for ~account:destination.alias client in + let* () = + Client.drain_delegate + ~delegate:Constant.bootstrap3.alias + ~consensus_key:key_a.alias + ~destination:destination.alias + client + in + let* () = Client.bake_for_and_wait ~keys:[Constant.bootstrap1.alias] client in + + (* The manager account has been drained... *) + let* b = Client.get_balance_for ~account:Constant.bootstrap3.alias client in + Check.((Tez.to_mutez b = 0) int) ~error_msg:"Manager balance is not empty" ; + + let* new_balance5 = + Client.get_balance_for ~account:Constant.bootstrap5.alias client + in + Check.((Tez.to_mutez new_balance5 = Tez.to_mutez old_balance5) int) + ~error_msg:"Manager operation was included" ; + + let* new_balance = Client.get_balance_for ~account:destination.alias client in + Check.((Tez.to_mutez old_balance < Tez.to_mutez new_balance) int) + ~error_msg:"Destination account has not been credited" ; + unit let register ~protocols = test_update_consensus_key protocols -- GitLab