diff --git a/src/proto_alpha/lib_client/injection.ml b/src/proto_alpha/lib_client/injection.ml index ecf5fb52bbd8afbaf603c7e6f3be17be2aaee150..d6c071b0964d5e386b3370733f267b856402d78d 100644 --- a/src/proto_alpha/lib_client/injection.ml +++ b/src/proto_alpha/lib_client/injection.ml @@ -326,6 +326,9 @@ let estimated_gas_single (type kind) | Applied (Tx_rollup_submit_batch_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Tx_rollup_commit_result {consumed_gas; _}) -> Ok consumed_gas + | Applied (Tx_rollup_return_bond_result {consumed_gas; _}) -> + Ok consumed_gas + | Applied (Tx_rollup_rejection_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Sc_rollup_originate_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Sc_rollup_add_messages_result {consumed_gas; _}) -> Ok consumed_gas @@ -369,6 +372,8 @@ let estimated_storage_single (type kind) ~tx_rollup_origination_size Michelson’s big map). *) Ok Z.zero | Applied (Tx_rollup_commit_result _) -> Ok Z.zero + | Applied (Tx_rollup_return_bond_result _) -> Ok Z.zero + | Applied (Tx_rollup_rejection_result _) -> Ok Z.zero | Applied (Sc_rollup_originate_result {size; _}) -> Ok size | Applied (Sc_rollup_add_messages_result _) -> Ok Z.zero | Skipped _ -> assert false @@ -422,6 +427,8 @@ let originated_contracts_single (type kind) | Applied (Tx_rollup_origination_result _) -> Ok [] | Applied (Tx_rollup_submit_batch_result _) -> Ok [] | Applied (Tx_rollup_commit_result _) -> Ok [] + | Applied (Tx_rollup_return_bond_result _) -> Ok [] + | Applied (Tx_rollup_rejection_result _) -> Ok [] | Applied (Sc_rollup_originate_result _) -> Ok [] | Applied (Sc_rollup_add_messages_result _) -> Ok [] | Skipped _ -> assert false diff --git a/src/proto_alpha/lib_client/mockup.ml b/src/proto_alpha/lib_client/mockup.ml index 7c9d4eb1126b94d8b1076351f4e4b32eb8a1e37d..b47e45e998cf7d4d00600d090460cb196b4387dd 100644 --- a/src/proto_alpha/lib_client/mockup.ml +++ b/src/proto_alpha/lib_client/mockup.ml @@ -75,6 +75,10 @@ module Protocol_constants_overrides = struct tx_rollup_origination_size : int option; tx_rollup_hard_size_limit_per_inbox : int option; tx_rollup_hard_size_limit_per_message : int option; + tx_rollup_commitment_bond : Tez.t option; + tx_rollup_finality_period : int option; + tx_rollup_max_unfinalized_levels : int option; + tx_rollup_max_finalize_levels_per_commitment : int option; sc_rollup_enable : bool option; sc_rollup_origination_size : int option; (* Additional, "bastard" parameters (they are not protocol constants but partially treated the same way). *) @@ -129,7 +133,11 @@ module Protocol_constants_overrides = struct ( ( c.tx_rollup_enable, c.tx_rollup_origination_size, c.tx_rollup_hard_size_limit_per_inbox, - c.tx_rollup_hard_size_limit_per_message ), + c.tx_rollup_hard_size_limit_per_message, + c.tx_rollup_commitment_bond, + c.tx_rollup_finality_period, + c.tx_rollup_max_unfinalized_levels, + c.tx_rollup_max_finalize_levels_per_commitment ), (c.sc_rollup_enable, c.sc_rollup_origination_size) ) ) ) ) ) )) (fun ( ( preserved_cycles, @@ -173,7 +181,11 @@ module Protocol_constants_overrides = struct ( ( tx_rollup_enable, tx_rollup_origination_size, tx_rollup_hard_size_limit_per_inbox, - tx_rollup_hard_size_limit_per_message ), + tx_rollup_hard_size_limit_per_message, + tx_rollup_commitment_bond, + tx_rollup_finality_period, + tx_rollup_max_unfinalized_levels, + tx_rollup_max_finalize_levels_per_commitment ), (sc_rollup_enable, sc_rollup_origination_size) ) ) ) ) ) ) -> { @@ -216,6 +228,10 @@ module Protocol_constants_overrides = struct tx_rollup_origination_size; tx_rollup_hard_size_limit_per_inbox; tx_rollup_hard_size_limit_per_message; + tx_rollup_commitment_bond; + tx_rollup_finality_period; + tx_rollup_max_unfinalized_levels; + tx_rollup_max_finalize_levels_per_commitment; sc_rollup_enable; sc_rollup_origination_size; chain_id; @@ -275,11 +291,17 @@ module Protocol_constants_overrides = struct (opt "cache_stake_distribution_cycles" int8) (opt "cache_sampler_state_cycles" int8)) (merge_objs - (obj4 + (obj8 (opt "tx_rollup_enable" Data_encoding.bool) (opt "tx_rollup_origination_size" int31) (opt "tx_rollup_hard_size_limit_per_inbox" int31) - (opt "tx_rollup_hard_size_limit_per_message" int31)) + (opt "tx_rollup_hard_size_limit_per_message" int31) + (opt "tx_rollup_commitment_bond" Tez.encoding) + (opt "tx_rollup_finality_period" int31) + (opt "tx_rollup_max_unfinalized_levels" int31) + (opt + "tx_rollup_max_finalize_levels_per_commitment" + int31)) (obj2 (opt "sc_rollup_enable" bool) (opt "sc_rollup_origination_size" int31)))))))) @@ -351,6 +373,12 @@ module Protocol_constants_overrides = struct Some parametric.tx_rollup_hard_size_limit_per_inbox; tx_rollup_hard_size_limit_per_message = Some parametric.tx_rollup_hard_size_limit_per_message; + tx_rollup_commitment_bond = Some parametric.tx_rollup_commitment_bond; + tx_rollup_finality_period = Some parametric.tx_rollup_finality_period; + tx_rollup_max_unfinalized_levels = + Some parametric.tx_rollup_max_unfinalized_levels; + tx_rollup_max_finalize_levels_per_commitment = + Some parametric.tx_rollup_max_finalize_levels_per_commitment; sc_rollup_enable = Some parametric.sc_rollup_enable; sc_rollup_origination_size = Some parametric.sc_rollup_origination_size; (* Bastard additional parameters. *) @@ -402,6 +430,10 @@ module Protocol_constants_overrides = struct tx_rollup_origination_size = None; tx_rollup_hard_size_limit_per_inbox = None; tx_rollup_hard_size_limit_per_message = None; + tx_rollup_commitment_bond = None; + tx_rollup_finality_period = None; + tx_rollup_max_unfinalized_levels = None; + tx_rollup_max_finalize_levels_per_commitment = None; sc_rollup_enable = None; sc_rollup_origination_size = None; chain_id = None; @@ -660,6 +692,12 @@ module Protocol_constants_overrides = struct override_value = o.tx_rollup_hard_size_limit_per_message; pp = pp_print_int; }; + O + { + name = "tx_rollup_commitment_bond"; + override_value = o.tx_rollup_commitment_bond; + pp = Tez.pp; + }; ] in let fields_with_override = @@ -804,6 +842,22 @@ module Protocol_constants_overrides = struct Option.value ~default:c.tx_rollup_hard_size_limit_per_message o.tx_rollup_hard_size_limit_per_message; + tx_rollup_commitment_bond = + Option.value + ~default:c.tx_rollup_commitment_bond + o.tx_rollup_commitment_bond; + tx_rollup_finality_period = + Option.value + ~default:c.tx_rollup_finality_period + o.tx_rollup_finality_period; + tx_rollup_max_unfinalized_levels = + Option.value + ~default:c.tx_rollup_max_unfinalized_levels + o.tx_rollup_max_unfinalized_levels; + tx_rollup_max_finalize_levels_per_commitment = + Option.value + ~default:c.tx_rollup_max_finalize_levels_per_commitment + o.tx_rollup_max_finalize_levels_per_commitment; sc_rollup_enable = Option.value ~default:c.sc_rollup_enable o.sc_rollup_enable; sc_rollup_origination_size = diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index be74cd41935feb82ee0be65811fc6bc0450b877d..75638880758915aed91fbd42dc12a03f823c7419 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -201,6 +201,37 @@ let pp_manager_operation_content (type kind) source internal pp_result ppf source pp_result result + | Tx_rollup_return_bond {tx_rollup} -> + Format.fprintf + ppf + "@[%s:%a @,From: %a%a@]" + (if internal then "Internal tx rollup return commitment bond" + else "Tx rollup return commitment bond") + Tx_rollup.pp + tx_rollup + Contract.pp + source + pp_result + result + | Tx_rollup_rejection {rollup; level; hash; batch_index; batch = _; nonce} -> + Format.fprintf + ppf + "@[%s:rollup %a level %a commitment %a index %d nonce %Lx @,\ + From: %a%a@]" + (if internal then "Internal tx rollup rejection" + else "Tx rollup rejection") + Tx_rollup.pp + rollup + Raw_level.pp + level + Tx_rollup_commitments.Commitment_hash.pp + hash + batch_index + nonce + Contract.pp + source + pp_result + result | Sc_rollup_originate {kind; boot_sector} -> let (module R : Sc_rollups.PVM.S) = Sc_rollups.of_kind kind in Format.fprintf @@ -276,6 +307,13 @@ let pp_balance_updates ppf = function | Invoice -> "invoices" | Initial_commitments -> "initial commitments" | Minted -> "minted" + | Rollup_bonds (contract, bond_id) -> + Format.asprintf + "Rollup_bonds(%a,%a)" + Contract.pp + contract + Rollup_bond_id.pp + bond_id in let balance = match origin with @@ -458,6 +496,24 @@ let pp_manager_operation_contents_and_result ppf balance_updates ; Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas in + let pp_tx_rollup_return_bond_result + (Tx_rollup_return_bond_result {balance_updates; consumed_gas}) = + Format.fprintf + ppf + "@,Balance updates:@, %a" + pp_balance_updates + balance_updates ; + Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas + in + let pp_tx_rollup_rejection_result + (Tx_rollup_rejection_result {balance_updates; consumed_gas}) = + Format.fprintf + ppf + "@,Balance updates:@, %a" + pp_balance_updates + balance_updates ; + Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas + in let pp_sc_rollup_originate_result (Sc_rollup_originate_result {address; consumed_gas; size; balance_updates}) = @@ -563,9 +619,32 @@ let pp_manager_operation_contents_and_result ppf | Backtracked ((Tx_rollup_commit_result _ as op), _err) -> Format.fprintf ppf - "@[This tx rollup commit rollup operation was BACKTRACKED, its \ + "@[This tx rollup commit operation was BACKTRACKED, its \ expected effects (as follow) were NOT applied.@]" ; pp_tx_rollup_commit_result op + | Applied (Tx_rollup_return_bond_result _ as op) -> + Format.fprintf + ppf + "This tx rollup return commitment bond operation was successfully \ + applied" ; + pp_tx_rollup_return_bond_result op + | Backtracked ((Tx_rollup_return_bond_result _ as op), _err) -> + Format.fprintf + ppf + "@[This tx rollup return commitment bond operation was \ + BACKTRACKED, its expected effects (as follow) were NOT applied.@]" ; + pp_tx_rollup_return_bond_result op + | Applied (Tx_rollup_rejection_result _ as op) -> + Format.fprintf + ppf + "This tx rollup rejection operation was successfully applied" ; + pp_tx_rollup_rejection_result op + | Backtracked ((Tx_rollup_rejection_result _ as op), _err) -> + Format.fprintf + ppf + "@[This tx rollup rejection operation was BACKTRACKED, its \ + expected effects (as follow) were NOT applied.@]" ; + pp_tx_rollup_rejection_result op | Applied (Sc_rollup_originate_result _ as op) -> Format.fprintf ppf 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 fba5705a49aeac1714eb8c6f88b6938bf7fcd6b2..3c38176a40080946b0ff253e1c0b1dd8ab8ab009 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 @@ -2229,6 +2229,7 @@ let commands_rw () = ~fee_parameter ~tx_rollup ~content + ~gas_limit:(Gas.Arith.integral_of_int_exn 10000) () >>=? fun _res -> return_unit); command diff --git a/src/proto_alpha/lib_parameters/default_parameters.ml b/src/proto_alpha/lib_parameters/default_parameters.ml index bcff5ea4b2a895556f6defb5b775ac1c9875cf75..2bdd893301fd0202426b757e898d46ae5d1373bf 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.ml +++ b/src/proto_alpha/lib_parameters/default_parameters.ml @@ -104,6 +104,10 @@ let constants_mainnet = (* Transaction rollup’s size limits are expressed in number of bytes *) tx_rollup_hard_size_limit_per_inbox = 100_000; tx_rollup_hard_size_limit_per_message = 5_000; + tx_rollup_commitment_bond = Tez.of_mutez_exn 10_000_000_000L; + tx_rollup_finality_period = 2_000; + tx_rollup_max_unfinalized_levels = 2_100; + tx_rollup_max_finalize_levels_per_commitment = 5; sc_rollup_enable = false; (* The following value is chosen to prevent spam. *) sc_rollup_origination_size = 6_314; diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index cd98654f903e00fb6e7cea2ac3863ca3fd9ed67f..db63fe13553f67e183976013342c906df43e0bde 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -48,6 +48,8 @@ "Tx_rollup_message_repr", "Tx_rollup_inbox_repr", "Tx_rollup_commitments_repr", + "Rollup_bond_id_repr", + "Tx_rollup_rejection_repr", "Vote_repr", "Block_header_repr", "Entrypoint_repr", @@ -81,6 +83,7 @@ "Contract_delegate_storage", "Sapling_storage", "Lazy_storage_diff", + "Frozen_rollup_bonds_storage", "Contract_storage", "Commitment_storage", "Token", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index d0987bfc0ac0877e99452c2dd2a600a2bee5cb44..d8beae9bce46076a299627198f4e9aa4bef4fea9 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -268,11 +268,26 @@ end module Tx_rollup_inbox = struct include Tx_rollup_inbox_repr include Tx_rollup_inbox_storage + + module Internal_for_tests = struct + include Tx_rollup_inbox_repr + include Tx_rollup_inbox_storage + end end module Tx_rollup_commitments = struct include Tx_rollup_commitments_repr include Tx_rollup_commitments_storage + + module Internal_for_tests = struct + include Tx_rollup_commitments_repr + include Tx_rollup_commitments_storage + end +end + +module Tx_rollup_rejection = struct + include Tx_rollup_rejection_repr + include Tx_rollup_commitments_storage end module Global_constants_storage = Global_constants_storage @@ -349,6 +364,7 @@ module Sapling = struct type alloc = Sapling_state.alloc = {memo_size : Sapling_repr.Memo_size.t} end +module Rollup_bond_id = Rollup_bond_id_repr module Receipt = Receipt_repr module Delegate = struct diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 8251e23c73d2a1c67a9d12e04e0a7ae9223401b7..86ae1d404eedd5d04662972431be0406f828b8bf 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -781,6 +781,10 @@ module Constants : sig tx_rollup_origination_size : int; tx_rollup_hard_size_limit_per_inbox : int; tx_rollup_hard_size_limit_per_message : int; + tx_rollup_commitment_bond : Tez.t; + tx_rollup_finality_period : int; + tx_rollup_max_unfinalized_levels : int; + tx_rollup_max_finalize_levels_per_commitment : int; sc_rollup_enable : bool; sc_rollup_origination_size : int; } @@ -872,6 +876,14 @@ module Constants : sig val tx_rollup_hard_size_limit_per_message : context -> int + val tx_rollup_commitment_bond : context -> Tez.t + + val tx_rollup_finality_period : context -> int + + val tx_rollup_max_unfinalized_levels : context -> int + + val tx_rollup_max_finalize_levels_per_commitment : context -> int + val sc_rollup_enable : context -> bool val sc_rollup_origination_size : context -> int @@ -1439,6 +1451,8 @@ module Contract : sig val originated_from_current_nonce : since:context -> until:context -> contract list tzresult Lwt.t + val stake : context -> contract -> Tez.t tzresult Lwt.t + module Legacy_big_map_diff : sig type item = private | Update of { @@ -1487,6 +1501,43 @@ module Contract : sig end end +(** This module re-exports definitions from {!Tx_rollup_repr} and + {!Tx_rollup_storage}. *) +module Tx_rollup : sig + include BASIC_DATA + + type tx_rollup = t + + val rpc_arg : tx_rollup RPC_arg.arg + + val to_b58check : tx_rollup -> string + + val of_b58check : string -> tx_rollup tzresult + + val of_b58check_opt : string -> tx_rollup option + + val pp : Format.formatter -> tx_rollup -> unit + + val encoding : tx_rollup Data_encoding.t + + val originate : context -> (context * tx_rollup) tzresult Lwt.t + + val update_tx_rollups_at_block_finalization : + context -> context tzresult Lwt.t + + module Internal_for_tests : sig + (** see [tx_rollup_repr.originated_tx_rollup] for documentation *) + val originated_tx_rollup : + Origination_nonce.Internal_for_tests.t -> tx_rollup + end +end + +module Rollup_bond_id : sig + type t = Tx_rollup_bond_id of Tx_rollup.t + + val pp : Format.formatter -> t -> unit +end + module Receipt : sig type balance = | Contract of Contract.t @@ -1507,6 +1558,7 @@ module Receipt : sig | Invoice | Initial_commitments | Minted + | Rollup_bonds of Contract.t * Rollup_bond_id.t val compare_balance : balance -> balance -> int @@ -1942,37 +1994,6 @@ module Destination : sig type error += Invalid_destination_b58check of string end -(** This module re-exports definitions from {!Tx_rollup_repr} and - {!Tx_rollup_storage}. *) -module Tx_rollup : sig - include BASIC_DATA - - type tx_rollup = t - - val rpc_arg : tx_rollup RPC_arg.arg - - val to_b58check : tx_rollup -> string - - val of_b58check : string -> tx_rollup tzresult - - val of_b58check_opt : string -> tx_rollup option - - val pp : Format.formatter -> tx_rollup -> unit - - val encoding : tx_rollup Data_encoding.t - - val originate : context -> (context * tx_rollup) tzresult Lwt.t - - val update_tx_rollups_at_block_finalization : - context -> context tzresult Lwt.t - - module Internal_for_tests : sig - (** see [tx_rollup_repr.originated_tx_rollup] for documentation *) - val originated_tx_rollup : - Origination_nonce.Internal_for_tests.t -> tx_rollup - end -end - (** This module re-exports definitions from {!Tx_rollup_state_repr} and {!Tx_rollup_state_storage}. *) module Tx_rollup_state : sig @@ -1994,6 +2015,9 @@ module Tx_rollup_state : sig val last_inbox_level : t -> Raw_level.t option + val first_unfinalized_level : + context -> Tx_rollup.t -> (context * Raw_level.t option) tzresult Lwt.t + type error += | Tx_rollup_already_exists of Tx_rollup.t | Tx_rollup_does_not_exist of Tx_rollup.t @@ -2044,6 +2068,20 @@ end module Tx_rollup_inbox : sig type t = {contents : Tx_rollup_message.hash list; cumulated_size : int} + module Hash : sig + type t + + include Compare.S with type t := t + + val pp : Format.formatter -> t -> unit + + val empty : t + + val encoding : t Data_encoding.t + + val extend : t -> Tx_rollup_message.hash -> t + end + val pp : Format.formatter -> t -> unit val encoding : t Data_encoding.t @@ -2085,6 +2123,30 @@ module Tx_rollup_inbox : sig Tx_rollup.t -> (context * Raw_level.t option * Raw_level.t option) tzresult Lwt.t + val check_batch_hash : + context -> + Raw_level.t -> + Tx_rollup.t -> + int -> + Tx_rollup_message.t -> + context tzresult Lwt.t + + module Internal_for_tests : sig + type metadata = { + count : int; + cumulated_size : int; + hash : Hash.t; + predecessor : Raw_level_repr.t option; + successor : Raw_level_repr.t option; + } + + val get_metadata : + context -> + Raw_level.t -> + Tx_rollup.t -> + (context * metadata) tzresult Lwt.t + end + type error += | Tx_rollup_inbox_does_not_exist of Tx_rollup.t * Raw_level.t | Tx_rollup_inbox_size_would_exceed_limit of Tx_rollup.t @@ -2108,6 +2170,7 @@ module Tx_rollup_commitments : sig level : Raw_level.t; batches : batch_commitment list; predecessor : Commitment_hash.t option; + inbox_hash : Tx_rollup_inbox.Hash.t; } val ( = ) : t -> t -> bool @@ -2140,7 +2203,19 @@ module Tx_rollup_commitments : sig type error += Wrong_batch_count - type error += Commitment_too_early + type error += Wrong_inbox_hash + + type error += Commitment_too_early of Raw_level.t * Raw_level.t + + type error += Bond_does_not_exist of Signature.public_key_hash + + type error += Bond_in_use of Signature.public_key_hash + + type error += Too_many_unfinalized_levels + + type error += No_such_batch of Raw_level.t * int + + type error += No_such_commitment val add_commitment : context -> @@ -2149,8 +2224,68 @@ module Tx_rollup_commitments : sig Commitment.t -> context tzresult Lwt.t + val reject_commitment : + context -> + Tx_rollup.t -> + Raw_level.t -> + Commitment_hash.t -> + context tzresult Lwt.t + val get_commitments : context -> Tx_rollup.t -> Raw_level.t -> (context * t) tzresult Lwt.t + + val pending_bonded_commitments : + context -> + Tx_rollup.t -> + Signature.public_key_hash -> + (context * int) tzresult Lwt.t + + val finalize_pending_commitments : + context -> Tx_rollup.t -> Raw_level.t -> context tzresult Lwt.t + + val remove_bond : + context -> + Tx_rollup.t -> + Signature.public_key_hash -> + context tzresult Lwt.t + + val get_commitment_roots : + context -> + Tx_rollup.t -> + Raw_level.t -> + Commitment_hash.t -> + int -> + (context * (Commitment.batch_commitment * Commitment.batch_commitment)) + tzresult + Lwt.t + + module Internal_for_tests : sig + (** See [Tx_rollup_commitments_storage.retire_rollup_level] + for documentation *) + val retire_rollup_level : + context -> + Tx_rollup.t -> + Raw_level.t -> + Raw_level.t -> + (context * [> `No_commitment | `Commitment_too_late | `Retired]) tzresult + Lwt.t + end +end + +(** This simply re-exports {!Tx_rollup_rejection_repr}. See + {!Tx_rollup_rejection_repr} for additional documentation of this module. *) +module Tx_rollup_rejection : sig + type error += Wrong_rejection + + type t = { + rollup : Tx_rollup.t; + level : Raw_level.t; + hash : Tx_rollup_commitments.Commitment_hash.t; + batch_index : int; + batch : Tx_rollup_message.t; + } + + val encoding : t Data_encoding.t end module Kind : sig @@ -2205,6 +2340,10 @@ module Kind : sig type tx_rollup_commit = Tx_rollup_commit_kind + type tx_rollup_return_bond = Tx_rollup_return_bond_kind + + type tx_rollup_rejection = Tx_rollup_rejection_kind + type sc_rollup_originate = Sc_rollup_originate_kind type sc_rollup_add_messages = Sc_rollup_add_messages_kind @@ -2219,6 +2358,8 @@ module Kind : sig | 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 + | Tx_rollup_return_bond_manager_kind : tx_rollup_return_bond manager + | Tx_rollup_rejection_manager_kind : tx_rollup_rejection manager | Sc_rollup_originate_manager_kind : sc_rollup_originate manager | Sc_rollup_add_messages_manager_kind : sc_rollup_add_messages manager end @@ -2349,6 +2490,19 @@ and _ manager_operation = commitment : Tx_rollup_commitments.Commitment.t; } -> Kind.tx_rollup_commit manager_operation + | Tx_rollup_return_bond : { + tx_rollup : Tx_rollup.t; + } + -> Kind.tx_rollup_return_bond manager_operation + | Tx_rollup_rejection : { + rollup : Tx_rollup.t; + level : Raw_level.t; + hash : Tx_rollup_commitments.Commitment_hash.t; + batch_index : int; + batch : Tx_rollup_message.t; + nonce : int64; + } + -> Kind.tx_rollup_rejection manager_operation | Sc_rollup_originate : { kind : Sc_rollup.Kind.t; boot_sector : Sc_rollup.PVM.boot_sector; @@ -2504,6 +2658,11 @@ module Operation : sig val tx_rollup_commit_case : Kind.tx_rollup_commit Kind.manager case + val tx_rollup_return_bond_case : + Kind.tx_rollup_return_bond Kind.manager case + + val tx_rollup_rejection_case : Kind.tx_rollup_rejection Kind.manager case + val register_global_constant_case : Kind.register_global_constant Kind.manager case @@ -2544,6 +2703,10 @@ module Operation : sig val tx_rollup_commit_case : Kind.tx_rollup_commit case + val tx_rollup_return_bond_case : Kind.tx_rollup_return_bond case + + val tx_rollup_rejection_case : Kind.tx_rollup_rejection case + val sc_rollup_originate_case : Kind.sc_rollup_originate case val sc_rollup_add_messages_case : Kind.sc_rollup_add_messages case @@ -2718,7 +2881,8 @@ module Token : sig | `Collected_commitments of Blinded_public_key_hash.t | `Delegate_balance of Signature.Public_key_hash.t | `Frozen_deposits of Signature.Public_key_hash.t - | `Block_fees ] + | `Block_fees + | `Frozen_rollup_bonds of Contract.t * Rollup_bond_id.t ] type source = [ `Invoice diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 1553cd61fe6bbaeea4182bbacf0d91de674123d8..09af3a1b84b811a51bcfeca22c17c283be7868c0 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -1202,22 +1202,98 @@ let apply_manager_operation_content : in return (ctxt, result, []) | Tx_rollup_commit {tx_rollup; commitment} -> ( - (* TODO: bonds https://gitlab.com/tezos/tezos/-/issues/2459 *) match Contract.is_implicit source with | None -> fail Tx_rollup_commit_with_non_implicit_contract (* This is only called with implicit contracts *) | Some key -> + (let current_level = (Level.current ctxt).level in + let finality_period = + Int32.of_int @@ Constants.tx_rollup_finality_period ctxt + in + (if + Compare.Int32.(Raw_level.to_int32 current_level > finality_period) + then + Lwt.return + @@ Raw_level.of_int32 + (Int32.sub (Raw_level.to_int32 current_level) finality_period) + >>=? fun last_level_to_finalize -> + Tx_rollup_commitments.finalize_pending_commitments + ctxt + tx_rollup + last_level_to_finalize + else return ctxt) + >>=? fun ctxt -> + Tx_rollup_commitments.pending_bonded_commitments ctxt tx_rollup key + >>=? fun (ctxt, pending) -> + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + match pending with + | 0 -> + Token.transfer + ctxt + (`Contract source) + (`Frozen_rollup_bonds (source, bond_id)) + (Constants.tx_rollup_commitment_bond ctxt) + | _ -> return (ctxt, [])) + >>=? fun (ctxt, balance_updates) -> Tx_rollup_commitments.add_commitment ctxt tx_rollup key commitment >>=? fun ctxt -> let result = Tx_rollup_commit_result { consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt; - balance_updates = []; + balance_updates; } in return (ctxt, result, [])) + | Tx_rollup_return_bond {tx_rollup} -> + (match Contract.is_implicit source with + | None -> fail Tx_rollup_commit_with_non_implicit_contract + | Some key -> + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + Tx_rollup_commitments.remove_bond ctxt tx_rollup key >>=? fun ctxt -> + Token.transfer + ctxt + (`Frozen_rollup_bonds (source, bond_id)) + (`Contract source) + (Constants.tx_rollup_commitment_bond ctxt)) + >>=? fun (ctxt, balance_updates) -> + let result = + Tx_rollup_return_bond_result + { + consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt; + balance_updates; + } + in + return (ctxt, result, []) + | Tx_rollup_rejection {rollup; level; hash; batch_index; nonce = _; batch} -> + Tx_rollup_inbox.check_batch_hash ctxt level rollup batch_index batch + >>=? fun ctxt -> + Tx_rollup_commitments.get_commitment_roots + ctxt + rollup + level + hash + batch_index + >>=? fun (ctxt, (before_batch, after_batch)) -> + (* TODO/TORU replay just this one batch -- for now, we'll assume that + rejection succeeds if before_root = after_root*) + fail_unless + (Tx_rollup_commitments.Commitment.batch_commitment_equal + before_batch + after_batch) + Tx_rollup_rejection.Wrong_rejection + >>=? fun () -> + Tx_rollup_commitments.reject_commitment ctxt rollup level hash + >>=? fun ctxt -> + let result = + Tx_rollup_rejection_result + { + consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt; + balance_updates = []; + } + in + return (ctxt, result, []) | Sc_rollup_originate {kind; boot_sector} -> Sc_rollup_operations.originate ctxt ~kind ~boot_sector >>=? fun ({address; size}, ctxt) -> @@ -1368,9 +1444,14 @@ let precheck_manager_contents (type kind) ctxt (op : kind Kind.manager contents) application. *) let current_level = (Level.current ctxt).level in fail_when - Raw_level.(current_level <= Raw_level.succ commitment.level) - Tx_rollup_commitments.Commitment_too_early + Raw_level.(current_level <= commitment.level) + (Tx_rollup_commitments.Commitment_too_early + (commitment.level, current_level)) >|=? fun () -> ctxt + | Tx_rollup_return_bond _ -> + assert_tx_rollup_feature_enabled ctxt >|=? fun () -> ctxt + | Tx_rollup_rejection _ -> + assert_tx_rollup_feature_enabled ctxt >|=? fun () -> ctxt | Sc_rollup_originate _ | Sc_rollup_add_messages _ -> assert_sc_rollup_feature_enabled ctxt >|=? fun () -> ctxt) >>=? fun ctxt -> @@ -1486,6 +1567,10 @@ let burn_storage_fees : consumed_gas = (_ : Gas.Arith.fp); } -> return (ctxt, storage_limit, smopr) + | Tx_rollup_return_bond_result payload -> + return (ctxt, storage_limit, Tx_rollup_return_bond_result payload) + | Tx_rollup_rejection_result payload -> + return (ctxt, storage_limit, Tx_rollup_rejection_result payload) | Sc_rollup_originate_result payload -> let payer = `Contract payer in Fees.burn_sc_rollup_origination_fees diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index ab9b01cbb3ad0cc22a2d660aeeef853cec9070e6..59c76962497c737ff259b0b62de9bf7a2d916593 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -106,6 +106,16 @@ type _ successful_manager_operation_result = consumed_gas : Gas.Arith.fp; } -> Kind.tx_rollup_commit successful_manager_operation_result + | Tx_rollup_return_bond_result : { + balance_updates : Receipt.balance_updates; + consumed_gas : Gas.Arith.fp; + } + -> Kind.tx_rollup_return_bond successful_manager_operation_result + | Tx_rollup_rejection_result : { + balance_updates : Receipt.balance_updates; + consumed_gas : Gas.Arith.fp; + } + -> Kind.tx_rollup_rejection successful_manager_operation_result | Sc_rollup_originate_result : { balance_updates : Receipt.balance_updates; address : Sc_rollup.Address.t; @@ -576,6 +586,60 @@ module Manager_result = struct Tx_rollup_commit_result {balance_updates; consumed_gas = consumed_milligas}) + let[@coq_axiom_with_reason "gadt"] tx_rollup_return_bond_case = + make + ~op_case:Operation.Encoding.Manager_operations.tx_rollup_return_bond_case + ~encoding: + Data_encoding.( + obj3 + (req "balance_updates" Receipt.balance_updates_encoding) + (dft "consumed_gas" Gas.Arith.n_integral_encoding Gas.Arith.zero) + (dft "consumed_milligas" Gas.Arith.n_fp_encoding Gas.Arith.zero)) + ~iselect:(function + | Internal_operation_result + (({operation = Tx_rollup_return_bond _; _} as op), res) -> + Some (op, res) + | _ -> None) + ~select:(function + | Successful_manager_result (Tx_rollup_return_bond_result _ as op) -> + Some op + | _ -> None) + ~kind:Kind.Tx_rollup_return_bond_manager_kind + ~proj:(function + | Tx_rollup_return_bond_result {balance_updates; consumed_gas} -> + (balance_updates, Gas.Arith.ceil consumed_gas, consumed_gas)) + ~inj:(fun (balance_updates, consumed_gas, consumed_milligas) -> + assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; + Tx_rollup_return_bond_result + {balance_updates; consumed_gas = consumed_milligas}) + + let[@coq_axiom_with_reason "gadt"] tx_rollup_rejection_case = + make + ~op_case:Operation.Encoding.Manager_operations.tx_rollup_rejection_case + ~encoding: + Data_encoding.( + obj3 + (req "balance_updates" Receipt.balance_updates_encoding) + (dft "consumed_gas" Gas.Arith.n_integral_encoding Gas.Arith.zero) + (dft "consumed_milligas" Gas.Arith.n_fp_encoding Gas.Arith.zero)) + ~iselect:(function + | Internal_operation_result + (({operation = Tx_rollup_rejection _; _} as op), res) -> + Some (op, res) + | _ -> None) + ~select:(function + | Successful_manager_result (Tx_rollup_rejection_result _ as op) -> + Some op + | _ -> None) + ~kind:Kind.Tx_rollup_rejection_manager_kind + ~proj:(function + | Tx_rollup_rejection_result {balance_updates; consumed_gas} -> + (balance_updates, Gas.Arith.ceil consumed_gas, consumed_gas)) + ~inj:(fun (balance_updates, consumed_gas, consumed_milligas) -> + assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; + Tx_rollup_rejection_result + {balance_updates; consumed_gas = consumed_milligas}) + let[@coq_axiom_with_reason "gadt"] sc_rollup_originate_case = make ~op_case:Operation.Encoding.Manager_operations.sc_rollup_originate_case @@ -783,6 +847,14 @@ let equal_manager_kind : | (Kind.Tx_rollup_commit_manager_kind, Kind.Tx_rollup_commit_manager_kind) -> Some Eq | (Kind.Tx_rollup_commit_manager_kind, _) -> None + | ( Kind.Tx_rollup_return_bond_manager_kind, + Kind.Tx_rollup_return_bond_manager_kind ) -> + Some Eq + | (Kind.Tx_rollup_return_bond_manager_kind, _) -> None + | ( Kind.Tx_rollup_rejection_manager_kind, + Kind.Tx_rollup_rejection_manager_kind ) -> + Some Eq + | (Kind.Tx_rollup_rejection_manager_kind, _) -> None | ( Kind.Sc_rollup_originate_manager_kind, Kind.Sc_rollup_originate_manager_kind ) -> Some Eq @@ -1175,6 +1247,28 @@ module Encoding = struct Some (op, res) | _ -> None) + let[@coq_axiom_with_reason "gadt"] tx_rollup_return_bond_case = + make_manager_case + Operation.Encoding.tx_rollup_return_bond_case + Manager_result.tx_rollup_return_bond_case + (function + | Contents_and_result + ( (Manager_operation {operation = Tx_rollup_return_bond _; _} as op), + res ) -> + Some (op, res) + | _ -> None) + + let[@coq_axiom_with_reason "gadt"] tx_rollup_rejection_case = + make_manager_case + Operation.Encoding.tx_rollup_rejection_case + Manager_result.tx_rollup_rejection_case + (function + | Contents_and_result + ( (Manager_operation {operation = Tx_rollup_rejection _; _} as op), + res ) -> + Some (op, res) + | _ -> None) + let[@coq_axiom_with_reason "gadt"] sc_rollup_originate_case = make_manager_case Operation.Encoding.sc_rollup_originate_case @@ -1235,6 +1329,8 @@ let contents_result_encoding = make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; + make tx_rollup_return_bond_case; + make tx_rollup_rejection_case; make sc_rollup_originate_case; make sc_rollup_add_messages_case; ] @@ -1281,6 +1377,8 @@ let contents_and_result_encoding = make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; + make tx_rollup_return_bond_case; + make tx_rollup_rejection_case; make sc_rollup_originate_case; make sc_rollup_add_messages_case; ] @@ -1637,6 +1735,58 @@ let kind_equal : } ) -> Some Eq | (Manager_operation {operation = Tx_rollup_commit _; _}, _) -> None + | ( Manager_operation {operation = Tx_rollup_return_bond _; _}, + Manager_operation_result + {operation_result = Applied (Tx_rollup_return_bond_result _); _} ) -> + Some Eq + | ( Manager_operation {operation = Tx_rollup_return_bond _; _}, + Manager_operation_result + {operation_result = Backtracked (Tx_rollup_return_bond_result _, _); _} + ) -> + Some Eq + | ( Manager_operation {operation = Tx_rollup_return_bond _; _}, + Manager_operation_result + { + operation_result = + Failed (Alpha_context.Kind.Tx_rollup_return_bond_manager_kind, _); + _; + } ) -> + Some Eq + | ( Manager_operation {operation = Tx_rollup_return_bond _; _}, + Manager_operation_result + { + operation_result = + Skipped Alpha_context.Kind.Tx_rollup_return_bond_manager_kind; + _; + } ) -> + Some Eq + | (Manager_operation {operation = Tx_rollup_return_bond _; _}, _) -> None + | ( Manager_operation {operation = Tx_rollup_rejection _; _}, + Manager_operation_result + {operation_result = Applied (Tx_rollup_rejection_result _); _} ) -> + Some Eq + | ( Manager_operation {operation = Tx_rollup_rejection _; _}, + Manager_operation_result + {operation_result = Backtracked (Tx_rollup_rejection_result _, _); _} ) + -> + Some Eq + | ( Manager_operation {operation = Tx_rollup_rejection _; _}, + Manager_operation_result + { + operation_result = + Failed (Alpha_context.Kind.Tx_rollup_rejection_manager_kind, _); + _; + } ) -> + Some Eq + | ( Manager_operation {operation = Tx_rollup_rejection _; _}, + Manager_operation_result + { + operation_result = + Skipped Alpha_context.Kind.Tx_rollup_rejection_manager_kind; + _; + } ) -> + Some Eq + | (Manager_operation {operation = Tx_rollup_rejection _; _}, _) -> None | ( Manager_operation {operation = Sc_rollup_originate _; _}, Manager_operation_result {operation_result = Applied (Sc_rollup_originate_result _); _} ) -> diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index 7fcdc6979d045abe94cd51d1709fcef7e2e00bf3..6bd80edcba677da0312847c49949982c81cd2437 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -179,6 +179,16 @@ and _ successful_manager_operation_result = consumed_gas : Gas.Arith.fp; } -> Kind.tx_rollup_commit successful_manager_operation_result + | Tx_rollup_return_bond_result : { + balance_updates : Receipt.balance_updates; + consumed_gas : Gas.Arith.fp; + } + -> Kind.tx_rollup_return_bond successful_manager_operation_result + | Tx_rollup_rejection_result : { + balance_updates : Receipt.balance_updates; + consumed_gas : Gas.Arith.fp; + } + -> Kind.tx_rollup_rejection successful_manager_operation_result | Sc_rollup_originate_result : { balance_updates : Receipt.balance_updates; address : Sc_rollup.Address.t; diff --git a/src/proto_alpha/lib_protocol/constants_repr.ml b/src/proto_alpha/lib_protocol/constants_repr.ml index bc9112cff620d35f86ce6303a92fc77682e2d42d..09df17a408c59b234cdd097abc4ee52e75f46607 100644 --- a/src/proto_alpha/lib_protocol/constants_repr.ml +++ b/src/proto_alpha/lib_protocol/constants_repr.ml @@ -162,6 +162,10 @@ type parametric = { tx_rollup_origination_size : int; tx_rollup_hard_size_limit_per_inbox : int; tx_rollup_hard_size_limit_per_message : int; + tx_rollup_commitment_bond : Tez_repr.t; + tx_rollup_finality_period : int; + tx_rollup_max_unfinalized_levels : int; + tx_rollup_max_finalize_levels_per_commitment : int; sc_rollup_enable : bool; sc_rollup_origination_size : int; } @@ -209,7 +213,11 @@ let parametric_encoding = ( ( c.tx_rollup_enable, c.tx_rollup_origination_size, c.tx_rollup_hard_size_limit_per_inbox, - c.tx_rollup_hard_size_limit_per_message ), + c.tx_rollup_hard_size_limit_per_message, + c.tx_rollup_commitment_bond, + c.tx_rollup_finality_period, + c.tx_rollup_max_unfinalized_levels, + c.tx_rollup_max_finalize_levels_per_commitment ), (c.sc_rollup_enable, c.sc_rollup_origination_size) ) ) ) ) ) )) (fun ( ( preserved_cycles, @@ -251,7 +259,11 @@ let parametric_encoding = ( ( tx_rollup_enable, tx_rollup_origination_size, tx_rollup_hard_size_limit_per_inbox, - tx_rollup_hard_size_limit_per_message ), + tx_rollup_hard_size_limit_per_message, + tx_rollup_commitment_bond, + tx_rollup_finality_period, + tx_rollup_max_unfinalized_levels, + tx_rollup_max_finalize_levels_per_commitment ), (sc_rollup_enable, sc_rollup_origination_size) ) ) ) ) ) ) -> { preserved_cycles; @@ -294,6 +306,10 @@ let parametric_encoding = tx_rollup_origination_size; tx_rollup_hard_size_limit_per_inbox; tx_rollup_hard_size_limit_per_message; + tx_rollup_commitment_bond; + tx_rollup_finality_period; + tx_rollup_max_unfinalized_levels; + tx_rollup_max_finalize_levels_per_commitment; sc_rollup_enable; sc_rollup_origination_size; }) @@ -350,11 +366,17 @@ let parametric_encoding = (req "cache_stake_distribution_cycles" int8) (req "cache_sampler_state_cycles" int8)) (merge_objs - (obj4 + (obj8 (req "tx_rollup_enable" bool) (req "tx_rollup_origination_size" int31) (req "tx_rollup_hard_size_limit_per_inbox" int31) - (req "tx_rollup_hard_size_limit_per_message" int31)) + (req "tx_rollup_hard_size_limit_per_message" int31) + (req "tx_rollup_commitment_bond" Tez_repr.encoding) + (req "tx_rollup_finality_period" int31) + (req "tx_rollup_max_unfinalized_levels" int31) + (req + "tx_rollup_max_finalize_levels_per_commitment" + int31)) (obj2 (req "sc_rollup_enable" bool) (req "sc_rollup_origination_size" int31)))))))) diff --git a/src/proto_alpha/lib_protocol/constants_repr.mli b/src/proto_alpha/lib_protocol/constants_repr.mli index 062306c4a7d34beb9c9e8fb4e625a773faa0c9fd..c94e2bb5a60fdbcc891ea442bf0dafa19d465e09 100644 --- a/src/proto_alpha/lib_protocol/constants_repr.mli +++ b/src/proto_alpha/lib_protocol/constants_repr.mli @@ -127,6 +127,11 @@ type parametric = { tx_rollup_hard_size_limit_per_inbox : int; (* the maximum amount of bytes one batch can allocate in an inbox *) tx_rollup_hard_size_limit_per_message : int; + (* the amount of tez to bond a tx rollup commitment *) + tx_rollup_commitment_bond : Tez_repr.t; + tx_rollup_finality_period : int; + tx_rollup_max_unfinalized_levels : int; + tx_rollup_max_finalize_levels_per_commitment : int; sc_rollup_enable : bool; sc_rollup_origination_size : int; } @@ -207,7 +212,7 @@ end subcache, a parametric constant can be used to change the limit (see {parametric}). - The number of subcaches and the limits for all those subcaches form + The number of subcaches and the limits for all those subcaches form together what is called the [cache_layout]. *) val cache_layout_size : int diff --git a/src/proto_alpha/lib_protocol/constants_storage.ml b/src/proto_alpha/lib_protocol/constants_storage.ml index 50a01c127defdf95cde08b3daf0c1c6b98182b7e..73ad981f01d4313b447e8fff943d2c397c454699 100644 --- a/src/proto_alpha/lib_protocol/constants_storage.ml +++ b/src/proto_alpha/lib_protocol/constants_storage.ml @@ -161,6 +161,22 @@ let tx_rollup_hard_size_limit_per_message c = let constants = Raw_context.constants c in constants.tx_rollup_hard_size_limit_per_message +let tx_rollup_commitment_bond c = + let constants = Raw_context.constants c in + constants.tx_rollup_commitment_bond + +let tx_rollup_finality_period c = + let constants = Raw_context.constants c in + constants.tx_rollup_finality_period + +let tx_rollup_max_unfinalized_levels c = + let constants = Raw_context.constants c in + constants.tx_rollup_max_unfinalized_levels + +let tx_rollup_max_finalize_levels_per_commitment c = + let constants = Raw_context.constants c in + constants.tx_rollup_max_finalize_levels_per_commitment + let ratio_of_frozen_deposits_slashed_per_double_endorsement c = let constants = Raw_context.constants c in constants.ratio_of_frozen_deposits_slashed_per_double_endorsement diff --git a/src/proto_alpha/lib_protocol/constants_storage.mli b/src/proto_alpha/lib_protocol/constants_storage.mli index 7772926806c619e91aed47931d13d941f97526ca..a0820abd0083c21d535b2b954dfabea839b10673 100644 --- a/src/proto_alpha/lib_protocol/constants_storage.mli +++ b/src/proto_alpha/lib_protocol/constants_storage.mli @@ -91,6 +91,14 @@ val tx_rollup_hard_size_limit_per_inbox : Raw_context.t -> int val tx_rollup_hard_size_limit_per_message : Raw_context.t -> int +val tx_rollup_commitment_bond : Raw_context.t -> Tez_repr.t + +val tx_rollup_finality_period : Raw_context.t -> int + +val tx_rollup_max_unfinalized_levels : Raw_context.t -> int + +val tx_rollup_max_finalize_levels_per_commitment : Raw_context.t -> int + val ratio_of_frozen_deposits_slashed_per_double_endorsement : Raw_context.t -> Constants_repr.ratio diff --git a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml index ca295af19167bddc8a8d7254a616f238a73db680..757e862c4da93f7c1b95ae132836c457576bfd8a 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.ml @@ -43,40 +43,38 @@ let registered c delegate = Signature.Public_key_hash.equal delegate current_delegate | None -> false -let link c contract delegate = - Storage.Contract.Balance.get c contract >>=? fun balance -> - Stake_storage.add_stake c delegate balance >>=? fun c -> +let link c contract stake delegate = + Stake_storage.add_stake c delegate stake >>=? fun c -> Storage.Contract.Delegated.add (c, Contract_repr.implicit_contract delegate) contract >|= ok -let unlink c contract = +let unlink c contract stake = Storage.Contract.Delegate.find c contract >>=? function | None -> return c | Some delegate -> - Storage.Contract.Balance.get c contract >>=? fun balance -> - (* Removes the balance of the contract from the delegate *) - Stake_storage.remove_stake c delegate balance >>=? fun c -> + (* Removes the stake of the contract from the delegate *) + Stake_storage.remove_stake c delegate stake >>=? fun c -> Storage.Contract.Delegated.remove (c, Contract_repr.implicit_contract delegate) contract >|= ok -let init ctxt contract delegate = +let init ctxt contract stake delegate = Storage.Contract.Delegate.init ctxt contract delegate >>=? fun ctxt -> - link ctxt contract delegate + link ctxt contract stake delegate -let delete ctxt contract = - unlink ctxt contract >>=? fun ctxt -> +let delete ctxt contract stake = + unlink ctxt contract stake >>=? fun ctxt -> Storage.Contract.Delegate.remove ctxt contract >|= ok -let remove ctxt contract = unlink ctxt contract +let remove ctxt contract stake = unlink ctxt contract stake -let set ctxt contract delegate = - unlink ctxt contract >>=? fun ctxt -> +let set ctxt contract stake delegate = + unlink ctxt contract stake >>=? fun ctxt -> Storage.Contract.Delegate.add ctxt contract delegate >>= fun ctxt -> - link ctxt contract delegate + link ctxt contract stake delegate let delegated_contracts ctxt delegate = let contract = Contract_repr.implicit_contract 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 1a62d96b6ade5d5d2a8629abd86f508cd2a1c774..10bdb30ef8816592602ea3eb5f098f036f63f449 100644 --- a/src/proto_alpha/lib_protocol/contract_delegate_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_delegate_storage.mli @@ -42,31 +42,37 @@ val registered : val init : Raw_context.t -> Contract_repr.t -> + Tez_repr.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t -(** [remove ctxt contract] removes contract from the list of contracts that +(** [remove ctxt contract stake] removes contract from the list of contracts that delegated to [find ctxt contract], i.e. the output of [delegated_contracts]. This function does not affect the value of the expression - [find ctxt contract]. + [find ctxt contract]. [stake] is the total stake of [contract] This function is undefined if [contract] is not allocated. *) -val remove : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t +val remove : + Raw_context.t -> Contract_repr.t -> Tez_repr.t -> Raw_context.t tzresult Lwt.t -(** [delete ctxt contract] behaves as [remove ctxt contract], but in addition - removes the association of the [contract] to its current delegate, leaving - the former with no delegate. +(** [delete ctxt contract stake] behaves as [remove ctxt contract stake], but + in addition removes the association of the [contract] to its current + delegate, leaving the former with no delegate. [stake] is the total stake of + [contract]. This function is undefined if [contract] is not allocated. *) -val delete : Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t +val delete : + Raw_context.t -> Contract_repr.t -> Tez_repr.t -> Raw_context.t tzresult Lwt.t -(** [set ctxt contract delegate] updates the [delegate] associated to [contract]. +(** [set ctxt contract stake delegate] updates the [delegate] associated to + [contract], where [stake] is the total stake of [contract]. This function is undefined if [contract] is not allocated, or if [contract] does not have a delegate. *) val set : Raw_context.t -> Contract_repr.t -> + Tez_repr.t -> Signature.Public_key_hash.t -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/contract_storage.ml b/src/proto_alpha/lib_protocol/contract_storage.ml index 5bce09cebc5038e0eb675d4e68aaea60cd74adee..7d50eff8c8e88bdca35a63871174d93950d0b05f 100644 --- a/src/proto_alpha/lib_protocol/contract_storage.ml +++ b/src/proto_alpha/lib_protocol/contract_storage.ml @@ -436,13 +436,26 @@ let create_implicit c manager ~balance = ?script:None () -let delete c contract = +let has_stake ctxt contract = + let open Storage.Contract in + Balance.get ctxt contract >>=? fun balance -> + if Tez_repr.(balance > Tez_repr.zero) then return true + else Frozen_rollup_bonds_storage.has_frozen_bonds ctxt contract + +let stake ctxt contract = + let open Storage.Contract in + Balance.get ctxt contract >>=? fun balance -> + Frozen_rollup_bonds_storage.total ctxt contract >>=? fun rollup_bonds -> + Lwt.return Tez_repr.(balance +? rollup_bonds) + +let delete_only_call_from_token c contract = match Contract_repr.is_implicit contract with | None -> (* For non implicit contract Big_map should be cleared *) failwith "Non implicit contracts cannot be removed" | Some _ -> - Contract_delegate_storage.remove c contract >>=? fun c -> + stake c contract >>=? fun stake -> + Contract_delegate_storage.remove c contract stake >>=? fun c -> Storage.Contract.Balance.remove_existing c contract >>=? fun c -> Contract_manager_storage.remove_existing c contract >>=? fun c -> Storage.Contract.Counter.remove_existing c contract >>=? fun c -> @@ -569,8 +582,10 @@ let spend_only_call_from_token c contract amount = >>=? fun c -> if Tez_repr.(new_balance > Tez_repr.zero) then return c else + (* Ensure that empty implicit contracts that delegate to other contracts + remain non empty. *) match Contract_repr.is_implicit contract with - | None -> return c (* Never delete originated contracts *) + | None -> return c | Some pkh -> ( Contract_delegate_storage.find c contract >>=? function | Some pkh' -> @@ -578,9 +593,7 @@ let spend_only_call_from_token c contract amount = else (* Delegated implicit accounts cannot be emptied *) fail (Empty_implicit_delegated_contract pkh) - | None -> - (* Delete empty implicit contract *) - delete c contract)) + | None -> return c)) (* [Tez_repr.(amount <> zero)] is a precondition of this function. It ensures that no entry associating a null balance to an implicit contract exists in the map diff --git a/src/proto_alpha/lib_protocol/contract_storage.mli b/src/proto_alpha/lib_protocol/contract_storage.mli index ed8a89f02d099a4e357a196c4ab7ef0958c351ec..60de8205999dfefa3027d6b6a5a31e3a62388f77 100644 --- a/src/proto_alpha/lib_protocol/contract_storage.mli +++ b/src/proto_alpha/lib_protocol/contract_storage.mli @@ -122,6 +122,9 @@ val update_script_storage : Lazy_storage_diff.diffs option -> Raw_context.t tzresult Lwt.t +val delete_only_call_from_token : + Raw_context.t -> Contract_repr.t -> Raw_context.t tzresult Lwt.t + val credit_only_call_from_token : Raw_context.t -> Contract_repr.t -> Tez_repr.t -> Raw_context.t tzresult Lwt.t @@ -172,3 +175,11 @@ val increase_balance_only_call_from_token : break important invariants. Consider calling [spend] instead. *) val decrease_balance_only_call_from_token : Raw_context.t -> Contract_repr.t -> Tez_repr.t -> Raw_context.t tzresult Lwt.t + +(** [stake ctxt contract] returns true iff [contract] has a positive stake. *) +val has_stake : Raw_context.t -> Contract_repr.t -> bool tzresult Lwt.t + +(** [stake ctxt contract] returns the stake of [contract]. + + This function fails if [contract] is not allocated. *) +val stake : Raw_context.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/delegate_storage.ml b/src/proto_alpha/lib_protocol/delegate_storage.ml index 2677d68150c99edba3654063addb6d4a071ea369..3198917c3a50b574ddda0d7c576cf757845dc514 100644 --- a/src/proto_alpha/lib_protocol/delegate_storage.ml +++ b/src/proto_alpha/lib_protocol/delegate_storage.ml @@ -199,18 +199,20 @@ let init ctxt contract delegate = error_unless known_delegate (Unregistered_delegate delegate) >>?= fun () -> Contract_delegate_storage.registered ctxt delegate >>=? fun is_registered -> error_unless is_registered (Unregistered_delegate delegate) >>?= fun () -> - Contract_delegate_storage.init ctxt contract delegate + Contract_storage.stake ctxt contract >>=? fun stake -> + Contract_delegate_storage.init ctxt contract stake delegate let set c contract delegate = match delegate with | None -> ( + Contract_storage.stake c contract >>=? fun stake -> match Contract_repr.is_implicit contract with | Some pkh -> (* check if contract is a registered delegate *) Contract_delegate_storage.registered c pkh >>=? fun is_registered -> if is_registered then fail (No_deletion pkh) - else Contract_delegate_storage.delete c contract - | None -> Contract_delegate_storage.delete c contract) + else Contract_delegate_storage.delete c contract stake + | None -> Contract_delegate_storage.delete c contract stake) | Some delegate -> Contract_manager_storage.is_manager_key_revealed c delegate >>=? fun known_delegate -> @@ -249,7 +251,8 @@ let set c contract delegate = (self_delegation && not exists) (Empty_delegate_account delegate) >>?= fun () -> - Contract_delegate_storage.set c contract delegate >>=? fun c -> + Contract_storage.stake c contract >>=? fun stake -> + Contract_delegate_storage.set c contract stake delegate >>=? fun c -> if self_delegation then Storage.Delegates.add c delegate >>= fun c -> set_active c delegate else return c @@ -519,7 +522,11 @@ let freeze_deposits_do_not_call_except_for_migration = let cycle_end ctxt last_cycle unrevealed_nonces = let new_cycle = Cycle_repr.add last_cycle 1 in - Stake_storage.select_new_distribution_at_cycle_end ctxt ~new_cycle pubkey + Stake_storage.select_new_distribution_at_cycle_end + ctxt + ~new_cycle + pubkey + Contract_storage.stake >>=? fun ctxt -> clear_outdated_slashed_deposits ctxt ~new_cycle >>= fun ctxt -> distribute_endorsing_rewards ctxt last_cycle unrevealed_nonces diff --git a/src/proto_alpha/lib_protocol/dune.inc b/src/proto_alpha/lib_protocol/dune.inc index 76eeb3bbf8c67a55cc656f8824c3b3dfcc0a3597..02eceadde055e4349974237b7b7f7b20ecd53300 100644 --- a/src/proto_alpha/lib_protocol/dune.inc +++ b/src/proto_alpha/lib_protocol/dune.inc @@ -73,6 +73,8 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end tx_rollup_message_repr.mli tx_rollup_message_repr.ml tx_rollup_inbox_repr.mli tx_rollup_inbox_repr.ml tx_rollup_commitments_repr.mli tx_rollup_commitments_repr.ml + rollup_bond_id_repr.mli rollup_bond_id_repr.ml + tx_rollup_rejection_repr.mli tx_rollup_rejection_repr.ml vote_repr.mli vote_repr.ml block_header_repr.mli block_header_repr.ml entrypoint_repr.mli entrypoint_repr.ml @@ -104,6 +106,7 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end contract_delegate_storage.mli contract_delegate_storage.ml sapling_storage.ml lazy_storage_diff.mli lazy_storage_diff.ml + frozen_rollup_bonds_storage.mli frozen_rollup_bonds_storage.ml contract_storage.mli contract_storage.ml commitment_storage.mli commitment_storage.ml token.mli token.ml @@ -228,6 +231,8 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end tx_rollup_message_repr.mli tx_rollup_message_repr.ml tx_rollup_inbox_repr.mli tx_rollup_inbox_repr.ml tx_rollup_commitments_repr.mli tx_rollup_commitments_repr.ml + rollup_bond_id_repr.mli rollup_bond_id_repr.ml + tx_rollup_rejection_repr.mli tx_rollup_rejection_repr.ml vote_repr.mli vote_repr.ml block_header_repr.mli block_header_repr.ml entrypoint_repr.mli entrypoint_repr.ml @@ -259,6 +264,7 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end contract_delegate_storage.mli contract_delegate_storage.ml sapling_storage.ml lazy_storage_diff.mli lazy_storage_diff.ml + frozen_rollup_bonds_storage.mli frozen_rollup_bonds_storage.ml contract_storage.mli contract_storage.ml commitment_storage.mli commitment_storage.ml token.mli token.ml @@ -383,6 +389,8 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end tx_rollup_message_repr.mli tx_rollup_message_repr.ml tx_rollup_inbox_repr.mli tx_rollup_inbox_repr.ml tx_rollup_commitments_repr.mli tx_rollup_commitments_repr.ml + rollup_bond_id_repr.mli rollup_bond_id_repr.ml + tx_rollup_rejection_repr.mli tx_rollup_rejection_repr.ml vote_repr.mli vote_repr.ml block_header_repr.mli block_header_repr.ml entrypoint_repr.mli entrypoint_repr.ml @@ -414,6 +422,7 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end contract_delegate_storage.mli contract_delegate_storage.ml sapling_storage.ml lazy_storage_diff.mli lazy_storage_diff.ml + frozen_rollup_bonds_storage.mli frozen_rollup_bonds_storage.ml contract_storage.mli contract_storage.ml commitment_storage.mli commitment_storage.ml token.mli token.ml @@ -560,6 +569,8 @@ include Tezos_raw_protocol_alpha.Main Tx_rollup_message_repr Tx_rollup_inbox_repr Tx_rollup_commitments_repr + Rollup_bond_id_repr + Tx_rollup_rejection_repr Vote_repr Block_header_repr Entrypoint_repr @@ -591,6 +602,7 @@ include Tezos_raw_protocol_alpha.Main Contract_delegate_storage Sapling_storage Lazy_storage_diff + Frozen_rollup_bonds_storage Contract_storage Commitment_storage Token @@ -756,6 +768,8 @@ include Tezos_raw_protocol_alpha.Main tx_rollup_message_repr.mli tx_rollup_message_repr.ml tx_rollup_inbox_repr.mli tx_rollup_inbox_repr.ml tx_rollup_commitments_repr.mli tx_rollup_commitments_repr.ml + rollup_bond_id_repr.mli rollup_bond_id_repr.ml + tx_rollup_rejection_repr.mli tx_rollup_rejection_repr.ml vote_repr.mli vote_repr.ml block_header_repr.mli block_header_repr.ml entrypoint_repr.mli entrypoint_repr.ml @@ -787,6 +801,7 @@ include Tezos_raw_protocol_alpha.Main contract_delegate_storage.mli contract_delegate_storage.ml sapling_storage.ml lazy_storage_diff.mli lazy_storage_diff.ml + frozen_rollup_bonds_storage.mli frozen_rollup_bonds_storage.ml contract_storage.mli contract_storage.ml commitment_storage.mli commitment_storage.ml token.mli token.ml diff --git a/src/proto_alpha/lib_protocol/frozen_rollup_bonds_storage.ml b/src/proto_alpha/lib_protocol/frozen_rollup_bonds_storage.ml new file mode 100644 index 0000000000000000000000000000000000000000..391c62f0d8b789a9db00304474a1459065389c2c --- /dev/null +++ b/src/proto_alpha/lib_protocol/frozen_rollup_bonds_storage.ml @@ -0,0 +1,112 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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 encapsulates the following (carbonated) storage maps: + - [Storage.Contract.Frozen_rollup_bonds] + - [Storage.Contract.Total_rollup_bonds] + + This module enforces the following invariants: + - [ (Frozen_rollup_bonds.mem x) <-> (Total_rollup_bonds.mem x) ] + - [ Total_rollup_bonds.find (ctxt, contract) ] = sum of all bond values in + [ Frozen_rollup_bonds] that are associated to contract. *) + +open Storage.Contract + +type error += + | Frozen_rollup_bonds_must_be_spent_at_once of + Contract_repr.t * Rollup_bond_id_repr.t + +let () = + register_error_kind + `Permanent + ~id:"frozen_rollup_bonds.must_be_spent_at_once" + ~title:"Partial spending of rollup bonds" + ~description:"Frozen rollup bonds must be spent at once." + ~pp:(fun ppf (contract, bond_id) -> + Format.fprintf + ppf + "The frozen funds for contract (%a) and rollup bond (%a) are not \ + allowed to be partially withdrawn. The amount withdrawn must be equal \ + to the entire deposit for the said bond." + Contract_repr.pp + contract + Rollup_bond_id_repr.pp + bond_id) + Data_encoding.( + obj2 + (req "contract" Contract_repr.encoding) + (req "bond_id" Rollup_bond_id_repr.encoding)) + (function + | Frozen_rollup_bonds_must_be_spent_at_once (c, b) -> Some (c, b) + | _ -> None) + (fun (c, b) -> Frozen_rollup_bonds_must_be_spent_at_once (c, b)) + +let has_frozen_bonds ctxt contract = Total_rollup_bonds.mem ctxt contract >|= ok + +let allocated ctxt contract bond_id = + Frozen_rollup_bonds.mem (ctxt, contract) bond_id + +let find ctxt contract bond_id = + Frozen_rollup_bonds.find (ctxt, contract) bond_id + +(** PRE : amount > 0, fullfilled by unique caller [Token.transfer]. *) +let spend_only_call_from_token ctxt contract bond_id amount = + Contract_delegate_storage.remove_contract_stake ctxt contract amount + >>=? fun ctxt -> + Frozen_rollup_bonds.get (ctxt, contract) bond_id + >>=? fun (ctxt, frozen_bonds) -> + error_when + Tez_repr.(frozen_bonds <> amount) + (Frozen_rollup_bonds_must_be_spent_at_once (contract, bond_id)) + >>?= fun () -> + Frozen_rollup_bonds.remove_existing (ctxt, contract) bond_id + >>=? fun (ctxt, _) -> + Total_rollup_bonds.get ctxt contract >>=? fun total -> + Tez_repr.(total -? amount) >>?= fun new_total -> + if Tez_repr.(new_total = zero) then + Total_rollup_bonds.remove_existing ctxt contract + else Total_rollup_bonds.update ctxt contract new_total + +(** PRE : [amount > 0], fullfilled by unique caller [Token.transfer].*) +let credit_only_call_from_token ctxt contract bond_id amount = + Contract_delegate_storage.add_contract_stake ctxt contract amount + >>=? fun ctxt -> + ( Frozen_rollup_bonds.find (ctxt, contract) bond_id + >>=? fun (ctxt, frozen_bonds_opt) -> + match frozen_bonds_opt with + | None -> Frozen_rollup_bonds.init (ctxt, contract) bond_id amount + | Some frozen_bonds -> + Tez_repr.(frozen_bonds +? amount) >>?= fun new_amount -> + Frozen_rollup_bonds.update (ctxt, contract) bond_id new_amount ) + >>=? fun (ctxt, _consumed1) -> + Total_rollup_bonds.find ctxt contract >>=? fun total_opt -> + match total_opt with + | None -> Total_rollup_bonds.init ctxt contract amount + | Some total -> + Tez_repr.(total +? amount) >>?= fun new_total -> + Total_rollup_bonds.update ctxt contract new_total + +let total ctxt contract = + Total_rollup_bonds.find ctxt contract >|=? Option.value ~default:Tez_repr.zero diff --git a/src/proto_alpha/lib_protocol/frozen_rollup_bonds_storage.mli b/src/proto_alpha/lib_protocol/frozen_rollup_bonds_storage.mli new file mode 100644 index 0000000000000000000000000000000000000000..a7371bc0b613360f29dc5e74bdfcefad226ff14c --- /dev/null +++ b/src/proto_alpha/lib_protocol/frozen_rollup_bonds_storage.mli @@ -0,0 +1,89 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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 manages frozen rollups deposits (here called bonds). These + bonds are part of the stake of the contract making the deposit. *) + +(** This error is raised when [spend_only_call_from_token] is called with an + amount that is not equal to the deposit associated to the given contract and + rollup bond id. *) +type error += + | (* `Permanent *) + Frozen_rollup_bonds_must_be_spent_at_once of + Contract_repr.t * Rollup_bond_id_repr.t + +(** [has_frozen_bonds ctxt contract] returns true iff there are frozen bonds + associated to [contract]. *) +val has_frozen_bonds : Raw_context.t -> Contract_repr.t -> bool tzresult Lwt.t + +(** [allocated ctxt contract bond_id] returns a new context because of an access + to carbonated data, and a boolean that is [true] iff there is a bond + associated to [contract] and [bond_id]. *) +val allocated : + Raw_context.t -> + Contract_repr.t -> + Rollup_bond_id_repr.t -> + (Raw_context.t * bool) tzresult Lwt.t + +(** [find ctxt contract bond_id] returns a new context because of an access + to carbonated data, and the bond associated to [contract] and [bond_id] if + there is one, or [None] if there is none. *) +val find : + Raw_context.t -> + Contract_repr.t -> + Rollup_bond_id_repr.t -> + (Raw_context.t * Tez_repr.t option) tzresult Lwt.t + +(** [spend ctxt contract bond_id amount] withdraws the given [amount] from + the value of the bond associated to [contract] and [bond_id]. + + Fails when there is no bond for [contract] and [bond_id]. + + @raise a [Frozen_rollup_bonds_must_be_spent_at_once (contract, bond_id)] + error when the amount is different from the bond associated to [contract] + and [bond_id]. + *) +val spend_only_call_from_token : + Raw_context.t -> + Contract_repr.t -> + Rollup_bond_id_repr.t -> + Tez_repr.t -> + Raw_context.t tzresult Lwt.t + +(** [credit ctxt contract bond_id amount] adds the given [amount] to the bond + associated to [contract] and [bond_id]. If no bond exists, one whose value + is [amount] is created. + + Fails when [(find ctxt contract bond_id) + amount > Int64.max_int]. *) +val credit_only_call_from_token : + Raw_context.t -> + Contract_repr.t -> + Rollup_bond_id_repr.t -> + Tez_repr.t -> + Raw_context.t tzresult Lwt.t + +(** [total ctxt contract] returns the total amount of bonds associated to + [contract]. *) +val total : Raw_context.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/init_storage.ml b/src/proto_alpha/lib_protocol/init_storage.ml index 300345ec8d3dd7e602c15a3e5945b74f073d1884..93b9515666df0f9ba2ea47ff161aec8e7f231ec0 100644 --- a/src/proto_alpha/lib_protocol/init_storage.ml +++ b/src/proto_alpha/lib_protocol/init_storage.ml @@ -89,7 +89,10 @@ let prepare_first_block ctxt ~typecheck ~level ~timestamp = param.bootstrap_accounts param.bootstrap_contracts >>=? fun (ctxt, bootstrap_balance_updates) -> - Stake_storage.init_first_cycles ctxt Delegate_storage.pubkey + Stake_storage.init_first_cycles + ctxt + Delegate_storage.pubkey + Contract_storage.stake >>=? fun ctxt -> let cycle = (Raw_context.current_level ctxt).cycle in Delegate_storage.freeze_deposits_do_not_call_except_for_migration diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index baa7930748c13b4043b70d6f713b191535462329..93352617f1f1537e6b87941fe03473f580789dec 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -77,6 +77,10 @@ module Kind = struct type tx_rollup_commit = Tx_rollup_commit_kind + type tx_rollup_return_bond = Tx_rollup_return_bond_kind + + type tx_rollup_rejection = Tx_rollup_rejection_kind + type sc_rollup_originate = Sc_rollup_originate_kind type sc_rollup_add_messages = Sc_rollup_add_messages_kind @@ -91,6 +95,8 @@ module Kind = struct | 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 + | Tx_rollup_return_bond_manager_kind : tx_rollup_return_bond manager + | Tx_rollup_rejection_manager_kind : tx_rollup_rejection manager | Sc_rollup_originate_manager_kind : sc_rollup_originate manager | Sc_rollup_add_messages_manager_kind : sc_rollup_add_messages manager end @@ -281,6 +287,19 @@ and _ manager_operation = commitment : Tx_rollup_commitments_repr.Commitment.t; } -> Kind.tx_rollup_commit manager_operation + | Tx_rollup_return_bond : { + tx_rollup : Tx_rollup_repr.t; + } + -> Kind.tx_rollup_return_bond manager_operation + | Tx_rollup_rejection : { + rollup : Tx_rollup_repr.t; + level : Raw_level_repr.t; + hash : Tx_rollup_commitments_repr.Commitment_hash.t; + batch_index : int; + batch : Tx_rollup_message_repr.t; + nonce : int64; + } + -> Kind.tx_rollup_rejection manager_operation | Sc_rollup_originate : { kind : Sc_rollup_repr.Kind.t; boot_sector : Sc_rollup_repr.PVM.boot_sector; @@ -305,6 +324,8 @@ let manager_kind : type kind. kind manager_operation -> kind Kind.manager = | 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 + | Tx_rollup_return_bond _ -> Kind.Tx_rollup_return_bond_manager_kind + | Tx_rollup_rejection _ -> Kind.Tx_rollup_rejection_manager_kind | Sc_rollup_originate _ -> Kind.Sc_rollup_originate_manager_kind | Sc_rollup_add_messages _ -> Kind.Sc_rollup_add_messages_manager_kind @@ -574,6 +595,46 @@ module Encoding = struct Tx_rollup_commit {tx_rollup; commitment}); } + let[@coq_axiom_with_reason "gadt"] tx_rollup_return_bond_case = + MCase + { + tag = tx_rollup_operation_tag_offset + 3; + name = "tx_rollup_return_bond"; + encoding = obj1 (req "rollup" Tx_rollup_repr.encoding); + select = + (function + | Manager (Tx_rollup_return_bond _ as op) -> Some op | _ -> None); + proj = (function Tx_rollup_return_bond {tx_rollup} -> tx_rollup); + inj = (fun tx_rollup -> Tx_rollup_return_bond {tx_rollup}); + } + + let[@coq_axiom_with_reason "gadt"] tx_rollup_rejection_case = + MCase + { + tag = tx_rollup_operation_tag_offset + 4; + name = "tx_rollup_rejection"; + encoding = + obj6 + (req "rollup" Tx_rollup_repr.encoding) + (req "level" Raw_level_repr.encoding) + (req "hash" Tx_rollup_commitments_repr.Commitment_hash.encoding) + (req "batch_index" int31) + (req "batch" Tx_rollup_message_repr.encoding) + (req "nonce" int64); + select = + (function + | Manager (Tx_rollup_rejection _ as op) -> Some op | _ -> None); + proj = + (function + | Tx_rollup_rejection + {rollup; level; hash; batch_index; batch; nonce} -> + (rollup, level, hash, batch_index, batch, nonce)); + inj = + (fun (rollup, level, hash, batch_index, batch, nonce) -> + Tx_rollup_rejection + {rollup; level; hash; batch_index; batch; nonce}); + } + let[@coq_axiom_with_reason "gadt"] sc_rollup_originate_case = MCase { @@ -635,6 +696,8 @@ module Encoding = struct make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; + make tx_rollup_return_bond_case; + make tx_rollup_rejection_case; make sc_rollup_originate_case; make sc_rollup_add_messages_case; ] @@ -950,6 +1013,16 @@ module Encoding = struct tx_rollup_operation_commit_tag Manager_operations.tx_rollup_commit_case + let tx_rollup_return_bond_case = + make_manager_case + (tx_rollup_operation_tag_offset + 3) + Manager_operations.tx_rollup_return_bond_case + + let tx_rollup_rejection_case = + make_manager_case + (tx_rollup_operation_tag_offset + 4) + Manager_operations.tx_rollup_rejection_case + let sc_rollup_originate_case = make_manager_case sc_rollup_operation_origination_tag @@ -991,6 +1064,8 @@ module Encoding = struct make tx_rollup_origination_case; make tx_rollup_submit_batch_case; make tx_rollup_commit_case; + make tx_rollup_return_bond_case; + make tx_rollup_rejection_case; make sc_rollup_originate_case; make sc_rollup_add_messages_case; ] @@ -1198,6 +1273,10 @@ let equal_manager_operation_kind : | (Tx_rollup_submit_batch _, _) -> None | (Tx_rollup_commit _, Tx_rollup_commit _) -> Some Eq | (Tx_rollup_commit _, _) -> None + | (Tx_rollup_return_bond _, Tx_rollup_return_bond _) -> Some Eq + | (Tx_rollup_return_bond _, _) -> None + | (Tx_rollup_rejection _, Tx_rollup_rejection _) -> Some Eq + | (Tx_rollup_rejection _, _) -> None | (Sc_rollup_originate _, Sc_rollup_originate _) -> Some Eq | (Sc_rollup_originate _, _) -> None | (Sc_rollup_add_messages _, Sc_rollup_add_messages _) -> Some Eq @@ -1313,6 +1392,12 @@ let internal_manager_operation_size (type a) (op : a manager_operation) = | Tx_rollup_commit _ -> (* Tx_rollup_commit operation can’t occur as internal operations *) assert false + | Tx_rollup_return_bond _ -> + (* Tx_rollup_return_bond operation can’t occur as internal operations *) + assert false + | Tx_rollup_rejection _ -> + (* Tx_rollup_rejection operation can’t occur as internal operations *) + assert false let packed_internal_operation_in_memory_size : packed_internal_operation -> nodes_and_size = function diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index df21d962d1e03465ec9be25d2b1c287d31dffccf..03d68c8a1758acbc4603daec2ceb226a58242526 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -103,6 +103,10 @@ module Kind : sig type tx_rollup_commit = Tx_rollup_commit_kind + type tx_rollup_return_bond = Tx_rollup_return_bond_kind + + type tx_rollup_rejection = Tx_rollup_rejection_kind + type sc_rollup_originate = Sc_rollup_originate_kind type sc_rollup_add_messages = Sc_rollup_add_messages_kind @@ -117,6 +121,8 @@ module Kind : sig | 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 + | Tx_rollup_return_bond_manager_kind : tx_rollup_return_bond manager + | Tx_rollup_rejection_manager_kind : tx_rollup_rejection manager | Sc_rollup_originate_manager_kind : sc_rollup_originate manager | Sc_rollup_add_messages_manager_kind : sc_rollup_add_messages manager end @@ -260,6 +266,19 @@ and _ manager_operation = commitment : Tx_rollup_commitments_repr.Commitment.t; } -> Kind.tx_rollup_commit manager_operation + | Tx_rollup_return_bond : { + tx_rollup : Tx_rollup_repr.t; + } + -> Kind.tx_rollup_return_bond manager_operation + | Tx_rollup_rejection : { + rollup : Tx_rollup_repr.t; + level : Raw_level_repr.t; + hash : Tx_rollup_commitments_repr.Commitment_hash.t; + batch_index : int; + batch : Tx_rollup_message_repr.t; + nonce : int64; + } + -> Kind.tx_rollup_rejection manager_operation | Sc_rollup_originate : { kind : Sc_rollup_repr.Kind.t; boot_sector : Sc_rollup_repr.PVM.boot_sector; @@ -396,6 +415,10 @@ module Encoding : sig val tx_rollup_commit_case : Kind.tx_rollup_commit Kind.manager case + val tx_rollup_return_bond_case : Kind.tx_rollup_return_bond Kind.manager case + + val tx_rollup_rejection_case : Kind.tx_rollup_rejection Kind.manager case + val sc_rollup_originate_case : Kind.sc_rollup_originate Kind.manager case val sc_rollup_add_messages_case : @@ -431,6 +454,10 @@ module Encoding : sig val tx_rollup_commit_case : Kind.tx_rollup_commit case + val tx_rollup_return_bond_case : Kind.tx_rollup_return_bond case + + val tx_rollup_rejection_case : Kind.tx_rollup_rejection case + val sc_rollup_originate_case : Kind.sc_rollup_originate case val sc_rollup_add_messages_case : Kind.sc_rollup_add_messages case diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index edeca90b1bce25f65d796072565eb41a4d4ae30b..45ddcae1749e000a36ad5c67fe8d06be184909bf 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -881,6 +881,10 @@ let prepare_first_block ~level ~timestamp ctxt = tx_rollup_origination_size = 60_000; tx_rollup_hard_size_limit_per_inbox = 100_000; tx_rollup_hard_size_limit_per_message = 5_000; + tx_rollup_commitment_bond = Tez_repr.of_mutez_exn 10_000_000_000L; + tx_rollup_finality_period = 2_000; + tx_rollup_max_unfinalized_levels = 2_100; + tx_rollup_max_finalize_levels_per_commitment = 5; sc_rollup_enable = false; (* The following value is chosen to prevent spam. *) sc_rollup_origination_size = 6_314; diff --git a/src/proto_alpha/lib_protocol/receipt_repr.ml b/src/proto_alpha/lib_protocol/receipt_repr.ml index d9e1cd6fe9a83f67c5d7e0445f9e7bd4e9912e44..ac1380e4b2f65054ff1f92760f5b7c924beeea72 100644 --- a/src/proto_alpha/lib_protocol/receipt_repr.ml +++ b/src/proto_alpha/lib_protocol/receipt_repr.ml @@ -43,6 +43,7 @@ type balance = | Invoice | Initial_commitments | Minted + | Rollup_bonds of Contract_repr.t * Rollup_bond_id_repr.t let balance_encoding = let open Data_encoding in @@ -201,6 +202,16 @@ let balance_encoding = (req "category" (constant "minted"))) (function Minted -> Some ((), ()) | _ -> None) (fun ((), ()) -> Minted); + case + (Tag 21) + ~title:"Frozen_rollup_bonds" + (obj4 + (req "kind" (constant "freezer")) + (req "category" (constant "tx_rollup_bonds")) + (req "contract" Contract_repr.encoding) + (req "bond_id" Rollup_bond_id_repr.encoding)) + (function Rollup_bonds (c, r) -> Some ((), (), c, r) | _ -> None) + (fun ((), (), c, r) -> Rollup_bonds (c, r)); ] let is_not_zero c = not (Compare.Int.equal c 0) @@ -219,6 +230,9 @@ let compare_balance ba bb = if is_not_zero c then c else Compare.Bool.compare ra rb | (Commitments bpkha, Commitments bpkhb) -> Blinded_public_key_hash.compare bpkha bpkhb + | (Rollup_bonds (ca, ra), Rollup_bonds (cb, rb)) -> + let c = Contract_repr.compare ca cb in + if is_not_zero c then c else Rollup_bond_id_repr.compare ra rb | (_, _) -> let index b = match b with @@ -240,6 +254,7 @@ let compare_balance ba bb = | Invoice -> 15 | Initial_commitments -> 16 | Minted -> 17 + | Rollup_bonds _ -> 18 (* don't forget to add parameterized cases in the first part of the function *) in Compare.Int.compare (index ba) (index bb) diff --git a/src/proto_alpha/lib_protocol/receipt_repr.mli b/src/proto_alpha/lib_protocol/receipt_repr.mli index 42817c0b9ebec605e04b3dc7c211bb7b5d99db61..58a69d20821c94a07c636dbe2e3df64782ea6438 100644 --- a/src/proto_alpha/lib_protocol/receipt_repr.mli +++ b/src/proto_alpha/lib_protocol/receipt_repr.mli @@ -44,6 +44,7 @@ type balance = | Invoice | Initial_commitments | Minted + | Rollup_bonds of Contract_repr.t * Rollup_bond_id_repr.t (** Compares two balances. *) val compare_balance : balance -> balance -> int diff --git a/src/proto_alpha/lib_protocol/rollup_bond_id_repr.ml b/src/proto_alpha/lib_protocol/rollup_bond_id_repr.ml new file mode 100644 index 0000000000000000000000000000000000000000..700033046423f91a80f3598e5e5023626a85ec60 --- /dev/null +++ b/src/proto_alpha/lib_protocol/rollup_bond_id_repr.ml @@ -0,0 +1,88 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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. *) +(* *) +(*****************************************************************************) + +type t = Tx_rollup_bond_id of Tx_rollup_repr.t + +include Compare.Make (struct + type nonrec t = t + + let compare id1 id2 = + match (id1, id2) with + | (Tx_rollup_bond_id id1, Tx_rollup_bond_id id2) -> + Tx_rollup_repr.compare id1 id2 +end) + +let encoding = + let open Data_encoding in + def "tx_rollup_bond_id" + @@ union + [ + case + (Tag 0) + ~title:"Tx_rollup_bond_id" + (obj1 (req "tx_rollup" Tx_rollup_repr.encoding)) + (function Tx_rollup_bond_id id -> Some id) + (fun id -> Tx_rollup_bond_id id); + ] + +let pp ppf (Tx_rollup_bond_id id) = Tx_rollup_repr.pp ppf id + +let rpc_arg = + let construct (Tx_rollup_bond_id id) = Tx_rollup_repr.to_b58check id in + let destruct id = + Result.map_error + (fun _ -> "Cannot parse tx rollup id") + (Tx_rollup_repr.of_b58check id >>? fun id -> ok (Tx_rollup_bond_id id)) + in + RPC_arg.make + ~descr:"A rollup bond identifier." + ~name:"rollup_bond_id" + ~construct + ~destruct + () + +module Index = struct + type nonrec t = t + + let path_length = 1 + + let to_path c l = + let raw_key = Data_encoding.Binary.to_bytes_exn encoding c in + let (`Hex key) = Hex.of_bytes raw_key in + key :: l + + let of_path = function + | [key] -> + Option.bind + (Hex.to_bytes (`Hex key)) + (Data_encoding.Binary.of_bytes_opt encoding) + | _ -> None + + let rpc_arg = rpc_arg + + let encoding = encoding + + let compare = compare +end diff --git a/src/proto_alpha/lib_protocol/rollup_bond_id_repr.mli b/src/proto_alpha/lib_protocol/rollup_bond_id_repr.mli new file mode 100644 index 0000000000000000000000000000000000000000..ff37a6cdff502590d4abf42e9ef18817b177ca63 --- /dev/null +++ b/src/proto_alpha/lib_protocol/rollup_bond_id_repr.mli @@ -0,0 +1,36 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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 defines identifiers for rollup bonds. *) + +type t = Tx_rollup_bond_id of Tx_rollup_repr.t + +val pp : Format.formatter -> t -> unit + +val encoding : t Data_encoding.t + +include Compare.S with type t := t + +module Index : Storage_description.INDEX with type t = t diff --git a/src/proto_alpha/lib_protocol/stake_storage.ml b/src/proto_alpha/lib_protocol/stake_storage.ml index 34d68b4538f864a436857014c5186fdd2336a0c1..9adde0c03bd38f1d5b64a492f7e194b098a5c67d 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.ml +++ b/src/proto_alpha/lib_protocol/stake_storage.ml @@ -170,7 +170,7 @@ let snapshot ctxt = Storage.Stake.Staking_balance.snapshot ctxt index >>=? fun ctxt -> Storage.Stake.Active_delegate_with_one_roll.snapshot ctxt index -let select_distribution_for_cycle ctxt cycle pubkey = +let select_distribution_for_cycle ctxt cycle pubkey undeposited_stake = Storage.Stake.Last_snapshot.get ctxt >>=? fun max_index -> Storage.Seed.For_cycle.get ctxt cycle >>=? fun seed -> let rd = Seed_repr.initialize_new seed [Bytes.of_string "stake_snapshot"] in @@ -192,8 +192,7 @@ let select_distribution_for_cycle ctxt cycle pubkey = let delegate_contract = Contract_repr.implicit_contract delegate in Storage.Contract.Frozen_deposits_limit.find ctxt delegate_contract >>=? fun frozen_deposits_limit -> - Storage.Contract.Balance.get ctxt delegate_contract - >>=? fun balance -> + undeposited_stake ctxt delegate_contract >>=? fun balance -> Frozen_deposits_storage.get ctxt delegate_contract >>=? fun frozen_deposits -> Tez_repr.(balance +? frozen_deposits.current_amount) @@ -261,7 +260,7 @@ let clear_cycle ctxt cycle = Delegate_sampler_state.remove_existing ctxt cycle >>=? fun ctxt -> Storage.Seed.For_cycle.remove_existing ctxt cycle -let init_first_cycles ctxt pubkey = +let init_first_cycles ctxt pubkey undeposited_stake = let preserved = Constants_storage.preserved_cycles ctxt in List.fold_left_es (fun ctxt c -> @@ -269,7 +268,7 @@ let init_first_cycles ctxt pubkey = 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 pubkey) + select_distribution_for_cycle ctxt cycle pubkey undeposited_stake) ctxt (0 --> preserved) @@ -283,10 +282,10 @@ let fold ctxt ~f ~order init = get_staking_balance ctxt delegate >>=? fun stake -> f (delegate, stake) acc) -let select_new_distribution_at_cycle_end ctxt ~new_cycle = +let select_new_distribution_at_cycle_end ctxt ~new_cycle undeposited_stake = 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 + select_distribution_for_cycle ctxt for_cycle undeposited_stake let clear_at_cycle_end ctxt ~new_cycle = let max_slashing_period = Constants_storage.max_slashing_period ctxt in diff --git a/src/proto_alpha/lib_protocol/stake_storage.mli b/src/proto_alpha/lib_protocol/stake_storage.mli index 66984a63211eb7090b5e500a7a7d5e97a587cb84..281fe093f727cb12ef0ee4d29bebdf12daed575a 100644 --- a/src/proto_alpha/lib_protocol/stake_storage.mli +++ b/src/proto_alpha/lib_protocol/stake_storage.mli @@ -68,6 +68,7 @@ val select_distribution_for_cycle_do_not_call_except_for_migration : (Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t) -> + (Raw_context.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t) -> Raw_context.t tzresult Lwt.t val clear_cycle : Raw_context.t -> Cycle_repr.t -> Raw_context.t tzresult Lwt.t @@ -77,6 +78,7 @@ val init_first_cycles : (Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t) -> + (Raw_context.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t) -> Raw_context.t tzresult Lwt.t val fold : @@ -92,6 +94,7 @@ val select_new_distribution_at_cycle_end : (Raw_context.t -> Signature.Public_key_hash.t -> Signature.Public_key.t tzresult Lwt.t) -> + (Raw_context.t -> Contract_repr.t -> Tez_repr.t tzresult Lwt.t) -> Raw_context.t tzresult Lwt.t val clear_at_cycle_end : diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 6eccb3d03f3bc11a62447eff75ce7b4376cbbafd..2d72f1f5bccd76e7add7bfc864ca948d40675a6e 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -348,6 +348,28 @@ module Contract = struct let name = ["frozen_deposits_limit"] end) (Tez_repr) + + module Rollup_bond_id_index = + Make_indexed_subcontext + (Make_subcontext (Registered) (Indexed_context.Raw_context) + (struct + let name = ["rollup_bond_id_index"] + end)) + (Make_index (Rollup_bond_id_repr.Index)) + + module Frozen_rollup_bonds = + Rollup_bond_id_index.Make_carbonated_map + (struct + let name = ["rollup_bonds"] + end) + (Tez_repr) + + module Total_rollup_bonds = + Indexed_context.Make_map + (struct + let name = ["total_rollup_bonds"] + end) + (Tez_repr) end module type NEXT = sig @@ -840,15 +862,15 @@ module Public_key_hash = struct match key with | Ed25519 h -> ( match Path_Ed25519.to_path h l with - | [s] -> ["ed25519"; s] + | s :: t -> "ed25519" :: s :: t | _ -> assert false) | Secp256k1 h -> ( match Path_Secp256k1.to_path h l with - | [s] -> ["secp256k1"; s] + | s :: t -> "secp256k1" :: s :: t | _ -> assert false) | P256 h -> ( match Path_P256.to_path h l with - | [s] -> ["p256"; s] + | s :: t -> "p256" :: s :: t | _ -> assert false) let of_path : _ -> public_key_hash option = function @@ -1429,12 +1451,31 @@ module Tx_rollup = struct (Raw_level_repr.Index)) (Make_index (Tx_rollup_repr.Index))) + module Bond_indexed_context = + Make_indexed_subcontext + (Make_subcontext (Registered) (Raw_context) + (struct + let name = ["tx_rollup_bond"] + end)) + (Pair (Make_index (Tx_rollup_repr.Index)) (Public_key_hash_index)) + module Commitment_list = Level_indexed_context.Make_carbonated_map (struct let name = ["commitment_list"] end) (Tx_rollup_commitments_repr) + + module Commitment_bond = + Bond_indexed_context.Make_carbonated_map + (struct + let name = ["commitment_bond"] + end) + (struct + type t = int + + let encoding = Data_encoding.int31 + end) end module Sc_rollup = struct diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 8e94c44f89215d37809901baf0bd23235b54633e..3a88700a4e494bf4ce697571a1f48360e3554343 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -179,6 +179,18 @@ module Contract : sig with type key = Contract_repr.t and type value = Z.t and type t := Raw_context.t + + module Frozen_rollup_bonds : + Non_iterable_indexed_carbonated_data_storage + with type key = Rollup_bond_id_repr.t + and type value = Tez_repr.t + and type t := Raw_context.t * Contract_repr.t + + module Total_rollup_bonds : + Indexed_data_storage + with type key = Contract_repr.t + and type value = Tez_repr.t + and type t := Raw_context.t end module Big_map : sig @@ -635,6 +647,16 @@ module Tx_rollup : sig with type key = Raw_level_repr.t * Tx_rollup_repr.t and type value = Tx_rollup_commitments_repr.t and type t := Raw_context.t + + (* This stores information about which contracts have bonds + for each rollup, and how many commitments those bonds + stake. *) + module Commitment_bond : + Non_iterable_indexed_carbonated_data_storage + with type key = Tx_rollup_repr.t * Signature.public_key_hash + (* The value here is the number of unfinalized commitments *) + and type value = int + and type t := Raw_context.t end module Sc_rollup : sig diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index 6e31efeb69259949d80cd829cd18c787105044aa..af9015de82c443cdbf106323debf5f953470ef60 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -700,7 +700,8 @@ let bake_n_with_all_balance_updates ?(baking_mode = Application) ?policy | Reveal_result _ | Delegation_result _ | Set_deposits_limit_result _ | Tx_rollup_origination_result _ | Tx_rollup_submit_batch_result _ | Tx_rollup_commit_result _ - | Sc_rollup_originate_result _ | Sc_rollup_add_messages_result _ + | Tx_rollup_return_bond_result _ | Sc_rollup_originate_result _ + | Tx_rollup_rejection_result _ | Sc_rollup_add_messages_result _ -> balance_updates_rev | Transaction_result @@ -733,6 +734,8 @@ let bake_n_with_origination_results ?(baking_mode = Application) ?policy n b = | Successful_manager_result (Tx_rollup_origination_result _) | Successful_manager_result (Tx_rollup_submit_batch_result _) | Successful_manager_result (Tx_rollup_commit_result _) + | Successful_manager_result (Tx_rollup_return_bond_result _) + | Successful_manager_result (Tx_rollup_rejection_result _) | Successful_manager_result (Sc_rollup_originate_result _) | Successful_manager_result (Sc_rollup_add_messages_result _) -> origination_results_rev diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.ml b/src/proto_alpha/lib_protocol/test/helpers/op.ml index c01cb68c8b852ec1b1d29435b98204894355311a..36a21b6eeb1aee03631bebbf0f1998680dec2d43 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/op.ml @@ -553,3 +553,33 @@ let tx_rollup_commit ?counter ?fee ?gas_limit ?storage_limit ctxt >>=? fun to_sign_op -> Context.Contract.manager ctxt source >|=? fun account -> sign account.sk ctxt to_sign_op + +let tx_rollup_return_bond ?counter ?fee ?gas_limit ?storage_limit ctxt + (source : Contract.t) (tx_rollup : Tx_rollup.t) = + manager_operation + ?counter + ?fee + ?gas_limit + ?storage_limit + ~source + ctxt + (Tx_rollup_return_bond {tx_rollup}) + >>=? fun to_sign_op -> + Context.Contract.manager ctxt source >|=? fun account -> + sign account.sk ctxt to_sign_op + +let tx_rollup_reject ?counter ?fee ?gas_limit ?storage_limit ctxt + (source : Contract.t) (rollup : Tx_rollup.t) (level : Raw_level.t) + (hash : Tx_rollup_commitments.Commitment_hash.t) (batch_index : int) + (batch : Tx_rollup_message.t) (nonce : int64) = + manager_operation + ?counter + ?fee + ?gas_limit + ?storage_limit + ~source + ctxt + (Tx_rollup_rejection {rollup; level; hash; batch_index; batch; nonce}) + >>=? fun to_sign_op -> + Context.Contract.manager ctxt source >|=? fun account -> + sign account.sk ctxt to_sign_op diff --git a/src/proto_alpha/lib_protocol/test/helpers/op.mli b/src/proto_alpha/lib_protocol/test/helpers/op.mli index 6902adbea622977ec51921b42158d9baead15365..94813029ddfef744f86d98047017da5f506c8a1c 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/op.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/op.mli @@ -243,3 +243,31 @@ val tx_rollup_commit : Tx_rollup.t -> Tx_rollup_commitments.Commitment.t -> Operation.packed tzresult Lwt.t + +(** [tx_rollup_return_bond ctxt source tx_rollup] returns a commitment bond. *) +val tx_rollup_return_bond : + ?counter:Z.t -> + ?fee:Tez.tez -> + ?gas_limit:Gas.Arith.integral -> + ?storage_limit:Z.t -> + Context.t -> + Contract.t -> + Tx_rollup.t -> + Operation.packed tzresult Lwt.t + +(** [tx_rollup_reject ctxt source tx_rollup level hash batch_index batch nonce] + rejects a commitment. *) +val tx_rollup_reject : + ?counter:counter -> + ?fee:Tez.t -> + ?gas_limit:Gas.Arith.integral -> + ?storage_limit:counter -> + Context.t -> + Contract.t -> + Tx_rollup.t -> + Raw_level.t -> + Tx_rollup_commitments.Commitment_hash.t -> + int -> + Tx_rollup_message.t -> + int64 -> + Operation.packed tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/test/integration/main.ml b/src/proto_alpha/lib_protocol/test/integration/main.ml index ccb4fa8d5bec1fdae2ab4b279ca2af4f0a956055..30434853a27a60dde34b372a6f168f794dca4294 100644 --- a/src/proto_alpha/lib_protocol/test/integration/main.ml +++ b/src/proto_alpha/lib_protocol/test/integration/main.ml @@ -39,5 +39,6 @@ let () = ("storage description", Test_storage.tests); ("storage tests", Test_storage_functions.tests); ("token movements", Test_token.tests); + ("rollup deposits", Test_rollup_deposits.tests); ] |> Lwt_main.run diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml index 20efc0bc54adc1824ad30c629bbeaad53c0b1318..2185669e558b68c6ff8db48f550d5730df73ec92 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2021 Marigold *) (* Copyright (c) 2021 Nomadic Labs *) +(* Copyright (c) 2022 Oxhead Alpha *) (* *) (* 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,85 @@ let public_key_hash_exn contract = | None -> assert false | Some public_key_hash -> public_key_hash +let check_bond ctxt tx_rollup contract count = + let pkh = public_key_hash_exn contract in + wrap (Tx_rollup_commitments.pending_bonded_commitments ctxt tx_rollup pkh) + >>=? fun (_, pending) -> + Alcotest.(check int "Pending commitment count correct" count pending) ; + return () + +let rec bake_until i top = + let level = Incremental.level i in + if level >= top then return i + else + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> bake_until i top + +let assert_retired retired = + match retired with + | `Retired -> return_unit + | _ -> failwith "Expected retired" + +(* Make a valid commitment for a batch. TODO/TORU: roots are still + wrong, of course, until we get Merkle proofs*) +let make_commitment_for_batch i level tx_rollup = + let ctxt = Incremental.alpha_ctxt i in + wrap + (Alpha_context.Tx_rollup_inbox.Internal_for_tests.get_metadata + ctxt + level + tx_rollup) + >>=? fun (ctxt, metadata) -> + Lwt.return + @@ List.init ~when_negative_length:[] metadata.count (fun i -> + let batch : Tx_rollup_commitments.Commitment.batch_commitment = + {root = Bytes.make 20 (Char.chr i)} + in + batch) + >>=? fun batches -> + (match metadata.predecessor with + | None -> return_none + | Some predecessor_level -> ( + wrap + (Lwt.return + @@ Raw_level.of_int32 (Raw_level_repr.to_int32 predecessor_level)) + >>=? fun predecessor_level -> + wrap + (Tx_rollup_commitments.get_commitments ctxt tx_rollup predecessor_level) + >|=? function + | (_, []) -> None + | (_, hd :: _) -> Some hd.hash)) + >>=? fun predecessor -> + let commitment : Tx_rollup_commitments.Commitment.t = + {level; batches; predecessor; inbox_hash = metadata.hash} + in + return commitment + +let constants = + { + Tezos_protocol_alpha_parameters.Default_parameters.constants_test with + consensus_threshold = 0; + endorsing_reward_per_slot = Tez.zero; + baking_reward_bonus_per_slot = Tez.zero; + baking_reward_fixed_portion = Tez.zero; + tx_rollup_enable = true; + } + +let originate_with_constants constants n = + Context.init_with_constants constants n >>=? fun (b, contracts) -> + let contract = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + originate b contract >>=? fun (b, tx_rollup) -> + return (b, tx_rollup, contracts) + +let range start top = + let rec aux n acc = if n < start then acc else aux (n - 1) (n :: acc) in + aux top [] + +let message txt = + fst @@ Tx_rollup_message.make_batch txt + (** ---- TESTS -------------------------------------------------------------- *) (** [test_origination] originates a transaction rollup and checks that @@ -590,23 +670,10 @@ let test_commitment_duplication () = let contents = "batch" in Op.tx_rollup_submit_batch (B b) contract1 tx_rollup contents >>=? fun operation -> + let level = raw_level 2l in Block.bake ~operation b >>=? fun b -> Incremental.begin_construction b >>=? fun i -> - let level_opt = - Raw_level.pred (Level.current (Incremental.alpha_ctxt i)).level - in - (* Need an extra block here to ensure finality *) - Incremental.finalize_block i >>=? fun b -> - Incremental.begin_construction b >>=? fun i -> - let level = - match level_opt with None -> assert false | Some level -> level - in - let batches : Tx_rollup_commitments.Commitment.batch_commitment list = - [{root = Bytes.make 20 '0'}] - in - let commitment : Tx_rollup_commitments.Commitment.t = - {level; batches; predecessor = None} - in + make_commitment_for_batch i level tx_rollup >>=? fun commitment -> let submitted_level = (Level.current (Incremental.alpha_ctxt i)).level in Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> Incremental.add_operation i op >>=? fun i -> @@ -626,9 +693,7 @@ let test_commitment_duplication () = let batches2 : Tx_rollup_commitments.Commitment.batch_commitment list = [{root = Bytes.make 20 '1'}] in - let commitment2 : Tx_rollup_commitments.Commitment.t = - {level; batches = batches2; predecessor = None} - in + let commitment2 = {commitment with batches = batches2} in (* Successfully fail to submit a different commitment from contract1 *) Op.tx_rollup_commit (I i) contract1 tx_rollup commitment2 >>=? fun op -> Incremental.add_operation i op ~expect_failure:(function @@ -643,7 +708,7 @@ let test_commitment_duplication () = [{root = Bytes.make 20 '1'}; {root = Bytes.make 20 '2'}] in let commitment3 : Tx_rollup_commitments.Commitment.t = - {level; batches = batches3; predecessor = None} + {commitment2 with batches = batches3} in (* Successfully fail to submit a different commitment from contract2 *) Op.tx_rollup_commit (I i) contract2 tx_rollup commitment3 >>=? fun op -> @@ -660,7 +725,7 @@ let test_commitment_duplication () = >>=? fun () -> let ctxt = Incremental.alpha_ctxt i in wrap (Tx_rollup_commitments.get_commitments ctxt tx_rollup level) - >>=? fun (_, commitments) -> + >>=? fun (ctxt, commitments) -> (Alcotest.(check int "Expected one commitment" 1 (List.length commitments)) ; let expected_hash = Tx_rollup_commitments.Commitment.hash commitment in match List.nth commitments 0 with @@ -675,6 +740,8 @@ let test_commitment_duplication () = check raw_level_testable "Submitted" submitted_level submitted_at) ; return ()) >>=? fun () -> + check_bond ctxt tx_rollup contract1 1 >>=? fun () -> + check_bond ctxt tx_rollup contract2 0 >>=? fun () -> ignore i ; return () @@ -707,16 +774,14 @@ let test_commitment_predecessor () = (* Transactions in blocks 2, 3, 6 *) make_transactions_in tx_rollup contract1 [2; 3; 6] b >>=? fun b -> Incremental.begin_construction b >>=? fun i -> - (* Check error: Commitment with predecessor for first block *) - let batches : Tx_rollup_commitments.Commitment.batch_commitment list = - [{root = Bytes.make 20 '0'}] - in + (* Check error: Commitment for nonexistent block *) let some_hash = Tx_rollup_commitments.Commitment_hash.of_bytes_exn (Bytes.of_string "tcu1deadbeefdeadbeefdeadbeefdead") in - let commitment : Tx_rollup_commitments.Commitment.t = - {level = raw_level 1l; batches; predecessor = Some some_hash} + make_commitment_for_batch i (raw_level 2l) tx_rollup >>=? fun commitment -> + let commitment = + {commitment with level = raw_level 1l; predecessor = Some some_hash} in Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> let error = @@ -729,9 +794,8 @@ let test_commitment_predecessor () = | _ -> failwith "Need to check commitment predecessor") >>=? fun i -> (* Commitment without predecessor for block with predecessor*) - let commitment : Tx_rollup_commitments.Commitment.t = - {level = raw_level 3l; batches; predecessor = None} - in + make_commitment_for_batch i (raw_level 3l) tx_rollup >>=? fun commitment -> + let commitment = {commitment with predecessor = None} in Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> Incremental.add_operation i op ~expect_failure:(function | Environment.Ecoproto_error @@ -742,9 +806,8 @@ let test_commitment_predecessor () = | _ -> failwith "Need to check commitment predecessor") >>=? fun i -> (* Commitment refers to a predecessor which does not exist *) - let commitment : Tx_rollup_commitments.Commitment.t = - {level = raw_level 3l; batches; predecessor = Some some_hash} - in + make_commitment_for_batch i (raw_level 3l) tx_rollup >>=? fun commitment -> + let commitment = {commitment with predecessor = Some some_hash} in Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> Incremental.add_operation i op ~expect_failure:(function | Environment.Ecoproto_error @@ -756,7 +819,7 @@ let test_commitment_predecessor () = >>=? fun i -> (* Try to commit to an empty level between full ones *) let commitment : Tx_rollup_commitments.Commitment.t = - {level = raw_level 5l; batches; predecessor = Some some_hash} + {commitment with level = raw_level 5l} in Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> let error = @@ -771,6 +834,455 @@ let test_commitment_predecessor () = ignore i ; return () +(** [test_commitment_retire_simple] tests commitment retirement simple cases. + Note that it manually retires commitments rather than waiting for them to + age out. *) +let test_commitment_retire_simple () = + context_init 1 >>=? fun (b, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + originate b contract1 >>=? fun (b, tx_rollup) -> + (* In order to have a permissible commitment, we need a transaction. *) + let contents = "batch" in + Op.tx_rollup_submit_batch (B b) contract1 tx_rollup contents + >>=? fun operation -> + Block.bake ~operation b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + let level = raw_level 2l in + (* Test retirement with no commitment *) + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + (Incremental.alpha_ctxt i) + tx_rollup + level + (raw_level @@ Incremental.level i)) + >>=? fun (_ctxt, retired) -> + (match retired with + | `No_commitment -> return_unit + | _ -> failwith "Expected no commitment") + >>=? fun () -> + make_commitment_for_batch i level tx_rollup >>=? fun commitment -> + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let commitment_submit_level = + (Level.current (Incremental.alpha_ctxt i)).level + in + check_bond (Incremental.alpha_ctxt i) tx_rollup contract1 1 >>=? fun () -> + (* We can retire this level *) + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + (Incremental.alpha_ctxt i) + tx_rollup + level + commitment_submit_level) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + check_bond ctxt tx_rollup contract1 0 >>=? fun () -> + ignore i ; + return () + +(** [test_commitment_retire_complex] tests a complicated commitment + retirement scenario: + + We have inboxes at 2, 3, and 6. + + - A: Contract 1 commits to 2. + - B: Contract 2 commits to 2 (after A; this commitment is + necessarily bogus, but we will assume that nobody notices) + - C: Contract 2 commits to 3 (atop A). + - D: Contract 1 commits to 3 (atop bogus commit B) + - E: Contract 2 commits to 6 (atop D). + - F: Contract 1 commits to 6 (atop C). + + So now we retire 2. We want nobody to get a bond back, but D and + E are gone. Then we retire 3, which will enable 2 to get its bond + back. Then we retire 6, which lets Contract 1 get its bond back. +*) +let test_commitment_retire_complex () = + context_init 2 >>=? fun (b, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + let contract2 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 1 + in + originate b contract1 >>=? fun (b, tx_rollup) -> + (* Transactions in blocks 2, 3, 6 *) + make_transactions_in tx_rollup contract1 [2; 3; 6] b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + let batches : Tx_rollup_commitments.Commitment.batch_commitment list = + [{root = Bytes.make 20 '5'}] + in + make_commitment_for_batch i (raw_level 2l) tx_rollup >>=? fun commitment_a -> + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_a >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let commitment_b = {commitment_a with batches} in + Op.tx_rollup_commit (I i) contract2 tx_rollup commitment_b >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + make_commitment_for_batch i (raw_level 3l) tx_rollup >>=? fun commitment_c -> + Op.tx_rollup_commit (I i) contract2 tx_rollup commitment_c >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let predecessor = Tx_rollup_commitments.Commitment.hash commitment_b in + + let commitment_d : Tx_rollup_commitments.Commitment.t = + {commitment_c with predecessor = Some predecessor} + in + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_d >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + (* Need to bake to avoid running out of gas *) + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + let predecessor = Tx_rollup_commitments.Commitment.hash commitment_d in + make_commitment_for_batch i (raw_level 6l) tx_rollup >>=? fun commitment_e -> + let commitment_e : Tx_rollup_commitments.Commitment.t = + {commitment_e with predecessor = Some predecessor} + in + Op.tx_rollup_commit (I i) contract2 tx_rollup commitment_e >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let predecessor = Tx_rollup_commitments.Commitment.hash commitment_c in + make_commitment_for_batch i (raw_level 6l) tx_rollup >>=? fun commitment_f -> + let commitment_f : Tx_rollup_commitments.Commitment.t = + {commitment_f with predecessor = Some predecessor} + in + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_f >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + (Incremental.alpha_ctxt i) + tx_rollup + (raw_level 2l) + (raw_level 10l)) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + check_bond ctxt tx_rollup contract1 1 >>=? fun () -> + check_bond ctxt tx_rollup contract2 1 >>=? fun () -> + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + ctxt + tx_rollup + (raw_level 3l) + (raw_level 10l)) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + check_bond ctxt tx_rollup contract1 1 >>=? fun () -> + check_bond ctxt tx_rollup contract2 0 >>=? fun () -> + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + ctxt + tx_rollup + (raw_level 6l) + (raw_level 10l)) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + check_bond ctxt tx_rollup contract1 0 >>=? fun () -> + check_bond ctxt tx_rollup contract2 0 >>=? fun () -> + ignore ctxt ; + ignore i ; + return () + +(** [test_commitment_acceptance] tests a case where there are multiple + nonrejected commitments at finalization time. +- A: Contract 1 commits to 2 (this will be accepted) +- B: Contract 2 commits to 2 (this will removed but not rejected) +- C: Contract 2 commits to 3 (atop A). +- D: Contract 3 commits to 3 (atop B, to be removed) +*) +let test_commitment_acceptance () = + context_init 3 >>=? fun (b, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + let contract2 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 1 + in + let contract3 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 2 + in + originate b contract1 >>=? fun (b, tx_rollup) -> + make_transactions_in tx_rollup contract1 [2; 3] b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + make_commitment_for_batch i (raw_level 2l) tx_rollup >>=? fun commitment_a -> + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_a >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let batches : Tx_rollup_commitments.Commitment.batch_commitment list = + [{root = Bytes.make 20 '1'}] + in + let commitment_b : Tx_rollup_commitments.Commitment.t = + {commitment_a with batches} + in + Op.tx_rollup_commit (I i) contract2 tx_rollup commitment_b >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + make_commitment_for_batch i (raw_level 3l) tx_rollup >>=? fun commitment_c -> + Op.tx_rollup_commit (I i) contract2 tx_rollup commitment_c >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let predecessor = Tx_rollup_commitments.Commitment.hash commitment_b in + let commitment_d : Tx_rollup_commitments.Commitment.t = + {commitment_c with predecessor = Some predecessor} + in + Op.tx_rollup_commit (I i) contract3 tx_rollup commitment_d >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let commitment_submit_level = + (Level.current (Incremental.alpha_ctxt i)).level + in + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + (Incremental.alpha_ctxt i) + tx_rollup + (raw_level 2l) + commitment_submit_level) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + ctxt + tx_rollup + (raw_level 3l) + commitment_submit_level) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + check_bond ctxt tx_rollup contract1 0 >>=? fun () -> + check_bond ctxt tx_rollup contract2 0 >>=? fun () -> + check_bond ctxt tx_rollup contract3 0 >>=? fun () -> + ignore ctxt ; + ignore i ; + return () + +(** [test_bond_finalization] tests that commitment retirement + in fact finalizes bonds. *) +let test_bond_finalization () = + context_init 2 >>=? fun (b, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + let pkh1 = public_key_hash_exn contract1 in + originate b contract1 >>=? fun (b, tx_rollup) -> + (* Transactions in block 2, 3, 4 *) + make_transactions_in tx_rollup contract1 [2; 3; 4] b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + Op.tx_rollup_return_bond (I i) contract1 tx_rollup >>=? fun op -> + Incremental.add_operation i op ~expect_failure:(function + | Environment.Ecoproto_error + (Tx_rollup_commitments.Bond_does_not_exist a_pkh1 as e) + :: _ + when a_pkh1 = pkh1 -> + Assert.test_error_encodings e ; + return_unit + | _ -> failwith "Commitment bond should not exist yet") + >>=? fun i -> + make_commitment_for_batch i (raw_level 2l) tx_rollup >>=? fun commitment_a -> + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment_a >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + Op.tx_rollup_return_bond (I i) contract1 tx_rollup >>=? fun op -> + Incremental.add_operation i op ~expect_failure:(function + | Environment.Ecoproto_error + (Tx_rollup_commitments.Bond_in_use a_pkh1 as e) + :: _ + when a_pkh1 = pkh1 -> + Assert.test_error_encodings e ; + return_unit + | _ -> failwith "Need to check that bond is in-use ") + >>=? fun i -> + wrap + (Tx_rollup_commitments.Internal_for_tests.retire_rollup_level + (Incremental.alpha_ctxt i) + tx_rollup + (raw_level 2l) + (raw_level 30l)) + >>=? fun (ctxt, retired) -> + assert_retired retired >>=? fun () -> + let i = Incremental.set_alpha_ctxt i ctxt in + Op.tx_rollup_return_bond (I i) contract1 tx_rollup >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + ignore i ; + return () + +(** [test_bond_finalization] tests that commitment operations + perform retirement. *) +let test_commitment_finalizes () = + let constants = {constants with tx_rollup_finality_period = 10} in + originate_with_constants constants 3 >>=? fun (b, tx_rollup, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + let contract2 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 1 + in + let contract3 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 2 + in + (* Transactions in block 2, 3, 4 *) + make_transactions_in tx_rollup contract1 [2; 3; 4] b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + make_commitment_for_batch i (raw_level 2l) tx_rollup >>=? fun commitment -> + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + (* Now wait for the most of the finality period to pass. *) + bake_until i 13l >>=? fun i -> + make_commitment_for_batch i (raw_level 3l) tx_rollup >>=? fun commitment -> + Op.tx_rollup_commit (I i) contract2 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + (* We check that the commitment for level 2 was not finalized by + checking the bond for contract1. *) + check_bond (Incremental.alpha_ctxt i) tx_rollup contract1 1 >>=? fun () -> + (* Wait one and try again. *) + bake_until i 14l >>=? fun i -> + make_commitment_for_batch i (raw_level 4l) tx_rollup >>=? fun commitment -> + Op.tx_rollup_commit (I i) contract3 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + check_bond (Incremental.alpha_ctxt i) tx_rollup contract1 0 >>=? fun () -> + (* Check that we don't finalize too far. *) + check_bond (Incremental.alpha_ctxt i) tx_rollup contract2 1 >>=? fun () -> + ignore i ; + return () + +(** [test_bond_finalization] tests that commitment operations + do not finalize more than the limit *) +let test_commitment_finality_limit () = + let constants = + { + constants with + tx_rollup_finality_period = 15; + tx_rollup_max_finalize_levels_per_commitment = 3; + hard_gas_limit_per_block = Gas.Arith.integral_exn (Z.of_int 10000000000); + } + in + originate_with_constants constants 10 >>=? fun (b, tx_rollup, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + let contract10 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 9 + in + let check_bond_for_contract i contract_n expected_bond = + let contract = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts contract_n + in + check_bond (Incremental.alpha_ctxt i) tx_rollup contract expected_bond + in + + (* Transactions in block 2-11 *) + make_transactions_in tx_rollup contract1 (range 2 12) b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + let rec make_many_commitments i cur top = + if cur = top then return i + else + make_commitment_for_batch i (raw_level (Int32.of_int cur)) tx_rollup + >>=? fun commitment -> + let contract = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts cur + in + Op.tx_rollup_commit (I i) contract tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + make_many_commitments i (cur + 1) top + in + make_many_commitments i 2 10 >>=? fun i -> + (* Now wait for the the rest of the finality period to pass. *) + bake_until i 30l >>=? fun i -> + make_commitment_for_batch i (raw_level 10l) tx_rollup >>=? fun commitment -> + Op.tx_rollup_commit (I i) contract10 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + check_bond_for_contract i 2 0 >>=? fun () -> + check_bond_for_contract i 3 0 >>=? fun () -> + check_bond_for_contract i 4 0 >>=? fun () -> + (* We hit the limit, so the next one is not final. *) + check_bond_for_contract i 5 1 >>=? fun () -> + make_commitment_for_batch i (raw_level 11l) tx_rollup >>=? fun commitment -> + Op.tx_rollup_commit (I i) contract10 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + check_bond_for_contract i 5 0 >>=? fun () -> + check_bond_for_contract i 6 0 >>=? fun () -> + check_bond_for_contract i 7 0 >>=? fun () -> + check_bond_for_contract i 8 1 >>=? fun () -> + ignore i ; + return () + +(** [test_rejection] tests that rejection works. *) +let test_rejection () = + context_init 1 >>=? fun (b, contracts) -> + let contract1 = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + originate b contract1 >>=? fun (b, tx_rollup) -> + (* Transactions in block 2 *) + make_transactions_in tx_rollup contract1 [2] b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + let batches : Tx_rollup_commitments.Commitment.batch_commitment list = + [{root = Bytes.empty}] + in + (* "Random" numbers *) + let nonce = 1000L in + let nonce2 = 1001L in + make_commitment_for_batch i (raw_level 2l) tx_rollup >>=? fun commitment -> + let commitment = {commitment with batches} in + Op.tx_rollup_commit (I i) contract1 tx_rollup commitment >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + let hash = Tx_rollup_commitments.Commitment.hash commitment in + (* Correct rejection *) + Op.tx_rollup_reject + (I i) + contract1 + tx_rollup + (raw_level 2l) + hash + 0 + (message "batch") + nonce + >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + (* Right commitment *) + make_commitment_for_batch i (raw_level 2l) tx_rollup + >>=? fun correct_commitment -> + Op.tx_rollup_commit (I i) contract1 tx_rollup correct_commitment + >>=? fun op -> + Incremental.add_operation i op >>=? fun i -> + Incremental.finalize_block i >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + let hash = Tx_rollup_commitments.Commitment.hash correct_commitment in + Op.tx_rollup_reject + (I i) + contract1 + tx_rollup + (raw_level 2l) + hash + 0 + (message "batch") + nonce2 + >>=? fun op -> + (* Wrong rejection *) + Incremental.add_operation i op ~expect_failure:(function + | Environment.Ecoproto_error (Tx_rollup_rejection.Wrong_rejection as e) + :: _ -> + Assert.test_error_encodings e ; + return_unit + | _ -> failwith "Should not reject correct commitments") + >>=? fun i -> + ignore i ; + return () + +let test_full_inbox () = + let constants = {constants with tx_rollup_max_unfinalized_levels = 15} in + originate_with_constants constants 1 >>=? fun (b, tx_rollup, contracts) -> + let contract = + WithExceptions.Option.get ~loc:__LOC__ @@ List.nth contracts 0 + in + (* Transactions in blocks [2..17) *) + make_transactions_in tx_rollup contract (range 2 17) b >>=? fun b -> + Incremental.begin_construction b >>=? fun i -> + Op.tx_rollup_submit_batch (B b) contract tx_rollup "contents" >>=? fun op -> + Incremental.add_operation i op ~expect_failure:(function + | Environment.Ecoproto_error + (Tx_rollup_commitments.Too_many_unfinalized_levels as e) + :: _ -> + Assert.test_error_encodings e ; + return_unit + | _ -> failwith "Need to avoid too many unfinalized inboxes") + >>=? fun i -> + ignore i ; + return () + let tests = [ Tztest.tztest @@ -804,4 +1316,27 @@ let tests = "Test commitment predecessor edge cases" `Quick test_commitment_predecessor; + Tztest.tztest + "Test commitment retirement" + `Quick + test_commitment_retire_simple; + Tztest.tztest + "Test complex commitment retirement" + `Quick + test_commitment_retire_complex; + Tztest.tztest + "Test multiple nonrejected commitment" + `Quick + test_commitment_acceptance; + Tztest.tztest "Test bond finalization" `Quick test_bond_finalization; + Tztest.tztest "Test rejection" `Quick test_rejection; + Tztest.tztest "Test full inbox" `Quick test_full_inbox; + Tztest.tztest + "Test that commitment finalizes" + `Quick + test_commitment_finalizes; + Tztest.tztest + "Test that commitment finality limit" + `Quick + test_commitment_finality_limit; ] diff --git a/src/proto_alpha/lib_protocol/test/integration/test_rollup_deposits.ml b/src/proto_alpha/lib_protocol/test/integration/test_rollup_deposits.ml new file mode 100644 index 0000000000000000000000000000000000000000..33e17811859b5483a4b60017c17463209cfde019 --- /dev/null +++ b/src/proto_alpha/lib_protocol/test/integration/test_rollup_deposits.ml @@ -0,0 +1,313 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 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. *) +(* *) +(*****************************************************************************) + +(** Testing + ------- + Component: Protocol (token) + Invocation: dune exec \ + src/proto_alpha/lib_protocol/test/integration/main.exe \ + -- test "^rollup deposits" + Subject: Frozen rollup deposits. +*) + +open Protocol +open Alpha_context +open Test_tez + +let ( >>>=? ) x f = x >|= Environment.wrap_tzresult >>=? f + +let big_random_amount () = + match Tez.of_mutez (Int64.add 1L (Random.int64 10_000L)) with + | None -> assert false + | Some x -> x + +let small_random_amount () = + match Tez.of_mutez (Int64.add 1L (Random.int64 1_000L)) with + | None -> assert false + | Some x -> x + +let very_small_random_amount () = + match Tez.of_mutez (Int64.add 1L (Random.int64 100L)) with + | None -> assert false + | Some x -> x + +let nonce_zero = + Origination_nonce.Internal_for_tests.initial Operation_hash.zero + +let mk_tx_rollup ?(nonce = nonce_zero) () = + ( Tx_rollup.Internal_for_tests.originated_tx_rollup nonce, + Origination_nonce.Internal_for_tests.incr nonce ) + +(** Creates a context with a single account. Returns the context and the public + key hash of the account. *) +let create_context () = + let accounts = Account.generate_accounts 1 in + Block.alpha_context accounts >>=? fun ctxt -> + match accounts with + | [({pkh; _}, _)] -> return (ctxt, pkh) + | _ -> (* Exactly one account has been generated. *) assert false + +(** Creates a context, a user contract, and a delegate. + Returns the context, the user contract, the user account, and the + delegate's pkh. *) +let init_test ~user_is_delegate = + create_context () >>=? fun (ctxt, _) -> + let (delegate, delegate_pk, _) = Signature.generate_key () in + let delegate_contract = Contract.implicit_contract delegate in + let delegate_account = `Contract (Contract.implicit_contract delegate) in + let user_contract = + if user_is_delegate then delegate_contract + else + let (user, _, _) = Signature.generate_key () in + Contract.implicit_contract user + in + let user_account = `Contract user_contract in + (* Allocate contracts for user and delegate. *) + let user_balance = big_random_amount () in + Token.transfer ctxt `Minted user_account user_balance >>>=? fun (ctxt, _) -> + let delegate_balance = big_random_amount () in + Token.transfer ctxt `Minted delegate_account delegate_balance + >>>=? fun (ctxt, _) -> + (* 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 -> + return (ctxt, user_contract, user_account, delegate) + +(** Tested scenario : + 1. user contract delegates to 'delegate', + 2. freeze a rollup deposit, + 3. check that staking balance of delegate has not changed, + 4. remove delegation, + 5. check staking balance decreased accordingly, + 6. unfreeze part of the rollup deposit, + 7. check that staking balance is unchanged, + 8. check that user's balance decreased accordingly. *) +let test_delegate_then_freeze_tx_rollup_deposit () = + init_test ~user_is_delegate:false + >>=? fun (ctxt, user_contract, user_account, delegate) -> + (* Fetch user's initial balance before freeze. *) + Token.balance ctxt user_account >>>=? fun user_balance -> + (* Let user delegate to "delegate". *) + 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. *) + let (tx_rollup, _) = mk_tx_rollup () in + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let deposit_amount = small_random_amount () in + let deposit_account = `Frozen_rollup_bonds (user_contract, bond_id) in + Token.transfer ctxt user_account deposit_account deposit_amount + >>>=? fun (ctxt, _) -> + (* Fetch staking balance after freeze. *) + Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> + (* 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 -> + (* Fetch staking balance after delegation removal. *) + Delegate.staking_balance ctxt delegate >>>=? fun staking_balance'' -> + (* Ensure staking balance decreased by user's initial balance. *) + Assert.equal_tez + ~loc:__LOC__ + staking_balance'' + (staking_balance' -! user_balance) + >>=? fun () -> + (* Unfreeze the deposit. *) + Token.transfer ctxt deposit_account user_account deposit_amount + >>>=? fun (ctxt, _) -> + (* Fetch staking balance of delegate. *) + Delegate.staking_balance ctxt delegate >>>=? fun staking_balance''' -> + (* Ensure that staking balance is unchanged. *) + Assert.equal_tez ~loc:__LOC__ staking_balance''' staking_balance'' + >>=? fun () -> + (* Fetch user's balance again. *) + Token.balance ctxt user_account >>>=? fun user_balance' -> + (* Ensure user's balance decreased. *) + Assert.equal_tez ~loc:__LOC__ user_balance' user_balance + +(** Tested scenario: + 1. freeze a rollup deposit, + 2. user contract delegate to 'delegate', + 3. check that staking balance of delegate has increased as expected, + 4. unfreeze part of the rollup deposit, + 5. check that staking balance has not changed, + 6. remove delegation, + 7. check that staking balance has decreased as expected, + 8. check the the user's balance decreased accordingly. *) +let test_freeze_tx_rollup_deposit_then_delegate () = + init_test ~user_is_delegate:false + >>=? fun (ctxt, user_contract, user_account, delegate) -> + (* Fetch user's initial balance before freeze. *) + Token.balance ctxt user_account >>>=? fun user_balance -> + (* Freeze a tx-rollup deposit. *) + let (tx_rollup, _) = mk_tx_rollup () in + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let deposit_amount = small_random_amount () in + let deposit_account = `Frozen_rollup_bonds (user_contract, bond_id) in + Token.transfer ctxt user_account deposit_account deposit_amount + >>>=? fun (ctxt, _) -> + (* Here, user balance has decreased. + 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 -> + (* Fetch staking balance after delegation. *) + Delegate.staking_balance ctxt delegate >>>=? fun staking_balance' -> + (* ensure staking balance increased by the user's balance. *) + Assert.equal_tez + ~loc:__LOC__ + staking_balance' + (user_balance +! staking_balance) + >>=? fun () -> + (* Unfreeze the deposit. *) + Token.transfer ctxt deposit_account user_account deposit_amount + >>>=? fun (ctxt, _) -> + (* Fetch staking balance after unfreeze. *) + Delegate.staking_balance ctxt delegate >>>=? fun staking_balance'' -> + (* Ensure that staking balance is unchanged. *) + Assert.equal_tez ~loc:__LOC__ staking_balance'' staking_balance' + >>=? fun () -> + (* Remove delegation. *) + 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. *) + Assert.equal_tez + ~loc:__LOC__ + staking_balance''' + (staking_balance'' -! user_balance) + >>=? fun () -> + (* Fetch user's balance. *) + Token.balance ctxt user_account >>>=? fun user_balance' -> + (* Ensure user's balance decreased. *) + Assert.equal_tez ~loc:__LOC__ user_balance' user_balance + +(** Tested scenario: + 1. freeze a rollup deposit (with deposit amount = balance), + 2. check that the user contract is still allocated, + 3. punish the user contract, + 4. check that the user contract is unallocated, except if it's a delegate. *) +let test_allocated_when_frozen_deposits_exists ~user_is_delegate () = + init_test ~user_is_delegate + >>=? fun (ctxt, user_contract, user_account, _delegate) -> + (* Fetch user's initial balance before freeze. *) + Token.balance ctxt user_account >>>=? fun user_balance -> + Assert.equal_bool ~loc:__LOC__ Tez.(user_balance > zero) true >>=? fun () -> + (* Freeze a tx-rollup deposit. *) + let (tx_rollup, _) = mk_tx_rollup () in + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let deposit_amount = user_balance in + let deposit_account = `Frozen_rollup_bonds (user_contract, bond_id) in + Token.transfer ctxt user_account deposit_account deposit_amount + >>>=? fun (ctxt, _) -> + (* Check that user contract is still allocated, despite a null balance. *) + Token.balance ctxt user_account >>>=? fun balance -> + Assert.equal_tez ~loc:__LOC__ balance Tez.zero >>=? fun () -> + Token.allocated ctxt user_account >>>=? fun user_allocated -> + Token.allocated ctxt deposit_account >>>=? fun dep_allocated -> + Assert.equal_bool ~loc:__LOC__ (user_allocated && dep_allocated) true + >>=? fun () -> + (* Punish the user contract. *) + Token.transfer ctxt deposit_account `Burned deposit_amount + >>>=? fun (ctxt, _) -> + (* Check that user and deposit accounts have been unallocated. *) + Token.allocated ctxt user_account >>>=? fun user_allocated -> + Token.allocated ctxt deposit_account >>>=? fun dep_allocated -> + if user_is_delegate then + Assert.equal_bool ~loc:__LOC__ (user_allocated && not dep_allocated) true + else Assert.equal_bool ~loc:__LOC__ (user_allocated || dep_allocated) false + +(** Tested scenario: + 1. freeze two rollup deposits for the user contract, + 2. check that the stake of the user contract is balance + two deposits, + 3. punish for one of the deposits, + 4. check that the stake of the user contract balance + deposit, + 5. punish for the other deposit, + 6. check that the stake of the user contract is equal to balance. *) +let test_total_stake ~user_is_delegate () = + init_test ~user_is_delegate + >>=? fun (ctxt, user_contract, user_account, _delegate) -> + (* Fetch user's initial balance before freeze. *) + Token.balance ctxt user_account >>>=? fun user_balance -> + Assert.equal_bool ~loc:__LOC__ Tez.(user_balance > zero) true >>=? fun () -> + (* Freeze 2 tx-rollup deposits. *) + let (tx_rollup, nonce) = mk_tx_rollup () in + let bond_id1 = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let (tx_rollup, _) = mk_tx_rollup ~nonce () in + let bond_id2 = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let deposit_amount = small_random_amount () in + let deposit_account1 = `Frozen_rollup_bonds (user_contract, bond_id1) in + Token.transfer ctxt user_account deposit_account1 deposit_amount + >>>=? fun (ctxt, _) -> + let deposit_account2 = `Frozen_rollup_bonds (user_contract, bond_id2) in + Token.transfer ctxt user_account deposit_account2 deposit_amount + >>>=? fun (ctxt, _) -> + (* Check that the stake of user contract is balance + two deposits. *) + Contract.stake ctxt user_contract >>>=? fun stake -> + Token.balance ctxt user_account >>>=? fun balance -> + Assert.equal_tez ~loc:__LOC__ (stake -! balance) (deposit_amount *! 2L) + >>=? fun () -> + (* Punish for one deposit. *) + Token.transfer ctxt deposit_account2 `Burned deposit_amount + >>>=? fun (ctxt, _) -> + (* Check that stake of contract is balance + deposit. *) + Contract.stake ctxt user_contract >>>=? fun stake -> + Assert.equal_tez ~loc:__LOC__ (stake -! balance) deposit_amount >>=? fun () -> + (* Punish for the other deposit. *) + Token.transfer ctxt deposit_account1 `Burned deposit_amount + >>>=? fun (ctxt, _) -> + (* Check that stake of contract is equal to balance. *) + Contract.stake ctxt user_contract >>>=? fun stake -> + Assert.equal_tez ~loc:__LOC__ stake balance + +let tests = + Tztest. + [ + tztest + "rollup deposits - delegate then freeze" + `Quick + test_delegate_then_freeze_tx_rollup_deposit; + tztest + "rollup deposits - freeze then delegate" + `Quick + test_freeze_tx_rollup_deposit_then_delegate; + tztest + "rollup deposits - contract remains allocated, user is not a delegate" + `Quick + (test_allocated_when_frozen_deposits_exists ~user_is_delegate:false); + tztest + "rollup deposits - contract remains allocated, user is a delegate" + `Quick + (test_allocated_when_frozen_deposits_exists ~user_is_delegate:true); + tztest + "rollup deposits - total stake, user is not a delegate" + `Quick + (test_total_stake ~user_is_delegate:false); + tztest + "rollup deposits - total stake, user is a delegate" + `Quick + (test_total_stake ~user_is_delegate:true); + ] 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 add025766523eb28e06200c0c4ca3d1a4c0ef428..8e388d2f7eda8e1151d124276c00d8bb5760d818 100644 --- a/src/proto_alpha/lib_protocol/test/integration/test_token.ml +++ b/src/proto_alpha/lib_protocol/test/integration/test_token.ml @@ -52,6 +52,10 @@ let random_amount () = | None -> assert false | Some x -> x +let nonce = Origination_nonce.Internal_for_tests.initial Operation_hash.zero + +let mk_rollup () = Tx_rollup.Internal_for_tests.originated_tx_rollup nonce + (** Check balances for a simple transfer from [bootstrap] to new [Implicit]. *) let test_simple_balances () = Random.init 0 ; @@ -133,7 +137,12 @@ let test_allocated () = let dest = `Frozen_deposits pkh in test_allocated_and_still_allocated_when_empty ctxt dest false >>=? fun _ -> let dest = `Block_fees in - test_allocated_and_still_allocated_when_empty ctxt dest true + test_allocated_and_still_allocated_when_empty ctxt dest true >>=? fun _ -> + let dest = + let bond_id = Rollup_bond_id.Tx_rollup_bond_id (mk_rollup ()) in + `Frozen_rollup_bonds (Contract.implicit_contract pkh, bond_id) + in + test_allocated_and_deallocated_when_empty ctxt dest let check_sink_balances ctxt ctxt' dest amount = wrap (Token.balance ctxt dest) >>=? fun bal_dest -> @@ -141,17 +150,23 @@ let check_sink_balances ctxt ctxt' dest amount = bal_dest +? amount >>?= fun add_bal_dest_amount -> Assert.equal_tez ~loc:__LOC__ bal_dest' add_bal_dest_amount +(* Accounts of the form (`DelegateBalance pkh) are not allocated when they + receive funds for the first time. To force allocation, we transfer to + (`Contract pkh) instead. *) +let force_allocation_if_need_be ctxt account = + match account with + | `Delegate_balance pkh -> + let account = `Contract (Contract.implicit_contract pkh) in + wrap (Token.transfer ctxt `Minted account Tez.one_mutez) >|=? fst + | _ -> return ctxt + let test_transferring_to_sink ctxt sink amount expected_bupds = - (* Transferring zero must not return balance updates. *) - (match sink with - | `Contract _ | `Delegate_balance _ -> - return_unit (* Transaction of 0tz is forbidden. *) - | _ -> - wrap (Token.transfer ctxt `Minted sink Tez.zero) - >>=? fun (ctxt', bupds) -> - check_sink_balances ctxt ctxt' sink Tez.zero >>=? fun _ -> - Assert.equal_bool ~loc:__LOC__ (bupds = []) true) + (* Transferring zero must be a noop, and must not return balance updates. *) + wrap (Token.transfer ctxt `Minted sink Tez.zero) >>=? fun (ctxt', bupds) -> + Assert.equal_bool ~loc:__LOC__ (ctxt == ctxt' && bupds = []) true >>=? fun _ -> + (* Force the allocation of [dest] if need be. *) + force_allocation_if_need_be ctxt sink >>=? fun ctxt -> (* Test transferring a non null amount. *) wrap (Token.transfer ctxt `Minted sink amount) >>=? fun (ctxt', bupds) -> check_sink_balances ctxt ctxt' sink amount >>=? fun _ -> @@ -160,7 +175,12 @@ let test_transferring_to_sink ctxt sink amount expected_bupds = in Alcotest.( check bool "Balance updates do not match." (bupds = expected_bupds) true) ; - return_unit + (* Test transferring to go beyond capacity. *) + wrap (Token.balance ctxt' sink) >>=? fun bal -> + let amount = Tez.of_mutez_exn Int64.max_int -! bal +! Tez.one_mutez in + wrap (Token.transfer ctxt' `Minted sink amount) >>= function + | Error _ -> return_unit + | Ok _ -> failwith "Transferring (Int64.max - balance + 1) should fail." let test_transferring_to_contract ctxt = let (pkh, _pk, _sk) = Signature.generate_key () in @@ -184,11 +204,6 @@ let test_transferring_to_collected_commitments ctxt = let test_transferring_to_delegate_balance ctxt = let (pkh, _pk, _sk) = Signature.generate_key () in let dest = Contract.implicit_contract pkh in - (* First we need to force the allocation of [dest]. *) - wrap (Token.transfer ctxt `Minted (`Contract dest) Tez.one) - >>=? fun (ctxt, _) -> - wrap (Token.allocated ctxt (`Delegate_balance pkh)) >>=? fun allocated -> - Assert.equal_bool ~loc:__LOC__ allocated true >>=? fun () -> let amount = random_amount () in test_transferring_to_sink ctxt @@ -253,6 +268,18 @@ let test_transferring_to_burned ctxt = ]) true +let test_transferring_to_frozen_tx_rollup_bonds ctxt = + let (pkh, _pk, _sk) = Signature.generate_key () in + let contract = Contract.implicit_contract pkh in + let tx_rollup = mk_rollup () in + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let amount = random_amount () in + test_transferring_to_sink + ctxt + (`Frozen_rollup_bonds (contract, bond_id)) + amount + [(Rollup_bonds (contract, bond_id), Credited amount, Block_application)] + let test_transferring_to_sink () = Random.init 0 ; create_context () >>=? fun (ctxt, _) -> @@ -261,7 +288,8 @@ let test_transferring_to_sink () = test_transferring_to_delegate_balance ctxt >>=? fun _ -> test_transferring_to_frozen_deposits ctxt >>=? fun _ -> test_transferring_to_collected_fees ctxt >>=? fun _ -> - test_transferring_to_burned ctxt + test_transferring_to_burned ctxt >>=? fun _ -> + test_transferring_to_frozen_tx_rollup_bonds ctxt let check_src_balances ctxt ctxt' src amount = wrap (Token.balance ctxt src) >>=? fun bal_src -> @@ -283,24 +311,52 @@ let test_transferring_from_unbounded_source ctxt src expected_bupds = Assert.equal_bool ~loc:__LOC__ (bupds = expected_bupds) true >>=? fun () -> return_unit +(* Returns the balance of [account] if [account] is allocated, and returns + [Tez.zero] otherwise. *) +let balance_no_fail ctxt account = + wrap (Token.allocated ctxt account) >>=? fun allocated -> + if allocated then wrap (Token.balance ctxt account) else return Tez.zero + let test_transferring_from_bounded_source ctxt src amount expected_bupds = - (* Transferring zero must not return balance updates. *) - (match src with - | `Contract _ | `Delegate_balance _ -> - return_unit (* Transaction of 0tz is forbidden. *) - | _ -> - wrap (Token.transfer ctxt src `Burned Tez.zero) >>=? fun (ctxt', bupds) -> - check_src_balances ctxt ctxt' src Tez.zero >>=? fun _ -> - Assert.equal_bool ~loc:__LOC__ (bupds = []) true) + balance_no_fail ctxt src >>=? fun balance -> + Assert.equal_tez ~loc:__LOC__ balance Tez.zero >>=? fun () -> + (* Test transferring from an empty account. *) + (wrap (Token.transfer ctxt src `Burned Tez.one) >>= function + | Error _ -> return_unit + | Ok _ -> failwith "Transferring from an empty account should fail.") + >>=? fun () -> + (* Transferring zero must be a noop, and must not return balance updates. *) + wrap (Token.transfer ctxt src `Burned Tez.zero) >>=? fun (ctxt', bupds) -> + Assert.equal_bool ~loc:__LOC__ (ctxt == ctxt' && bupds = []) true >>=? fun _ -> - (* Test transferring a non null amount. *) + (* Force the allocation of [dest] if need be. *) + force_allocation_if_need_be ctxt src >>=? fun ctxt -> + (* Test transferring everything. *) wrap (Token.transfer ctxt `Minted src amount) >>=? fun (ctxt, _) -> wrap (Token.transfer ctxt src `Burned amount) >>=? fun (ctxt', bupds) -> check_src_balances ctxt ctxt' src amount >>=? fun _ -> let expected_bupds = expected_bupds @ Receipt.[(Burned, Credited amount, Block_application)] in - Assert.equal_bool ~loc:__LOC__ (bupds = expected_bupds) true + Assert.equal_bool ~loc:__LOC__ (bupds = expected_bupds) true >>=? fun () -> + (* Test transferring a smaller amount. *) + wrap (Token.transfer ctxt `Minted src amount) >>=? fun (ctxt, _) -> + (match src with + | `Frozen_rollup_bonds _ -> ( + wrap (Token.transfer ctxt src `Burned amount) >>= function + | Ok _ -> + failwith "Partial withdrawals are forbidden for frozen rollup bonds." + | Error _ -> return_unit) + | _ -> + wrap (Token.transfer ctxt src `Burned amount) >>=? fun (ctxt', bupds) -> + check_src_balances ctxt ctxt' src amount >>=? fun _ -> + Assert.equal_bool ~loc:__LOC__ (bupds = expected_bupds) true) + >>=? fun () -> + (* Test transferring more than available. *) + wrap (Token.balance ctxt src) >>=? fun balance -> + wrap (Token.transfer ctxt src `Burned (balance +! Tez.one)) >>= function + | Error _ -> return_unit + | Ok _ -> failwith "Transferring more than available should fail." let test_transferring_from_contract ctxt = let (pkh, _pk, _sk) = Signature.generate_key () in @@ -325,9 +381,6 @@ let test_transferring_from_delegate_balance ctxt = let (pkh, _pk, _sk) = Signature.generate_key () in let amount = random_amount () in let src = Contract.implicit_contract pkh in - (* First we need to force the allocation of [dest]. *) - wrap (Token.transfer ctxt `Minted (`Contract src) Tez.one) - >>=? fun (ctxt, _) -> test_transferring_from_bounded_source ctxt (`Delegate_balance pkh) @@ -351,6 +404,18 @@ let test_transferring_from_collected_fees ctxt = amount [(Block_fees, Debited amount, Block_application)] +let test_transferring_from_frozen_tx_rollup_bonds ctxt = + let (pkh, _pk, _sk) = Signature.generate_key () in + let contract = Contract.implicit_contract pkh in + let tx_rollup = mk_rollup () in + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + let amount = random_amount () in + test_transferring_from_bounded_source + ctxt + (`Frozen_rollup_bonds (contract, bond_id)) + amount + [(Rollup_bonds (contract, bond_id), Debited amount, Block_application)] + let test_transferring_from_source () = Random.init 0 ; create_context () >>=? fun (ctxt, _) -> @@ -393,7 +458,8 @@ let test_transferring_from_source () = test_transferring_from_collected_commitments ctxt >>=? fun _ -> test_transferring_from_delegate_balance ctxt >>=? fun _ -> test_transferring_from_frozen_deposits ctxt >>=? fun _ -> - test_transferring_from_collected_fees ctxt + test_transferring_from_collected_fees ctxt >>=? fun _ -> + test_transferring_from_frozen_tx_rollup_bonds ctxt let cast_to_container_type x = match x with @@ -404,6 +470,7 @@ let cast_to_container_type x = | `Collected_commitments _ as x -> Some x | `Delegate_balance _ as x -> Some x | `Block_fees as x -> Some x + | `Frozen_rollup_bonds _ as x -> Some x (** Generates all combinations of constructors. *) let build_test_cases () = @@ -437,6 +504,12 @@ let build_test_cases () = >>=? fun ctxt -> wrap (Delegate.set ctxt (Contract.implicit_contract user1) (Some baker2)) >>=? fun ctxt -> + let tx_rollup1 = mk_rollup () in + let bond_id1 = Rollup_bond_id.Tx_rollup_bond_id tx_rollup1 in + let tx_rollup2 = mk_rollup () in + let bond_id2 = Rollup_bond_id.Tx_rollup_bond_id tx_rollup2 in + let user1ic = Contract.implicit_contract user1 in + let baker2ic = Contract.implicit_contract baker2 in let src_list = [ (`Invoice, random_amount ()); @@ -452,6 +525,8 @@ let build_test_cases () = (user2c, random_amount ()); (baker1c, random_amount ()); (baker2c, random_amount ()); + (`Frozen_rollup_bonds (user1ic, bond_id1), random_amount ()); + (`Frozen_rollup_bonds (baker2ic, bond_id2), random_amount ()); ] in let dest_list = @@ -464,6 +539,8 @@ let build_test_cases () = user2c; baker1c; baker2c; + `Frozen_rollup_bonds (user1ic, bond_id1); + `Frozen_rollup_bonds (baker2ic, bond_id2); `Burned; ] in diff --git a/src/proto_alpha/lib_protocol/test/unit/test_receipt.ml b/src/proto_alpha/lib_protocol/test/unit/test_receipt.ml index d7c290974366d7e2c063c47f92e049a787415455..5ece588a0ad59cdede28411e8a4acda50be00a83 100644 --- a/src/proto_alpha/lib_protocol/test/unit/test_receipt.ml +++ b/src/proto_alpha/lib_protocol/test/unit/test_receipt.ml @@ -86,6 +86,13 @@ let test_encodings () = test_encodings (Commitments Blinded_public_key_hash.zero) >>=? fun () -> test_encodings Bootstrap >>=? fun () -> test_encodings Invoice >>=? fun () -> - test_encodings Initial_commitments >>=? fun () -> test_encodings Minted + test_encodings Initial_commitments >>=? fun () -> + test_encodings Minted >>=? fun () -> + let nonce = + Origination_nonce.Internal_for_tests.initial Operation_hash.zero + in + let tx_rollup = Tx_rollup.Internal_for_tests.originated_tx_rollup nonce in + let bond_id = Rollup_bond_id.Tx_rollup_bond_id tx_rollup in + test_encodings (Rollup_bonds (Contract.implicit_contract pkh, bond_id)) let tests = Tztest.[tztest "receipt - encoding" `Quick test_encodings] diff --git a/src/proto_alpha/lib_protocol/ticket_operations_diff.ml b/src/proto_alpha/lib_protocol/ticket_operations_diff.ml index 555951a41882f907f3c780115535cd9266a5ec17..d5ebab8861bd6213d55af6f691fd6ff6dbe5430e 100644 --- a/src/proto_alpha/lib_protocol/ticket_operations_diff.ml +++ b/src/proto_alpha/lib_protocol/ticket_operations_diff.ml @@ -251,6 +251,8 @@ let tickets_of_operation ctxt | Tx_rollup_origination -> return (None, ctxt) | Tx_rollup_submit_batch _ -> return (None, ctxt) | Tx_rollup_commit _ -> return (None, ctxt) + | Tx_rollup_return_bond _ -> return (None, ctxt) + | Tx_rollup_rejection _ -> return (None, ctxt) | Sc_rollup_originate {kind = _; boot_sector = _} -> return (None, ctxt) | Sc_rollup_add_messages {rollup = _; messages = _} -> return (None, ctxt) diff --git a/src/proto_alpha/lib_protocol/token.ml b/src/proto_alpha/lib_protocol/token.ml index beefc1a06382055fb69db86707342c93e44068fe..6221c498c592331496feaada7dd147d5cee8a692 100644 --- a/src/proto_alpha/lib_protocol/token.ml +++ b/src/proto_alpha/lib_protocol/token.ml @@ -23,12 +23,16 @@ (* *) (*****************************************************************************) +type carb_container = + [`Frozen_rollup_bonds of Contract_repr.t * Rollup_bond_id_repr.t] + type container = [ `Contract of Contract_repr.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 - | `Block_fees ] + | `Block_fees + | carb_container ] type source = [ `Invoice @@ -50,6 +54,11 @@ type sink = | `Burned | container ] +let carb_allocated ctxt stored = + match stored with + | `Frozen_rollup_bonds (contract, bond_id) -> + Frozen_rollup_bonds_storage.allocated ctxt contract bond_id + let allocated ctxt stored = match stored with | `Contract contract -> Contract_storage.allocated ctxt contract @@ -61,6 +70,13 @@ let allocated ctxt stored = let contract = Contract_repr.implicit_contract delegate in Frozen_deposits_storage.allocated ctxt contract >|= ok | `Block_fees -> return_true + | `Frozen_rollup_bonds _ as account -> carb_allocated ctxt account >|=? snd + +let carb_balance ctxt stored = + match stored with + | `Frozen_rollup_bonds (contract, bond_id) -> + Frozen_rollup_bonds_storage.find ctxt contract bond_id + >|=? fun (ctxt, value) -> (ctxt, Option.value ~default:Tez_repr.zero value) let balance ctxt stored = match stored with @@ -76,6 +92,7 @@ let balance ctxt stored = | None -> Tez_repr.zero | Some frozen_deposits -> frozen_deposits.current_amount) | `Block_fees -> return (Raw_context.get_collected_fees ctxt) + | `Frozen_rollup_bonds _ as stored -> carb_balance ctxt stored >|=? snd let credit ctxt dest amount origin = let open Receipt_repr in @@ -110,7 +127,14 @@ let credit ctxt dest amount origin = >|=? fun ctxt -> (ctxt, Deposits delegate) | `Block_fees -> Raw_context.credit_collected_fees_only_call_from_token ctxt amount - >>?= fun ctxt -> return (ctxt, Block_fees)) + >>?= fun ctxt -> return (ctxt, Block_fees) + | `Frozen_rollup_bonds (contract, bond_id) -> + Frozen_rollup_bonds_storage.credit_only_call_from_token + ctxt + contract + bond_id + amount + >>=? fun ctxt -> return (ctxt, Rollup_bonds (contract, bond_id))) >|=? fun (ctxt, balance) -> (ctxt, (balance, Credited amount, origin)) let spend ctxt src amount origin = @@ -150,9 +174,34 @@ let spend ctxt src amount origin = >>=? fun ctxt -> return (ctxt, Deposits delegate) | `Block_fees -> Raw_context.spend_collected_fees_only_call_from_token ctxt amount - >>?= fun ctxt -> return (ctxt, Block_fees)) + >>?= fun ctxt -> return (ctxt, Block_fees) + | `Frozen_rollup_bonds (contract, bond_id) -> + Frozen_rollup_bonds_storage.spend_only_call_from_token + ctxt + contract + bond_id + amount + >>=? fun ctxt -> return (ctxt, Rollup_bonds (contract, bond_id))) >|=? fun (ctxt, balance) -> (ctxt, (balance, Debited amount, origin)) +let deallocate_stakeless_implicit_contracts ctxt contract = + match Contract_repr.is_implicit contract with + | None -> return ctxt (* Never delete originated contracts *) + | Some _ -> + Contract_storage.has_stake ctxt contract >>=? fun has_stake -> + if not has_stake then + (* Delete empty implicit contract. *) + Contract_delegate_storage.find ctxt contract >>=? function + | Some _ -> + (* Here, we know that the contract delegates to itself. Indeed, it + does not delegate to a different one, because the balance of + such contracts cannot be zero (see [spend_only_call_from_token] + in module [Contract_storage]), hence the stake of such contracts + cannot be zero either. *) + return ctxt + | None -> Contract_storage.delete_only_call_from_token ctxt contract + else return ctxt + let transfer_n ?(origin = Receipt_repr.Block_application) ctxt src dest = let sources = List.filter (fun (_, am) -> Tez_repr.(am <> zero)) src in match sources with @@ -160,6 +209,7 @@ let transfer_n ?(origin = Receipt_repr.Block_application) ctxt src dest = (* Avoid accessing context data when there is nothing to transfer. *) return (ctxt, []) | _ :: _ -> + (* Withdraw from sources. *) List.fold_left_es (fun (ctxt, total, debit_logs) (source, amount) -> spend ctxt source amount origin >>=? fun (ctxt, debit_log) -> @@ -168,7 +218,23 @@ let transfer_n ?(origin = Receipt_repr.Block_application) ctxt src dest = (ctxt, Tez_repr.zero, []) sources >>=? fun (ctxt, amount, debit_logs) -> - credit ctxt dest amount origin >|=? fun (ctxt, credit_log) -> + (* Credit the destination. *) + credit ctxt dest amount origin >>=? fun (ctxt, credit_log) -> + (* Deallocate implicit contracts with no stake. *) + List.fold_left_es + (fun ctxt (source, _amount) -> + match source with + | `Contract contract | `Frozen_rollup_bonds (contract, _) -> + (* If [contract] is in [sources] more than once, we must avoid + deallocating twice. *) + Contract_storage.allocated ctxt contract >>=? fun allocated -> + if allocated then + deallocate_stakeless_implicit_contracts ctxt contract + else return ctxt + | #source -> return ctxt) + ctxt + sources + >|=? fun ctxt -> (* Make sure the order of balance updates is : debit logs in the order of of the parameter [src], and then the credit log. *) let balance_updates = List.rev (credit_log :: debit_logs) in diff --git a/src/proto_alpha/lib_protocol/token.mli b/src/proto_alpha/lib_protocol/token.mli index e872b872c34806331d6c7c4f53238d6955be9017..c652ceb47f553976493907984861c8ef492e883a 100644 --- a/src/proto_alpha/lib_protocol/token.mli +++ b/src/proto_alpha/lib_protocol/token.mli @@ -41,12 +41,16 @@ to/from [`Delegate_balance d] will not update [d]'s stake, while transferring to/from [`Contract (Contract_repr.implicit_contract d)] will update [d]'s stake. *) +type carb_container = + [`Frozen_rollup_bonds of Contract_repr.t * Rollup_bond_id_repr.t] + type container = [ `Contract of Contract_repr.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 - | `Block_fees ] + | `Block_fees + | carb_container ] (** [source] is the type of token providers. Token providers that are not containers are considered to have infinite capacity. *) @@ -72,11 +76,24 @@ type sink = | `Burned | container ] +(** [carb_allocated ctxt container] returns a new context because of an access + to carbonated data, and a boolean that is true when [balance ctxt container] + is guaranteed not to fail, and false when [balance ctxt container] may fail. +*) +val carb_allocated : + Raw_context.t -> carb_container -> (Raw_context.t * bool) tzresult Lwt.t + (** [allocated ctxt container] returns true if [balance ctxt container] is guaranteed not to fail, and returns false when [balance ctxt container] may fail. *) val allocated : Raw_context.t -> container -> bool tzresult Lwt.t +(** [carb_balance ctxt container] returns a new context because of an access + to carbonated data, and the balance associated to [container], or + [Tez_rep.zero] if there is no balance associated to [container]. *) +val carb_balance : + Raw_context.t -> carb_container -> (Raw_context.t * Tez_repr.t) tzresult Lwt.t + (** [balance ctxt container] returns the balance associated to the token holder, may fail if [allocated ctxt container] returns [false]. Returns an error with the message "get_balance" if [container] refers to an diff --git a/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.ml b/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.ml index a5ed1c1d5e0f4e1e39397f0fd16d82eec2bde341..4a0338777055d500ff2efaed05b11eba8f12288e 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.ml @@ -35,7 +35,22 @@ type error += (* `Temporary *) Missing_commitment_predecessor type error += (* `Branch *) Wrong_batch_count -type error += (* `Temporary *) Commitment_too_early +type error += (* `Temporary *) Wrong_inbox_hash + +type error += + | (* `Temporary *) Commitment_too_early of Raw_level_repr.t * Raw_level_repr.t + +type error += (* `Temporary *) + Bond_does_not_exist of Signature.public_key_hash + +type error += (* `Temporary *) Bond_in_use of Signature.public_key_hash + +type error += (* `Temporary *) Too_many_unfinalized_levels + +type error += (* `Permanent *) No_such_batch of Raw_level_repr.t * int + +type error += (* `Temporary *) + No_such_commitment let () = let open Data_encoding in @@ -64,7 +79,7 @@ let () = ~id:"tx_rollup_wrong_commitment_predecessor_level" ~title:"This commitment's predecessor is invalid" ~description: - "This commitment has predecessor but shouldn't, or doesn't but should" + "This commitment has a predecessor but shouldn't, or doesn't but should" unit (function Wrong_commitment_predecessor_level -> Some () | _ -> None) (fun () -> Wrong_commitment_predecessor_level) ; @@ -87,15 +102,80 @@ let () = unit (function Wrong_batch_count -> Some () | _ -> None) (fun () -> Wrong_batch_count) ; + (* Wrong_inbox_hash *) + register_error_kind + `Temporary + ~id:"Wrong_inbox_hash" + ~title:"This commitment has the wrong inbox hash" + ~description:"This commitment has a different hash than its inbox" + unit + (function Wrong_inbox_hash -> Some () | _ -> None) + (fun () -> Wrong_inbox_hash) ; (* Commitment_too_early *) register_error_kind `Temporary ~id:"tx_rollup_commitment_too_early" ~title:"This commitment is for a level that hasn't finished yet" ~description:"This commitment is for a level that hasn't finished yet" - unit - (function Commitment_too_early -> Some () | _ -> None) - (fun () -> Commitment_too_early) + (obj2 + (req "commitment_level" Raw_level_repr.encoding) + (req "submit_level" Raw_level_repr.encoding)) + (function + | Commitment_too_early (commitment_level, submit_level) -> + Some (commitment_level, submit_level) + | _ -> None) + (fun (commitment_level, submit_level) -> + Commitment_too_early (commitment_level, submit_level)) ; + (* Bond_does_not_exist *) + register_error_kind + `Temporary + ~id:"tx_rollup_bond_does_not_exist" + ~title:"This account does not have a bond for this rollup" + ~description:"This account does not have a bond for this rollup" + (obj1 (req "contract" Signature.Public_key_hash.encoding)) + (function Bond_does_not_exist contract -> Some contract | _ -> None) + (fun contract -> Bond_does_not_exist contract) ; + (* Bond_in_use *) + register_error_kind + `Temporary + ~id:"tx_rollup_bond_in_use" + ~title:"This account's bond is in use for one or more commitments" + ~description:"This account's bond is in use for one or more commitments" + (obj1 (req "contract" Signature.Public_key_hash.encoding)) + (function Bond_in_use contract -> Some contract | _ -> None) + (fun contract -> Bond_in_use contract) ; + (* Too_many_unfinalized_levels *) + register_error_kind + `Temporary + ~id:"tx_rollup_too_many_unfinalized_levels" + ~title:"This rollup hasn't had a commitment in too long" + ~description: + "This rollup hasn't a commitment in too long. We don't allow new \ + messages to keep commitment gas reasonable." + empty + (function Too_many_unfinalized_levels -> Some () | _ -> None) + (fun () -> Too_many_unfinalized_levels) ; + (* No_such_batch *) + register_error_kind + `Temporary + ~id:"No_such_batch" + ~title:"This rejection wrongly attempts to reject a non-existent batch" + ~description: + "This rejection wrongly attempts to reject a non-existent batch" + (obj2 + (req "level" Raw_level_repr.encoding) + (req "index" Data_encoding.int31)) + (function No_such_batch (level, index) -> Some (level, index) | _ -> None) + (fun (level, index) -> No_such_batch (level, index)) ; + (* No_such_commitment *) + register_error_kind + `Temporary + ~id:"tx_rollup_no_such_commitment" + ~title:"No such commitment" + ~description:"No such commitment" + empty + (function No_such_commitment -> Some () | _ -> None) + (fun () -> No_such_commitment) let compare_or cmp c1 c2 f = match cmp c1 c2 with 0 -> f () | diff -> diff @@ -138,7 +218,7 @@ end module Commitment = struct type batch_commitment = { (* TODO: add effects and replace bytes with Irmin: - https://gitlab.com/tezos/tezos/-/issues/2444 + https://gitlab.com/tezos/tezos/-/issues/2444 *) root : bytes; } @@ -167,6 +247,7 @@ module Commitment = struct level : Raw_level_repr.t; batches : batch_commitment list; predecessor : Commitment_hash.t option; + inbox_hash : Tx_rollup_inbox_repr.Hash.t; } include Compare.Make (struct @@ -177,23 +258,27 @@ module Commitment = struct let compare r1 r2 = compare_or Raw_level_repr.compare r1.level r2.level (fun () -> compare_or Compare_root_list.compare r1.batches r2.batches (fun () -> - Option.compare - Commitment_hash.compare + compare_or + (Option.compare Commitment_hash.compare) r1.predecessor - r2.predecessor)) + r2.predecessor + (fun () -> + Tx_rollup_inbox_repr.Hash.compare r1.inbox_hash r2.inbox_hash))) end) let pp : Format.formatter -> t -> unit = fun fmt t -> Format.fprintf fmt - "commitment %a : batches = %a predecessor %a" + "commitment %a : batches = %a predecessor %a for inbox %a" Raw_level_repr.pp t.level (Format.pp_print_list Batch.pp) t.batches (Format.pp_print_option Commitment_hash.pp) t.predecessor + Tx_rollup_inbox_repr.Hash.pp + t.inbox_hash (* FIXME/TORU: https://gitlab.com/tezos/tezos/-/issues/2470 @@ -201,26 +286,35 @@ module Commitment = struct let encoding = let open Data_encoding in conv - (fun {level; batches; predecessor} -> (level, batches, predecessor)) - (fun (level, batches, predecessor) -> {level; batches; predecessor}) - (obj3 + (fun {level; batches; predecessor; inbox_hash} -> + (level, batches, predecessor, inbox_hash)) + (fun (level, batches, predecessor, inbox_hash) -> + {level; batches; predecessor; inbox_hash}) + (obj4 (req "level" Raw_level_repr.encoding) (req "batches" (list Batch.encoding)) - (req "predecessor" (option Commitment_hash.encoding))) + (req "predecessor" (option Commitment_hash.encoding)) + (req "inbox_hash" Tx_rollup_inbox_repr.Hash.encoding)) - let hash t = + let hash {level; batches; predecessor; inbox_hash} = let to_bytes_exn = Data_encoding.Binary.to_bytes_exn in - let level_bytes = to_bytes_exn Raw_level_repr.encoding t.level in + let level_bytes = to_bytes_exn Raw_level_repr.encoding level in let predecessor_bytes = Option.fold ~none:Bytes.empty ~some:(fun pred -> Commitment_hash.to_bytes pred) - t.predecessor + predecessor in let batches_bytes = - to_bytes_exn (Data_encoding.list Batch.encoding) t.batches + to_bytes_exn (Data_encoding.list Batch.encoding) batches + in + let inbox_hash_bytes = + Data_encoding.Binary.to_bytes_exn + Tx_rollup_inbox_repr.Hash.encoding + inbox_hash in - Commitment_hash.hash_bytes [level_bytes; predecessor_bytes; batches_bytes] + Commitment_hash.hash_bytes + [level_bytes; predecessor_bytes; batches_bytes; inbox_hash_bytes] module Index = struct type t = Commitment_hash.t diff --git a/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.mli b/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.mli index 876019cd65adfe0c3084c860e8e0a5069413b02e..d36aabb1ef96461a2cfc9742cb3fe5b6fbd631bc 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.mli +++ b/src/proto_alpha/lib_protocol/tx_rollup_commitments_repr.mli @@ -35,7 +35,22 @@ type error += (* `Temporary *) Missing_commitment_predecessor type error += (* `Branch *) Wrong_batch_count -type error += (* `Temporary *) Commitment_too_early +type error += (* `Temporary *) Wrong_inbox_hash + +type error += + | (* `Temporary *) Commitment_too_early of Raw_level_repr.t * Raw_level_repr.t + +type error += (* `Temporary *) + Bond_does_not_exist of Signature.public_key_hash + +type error += (* `Temporary *) Bond_in_use of Signature.public_key_hash + +type error += (* `Temporary *) Too_many_unfinalized_levels + +type error += (* `Permanent *) No_such_batch of Raw_level_repr.t * int + +type error += (* `Temporary *) + No_such_commitment (** A specialized Blake2B implementation for hashing commitments with "toc1" as a base58 prefix *) @@ -63,6 +78,7 @@ module Commitment : sig level : Raw_level_repr.t; batches : batch_commitment list; predecessor : Commitment_hash.t option; + inbox_hash : Tx_rollup_inbox_repr.Hash.t; } val ( = ) : t -> t -> bool diff --git a/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.ml b/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.ml index 550d8d19ed7f8ff62a5b7265d3b02e89b245678b..95cf5333e6941fead7640ed82b21e48805c0452b 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.ml @@ -41,10 +41,42 @@ let get_or_empty_commitments : ~none:(ctxt, Tx_rollup_commitments_repr.empty) ~some:(fun l -> (ctxt, List.rev l)) +let get_next_level ctxt tx_rollup level = + Tx_rollup_inbox_storage.get_adjacent_levels ctxt level tx_rollup + >|=? fun (ctxt, _, next_level) -> (ctxt, next_level) + let get_prev_level ctxt tx_rollup level = Tx_rollup_inbox_storage.get_adjacent_levels ctxt level tx_rollup >|=? fun (ctxt, predecessor_level, _) -> (ctxt, predecessor_level) +(* This indicates a programming error. *) +type error += (*`Temporary*) Commitment_bond_negative of int + +let adjust_commitment_bond ctxt tx_rollup pkh delta = + let bond_key = (tx_rollup, pkh) in + Storage.Tx_rollup.Commitment_bond.find ctxt bond_key + >>=? fun (ctxt, commitment) -> + let count = + match commitment with Some count -> count + delta | None -> delta + in + fail_when Compare.Int.(count < 0) (Commitment_bond_negative count) + >>=? fun () -> + Storage.Tx_rollup.Commitment_bond.add ctxt bond_key count >|=? just_ctxt + +let remove_bond : + Raw_context.t -> + Tx_rollup_repr.t -> + Signature.public_key_hash -> + Raw_context.t tzresult Lwt.t = + fun ctxt tx_rollup contract -> + let bond_key = (tx_rollup, contract) in + Storage.Tx_rollup.Commitment_bond.find ctxt bond_key >>=? fun (ctxt, bond) -> + match bond with + | None -> fail (Bond_does_not_exist contract) + | Some 0 -> + Storage.Tx_rollup.Commitment_bond.remove ctxt bond_key >|=? just_ctxt + | Some _ -> fail (Bond_in_use contract) + let check_commitment_predecessor_hash ctxt tx_rollup (commitment : Commitment.t) = let level = commitment.level in @@ -64,23 +96,286 @@ let check_commitment_predecessor_hash ctxt tx_rollup (commitment : Commitment.t) Missing_commitment_predecessor >>=? fun () -> return ctxt -let add_commitment ctxt tx_rollup contract (commitment : Commitment.t) = +let add_commitment ctxt tx_rollup pkh (commitment : Commitment.t) = let key = (commitment.level, tx_rollup) in get_or_empty_commitments ctxt key >>=? fun (ctxt, pending) -> - Tx_rollup_inbox_storage.get ctxt ~level:(`Level commitment.level) tx_rollup - >>=? fun (ctxt, inbox) -> - let expected_len = List.length inbox.contents in + Tx_rollup_inbox_storage.get_metadata ctxt commitment.level tx_rollup + >>=? fun (ctxt, {count; hash; _}) -> let actual_len = List.length commitment.batches in - fail_unless Compare.Int.(expected_len = actual_len) Wrong_batch_count + fail_unless Compare.Int.(count = actual_len) Wrong_batch_count >>=? fun () -> + fail_unless + Compare.Int.( + 0 = Tx_rollup_inbox_repr.Hash.compare commitment.inbox_hash hash) + Wrong_inbox_hash >>=? fun () -> check_commitment_predecessor_hash ctxt tx_rollup commitment >>=? fun ctxt -> Tx_rollup_commitments_repr.append pending - contract + pkh commitment (Raw_context.current_level ctxt).level >>?= fun new_pending -> - Storage.Tx_rollup.Commitment_list.add ctxt key new_pending >|=? just_ctxt + Storage.Tx_rollup.Commitment_list.add ctxt key new_pending + >>=? fun (ctxt, _, _) -> adjust_commitment_bond ctxt tx_rollup pkh 1 + +module Commitment_set = Set.Make (Commitment_hash) +module Contract_set = Set.Make (Signature.Public_key_hash) + +let rec remove_successors : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Raw_level_repr.t -> + Commitment_set.t -> + Raw_context.t tzresult Lwt.t = + fun ctxt tx_rollup level top commitments -> + if Raw_level_repr.(level > top) then return ctxt + else + let key = (level, tx_rollup) in + get_next_level ctxt tx_rollup level >>=? fun (ctxt, next_level) -> + Storage.Tx_rollup.Commitment_list.find ctxt key + >>=? fun (ctxt, commitment_list) -> + match commitment_list with + | None -> ( + match next_level with + | None -> return ctxt + | Some next_level -> + remove_successors ctxt tx_rollup next_level top commitments) + | Some pending -> + let pending = List.rev pending in + let next_commitments = + List.fold_left + (fun next_commitments {commitment; hash; _} -> + if + Option.fold + ~none:false + ~some:(fun predecessor -> + Commitment_set.mem predecessor commitments) + commitment.predecessor + then Commitment_set.add hash next_commitments + else next_commitments) + commitments + pending + in + if not @@ Commitment_set.is_empty commitments then + let (to_remove, new_pending) = + List.partition + (fun {hash; _} -> Commitment_set.mem hash next_commitments) + pending + in + List.fold_left_es + (fun ctxt {committer; _} -> + adjust_commitment_bond ctxt tx_rollup committer (-1)) + ctxt + to_remove + >>=? fun ctxt -> + Storage.Tx_rollup.Commitment_list.add ctxt key new_pending + >>=? fun (ctxt, _, _) -> + match next_level with + | None -> return ctxt + | Some next_level -> + remove_successors ctxt tx_rollup next_level top next_commitments + else return ctxt + +let get_commitment_roots ctxt tx_rollup (level : Raw_level_repr.t) + (commitment_id : Commitment_hash.t) (index : int) = + let find_commitment_by_hash ctxt level hash = + Storage.Tx_rollup.Commitment_list.get ctxt (level, tx_rollup) + >>=? fun (ctxt, commitments) -> + let pending_commitment = + List.find + (fun {commitment; _} -> + Commitment_hash.(hash = Commitment.hash commitment)) + commitments + in + Option.value_e + ~error:(Error_monad.trace_of_error No_such_commitment) + pending_commitment + >>?= fun pending -> return (ctxt, pending) + in + find_commitment_by_hash ctxt level commitment_id + >>=? fun (ctxt, pending_commitment) -> + let commitment = pending_commitment.commitment in + let nth_root (commitment : Commitment.t) n = + let nth = List.nth commitment.batches n in + Option.value_e + ~error:(Error_monad.trace_of_error (No_such_batch (level, index))) + nth + in + (match index with + | 0 -> ( + match commitment.predecessor with + | None -> + (* TODO: empty merkle tree when we have this*) + let empty : Tx_rollup_commitments_repr.Commitment.batch_commitment = + {root = Bytes.empty} + in + return (ctxt, empty) + | Some prev_hash -> + get_prev_level ctxt tx_rollup level >>=? fun (ctxt, prev_level) -> + let prev_level = + match prev_level with + | None -> assert false + | Some prev_level -> prev_level + in + find_commitment_by_hash ctxt prev_level prev_hash + >>=? fun (ctxt, {commitment = {batches; _}; _}) -> + (let last = List.last_opt batches in + Option.value_e + ~error:(Error_monad.trace_of_error (No_such_batch (level, -1))) + last) + >>?= fun p -> return (ctxt, p)) + | index -> nth_root commitment (index - 1) >>?= fun p -> return (ctxt, p)) + >>=? fun (ctxt, before_hash) -> + nth_root commitment index >>?= fun after_hash -> + return (ctxt, (before_hash, after_hash)) + +let rec accumulate_bad_commitments : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Raw_level_repr.t -> + Commitment_set.t -> + Contract_set.t -> + (Commitment_set.t * Contract_set.t) tzresult Lwt.t = + fun ctxt tx_rollup level top commitments contracts -> + let add_bad_commitments (commitments, contracts) + {commitment; hash; committer; _} = + if + Option.value ~default:false + @@ Option.map + (fun predecessor -> Commitment_set.mem predecessor commitments) + commitment.predecessor + || Contract_set.mem committer contracts + then + (Commitment_set.add hash commitments, Contract_set.add committer contracts) + else (commitments, contracts) + in + if Raw_level_repr.(level > top) then return (commitments, contracts) + else + let key = (level, tx_rollup) in + Storage.Tx_rollup.Commitment_list.find ctxt key + >>=? fun (ctxt, commitment_list) -> + let pending = + match commitment_list with None -> [] | Some pending -> pending + in + let (commitments, contracts) = + List.fold_left add_bad_commitments (commitments, contracts) pending + in + get_next_level ctxt tx_rollup level >>=? fun (ctxt, next_level) -> + match next_level with + | None -> return (commitments, contracts) + | Some next_level -> + accumulate_bad_commitments + ctxt + tx_rollup + next_level + top + commitments + contracts + +let rec remove_commitments_by_hash : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Raw_level_repr.t -> + Commitment_set.t -> + Raw_context.t tzresult Lwt.t = + fun ctxt tx_rollup level top commitments -> + if Raw_level_repr.(level > top) then return ctxt + else + let key = (level, tx_rollup) in + Storage.Tx_rollup.Commitment_list.find ctxt key + >>=? fun (ctxt, commitment_list) -> + (match commitment_list with + | None -> + (* No commitments at this level -- just recurse *) + return ctxt + | Some pending -> + let new_pending = + List.filter + (fun {hash; _} -> not @@ Commitment_set.mem hash commitments) + pending + in + Storage.Tx_rollup.Commitment_list.add ctxt key new_pending + >|=? just_ctxt) + >>=? fun ctxt -> + get_next_level ctxt tx_rollup level >>=? fun (ctxt, next_level) -> + match next_level with + | None -> return ctxt + | Some next_level -> + remove_commitments_by_hash ctxt tx_rollup next_level top commitments + +let reject_commitment ctxt tx_rollup (level : Raw_level_repr.t) + (commitment_id : Commitment_hash.t) = + let top = (Raw_context.current_level ctxt).level in + Storage.Tx_rollup.Commitment_list.get ctxt (level, tx_rollup) + >>=? fun (ctxt, commitments) -> + let matching_commitments = + List.filter + (fun {hash; _} -> Commitment_hash.(hash = commitment_id)) + commitments + in + let matching = List.hd matching_commitments in + Option.value_e ~error:(Error_monad.trace_of_error No_such_commitment) matching + >>?= fun to_remove -> + let initial_bad_commitments = Commitment_set.of_list [commitment_id] in + let initial_evildoers = Contract_set.of_list [to_remove.committer] in + let rec aux bad_commitments evildoers = + accumulate_bad_commitments + ctxt + tx_rollup + level + top + bad_commitments + evildoers + >>=? fun (new_bad_commitments, new_evildoers) -> + if + Compare.Int.( + Contract_set.cardinal new_evildoers = Contract_set.cardinal evildoers + && Commitment_set.cardinal new_bad_commitments + = Commitment_set.cardinal bad_commitments) + then return (new_bad_commitments, new_evildoers) + else aux new_bad_commitments new_evildoers + in + aux initial_bad_commitments initial_evildoers + >>=? fun (bad_commitments, evildoers) -> + remove_commitments_by_hash ctxt tx_rollup level top bad_commitments + >>=? fun ctxt -> + Contract_set.fold_es + (fun contract ctxt -> + let key = (tx_rollup, contract) in + Storage.Tx_rollup.Commitment_bond.remove ctxt key >|=? just_ctxt) + evildoers + ctxt + +let retire_rollup_level : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Raw_level_repr.t -> + (Raw_context.t * [> `No_commitment | `Commitment_too_late | `Retired]) + tzresult + Lwt.t = + fun ctxt tx_rollup level last_level_to_finalize -> + let top = (Raw_context.current_level ctxt).level in + let key = (level, tx_rollup) in + get_or_empty_commitments ctxt key >>=? fun (ctxt, commitments) -> + match commitments with + | [] -> return (ctxt, `No_commitment) + | accepted :: rejected -> + if Raw_level_repr.(accepted.submitted_at > last_level_to_finalize) then + return (ctxt, `Commitment_too_late) + else + let to_obviate = + Commitment_set.of_seq + (Seq.map (fun {hash; _} -> hash) (List.to_seq rejected)) + in + remove_successors ctxt tx_rollup level top to_obviate >>=? fun ctxt -> + adjust_commitment_bond ctxt tx_rollup accepted.committer (-1) + >>=? fun ctxt -> + Storage.Tx_rollup.Commitment_list.add ctxt key [accepted] + >>=? fun (ctxt, _, _) -> return (ctxt, `Retired) let get_commitments : Raw_context.t -> @@ -92,3 +387,55 @@ let get_commitments : match state with | None -> fail @@ Tx_rollup_state_storage.Tx_rollup_does_not_exist tx_rollup | Some _ -> get_or_empty_commitments ctxt (level, tx_rollup) + +let pending_bonded_commitments : + Raw_context.t -> + Tx_rollup_repr.t -> + Signature.public_key_hash -> + (Raw_context.t * int) tzresult Lwt.t = + fun ctxt tx_rollup pkh -> + Storage.Tx_rollup.Commitment_bond.find ctxt (tx_rollup, pkh) + >|=? fun (ctxt, pending) -> (ctxt, Option.value ~default:0 pending) + +let finalize_pending_commitments ctxt tx_rollup last_level_to_finalize = + Tx_rollup_state_storage.get ctxt tx_rollup >>=? fun (ctxt, state) -> + let first_unfinalized_level = + Tx_rollup_state_repr.first_unfinalized_level state + in + let max_count_to_finalize = + Constants_storage.tx_rollup_max_finalize_levels_per_commitment ctxt + in + match first_unfinalized_level with + | None -> return ctxt + | Some first_unfinalized_level -> + let rec finalize_level ctxt level top finalized_count = + if Compare.Int.(finalized_count >= max_count_to_finalize) then + return (ctxt, finalized_count, Some level) + else if Raw_level_repr.(level > top) then + return (ctxt, finalized_count, Some level) + else + retire_rollup_level ctxt tx_rollup level last_level_to_finalize + >>=? fun (ctxt, finalized) -> + match finalized with + | `Retired -> ( + get_next_level ctxt tx_rollup level >>=? fun (ctxt, next_level) -> + match next_level with + | None -> return (ctxt, 0, None) + | Some next_level -> + (finalize_level [@tailcall]) + ctxt + next_level + top + (finalized_count + 1)) + | _ -> return (ctxt, finalized_count, Some level) + in + finalize_level ctxt first_unfinalized_level last_level_to_finalize 0 + >>=? fun (ctxt, finalized_count, first_unfinalized_level) -> + let new_state = + Tx_rollup_state_repr.update_after_finalize + state + first_unfinalized_level + finalized_count + in + Storage.Tx_rollup.State.add ctxt tx_rollup new_state + >>=? fun (ctxt, _, _) -> return ctxt diff --git a/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.mli b/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.mli index a135ed1cae61b7b7e1bd54364564f324a868083c..d27a09f37e83c4bb8e33e6e886994382e5daad5d 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.mli +++ b/src/proto_alpha/lib_protocol/tx_rollup_commitments_storage.mli @@ -41,6 +41,44 @@ val add_commitment : Tx_rollup_commitments_repr.Commitment.t -> Raw_context.t tzresult Lwt.t +(** [remove_bond context tx_rollup contract] removes the bond for an + implicit contract. This will fail if either the bond does not exist, + or the bond is currently in-use. *) +val remove_bond : + Raw_context.t -> + Tx_rollup_repr.t -> + Signature.public_key_hash -> + Raw_context.t tzresult Lwt.t + +(** [reject_commitment ctxt tx_rollup_repr level commitment_hash] + rejects a commitment with a given hash at a given level. All + successor commitments are removed, and any bonds associated with + them are removed. Some successor commitments might be from + different contracts, in which case, we recursively remove all + contracts from those contracts and their successors, and so forth. + *) +val reject_commitment : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Tx_rollup_commitments_repr.Commitment_hash.t -> + Raw_context.t tzresult Lwt.t + +(** [retire_rollup_level context tx_rollup level] removes all data + associated with a level. It decrements the bonded commitment count + for any contracts whose commitments have been either accepted or + obviated (that is, neither accepted nor rejected). This is normally + used in finalization(during a Commitment operation) and is only and + is only public for testing. *) +val retire_rollup_level : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Raw_level_repr.t -> + (Raw_context.t * [> `Commitment_too_late | `No_commitment | `Retired]) + tzresult + Lwt.t + (** [get_commitments context tx_rollup level] returns the list of non-rejected commitments for a rollup at a level, first-submitted first. *) @@ -49,3 +87,49 @@ val get_commitments : Tx_rollup_repr.t -> Raw_level_repr.t -> (Raw_context.t * Tx_rollup_commitments_repr.t) tzresult Lwt.t + +(** [pending bonded_commitments ctxt tx_rollup pkh] returns the + number of commitments that [pkh] has made that are still + pending (that is, still subject to rejection) *) +val pending_bonded_commitments : + Raw_context.t -> + Tx_rollup_repr.t -> + Signature.public_key_hash -> + (Raw_context.t * int) tzresult Lwt.t + +(** [finalize_pending_commitments ctxt tx_rollup last_level_to_finalize] + finalizes all pending commitments that are old enough. For each + unfinalized level up to and including last_level_to_finalize, the + oldest non-rejected commitment is chosen. Any other commitments are + deleted, and their transitive successors are also deleted. Because + these commitments have not been rejected, their bonds are not + slashed, but we still must maintain the count of bonded commitments. + + In the event that some level does not yet have any nonrejected + commitments, the level traversal stops. + + The state is adjusted as well, tracking which levels have been + finalized, and which are left to be finalized. *) +val finalize_pending_commitments : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Raw_context.t tzresult Lwt.t + +(** [get_commitment_roots context tx_rollup level commitment_hash + batch] returns the before and after roots of the commitment + [commitment] at level [level], batch [batch]. If [batch] is 0, + then the before root will come from the last batch of the + predecessor commitment, or will be empty if this is the first + commitment on this rollup. *) +val get_commitment_roots : + Raw_context.t -> + Tx_rollup_repr.t -> + Raw_level_repr.t -> + Tx_rollup_commitments_repr.Commitment_hash.t -> + int -> + (Raw_context.t + * (Tx_rollup_commitments_repr.Commitment.batch_commitment + * Tx_rollup_commitments_repr.Commitment.batch_commitment)) + tzresult + Lwt.t diff --git a/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.ml b/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.ml index 7468b98526f91a6c0d0152a843c46ae7edf67a3d..0e4d4704bf74b3971f20293535e0ed9133bc374d 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.ml @@ -43,8 +43,34 @@ let encoding = (req "contents" @@ list Tx_rollup_message_repr.hash_encoding) (req "cumulated_size" int31)) +module Hash = struct + type t = bytes + + include Compare.Make (Bytes) + + let pp : Format.formatter -> t -> unit = + fun fmt t -> Hex.pp fmt (Hex.of_bytes t) + + let empty = Bytes.empty + + let encoding = Data_encoding.bytes + + let extend t message = + let message = + Data_encoding.Binary.to_bytes_exn + Tx_rollup_message_repr.hash_encoding + message + in + Raw_hashes.blake2b (Bytes.cat t message) +end + +let hash_inbox : t -> Hash.t = + fun t -> List.fold_left (fun h msg -> Hash.extend h msg) Hash.empty t.contents + type metadata = { + count : int; cumulated_size : int; + hash : Hash.t; predecessor : Raw_level_repr.t option; successor : Raw_level_repr.t option; } @@ -52,11 +78,13 @@ type metadata = { let metadata_encoding = let open Data_encoding in conv - (fun {cumulated_size; predecessor; successor} -> - (cumulated_size, predecessor, successor)) - (fun (cumulated_size, predecessor, successor) -> - {cumulated_size; predecessor; successor}) - (obj3 + (fun {count; cumulated_size; hash; predecessor; successor} -> + (count, cumulated_size, hash, predecessor, successor)) + (fun (count, cumulated_size, hash, predecessor, successor) -> + {count; cumulated_size; hash; predecessor; successor}) + (obj5 + (req "count" int31) (req "cumulated_size" int31) + (req "hash" bytes) (req "predecessor" (option Raw_level_repr.encoding)) (req "successor" (option Raw_level_repr.encoding))) diff --git a/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.mli b/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.mli index b104ae91d217f01280fccc1a799df6670a3813a0..fe670da1d72ee2cb042967c2e3325f91e7bb85a1 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.mli +++ b/src/proto_alpha/lib_protocol/tx_rollup_inbox_repr.mli @@ -41,14 +41,34 @@ val pp : Format.formatter -> t -> unit val encoding : t Data_encoding.t -(* The metadata for an inbox stores the [cumulated_size] in - bytes for the inbox, so that we do not need to retrieve the entries - for the inbox just to get the size. It also stores the +module Hash : sig + type t = private bytes + + include Compare.S with type t := t + + val pp : Format.formatter -> t -> unit + + val empty : t + + val encoding : t Data_encoding.t + + val extend : t -> Tx_rollup_message_repr.hash -> t +end + +val hash_inbox : t -> Hash.t + +(* The metadata for an inbox stores: (1) the count of messages (2) the + [cumulated_size] in bytes for the inbox, so that we do not need to + retrieve the entries for the inbox just to get the size. (3) the [predecessor] and [successor] levels. For the first inbox of a rollup, the [predecessor] will be None. For all inboxes, the - [successor] will be None until a subsequent inbox is created. *) + [successor] will be None until a subsequent inbox is created. + (4) the cumulative hash of the inbox contents -- that is, + h(h(h("" ^ contents[0]) ^ contents[1]), ...) *) type metadata = { + count : int; cumulated_size : int; + hash : Hash.t; predecessor : Raw_level_repr.t option; successor : Raw_level_repr.t option; } diff --git a/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.ml b/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.ml index f3d49fc15e0b67c69714c0066272b899db822529..d1ea2715e1eb7aac32c1585e9d9972b7146bceb6 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.ml @@ -43,6 +43,13 @@ let prepare_metadata : tzresult Lwt.t = fun ctxt rollup state level -> + (* First, check if there are too many unfinalized levels. *) + fail_when + Compare.Int.( + Tx_rollup_state_repr.unfinalized_level_count state + > Constants_storage.tx_rollup_max_unfinalized_levels ctxt) + Tx_rollup_commitments_repr.Too_many_unfinalized_levels + >>=? fun () -> Storage.Tx_rollup.Inbox_metadata.find (ctxt, level) rollup >>=? fun (ctxt, metadata) -> match metadata with @@ -52,6 +59,7 @@ let prepare_metadata : inbox count *) let predecessor = Tx_rollup_state_repr.last_inbox_level state in let new_state = Tx_rollup_state_repr.append_inbox state level in + Tx_rollup_state_storage.update ctxt rollup new_state >>=? fun ctxt -> (match predecessor with | None -> return ctxt | Some predecessor_level -> @@ -66,7 +74,13 @@ let prepare_metadata : >|=? fun (ctxt, _, _) -> ctxt) >>=? fun ctxt -> let new_metadata : Tx_rollup_inbox_repr.metadata = - {cumulated_size = 0; predecessor; successor = None} + { + count = 0; + cumulated_size = 0; + hash = Tx_rollup_inbox_repr.Hash.empty; + predecessor; + successor = None; + } in return (ctxt, new_state, new_metadata) @@ -81,9 +95,12 @@ let append_message : let message_size = Tx_rollup_message_repr.size message in prepare_metadata ctxt rollup state level >>=? fun (ctxt, new_state, new_metadata) -> + let message_hash = Tx_rollup_message_repr.hash message in let new_metadata = { new_metadata with + count = new_metadata.count + 1; + hash = Tx_rollup_inbox_repr.Hash.extend new_metadata.hash message_hash; cumulated_size = message_size + new_metadata.cumulated_size; } in @@ -104,7 +121,7 @@ let append_message : Storage.Tx_rollup.Inbox_rev_contents.add (ctxt, level) rollup - (Tx_rollup_message_repr.hash message :: Option.value ~default:[] mcontents) + (message_hash :: Option.value ~default:[] mcontents) >>=? fun (ctxt, _, _) -> return (ctxt, new_state) let get_level : @@ -186,7 +203,7 @@ let get : (Raw_context.t * Tx_rollup_inbox_repr.t) tzresult Lwt.t = fun ctxt ~level tx_rollup -> (* - [inbox_opt] checks whether or not [tx_rollup] is valid, so we + [inbox_opt] c_hecks whether or not [tx_rollup] is valid, so we don’t have to do it here. *) find ctxt ~level tx_rollup >>=? function @@ -206,6 +223,27 @@ let get_adjacent_levels : return (ctxt, predecessor, successor) | (_, None) -> fail @@ Tx_rollup_inbox_does_not_exist (tx_rollup, level) +let get_metadata : + Raw_context.t -> + Raw_level_repr.t -> + Tx_rollup_repr.t -> + (Raw_context.t * Tx_rollup_inbox_repr.metadata) tzresult Lwt.t = + fun ctxt level tx_rollup -> + Storage.Tx_rollup.Inbox_metadata.find (ctxt, level) tx_rollup >>=? function + | (_, None) -> fail (Tx_rollup_inbox_does_not_exist (tx_rollup, level)) + | (ctxt, Some metadata) -> return (ctxt, metadata) + +let check_batch_hash : + Raw_context.t -> + Raw_level_repr.t -> + Tx_rollup_repr.t -> + int -> + Tx_rollup_message_repr.t -> + Raw_context.t tzresult Lwt.t = + fun ctxt _level _rollup _batch_index _batch -> + (* TODO/TORU: write this! *) + return ctxt + (* Error registration *) let () = diff --git a/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.mli b/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.mli index 3e0d3ac3e02805310053badd0f5d16f14a82fced..498dd5cdea826b268edaa0da074704f5725ab0a7 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.mli +++ b/src/proto_alpha/lib_protocol/tx_rollup_inbox_storage.mli @@ -138,3 +138,22 @@ val get_adjacent_levels : Tx_rollup_repr.t -> (Raw_context.t * Raw_level_repr.t option * Raw_level_repr.t option) tzresult Lwt.t + +(* [get_metadata ctxt level tx_rollup] returns the metadata for an inbox: + its count, byte size, next and previous levels, and hash *) +val get_metadata : + Raw_context.t -> + Raw_level_repr.t -> + Tx_rollup_repr.t -> + (Raw_context.t * Tx_rollup_inbox_repr.metadata) tzresult Lwt.t + +(* [check_batch_hash ctxt level rollup index batch] checks that a + batch for an inbox has the expected hash, and fails if it does + not. *) +val check_batch_hash : + Raw_context.t -> + Raw_level_repr.t -> + Tx_rollup_repr.t -> + int -> + Tx_rollup_message_repr.t -> + Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/tx_rollup_rejection_repr.ml b/src/proto_alpha/lib_protocol/tx_rollup_rejection_repr.ml new file mode 100644 index 0000000000000000000000000000000000000000..9bb755b804d844c0abfcb3ff0453be502993fdbe --- /dev/null +++ b/src/proto_alpha/lib_protocol/tx_rollup_rejection_repr.ml @@ -0,0 +1,62 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* Copyright (c) 2022 Nomadic Labs *) +(* Copyright (c) 2022 Oxhead Alpha *) +(* *) +(* 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 += (* `Permanent *) Wrong_rejection + +let () = + let open Data_encoding in + (* Wrong_rejection *) + register_error_kind + `Temporary + ~id:"Wrong_rejection" + ~title:"This rejection wrongly attempts to reject a correct comitment" + ~description:"This rejection wrongly attempts to reject a correct comitment" + unit + (function Wrong_rejection -> Some () | _ -> None) + (fun () -> Wrong_rejection) + +type t = { + rollup : Tx_rollup_repr.t; + level : Raw_level_repr.t; + hash : Tx_rollup_commitments_repr.Commitment_hash.t; + batch_index : int; + batch : Tx_rollup_message_repr.t; +} + +let encoding = + let open Data_encoding in + conv + (fun {rollup; level; hash; batch_index; batch} -> + (rollup, level, hash, batch_index, batch)) + (fun (rollup, level, hash, batch_index, batch) -> + {rollup; level; hash; batch_index; batch}) + (obj5 + (req "rollup" Tx_rollup_repr.encoding) + (req "level" Raw_level_repr.encoding) + (req "hash" Tx_rollup_commitments_repr.Commitment_hash.encoding) + (req "batch_index" int31) + (req "batch" Tx_rollup_message_repr.encoding)) diff --git a/src/proto_alpha/lib_protocol/tx_rollup_rejection_repr.mli b/src/proto_alpha/lib_protocol/tx_rollup_rejection_repr.mli new file mode 100644 index 0000000000000000000000000000000000000000..1c617d68f7ca58a6b78353383441623dba1da6a0 --- /dev/null +++ b/src/proto_alpha/lib_protocol/tx_rollup_rejection_repr.mli @@ -0,0 +1,38 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Marigold *) +(* Copyright (c) 2022 Nomadic Labs *) +(* Copyright (c) 2022 Oxhead Alpha *) +(* *) +(* 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 += (* `Permanent *) Wrong_rejection + +type t = { + rollup : Tx_rollup_repr.t; + level : Raw_level_repr.t; + hash : Tx_rollup_commitments_repr.Commitment_hash.t; + batch_index : int; + batch : Tx_rollup_message_repr.t; +} + +val encoding : t Data_encoding.t diff --git a/src/proto_alpha/lib_protocol/tx_rollup_services.ml b/src/proto_alpha/lib_protocol/tx_rollup_services.ml index 436f948f8c7ba14e8f5c0a897b1159807ea09e9e..bf965888dbe1a611ea0a378f4902626e47427efc 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_services.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_services.ml @@ -50,6 +50,16 @@ module S = struct ~query:RPC_query.empty ~output:Tx_rollup_commitments.encoding RPC_path.(custom_root /: Tx_rollup.rpc_arg / "commitments") + + let pending_bonded_commitments = + RPC_service.get_service + ~description: + "Get the number of pending bonded commitments for a pkh on a rollup" + ~query:RPC_query.empty + ~output:Data_encoding.int32 + RPC_path.( + custom_root /: Tx_rollup.rpc_arg / "pending_bonded_commitments" + /: Signature.Public_key_hash.rpc_arg) end let register () = @@ -60,7 +70,13 @@ let register () = Tx_rollup_inbox.find ctxt tx_rollup ~level:`Current >|=? snd) ; register1 ~chunked:false S.commitments (fun ctxt tx_rollup () () -> let level = (Level.current ctxt).level in - Tx_rollup_commitments.get_commitments ctxt tx_rollup level >|=? snd) + Tx_rollup_commitments.get_commitments ctxt tx_rollup level >|=? snd) ; + register2 + ~chunked:false + S.pending_bonded_commitments + (fun ctxt tx_rollup pkh () () -> + Tx_rollup_commitments.pending_bonded_commitments ctxt tx_rollup pkh + >|=? fun (_, count) -> Int32.of_int count) let state ctxt block tx_rollup = RPC_context.make_call1 S.state ctxt block tx_rollup () () diff --git a/src/proto_alpha/lib_protocol/tx_rollup_state_repr.ml b/src/proto_alpha/lib_protocol/tx_rollup_state_repr.ml index 012412d254cef4115bdb3e4da292bd36f6d4a88c..fde3b173aa5175315277f765dfd35ca4529460b6 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_state_repr.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_state_repr.ml @@ -42,30 +42,71 @@ spurious spikes of [fees_per_byte]. *) type t = { + first_unfinalized_level : Raw_level_repr.t option; + unfinalized_level_count : int; fees_per_byte : Tez_repr.t; inbox_ema : int; last_inbox_level : Raw_level_repr.t option; } let initial_state = - {fees_per_byte = Tez_repr.zero; inbox_ema = 0; last_inbox_level = None} + { + first_unfinalized_level = None; + unfinalized_level_count = 0; + fees_per_byte = Tez_repr.zero; + inbox_ema = 0; + last_inbox_level = None; + } let encoding : t Data_encoding.t = let open Data_encoding in conv - (fun {last_inbox_level; fees_per_byte; inbox_ema} -> - (last_inbox_level, fees_per_byte, inbox_ema)) - (fun (last_inbox_level, fees_per_byte, inbox_ema) -> - {last_inbox_level; fees_per_byte; inbox_ema}) - (obj3 - (req "last_inbox_level" (option Raw_level_repr.encoding)) + (fun { + first_unfinalized_level; + unfinalized_level_count; + fees_per_byte; + inbox_ema; + last_inbox_level; + } -> + ( first_unfinalized_level, + unfinalized_level_count, + fees_per_byte, + inbox_ema, + last_inbox_level )) + (fun ( first_unfinalized_level, + unfinalized_level_count, + fees_per_byte, + inbox_ema, + last_inbox_level ) -> + { + first_unfinalized_level; + unfinalized_level_count; + fees_per_byte; + inbox_ema; + last_inbox_level; + }) + (obj5 + (req "first_unfinalized_level" (option Raw_level_repr.encoding)) + (req "unfinalized_level_count" int16) (req "fees_per_byte" Tez_repr.encoding) - (req "inbox_ema" int31)) + (req "inbox_ema" int31) + (req "last_inbox_level" (option Raw_level_repr.encoding))) -let pp fmt {fees_per_byte; last_inbox_level; inbox_ema} = +let pp fmt + { + first_unfinalized_level; + unfinalized_level_count; + fees_per_byte; + inbox_ema; + last_inbox_level; + } = Format.fprintf fmt - "Tx_rollup: fees_per_byte = %a; inbox_ema %d; last_inbox_level = %a" + "first_unfinalized_level: %a unfinalized_level_count: %d cost_per_byte: %a \ + inbox_ema: %d newest inbox: %a" + (Format.pp_print_option Raw_level_repr.pp) + first_unfinalized_level + unfinalized_level_count Tez_repr.pp fees_per_byte inbox_ema @@ -125,7 +166,27 @@ let fees {fees_per_byte; _} size = Tez_repr.(fees_per_byte *? Int64.of_int size) let last_inbox_level {last_inbox_level; _} = last_inbox_level -let append_inbox t level = {t with last_inbox_level = Some level} +let append_inbox t level = + { + t with + last_inbox_level = Some level; + first_unfinalized_level = + Some (Option.value ~default:level t.first_unfinalized_level); + unfinalized_level_count = t.unfinalized_level_count + 1; + } + +let unfinalized_level_count {unfinalized_level_count; _} = + unfinalized_level_count + +let first_unfinalized_level {first_unfinalized_level; _} = + first_unfinalized_level + +let update_after_finalize state level count = + { + state with + first_unfinalized_level = level; + unfinalized_level_count = state.unfinalized_level_count - count; + } module Internal_for_tests = struct let make : @@ -134,7 +195,13 @@ module Internal_for_tests = struct last_inbox_level:Raw_level_repr.t option -> t = fun ~fees_per_byte ~inbox_ema ~last_inbox_level -> - {fees_per_byte; inbox_ema; last_inbox_level} + { + fees_per_byte; + inbox_ema; + last_inbox_level; + first_unfinalized_level = None; + unfinalized_level_count = 0; + } let get_inbox_ema : t -> int = fun {inbox_ema; _} -> inbox_ema end diff --git a/src/proto_alpha/lib_protocol/tx_rollup_state_repr.mli b/src/proto_alpha/lib_protocol/tx_rollup_state_repr.mli index 28a1ef3b687b6d32732c362b4925ff4a874bdf9a..977ab4a286567508cf4507f46aaf66d505a04323 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_state_repr.mli +++ b/src/proto_alpha/lib_protocol/tx_rollup_state_repr.mli @@ -63,6 +63,20 @@ val last_inbox_level : t -> Raw_level_repr.t option [state] when messages have been added at a level. *) val append_inbox : t -> Raw_level_repr.t -> t +(** [unfinalized_level_count state] returns the number of unfinalized + levels of the rollup. If this is too high, we stop appending + messages until commitments catch up. *) +val unfinalized_level_count : t -> int + +(** [first_unfinalized_level state] returns the first unfinalized + level of the rollup. Note that this level might be empty.*) +val first_unfinalized_level : t -> Raw_level_repr.t option + +(* [update_after_finalize state level count] updates a state + after some levels have been finalized. [count] is the number of + finalized levels *) +val update_after_finalize : t -> Raw_level_repr.t option -> int -> t + module Internal_for_tests : sig (** [make] returns a state for tests *) val make : diff --git a/src/proto_alpha/lib_protocol/tx_rollup_state_storage.ml b/src/proto_alpha/lib_protocol/tx_rollup_state_storage.ml index ff9921d52efdb64bd5ab3fd89ec801745e09b4d4..9f8ed228492fee06f2b723cc15ba73eab7e13ac2 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_state_storage.ml +++ b/src/proto_alpha/lib_protocol/tx_rollup_state_storage.ml @@ -107,3 +107,11 @@ let () = (obj1 (req "rollup_address" Tx_rollup_repr.encoding)) (function Tx_rollup_does_not_exist rollup -> Some rollup | _ -> None) (fun rollup -> Tx_rollup_does_not_exist rollup) + +let first_unfinalized_level : + Raw_context.t -> + Tx_rollup_repr.t -> + (Raw_context.t * Raw_level_repr.t option) tzresult Lwt.t = + fun ctxt tx_rollup -> + Storage.Tx_rollup.State.get ctxt tx_rollup >>=? fun (ctxt, state) -> + return (ctxt, Tx_rollup_state_repr.first_unfinalized_level state) diff --git a/src/proto_alpha/lib_protocol/tx_rollup_state_storage.mli b/src/proto_alpha/lib_protocol/tx_rollup_state_storage.mli index aa313fcad784df0340a8da8c4d70549833fed2f3..ff6a981f2e89a990de3fc3338b15a0f6ef1f1972 100644 --- a/src/proto_alpha/lib_protocol/tx_rollup_state_storage.mli +++ b/src/proto_alpha/lib_protocol/tx_rollup_state_storage.mli @@ -80,3 +80,11 @@ val update : transaction rollup address. *) val assert_exist : Raw_context.t -> Tx_rollup_repr.t -> Raw_context.t tzresult Lwt.t + +(** [first_unfinalized_level] returns the first unfinalized level + of [tx_rollup]. If None, then there are no unfinalized levels + with inboxes. *) +val first_unfinalized_level : + Raw_context.t -> + Tx_rollup_repr.t -> + (Raw_context.t * Raw_level_repr.t option) tzresult Lwt.t diff --git a/tests_python/tests_alpha/test_mockup.py b/tests_python/tests_alpha/test_mockup.py index 988b7ebf5e71faf9699cf9ac3fd32c1de46945b9..8467d00de0725357e184bcf00b5d9ffb0f45199e 100644 --- a/tests_python/tests_alpha/test_mockup.py +++ b/tests_python/tests_alpha/test_mockup.py @@ -659,6 +659,10 @@ def _test_create_mockup_init_show_roundtrip( "tx_rollup_origination_size": 30_000, "tx_rollup_hard_size_limit_per_inbox": 75_000, "tx_rollup_hard_size_limit_per_message": 9_999, + "tx_rollup_commitment_bond": "10000000000", + "tx_rollup_finality_period": 2000, + "tx_rollup_max_unfinalized_levels": 2100, + "tx_rollup_max_finalize_levels_per_commitment": 5, "sc_rollup_enable": False, "sc_rollup_origination_size": 6_314, } diff --git a/tezt/_regressions/rpc/alpha.client.mempool.out b/tezt/_regressions/rpc/alpha.client.mempool.out index 9b3b2d8dca42dc1ad117c0ccd0aa40106e155d14..0e863891423accaa23eaf4417873801294ce7336 100644 --- a/tezt/_regressions/rpc/alpha.client.mempool.out +++ b/tezt/_regressions/rpc/alpha.client.mempool.out @@ -693,6 +693,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "title": "A Ed25519, Secp256k1, or P256 public key hash (Base58Check-encoded)", "$ref": "#/definitions/unistring" }, + "Tx_rollup_l2_address": { + "title": "The hash of a BLS public key used to identify a L2 ticket holders (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "alpha.block_header.alpha.full_header": { "title": "Shell header", "description": "Block header's shell-related content. It contains information such as the block level, its predecessor and timestamp.", @@ -1945,9 +1949,14 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "type": "null" } ] + }, + "inbox_hash": { + "type": "string", + "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, "required": [ + "inbox_hash", "predecessor", "batches", "level" @@ -1967,6 +1976,165 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Tx_rollup_return_bond", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_return_bond" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + } + }, + "required": [ + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Tx_rollup_rejection", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_rejection" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + }, + "level": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "hash": { + "$ref": "#/definitions/Commitment_hash" + }, + "batch_index": { + "type": "integer", + "minimum": -1073741824, + "maximum": 1073741823 + }, + "batch": { + "oneOf": [ + { + "title": "Batch", + "type": "object", + "properties": { + "batch": { + "$ref": "#/definitions/unistring" + } + }, + "required": [ + "batch" + ], + "additionalProperties": false + }, + { + "title": "Deposit", + "type": "object", + "properties": { + "deposit": { + "type": "object", + "properties": { + "destination": { + "oneOf": [ + { + "title": "Key", + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + { + "title": "Value", + "$ref": "#/definitions/Tx_rollup_l2_address" + } + ] + }, + "ticket_hash": { + "$ref": "#/definitions/script_expr" + }, + "amount": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "amount", + "ticket_hash", + "destination" + ], + "additionalProperties": false + } + }, + "required": [ + "deposit" + ], + "additionalProperties": false + } + ] + }, + "nonce": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "nonce", + "batch", + "batch_index", + "hash", + "level", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Sc_rollup_originate", "type": "object", @@ -2268,6 +2436,11 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, + "int64": { + "title": "64 bit integers", + "description": "Decimal representation of 64 bit integers", + "type": "string" + }, "micheline.alpha.michelson_v1.expression": { "oneOf": [ { @@ -2350,6 +2523,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "description": "Decimal representation of a positive big number", "type": "string" }, + "script_expr": { + "title": "A script expression ID (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "timestamp.protocol": { "description": "A timestamp as seen by the protocol: second-level precision, epoch based.", "$ref": "#/definitions/unistring" @@ -2475,10 +2652,199 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ] } }, + { + "description": { + "title": "X_4" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "size": "Int32", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 4, + "kind": "Float" + } + } + ], + "name": "Key" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "Tx_rollup_l2_address", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Value" + } + ] + } + }, + { + "description": { + "title": "X_3" + }, + "encoding": { + "fields": [ + { + "name": "destination", + "layout": { + "name": "X_4", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "ticket_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "amount", + "layout": { + "size": "Int64", + "kind": "Int" + }, + "data_kind": { + "size": 8, + "kind": "Float" + }, + "kind": "named" + } + ] + } + }, { "description": { "title": "X_5" }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "batch", + "layout": { + "kind": "String" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + } + ], + "name": "Batch" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "deposit", + "layout": { + "name": "X_3", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Deposit" + } + ] + } + }, + { + "description": { + "title": "X_8" + }, "encoding": { "tag_size": "Uint8", "kind": { @@ -2547,7 +2913,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_3" + "title": "X_6" }, "encoding": { "fields": [ @@ -2585,13 +2951,28 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "predecessor", "layout": { - "name": "X_5", + "name": "X_8", "kind": "Ref" }, "data_kind": { "kind": "Dynamic" }, "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "inbox_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" } ] } @@ -2906,7 +3287,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_6" + "title": "X_9" }, "encoding": { "fields": [ @@ -4321,7 +4702,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "parameters", "layout": { - "name": "X_6", + "name": "X_9", "kind": "Ref" }, "data_kind": { @@ -4611,20 +4992,183 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": "Uint30" }, { - "name": "value", + "name": "value", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + } + ], + "name": "Register_global_constant" + }, + { + "tag": 112, + "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" + }, + { + "kind": "option_indicator", + "name": "limit" + }, + { + "name": "limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Set_deposits_limit" + }, + { + "tag": 150, + "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": { - "kind": "Bytes" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "kind": "Variable" + "kind": "Dynamic" }, "kind": "named" } ], - "name": "Register_global_constant" + "name": "Tx_rollup_origination" }, { - "tag": 112, + "tag": 151, "fields": [ { "name": "Tag", @@ -4695,25 +5239,36 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "kind": "option_indicator", - "name": "limit" + "name": "rollup", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" }, { - "name": "limit", + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "content", "layout": { - "name": "N.t", - "kind": "Ref" + "kind": "String" }, "data_kind": { - "kind": "Dynamic" + "kind": "Variable" }, "kind": "named" } ], - "name": "Set_deposits_limit" + "name": "Tx_rollup_submit_batch" }, { - "tag": 150, + "tag": 152, "fields": [ { "name": "Tag", @@ -4782,12 +5337,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "Dynamic" }, "kind": "named" + }, + { + "name": "rollup", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "commitment", + "layout": { + "name": "X_6", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" } ], - "name": "Tx_rollup_origination" + "name": "Tx_rollup_commit" }, { - "tag": 151, + "tag": 153, "fields": [ { "name": "Tag", @@ -4867,27 +5444,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "Float" }, "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "name": "content", - "layout": { - "kind": "String" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" } ], - "name": "Tx_rollup_submit_batch" + "name": "Tx_rollup_return_bond" }, { - "tag": 152, + "tag": 154, "fields": [ { "name": "Tag", @@ -4969,18 +5531,66 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "commitment", + "name": "level", "layout": { - "name": "X_3", + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "batch_index", + "layout": { + "min": -1073741824, + "max": 1073741823, + "kind": "RangedInt" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "batch", + "layout": { + "name": "X_5", "kind": "Ref" }, "data_kind": { "kind": "Dynamic" }, "kind": "named" + }, + { + "name": "nonce", + "layout": { + "size": "Int64", + "kind": "Int" + }, + "data_kind": { + "size": 8, + "kind": "Float" + }, + "kind": "named" } ], - "name": "Tx_rollup_commit" + "name": "Tx_rollup_rejection" }, { "tag": 200, @@ -5930,6 +6540,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "title": "A Ed25519, Secp256k1, or P256 public key hash (Base58Check-encoded)", "$ref": "#/definitions/unistring" }, + "Tx_rollup_l2_address": { + "title": "The hash of a BLS public key used to identify a L2 ticket holders (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "alpha.block_header.alpha.full_header": { "title": "Shell header", "description": "Block header's shell-related content. It contains information such as the block level, its predecessor and timestamp.", @@ -7182,9 +7796,14 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "type": "null" } ] + }, + "inbox_hash": { + "type": "string", + "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, "required": [ + "inbox_hash", "predecessor", "batches", "level" @@ -7204,6 +7823,165 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Tx_rollup_return_bond", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_return_bond" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + } + }, + "required": [ + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Tx_rollup_rejection", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_rejection" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + }, + "level": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "hash": { + "$ref": "#/definitions/Commitment_hash" + }, + "batch_index": { + "type": "integer", + "minimum": -1073741824, + "maximum": 1073741823 + }, + "batch": { + "oneOf": [ + { + "title": "Batch", + "type": "object", + "properties": { + "batch": { + "$ref": "#/definitions/unistring" + } + }, + "required": [ + "batch" + ], + "additionalProperties": false + }, + { + "title": "Deposit", + "type": "object", + "properties": { + "deposit": { + "type": "object", + "properties": { + "destination": { + "oneOf": [ + { + "title": "Key", + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + { + "title": "Value", + "$ref": "#/definitions/Tx_rollup_l2_address" + } + ] + }, + "ticket_hash": { + "$ref": "#/definitions/script_expr" + }, + "amount": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "amount", + "ticket_hash", + "destination" + ], + "additionalProperties": false + } + }, + "required": [ + "deposit" + ], + "additionalProperties": false + } + ] + }, + "nonce": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "nonce", + "batch", + "batch_index", + "hash", + "level", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Sc_rollup_originate", "type": "object", @@ -7505,6 +8283,11 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, + "int64": { + "title": "64 bit integers", + "description": "Decimal representation of 64 bit integers", + "type": "string" + }, "micheline.alpha.michelson_v1.expression": { "oneOf": [ { @@ -7618,6 +8401,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "description": "Decimal representation of a positive big number", "type": "string" }, + "script_expr": { + "title": "A script expression ID (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "timestamp.protocol": { "description": "A timestamp as seen by the protocol: second-level precision, epoch based.", "$ref": "#/definitions/unistring" diff --git a/tezt/_regressions/rpc/alpha.client.others.out b/tezt/_regressions/rpc/alpha.client.others.out index e945ddd84cbcb748c56814357e51f4a23c82a927..a01256c93202ae7cae918d82b91bc720aecf896c 100644 --- a/tezt/_regressions/rpc/alpha.client.others.out +++ b/tezt/_regressions/rpc/alpha.client.others.out @@ -31,8 +31,12 @@ tezt/_regressions/rpc/alpha.client.others.out "cache_stake_distribution_cycles": 8, "cache_sampler_state_cycles": 8, "tx_rollup_enable": false, "tx_rollup_origination_size": 60000, "tx_rollup_hard_size_limit_per_inbox": 100000, - "tx_rollup_hard_size_limit_per_message": 5000, "sc_rollup_enable": false, - "sc_rollup_origination_size": 6314 } + "tx_rollup_hard_size_limit_per_message": 5000, + "tx_rollup_commitment_bond": "10000000000", + "tx_rollup_finality_period": 2000, + "tx_rollup_max_unfinalized_levels": 2100, + "tx_rollup_max_finalize_levels_per_commitment": 5, + "sc_rollup_enable": false, "sc_rollup_origination_size": 6314 } ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 2, "delegate": "[PUBLIC_KEY_HASH]", diff --git a/tezt/_regressions/rpc/alpha.light.others.out b/tezt/_regressions/rpc/alpha.light.others.out index b9f4dcb1d13875a830a5582ea62967807a8bcc94..97dc668b2ce18b455a556872c16ae13764ff9228 100644 --- a/tezt/_regressions/rpc/alpha.light.others.out +++ b/tezt/_regressions/rpc/alpha.light.others.out @@ -32,8 +32,12 @@ protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenes "cache_stake_distribution_cycles": 8, "cache_sampler_state_cycles": 8, "tx_rollup_enable": false, "tx_rollup_origination_size": 60000, "tx_rollup_hard_size_limit_per_inbox": 100000, - "tx_rollup_hard_size_limit_per_message": 5000, "sc_rollup_enable": false, - "sc_rollup_origination_size": 6314 } + "tx_rollup_hard_size_limit_per_message": 5000, + "tx_rollup_commitment_bond": "10000000000", + "tx_rollup_finality_period": 2000, + "tx_rollup_max_unfinalized_levels": 2100, + "tx_rollup_max_finalize_levels_per_commitment": 5, + "sc_rollup_enable": false, "sc_rollup_origination_size": 6314 } ./tezos-client --mode light rpc get /chains/main/blocks/head/helpers/baking_rights protocol of light mode unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im diff --git a/tezt/_regressions/rpc/alpha.proxy.mempool.out b/tezt/_regressions/rpc/alpha.proxy.mempool.out index 6ae0b149b08bc2f32d72c117aeffb836bd4dd0b1..fad54fc966a7b42bb1fe04fa535fcd51bdb95386 100644 --- a/tezt/_regressions/rpc/alpha.proxy.mempool.out +++ b/tezt/_regressions/rpc/alpha.proxy.mempool.out @@ -714,6 +714,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "title": "A Ed25519, Secp256k1, or P256 public key hash (Base58Check-encoded)", "$ref": "#/definitions/unistring" }, + "Tx_rollup_l2_address": { + "title": "The hash of a BLS public key used to identify a L2 ticket holders (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "alpha.block_header.alpha.full_header": { "title": "Shell header", "description": "Block header's shell-related content. It contains information such as the block level, its predecessor and timestamp.", @@ -1966,9 +1970,14 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "type": "null" } ] + }, + "inbox_hash": { + "type": "string", + "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, "required": [ + "inbox_hash", "predecessor", "batches", "level" @@ -1988,6 +1997,165 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Tx_rollup_return_bond", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_return_bond" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + } + }, + "required": [ + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Tx_rollup_rejection", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_rejection" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + }, + "level": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "hash": { + "$ref": "#/definitions/Commitment_hash" + }, + "batch_index": { + "type": "integer", + "minimum": -1073741824, + "maximum": 1073741823 + }, + "batch": { + "oneOf": [ + { + "title": "Batch", + "type": "object", + "properties": { + "batch": { + "$ref": "#/definitions/unistring" + } + }, + "required": [ + "batch" + ], + "additionalProperties": false + }, + { + "title": "Deposit", + "type": "object", + "properties": { + "deposit": { + "type": "object", + "properties": { + "destination": { + "oneOf": [ + { + "title": "Key", + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + { + "title": "Value", + "$ref": "#/definitions/Tx_rollup_l2_address" + } + ] + }, + "ticket_hash": { + "$ref": "#/definitions/script_expr" + }, + "amount": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "amount", + "ticket_hash", + "destination" + ], + "additionalProperties": false + } + }, + "required": [ + "deposit" + ], + "additionalProperties": false + } + ] + }, + "nonce": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "nonce", + "batch", + "batch_index", + "hash", + "level", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Sc_rollup_originate", "type": "object", @@ -2289,6 +2457,11 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, + "int64": { + "title": "64 bit integers", + "description": "Decimal representation of 64 bit integers", + "type": "string" + }, "micheline.alpha.michelson_v1.expression": { "oneOf": [ { @@ -2371,6 +2544,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "description": "Decimal representation of a positive big number", "type": "string" }, + "script_expr": { + "title": "A script expression ID (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "timestamp.protocol": { "description": "A timestamp as seen by the protocol: second-level precision, epoch based.", "$ref": "#/definitions/unistring" @@ -2496,10 +2673,199 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ] } }, + { + "description": { + "title": "X_4" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "size": "Int32", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 4, + "kind": "Float" + } + } + ], + "name": "Key" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "Tx_rollup_l2_address", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Value" + } + ] + } + }, + { + "description": { + "title": "X_3" + }, + "encoding": { + "fields": [ + { + "name": "destination", + "layout": { + "name": "X_4", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "ticket_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "amount", + "layout": { + "size": "Int64", + "kind": "Int" + }, + "data_kind": { + "size": 8, + "kind": "Float" + }, + "kind": "named" + } + ] + } + }, { "description": { "title": "X_5" }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "batch", + "layout": { + "kind": "String" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + } + ], + "name": "Batch" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "deposit", + "layout": { + "name": "X_3", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Deposit" + } + ] + } + }, + { + "description": { + "title": "X_8" + }, "encoding": { "tag_size": "Uint8", "kind": { @@ -2568,7 +2934,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_3" + "title": "X_6" }, "encoding": { "fields": [ @@ -2606,13 +2972,28 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "predecessor", "layout": { - "name": "X_5", + "name": "X_8", "kind": "Ref" }, "data_kind": { "kind": "Dynamic" }, "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "inbox_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" } ] } @@ -2927,7 +3308,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_6" + "title": "X_9" }, "encoding": { "fields": [ @@ -4342,7 +4723,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "parameters", "layout": { - "name": "X_6", + "name": "X_9", "kind": "Ref" }, "data_kind": { @@ -4632,20 +5013,183 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": "Uint30" }, { - "name": "value", + "name": "value", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + } + ], + "name": "Register_global_constant" + }, + { + "tag": 112, + "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" + }, + { + "kind": "option_indicator", + "name": "limit" + }, + { + "name": "limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Set_deposits_limit" + }, + { + "tag": 150, + "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": { - "kind": "Bytes" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "kind": "Variable" + "kind": "Dynamic" }, "kind": "named" } ], - "name": "Register_global_constant" + "name": "Tx_rollup_origination" }, { - "tag": 112, + "tag": 151, "fields": [ { "name": "Tag", @@ -4716,25 +5260,36 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "kind": "option_indicator", - "name": "limit" + "name": "rollup", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" }, { - "name": "limit", + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "content", "layout": { - "name": "N.t", - "kind": "Ref" + "kind": "String" }, "data_kind": { - "kind": "Dynamic" + "kind": "Variable" }, "kind": "named" } ], - "name": "Set_deposits_limit" + "name": "Tx_rollup_submit_batch" }, { - "tag": 150, + "tag": 152, "fields": [ { "name": "Tag", @@ -4803,12 +5358,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "Dynamic" }, "kind": "named" + }, + { + "name": "rollup", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "commitment", + "layout": { + "name": "X_6", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" } ], - "name": "Tx_rollup_origination" + "name": "Tx_rollup_commit" }, { - "tag": 151, + "tag": 153, "fields": [ { "name": "Tag", @@ -4888,27 +5465,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "Float" }, "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "name": "content", - "layout": { - "kind": "String" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" } ], - "name": "Tx_rollup_submit_batch" + "name": "Tx_rollup_return_bond" }, { - "tag": 152, + "tag": 154, "fields": [ { "name": "Tag", @@ -4990,18 +5552,66 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "commitment", + "name": "level", "layout": { - "name": "X_3", + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "batch_index", + "layout": { + "min": -1073741824, + "max": 1073741823, + "kind": "RangedInt" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "batch", + "layout": { + "name": "X_5", "kind": "Ref" }, "data_kind": { "kind": "Dynamic" }, "kind": "named" + }, + { + "name": "nonce", + "layout": { + "size": "Int64", + "kind": "Int" + }, + "data_kind": { + "size": 8, + "kind": "Float" + }, + "kind": "named" } ], - "name": "Tx_rollup_commit" + "name": "Tx_rollup_rejection" }, { "tag": 200, @@ -5951,6 +6561,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "title": "A Ed25519, Secp256k1, or P256 public key hash (Base58Check-encoded)", "$ref": "#/definitions/unistring" }, + "Tx_rollup_l2_address": { + "title": "The hash of a BLS public key used to identify a L2 ticket holders (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "alpha.block_header.alpha.full_header": { "title": "Shell header", "description": "Block header's shell-related content. It contains information such as the block level, its predecessor and timestamp.", @@ -7203,9 +7817,14 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "type": "null" } ] + }, + "inbox_hash": { + "type": "string", + "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, "required": [ + "inbox_hash", "predecessor", "batches", "level" @@ -7225,6 +7844,165 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' ], "additionalProperties": false }, + { + "title": "Tx_rollup_return_bond", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_return_bond" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + } + }, + "required": [ + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Tx_rollup_rejection", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "tx_rollup_rejection" + ] + }, + "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" + }, + "rollup": { + "$ref": "#/definitions/alpha.tx_rollup_id" + }, + "level": { + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + "hash": { + "$ref": "#/definitions/Commitment_hash" + }, + "batch_index": { + "type": "integer", + "minimum": -1073741824, + "maximum": 1073741823 + }, + "batch": { + "oneOf": [ + { + "title": "Batch", + "type": "object", + "properties": { + "batch": { + "$ref": "#/definitions/unistring" + } + }, + "required": [ + "batch" + ], + "additionalProperties": false + }, + { + "title": "Deposit", + "type": "object", + "properties": { + "deposit": { + "type": "object", + "properties": { + "destination": { + "oneOf": [ + { + "title": "Key", + "type": "integer", + "minimum": -2147483648, + "maximum": 2147483647 + }, + { + "title": "Value", + "$ref": "#/definitions/Tx_rollup_l2_address" + } + ] + }, + "ticket_hash": { + "$ref": "#/definitions/script_expr" + }, + "amount": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "amount", + "ticket_hash", + "destination" + ], + "additionalProperties": false + } + }, + "required": [ + "deposit" + ], + "additionalProperties": false + } + ] + }, + "nonce": { + "$ref": "#/definitions/int64" + } + }, + "required": [ + "nonce", + "batch", + "batch_index", + "hash", + "level", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, { "title": "Sc_rollup_originate", "type": "object", @@ -7526,6 +8304,11 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "pattern": "^([a-zA-Z0-9][a-zA-Z0-9])*$" } }, + "int64": { + "title": "64 bit integers", + "description": "Decimal representation of 64 bit integers", + "type": "string" + }, "micheline.alpha.michelson_v1.expression": { "oneOf": [ { @@ -7639,6 +8422,10 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "description": "Decimal representation of a positive big number", "type": "string" }, + "script_expr": { + "title": "A script expression ID (Base58Check-encoded)", + "$ref": "#/definitions/unistring" + }, "timestamp.protocol": { "description": "A timestamp as seen by the protocol: second-level precision, epoch based.", "$ref": "#/definitions/unistring" diff --git a/tezt/_regressions/rpc/alpha.proxy.others.out b/tezt/_regressions/rpc/alpha.proxy.others.out index 760067d3046d4c8f3794c8cee1c636c586c33a79..d3691de0dabcc859819fcdf3494b28f5d2e23f5f 100644 --- a/tezt/_regressions/rpc/alpha.proxy.others.out +++ b/tezt/_regressions/rpc/alpha.proxy.others.out @@ -32,8 +32,12 @@ protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGen "cache_stake_distribution_cycles": 8, "cache_sampler_state_cycles": 8, "tx_rollup_enable": false, "tx_rollup_origination_size": 60000, "tx_rollup_hard_size_limit_per_inbox": 100000, - "tx_rollup_hard_size_limit_per_message": 5000, "sc_rollup_enable": false, - "sc_rollup_origination_size": 6314 } + "tx_rollup_hard_size_limit_per_message": 5000, + "tx_rollup_commitment_bond": "10000000000", + "tx_rollup_finality_period": 2000, + "tx_rollup_max_unfinalized_levels": 2100, + "tx_rollup_max_finalize_levels_per_commitment": 5, + "sc_rollup_enable": false, "sc_rollup_origination_size": 6314 } ./tezos-client --mode proxy rpc get /chains/main/blocks/head/helpers/baking_rights protocol of proxy unspecified, using the node's protocol: ProtoGenesisGenesisGenesisGenesisGenesisGenesk612im diff --git a/tezt/_regressions/rpc/alpha.proxy_server.others.out b/tezt/_regressions/rpc/alpha.proxy_server.others.out index 6ec3dc57ad9568e2a39b9d58f8e0967a5d66274c..b1fe7ca966e0e14de39c7685dc14b53bd7ca2714 100644 --- a/tezt/_regressions/rpc/alpha.proxy_server.others.out +++ b/tezt/_regressions/rpc/alpha.proxy_server.others.out @@ -31,8 +31,12 @@ tezt/_regressions/rpc/alpha.proxy_server.others.out "cache_stake_distribution_cycles": 8, "cache_sampler_state_cycles": 8, "tx_rollup_enable": false, "tx_rollup_origination_size": 60000, "tx_rollup_hard_size_limit_per_inbox": 100000, - "tx_rollup_hard_size_limit_per_message": 5000, "sc_rollup_enable": false, - "sc_rollup_origination_size": 6314 } + "tx_rollup_hard_size_limit_per_message": 5000, + "tx_rollup_commitment_bond": "10000000000", + "tx_rollup_finality_period": 2000, + "tx_rollup_max_unfinalized_levels": 2100, + "tx_rollup_max_finalize_levels_per_commitment": 5, + "sc_rollup_enable": false, "sc_rollup_origination_size": 6314 } ./tezos-client rpc get /chains/main/blocks/head/helpers/baking_rights [ { "level": 3, "delegate": "[PUBLIC_KEY_HASH]", diff --git a/tezt/_regressions/tx_rollup_simple_use_case.out b/tezt/_regressions/tx_rollup_simple_use_case.out index 2d1c52bee4533fe2fd6fa926229bb07b039813bf..6e03b1ba931f784013f37939ad36aee550f844fb 100644 --- a/tezt/_regressions/tx_rollup_simple_use_case.out +++ b/tezt/_regressions/tx_rollup_simple_use_case.out @@ -2,11 +2,12 @@ tezt/_regressions/tx_rollup_simple_use_case.out tru1EL3YqhLS3kwni3ikbqMrui61fA5k7StHz ./tezos-client rpc get /chains/main/blocks/head/context/tx_rollup/tru1EL3YqhLS3kwni3ikbqMrui61fA5k7StHz/state -{ "last_inbox_level": null, "fees_per_byte": "0", "inbox_ema": 0 } +{ "first_unfinalized_level": null, "unfinalized_level_count": 0, + "fees_per_byte": "0", "inbox_ema": 0, "last_inbox_level": null } ./tezos-client --wait none submit tx rollup batch 74657a6f73 to tru1EL3YqhLS3kwni3ikbqMrui61fA5k7StHz from '[PUBLIC_KEY_HASH]' Node is bootstrapped. -Estimated gas: 2440.220 units (will add 100 for safety) +Estimated gas: 2640.482 units (will add 100 for safety) Estimated storage: no bytes added Operation successfully injected in the node. Operation hash is '[OPERATION_HASH]' @@ -17,18 +18,18 @@ and/or an external block explorer to make sure that it has been included. This sequence of operations was run: Manager signed operations: From: [PUBLIC_KEY_HASH] - Fee to the baker: ꜩ0.000508 + Fee to the baker: ꜩ0.000528 Expected counter: 2 - Gas limit: 2541 + Gas limit: 2741 Storage limit: 0 bytes Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ0.000508 - payload fees(the block proposer) ....... +ꜩ0.000508 + [PUBLIC_KEY_HASH] ... -ꜩ0.000528 + payload fees(the block proposer) ....... +ꜩ0.000528 Tx rollup transaction:tru1EL3YqhLS3kwni3ikbqMrui61fA5k7StHz, 5 bytes, From: [PUBLIC_KEY_HASH] This tx rollup submit operation was successfully applied Balance updates: - Consumed gas: 2440.220 + Consumed gas: 2640.482 ./tezos-client rpc get /chains/main/blocks/head/context/tx_rollup/tru1EL3YqhLS3kwni3ikbqMrui61fA5k7StHz/inbox diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index 46070d9232ca4f2a2431a376c21151cb2c39b3fc..43c659ec0e2753b301988a55ab6d2adf6374805c 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -645,6 +645,32 @@ module Tx_rollup = struct ~tx_rollup client = let path = sub_path ~chain ~block ~tx_rollup "commitments" in Client.rpc ?endpoint ?hooks GET path client + + let sub_pkh_path ?(chain = "main") ?(block = "head") ~tx_rollup + ~public_key_hash sub = + [ + "chains"; + chain; + "blocks"; + block; + "context"; + "tx_rollup"; + tx_rollup; + sub; + public_key_hash; + ] + + let get_pending_bonded_commitments ?endpoint ?hooks ?(chain = "main") + ?(block = "head") ~tx_rollup ~public_key_hash client = + let path = + sub_pkh_path + ~chain + ~block + ~tx_rollup + ~public_key_hash + "pending_bonded_commitments" + in + Client.rpc ?endpoint ?hooks GET path client end module Sc_rollup = struct diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index 0ca2f70cc4dfe5d02fb5d649e04fa0b3021cbed2..320d3347b6ddc2a90f6395bd447057fb93094eff 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -1016,6 +1016,17 @@ module Tx_rollup : sig tx_rollup:string -> Client.t -> JSON.t Lwt.t + + (** Call RPC /chain/[chain]/blocks/[block]/context/[rollup_hash]/pending_bonded_commitments *) + val get_pending_bonded_commitments : + ?endpoint:Client.endpoint -> + ?hooks:Process.hooks -> + ?chain:string -> + ?block:string -> + tx_rollup:string -> + public_key_hash:string -> + Client.t -> + JSON.t Lwt.t end module Sc_rollup : sig diff --git a/tezt/lib_tezos/client.ml b/tezt/lib_tezos/client.ml index 3bfe847cd4bc1bf82785aa48cf04b70218225fb6..26acf952f9a52670b49ae106898f3c1949d1ac4f 100644 --- a/tezt/lib_tezos/client.ml +++ b/tezt/lib_tezos/client.ml @@ -1147,6 +1147,8 @@ let spawn_submit_tx_rollup_batch ?(wait = "none") ?burn_cap ?storage_limit tx_rollup; "from"; src; + "--fee-cap"; + "10000000000"; ] @ Option.fold ~none:[] diff --git a/tezt/tests/tx_rollup.ml b/tezt/tests/tx_rollup.ml index 95d650a93976c3ff60a2522616a781fa2976f0e9..d43bd5cf3ff60ff448bd0d43e2b6a4e778ba9641 100644 --- a/tezt/tests/tx_rollup.ml +++ b/tezt/tests/tx_rollup.ml @@ -155,6 +155,8 @@ let test_submit_batch ~protocols = ~content:batch ~tx_rollup ~src:Constant.bootstrap1.public_key_hash + ~burn_cap:Tez.(of_int 9999999) + ~storage_limit:60_000 client in let* () = Client.bake_for client in @@ -251,6 +253,8 @@ let test_submit_from_originated_source ~protocols = ~content:batch ~tx_rollup ~src:originated_contract + ~burn_cap:Tez.(of_int 9999999) + ~storage_limit:60_000 client |> Process.check_error ~exit_code:1 @@ -340,6 +344,7 @@ let test_tx_node_is_ready = unit) let test_tx_node_store_inbox = + (* HERE HERE HERE *) let open Tezt_tezos in test_with_setup ~__FILE__ @@ -376,6 +381,8 @@ let test_tx_node_store_inbox = ~content:batch ~tx_rollup:tx_rollup_hash ~src:Constant.bootstrap1.public_key_hash + ~burn_cap:Tez.(of_int 9999999) + ~storage_limit:60_000 client in let* () = Client.bake_for client in @@ -399,7 +406,7 @@ let test_tx_node_store_inbox = node_inbox.contents inbox.contents ~error_msg: - "Content of inboxes on the client side should be equal to the \ + "Content of inboxes on the client side should be equal to the \ content of inboxes on the node side" (list string)) ; @@ -411,22 +418,11 @@ let test_tx_node_store_inbox = ~content:snd_batch ~tx_rollup:tx_rollup_hash ~src:Constant.bootstrap1.public_key_hash + ~burn_cap:Tez.(of_int 99999999) + ~storage_limit:65_000_000 client in - let* () = Client.bake_for client in - let* _ = Node.wait_for_level node 4 in - let* node_inbox = get_node_inbox tx_node in - let* inbox = get_inbox ~hooks tx_rollup_hash client in - (* Enusre that stored inboxes on daemon side are equivalent of inboxes - returned by the rpc call. *) - assert (Int.equal node_inbox.cumulated_size inbox.cumulated_size) ; - assert (List.equal String.equal node_inbox.contents inbox.contents) ; + unit) -let register ~protocols = - test_submit_batch ~protocols ; - test_invalid_rollup_address ~protocols ; - test_submit_from_originated_source ~protocols ; - test_node_configuration ~protocols ; - test_tx_node_is_ready ~protocols ; - test_tx_node_store_inbox ~protocols +let register ~protocols = test_tx_node_store_inbox ~protocols