From 9e0a7ee6d1abcf178ea115ce7cf5f8a0a9b1e3fd Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Mon, 4 Mar 2024 11:05:36 +0100 Subject: [PATCH 1/7] Alpha/DAL: the DAL committee is implicitly a subset of the TB committee More precisely, shard indexes coincide with TB slots up to `number_of_shards`. --- src/bin_dal_node/committee_cache.ml | 4 +- src/bin_dal_node/committee_cache.mli | 8 +- src/bin_dal_node/node_context.ml | 11 +- src/bin_dal_node/slot_manager.ml | 38 +++-- src/bin_dal_node/slot_manager.mli | 2 +- src/lib_dal_node/dal_plugin.ml | 2 +- src/lib_dal_node/dal_plugin.mli | 5 +- .../lib_dal/dal_plugin_registration.ml | 10 +- .../lib_dal/dal_plugin_registration.ml | 10 +- .../lib_dal/dal_plugin_registration.ml | 3 +- src/proto_alpha/lib_plugin/RPC.ml | 70 ++++++++-- .../lib_protocol/alpha_context.mli | 18 +-- src/proto_alpha/lib_protocol/apply.ml | 17 ++- src/proto_alpha/lib_protocol/baking.ml | 9 +- src/proto_alpha/lib_protocol/baking.mli | 6 +- src/proto_alpha/lib_protocol/dal_apply.ml | 55 ++------ src/proto_alpha/lib_protocol/dal_apply.mli | 37 ++--- .../lib_protocol/dal_errors_repr.ml | 22 +-- src/proto_alpha/lib_protocol/dal_services.ml | 23 ++- src/proto_alpha/lib_protocol/dal_services.mli | 11 +- src/proto_alpha/lib_protocol/main.ml | 1 - src/proto_alpha/lib_protocol/raw_context.ml | 131 +++--------------- src/proto_alpha/lib_protocol/raw_context.mli | 59 ++------ .../lib_protocol/test/helpers/context.mli | 3 +- .../integration/consensus/test_attestation.ml | 14 +- src/proto_alpha/lib_protocol/validate.ml | 49 +++---- 26 files changed, 240 insertions(+), 378 deletions(-) diff --git a/src/bin_dal_node/committee_cache.ml b/src/bin_dal_node/committee_cache.ml index b6d42d02a2bf..3c0ba71a1aaf 100644 --- a/src/bin_dal_node/committee_cache.ml +++ b/src/bin_dal_node/committee_cache.ml @@ -31,9 +31,9 @@ module Level_map = let hash = Hashtbl.hash end) -type shard_indices = {start_index : int; offset : int} +type shard_indexes = int list -type committee = shard_indices Tezos_crypto.Signature.Public_key_hash.Map.t +type committee = shard_indexes Tezos_crypto.Signature.Public_key_hash.Map.t type t = committee Level_map.t diff --git a/src/bin_dal_node/committee_cache.mli b/src/bin_dal_node/committee_cache.mli index db60d32410f2..0fe7d6239d69 100644 --- a/src/bin_dal_node/committee_cache.mli +++ b/src/bin_dal_node/committee_cache.mli @@ -27,12 +27,12 @@ type t -(** Represents shard indexes from [start_index] to [start_index + offset - 1]. *) -type shard_indices = {start_index : int; offset : int} +(** Represents shard indexes of an attester. *) +type shard_indexes = int list -(** Represents the committee for a given level, +(** Represents the committee for a given level, as a mapping from an attester to its assigned shard indexes. *) -type committee = shard_indices Tezos_crypto.Signature.Public_key_hash.Map.t +type committee = shard_indexes Tezos_crypto.Signature.Public_key_hash.Map.t (** [create ~max_size] returns an empty cache. If the cache size exceeds [max_size], committees of old [level]s are removed in FIFO order. *) diff --git a/src/bin_dal_node/node_context.ml b/src/bin_dal_node/node_context.ml index 5cfe529add44..3af1a1a46170 100644 --- a/src/bin_dal_node/node_context.ml +++ b/src/bin_dal_node/node_context.ml @@ -156,11 +156,6 @@ let fetch_committee ctxt ~level = | None -> let*? {plugin = (module Plugin); _} = get_ready ctxt in let+ committee = Plugin.get_committee cctxt ~level in - let committee = - Tezos_crypto.Signature.Public_key_hash.Map.map - (fun (start_index, offset) -> Committee_cache.{start_index; offset}) - committee - in Committee_cache.add cache ~level ~committee ; committee @@ -169,11 +164,7 @@ let fetch_assigned_shard_indices ctxt ~level ~pkh = let+ committee = fetch_committee ctxt ~level in match Tezos_crypto.Signature.Public_key_hash.Map.find pkh committee with | None -> [] - | Some {start_index; offset} -> - (* TODO: https://gitlab.com/tezos/tezos/-/issues/4540 - Consider returning some abstract representation of [(s, n)] - instead of [int list] *) - Stdlib.List.init offset (fun i -> start_index + i) + | Some indexes -> indexes let version {config; _} = let network_name = config.Configuration_file.network_name in diff --git a/src/bin_dal_node/slot_manager.ml b/src/bin_dal_node/slot_manager.ml index 030f8a705621..214ec26d9d30 100644 --- a/src/bin_dal_node/slot_manager.ml +++ b/src/bin_dal_node/slot_manager.ml @@ -146,28 +146,26 @@ let add_commitment_shards ~shards_proofs_precomputation node_store cryptobox let get_opt array i = if i >= 0 && i < Array.length array then Some array.(i) else None -(** [shards_to_attesters committee] takes a committee [Committee_cache.committee] - and returns a function that, given a shard index, yields the pkh of its - attester for that level. *) +module IndexMap = Map.Make (struct + type t = int + + let compare = compare +end) + +(** [shards_to_attesters committee] takes a committee + [Committee_cache.committee] and returns a function that, given a shard + index, yields the pkh of its attester for that level. *) let shards_to_attesters committee = - let rec do_n ~n f acc = if n <= 0 then acc else do_n ~n:(n - 1) f (f acc) in let to_array committee = - (* We transform the map to a list *) - Tezos_crypto.Signature.Public_key_hash.Map.bindings committee - (* We sort the list in decreasing order w.r.t. to start_indices. *) - |> List.fast_sort (fun (_pkh1, shard_indices1) (_pkh2, shard_indices2) -> - shard_indices2.Committee_cache.start_index - - shard_indices1.Committee_cache.start_index) - (* We fold on the sorted list, starting from bigger start_indices. *) - |> List.fold_left - (fun accu (pkh, Committee_cache.{start_index = _; offset}) -> - (* We put in the accu list as many [pkh] occurrences as the number - of shards this pkh should attest, namely, [offset]. *) - do_n ~n:offset (fun acc -> pkh :: acc) accu) - [] - (* We build an array from the list. The array indices coincide with shard - indices. *) - |> Array.of_list + Tezos_crypto.Signature.Public_key_hash.Map.fold + (fun pkh indexes index_map -> + List.fold_left + (fun index_map index -> IndexMap.add index pkh index_map) + index_map + indexes) + committee + IndexMap.empty + |> IndexMap.bindings |> List.map snd |> Array.of_list in let committee = to_array committee in fun index -> get_opt committee index diff --git a/src/bin_dal_node/slot_manager.mli b/src/bin_dal_node/slot_manager.mli index 6bd06a7f88c6..8ecc05faa5a0 100644 --- a/src/bin_dal_node/slot_manager.mli +++ b/src/bin_dal_node/slot_manager.mli @@ -94,7 +94,7 @@ val add_commitment_shards : val publish_slot_data : level_committee: (level:int32 -> - Committee_cache.shard_indices Signature.Public_key_hash.Map.t tzresult Lwt.t) -> + Committee_cache.shard_indexes Signature.Public_key_hash.Map.t tzresult Lwt.t) -> Store.node_store -> Gossipsub.Worker.t -> Cryptobox.t -> diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index 91d51bde6a9d..22014dce6d32 100644 --- a/src/lib_dal_node/dal_plugin.ml +++ b/src/lib_dal_node/dal_plugin.ml @@ -67,7 +67,7 @@ module type T = sig val get_committee : Tezos_rpc.Context.generic -> level:int32 -> - (int * int) Tezos_crypto.Signature.Public_key_hash.Map.t tzresult Lwt.t + int list Tezos_crypto.Signature.Public_key_hash.Map.t tzresult Lwt.t val attested_slot_headers : block_info -> number_of_slots:int -> slot_index list tzresult diff --git a/src/lib_dal_node/dal_plugin.mli b/src/lib_dal_node/dal_plugin.mli index 56419401d6e0..e3fbe4def1b5 100644 --- a/src/lib_dal_node/dal_plugin.mli +++ b/src/lib_dal_node/dal_plugin.mli @@ -79,12 +79,11 @@ module type T = sig (** [get_committee ctxt ~level] retrieves the DAL committee at [level] from L1 as a map that associates to the public key hash [pkh] of the member of - the committee an interval [(s,n)], meaning that the slots [s;s+1;...;s+n-1] - belong to [pkh] *) + the committee its assigned shard indexes. *) val get_committee : Tezos_rpc.Context.generic -> level:int32 -> - (int * int) Tezos_crypto.Signature.Public_key_hash.Map.t tzresult Lwt.t + int list Tezos_crypto.Signature.Public_key_hash.Map.t tzresult Lwt.t (** [attested_slot_headers block_info number_of_slots] reads the metadata of the given [block_info] and constructs the list of attested slots diff --git a/src/proto_017_PtNairob/lib_dal/dal_plugin_registration.ml b/src/proto_017_PtNairob/lib_dal/dal_plugin_registration.ml index 1b0268414035..f5ed9c8f9506 100644 --- a/src/proto_017_PtNairob/lib_dal/dal_plugin_registration.ml +++ b/src/proto_017_PtNairob/lib_dal/dal_plugin_registration.ml @@ -110,8 +110,16 @@ module Plugin = struct let+ pkh_to_shards = Plugin.RPC.Dal.dal_shards cpctxt (`Main, `Head 0) ~level () in + let indexes (initial_slot, power) = + let last_slot = initial_slot + power - 1 in + let rec iter acc i = + if i < initial_slot then acc else iter (i :: acc) (i - 1) + in + iter [] last_slot + in List.fold_left - (fun acc (pkh, s) -> Signature.Public_key_hash.Map.add pkh s acc) + (fun acc (pkh, s) -> + Signature.Public_key_hash.Map.add pkh (indexes s) acc) Signature.Public_key_hash.Map.empty pkh_to_shards diff --git a/src/proto_018_Proxford/lib_dal/dal_plugin_registration.ml b/src/proto_018_Proxford/lib_dal/dal_plugin_registration.ml index 8bb2e8072fcf..87c820fe7ee1 100644 --- a/src/proto_018_Proxford/lib_dal/dal_plugin_registration.ml +++ b/src/proto_018_Proxford/lib_dal/dal_plugin_registration.ml @@ -107,8 +107,16 @@ module Plugin = struct let+ pkh_to_shards = Plugin.RPC.Dal.dal_shards cpctxt (`Main, `Head 0) ~level () in + let indexes (initial_slot, power) = + let last_slot = initial_slot + power - 1 in + let rec iter acc i = + if i < initial_slot then acc else iter (i :: acc) (i - 1) + in + iter [] last_slot + in List.fold_left - (fun acc (pkh, s) -> Signature.Public_key_hash.Map.add pkh s acc) + (fun acc (pkh, s) -> + Signature.Public_key_hash.Map.add pkh (indexes s) acc) Signature.Public_key_hash.Map.empty pkh_to_shards diff --git a/src/proto_alpha/lib_dal/dal_plugin_registration.ml b/src/proto_alpha/lib_dal/dal_plugin_registration.ml index 24727e729650..3c884f4c45b7 100644 --- a/src/proto_alpha/lib_dal/dal_plugin_registration.ml +++ b/src/proto_alpha/lib_dal/dal_plugin_registration.ml @@ -108,7 +108,8 @@ module Plugin = struct Plugin.RPC.Dal.dal_shards cpctxt (`Main, `Head 0) ~level () in List.fold_left - (fun acc (pkh, s) -> Signature.Public_key_hash.Map.add pkh s acc) + (fun acc ({delegate; indexes} : Plugin.RPC.Dal.S.shards_assignment) -> + Signature.Public_key_hash.Map.add delegate indexes acc) Signature.Public_key_hash.Map.empty pkh_to_shards diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index 4ba314064275..f937f8b6666c 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -3126,21 +3126,42 @@ module Dal = struct ~query RPC_path.(path / "confirmed_slot_headers_history") + type shards_query = { + level : Raw_level.t option; + delegates : Signature.Public_key_hash.t list; + } + let shards_query = - RPC_query.( - query (fun level -> level) - |+ opt_field "level" Raw_level.rpc_arg (fun t -> t) - |> seal) + let open RPC_query in + query (fun level delegates -> {level; delegates}) + |+ opt_field "level" Raw_level.rpc_arg (fun t -> t.level) + |+ multi_field "delegates" Signature.Public_key_hash.rpc_arg (fun t -> + t.delegates) + |> seal + + type shards_assignment = { + delegate : Signature.Public_key_hash.t; + indexes : int list; + } + + let shards_assignment_encoding = + let open Data_encoding in + conv + (fun {delegate; indexes} -> (delegate, indexes)) + (fun (delegate, indexes) -> {delegate; indexes}) + (obj2 + (req "delegate" Signature.Public_key_hash.encoding) + (req "indexes" (list int16))) + + type shards_output = shards_assignment list let shards = RPC_service.get_service ~description: - "Get the shard assignements for a given level (the default is the \ - current level)" + "Get the shards assignment for a given level (the default is the \ + current level) and given delegates (the default is all delegates)" ~query:shards_query - ~output: - Data_encoding.( - list (tup2 Signature.Public_key_hash.encoding (tup2 int16 int16))) + ~output:(Data_encoding.list shards_assignment_encoding) RPC_path.(path / "shards") end @@ -3158,13 +3179,31 @@ module Dal = struct let dal_confirmed_slots_history ctxt block = RPC_context.make_call0 S.dal_confirmed_slot_headers_history ctxt block () () - let dal_shards ctxt block ?level () = - RPC_context.make_call0 S.shards ctxt block level () + let dal_shards ctxt block ?level ?(delegates = []) () = + RPC_context.make_call0 S.shards ctxt block {level; delegates} () let register_shards () = - Registration.register0 ~chunked:true S.shards @@ fun ctxt level () -> - let level = Option.value level ~default:(Level.current ctxt).level in - Dal_services.shards ctxt ~level + Registration.register0 ~chunked:true S.shards @@ fun ctxt q () -> + let open Lwt_result_syntax in + let*? level_opt = + Option.map_e (Level.from_raw_with_offset ctxt ~offset:0l) q.level + in + let level = Option.value level_opt ~default:(Level.current ctxt) in + let* _ctxt, map = Dal_services.shards ctxt ~level in + let query_delegates = Signature.Public_key_hash.Set.of_list q.delegates in + let all_delegates = + Signature.Public_key_hash.Set.is_empty query_delegates + in + Signature.Public_key_hash.Map.fold + (fun delegate indexes acc -> + if + all_delegates + || Signature.Public_key_hash.Set.mem delegate query_delegates + then ({delegate; indexes} : S.shards_assignment) :: acc + else acc) + map + [] + |> return let register () = register_dal_confirmed_slot_headers_history () ; @@ -3883,7 +3922,8 @@ module Attestation_rights = struct consensus_pk = _; consensus_pkh = consensus_key; }, - attestation_power ) + attestation_power, + _dal_power ) acc -> {delegate; consensus_key; first_slot; attestation_power} :: acc) rights diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 3b1211e81d38..8ab973a73d01 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -80,6 +80,8 @@ module Slot : sig val succ : t -> t tzresult + val to_int : t -> int + val of_int_do_not_use_except_for_parameters : int -> t val encoding : t Data_encoding.encoding @@ -2733,6 +2735,8 @@ module Dal : sig val number_of_slots : context -> int + val number_of_shards : context -> int + (** This module re-exports definitions from {!Dal_slot_index_repr}. *) module Slot_index : sig type t @@ -2785,20 +2789,7 @@ module Dal : sig val expected_size_in_bits : max_index:Slot_index.t -> int - val power_of_attester : context -> attester:public_key_hash -> int option - val record_number_of_attested_shards : context -> t -> int -> context - - type committee = { - pkh_to_shards : (shard_index * int) Signature.Public_key_hash.Map.t; - } - - val compute_committee : - context -> - (Slot.t -> (context * Signature.Public_key_hash.t) tzresult Lwt.t) -> - committee tzresult Lwt.t - - val init_committee : context -> committee -> context end type slot_id = {published_level : Raw_level.t; index : Slot_index.t} @@ -2975,6 +2966,7 @@ module Dal_errors : sig | Dal_data_availibility_attester_not_in_committee of { attester : Signature.Public_key_hash.t; level : Raw_level.t; + slot : Slot.t; } | Dal_cryptobox_error of {explanation : string} end diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 414ca479208a..461cc5673b60 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2237,7 +2237,8 @@ let find_in_slot_map slot slot_map = | None -> (* This should not happen: operation validation should have failed. *) tzfail Faulty_validation_wrong_slot - | Some (consensus_key, power) -> return (consensus_key, power)) + | Some (consensus_key, power, dal_power) -> + return (consensus_key, power, dal_power)) let record_preattestation ctxt (mode : mode) (content : consensus_content) : (context * Kind.preattestation contents_result_list) tzresult Lwt.t = @@ -2263,7 +2264,7 @@ let record_preattestation ctxt (mode : mode) (content : consensus_content) : in match mode with | Application _ | Full_construction _ -> - let*? consensus_key, power = + let*? consensus_key, power, _dal_power = find_in_slot_map content.slot (Consensus.allowed_preattestations ctxt) in let*? ctxt = @@ -2305,7 +2306,7 @@ let record_attestation ctxt (mode : mode) (consensus : consensus_content) in match mode with | Application _ | Full_construction _ -> - let*? consensus_key, power = + let*? consensus_key, power, dal_power = find_in_slot_map consensus.slot (Consensus.allowed_attestations ctxt) in let*? ctxt = @@ -2315,11 +2316,7 @@ let record_attestation ctxt (mode : mode) (consensus : consensus_content) Option.fold ~none:(Result_syntax.return ctxt) ~some:(fun dal -> - Dal_apply.apply_attestation - ctxt - consensus_key - consensus.level - dal.attestation) + Dal_apply.apply_attestation ctxt dal.attestation ~power:dal_power) dal in return (ctxt, mk_attestation_result consensus_key power) @@ -2788,7 +2785,9 @@ let record_attesting_participation ctxt = | None -> tzfail (Consensus.Slot_map_not_found {loc = __LOC__}) | Some validators -> Slot.Map.fold_es - (fun initial_slot ((consensus_pk : Consensus_key.pk), power) ctxt -> + (fun initial_slot + ((consensus_pk : Consensus_key.pk), power, _dal_power) + ctxt -> let participation = if Slot.Set.mem initial_slot (Consensus.attestations_seen ctxt) then Delegate.Participated diff --git a/src/proto_alpha/lib_protocol/baking.ml b/src/proto_alpha/lib_protocol/baking.ml index 2bff27b7289e..7cc21c60edc0 100644 --- a/src/proto_alpha/lib_protocol/baking.ml +++ b/src/proto_alpha/lib_protocol/baking.ml @@ -110,6 +110,7 @@ let attesting_rights_by_first_slot ctxt level = let*? slots = Slot.Range.create ~min:0 ~count:(Constants.consensus_committee_size ctxt) in + let number_of_shards = Dal.number_of_shards ctxt in let* ctxt, (_, slots_map) = Slot.Range.fold_es (fun (ctxt, (delegates_map, slots_map)) slot -> @@ -132,12 +133,16 @@ let attesting_rights_by_first_slot ctxt level = in (* [slots_map]'keys are the minimal slots of delegates because we fold on slots in increasing order *) + let in_dal_committee = + if Compare.Int.(Slot.to_int slot < number_of_shards) then 1 else 0 + in let slots_map = Slot.Map.update initial_slot (function - | None -> Some (consensus_pk, 1) - | Some (consensus_pk, count) -> Some (consensus_pk, count + 1)) + | None -> Some (consensus_pk, 1, in_dal_committee) + | Some (consensus_pk, count, dal_count) -> + Some (consensus_pk, count + 1, dal_count + in_dal_committee)) slots_map in (ctxt, (delegates_map, slots_map))) diff --git a/src/proto_alpha/lib_protocol/baking.mli b/src/proto_alpha/lib_protocol/baking.mli index d99e7b9dab7b..0aeabf4f5731 100644 --- a/src/proto_alpha/lib_protocol/baking.mli +++ b/src/proto_alpha/lib_protocol/baking.mli @@ -53,12 +53,12 @@ val attesting_rights : (** Computes attesting rights for a given level. - @return map from allocated first slots to their owner's public key, public key - hash, and attesting power. *) + @return map from allocated first slots to their owner's public key, consensus + attesting power, and DAL attesting power. *) val attesting_rights_by_first_slot : context -> Level.t -> - (context * (Consensus_key.pk * int) Slot.Map.t) tzresult Lwt.t + (context * (Consensus_key.pk * int * int) Slot.Map.t) tzresult Lwt.t (** Computes the bonus baking reward depending on the attestation power. *) val bonus_baking_reward : context -> attestation_power:int -> Tez.t tzresult diff --git a/src/proto_alpha/lib_protocol/dal_apply.ml b/src/proto_alpha/lib_protocol/dal_apply.ml index 4ea774778029..c3c5b1aa16fc 100644 --- a/src/proto_alpha/lib_protocol/dal_apply.ml +++ b/src/proto_alpha/lib_protocol/dal_apply.ml @@ -53,7 +53,7 @@ let slot_of_int_e ~number_of_slots n = let pkh_of_consensus_key (consensus_key : Consensus_key.pk) = consensus_key.delegate -let validate_block_attestation ctxt level consensus_key attestation = +let validate_attestation ctxt level slot consensus_key attestation = let open Lwt_result_syntax in let*? () = assert_dal_feature_enabled ctxt in let number_of_slots = Dal.number_of_slots ctxt in @@ -65,36 +65,17 @@ let validate_block_attestation ctxt level consensus_key attestation = Compare.Int.(size <= maximum_size) (Dal_attestation_size_limit_exceeded {maximum_size; got = size}) in - let attester = pkh_of_consensus_key consensus_key in + let number_of_shards = Dal.number_of_shards ctxt in fail_when - (Option.is_none @@ Dal.Attestation.power_of_attester ctxt ~attester) - (Dal_data_availibility_attester_not_in_committee {attester; level}) + Compare.Int.(Slot.to_int slot >= number_of_shards) + (let attester = pkh_of_consensus_key consensus_key in + Dal_data_availibility_attester_not_in_committee {attester; level; slot}) -let validate_mempool_attestation ctxt attestation = - let open Lwt_result_syntax in - let*? () = assert_dal_feature_enabled ctxt in - let number_of_slots = Dal.number_of_slots ctxt in - let*? max_index = number_of_slots - 1 |> slot_of_int_e ~number_of_slots in - let maximum_size = Dal.Attestation.expected_size_in_bits ~max_index in - let size = Dal.Attestation.occupied_size_in_bits attestation in - fail_unless - Compare.Int.(size <= maximum_size) - (Dal_attestation_size_limit_exceeded {maximum_size; got = size}) - -let apply_attestation ctxt consensus_key level attestation = +let apply_attestation ctxt attestation ~power = let open Result_syntax in let* () = assert_dal_feature_enabled ctxt in - let attester = pkh_of_consensus_key consensus_key in - match Dal.Attestation.power_of_attester ctxt ~attester with - | None -> - (* This should not happen: operation validation should have failed. *) - error (Dal_data_availibility_attester_not_in_committee {attester; level}) - | Some power -> - return - (Dal.Attestation.record_number_of_attested_shards - ctxt - attestation - power) + return + (Dal.Attestation.record_number_of_attested_shards ctxt attestation power) (* This function should fail if we don't want the operation to be propagated over the L1 gossip network. Because this is a manager @@ -149,23 +130,3 @@ let finalisation ctxt = Dal.Slot.finalize_pending_slot_headers ctxt ~number_of_slots in (ctxt, attestation)) - -let compute_committee ctxt level = - let open Lwt_result_syntax in - let*? () = assert_dal_feature_enabled ctxt in - let pkh_from_tenderbake_slot slot = - let+ ctxt, consensus_key = Stake_distribution.slot_owner ctxt level slot in - (ctxt, pkh_of_consensus_key consensus_key) - in - Alpha_context.Dal.Attestation.compute_committee ctxt pkh_from_tenderbake_slot - -let initialisation ctxt ~level = - let open Lwt_result_syntax in - only_if_dal_feature_enabled - ctxt - ~default:(fun ctxt -> return ctxt) - (fun ctxt -> - let+ committee = compute_committee ctxt level in - (* This committee is cached because it is the one we will use - for the validation of the DAL attestations. *) - Alpha_context.Dal.Attestation.init_committee ctxt committee) diff --git a/src/proto_alpha/lib_protocol/dal_apply.mli b/src/proto_alpha/lib_protocol/dal_apply.mli index b7da86a8bb47..55bd78051f1a 100644 --- a/src/proto_alpha/lib_protocol/dal_apply.mli +++ b/src/proto_alpha/lib_protocol/dal_apply.mli @@ -28,37 +28,27 @@ open Alpha_context -(** [validate_block_attestation ctxt level consensus_key attestation] checks +(** [validate_attestation ctxt level slot consensus_key attestation] checks whether the DAL attestation [attestation] emitted at given [level] by the - attester with the given [consensus_key] is valid for block inclusion. If an + attester with the given [consensus_key] and given [slot] is valid. If an [Error _] is returned, the [op] is not valid. The checks made are: * the attestation size does not exceed the maximum; * the delegate is in the DAL committee. These are checks done for the DAL part alone, checks on other fields of an - attestation (like level, round, slot) are done by the caller. *) -val validate_block_attestation : + attestation (like level, round) are done by the caller. *) +val validate_attestation : t -> Raw_level.t -> + Slot.t -> Consensus_key.pk -> Dal.Attestation.t -> unit tzresult Lwt.t -(** [validate_mempool_attestation ctxt level consensus_key attestation] checks - whether the DAL attestation [attestation] is valid for the mempool. It is - similar to [check_block_attestion], but it performs only the check on the - size, as [consensus_key] is not available. If an [Error _] is returned, the - [op] is not valid. - - These are checks done for the DAL part alone, checks on other fields of an - attestation (like level, round, slot) are done by the caller. *) -val validate_mempool_attestation : t -> Dal.Attestation.t -> unit tzresult Lwt.t - -(** [apply_attestation ctxt consensus_key level attestation] applies - [attestation] into the [ctxt] assuming [consensus_key.delegate] issued those - attestations at level [level]. *) -val apply_attestation : - t -> Consensus_key.pk -> Raw_level.t -> Dal.Attestation.t -> t tzresult +(** [apply_attestation ctxt attestation] records in the context that the given + [attestation] was issued and the corresponding attester has the given + [power]. *) +val apply_attestation : t -> Dal.Attestation.t -> power:int -> t tzresult (** [validate_publish_commitment ctxt slot] ensures that [slot_header] is valid and prevents an operation containing [slot_header] to be @@ -80,12 +70,3 @@ val apply_publish_commitment : [lag] is a parametric constant specific to the data-availability layer. *) val finalisation : t -> (t * Dal.Attestation.t) tzresult Lwt.t - -(** [initialize ctxt ~level] should be executed at block - initialisation time. It allows to cache the committee for [level] - in memory so that every time we need to use this committee, there - is no need to recompute it again. *) -val initialisation : t -> level:Level.t -> t tzresult Lwt.t - -(** [compute_committee ctxt level] computes the DAL committee for [level]. *) -val compute_committee : t -> Level.t -> Dal.Attestation.committee tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/dal_errors_repr.ml b/src/proto_alpha/lib_protocol/dal_errors_repr.ml index dd62bf25d8e7..585b9b6f78cc 100644 --- a/src/proto_alpha/lib_protocol/dal_errors_repr.ml +++ b/src/proto_alpha/lib_protocol/dal_errors_repr.ml @@ -42,6 +42,7 @@ type error += | Dal_data_availibility_attester_not_in_committee of { attester : Signature.Public_key_hash.t; level : Raw_level_repr.t; + slot : Slot_repr.t; } | Dal_cryptobox_error of {explanation : string} | Dal_register_invalid_slot_header of { @@ -186,24 +187,29 @@ let () = ~id:"Dal_data_availibility_attester_not_in_committee" ~title:"The attester is not part of the DAL committee for this level" ~description:"The attester is not part of the DAL committee for this level" - ~pp:(fun ppf (attester, level) -> + ~pp:(fun ppf (attester, level, slot) -> Format.fprintf ppf - "The attester %a is not part of the DAL committee for the level %a" + "The attester %a, with slot %a, is not part of the DAL committee for \ + the level %a." Signature.Public_key_hash.pp attester + Slot_repr.pp + slot Raw_level_repr.pp level) Data_encoding.( - obj2 + obj3 (req "attester" Signature.Public_key_hash.encoding) - (req "level" Raw_level_repr.encoding)) + (req "level" Raw_level_repr.encoding) + (req "slot" Slot_repr.encoding)) (function - | Dal_data_availibility_attester_not_in_committee {attester; level} -> - Some (attester, level) + | Dal_data_availibility_attester_not_in_committee {attester; level; slot} + -> + Some (attester, level, slot) | _ -> None) - (fun (attester, level) -> - Dal_data_availibility_attester_not_in_committee {attester; level}) ; + (fun (attester, level, slot) -> + Dal_data_availibility_attester_not_in_committee {attester; level; slot}) ; register_error_kind `Permanent ~id:"dal_cryptobox_error" diff --git a/src/proto_alpha/lib_protocol/dal_services.ml b/src/proto_alpha/lib_protocol/dal_services.ml index 038e980885f4..1668c2f1b40a 100644 --- a/src/proto_alpha/lib_protocol/dal_services.ml +++ b/src/proto_alpha/lib_protocol/dal_services.ml @@ -32,12 +32,23 @@ let assert_dal_feature_enabled ctxt = Compare.Bool.(feature_enable = true) Dal_errors.Dal_feature_disabled +(* Slots returned by this function are assumed by consumers to be in increasing + order, hence the use of [Slot.Range.rev_fold_es]. *) let shards ctxt ~level = let open Lwt_result_syntax in - let open Dal.Attestation in let*? () = assert_dal_feature_enabled ctxt in - let level = Level.from_raw ctxt level in - (* We do not cache this committee. This function being used by RPCs - to know the DAL committee at some particular level. *) - let+ committee = Dal_apply.compute_committee ctxt level in - Signature.Public_key_hash.Map.bindings committee.pkh_to_shards + let number_of_shards = Dal.number_of_shards ctxt in + let*? slots = Slot.Range.create ~min:0 ~count:number_of_shards in + Slot.Range.rev_fold_es + (fun (ctxt, map) slot -> + let* ctxt, consensus_pk = Stake_distribution.slot_owner ctxt level slot in + let slot = Slot.to_int slot in + let map = + Signature.Public_key_hash.Map.update + consensus_pk.delegate + (function None -> Some [slot] | Some slots -> Some (slot :: slots)) + map + in + return (ctxt, map)) + (ctxt, Signature.Public_key_hash.Map.empty) + slots diff --git a/src/proto_alpha/lib_protocol/dal_services.mli b/src/proto_alpha/lib_protocol/dal_services.mli index d6394f128dec..25d8803106af 100644 --- a/src/proto_alpha/lib_protocol/dal_services.mli +++ b/src/proto_alpha/lib_protocol/dal_services.mli @@ -23,11 +23,10 @@ (* *) (*****************************************************************************) -(** [shards ctxt ~level] returns the DAL committee as an association - list that associates to the public key hash [pkh] of the member of - the committee an interval [(s,n)], meaning that the slots - [s;s+1;...;s+n-1] belongs to [pkh]. It is guaranteed that [n>0]. *) +(** [shards ctxt ~level] returns the DAL committee as a mapping from the public + key hash of members of the committee to the list of shard indexes associated + to that member. *) val shards : Alpha_context.t -> - level:Alpha_context.Raw_level.t -> - (Signature.Public_key_hash.t * (int * int)) list tzresult Lwt.t + level:Alpha_context.Level.t -> + (Alpha_context.t * int list Signature.Public_key_hash.Map.t) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 5583e2b9bac0..be4b1afce84a 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -223,7 +223,6 @@ let prepare_ctxt ctxt mode ~(predecessor : Block_header.shell_header) = | Partial_construction _ -> init_consensus_rights_for_mempool ctxt ~predecessor_level in - let* ctxt = Dal_apply.initialisation ~level:predecessor_level ctxt in return ( ctxt, migration_balance_updates, diff --git a/src/proto_alpha/lib_protocol/raw_context.ml b/src/proto_alpha/lib_protocol/raw_context.ml index b0fe9d38b8fc..9ccc46907926 100644 --- a/src/proto_alpha/lib_protocol/raw_context.ml +++ b/src/proto_alpha/lib_protocol/raw_context.ml @@ -87,18 +87,19 @@ module Raw_consensus = struct type t = { current_attestation_power : int; (** Number of attestation slots recorded for the current block. *) - allowed_attestations : (consensus_pk * int) Slot_repr.Map.t option; - (** Attestations rights for the current block. Only an attestation - for the lowest slot in the block can be recorded. The map - associates to each initial slot the [pkh] associated to this - slot with its power. This is [None] only in mempool mode. *) - allowed_preattestations : (consensus_pk * int) Slot_repr.Map.t option; + allowed_attestations : (consensus_pk * int * int) Slot_repr.Map.t option; + (** Attestations rights for the current block. Only an attestation for + the lowest slot in the block can be recorded. The map associates to + each initial slot the [pkh] associated to this slot with its + consensus attestation power and DAL attestation power. This is + [None] only in mempool mode. *) + allowed_preattestations : (consensus_pk * int * int) Slot_repr.Map.t option; (** Preattestations rights for the current block. Only a preattestation - for the lowest slot in the block can be recorded. The map - associates to each initial slot the [pkh] associated to this - slot with its power. This is [None] only in mempool mode, or in - application mode when there is no locked round (so the block - cannot contain any preattestations). *) + for the lowest slot in the block can be recorded. The map associates + to each initial slot the [pkh] associated to this slot with its + consensus attestation power and DAL attestation power. This is + [None] only in mempool mode, or in application mode when there is no + locked round (so the block cannot contain any preattestations). *) forbidden_delegates : Signature.Public_key_hash.Set.t; (** Delegates that are not allowed to bake or attest blocks; i.e., delegates which have zero frozen deposit due to a previous @@ -222,13 +223,6 @@ module Raw_consensus = struct {t with attestation_branch = Some attestation_branch} end -type dal_committee = { - pkh_to_shards : - (Dal_attestation_repr.shard_index * int) Signature.Public_key_hash.Map.t; -} - -let empty_dal_committee = {pkh_to_shards = Signature.Public_key_hash.Map.empty} - type back = { context : Context.t; constants : Constants_parametric_repr.t; @@ -267,7 +261,6 @@ type back = { - We need to provide an incentive to avoid byzantines to post dummy slot headers. *) dal_attestation_slot_accountability : Dal_attestation_repr.Accountability.t; - dal_committee : dal_committee; dal_cryptobox : Dal.t option; adaptive_issuance_enable : bool; } @@ -883,7 +876,6 @@ let prepare ~level ~predecessor_timestamp ~timestamp ~adaptive_issuance_enable Dal_attestation_repr.Accountability.init ~number_of_slots: constants.Constants_parametric_repr.dal.number_of_slots; - dal_committee = empty_dal_committee; dal_cryptobox = None; adaptive_issuance_enable; }; @@ -1672,9 +1664,9 @@ module type CONSENSUS = sig type consensus_pk - val allowed_attestations : t -> (consensus_pk * int) slot_map option + val allowed_attestations : t -> (consensus_pk * int * int) slot_map option - val allowed_preattestations : t -> (consensus_pk * int) slot_map option + val allowed_preattestations : t -> (consensus_pk * int * int) slot_map option val forbidden_delegates : t -> Signature.Public_key_hash.Set.t @@ -1684,8 +1676,8 @@ module type CONSENSUS = sig val initialize_consensus_operation : t -> - allowed_attestations:(consensus_pk * int) slot_map option -> - allowed_preattestations:(consensus_pk * int) slot_map option -> + allowed_attestations:(consensus_pk * int * int) slot_map option -> + allowed_preattestations:(consensus_pk * int * int) slot_map option -> t val record_attestation : t -> initial_slot:slot -> power:int -> t tzresult @@ -1829,6 +1821,9 @@ module Dal = struct let number_of_slots ctxt = ctxt.back.constants.dal.number_of_slots + let number_of_shards ctxt = + ctxt.back.constants.dal.cryptobox_parameters.number_of_shards + let record_number_of_attested_shards ctxt attestation number = let dal_attestation_slot_accountability = Dal_attestation_repr.Accountability.record_number_of_attested_shards @@ -1873,94 +1868,6 @@ module Dal = struct ctxt.back.dal_attestation_slot_accountability ~threshold ~number_of_shards - - type committee = dal_committee = { - pkh_to_shards : - (Dal_attestation_repr.shard_index * int) Signature.Public_key_hash.Map.t; - } - - (* DAL/FIXME https://gitlab.com/tezos/tezos/-/issues/3110 - - A committee is selected by the callback function - [pkh_from_tenderbake_slot]. We use a callback because of circular - dependencies. It is not clear whether it will be the final choice - for the DAL committee. The current solution is a bit hackish but - should work. If we decide to differ from the Tenderbake - committee, one could just draw a new committee. - - The problem with drawing a new committee is that it is not - guaranteed that everyone in the DAL committee will be in the - Tenderbake committee. Consequently, either we decide to have a - new consensus operation which does not count for Tenderbake, - and/or we take into account for the model of DAL that at every - level, a percentage of DAL attestations cannot be received. *) - let compute_committee ctxt pkh_from_tenderbake_slot = - let open Lwt_result_syntax in - let Constants_parametric_repr. - { - dal = {cryptobox_parameters = {number_of_shards; _}; _}; - consensus_committee_size; - _; - } = - ctxt.back.constants - in - (* We first draw a committee by drawing slots from the Tenderbake - committee. To have a compact representation of slots, we can - sort the Tenderbake slots by [pkh], so that a committee is - actually only an interval. This is done by recomputing a - committee from the first one. *) - let update_committee committee pkh ~slot_index ~power = - { - pkh_to_shards = - Signature.Public_key_hash.Map.update - pkh - (function - | None -> Some (slot_index, power) - | Some (initial_shard_index, old_power) -> - Some (initial_shard_index, old_power + power)) - committee.pkh_to_shards; - } - in - let rec compute_power index committee = - if Compare.Int.(index < 0) then return committee - else - let shard_index = index mod consensus_committee_size in - let*? slot = Slot_repr.of_int shard_index in - let* _ctxt, pkh = pkh_from_tenderbake_slot slot in - (* The [Slot_repr] module is related to the Tenderbake committee. *) - let slot_index = Slot_repr.to_int slot in - let committee = update_committee committee pkh ~slot_index ~power:1 in - compute_power (index - 1) committee - in - (* This committee is an intermediate to compute the final DAL - committee. This one only projects the Tenderbake committee into - the DAL committee. The next one reorders the slots so that they - are grouped by public key hash. *) - let* unordered_committee = - compute_power (number_of_shards - 1) empty_dal_committee - in - let dal_committee = - Signature.Public_key_hash.Map.fold - (fun pkh (_, power) (total_power, committee) -> - let committee = - update_committee committee pkh ~slot_index:total_power ~power - in - let new_total_power = total_power + power in - (new_total_power, committee)) - unordered_committee.pkh_to_shards - (0, empty_dal_committee) - |> snd - in - return dal_committee - - let init_committee ctxt committee = - {ctxt with back = {ctxt.back with dal_committee = committee}} - - let power_of_attester ctxt ~attester:pkh = - Signature.Public_key_hash.Map.find_opt - pkh - ctxt.back.dal_committee.pkh_to_shards - |> Option.map snd end (* The type for relative context accesses instead from the root. In order for diff --git a/src/proto_alpha/lib_protocol/raw_context.mli b/src/proto_alpha/lib_protocol/raw_context.mli index a6fc18439b4f..8ce1fbe2de04 100644 --- a/src/proto_alpha/lib_protocol/raw_context.mli +++ b/src/proto_alpha/lib_protocol/raw_context.mli @@ -328,15 +328,13 @@ module type CONSENSUS = sig type consensus_pk - (** Returns a map where each attester's pkh is associated to the - list of its attesting slots (in decreasing order) for a given - level. *) - val allowed_attestations : t -> (consensus_pk * int) slot_map option + (** Returns a map where from the initial slot of each attester in the TB + committee for a given level, to the attester's public key and its + consensus power and DAL power. *) + val allowed_attestations : t -> (consensus_pk * int * int) slot_map option - (** Returns a map where each attester's pkh is associated to the - list of its attesting slots (in decreasing order) for a given - level. *) - val allowed_preattestations : t -> (consensus_pk * int) slot_map option + (** See {!allowed_attestations}. *) + val allowed_preattestations : t -> (consensus_pk * int * int) slot_map option (** Returns the set of delegates that are not allowed to bake or attest blocks; i.e., delegates which have zero frozen deposit @@ -355,8 +353,8 @@ module type CONSENSUS = sig operation. *) val initialize_consensus_operation : t -> - allowed_attestations:(consensus_pk * int) slot_map option -> - allowed_preattestations:(consensus_pk * int) slot_map option -> + allowed_attestations:(consensus_pk * int * int) slot_map option -> + allowed_preattestations:(consensus_pk * int * int) slot_map option -> t (** [record_attestation ctx ~initial_slot ~power] records an @@ -433,6 +431,8 @@ module Dal : sig val number_of_slots : t -> int + val number_of_shards : t -> int + (** [record_number_of_attested_shards ctxt attestation number_of_shards] records that the [number_of_shards] shards were attested (declared available by some attester). *) @@ -455,43 +455,4 @@ module Dal : sig otherwise. If the [index] is out of the interval [0;number_of_slots - 1], returns [false]. *) val is_slot_index_attested : t -> Dal_slot_index_repr.t -> bool - - (** [power_of_attester ctxt ~attester] returns the number of shards assigned - to [attester] at the current level. It never returns [Some 0]. Instead, it - returns [None] if the attester is not in the DAL committee. *) - val power_of_attester : - t -> attester:Signature.Public_key_hash.t -> int option - - (** The DAL committee is a subset of the Tenderbake committee. A - shard from [0; number_of_shards - 1] is associated to a public key - hash. The committee is a mapping from public key hashes to shards and - The DAL committee ensures the shards associated to the - same public key hash are contiguous. The list of shards is - represented as two natural numbers [(initial, power)] which - encodes the list of shards: - [initial; initial + 1; ... ; initial + power - 1]. *) - type committee = { - pkh_to_shards : - (Dal_attestation_repr.shard_index * int) Signature.Public_key_hash.Map.t; - } - - (** [compute_committee ctxt pkh_from_tenderbake_slot] computes the - DAL committee using the [pkh_from_tenderbake_slot] function. This - functions takes into account the fact that the DAL committee and - the Tenderbake committee may have different sizes. If the DAL - committee is smaller, then we simply take a projection of the - Tenderbake committee for the first [n] slots. If the DAL - committee is larger, shards are computed modulo the Tenderbake - committee. Slots assignments are reordered for a given a public - key hash to ensure all the slots (or shards in the context of - DAL) shards are contiguous (see {!type:committee}). *) - val compute_committee : - t -> - (Slot_repr.t -> (t * Signature.Public_key_hash.t) tzresult Lwt.t) -> - committee tzresult Lwt.t - - (** [init_committee ctxt committee] returns a context where the - [committee] is cached. The committee is expected to be the one - for the current level. *) - val init_committee : t -> committee -> t end diff --git a/src/proto_alpha/lib_protocol/test/helpers/context.mli b/src/proto_alpha/lib_protocol/test/helpers/context.mli index 05737069baf7..67429874ad71 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/context.mli +++ b/src/proto_alpha/lib_protocol/test/helpers/context.mli @@ -199,8 +199,9 @@ module Dal : sig val shards : t -> ?level:Raw_level.t -> + ?delegates:Signature.public_key_hash list -> unit -> - (Signature.Public_key_hash.t * (int * int)) list tzresult Lwt.t + Plugin.RPC.Dal.S.shards_output tzresult Lwt.t end module Contract : sig diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml index d3d30db6a04b..6882d56aa94f 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_attestation.ml @@ -725,7 +725,10 @@ let test_attester_not_in_dal_committee () = committee in let in_dal_committee = - List.mem_assoc ~equal:Signature.Public_key_hash.equal pkh dal_committee + List.exists + (fun ({delegate; _} : Plugin.RPC.Dal.S.shards_assignment) -> + Signature.Public_key_hash.equal pkh delegate) + dal_committee in if in_committee && not in_dal_committee then let dal_content = {attestation = Dal.Attestation.empty} in @@ -736,7 +739,7 @@ let test_attester_not_in_dal_committee () = Environment.Ecoproto_error (Alpha_context.Dal_errors .Dal_data_availibility_attester_not_in_committee - {attester; level}); + {attester; level; slot = _}); ] when Signature.Public_key_hash.equal attester pkh && Raw_level.to_int32 level = b.header.shell.level -> @@ -797,10 +800,11 @@ let test_dal_attestation_threshold () = Log.info "Number of minimum required attested shards: %d" min_power ; let* _ = List.fold_left_es - (fun (acc_ops, acc_power) (pkh, (_first_index, power)) -> - let* op = Op.attestation ~delegate:pkh ~dal_content b in + (fun (acc_ops, acc_power) + ({delegate; indexes} : RPC.Dal.S.shards_assignment) -> + let* op = Op.attestation ~delegate ~dal_content b in let ops = op :: acc_ops in - let power = acc_power + power in + let power = acc_power + List.length indexes in let* _b, (metadata, _ops) = Block.bake_with_metadata ~operations:ops b in diff --git a/src/proto_alpha/lib_protocol/validate.ml b/src/proto_alpha/lib_protocol/validate.ml index 6775bf626eb6..7915e67fe1db 100644 --- a/src/proto_alpha/lib_protocol/validate.ml +++ b/src/proto_alpha/lib_protocol/validate.ml @@ -29,8 +29,8 @@ open Alpha_context type consensus_info = { predecessor_level : Raw_level.t; predecessor_round : Round.t; - preattestation_slot_map : (Consensus_key.pk * int) Slot.Map.t option; - attestation_slot_map : (Consensus_key.pk * int) Slot.Map.t option; + preattestation_slot_map : (Consensus_key.pk * int * int) Slot.Map.t option; + attestation_slot_map : (Consensus_key.pk * int * int) Slot.Map.t option; } let init_consensus_info ctxt (predecessor_level, predecessor_round) = @@ -505,7 +505,7 @@ module Consensus = struct let*? () = check_round kind locked_round round in let expected_payload_hash = block_info.header_contents.payload_hash in let*? () = check_payload_hash kind expected_payload_hash bph in - let*? consensus_key, voting_power = + let*? consensus_key, voting_power, _dal_power = get_delegate_details consensus_info.preattestation_slot_map kind slot in return (consensus_key, voting_power) @@ -533,7 +533,7 @@ module Consensus = struct however check that all preattestations have the same round in [check_construction_preattestation_round_consistency] further below. *) let*? () = check_payload_hash kind expected_payload_hash bph in - let*? consensus_key, voting_power = + let*? consensus_key, voting_power, _dal_power = get_delegate_details consensus_info.preattestation_slot_map kind slot in return (consensus_key, voting_power) @@ -697,10 +697,11 @@ module Consensus = struct (** Attestation checks for all modes that involve a block: Application, Partial_validation, and Construction. + Checks regarding the DAL content are done separately. Return the slot owner's consensus key and voting power. *) let check_block_attestation vi consensus_info - {level; round; block_payload_hash = bph; slot} dal_content_opt = + {level; round; block_payload_hash = bph; slot} = let open Lwt_result_syntax in let*? expected_payload_hash = match Consensus.attestation_branch vi.ctxt with @@ -717,20 +718,9 @@ module Consensus = struct let*? () = check_level kind consensus_info.predecessor_level level in let*? () = check_round kind consensus_info.predecessor_round round in let*? () = check_payload_hash kind expected_payload_hash bph in - let*? consensus_key, voting_power = + let*? consensus_key, voting_power, _dal_power = get_delegate_details consensus_info.attestation_slot_map kind slot in - let* () = - Option.fold - ~none:return_unit - ~some:(fun dal -> - Dal_apply.validate_block_attestation - vi.ctxt - level - consensus_key - dal.attestation) - dal_content_opt - in return (consensus_key, voting_power) let check_attestation vi ~check_signature @@ -747,19 +737,8 @@ module Consensus = struct let* consensus_key, voting_power = match vi.mode with | Application _ | Partial_validation _ | Construction _ -> - check_block_attestation - vi - consensus_info - consensus_content - dal_content + check_block_attestation vi consensus_info consensus_content | Mempool -> - let* () = - Option.fold - ~none:return_unit - ~some:(fun dal -> - Dal_apply.validate_mempool_attestation vi.ctxt dal.attestation) - dal_content - in check_mempool_consensus vi consensus_info @@ -767,6 +746,18 @@ module Consensus = struct consensus_content in let* () = check_delegate_is_not_forbidden vi.ctxt consensus_key.delegate in + let* () = + Option.fold + ~none:return_unit + ~some:(fun dal -> + Dal_apply.validate_attestation + vi.ctxt + consensus_content.level + consensus_content.slot + consensus_key + dal.attestation) + dal_content + in let*? () = if check_signature then Operation.check_signature -- GitLab From 9c83d3db25b8dce55fee4f0876f453538aad2693 Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 5 Mar 2024 13:28:36 +0100 Subject: [PATCH 2/7] DAL/Node: fix plugin updating to fetch the committee for the migration block we need to use the new plugin. So we need to first update the plugin. Note that the new plugin is not used directly in the `process_block` function. But `Node_context.fetch_committee` retrieves it and will use the right plugin. --- src/bin_dal_node/daemon.ml | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/bin_dal_node/daemon.ml b/src/bin_dal_node/daemon.ml index 88b6a94f99c9..6eebaf439f23 100644 --- a/src/bin_dal_node/daemon.ml +++ b/src/bin_dal_node/daemon.ml @@ -360,6 +360,20 @@ module Handler = struct Plugin.block_info cctxt ~block ~metadata:`Always in let shell_header = Plugin.block_shell_header block_info in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/6036 + Note that the first processed block is special: in contrast to + the general case, as implemented by this function, the plugin was + set before processing the block, by + [resolve_plugin_and_set_ready], not after processing the + block. *) + let* () = + may_update_plugin + cctxt + ctxt + ~block + ~current_proto:plugin_proto + ~block_proto:shell_header.proto_level + in let*? block_round = Plugin.get_round shell_header.fitness in let* slot_headers = Plugin.get_published_slot_headers block_info in let* () = @@ -428,18 +442,7 @@ module Handler = struct let*! () = Event.(emit layer1_node_final_block (block_level, block_round)) in - (* TODO: https://gitlab.com/tezos/tezos/-/issues/6036 - Note that the first processed block is special: in contrast to - the general case, as implemented by this function, the plugin was - set before processing the block, by - [resolve_plugin_and_set_ready], not after processing the - block. *) - may_update_plugin - cctxt - ctxt - ~block - ~current_proto:plugin_proto - ~block_proto:shell_header.proto_level + return_unit in match last_processed_level with (* TODO: https://gitlab.com/tezos/tezos/-/issues/6849 -- GitLab From d361a50ccb4ceee5c22b765fcad8e714998acf4b Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Tue, 5 Mar 2024 13:29:18 +0100 Subject: [PATCH 3/7] DAL/Tezt: update to new shard assignment --- tezt/lib_tezos/RPC.ml | 7 +- tezt/lib_tezos/RPC.mli | 12 ++- tezt/lib_tezos/dal_common.ml | 21 ++-- tezt/lib_tezos/dal_common.mli | 5 +- tezt/lib_tezos/operation_core.ml | 4 + tezt/lib_tezos/operation_core.mli | 7 ++ tezt/tests/dal.ml | 155 ++++++++++++++++-------------- 7 files changed, 121 insertions(+), 90 deletions(-) diff --git a/tezt/lib_tezos/RPC.ml b/tezt/lib_tezos/RPC.ml index c8d2919259e8..0a48f5217d40 100644 --- a/tezt/lib_tezos/RPC.ml +++ b/tezt/lib_tezos/RPC.ml @@ -1670,11 +1670,10 @@ let get_chain_block_votes_total_voting_power ?(chain = "main") ?(block = "head") Fun.id let get_chain_block_context_dal_shards ?(chain = "main") ?(block = "head") - ?level () = + ?level ?delegates () = let query_string = - match level with - | None -> [] - | Some level -> [("level", string_of_int level)] + Query_arg.opt_list "delegates" (fun x y -> (x, y)) delegates + @ Query_arg.opt "level" Int.to_string level in make GET diff --git a/tezt/lib_tezos/RPC.mli b/tezt/lib_tezos/RPC.mli index 299078846bae..29aa9029306d 100644 --- a/tezt/lib_tezos/RPC.mli +++ b/tezt/lib_tezos/RPC.mli @@ -1313,9 +1313,17 @@ val get_chain_block_votes_successor_period : val get_chain_block_votes_total_voting_power : ?chain:string -> ?block:string -> unit -> JSON.t t -(** RPC: [GET /chains/[chain]/blocks/[block]/context/dal/shards?level=[level]] *) +(** RPC: [GET /chains/[chain]/blocks/[block]/context/dal/shards] + + [chain] defaults to ["main"]. + [block] defaults to ["head"]. *) val get_chain_block_context_dal_shards : - ?chain:string -> ?block:string -> ?level:int -> unit -> JSON.t t + ?chain:string -> + ?block:string -> + ?level:int -> + ?delegates:string list -> + unit -> + JSON.t t (** RPC: [GET /monitor/applied_blocks] *) val get_monitor_applied_blocks : JSON.t t diff --git a/tezt/lib_tezos/dal_common.ml b/tezt/lib_tezos/dal_common.ml index 76817967accc..d247a855768a 100644 --- a/tezt/lib_tezos/dal_common.ml +++ b/tezt/lib_tezos/dal_common.ml @@ -83,7 +83,7 @@ module Parameters = struct end module Committee = struct - type member = {attester : string; first_shard_index : int; power : int} + type member = {attester : string; indexes : int list} type t = member list @@ -91,21 +91,22 @@ module Committee = struct let open Check in list @@ convert - (fun {attester; first_shard_index; power} -> - (attester, first_shard_index, power)) - (tuple3 string int int) + (fun {attester; indexes} -> (attester, indexes)) + (tuple2 string (list int)) - let at_level node ~level = + let at_level node ?level ?delegates () = let* json = - Node.RPC.call node @@ RPC.get_chain_block_context_dal_shards ~level () + Node.RPC.call node + @@ RPC.get_chain_block_context_dal_shards ?level ?delegates () in return @@ List.map (fun json -> - let pkh = JSON.(json |=> 0 |> as_string) in - let first_shard_index = JSON.(json |=> 1 |=> 0 |> as_int) in - let power = JSON.(json |=> 1 |=> 1 |> as_int) in - {attester = pkh; first_shard_index; power}) + let attester = JSON.(json |-> "delegate" |> as_string) in + let indexes = + JSON.(json |-> "indexes" |> as_list |> List.map as_int) + in + {attester; indexes}) (JSON.as_list json) end diff --git a/tezt/lib_tezos/dal_common.mli b/tezt/lib_tezos/dal_common.mli index ba19b43c6eca..5df0dd0b9804 100644 --- a/tezt/lib_tezos/dal_common.mli +++ b/tezt/lib_tezos/dal_common.mli @@ -264,13 +264,14 @@ module Commitment : sig end module Committee : sig - type member = {attester : string; first_shard_index : int; power : int} + type member = {attester : string; indexes : int list} type t = member list val typ : t Check.typ - val at_level : Node.t -> level:int -> t Lwt.t + val at_level : + Node.t -> ?level:int -> ?delegates:string list -> unit -> t Lwt.t end module Check : sig diff --git a/tezt/lib_tezos/operation_core.ml b/tezt/lib_tezos/operation_core.ml index 4407dd160edd..147cd49a195e 100644 --- a/tezt/lib_tezos/operation_core.ml +++ b/tezt/lib_tezos/operation_core.ml @@ -830,3 +830,7 @@ let inject_error_check_recommended_fee ~loc ~rex ~expected_fee op client = int ~error_msg:("The recommended fee is %L but expected %R at " ^ loc)) ; unit + +let dal_data_availibility_attester_not_in_committee = + rex + {|The attester (tz[\w\d]+), with slot ([\d]+), is not part of the DAL committee for the level ([\d]+)\.|} diff --git a/tezt/lib_tezos/operation_core.mli b/tezt/lib_tezos/operation_core.mli index 270f8833c89b..28e140fd41c4 100644 --- a/tezt/lib_tezos/operation_core.mli +++ b/tezt/lib_tezos/operation_core.mli @@ -675,6 +675,13 @@ val rejected_by_full_mempool_with_needed_fee : rex Captures [hash]. *) val rejected_by_full_mempool_no_possible_fee : rex +(** Matches the message produced by + [Dal_data_availibility_attester_not_in_committee {attester; level; slot}] + from [src/proto_alpha/lib_protocol/dal_errors_repr]. + + Captures [attester], [level], and [slot]. *) +val dal_data_availibility_attester_not_in_committee : rex + (** Calls {!inject_and_capture2_stderr} and checks that the second captured group is [expected_fee]. diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index dd89750ab5f1..cb6cf26553b7 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -618,10 +618,10 @@ let test_one_committee_per_level _protocol _parameters _cryptobox node _client that is indeed the case. *) assert (current_level.cycle_position = 0) ; let* current_committee = - Dal.Committee.at_level node ~level:current_level.level + Dal.Committee.at_level node ~level:current_level.level () in let* next_committee = - Dal.Committee.at_level node ~level:(current_level.level + 1) + Dal.Committee.at_level node ~level:(current_level.level + 1) () in Check.((current_committee <> next_committee) Dal.Committee.typ) ~error_msg:"Unexpected equal DAL committees at subsequent levels: %L and %R" ; @@ -1046,24 +1046,16 @@ let test_slots_attestation_operation_dal_committee_membership_check protocol let* () = bake_for ~count:(num_cycles * blocks_per_cycle) client in (* We iterate until we find a level for which the new account has no assigned shard. Recall that the probability to not be assigned a shard is around - 3/4, we so a level is found quickly. *) + 3/4, so a level is found quickly. *) let rec iter () = let* level = Client.level client in - let* json = - Node.RPC.(call node @@ get_chain_block_context_dal_shards ~level ()) - in - let committee = - JSON.as_list json - |> List.map (fun tuple -> - let pair = JSON.as_list tuple in - match pair with - | [pkh; _] -> JSON.as_string pkh - | _ -> - Test.fail - "could not parse result of \ - [get_chain_block_context_dal_shards] RPC") - in - if List.mem new_account.public_key_hash committee then ( + let* committee = Dal.Committee.at_level node ~level () in + if + List.exists + (fun member -> + String.equal member.Dal.Committee.attester new_account.public_key_hash) + committee + then ( Log.info "Bake another block to change the DAL committee" ; let* () = bake_for client in iter ()) @@ -1087,7 +1079,13 @@ let test_slots_attestation_operation_dal_committee_membership_check protocol ~error_msg: "The new account is not a validator, the test needs to be adapted") ; let* (`OpHash _oph) = - inject_dal_attestation ~nb_slots ~level ~signer:new_account [] client + inject_dal_attestation + ~error:Operation.dal_data_availibility_attester_not_in_committee + ~nb_slots + ~level + ~signer:new_account + [] + client in (* Bake with all the bootstrap accounts, but not with the new account. *) let* () = @@ -2302,7 +2300,7 @@ let test_dal_node_get_assigned_shard_indices _protocol _parameters _cryptobox let* {level; _} = Node.RPC.(call node @@ get_chain_block_helper_current_level ()) in - let* committee_from_l1 = Dal.Committee.at_level node ~level in + let* committee_from_l1 = Dal.Committee.at_level node ~level () in let* shards_from_dal = Dal_RPC.(call dal_node @@ get_assigned_shard_indices ~level ~pkh) in @@ -2313,9 +2311,7 @@ let test_dal_node_get_assigned_shard_indices _protocol _parameters _cryptobox with | None -> Test.fail ~__LOC__ "pkh %S not found in committee from L1." pkh | Some member -> - let shards_from_l1 = - List.init member.power (fun i -> member.first_shard_index + i) - in + let shards_from_l1 = member.indexes in Check.( (shards_from_dal = shards_from_l1) (list int) @@ -2605,6 +2601,8 @@ let test_attester_with_bake_for _protocol parameters cryptobox node client let last_checked_level = intermediary_level + unattested_levels - 1 in let last_level = last_checked_level + lag + 1 in + Log.info "attestation_lag = %d" lag ; + (* Publish and bake with client *) let publish source ~index message = let* _op_hash = @@ -2624,6 +2622,7 @@ let test_attester_with_bake_for _protocol parameters cryptobox node client store_slot ~with_proof:false dal_node @@ make_slot ~slot_size slot_content) in + Log.info "Slot with %d index (normally) published at level %d" index level ; unit in let publish_and_bake ~from_level ~to_level delegates = @@ -3196,10 +3195,11 @@ let event_with_message_to_string = function {!check_events_with_topic}, except that what varies here is the shard index instead of slot index. Moreover, shards do not necessiraly start from 0 or end at number_of_shards - 1. *) -let check_events_with_message ~event_with_message dal_node ~from_shard ~to_shard - ~expected_commitment ~expected_level ~expected_pkh ~expected_slot = - let remaining = ref (to_shard - from_shard + 1) in - let seen = Array.make !remaining false in +let check_events_with_message ~event_with_message dal_node ~number_of_shards + ~shard_indexes ~expected_commitment ~expected_level ~expected_pkh + ~expected_slot = + let remaining = ref (List.length shard_indexes) in + let seen = Array.make number_of_shards false in let get_shard_index_opt event = let topic_slot_index = JSON.(event |-> "topic" |-> "slot_index" |> as_int) @@ -3228,21 +3228,23 @@ let check_events_with_message ~event_with_message dal_node ~from_shard ~to_shard in Some shard_index in + let all_seen () = + seen |> Array.to_seqi + |> Seq.for_all (fun (i, b) -> if List.mem i shard_indexes then b else true) + in wait_for_gossipsub_worker_event dal_node ~name:(event_with_message_to_string event_with_message) (fun event -> let*?? shard_index = get_shard_index_opt event in - let index = shard_index - from_shard in Check.( - (seen.(index) = false) + (seen.(shard_index) = false) bool ~error_msg: (sf "Shard_index %d already seen. Invariant broken" shard_index)) ; - seen.(index) <- true ; + seen.(shard_index) <- true ; let () = remaining := !remaining - 1 in - if !remaining = 0 && Array.for_all (fun b -> b) seen then Some () - else None) + if !remaining = 0 && all_seen () then Some () else None) type event_with_message_id = IHave of {pkh : string; slot_index : int} | IWant @@ -3253,11 +3255,11 @@ let event_with_message_id_to_string = function (** This function monitors the Gossipsub worker events whose name is given by [event_with_message_id]. It's somehow similar to function {!check_events_with_message}, but for IHave and IWant messages' events. *) -let check_events_with_message_id ~event_with_message_id dal_node ~from_shard - ~to_shard ~expected_commitment ~expected_level ~expected_pkh ~expected_slot - ~expected_peer = - let remaining = ref (to_shard - from_shard + 1) in - let seen = Array.make !remaining false in +let check_events_with_message_id ~event_with_message_id dal_node + ~number_of_shards ~shard_indexes ~expected_commitment ~expected_level + ~expected_pkh ~expected_slot ~expected_peer = + let remaining = ref (List.length shard_indexes) in + let seen = Array.make number_of_shards false in let get_shard_indices_of_messages event = let*?? () = check_expected expected_peer JSON.(event |-> "peer" |> as_string) @@ -3287,6 +3289,10 @@ let check_events_with_message_id ~event_with_message_id dal_node ~from_shard Some shard_index) message_ids in + let all_seen () = + seen |> Array.to_seqi + |> Seq.for_all (fun (i, b) -> if List.mem i shard_indexes then b else true) + in wait_for_gossipsub_worker_event ~name:(event_with_message_id_to_string event_with_message_id) dal_node @@ -3297,27 +3303,26 @@ let check_events_with_message_id ~event_with_message_id dal_node ~from_shard match shard_index_opt with | None -> () | Some shard_index -> - let index = shard_index - from_shard in Check.( - (seen.(index) = false) + (seen.(shard_index) = false) bool ~error_msg: (sf "Shard_index %d already seen. Invariant broken" shard_index)) ; - seen.(index) <- true ; + seen.(shard_index) <- true ; decr remaining) shard_indices ; - if !remaining = 0 && Array.for_all (fun b -> b) seen then Some () - else None) + if !remaining = 0 && all_seen () then Some () else None) (** This function is quite similar to those above, except that it checks that a range of messages (shards) on a tracked topic have been notified by GS to the DAL node. This is typically needed to then be able to attest slots. *) -let check_message_notified_to_app_event dal_node ~from_shard ~to_shard - ~expected_commitment ~expected_level ~expected_pkh ~expected_slot = - let remaining = ref (to_shard - from_shard + 1) in - let seen = Array.make !remaining false in +let check_message_notified_to_app_event dal_node ~number_of_shards + ~shard_indexes ~expected_commitment ~expected_level ~expected_pkh + ~expected_slot = + let remaining = ref (List.length shard_indexes) in + let seen = Array.make number_of_shards false in let get_shard_index_opt event = let level = JSON.(event |-> "level" |> as_int) in let slot_index = JSON.(event |-> "slot_index" |> as_int) in @@ -3330,21 +3335,23 @@ let check_message_notified_to_app_event dal_node ~from_shard ~to_shard let*?? () = check_expected expected_commitment commitment in Some shard_index in + let all_seen () = + seen |> Array.to_seqi + |> Seq.for_all (fun (i, b) -> if List.mem i shard_indexes then b else true) + in Dal_node.wait_for dal_node "gossipsub_transport_event-message_notified_to_app.v0" (fun event -> let*?? shard_index = get_shard_index_opt event in - let index = shard_index - from_shard in Check.( - (seen.(index) = false) + (seen.(shard_index) = false) bool ~error_msg: (sf "Shard_index %d already seen. Invariant broken" shard_index)) ; - seen.(index) <- true ; + seen.(shard_index) <- true ; let () = remaining := !remaining - 1 in - if !remaining = 0 && Array.for_all (fun b -> b) seen then Some () - else None) + if !remaining = 0 && all_seen () then Some () else None) (** Connect [dal_node1] and [dal_node2] using the bootstrap peer mechanism. [dal_node2] will use [dal_node1] as a bootstrap peer. @@ -3414,15 +3421,15 @@ let nodes_join_the_same_topics dal_node1 dal_node2 ~num_slots ~pkh1 = one at the attesattion level corresponding to [publish_level]. *) let waiters_publish_shards l1_committee dal_node commitment ~publish_level - ~slot_index = + ~slot_index ~number_of_shards = let open Dal.Committee in List.map - (fun {attester; first_shard_index; power} -> + (fun {attester; indexes} -> check_events_with_message ~event_with_message:Publish_message dal_node - ~from_shard:first_shard_index - ~to_shard:(first_shard_index + power - 1) + ~number_of_shards + ~shard_indexes:indexes ~expected_commitment:commitment ~expected_level:publish_level ~expected_pkh:attester @@ -3436,15 +3443,15 @@ let waiters_publish_shards l1_committee dal_node commitment ~publish_level The [l1_committee] used to determine the topic of published messages is the one at the attesattion level corresponding to [publish_level]. *) let waiter_receive_shards l1_committee dal_node commitment ~publish_level - ~slot_index ~pkh ~from_peer = + ~slot_index ~pkh ~from_peer ~number_of_shards = let open Dal.Committee in match List.find (fun {attester; _} -> attester = pkh) l1_committee with - | {attester; first_shard_index; power} -> + | {attester; indexes} -> check_events_with_message ~event_with_message:(Message_with_header from_peer) dal_node - ~from_shard:first_shard_index - ~to_shard:(first_shard_index + power - 1) + ~number_of_shards + ~shard_indexes:indexes ~expected_commitment:commitment ~expected_level:publish_level ~expected_pkh:attester @@ -3459,14 +3466,14 @@ let waiter_receive_shards l1_committee dal_node commitment ~publish_level The [l1_committee] used to determine the topic of published messages is the one at the attesattion level corresponding to [publish_level]. *) let waiter_successful_shards_app_notification l1_committee dal_node commitment - ~publish_level ~slot_index ~pkh = + ~publish_level ~slot_index ~pkh ~number_of_shards = let open Dal.Committee in match List.find (fun {attester; _} -> attester = pkh) l1_committee with - | {attester; first_shard_index; power} -> + | {attester; indexes} -> check_message_notified_to_app_event dal_node - ~from_shard:first_shard_index - ~to_shard:(first_shard_index + power - 1) + ~number_of_shards + ~shard_indexes:indexes ~expected_commitment:commitment ~expected_level:publish_level ~expected_pkh:attester @@ -3523,6 +3530,7 @@ let generic_gs_messages_exchange protocol parameters _cryptobox node client let* () = connect_nodes_via_p2p dal_node1 dal_node2 in let num_slots = parameters.Dal.Parameters.number_of_slots in + let number_of_shards = parameters.Dal.Parameters.cryptobox.number_of_shards in let account1 = Constant.bootstrap1 in let pkh1 = account1.public_key_hash in (* The two nodes join the same topics *) @@ -3552,7 +3560,7 @@ let generic_gs_messages_exchange protocol parameters _cryptobox node client let* publish_level = next_level node in let attested_level = publish_level + parameters.attestation_lag in let attestation_level = attested_level - 1 in - let* committee = Dal.Committee.at_level node ~level:attestation_level in + let* committee = Dal.Committee.at_level node ~level:attestation_level () in let waiter_publish_list = waiters_publish_shards @@ -3561,6 +3569,7 @@ let generic_gs_messages_exchange protocol parameters _cryptobox node client slot_commitment ~publish_level ~slot_index + ~number_of_shards in let waiter_receive_shards = waiter_receive_shards @@ -3572,6 +3581,7 @@ let generic_gs_messages_exchange protocol parameters _cryptobox node client ~pkh:pkh1 ~from_peer: JSON.(Dal_node.read_identity dal_node1 |-> "peer_id" |> as_string) + ~number_of_shards in let waiter_app_notifs = if expect_app_notification then @@ -3582,6 +3592,7 @@ let generic_gs_messages_exchange protocol parameters _cryptobox node client ~publish_level ~slot_index ~pkh:pkh1 + ~number_of_shards else unit in @@ -3670,6 +3681,7 @@ let _test_gs_prune_ihave_and_iwant protocol parameters _cryptobox node client repeat_i (n - 1) f in let crypto_params = parameters.Dal.Parameters.cryptobox in + let number_of_shards = crypto_params.number_of_shards in let slot_size = crypto_params.slot_size in let slot_content = generate_dummy_slot slot_size in @@ -3748,23 +3760,22 @@ let _test_gs_prune_ihave_and_iwant protocol parameters _cryptobox node client let* publish_level = next_level node in let attested_level = publish_level + parameters.attestation_lag in let attestation_level = attested_level - 1 in - let* committee = Dal.Committee.at_level node ~level:attestation_level in + let* committee = Dal.Committee.at_level node ~level:attestation_level () in - let Dal.Committee.{attester; first_shard_index; power} = + let Dal.Committee.{attester; indexes = shard_indexes} = match List.find (fun Dal.Committee.{attester; _} -> attester = pkh1) committee with - | {attester; first_shard_index; power} -> - {attester; first_shard_index; power} | exception Not_found -> Test.fail "Should not happen as %s is part of the committee" pkh1 + | v -> v in let iwant_events_waiter = check_events_with_message_id ~event_with_message_id:IWant dal_node1 - ~from_shard:first_shard_index - ~to_shard:(first_shard_index + power - 1) + ~number_of_shards + ~shard_indexes ~expected_commitment:commitment ~expected_level:publish_level ~expected_pkh:attester @@ -3775,8 +3786,8 @@ let _test_gs_prune_ihave_and_iwant protocol parameters _cryptobox node client check_events_with_message_id ~event_with_message_id:(IHave {pkh = pkh1; slot_index = 0}) dal_node2 - ~from_shard:first_shard_index - ~to_shard:(first_shard_index + power - 1) + ~number_of_shards + ~shard_indexes ~expected_commitment:commitment ~expected_level:publish_level ~expected_pkh:attester -- GitLab From 4dc5ef7a58a9eb1cb7386a9d539b46e68056863a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Wed, 13 Mar 2024 16:20:59 +0100 Subject: [PATCH 4/7] Alpha/DAL: update DAL parameters for sandbox and test --- .../lib_parameters/default_parameters.ml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/proto_alpha/lib_parameters/default_parameters.ml b/src/proto_alpha/lib_parameters/default_parameters.ml index 0c86fa36ac1d..84221c87ba73 100644 --- a/src/proto_alpha/lib_parameters/default_parameters.ml +++ b/src/proto_alpha/lib_parameters/default_parameters.ml @@ -329,12 +329,7 @@ let constants_sandbox = constants_mainnet.dal with number_of_slots = 16; cryptobox_parameters = - { - Dal.redundancy_factor = 16; - page_size = 4096; - number_of_shards = 2048; - slot_size = 1 lsl 16; - }; + {default_cryptobox_parameters with number_of_shards = 256}; }; issuance_weights; blocks_preservation_cycles = 1; @@ -356,7 +351,7 @@ let constants_sandbox = } let constants_test = - let consensus_committee_size = 25 in + let consensus_committee_size = 67 in let Constants.Generated. {max_slashing_threshold = _; consensus_threshold; issuance_weights} = Constants.Generated.generate ~consensus_committee_size @@ -370,10 +365,9 @@ let constants_test = number_of_slots = 8; cryptobox_parameters = { - redundancy_factor = 16; - page_size = 4096; - number_of_shards = 2048; - slot_size = 1 lsl 16; + default_cryptobox_parameters with + redundancy_factor = 2; + number_of_shards = 64; }; }; issuance_weights; -- GitLab From 1cbf4be750125bac7495c042de4df37f2717a92e Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Wed, 13 Mar 2024 17:51:00 +0100 Subject: [PATCH 5/7] Tezt: reset regressions for new constants --- ...pha- (mode client) RPC regression tests- misc_protocol.out | 4 ++-- ...lpha- (mode light) RPC regression tests- misc_protocol.out | 4 ++-- ...lpha- (mode proxy) RPC regression tests- misc_protocol.out | 4 ++-- ...y_server_data_dir) RPC regression tests- misc_protocol.out | 4 ++-- ... proxy_server_rpc) RPC regression tests- misc_protocol.out | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out index b1531bb1e30c..23fd8a4909f5 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode client) RPC regression tests- misc_protocol.out @@ -39,8 +39,8 @@ "dal_parametric": { "feature_enable": true, "incentives_enable": false, "number_of_slots": 16, "attestation_lag": 8, - "attestation_threshold": 66, "redundancy_factor": 16, - "page_size": 4096, "slot_size": 65536, "number_of_shards": 2048 }, + "attestation_threshold": 66, "redundancy_factor": 8, "page_size": 3967, + "slot_size": 126944, "number_of_shards": 256 }, "smart_rollup_arith_pvm_enable": false, "smart_rollup_origination_size": 6314, "smart_rollup_challenge_window_in_blocks": 120960, diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out index fd631fadbdea..18fb176fa807 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode light) RPC regression tests- misc_protocol.out @@ -39,8 +39,8 @@ "dal_parametric": { "feature_enable": true, "incentives_enable": false, "number_of_slots": 16, "attestation_lag": 8, - "attestation_threshold": 66, "redundancy_factor": 16, - "page_size": 4096, "slot_size": 65536, "number_of_shards": 2048 }, + "attestation_threshold": 66, "redundancy_factor": 8, "page_size": 3967, + "slot_size": 126944, "number_of_shards": 256 }, "smart_rollup_arith_pvm_enable": false, "smart_rollup_origination_size": 6314, "smart_rollup_challenge_window_in_blocks": 120960, diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out index 55b3af2b38d9..e32252a663fe 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy) RPC regression tests- misc_protocol.out @@ -39,8 +39,8 @@ "dal_parametric": { "feature_enable": true, "incentives_enable": false, "number_of_slots": 16, "attestation_lag": 8, - "attestation_threshold": 66, "redundancy_factor": 16, - "page_size": 4096, "slot_size": 65536, "number_of_shards": 2048 }, + "attestation_threshold": 66, "redundancy_factor": 8, "page_size": 3967, + "slot_size": 126944, "number_of_shards": 256 }, "smart_rollup_arith_pvm_enable": false, "smart_rollup_origination_size": 6314, "smart_rollup_challenge_window_in_blocks": 120960, diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out index 2acfc59c2cbb..fa4874fddcc1 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_data_dir) RPC regression tests- misc_protocol.out @@ -39,8 +39,8 @@ "dal_parametric": { "feature_enable": true, "incentives_enable": false, "number_of_slots": 16, "attestation_lag": 8, - "attestation_threshold": 66, "redundancy_factor": 16, - "page_size": 4096, "slot_size": 65536, "number_of_shards": 2048 }, + "attestation_threshold": 66, "redundancy_factor": 8, "page_size": 3967, + "slot_size": 126944, "number_of_shards": 256 }, "smart_rollup_arith_pvm_enable": false, "smart_rollup_origination_size": 6314, "smart_rollup_challenge_window_in_blocks": 120960, diff --git a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out index 2acfc59c2cbb..fa4874fddcc1 100644 --- a/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out +++ b/tezt/tests/expected/RPC_test.ml/Alpha- (mode proxy_server_rpc) RPC regression tests- misc_protocol.out @@ -39,8 +39,8 @@ "dal_parametric": { "feature_enable": true, "incentives_enable": false, "number_of_slots": 16, "attestation_lag": 8, - "attestation_threshold": 66, "redundancy_factor": 16, - "page_size": 4096, "slot_size": 65536, "number_of_shards": 2048 }, + "attestation_threshold": 66, "redundancy_factor": 8, "page_size": 3967, + "slot_size": 126944, "number_of_shards": 256 }, "smart_rollup_arith_pvm_enable": false, "smart_rollup_origination_size": 6314, "smart_rollup_challenge_window_in_blocks": 120960, -- GitLab From c2fbcaab27ad7e7b57ff599f0ea2450cb5d5caaa Mon Sep 17 00:00:00 2001 From: Eugen Zalinescu Date: Wed, 13 Mar 2024 18:25:18 +0100 Subject: [PATCH 6/7] Tezt/DAL: reset regressions --- ...ith L1 (mainnet_lag-3_time-10_preinject-1_sl.out | 13 ++++++------- ...ith L1 (mainnet_lag-3_time-5_preinject-10_sl.out | 12 ++++++------ ...node with L1 (rollup_node_applies_dal_pages).out | 2 +- ...d node with L1 (rollup_node_downloads_slots).out | 2 +- ...ith L1 (test_lag-3_time-12_preinject-1_slots.out | 2 +- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out index 49c59a0490de..336645b303e3 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out @@ -34,9 +34,9 @@ This sequence of operations was run: Smart rollup [SMART_ROLLUP_HASH] memorized as "rollup" -./octez-client --wait none send smart rollup message '[" dal:256:3:256:5 "]' from bootstrap2 +./octez-client --wait none send smart rollup message '[" dal:32:3:32:5 "]' from bootstrap2 Node is bootstrapped. -Estimated gas: 170.826 units (will add 100 for safety) +Estimated gas: 170.750 units (will add 100 for safety) Estimated storage: no bytes added Operation successfully injected in the node. Operation hash is '[OPERATION_HASH]' @@ -47,16 +47,16 @@ 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.000277 + Fee to the baker: ꜩ0.000275 Expected counter: 1 Gas limit: 271 Storage limit: 0 bytes Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ0.000277 - payload fees(the block proposer) ....... +ꜩ0.000277 + [PUBLIC_KEY_HASH] ... -ꜩ0.000275 + payload fees(the block proposer) ....... +ꜩ0.000275 Smart rollup messages submission: This smart rollup messages submission was successfully applied - Consumed gas: 170.760 + Consumed gas: 170.684 ./octez-client --wait none send smart rollup message '[" value"]' from bootstrap2 @@ -90,4 +90,3 @@ GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value 200 OK "00000005" - diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-5_preinject-10_sl.out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-5_preinject-10_sl.out index e3abe8d06354..2cc8320b3196 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-5_preinject-10_sl.out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-5_preinject-10_sl.out @@ -34,9 +34,9 @@ This sequence of operations was run: Smart rollup [SMART_ROLLUP_HASH] memorized as "rollup" -./octez-client --wait none send smart rollup message '[" dal:256:3:256:5 "]' from bootstrap2 +./octez-client --wait none send smart rollup message '[" dal:32:3:32:5 "]' from bootstrap2 Node is bootstrapped. -Estimated gas: 170.826 units (will add 100 for safety) +Estimated gas: 170.750 units (will add 100 for safety) Estimated storage: no bytes added Operation successfully injected in the node. Operation hash is '[OPERATION_HASH]' @@ -47,16 +47,16 @@ 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.000277 + Fee to the baker: ꜩ0.000275 Expected counter: 1 Gas limit: 271 Storage limit: 0 bytes Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ0.000277 - payload fees(the block proposer) ....... +ꜩ0.000277 + [PUBLIC_KEY_HASH] ... -ꜩ0.000275 + payload fees(the block proposer) ....... +ꜩ0.000275 Smart rollup messages submission: This smart rollup messages submission was successfully applied - Consumed gas: 170.760 + Consumed gas: 170.684 ./octez-client --wait none send smart rollup message '["++++ value"]' from bootstrap2 diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out index d64fd51a1bf9..b1c30c2808ff 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_applies_dal_pages).out @@ -38,7 +38,7 @@ Smart rollup [SMART_ROLLUP_HASH] memorized as "rollup" { "level": 2, "commitment_hash": "[SC_ROLLUP_COMMITMENT_HASH]" } -./octez-client --wait none send smart rollup message '["dal:16:8:16:0:2:4:6"]' from bootstrap2 +./octez-client --wait none send smart rollup message '["dal:16:8:32:0:2:4:6"]' from bootstrap2 Node is bootstrapped. Estimated gas: 170.903 units (will add 100 for safety) Estimated storage: no bytes added diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out index 740ae2854036..b550bb53b458 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (rollup_node_downloads_slots).out @@ -38,7 +38,7 @@ Smart rollup [SMART_ROLLUP_HASH] memorized as "rollup" { "level": 2, "commitment_hash": "[SC_ROLLUP_COMMITMENT_HASH]" } -./octez-client --wait none send smart rollup message '["dal:16:8:16:0:2:4:6"]' from bootstrap2 +./octez-client --wait none send smart rollup message '["dal:16:8:32:0:2:4:6"]' from bootstrap2 Node is bootstrapped. Estimated gas: 170.903 units (will add 100 for safety) Estimated storage: no bytes added diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-12_preinject-1_slots.out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-12_preinject-1_slots.out index a73abd633dde..b66d0546d08d 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-12_preinject-1_slots.out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-12_preinject-1_slots.out @@ -34,7 +34,7 @@ This sequence of operations was run: Smart rollup [SMART_ROLLUP_HASH] memorized as "rollup" -./octez-client --wait none send smart rollup message '[" dal:16:3:16:5 "]' from bootstrap2 +./octez-client --wait none send smart rollup message '[" dal:16:3:32:5 "]' from bootstrap2 Node is bootstrapped. Estimated gas: 170.750 units (will add 100 for safety) Estimated storage: no bytes added -- GitLab From 2038aaa0776fb812aa0bb220978cb457cf461321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Wed, 13 Mar 2024 22:55:15 +0100 Subject: [PATCH 7/7] Tezt/DAL: more regression tests --- tezt/tests/dal.ml | 12 +-- ... (mainnet_lag-3_time-10_preinject-1_sl.out | 1 + ... (test_lag-3_time-4_preinject-5_slots-.out | 97 ------------------- 3 files changed, 2 insertions(+), 108 deletions(-) delete mode 100644 tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-4_preinject-5_slots-.out diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index cb6cf26553b7..4f5e021fb086 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -2987,16 +2987,6 @@ let e2e_tests = num_extra_nodes = 2; } in - let test2 = - { - constants = Protocol.Constants_test; - attestation_lag = 3; - block_delay = 4; - number_of_dal_slots = 5; - beforehand_slot_injection = 5; - num_extra_nodes = 2; - } - in let mainnet1 = { constants = Protocol.Constants_mainnet; @@ -3017,7 +3007,7 @@ let e2e_tests = num_extra_nodes = 0; } in - [test1; test2; mainnet1; mainnet2] + [test1; mainnet1; mainnet2] (* This function allows to register new (end-to-end) tests using [scenario_with_all_nodes] helper. For that, it instantiate function diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out index 336645b303e3..3315b709735e 100644 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out +++ b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (mainnet_lag-3_time-10_preinject-1_sl.out @@ -90,3 +90,4 @@ GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value 200 OK "00000005" + diff --git a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-4_preinject-5_slots-.out b/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-4_preinject-5_slots-.out deleted file mode 100644 index 764b15a75e10..000000000000 --- a/tezt/tests/expected/dal.ml/Alpha- Testing DAL rollup and node with L1 (test_lag-3_time-4_preinject-5_slots-.out +++ /dev/null @@ -1,97 +0,0 @@ - -./octez-client --wait none originate smart rollup rollup from '[PUBLIC_KEY_HASH]' of kind arith of type string with kernel --burn-cap 9999999 -Node is bootstrapped. -Estimated gas: 1930.030 units (will add 100 for safety) -Estimated storage: 6552 bytes added (will add 20 for safety) -Operation successfully injected in the node. -Operation hash is '[OPERATION_HASH]' -NOT waiting for the operation to be included. -Use command - octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] -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.000441 - Expected counter: 1 - Gas limit: 2031 - Storage limit: 6572 bytes - Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ0.000441 - payload fees(the block proposer) ....... +ꜩ0.000441 - Smart rollup origination: - Kind: arith - Parameter type: string - Kernel Blake2B hash: '0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8' - This smart rollup origination was successfully applied - Consumed gas: 1929.997 - Storage size: 6552 bytes - Address: [SMART_ROLLUP_HASH] - Genesis commitment hash: [SC_ROLLUP_COMMITMENT_HASH] - Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ1.638 - storage fees ........................... +ꜩ1.638 - -Smart rollup [SMART_ROLLUP_HASH] memorized as "rollup" - -./octez-client --wait none send smart rollup message '[" dal:16:3:16:5 "]' from bootstrap2 -Node is bootstrapped. -Estimated gas: 170.750 units (will add 100 for safety) -Estimated storage: no bytes added -Operation successfully injected in the node. -Operation hash is '[OPERATION_HASH]' -NOT waiting for the operation to be included. -Use command - octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] -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.000275 - Expected counter: 1 - Gas limit: 271 - Storage limit: 0 bytes - Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ0.000275 - payload fees(the block proposer) ....... +ꜩ0.000275 - Smart rollup messages submission: - This smart rollup messages submission was successfully applied - Consumed gas: 170.684 - - -./octez-client --wait none send smart rollup message '["++++ value"]' from bootstrap2 -Node is bootstrapped. -Estimated gas: 170.559 units (will add 100 for safety) -Estimated storage: no bytes added -Operation successfully injected in the node. -Operation hash is '[OPERATION_HASH]' -NOT waiting for the operation to be included. -Use command - octez-client wait for [OPERATION_HASH] to be included --confirmations 1 --branch [BLOCK_HASH] -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.00027 - Expected counter: 7 - Gas limit: 271 - Storage limit: 0 bytes - Balance updates: - [PUBLIC_KEY_HASH] ... -ꜩ0.00027 - payload fees(the block proposer) ....... +ꜩ0.00027 - Smart rollup messages submission: - This smart rollup messages submission was successfully applied - Consumed gas: 170.492 - -GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value -200 OK -"00000037" - -GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value -200 OK -"00000037" - -GET http://[HOST]:[PORT]/global/block/head/state?key=vars/value -200 OK -"00000037" - -- GitLab