From c17f04dfbccdbe2c4c86f0e4bab21b2ed253cc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 14 Jan 2025 17:11:57 +0100 Subject: [PATCH 1/8] Manifest: The DAL plugin depends on Octez shell services --- manifest/product_octez.ml | 1 + src/proto_020_PsParisC/lib_dal/dune | 2 ++ src/proto_021_PsQuebec/lib_dal/dune | 2 ++ src/proto_alpha/lib_dal/dune | 2 ++ 4 files changed, 7 insertions(+) diff --git a/manifest/product_octez.ml b/manifest/product_octez.ml index fc32fd9b2b48..3a55338397a6 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/proto_020_PsParisC/lib_dal/dune b/src/proto_020_PsParisC/lib_dal/dune index 49958af8559f..fd0e4457c4c1 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/dune b/src/proto_021_PsQuebec/lib_dal/dune index 65fb946cd70f..2bc3c492d48d 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/dune b/src/proto_alpha/lib_dal/dune index 6efe42b5f1d7..f261457a9a5e 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 -- GitLab From b8b29aeb8b4be652e46c8d945055ca6f95d6c007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 14 Jan 2025 17:12:16 +0100 Subject: [PATCH 2/8] Proto/Paris: Implement a fallback for injecting entrpament evidence --- .../lib_dal/dal_plugin_registration.ml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 654898ba401f..06b584dffcbe 100644 --- a/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml +++ b/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml @@ -72,6 +72,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 -- GitLab From 0e5da20367e68175456f4ae6661862d8103a6d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 14 Jan 2025 17:12:55 +0100 Subject: [PATCH 3/8] Proto/Quebec: Implement a fallback for injecting entrpament evidence --- .../lib_dal/dal_plugin_registration.ml | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) 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 9b752919c95b..5467b0b3af1e 100644 --- a/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml +++ b/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml @@ -72,6 +72,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 -- GitLab From 87edecc4d624e850778adb4da62c7fa0e38e1363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 14 Jan 2025 17:13:06 +0100 Subject: [PATCH 4/8] Proto/Alpha: Forge and inject an entrapment evidence --- .../lib_dal/dal_plugin_registration.ml | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/proto_alpha/lib_dal/dal_plugin_registration.ml b/src/proto_alpha/lib_dal/dal_plugin_registration.ml index 104451287206..4bd4a866b113 100644 --- a/src/proto_alpha/lib_dal/dal_plugin_registration.ml +++ b/src/proto_alpha/lib_dal/dal_plugin_registration.ml @@ -74,6 +74,87 @@ 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) + + (* We can provide more context in the future. *) + type error += Not_an_attestation + + let () = + register_error_kind + `Permanent + ~id:"not_an_attestation" + ~title:"Not an attestation" + ~description: + "This error is raised if the DAL node tries to accuse with an \ + operation which is not an attestation" + ~pp:(fun fmt () -> + Format.fprintf + fmt + "This error is raised if the DAL node tries to accuse with an \ + operation which is not an attestation") + Data_encoding.unit + (function Not_an_attestation -> Some () | _ -> None) + (fun () -> Not_an_attestation) + + let inject_entrapment_evidence cctxt ~attested_level 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 + match operation.protocol_data with + | Operation_data protocol_data -> ( + match 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 + | _ -> fail [Not_an_attestation]) + 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 -- GitLab From 0099b581071dcb093422447b2ce3181737e8a05f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 14 Jan 2025 17:13:23 +0100 Subject: [PATCH 5/8] DAL/Node: Enable to inject an entrapment evidence --- src/lib_dal_node/dal_plugin.ml | 9 +++++++++ src/lib_dal_node/dal_plugin.mli | 11 +++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index f0bcca8cd694..d3aff5755d91 100644 --- a/src/lib_dal_node/dal_plugin.ml +++ b/src/lib_dal_node/dal_plugin.ml @@ -161,6 +161,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 -> + Proto.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 4bc7f0429ab2..0fb0a8420ca1 100644 --- a/src/lib_dal_node/dal_plugin.mli +++ b/src/lib_dal_node/dal_plugin.mli @@ -120,6 +120,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 -> + Proto.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 -- GitLab From ee674523cb10e8ca97401c0e65e13bc7958eabe5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Thir=C3=A9?= Date: Tue, 14 Jan 2025 17:13:23 +0100 Subject: [PATCH 6/8] DAL/Node: use attestation_operation to refine the operation data --- src/lib_dal_node/dal_plugin.ml | 4 +- src/lib_dal_node/dal_plugin.mli | 4 +- .../lib_dal/dal_plugin_registration.ml | 2 + .../lib_dal/dal_plugin_registration.ml | 2 + .../lib_dal/dal_plugin_registration.ml | 66 +++++++------------ 5 files changed, 32 insertions(+), 46 deletions(-) diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index d3aff5755d91..0ef620011d41 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 -> @@ -164,7 +166,7 @@ module type T = sig val inject_entrapment_evidence : Tezos_rpc.Context.generic -> attested_level:Int32.t -> - Proto.operation -> + attestation_operation -> slot_index:slot_index -> shard:Cryptobox.shard -> proof:Cryptobox.shard_proof -> diff --git a/src/lib_dal_node/dal_plugin.mli b/src/lib_dal_node/dal_plugin.mli index 0fb0a8420ca1..bd78e9d91929 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 @@ -125,7 +127,7 @@ module type T = sig val inject_entrapment_evidence : Tezos_rpc.Context.generic -> attested_level:Int32.t -> - Proto.operation -> + attestation_operation -> slot_index:slot_index -> shard:Cryptobox.shard -> proof:Cryptobox.shard_proof -> 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 06b584dffcbe..819d863e73ad 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) 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 5467b0b3af1e..fcf47e8e365d 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) diff --git a/src/proto_alpha/lib_dal/dal_plugin_registration.ml b/src/proto_alpha/lib_dal/dal_plugin_registration.ml index 4bd4a866b113..733dddbd848e 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) @@ -86,28 +88,8 @@ module Plugin = struct if Compare.Int32.(level_with_offset >= 0l) then return (`Head offset) else return (`Head 0) - (* We can provide more context in the future. *) - type error += Not_an_attestation - - let () = - register_error_kind - `Permanent - ~id:"not_an_attestation" - ~title:"Not an attestation" - ~description: - "This error is raised if the DAL node tries to accuse with an \ - operation which is not an attestation" - ~pp:(fun fmt () -> - Format.fprintf - fmt - "This error is raised if the DAL node tries to accuse with an \ - operation which is not an attestation") - Data_encoding.unit - (function Not_an_attestation -> Some () | _ -> None) - (fun () -> Not_an_attestation) - - let inject_entrapment_evidence cctxt ~attested_level operation ~slot_index - ~shard ~proof = + 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 @@ -132,28 +114,24 @@ module Plugin = struct Protocol_client_context.Alpha_block_services.hash cctxt ~chain ~block () in let shard_with_proof = Dal.Shard_with_proof.{shard; proof} in - match operation.protocol_data with - | Operation_data protocol_data -> ( - match 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 - | _ -> fail [Not_an_attestation]) + 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 -- GitLab From ef5c58a4b2c39d1a90596c68a614f9631f25ce25 Mon Sep 17 00:00:00 2001 From: phink Date: Wed, 15 Jan 2025 13:01:16 +0100 Subject: [PATCH 7/8] DAL/Node: extend the protocol plugin with get_dal_attestation_operations --- src/lib_dal_node/dal_plugin.ml | 4 ++ src/lib_dal_node/dal_plugin.mli | 6 +++ .../lib_dal/dal_plugin_registration.ml | 52 +++++++++++++++++++ .../lib_dal/dal_plugin_registration.ml | 52 +++++++++++++++++++ .../lib_dal/dal_plugin_registration.ml | 52 +++++++++++++++++++ 5 files changed, 166 insertions(+) diff --git a/src/lib_dal_node/dal_plugin.ml b/src/lib_dal_node/dal_plugin.ml index 0ef620011d41..16ce3cb1a4a6 100644 --- a/src/lib_dal_node/dal_plugin.ml +++ b/src/lib_dal_node/dal_plugin.ml @@ -146,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 diff --git a/src/lib_dal_node/dal_plugin.mli b/src/lib_dal_node/dal_plugin.mli index bd78e9d91929..d2a79b138b97 100644 --- a/src/lib_dal_node/dal_plugin.mli +++ b/src/lib_dal_node/dal_plugin.mli @@ -88,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. *) 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 819d863e73ad..50b5ae17cb57 100644 --- a/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml +++ b/src/proto_020_PsParisC/lib_dal/dal_plugin_registration.ml @@ -169,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/dal_plugin_registration.ml b/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml index fcf47e8e365d..e88b5e3b8129 100644 --- a/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml +++ b/src/proto_021_PsQuebec/lib_dal/dal_plugin_registration.ml @@ -169,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_alpha/lib_dal/dal_plugin_registration.ml b/src/proto_alpha/lib_dal/dal_plugin_registration.ml index 733dddbd848e..3b97016b442e 100644 --- a/src/proto_alpha/lib_dal/dal_plugin_registration.ml +++ b/src/proto_alpha/lib_dal/dal_plugin_registration.ml @@ -208,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 -- GitLab From 6fbc98fa25ee3d349a77d3db56ec68adb2534ceb Mon Sep 17 00:00:00 2001 From: phink Date: Wed, 15 Jan 2025 00:48:43 +0100 Subject: [PATCH 8/8] DAL/Node: implement the DAL accuser daemon handler --- src/bin_dal_node/accuser.ml | 163 +++++++++++++++++++++++++++++++++++ src/bin_dal_node/accuser.mli | 21 +++++ src/bin_dal_node/daemon.ml | 3 + 3 files changed, 187 insertions(+) create mode 100644 src/bin_dal_node/accuser.ml create mode 100644 src/bin_dal_node/accuser.mli diff --git a/src/bin_dal_node/accuser.ml b/src/bin_dal_node/accuser.ml new file mode 100644 index 000000000000..24be48b8259b --- /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 000000000000..86a957092a39 --- /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 2a34027b4232..45e0df1d0d7c 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 -- GitLab