diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 30dd8f454a6182d7fa0b27fa13bc8f5d07a24d7c..967a974461a9ad22bb2c7d9bc86ad73cf7ee523c 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -25,6 +25,7 @@ "Slot_repr", "Tez_repr", "Deposits_repr", + "Staking_pseudotoken_repr", "Period_repr", "Time_repr", "Ratio_repr", @@ -138,6 +139,7 @@ "Unstaked_frozen_deposits_storage", "Unstake_requests_storage", "Frozen_deposits_storage", + "Staking_pseudotokens_storage", "Contract_storage", "Token", "Fees_storage", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 04b75aef3703658fea484ccdfc9d20be2c2f84b3..844c28b7439f508c4daaf0f3f4d3c43b5597840b 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -626,6 +626,11 @@ module Token = Token module Cache = Cache_repr module Unstake_requests = Unstake_requests_storage +module Staking_pseudotokens = struct + include Staking_pseudotoken_repr + include Staking_pseudotokens_storage +end + module Internal_for_tests = struct let to_raw x = x end diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index b8e035e4db64f33851601b6e0560c2783c651fcf..35a7e205fe73370bd796676542e20ff7efac6801 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -4892,6 +4892,18 @@ module Unstake_requests : sig context -> Contract.t -> stored_requests -> context tzresult Lwt.t end +(** This module re-exports definitions from {!Staking_pseudotoken_repr} and + {!Staking_pseudotokens_storage}. *) +module Staking_pseudotokens : sig + type t + + val credit_frozen_deposits_pseudotokens_for_tez_amount : + context -> public_key_hash -> Tez.t -> (context * t) tzresult Lwt.t + + val credit_costaking_pseudotokens : + context -> Contract.t -> t -> context tzresult Lwt.t +end + (** This module re-exports definitions from {!Fees_storage}. *) module Fees : sig val record_paid_storage_space : diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 268d476696ecb836e3573473ccc829184898a567..68b91814b853ad25cbbb0580f14cdaaee2632a37 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -53,6 +53,7 @@ Slot_repr Tez_repr Deposits_repr + Staking_pseudotoken_repr Period_repr Time_repr Ratio_repr @@ -156,6 +157,7 @@ Unstaked_frozen_deposits_storage Unstake_requests_storage Frozen_deposits_storage + Staking_pseudotokens_storage Contract_storage Token Fees_storage @@ -315,6 +317,7 @@ slot_repr.ml slot_repr.mli tez_repr.ml tez_repr.mli deposits_repr.ml deposits_repr.mli + staking_pseudotoken_repr.ml staking_pseudotoken_repr.mli period_repr.ml period_repr.mli time_repr.ml time_repr.mli ratio_repr.ml ratio_repr.mli @@ -421,6 +424,7 @@ unstaked_frozen_deposits_storage.ml unstaked_frozen_deposits_storage.mli unstake_requests_storage.ml unstake_requests_storage.mli frozen_deposits_storage.ml frozen_deposits_storage.mli + staking_pseudotokens_storage.ml staking_pseudotokens_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli fees_storage.ml fees_storage.mli @@ -582,6 +586,7 @@ slot_repr.ml slot_repr.mli tez_repr.ml tez_repr.mli deposits_repr.ml deposits_repr.mli + staking_pseudotoken_repr.ml staking_pseudotoken_repr.mli period_repr.ml period_repr.mli time_repr.ml time_repr.mli ratio_repr.ml ratio_repr.mli @@ -688,6 +693,7 @@ unstaked_frozen_deposits_storage.ml unstaked_frozen_deposits_storage.mli unstake_requests_storage.ml unstake_requests_storage.mli frozen_deposits_storage.ml frozen_deposits_storage.mli + staking_pseudotokens_storage.ml staking_pseudotokens_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli fees_storage.ml fees_storage.mli @@ -833,6 +839,7 @@ slot_repr.ml slot_repr.mli tez_repr.ml tez_repr.mli deposits_repr.ml deposits_repr.mli + staking_pseudotoken_repr.ml staking_pseudotoken_repr.mli period_repr.ml period_repr.mli time_repr.ml time_repr.mli ratio_repr.ml ratio_repr.mli @@ -939,6 +946,7 @@ unstaked_frozen_deposits_storage.ml unstaked_frozen_deposits_storage.mli unstake_requests_storage.ml unstake_requests_storage.mli frozen_deposits_storage.ml frozen_deposits_storage.mli + staking_pseudotokens_storage.ml staking_pseudotokens_storage.mli contract_storage.ml contract_storage.mli token.ml token.mli fees_storage.ml fees_storage.mli diff --git a/src/proto_alpha/lib_protocol/staking.ml b/src/proto_alpha/lib_protocol/staking.ml index f87d5ffc59cabf95e8d435224366fc422743bfcb..663c545b1290cec7e82cee969067d064c95989bc 100644 --- a/src/proto_alpha/lib_protocol/staking.ml +++ b/src/proto_alpha/lib_protocol/staking.ml @@ -122,11 +122,24 @@ let stake ctxt ~sender ~delegate amount = let* ctxt, finalize_balance_updates = finalize_unstake_and_check ~check_unfinalizable ctxt sender in + let* ctxt, new_pseudotokens = + Staking_pseudotokens.credit_frozen_deposits_pseudotokens_for_tez_amount + ctxt + delegate + amount + in + let sender_contract = Contract.Implicit sender in let* ctxt, stake_balance_updates = Token.transfer ctxt - (`Contract (Contract.Implicit sender)) + (`Contract sender_contract) (`Frozen_deposits delegate) amount in + let* ctxt = + Staking_pseudotokens.credit_costaking_pseudotokens + ctxt + sender_contract + new_pseudotokens + in return (ctxt, stake_balance_updates @ finalize_balance_updates) diff --git a/src/proto_alpha/lib_protocol/staking_pseudotoken_repr.ml b/src/proto_alpha/lib_protocol/staking_pseudotoken_repr.ml new file mode 100644 index 0000000000000000000000000000000000000000..5ed0def698916f9eecd220f34e06a88915da093f --- /dev/null +++ b/src/proto_alpha/lib_protocol/staking_pseudotoken_repr.ml @@ -0,0 +1,32 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(* A pseudotoken is not a Tez but it behaves similarly so let's reuse its operations. *) + +include Tez_repr + +let of_int64_exn = of_mutez_exn + +let to_int64 = to_mutez diff --git a/src/proto_alpha/lib_protocol/staking_pseudotoken_repr.mli b/src/proto_alpha/lib_protocol/staking_pseudotoken_repr.mli new file mode 100644 index 0000000000000000000000000000000000000000..fce0311008aee679fe96702d5fb43c48b0155a4f --- /dev/null +++ b/src/proto_alpha/lib_protocol/staking_pseudotoken_repr.mli @@ -0,0 +1,40 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Pseudotoken used in staking. + It represents a share of the total frozen deposits of a baker. *) +type t + +val encoding : t Data_encoding.t + +val zero : t + +val of_int64_exn : int64 -> t + +val to_int64 : t -> int64 + +val ( = ) : t -> t -> bool + +val ( +? ) : t -> t -> t tzresult diff --git a/src/proto_alpha/lib_protocol/staking_pseudotokens_storage.ml b/src/proto_alpha/lib_protocol/staking_pseudotokens_storage.ml new file mode 100644 index 0000000000000000000000000000000000000000..17b45f13a38cd05d35f61de7435f658ddcc4d0ab --- /dev/null +++ b/src/proto_alpha/lib_protocol/staking_pseudotokens_storage.ml @@ -0,0 +1,134 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Avoids a stitching. + Initializes contract's pseudotokens so that 1 pseudotoken = 1 mutez. *) +let get_or_init_frozen_deposits_pseudotokens ctxt contract ~frozen_deposits_tez + = + let open Lwt_result_syntax in + let* frozen_deposits_pseudotokens_opt = + Storage.Contract.Frozen_deposits_pseudotokens.find ctxt contract + in + match frozen_deposits_pseudotokens_opt with + | None -> + let initial_pseudotokens = + Staking_pseudotoken_repr.of_int64_exn + (Tez_repr.to_mutez frozen_deposits_tez) + in + let+ ctxt = + Storage.Contract.Frozen_deposits_pseudotokens.init + ctxt + contract + initial_pseudotokens + in + (ctxt, initial_pseudotokens) + | Some frozen_deposits_pseudotokens -> + return (ctxt, frozen_deposits_pseudotokens) + +let pseudotokens_of ~frozen_deposits_pseudotokens ~frozen_deposits_tez + ~tez_amount = + if Tez_repr.(frozen_deposits_tez = zero) then ( + (* When there are no frozen deposits, starts with 1 pseudotoken = 1 mutez. *) + assert (Staking_pseudotoken_repr.(frozen_deposits_pseudotokens = zero)) ; + Staking_pseudotoken_repr.of_int64_exn (Tez_repr.to_mutez tez_amount)) + else + let frozen_deposits_tez_z = + Z.of_int64 (Tez_repr.to_mutez frozen_deposits_tez) + in + let frozen_deposits_pseudotokens_z = + Z.of_int64 + (Staking_pseudotoken_repr.to_int64 frozen_deposits_pseudotokens) + in + let tez_amount_z = Z.of_int64 (Tez_repr.to_mutez tez_amount) in + let res_z = + Z.div + (Z.mul tez_amount_z frozen_deposits_pseudotokens_z) + frozen_deposits_tez_z + in + Staking_pseudotoken_repr.of_int64_exn (Z.to_int64 res_z) + +let update_frozen_deposits_pseudotokens ~f ctxt delegate = + let open Lwt_result_syntax in + let contract = Contract_repr.Implicit delegate in + let* {current_amount = frozen_deposits_tez; initial_amount = _} = + Frozen_deposits_storage.get ctxt contract + in + let* ctxt, frozen_deposits_pseudotokens = + get_or_init_frozen_deposits_pseudotokens ctxt contract ~frozen_deposits_tez + in + let*? new_frozen_deposits_pseudotokens, x = + f ~frozen_deposits_pseudotokens ~frozen_deposits_tez + in + let+ ctxt = + Storage.Contract.Frozen_deposits_pseudotokens.update + ctxt + contract + new_frozen_deposits_pseudotokens + in + (ctxt, x) + +let credit_frozen_deposits_pseudotokens_for_tez_amount ctxt delegate tez_amount + = + let f ~frozen_deposits_pseudotokens ~frozen_deposits_tez = + let open Result_syntax in + let pseudotokens_to_add = + pseudotokens_of + ~frozen_deposits_pseudotokens + ~frozen_deposits_tez + ~tez_amount + in + let+ new_pseudotokens_balance = + Staking_pseudotoken_repr.( + pseudotokens_to_add +? frozen_deposits_pseudotokens) + in + (new_pseudotokens_balance, pseudotokens_to_add) + in + update_frozen_deposits_pseudotokens ~f ctxt delegate + +let update_costaking_pseudotokens ~f ctxt contract = + let open Lwt_result_syntax in + let* costaking_pseudotokens_opt = + Storage.Contract.Costaking_pseudotokens.find ctxt contract + in + let costaking_pseudotokens = + Option.value + ~default:Staking_pseudotoken_repr.zero + costaking_pseudotokens_opt + in + let*? new_costaking_pseudotokens = f costaking_pseudotokens in + let*! ctxt = + Storage.Contract.Costaking_pseudotokens.add + ctxt + contract + new_costaking_pseudotokens + in + return ctxt + +let credit_costaking_pseudotokens ctxt contract pseudotokens_to_add = + let f current_pseudotokens_balance = + Staking_pseudotoken_repr.( + current_pseudotokens_balance +? pseudotokens_to_add) + in + update_costaking_pseudotokens ~f ctxt contract diff --git a/src/proto_alpha/lib_protocol/staking_pseudotokens_storage.mli b/src/proto_alpha/lib_protocol/staking_pseudotokens_storage.mli new file mode 100644 index 0000000000000000000000000000000000000000..dd60fadcec83bb84174bd8aac93a466078c3ea05 --- /dev/null +++ b/src/proto_alpha/lib_protocol/staking_pseudotokens_storage.mli @@ -0,0 +1,50 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 Nomadic Labs *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** This module is responsible for maintaining the + {!Storage.Contract.Frozen_deposits_pseudotokens} and + {!Storage.Contract.Costaking_pseudotokens} tables. *) + +(** [credit_frozen_deposits_pseudotokens_for_tez_amount ctxt delegate tez_amount] + increases [delegate]'s stake pseudotokens by an amount [pa] corresponding to + [tez_amount] multiplied by the current rate of the delegate's frozen + deposits pseudotokens per tez. + The function also returns [pa]. + + This function must be called on "stake" before transferring tez to + [delegate]'s frozen deposits. *) +val credit_frozen_deposits_pseudotokens_for_tez_amount : + Raw_context.t -> + Signature.Public_key_hash.t -> + Tez_repr.t -> + (Raw_context.t * Staking_pseudotoken_repr.t) tzresult Lwt.t + +(** [credit_costaking_pseudotokens ctxt contract p_amount] increases + [contract]'s costaking pseudotokens balance by [p_amount]. *) +val credit_costaking_pseudotokens : + Raw_context.t -> + Contract_repr.t -> + Staking_pseudotoken_repr.t -> + Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index b83708463c71cd2ad69a63d97708054fa791a916..ff6c3b966122f2782cedab76ae58f858600b84ae 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -436,6 +436,22 @@ module Contract = struct end) (Unstake_request) + module Frozen_deposits_pseudotokens = + Indexed_context.Make_map + (Registered) + (struct + let name = ["frozen_deposits_pseudotokens"] + end) + (Staking_pseudotoken_repr) + + module Costaking_pseudotokens = + Indexed_context.Make_map + (Registered) + (struct + let name = ["costaking_pseudotokens"] + end) + (Staking_pseudotoken_repr) + module Frozen_deposits_limit = Indexed_context.Make_map (Registered) diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 3e909bce87bb63ed443ee7afc4906d12d5d7b770..764545e67faa989c3d33d0a3f9e17434d3600376 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -197,6 +197,21 @@ module Contract : sig and type value = Unstake_request.t and type t := Raw_context.t + (** The sum of all pseudotokens owned by costakers (the delegate included) + corresponding to shares of the {!Frozen_deposits} current amount. *) + module Frozen_deposits_pseudotokens : + Indexed_data_storage + with type key = Contract_repr.t + and type value = Staking_pseudotoken_repr.t + and type t := Raw_context.t + + (** Share of the contract's delegate frozen deposits the contract owns. *) + module Costaking_pseudotokens : + Indexed_data_storage + with type key = Contract_repr.t + and type value = Staking_pseudotoken_repr.t + and type t := Raw_context.t + (** If there is a value, the frozen balance for the contract won't exceed it (starting in preserved_cycles + 1). *) module Frozen_deposits_limit :