diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index c8667e7331fd69998d79c1b7909f26c281c4e860..6304cbea30a2dac242e7d4e0d0d183569c30a234 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2837,6 +2837,12 @@ module Dal : sig module Endorsement : sig type t + type operation = { + endorser : public_key_hash; + slot_availability : t; + level : Raw_level.t; + } + type shard_index = int module Shard_map : Map.S with type key = shard_index @@ -2990,6 +2996,14 @@ module Dal_errors : sig endorser : Signature.Public_key_hash.t; level : Level.t; } + | Dal_operation_for_old_level of { + current : Raw_level.t; + given : Raw_level.t; + } + | Dal_operation_for_future_level of { + current : Raw_level.t; + given : Raw_level.t; + } end (** This module re-exports definitions from {!Sc_rollup_storage} and @@ -4318,7 +4332,7 @@ and _ contents = | Preendorsement : consensus_content -> Kind.preendorsement contents | Endorsement : consensus_content -> Kind.endorsement contents | Dal_slot_availability : - public_key_hash * Dal.Endorsement.t + Dal.Endorsement.operation -> Kind.dal_slot_availability contents | Seed_nonce_revelation : { level : Raw_level.t; diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 1a6144dd2ce6e8ff18f23cbc9328e24b7a877ee3..fcdb6314116d6cfcacbfb96aa4c4a5e62dec1519 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2159,7 +2159,7 @@ let apply_contents_list (type kind) ctxt chain_id (mode : mode) record_preendorsement ctxt mode consensus_content |> Lwt.return | Single (Endorsement consensus_content) -> record_endorsement ctxt mode consensus_content - | Single (Dal_slot_availability (endorser, slot_availability)) -> + | Single (Dal_slot_availability op) -> (* DAL/FIXME https://gitlab.com/tezos/tezos/-/issues/3115 This is a temporary operation. We do no check for the @@ -2170,11 +2170,11 @@ let apply_contents_list (type kind) ctxt chain_id (mode : mode) endorsement encoding. However, once the DAL will be ready, this operation should be merged with an endorsement or at least refined. *) - Dal_apply.apply_data_availability ctxt slot_availability ~endorser - >>?= fun ctxt -> + Dal_apply.apply_data_availability ctxt op >>?= fun ctxt -> return ( ctxt, - Single_result (Dal_slot_availability_result {delegate = endorser}) ) + Single_result (Dal_slot_availability_result {delegate = op.endorser}) + ) | Single (Seed_nonce_revelation {level; nonce}) -> let level = Level.from_raw ctxt level in Nonce.reveal ctxt level nonce >>=? fun ctxt -> diff --git a/src/proto_alpha/lib_protocol/dal_apply.ml b/src/proto_alpha/lib_protocol/dal_apply.ml index 617e99089a7aa58eab5294f50737da81b0493aba..0bdd88d46e69cb07193ae61e7dd19512fe395764 100644 --- a/src/proto_alpha/lib_protocol/dal_apply.ml +++ b/src/proto_alpha/lib_protocol/dal_apply.ml @@ -44,26 +44,42 @@ let slot_of_int_e n = | None -> fail Dal_errors.Dal_slot_index_above_hard_limit | Some slot_index -> return slot_index -let validate_data_availability ctxt data_availability = +let validate_data_availability ctxt op = assert_dal_feature_enabled ctxt >>? fun () -> let open Tzresult_syntax in + (* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/4163 + check the signature of the endorser as well *) + let Dal.Endorsement.{endorser = _; slot_availability; level = given} = op in let* max_index = slot_of_int_e @@ ((Constants.parametric ctxt).dal.number_of_slots - 1) in let maximum_size = Dal.Endorsement.expected_size_in_bits ~max_index in - let size = Dal.Endorsement.occupied_size_in_bits data_availability in - error_unless - Compare.Int.(size <= maximum_size) - (Dal_endorsement_size_limit_exceeded {maximum_size; got = size}) + let size = Dal.Endorsement.occupied_size_in_bits slot_availability in + let* () = + error_unless + Compare.Int.(size <= maximum_size) + (Dal_endorsement_size_limit_exceeded {maximum_size; got = size}) + in + let current = Level.(current ctxt).level in + let delta_levels = Raw_level.diff current given in + let* () = + error_when + Compare.Int32.(delta_levels > 0l) + (Dal_operation_for_old_level {current; given}) + in + error_when + Compare.Int32.(delta_levels < 0l) + (Dal_operation_for_future_level {current; given}) -let apply_data_availability ctxt data_availability ~endorser = +let apply_data_availability ctxt op = assert_dal_feature_enabled ctxt >>? fun () -> + let Dal.Endorsement.{endorser; slot_availability; level = _} = op in match Dal.Endorsement.shards_of_endorser ctxt ~endorser with | None -> let level = Level.current ctxt in error (Dal_data_availibility_endorser_not_in_committee {endorser; level}) | Some shards -> - Ok (Dal.Endorsement.record_available_shards ctxt data_availability shards) + Ok (Dal.Endorsement.record_available_shards ctxt slot_availability shards) let validate_publish_slot_header ctxt Dal.Slot.Header.{id = {index; _}; _} = assert_dal_feature_enabled ctxt >>? fun () -> diff --git a/src/proto_alpha/lib_protocol/dal_apply.mli b/src/proto_alpha/lib_protocol/dal_apply.mli index 6d7b5ff1b2ce437283635ce92afac85dd6645f81..241ab959df6a515672b7d197b08a52aaeccb04d0 100644 --- a/src/proto_alpha/lib_protocol/dal_apply.mli +++ b/src/proto_alpha/lib_protocol/dal_apply.mli @@ -28,17 +28,16 @@ open Alpha_context -(** [validate_data_availability ctxt endorsement] ensures the - [endorsement] is valid and cannot prevent an operation containing - [endorsement] to be refused on top of [ctxt]. If an [Error _] is - returned, the [endorsement] is not valid. *) -val validate_data_availability : t -> Dal.Endorsement.t -> unit tzresult +(** [validate_data_availability ctxt op] ensures that + [op.slot_availability] is valid and cannot prevent an operation containing + [op.slot_availability] to be refused on top of [ctxt]. If an [Error _] is + returned, the [op.slot_availability] is not valid. *) +val validate_data_availability : t -> Dal.Endorsement.operation -> unit tzresult -(** [apply_data_availability ctxt endorsement ~endorser] applies - [endorsement] into the [ctxt] assuming [endorser] issued those +(** [apply_data_availability ctxt op] applies + [op.slot_availability] into the [ctxt] assuming [op.endorser] issued those endorsements. *) -val apply_data_availability : - t -> Dal.Endorsement.t -> endorser:Signature.Public_key_hash.t -> t tzresult +val apply_data_availability : t -> Dal.Endorsement.operation -> t tzresult (** [validate_publish_slot_header ctxt slot] ensures that [slot_header] is valid and cannot prevent an operation containing [slot_header] to be diff --git a/src/proto_alpha/lib_protocol/dal_endorsement_repr.ml b/src/proto_alpha/lib_protocol/dal_endorsement_repr.ml index 3ab3a78b6ff7553ef7d4c37751600ae76ffa2d8f..7291c37eeba45af2a3eab9315790dd83d9fe0ddd 100644 --- a/src/proto_alpha/lib_protocol/dal_endorsement_repr.ml +++ b/src/proto_alpha/lib_protocol/dal_endorsement_repr.ml @@ -43,6 +43,15 @@ type t = Bitset.t type available_slots = t +type operation = { + endorser : Signature.Public_key_hash.t; + (* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/4165 + Compute this from the endorsed slots in [slot_availability] below, + or provide a field `min_endorser_slot : int / int32` *) + slot_availability : t; + level : Raw_level_repr.t; +} + let encoding = Bitset.encoding let empty = Bitset.empty diff --git a/src/proto_alpha/lib_protocol/dal_endorsement_repr.mli b/src/proto_alpha/lib_protocol/dal_endorsement_repr.mli index 0a47007c346af0a05d553180546369291dda46af..597f55768fad13ba9fa540b20050fe84ad0085bb 100644 --- a/src/proto_alpha/lib_protocol/dal_endorsement_repr.mli +++ b/src/proto_alpha/lib_protocol/dal_endorsement_repr.mli @@ -44,6 +44,17 @@ type t type available_slots = t +(** The shape of Dal endorsement operations injected by delegates. *) +type operation = { + endorser : Signature.Public_key_hash.t; + (** The endorser who attests the availability of the slots. *) + slot_availability : t; + (** The bitset of slots that are attested to be available. *) + level : Raw_level_repr.t; + (** The level at which the operation is valid. It should be equal to the + attested slot's published level plus the DAL endorsement lag. *) +} + val encoding : t Data_encoding.t (** [empty] returns an empty [slot_endorsement] which commits that diff --git a/src/proto_alpha/lib_protocol/dal_errors_repr.ml b/src/proto_alpha/lib_protocol/dal_errors_repr.ml index f41922589142ddbdbb9a0b430b170f4eecabcd2f..639a47a51b8205012a6ea46a30fb9b0c5b1d62e0 100644 --- a/src/proto_alpha/lib_protocol/dal_errors_repr.ml +++ b/src/proto_alpha/lib_protocol/dal_errors_repr.ml @@ -36,14 +36,18 @@ type error += } | Dal_endorsement_size_limit_exceeded of {maximum_size : int; got : int} | Dal_publish_slot_header_duplicate of {slot_header : Dal_slot_repr.Header.t} - | Dal_rollup_already_registered_to_slot_index of - (Sc_rollup_repr.t * Dal_slot_repr.Index.t) - | Dal_requested_subscriptions_at_future_level of - (Raw_level_repr.t * Raw_level_repr.t) | Dal_data_availibility_endorser_not_in_committee of { endorser : Signature.Public_key_hash.t; level : Level_repr.t; } + | Dal_operation_for_old_level of { + current : Raw_level_repr.t; + given : Raw_level_repr.t; + } + | Dal_operation_for_future_level of { + current : Raw_level_repr.t; + given : Raw_level_repr.t; + } let () = let open Data_encoding in @@ -172,56 +176,47 @@ let () = | _ -> None) (fun slot_header -> Dal_publish_slot_header_duplicate {slot_header}) ; register_error_kind - `Permanent - ~id:"Dal_rollup_already_subscribed_to_slot" - ~title:"DAL rollup already subscribed to slot" - ~description - ~pp:(fun ppf (rollup, slot_index) -> + `Outdated + ~id:"Dal_operation_for_old_level" + ~title:"Dal operation for an old level" + ~description:"The Dal operation targets an old level" + ~pp:(fun ppf (current_lvl, given_lvl) -> Format.fprintf ppf - "Rollup %a is already subscribed to data availability slot index %a" - Sc_rollup_repr.pp - rollup - Dal_slot_repr.Index.pp - slot_index) + "Dal operation targets an old level %a. Current level is %a." + Raw_level_repr.pp + given_lvl + Raw_level_repr.pp + current_lvl) Data_encoding.( obj2 - (req "rollup" Sc_rollup_repr.encoding) - (req "slot_index" Dal_slot_repr.Index.encoding)) + (req "current_level" Raw_level_repr.encoding) + (req "given_level" Raw_level_repr.encoding)) (function - | Dal_rollup_already_registered_to_slot_index (rollup, slot_index) -> - Some (rollup, slot_index) + | Dal_operation_for_old_level {current; given} -> Some (current, given) | _ -> None) - (fun (rollup, slot_index) -> - Dal_rollup_already_registered_to_slot_index (rollup, slot_index)) ; - let description = - "Requested List of subscribed rollups to slot at a future level" - in + (fun (current, given) -> Dal_operation_for_old_level {current; given}) ; register_error_kind `Temporary - ~id:"Dal_requested_subscriptions_at_future_level" - ~title:"Requested list of subscribed dal slots at a future level" - ~description - ~pp:(fun ppf (current_level, future_level) -> + ~id:"Dal_operation_for_future_level" + ~title:"Dal operation for a future level" + ~description:"The Dal operation target a future level" + ~pp:(fun ppf (current_lvl, given_lvl) -> Format.fprintf ppf - "The list of subscribed dal slot indices has been requested for level \ - %a, but the current level is %a" + "Dal operation targets a future level %a. Current level is %a." Raw_level_repr.pp - future_level + given_lvl Raw_level_repr.pp - current_level) + current_lvl) Data_encoding.( obj2 (req "current_level" Raw_level_repr.encoding) - (req "future_level" Raw_level_repr.encoding)) + (req "given_level" Raw_level_repr.encoding)) (function - | Dal_requested_subscriptions_at_future_level (current_level, future_level) - -> - Some (current_level, future_level) + | Dal_operation_for_future_level {current; given} -> Some (current, given) | _ -> None) - (fun (current_level, future_level) -> - Dal_requested_subscriptions_at_future_level (current_level, future_level)) ; + (fun (current, given) -> Dal_operation_for_future_level {current; given}) ; register_error_kind `Permanent ~id:"Dal_data_availibility_endorser_not_in_committee" diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index 74a3116c6c6273443d7012e667e261928f290891..598aa67883570ce44188069e52651880e4ebd945 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -272,7 +272,7 @@ and _ contents = | Preendorsement : consensus_content -> Kind.preendorsement contents | Endorsement : consensus_content -> Kind.endorsement contents | Dal_slot_availability : - Signature.Public_key_hash.t * Dal_endorsement_repr.t + Dal_endorsement_repr.operation -> Kind.dal_slot_availability contents | Seed_nonce_revelation : { level : Raw_level_repr.t; @@ -1368,9 +1368,10 @@ module Encoding = struct (varopt "signature" Signature.encoding))) let dal_slot_availability_encoding = - obj2 + obj3 (req "endorser" Signature.Public_key_hash.encoding) (req "endorsement" Dal_endorsement_repr.encoding) + (req "level" Raw_level_repr.encoding) let dal_slot_availability_case = Case @@ -1382,11 +1383,13 @@ module Encoding = struct (function | Contents (Dal_slot_availability _ as op) -> Some op | _ -> None); proj = - (fun (Dal_slot_availability (endorser, endorsement)) -> - (endorser, endorsement)); + (fun (Dal_slot_availability + Dal_endorsement_repr.{endorser; slot_availability; level}) -> + (endorser, slot_availability, level)); inj = - (fun (endorser, endorsement) -> - Dal_slot_availability (endorser, endorsement)); + (fun (endorser, slot_availability, level) -> + Dal_slot_availability + Dal_endorsement_repr.{endorser; slot_availability; level}); } let seed_nonce_revelation_case = @@ -2251,7 +2254,8 @@ type _ weight = | Weight_endorsement : endorsement_infos -> consensus_pass_type weight | Weight_preendorsement : endorsement_infos -> consensus_pass_type weight | Weight_dal_slot_availability : - int * Signature.Public_key_hash.t + (* endorser * num_attestations * level *) + (Signature.Public_key_hash.t * int * int32) -> consensus_pass_type weight | Weight_proposals : int32 * Signature.Public_key_hash.t @@ -2351,12 +2355,15 @@ let weight_of : packed_operation -> operation_weight = ( Consensus, Weight_endorsement (endorsement_infos_from_consensus_content consensus_content) ) - | Single (Dal_slot_availability (endorser, endorsements)) -> + | Single + (Dal_slot_availability + Dal_endorsement_repr.{endorser; slot_availability; level}) -> W ( Consensus, Weight_dal_slot_availability - (Dal_endorsement_repr.occupied_size_in_bits endorsements, endorser) - ) + ( endorser, + Dal_endorsement_repr.occupied_size_in_bits slot_availability, + Raw_level_repr.to_int32 level ) ) | Single (Proposals {period; source; _}) -> W (Voting, Weight_proposals (period, source)) | Single (Ballot {period; source; _}) -> @@ -2486,13 +2493,16 @@ let compare_baking_infos infos1 infos2 = (** Two valid {!Dal_slot_availability} are compared in the lexicographic order of their pairs of bitsets size and endorser hash. *) -let compare_dal_slot_availability (endorsements1, endorser1) - (endorsements2, endorser2) = +let compare_dal_slot_availability (endorser1, endorsements1, level1) + (endorser2, endorsements2, level2) = compare_pair_in_lexico_order - ~cmp_fst:Compare.Int.compare + ~cmp_fst: + (compare_pair_in_lexico_order + ~cmp_fst:Compare.Int32.compare + ~cmp_snd:Compare.Int.compare) ~cmp_snd:Signature.Public_key_hash.compare - (endorsements1, endorser1) - (endorsements2, endorser2) + ((level1, endorsements1), endorser1) + ((level2, endorsements2), endorser2) (** {4 Comparison of valid operations of the same validation pass} *) @@ -2515,9 +2525,11 @@ let compare_consensus_weight w1 w2 = compare_endorsement_infos ~prioritized_position:Fstpos infos1 infos2 | Weight_preendorsement infos1, Weight_endorsement infos2 -> compare_endorsement_infos ~prioritized_position:Sndpos infos1 infos2 - | ( Weight_dal_slot_availability (size1, endorser1), - Weight_dal_slot_availability (size2, endorser2) ) -> - compare_dal_slot_availability (size1, endorser1) (size2, endorser2) + | ( Weight_dal_slot_availability (endorser1, size1, lvl1), + Weight_dal_slot_availability (endorser2, size2, lvl2) ) -> + compare_dal_slot_availability + (endorser1, size1, lvl1) + (endorser2, size2, lvl2) | ( Weight_dal_slot_availability _, (Weight_endorsement _ | Weight_preendorsement _) ) -> -1 diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index e40dba3e0f92dfe59289ed39581633398bf44472..a52ff693eb4fc248c887ad5d7db41412e71ae849 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -265,7 +265,7 @@ and _ contents = Temporary operation to avoid modifying endorsement encoding. *) | Dal_slot_availability : - Signature.Public_key_hash.t * Dal_endorsement_repr.t + Dal_endorsement_repr.operation -> Kind.dal_slot_availability contents (* Seed_nonce_revelation: Nonces are created by bakers and are combined to create pseudo-random seeds. Bakers are urged to reveal their diff --git a/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml b/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml index 6c5247c65806021f7b81fbd2e56ecf7b3d7db2f9..1a954c098f956f936cd977f25686db8a6dda043d 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/operation_generator.ml @@ -400,8 +400,14 @@ let generate_endorsement = let generate_dal_slot = let open QCheck2.Gen in - let+ pkh = random_pkh in - Dal_slot_availability (pkh, Dal.Endorsement.empty) + let+ endorser = random_pkh in + Dal_slot_availability + Dal.Endorsement. + { + endorser; + slot_availability = Dal.Endorsement.empty; + level = Raw_level.root; + } let generate_vdf_revelation = let open QCheck2.Gen in diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml index 6bb880073e97597502b805daacd90817596a619e..8e38b709b86c0f0c35213811b0850fdd5b80be82 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/generator_descriptors.ml @@ -650,8 +650,10 @@ let dal_slot_availibility ctxt delegate = | None -> return_none | Some _interval -> (* The content of the endorsement does not matter for covalidity. *) - let attestation = Dal.Endorsement.empty in - return_some (Dal_slot_availability (delegate, attestation)) + let slot_availability = Dal.Endorsement.empty in + let level = Raw_level.succ level.Level.level in + return_some + (Dal_slot_availability {endorser = delegate; slot_availability; level}) let dal_slot_availability_descriptor = let open Lwt_result_syntax in diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 6fc8437e721ef7a89706ad76af0ea03777081f5c..98ba4f7520b1f5d1bbd0fa8fb9006e5355c3b2e4 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -1059,18 +1059,20 @@ module Consensus = struct operation should be merged with an endorsement or at least refined. *) let open Lwt_tzresult_syntax in - let (Single (Dal_slot_availability (_endorser, slot_availability))) = + let (Single (Dal_slot_availability op)) = operation.protocol_data.contents in let*? () = (* Note that this function checks the dal feature flag. *) - Dal_apply.validate_data_availability vi.ctxt slot_availability + Dal_apply.validate_data_availability vi.ctxt op in return_unit let check_dal_slot_availability_conflict vs oph (operation : Kind.dal_slot_availability operation) = - let (Single (Dal_slot_availability (endorser, _slot_availability))) = + let (Single + (Dal_slot_availability {endorser; slot_availability = _; level = _})) + = operation.protocol_data.contents in match @@ -1092,7 +1094,9 @@ module Consensus = struct let add_dal_slot_availability vs oph (operation : Kind.dal_slot_availability operation) = - let (Single (Dal_slot_availability (endorser, _slot_availability))) = + let (Single + (Dal_slot_availability {endorser; slot_availability = _; level = _})) + = operation.protocol_data.contents in { @@ -1110,7 +1114,9 @@ module Consensus = struct let remove_dal_slot_availability vs (operation : Kind.dal_slot_availability operation) = - let (Single (Dal_slot_availability (endorser, _slot_availability))) = + let (Single + (Dal_slot_availability {endorser; slot_availability = _; level = _})) + = operation.protocol_data.contents in let dal_slot_availability_seen = diff --git a/tezt/lib_tezos/operation_core.ml b/tezt/lib_tezos/operation_core.ml index 5f5bfe9ea6fbff677e3de09945046d85eba50fc0..575479ff13ef9f25d2d300d978650a12b0345c44 100644 --- a/tezt/lib_tezos/operation_core.ml +++ b/tezt/lib_tezos/operation_core.ml @@ -173,12 +173,13 @@ let make_run_operation_input ?chain_id t client = ]) module Consensus = struct - type t = Slot_availability of {endorsement : bool array} + type t = Slot_availability of {endorsement : bool array; level : int} - let slot_availability ~endorsement = Slot_availability {endorsement} + let slot_availability ~endorsement ~level = + Slot_availability {endorsement; level} let json signer = function - | Slot_availability {endorsement} -> + | Slot_availability {endorsement; level} -> let string_of_bool_vector endorsement = let aux (acc, n) b = let bit = if b then 1 else 0 in @@ -191,6 +192,7 @@ module Consensus = struct ("kind", Ezjsonm.string "dal_slot_availability"); ("endorser", Ezjsonm.string signer.Account.public_key_hash); ("endorsement", Ezjsonm.string (string_of_bool_vector endorsement)); + ("level", Ezjsonm.int level); ] let operation ?branch ?chain_id ~signer consensus_operation client = diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index 97369b565f65215e9346a81b18881db2d02b8276..5da321250d6695c9110e2a0ccb0afbbacf2bb589 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -156,10 +156,12 @@ module Consensus : sig (** A representation of a consensus operation. *) type t - (** [slot_availability ~endorsement] crafts a data-availability - consensus to endorse slot headers. For each slot, the value of - the booleans indicates whether the data is available. *) - val slot_availability : endorsement:bool array -> t + (** [slot_availability ~endorsement ~level] crafts a data-availability + consensus to endorse at [level] slot headers published at level + [level - endorsement_lag]. + For each slot, the value of the booleans indicates whether the data is + available. *) + val slot_availability : endorsement:bool array -> level:int -> t (** [operation] constructs an operation from a consensus operation. the [client] is used to fetch the branch and the diff --git a/tezt/lib_tezos/rollup.ml b/tezt/lib_tezos/rollup.ml index 47882229ee75d0ba97680b82fe0042a767470b5b..bc0ec7b5bbd492461dc39d14c4a1c3b34614990b 100644 --- a/tezt/lib_tezos/rollup.ml +++ b/tezt/lib_tezos/rollup.ml @@ -507,7 +507,11 @@ module Dal = struct number_of_shards : int; } - type t = {cryptobox : cryptobox; number_of_slots : int} + type t = { + cryptobox : cryptobox; + number_of_slots : int; + endorsement_lag : int; + } let parameter_file protocol = let args = [(["dal_parametric"; "feature_enable"], `Bool true)] in @@ -523,12 +527,14 @@ module Dal = struct let slot_size = JSON.(json |-> "slot_size" |> as_int) in let page_size = JSON.(json |-> "page_size" |> as_int) in let number_of_slots = JSON.(json |-> "number_of_slots" |> as_int) in + let endorsement_lag = JSON.(json |-> "endorsement_lag" |> as_int) in return { cryptobox = Tezos_crypto_dal.Cryptobox.Verifier. {number_of_shards; redundancy_factor; slot_size; page_size}; number_of_slots; + endorsement_lag; } end diff --git a/tezt/lib_tezos/rollup.mli b/tezt/lib_tezos/rollup.mli index 2f3b8ae0f7a4a779e3755c980436f3047d5b6f27..602c9bfc5b9b60420311ad605f920779e848f136 100644 --- a/tezt/lib_tezos/rollup.mli +++ b/tezt/lib_tezos/rollup.mli @@ -223,7 +223,11 @@ module Dal : sig number_of_shards : int; } - type t = {cryptobox : cryptobox; number_of_slots : int} + type t = { + cryptobox : cryptobox; + number_of_slots : int; + endorsement_lag : int; + } val parameter_file : Protocol.t -> string Lwt.t diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index ce3d7e1de7bc52b08733adc89e13ec14f1ca4d5d..48447651d9fa58df8724cd5bdc62eb1400b71d02 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -54,11 +54,15 @@ let regression_test ~__FILE__ ?(tags = []) title f = let dal_enable_param dal_enable = make_bool_parameter ["dal_parametric"; "feature_enable"] dal_enable -let setup ?commitment_period ?challenge_window ?dal_enable f ~protocol = +let setup ?(endorsement_lag = 1) ?commitment_period ?challenge_window + ?dal_enable f ~protocol = let parameters = make_int_parameter - ["sc_rollup_commitment_period_in_blocks"] - commitment_period + ["dal_parametric"; "endorsement_lag"] + (Some endorsement_lag) + @ make_int_parameter + ["sc_rollup_commitment_period_in_blocks"] + commitment_period @ make_int_parameter ["sc_rollup_challenge_window_in_blocks"] challenge_window @@ -125,7 +129,7 @@ let with_fresh_rollup ?dal_node f tezos_node tezos_client bootstrap1_key = let* configuration_filename = Sc_rollup_node.config_init sc_rollup_node rollup_address in - let* () = Client.bake_for tezos_client in + let* () = Client.bake_for_and_wait tezos_client in f rollup_address sc_rollup_node configuration_filename let with_dal_node tezos_node f key = @@ -243,12 +247,15 @@ let test_feature_flag _protocol _sc_rollup_node _sc_rollup_address node client = ~msg:(rex "Data-availability layer will be enabled in a future proposal") process in + let level = Node.get_level node + 1 in let* (`OpHash oph1) = Operation.Consensus.( inject ~force:true ~signer:Constant.bootstrap1 - (slot_availability ~endorsement:(Array.make number_of_slots false)) + (slot_availability + ~level + ~endorsement:(Array.make number_of_slots false)) client) in let* (`OpHash oph2) = @@ -306,10 +313,19 @@ let publish_slot_header ~source ?(fee = 1200) ~index ~commitment node client = ] client) -let slot_availability ~signer ~nb_slots availability client = +let slot_availability ?level ?(force = false) ~signer ~nb_slots availability + client = let endorsement = Array.make nb_slots false in List.iter (fun i -> endorsement.(i) <- true) availability ; - Operation.Consensus.(inject ~signer (slot_availability ~endorsement) client) + let* level = + match level with + | Some level -> return level + | None -> + let* level = Client.level client in + return @@ (level + 1) + in + Operation.Consensus.( + inject ~force ~signer (slot_availability ~level ~endorsement) client) type status = Applied | Failed of {error_id : string} @@ -483,6 +499,146 @@ let test_slot_management_logic = ~error_msg:"Expected slot 1 to be available") ; check_dal_raw_context node +(** This test tests various situations related to DAL slots attestation. It's + many made of two parts (A) and (B). See the step inside the test. +*) +let test_slots_attestation_operation_behavior = + Protocol.register_test + ~__FILE__ + ~title:(sf "Slots attestation operation behavior") + ~tags:["dal"] + ~supports:Protocol.(From_protocol (Protocol.number Alpha)) + @@ fun protocol -> + setup ~endorsement_lag:5 ~dal_enable:true ~protocol + @@ fun parameters cryptobox node client _bootstrap -> + (* Some helpers *) + let nb_slots = parameters.number_of_slots in + let lag = parameters.endorsement_lag in + assert (lag > 1) ; + let attest ~level = + slot_availability + ~force:true + ~nb_slots + ~level + ~signer:Constant.bootstrap2 + [0] + client + in + let mempool_is ~__LOC__ expected_mempool = + let* mempool = Mempool.get_mempool client in + Check.( + (mempool = expected_mempool) + Mempool.classified_typ + ~error_msg:(__LOC__ ^ " : Bad mempool !!!. Got %L")) ; + unit + in + let check_slots_availability ~__LOC__ ~attested = + let* metadata = RPC.call node (RPC.get_chain_block_metadata ()) in + let dal_slot_availability = + (* Field is part of the encoding when the feature flag is true *) + Option.get metadata.dal_slot_availability + in + List.iter + (fun i -> + Check.( + (Array.get dal_slot_availability i = true) + bool + ~error_msg: + (Format.sprintf + "%s : Slot %d is expected to be confirmed." + __LOC__ + i))) + attested + |> return + in + (* Just bake some blocks before starting publishing. *) + let* () = repeat (2 * lag) (fun () -> Client.bake_for_and_wait client) in + + (* Part A. + - No header published yet, just play with attestations with various levels; + - Initially, only [h3] is applied, [h1; h2] are outdated, and [h4] is + branch_delayed. After baking a block, [h3] is included in a block and + [h4] becomes applied; + - No slot is confirmed as no slot header is published. + *) + let now = Node.get_level node in + let* (`OpHash h1) = attest ~level:1 in + let outdated = [h1] in + let* () = mempool_is ~__LOC__ Mempool.{empty with outdated} in + let* (`OpHash h2) = attest ~level:(now - 1) in + let outdated = [h1; h2] in + let* () = mempool_is ~__LOC__ Mempool.{empty with outdated} in + let* (`OpHash h3) = attest ~level:(now + 1) in + let applied = [h3] in + let* () = mempool_is ~__LOC__ Mempool.{empty with outdated; applied} in + let* (`OpHash h4) = attest ~level:(now + 2) in + let branch_delayed = [h4] in + let* () = + mempool_is ~__LOC__ Mempool.{empty with outdated; applied; branch_delayed} + in + let* () = Client.bake_for_and_wait client in + let applied = [h4] in + let branch_delayed = [] in + let* () = + mempool_is ~__LOC__ Mempool.{empty with outdated; applied; branch_delayed} + in + let* () = check_slots_availability ~__LOC__ ~attested:[] in + (* Part B. + - Publish a slot header (index 10) and bake; + - All delegates attest the slot, but the operation is injected too early. + The operation is branch_delayed; + - We bake sufficiently many blocks to get the attestation applied and + included in a block; + - We check in the metadata that the slot with index 10 is attested. + *) + let* (`OpHash h5) = + publish_dummy_slot + ~source:Constant.bootstrap1 + ~fee:1_200 + ~index:10 + ~message:" TEST!!! " + parameters + cryptobox + node + client + in + let applied = h5 :: applied in + let* () = + mempool_is ~__LOC__ Mempool.{empty with outdated; applied; branch_delayed} + in + let* () = Client.bake_for_and_wait client in + let now = Node.get_level node in + let* attestation_ops = + let open Constant in + let level = now + lag in + Lwt_list.map_s + (fun signer -> + let* (`OpHash h) = + slot_availability ~force:true ~nb_slots ~level ~signer [10] client + in + return h) + [bootstrap1; bootstrap2; bootstrap3; bootstrap4; bootstrap5] + in + let applied = [] in + let branch_delayed = attestation_ops in + let* () = + mempool_is ~__LOC__ Mempool.{empty with outdated; applied; branch_delayed} + in + let* () = repeat (lag - 1) (fun () -> Client.bake_for_and_wait client) in + let applied = attestation_ops in + let branch_delayed = [] in + let* () = + mempool_is ~__LOC__ Mempool.{empty with outdated; applied; branch_delayed} + in + let* () = check_slots_availability ~__LOC__ ~attested:[] in + let* () = Client.bake_for_and_wait client in + let applied = [] in + let branch_delayed = [] in + let* () = + mempool_is ~__LOC__ Mempool.{empty with outdated; applied; branch_delayed} + in + check_slots_availability ~__LOC__ ~attested:[10] + let init_dal_node protocol = let* node, client = let* parameter_file = Rollup.Dal.Parameters.parameter_file protocol in @@ -972,4 +1128,5 @@ let register ~protocols = ~dal_enable:true "rollup_node_applies_dal_pages" (rollup_node_stores_dal_slots ~expand_test:rollup_node_interprets_dal_pages) - protocols + protocols ; + test_slots_attestation_operation_behavior protocols