diff --git a/manifest/product_octez.ml b/manifest/product_octez.ml index fc32fd9b2b48783e7000115066cac71007882b5c..3a55338397a67615d9864f2c81c69251e6612864 100644 --- a/manifest/product_octez.ml +++ b/manifest/product_octez.ml @@ -6889,6 +6889,7 @@ let hash = Protocol.hash |> error_monad_module N.(number <= 018); octez_protocol_compiler_registerer |> open_; octez_stdlib_unix |> open_; + octez_shell_services |> open_; octez_dal_node_lib |> open_; client |> if_some |> open_; plugin |> if_some |> open_; diff --git a/src/bin_dal_node/accuser.ml b/src/bin_dal_node/accuser.ml new file mode 100644 index 0000000000000000000000000000000000000000..24be48b8259b89a45a45993b85e24dcd550ec818 --- /dev/null +++ b/src/bin_dal_node/accuser.ml @@ -0,0 +1,163 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +module Shard_index_map = Map.Make (Int) + +let shard_index_map_from_committee committee = + Signature.Public_key_hash.Map.fold + (fun pkh shard_indexes map -> + List.fold_left + (fun map shard_index -> Shard_index_map.add shard_index pkh map) + map + shard_indexes) + committee + Shard_index_map.empty + +let get_shards_for_slot_id node_ctxt ~slot_id = + let proto_parameters = Node_context.get_proto_parameters node_ctxt in + let number_of_shards = + proto_parameters.cryptobox_parameters.number_of_shards + in + let store = Node_context.get_store node_ctxt in + let shards_store = Store.shards store in + let shards = Store.Shards.read_all shards_store slot_id ~number_of_shards in + Seq_s.filter_map + (function + | _slot_id, index, Ok share -> Some Cryptobox.{index; share} + | _slot_id, _, Error _error -> None) + shards + +let get_shard_proof_for_shard_index node_ctxt ~slot_id ~shard_index = + let store = Node_context.get_store node_ctxt in + let slot_id_cache = Store.finalized_commitments store in + match Store.Slot_id_cache.find_opt slot_id_cache slot_id with + | None -> None + | Some commitment -> ( + let commitments_indexed_cache = Store.cache store in + let shard_proof_opt = + Store.Commitment_indexed_cache.find_opt + commitments_indexed_cache + commitment + in + match shard_proof_opt with + | None -> None + | Some (_slot, _shares, shard_proofs) -> + let proof = Array.get shard_proofs shard_index in + Some proof) + +let get_entrapments_for_slot_index node_ctxt ~shard_index_map ~published_level + ~slot_index = + let open Lwt_result_syntax in + let proto_parameters = Node_context.get_proto_parameters node_ctxt in + let slot_id = Types.Slot_id.{slot_level = published_level; slot_index} in + let shards = get_shards_for_slot_id node_ctxt ~slot_id in + let*! () = + let number_of_shards = + proto_parameters.cryptobox_parameters.number_of_shards + in + let*! num_stored_shards = Seq_s.length shards in + if not (Int.equal num_stored_shards number_of_shards) then Lwt.return_unit + (* fixme : raise event *) + else Lwt.return_unit + in + Seq_es.ES.fold_left + (fun acc shard -> + let Cryptobox.{index = shard_index; share} = shard in + match Shard_index_map.find_opt shard_index shard_index_map with + | None -> (* unreachable *) return acc + | Some delegate -> ( + let trap_res = + Trap.share_is_trap + ~traps_fraction:proto_parameters.traps_fraction + delegate + share + in + match trap_res with + | Ok true -> ( + let shard_proof_opt = + get_shard_proof_for_shard_index node_ctxt ~slot_id ~shard_index + in + match shard_proof_opt with + | None -> + (* TODO: emit warning *) + return acc + | Some shard_proof -> + return ((delegate, shard, shard_proof) :: acc)) + | Ok false -> return acc + | Error _ -> + (* TODO: emit warning *) + (* do not accuse if we're not sure *) + return acc)) + [] + (Seq_es.of_seqs shards) + +let get_monitored_slot_indices node_ctxt = + let profile = + Profile_manager.get_profiles (Node_context.get_profile_ctxt node_ctxt) + in + match profile with + | Bootstrap | Random_observer -> [] + | Operator operator_profile -> + Operator_profile.get_all_slot_indexes operator_profile + +let run (module Plugin : Dal_plugin.T) node_ctxt rpc_ctxt ~attested_level = + let open Lwt_result_syntax in + let proto_parameters = Node_context.get_proto_parameters node_ctxt in + when_ proto_parameters.incentives_enable (fun () -> + let monitored_slot_indices = get_monitored_slot_indices node_ctxt in + if List.is_empty monitored_slot_indices then return_unit + else + let published_level = + (* FIXME: https://gitlab.com/tezos/tezos/-/issues/4612 + Correctly compute [published_level] in case of protocol changes, in + particular a change of the value of [attestation_lag]. *) + Int32.(sub attested_level (of_int proto_parameters.attestation_lag)) + in + let* committee = + let attestation_level = Int32.pred attested_level in + Node_context.fetch_committee node_ctxt ~level:attestation_level + in + let shard_index_map = shard_index_map_from_committee committee in + let* block_info = + Plugin.block_info + rpc_ctxt + ~block:(`Level attested_level) + ~metadata:`Always + in + let* attestation_operations = + Plugin.get_attestation_operations block_info + in + let attestation_map = + Signature.Public_key_hash.Map.of_seq + (List.to_seq attestation_operations) + in + List.iter_es + (fun slot_index -> + let* entrapments = + get_entrapments_for_slot_index + node_ctxt + ~shard_index_map + ~published_level + ~slot_index + in + List.iter_es + (fun (delegate, shard, proof) -> + let attestation_opt = + Signature.Public_key_hash.Map.find delegate attestation_map + in + match attestation_opt with + | None -> (* fixme: should not happen *) return_unit + | Some attestation -> + Plugin.inject_entrapment_evidence + rpc_ctxt + ~attested_level + attestation + ~slot_index + ~shard + ~proof) + entrapments) + monitored_slot_indices) diff --git a/src/bin_dal_node/accuser.mli b/src/bin_dal_node/accuser.mli new file mode 100644 index 0000000000000000000000000000000000000000..86a957092a399d9bdc72ae1ecf1e879646d9c516 --- /dev/null +++ b/src/bin_dal_node/accuser.mli @@ -0,0 +1,21 @@ +(*****************************************************************************) +(* *) +(* SPDX-License-Identifier: MIT *) +(* SPDX-FileCopyrightText: 2025 Nomadic Labs, *) +(* *) +(*****************************************************************************) + +(** [run plugin node_ctxt rpc_ctxt ~attested_level] processes + entrapment evidence for slot indices monitored by the DAL node. + + More notably, it: + - fetches attestation operations for the attested block, + - processes potential entrapments for each monitored slot, + - injects entrapment evidence when found. +*) +val run : + (module Dal_plugin.T) -> + Node_context.t -> + Rpc_context.t -> + attested_level:int32 -> + unit tzresult Lwt.t diff --git a/src/bin_dal_node/daemon.ml b/src/bin_dal_node/daemon.ml index 2a34027b4232a0695acde6d08a4cd3fc4149f383..45e0df1d0d7cd77638097af32e4964c642f53f4b 100644 --- a/src/bin_dal_node/daemon.ml +++ b/src/bin_dal_node/daemon.ml @@ -612,6 +612,9 @@ module Handler = struct get_attestations Plugin.is_attested in + let* () = + Accuser.run (module Plugin) ctxt cctxt ~attested_level:block_level + in return_unit else return_unit in diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index f0bcca8cd694893ff6576ce723552dc8e289c0f0..16ce3cb1a4a6c15bf8e0c09d00cf1226ea2c05ba 100644 --- a/src/lib_dal_node/dal_plugin.ml +++ b/src/lib_dal_node/dal_plugin.ml @@ -127,6 +127,8 @@ module type T = sig type dal_attestation + type attestation_operation + val block_info : ?chain:Tezos_shell_services.Block_services.chain -> ?block:Tezos_shell_services.Block_services.block -> @@ -144,6 +146,10 @@ module type T = sig block_info -> (slot_header * operation_application_result) list tzresult Lwt.t + val get_attestation_operations : + block_info -> + (Signature.public_key_hash * attestation_operation) list tzresult Lwt.t + val get_dal_content_of_attestations : block_info -> (int * Signature.Public_key_hash.t option * dal_attestation option) list @@ -161,6 +167,15 @@ module type T = sig val block_shell_header : block_info -> Block_header.shell_header + val inject_entrapment_evidence : + Tezos_rpc.Context.generic -> + attested_level:Int32.t -> + attestation_operation -> + slot_index:slot_index -> + shard:Cryptobox.shard -> + proof:Cryptobox.shard_proof -> + unit tzresult Lwt.t + (* Section of helpers for Skip lists *) module Skip_list : sig diff --git a/src/lib_dal_node/dal_plugin.mli b/src/lib_dal_node/dal_plugin.mli index 4bc7f0429ab2570f3d974136ec5bff0ec56f2720..d2a79b138b971448a0777862e1d5faee8f7c27a6 100644 --- a/src/lib_dal_node/dal_plugin.mli +++ b/src/lib_dal_node/dal_plugin.mli @@ -65,6 +65,8 @@ module type T = sig type dal_attestation + type attestation_operation + (** [block_info ?chain ?block ~metadata ctxt] returns the information of the [block] in [ctxt] for the given [chain]. Block's metadata are included or skipped depending on the value of [metadata]. This is a wrapper on top of @@ -86,6 +88,12 @@ module type T = sig block_info -> (slot_header * operation_application_result) list tzresult Lwt.t + (** For a given block, returns for each included attestation, as a + list, its attester and its attestation operation. *) + val get_attestation_operations : + block_info -> + (Signature.public_key_hash * attestation_operation) list tzresult Lwt.t + (** For a given block, returns for each included attestation, as a list, its Tenderbake slot, its attester (if available in the operation receipt), and its DAL attestation. *) @@ -120,6 +128,17 @@ module type T = sig whose information are given . *) val block_shell_header : block_info -> Block_header.shell_header + (** Call this function to inject an entrapment evidence to the + corresponding L1 node. *) + val inject_entrapment_evidence : + Tezos_rpc.Context.generic -> + attested_level:Int32.t -> + attestation_operation -> + slot_index:slot_index -> + shard:Cryptobox.shard -> + proof:Cryptobox.shard_proof -> + unit tzresult Lwt.t + (* Section of helpers for Skip lists *) module Skip_list : sig diff --git a/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml b/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml index 654898ba401f7b6ab801a54f8e318c1874d1da81..50b5ae17cb5717caeaf444dc9d5e295e775a5caa 100644 --- a/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml +++ b/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml @@ -35,6 +35,8 @@ module Plugin = struct type dal_attestation = Bitset.t + type attestation_operation = Kind.attestation Alpha_context.operation + let parametric_constants chain block ctxt = let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in Protocol.Constants_services.parametric cpctxt (chain, block) @@ -72,6 +74,26 @@ module Plugin = struct blocks_per_cycle = parametric.blocks_per_cycle; } + type error += DAL_accusation_not_available + + let () = + register_error_kind + `Permanent + ~id:"dal_accusation_not_available" + ~title:"DAL accusation not available" + ~description:"DAL accusation is not available in protocol Paris." + ~pp:(fun fmt () -> + Format.fprintf fmt "DAL accusation is not available in protocol Paris") + Data_encoding.unit + (function DAL_accusation_not_available -> Some () | _ -> None) + (fun () -> DAL_accusation_not_available) + + let inject_entrapment_evidence _cctxt ~attested_level:_ _attestation + ~slot_index:_ ~shard:_ ~proof:_ = + let open Lwt_result_syntax in + (* This is supposed to be dead code, but we implement a fallback to be defensive. *) + fail [DAL_accusation_not_available] + let block_info ?chain ?block ~metadata ctxt = let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in Protocol_client_context.Alpha_block_services.info @@ -147,6 +169,58 @@ module Plugin = struct | _ -> None) consensus_ops + (* We can provide more context in the future. *) + type error += Delegate_not_found + + let () = + register_error_kind + `Permanent + ~id:"delegate_not_found" + ~title:"No delegate found in attestation result" + ~description: + "No delegate found in the attestation result when one was expected" + ~pp:(fun fmt () -> + Format.fprintf + fmt + "No delegate found in the attestation result when one was expected") + Data_encoding.unit + (function Delegate_not_found -> Some () | _ -> None) + (fun () -> Delegate_not_found) + + let get_attestation_operations block_info = + let open Lwt_result_syntax in + let open Protocol.Alpha_context in + let open Protocol_client_context.Alpha_block_services in + match List.hd block_info.operations with + | None -> + (* that should be unreachable, as there are 4 operation passes *) + return [] + | Some consensus_ops -> + List.filter_map_es + (fun operation -> + let (Operation_data operation_data) = operation.protocol_data in + match operation_data.contents with + | Single (Attestation _) -> ( + match operation.receipt with + | Receipt (Operation_metadata operation_metadata) -> ( + match operation_metadata.contents with + | Single_result (Attestation_result result) -> + let packed_operation : + Kind.attestation Alpha_context.operation = + { + Alpha_context.shell = operation.shell; + protocol_data = operation_data; + } + in + return_some (result.delegate, packed_operation) + | _ -> + (* should not happen *) + tzfail Delegate_not_found) + | Empty | Too_large | Receipt No_operation_metadata -> + tzfail Delegate_not_found) + | _ -> return_none) + consensus_ops + let get_committee ctxt ~level = let open Lwt_result_syntax in let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in diff --git a/src/proto_020_PsParisC/lib_dal/dune b/src/proto_020_PsParisC/lib_dal/dune index 49958af8559f15be026c11acb4c20ff4f75600e2..fd0e4457c4c157465b123aa57d082db95a2d44fc 100644 --- a/src/proto_020_PsParisC/lib_dal/dune +++ b/src/proto_020_PsParisC/lib_dal/dune @@ -9,6 +9,7 @@ octez-libs.base octez-protocol-compiler.registerer octez-libs.stdlib-unix + octez-shell-libs.shell-services tezos-dal-node-lib octez-protocol-020-PsParisC-libs.client octez-protocol-020-PsParisC-libs.plugin @@ -28,6 +29,7 @@ -open Tezos_base.TzPervasives -open Tezos_protocol_registerer -open Tezos_stdlib_unix + -open Tezos_shell_services -open Tezos_dal_node_lib -open Tezos_client_020_PsParisC -open Tezos_protocol_plugin_020_PsParisC diff --git a/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml b/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml index 9b752919c95b09f9afbdfb7d40ac8700c3ad2331..e88b5e3b81291a730c1048857b4263a1d8790154 100644 --- a/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml +++ b/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml @@ -35,6 +35,8 @@ module Plugin = struct type dal_attestation = Bitset.t + type attestation_operation = Kind.attestation Alpha_context.operation + let parametric_constants chain block ctxt = let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in Protocol.Constants_services.parametric cpctxt (chain, block) @@ -72,6 +74,26 @@ module Plugin = struct blocks_per_cycle = parametric.blocks_per_cycle; } + type error += DAL_accusation_not_available + + let () = + register_error_kind + `Permanent + ~id:"dal_accusation_not_available" + ~title:"DAL accusation not available" + ~description:"DAL accusation is not available in protocol Quebec." + ~pp:(fun fmt () -> + Format.fprintf fmt "DAL accusation is not available in protocol Quebec") + Data_encoding.unit + (function DAL_accusation_not_available -> Some () | _ -> None) + (fun () -> DAL_accusation_not_available) + + let inject_entrapment_evidence _cctxt ~attested_level:_ _attestation + ~slot_index:_ ~shard:_ ~proof:_ = + let open Lwt_result_syntax in + (* This is supposed to be dead code, but we implement a fallback to be defensive. *) + fail [DAL_accusation_not_available] + let block_info ?chain ?block ~metadata ctxt = let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in Protocol_client_context.Alpha_block_services.info @@ -147,6 +169,58 @@ module Plugin = struct | _ -> None) consensus_ops + (* We can provide more context in the future. *) + type error += Delegate_not_found + + let () = + register_error_kind + `Permanent + ~id:"delegate_not_found" + ~title:"No delegate found in attestation result" + ~description: + "No delegate found in the attestation result when one was expected" + ~pp:(fun fmt () -> + Format.fprintf + fmt + "No delegate found in the attestation result when one was expected") + Data_encoding.unit + (function Delegate_not_found -> Some () | _ -> None) + (fun () -> Delegate_not_found) + + let get_attestation_operations block_info = + let open Lwt_result_syntax in + let open Protocol.Alpha_context in + let open Protocol_client_context.Alpha_block_services in + match List.hd block_info.operations with + | None -> + (* that should be unreachable, as there are 4 operation passes *) + return [] + | Some consensus_ops -> + List.filter_map_es + (fun operation -> + let (Operation_data operation_data) = operation.protocol_data in + match operation_data.contents with + | Single (Attestation _) -> ( + match operation.receipt with + | Receipt (Operation_metadata operation_metadata) -> ( + match operation_metadata.contents with + | Single_result (Attestation_result result) -> + let packed_operation : + Kind.attestation Alpha_context.operation = + { + Alpha_context.shell = operation.shell; + protocol_data = operation_data; + } + in + return_some (result.delegate, packed_operation) + | _ -> + (* should not happen *) + tzfail Delegate_not_found) + | Empty | Too_large | Receipt No_operation_metadata -> + tzfail Delegate_not_found) + | _ -> return_none) + consensus_ops + let get_committee ctxt ~level = let open Lwt_result_syntax in let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in diff --git a/src/proto_021_PsQuebec/lib_dal/dune b/src/proto_021_PsQuebec/lib_dal/dune index 65fb946cd70fbe7576bee395bf5f935ad2c5bf9f..2bc3c492d48d88d66b35e32dcb9ce6b9a8309e6a 100644 --- a/src/proto_021_PsQuebec/lib_dal/dune +++ b/src/proto_021_PsQuebec/lib_dal/dune @@ -9,6 +9,7 @@ octez-libs.base octez-protocol-compiler.registerer octez-libs.stdlib-unix + octez-shell-libs.shell-services tezos-dal-node-lib octez-protocol-021-PsQuebec-libs.client octez-protocol-021-PsQuebec-libs.plugin @@ -28,6 +29,7 @@ -open Tezos_base.TzPervasives -open Tezos_protocol_registerer -open Tezos_stdlib_unix + -open Tezos_shell_services -open Tezos_dal_node_lib -open Tezos_client_021_PsQuebec -open Tezos_protocol_plugin_021_PsQuebec diff --git a/src/proto_alpha/lib_dal/dal_plugin_registration.ml b/src/proto_alpha/lib_dal/dal_plugin_registration.ml index 104451287206b21aae63dca596d9754b7470248c..3b97016b442e8ee46c4007938145049c92da2542 100644 --- a/src/proto_alpha/lib_dal/dal_plugin_registration.ml +++ b/src/proto_alpha/lib_dal/dal_plugin_registration.ml @@ -35,6 +35,8 @@ module Plugin = struct type dal_attestation = Environment.Bitset.t + type attestation_operation = Kind.attestation Alpha_context.operation + let parametric_constants chain block ctxt = let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in Protocol.Constants_services.parametric cpctxt (chain, block) @@ -74,6 +76,63 @@ module Plugin = struct blocks_per_cycle = parametric.blocks_per_cycle; } + (* We choose a previous offset (5 blocks from head) to ensure that the + injected operation is branched from a valid + predecessor. Denunciation operations can be emitted when the + consensus is under attack and may occur so you want to inject the + operation from a block which is considered "final". *) + let get_block_offset ~offset level = + let open Lwt_syntax in + let offset_int32 = Raw_level.of_int32_exn (Int32.of_int offset) in + let level_with_offset = Raw_level.diff level offset_int32 in + if Compare.Int32.(level_with_offset >= 0l) then return (`Head offset) + else return (`Head 0) + + let inject_entrapment_evidence cctxt ~attested_level + (operation : attestation_operation) ~slot_index ~shard ~proof = + let open Lwt_result_syntax in + let cpctxt = new Protocol_client_context.wrap_rpc_context cctxt in + let chain = `Main in + let*? attested_level = + Raw_level.of_int32 attested_level |> Environment.wrap_tzresult + in + let*! block = get_block_offset ~offset:5 attested_level in + let* {dal = {number_of_slots; _}; _} = + (* Let's use Head. In practice the number of slots of `Head will + be greater. If the slot index is not valid, in any case it + will be caught by the forge or during the injection. *) + Protocol.Constants_services.parametric cpctxt (chain, `Head 0) + in + (* If the number of slots changes between two protocol, the call + could fail while it should succeed. This is a corner case to + solve in the future (or just apply a retry policy). *) + let*? slot_index = + Dal.Slot_index.of_int ~number_of_slots slot_index + |> Environment.wrap_tzresult + in + let* block_hash = + Protocol_client_context.Alpha_block_services.hash cctxt ~chain ~block () + in + let shard_with_proof = Dal.Shard_with_proof.{shard; proof} in + let protocol_data = operation.protocol_data in + match operation.protocol_data.contents with + | Single (Attestation _) -> + let attestation : Kind.attestation Alpha_context.operation = + {shell = operation.shell; protocol_data} + in + let* bytes = + Plugin.RPC.Forge.dal_entrapment_evidence + cpctxt + (chain, block) + ~branch:block_hash + ~attestation + ~slot_index + ~shard_with_proof + in + let bytes = Signature.concat bytes Signature.zero in + let* _op_hash = Shell_services.Injection.operation cctxt ~chain bytes in + return_unit + let block_info ?chain ?block ~metadata ctxt = let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in Protocol_client_context.Alpha_block_services.info @@ -149,6 +208,58 @@ module Plugin = struct | _ -> None) consensus_ops + (* We can provide more context in the future. *) + type error += Delegate_not_found + + let () = + register_error_kind + `Permanent + ~id:"delegate_not_found" + ~title:"No delegate found in attestation result" + ~description: + "No delegate found in the attestation result when one was expected" + ~pp:(fun fmt () -> + Format.fprintf + fmt + "No delegate found in the attestation result when one was expected") + Data_encoding.unit + (function Delegate_not_found -> Some () | _ -> None) + (fun () -> Delegate_not_found) + + let get_attestation_operations block_info = + let open Lwt_result_syntax in + let open Protocol.Alpha_context in + let open Protocol_client_context.Alpha_block_services in + match List.hd block_info.operations with + | None -> + (* that should be unreachable, as there are 4 operation passes *) + return [] + | Some consensus_ops -> + List.filter_map_es + (fun operation -> + let (Operation_data operation_data) = operation.protocol_data in + match operation_data.contents with + | Single (Attestation _) -> ( + match operation.receipt with + | Receipt (Operation_metadata operation_metadata) -> ( + match operation_metadata.contents with + | Single_result (Attestation_result result) -> + let packed_operation : + Kind.attestation Alpha_context.operation = + { + Alpha_context.shell = operation.shell; + protocol_data = operation_data; + } + in + return_some (result.delegate, packed_operation) + | _ -> + (* should not happen *) + tzfail Delegate_not_found) + | Empty | Too_large | Receipt No_operation_metadata -> + tzfail Delegate_not_found) + | _ -> return_none) + consensus_ops + let get_committee ctxt ~level = let open Lwt_result_syntax in let cpctxt = new Protocol_client_context.wrap_rpc_context ctxt in diff --git a/src/proto_alpha/lib_dal/dune b/src/proto_alpha/lib_dal/dune index 6efe42b5f1d778a5437540b86f8a7fb3082140f5..f261457a9a5e2fe983528d9f8d0cf763173e8b64 100644 --- a/src/proto_alpha/lib_dal/dune +++ b/src/proto_alpha/lib_dal/dune @@ -9,6 +9,7 @@ octez-libs.base octez-protocol-compiler.registerer octez-libs.stdlib-unix + octez-shell-libs.shell-services tezos-dal-node-lib octez-protocol-alpha-libs.client octez-protocol-alpha-libs.plugin @@ -28,6 +29,7 @@ -open Tezos_base.TzPervasives -open Tezos_protocol_registerer -open Tezos_stdlib_unix + -open Tezos_shell_services -open Tezos_dal_node_lib -open Tezos_client_alpha -open Tezos_protocol_plugin_alpha