diff --git a/.gitlab/ci/jobs/packaging/opam_package.yml b/.gitlab/ci/jobs/packaging/opam_package.yml index 92fba74f5489ae1366b1f49f5b5300fe5d3383a5..214e0fc9b657c360d1e5497eb6022737d5fbe7f0 100644 --- a/.gitlab/ci/jobs/packaging/opam_package.yml +++ b/.gitlab/ci/jobs/packaging/opam_package.yml @@ -252,6 +252,13 @@ opam:octez-codec: variables: package: octez-codec +opam:octez-crawler: + extends: + - .opam_template + - .rules_template__trigger_opam_batch_1 + variables: + package: octez-crawler + # Ignoring unreleased package octez-dac-node. # Ignoring unreleased package octez-dal-node. @@ -469,7 +476,7 @@ opam:tezos-client-001-PtCJ7pwo: opam:tezos-client-002-PsYLVpVv: extends: - .opam_template - - .rules_template__trigger_opam_batch_2 + - .rules_template__trigger_opam_batch_3 variables: package: tezos-client-002-PsYLVpVv @@ -518,7 +525,7 @@ opam:tezos-client-008-PtEdo2Zk: opam:tezos-client-009-PsFLoren: extends: - .opam_template - - .rules_template__trigger_opam_batch_1 + - .rules_template__trigger_opam_batch_2 variables: package: tezos-client-009-PsFLoren @@ -766,7 +773,7 @@ opam:tezos-embedded-protocol-011-PtHangz2: opam:tezos-embedded-protocol-012-Psithaca: extends: - .opam_template - - .rules_template__trigger_opam_batch_3 + - .rules_template__trigger_opam_batch_4 variables: package: tezos-embedded-protocol-012-Psithaca @@ -1101,7 +1108,7 @@ opam:tezos-protocol-alpha: opam:tezos-protocol-demo-counter: extends: - .opam_template - - .rules_template__trigger_opam_batch_5 + - .rules_template__trigger_opam_batch_6 variables: package: tezos-protocol-demo-counter @@ -1420,7 +1427,7 @@ opam:tezos-shell-services-test-helpers: opam:tezos-signer-backends: extends: - .opam_template - - .rules_template__trigger_opam_batch_4 + - .rules_template__trigger_opam_batch_5 variables: package: tezos-signer-backends @@ -1519,7 +1526,7 @@ opam:tezos-validation: opam:tezos-version: extends: - .opam_template - - .rules_template__trigger_opam_batch_6 + - .rules_template__trigger_opam_batch_7 variables: package: tezos-version diff --git a/dune-project b/dune-project index 20891b777c0748178879fd29ba7935d3e447009b..8e384b2bb441a7ef7a4001b97141977a9b3fd437 100644 --- a/dune-project +++ b/dune-project @@ -12,6 +12,7 @@ (package (name octez-baker-alpha)) (package (name octez-client)) (package (name octez-codec)) +(package (name octez-crawler)) (package (name octez-dac-node)) (package (name octez-dal-node)) (package (name octez-evm-proxy)) diff --git a/manifest/main.ml b/manifest/main.ml index 764f38105cb03d6f1b387a31dbab928ed2f0508c..8b681083324a67b720bb19efbee695bc3bb72647 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -3418,6 +3418,24 @@ let octez_node_config = octez_validation |> open_; ] +let octez_crawler = + public_lib + "octez-crawler" + ~internal_name:"octez_crawler" + ~path:"src/lib_crawler" + ~synopsis:"Octez: library to crawl blocks of the L1 chain" + ~deps: + [ + octez_base |> open_ ~m:"TzPervasives" + |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals" + |> open_; + octez_rpc_http |> open_; + octez_base_unix; + octez_stdlib_unix |> open_; + octez_client_base |> open_; + octez_shell; + ] + let octez_injector = public_lib "octez-injector" @@ -3436,6 +3454,7 @@ let octez_injector = octez_client_base |> open_; octez_workers |> open_; octez_shell; + octez_crawler |> open_; ] let octez_scoru_wasm_helpers = @@ -5204,6 +5223,7 @@ module Protocol = Protocol octez_sc_rollup_layer2 |> if_some |> open_; layer2_utils |> if_some |> open_; octez_layer2_store |> open_; + octez_crawler |> open_; tree_encoding; data_encoding; irmin_pack; diff --git a/opam/octez-crawler.opam b/opam/octez-crawler.opam new file mode 100644 index 0000000000000000000000000000000000000000..2b8da4a26683ea4d3db688dce7faa481078b163c --- /dev/null +++ b/opam/octez-crawler.opam @@ -0,0 +1,24 @@ +# This file was automatically generated, do not edit. +# Edit file manifest/main.ml instead. +opam-version: "2.0" +maintainer: "contact@tezos.com" +authors: ["Tezos devteam"] +homepage: "https://www.tezos.com/" +bug-reports: "https://gitlab.com/tezos/tezos/issues" +dev-repo: "git+https://gitlab.com/tezos/tezos.git" +license: "MIT" +depends: [ + "dune" { >= "3.0" } + "ocaml" { >= "4.14" } + "tezos-base" + "tezos-rpc-http" + "tezos-stdlib-unix" + "tezos-client-base" + "tezos-shell" +] +build: [ + ["rm" "-r" "vendors"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +synopsis: "Octez: library to crawl blocks of the L1 chain" diff --git a/opam/octez-injector.opam b/opam/octez-injector.opam index 48eae6fd1f3bdd68633d3a22b8d033eec4ff45ac..6f15d96cb7f91ec4c5143d0e9be04074ffd3b497 100644 --- a/opam/octez-injector.opam +++ b/opam/octez-injector.opam @@ -18,6 +18,7 @@ depends: [ "tezos-client-base" "tezos-workers" "tezos-shell" + "octez-crawler" ] build: [ ["rm" "-r" "vendors"] diff --git a/opam/octez-smart-rollup-node-PtMumbai.opam b/opam/octez-smart-rollup-node-PtMumbai.opam index 598e7461c46dc088f4f43574012a9326c87ea764..b115cea608d6fbe34f92f7e8b294448f824d9caa 100644 --- a/opam/octez-smart-rollup-node-PtMumbai.opam +++ b/opam/octez-smart-rollup-node-PtMumbai.opam @@ -29,6 +29,7 @@ depends: [ "tezos-smart-rollup-layer2-016-PtMumbai" "tezos-layer2-utils-016-PtMumbai" "tezos-layer2-store" + "octez-crawler" "tezos-tree-encoding" "data-encoding" { >= "0.7.1" & < "1.0.0" } "irmin-pack" { >= "3.6.0" & < "3.7.0" } diff --git a/opam/octez-smart-rollup-node-alpha.opam b/opam/octez-smart-rollup-node-alpha.opam index cf8a0b48f0fba850c9965d81e02e2940c19c02d0..3cb31b9221adac2135631fee9be0f83a570e79e0 100644 --- a/opam/octez-smart-rollup-node-alpha.opam +++ b/opam/octez-smart-rollup-node-alpha.opam @@ -29,6 +29,7 @@ depends: [ "tezos-smart-rollup-layer2-alpha" "tezos-layer2-utils-alpha" "tezos-layer2-store" + "octez-crawler" "tezos-tree-encoding" "data-encoding" { >= "0.7.1" & < "1.0.0" } "irmin-pack" { >= "3.6.0" & < "3.7.0" } diff --git a/src/lib_crawler/dune b/src/lib_crawler/dune new file mode 100644 index 0000000000000000000000000000000000000000..44d5460e39f36d944438dd11b237da43bc5e9c5d --- /dev/null +++ b/src/lib_crawler/dune @@ -0,0 +1,22 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(library + (name octez_crawler) + (public_name octez-crawler) + (instrumentation (backend bisect_ppx)) + (libraries + tezos-base + tezos-rpc-http + tezos-base.unix + tezos-stdlib-unix + tezos-client-base + tezos-shell) + (flags + (:standard) + -open Tezos_base.TzPervasives + -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals + -open Tezos_base + -open Tezos_rpc_http + -open Tezos_stdlib_unix + -open Tezos_client_base)) diff --git a/src/lib_crawler/layer1_event.ml b/src/lib_crawler/layer1_event.ml new file mode 100644 index 0000000000000000000000000000000000000000..db319683a8e2355a67a8218453db4202b505c31e --- /dev/null +++ b/src/lib_crawler/layer1_event.ml @@ -0,0 +1,123 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, *) +(* Copyright (c) Functori, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +module Simple = struct + include Internal_event.Simple + + let section = ["lib_crawler"; "layer_1"] + + let declare_0 ~name ~msg ?level () = + declare_1 + ~section + ~name + ~msg:("[{name}] " ^ msg) + ?level + ("name", Data_encoding.string) + ~pp1:Format.pp_print_string + + let declare_1 ~name ~msg ?level ?pp1 enc1 = + declare_2 + ~section + ~name + ~msg:("[{name}] " ^ msg) + ?level + ("name", Data_encoding.string) + enc1 + ~pp1:Format.pp_print_string + ?pp2:pp1 + + let declare_2 ~name ~msg ?level ?pp1 ?pp2 enc1 enc2 = + declare_3 + ~section + ~name + ~msg:("[{name}] " ^ msg) + ?level + ("name", Data_encoding.string) + enc1 + enc2 + ~pp1:Format.pp_print_string + ?pp2:pp1 + ?pp3:pp2 + + let starting = + declare_0 + ~name:"lib_crawler_layer_1_starting" + ~msg:"Starting layer 1 tracker of the smart rollup node" + ~level:Notice + () + + let stopping = + declare_0 + ~name:"lib_crawler_layer_1_stopping" + ~msg:"Stopping layer 1 tracker of the smart rollup node" + ~level:Notice + () + + let connection_lost = + declare_0 + ~name:"lib_crawler_connection_lost" + ~msg:"connection to the node has been lost" + ~level:Warning + () + + let cannot_connect = + declare_2 + ~name:"lib_crawler_cannot_connect" + ~msg:"cannot connect to Tezos node ({count}) {error}" + ~level:Warning + ("count", Data_encoding.int31) + ("error", trace_encoding) + ~pp2:pp_print_trace + + let wait_reconnect = + declare_1 + ~name:"lib_crawler_wait_reconnect" + ~msg:"Retrying to connect in {delay}s" + ~level:Warning + ("delay", Data_encoding.float) + + let switched_new_head = + declare_2 + ~name:"lib_crawler_layer_1_new_head" + ~msg:"Layer 1 node has switched to head {hash} at level {level}" + ~level:Notice + ("hash", Block_hash.encoding) + ("level", Data_encoding.int32) +end + +let starting ~name = Simple.(emit starting) name + +let stopping ~name = Simple.(emit stopping) name + +let connection_lost ~name = Simple.(emit connection_lost) name + +let cannot_connect ~name ~count error = + Simple.(emit cannot_connect) (name, count, error) + +let wait_reconnect ~name delay = Simple.(emit wait_reconnect) (name, delay) + +let switched_new_head ~name hash level = + Simple.(emit switched_new_head) (name, hash, level) diff --git a/src/lib_crawler/layer1_event.mli b/src/lib_crawler/layer1_event.mli new file mode 100644 index 0000000000000000000000000000000000000000..9fea581153cef60fa3df892f9e1157643b5892cb --- /dev/null +++ b/src/lib_crawler/layer1_event.mli @@ -0,0 +1,47 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs *) +(* Copyright (c) Functori, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** This module defines functions that emit the events used by the layer 1 chain + (see {!Layer_1}). *) + +val starting : name:string -> unit Lwt.t + +val stopping : name:string -> unit Lwt.t + +(** Emits the event that the connection to the Tezos node has been lost. *) +val connection_lost : name:string -> unit Lwt.t + +(** [cannot_connect ~count error] emits the event that the rollup node cannot + connect to the Tezos node because of [error] for the [count]'s time. *) +val cannot_connect : name:string -> count:int -> tztrace -> unit Lwt.t + +(** [wait_reconnect delay] emits the event that the rollup will wait [delay] + seconds before attempting to reconnect to the Tezos node . *) +val wait_reconnect : name:string -> float -> unit Lwt.t + +(** [switched_new_head hash level] emits the event that the layer 1 has notified + a new head with [hash] at some given [level]. *) +val switched_new_head : name:string -> Block_hash.t -> int32 -> unit Lwt.t diff --git a/src/lib_crawler/layer_1.ml b/src/lib_crawler/layer_1.ml new file mode 100644 index 0000000000000000000000000000000000000000..9c8d7eedf8ea8d0d216245329810d0faa399365b --- /dev/null +++ b/src/lib_crawler/layer_1.ml @@ -0,0 +1,296 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, *) +(* Copyright (c) Functori, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** + + Errors + ====== + +*) + +type error += Cannot_find_predecessor of Block_hash.t + +let () = + register_error_kind + ~id:"lib_crawler.cannot_find_predecessor" + ~title:"Cannot find block predecessor from L1" + ~description:"A predecessor couldn't be found from the L1 node" + ~pp:(fun ppf hash -> + Format.fprintf + ppf + "Block with hash %a has no predecessor on the L1 node." + Block_hash.pp + hash) + `Temporary + Data_encoding.(obj1 (req "hash" Block_hash.encoding)) + (function Cannot_find_predecessor hash -> Some hash | _ -> None) + (fun hash -> Cannot_find_predecessor hash) + +(** + + State + ===== + +*) + +type t = { + name : string; + reconnection_delay : float; + heads : (Block_hash.t * Block_header.t) Lwt_stream.t; + cctxt : Client_context.full; + stopper : Tezos_rpc.Context.stopper; + mutable running : bool; +} + +let rec connect ~name ?(count = 0) ~delay cctxt = + let open Lwt_syntax in + let* () = + if count = 0 then return_unit + else + let fcount = float_of_int (count - 1) in + (* Randomized exponential backoff capped to 1.5h: 1.5^count * delay ± 50% *) + let delay = delay *. (1.5 ** fcount) in + let delay = min delay 3600. in + let randomization_factor = 0.5 (* 50% *) in + let delay = + delay + +. Random.float (delay *. 2. *. randomization_factor) + -. (delay *. randomization_factor) + in + let* () = Layer1_event.wait_reconnect ~name delay in + Lwt_unix.sleep delay + in + let* res = Tezos_shell_services.Monitor_services.heads cctxt cctxt#chain in + match res with + | Ok (heads, stopper) -> + let heads = + Lwt_stream.map_s + (fun ( hash, + (Tezos_base.Block_header.{shell = {level; _}; _} as header) ) -> + let+ () = Layer1_event.switched_new_head ~name hash level in + (hash, header)) + heads + in + return_ok (heads, stopper) + | Error e -> + let* () = Layer1_event.cannot_connect ~name ~count e in + connect ~name ~delay ~count:(count + 1) cctxt + +let start ~name ~reconnection_delay (cctxt : #Client_context.full) = + let open Lwt_result_syntax in + let*! () = Layer1_event.starting ~name in + let+ heads, stopper = connect ~name ~delay:reconnection_delay cctxt in + { + name; + cctxt = (cctxt :> Client_context.full); + heads; + stopper; + reconnection_delay; + running = true; + } + +let reconnect l1_ctxt = + let open Lwt_result_syntax in + let* heads, stopper = + connect + ~name:l1_ctxt.name + ~count:1 + ~delay:l1_ctxt.reconnection_delay + l1_ctxt.cctxt + in + return {l1_ctxt with heads; stopper} + +let shutdown state = + state.stopper () ; + state.running <- false ; + Lwt.return_unit + +let is_connection_error trace = + TzTrace.fold + (fun yes error -> + yes + || + match error with + | RPC_client_errors.(Request_failed {error = Connection_failed _; _}) -> + true + | _ -> false) + false + trace + +(* TODO: https://gitlab.com/tezos/tezos/-/issues/2895 + Use Lwt_stream.iter_es once it is exposed. *) +let iter_heads l1_ctxt f = + let exception Iter_error of tztrace in + let rec loop l1_ctxt = + let open Lwt_result_syntax in + let*! () = + Lwt_stream.iter_s + (fun head -> + let open Lwt_syntax in + let* res = f head in + match res with + | Ok () -> return_unit + | Error trace when is_connection_error trace -> + Format.eprintf + "@[Connection error:@ %a@]@." + pp_print_trace + trace ; + l1_ctxt.stopper () ; + return_unit + | Error e -> raise (Iter_error e)) + l1_ctxt.heads + in + when_ l1_ctxt.running @@ fun () -> + let*! () = Layer1_event.connection_lost ~name:l1_ctxt.name in + let* l1_ctxt = reconnect l1_ctxt in + loop l1_ctxt + in + Lwt.catch + (fun () -> Lwt.no_cancel @@ loop l1_ctxt) + (function Iter_error e -> Lwt.return_error e | exn -> fail (Exn exn)) + +(** [predecessors_of_blocks hashes] given a list of successive block hashes, + from newest to oldest, returns an associative list that associates a hash to + its predecessor in this list. *) +let predecessors_of_blocks hashes = + let rec aux next = function [] -> [] | x :: xs -> (next, x) :: aux x xs in + match hashes with [] -> [] | x :: xs -> aux x xs + +(** [get_predecessor block_hash] returns the predecessor block hash of + some [block_hash] through an RPC to the Tezos node. To limit the + number of RPCs, this information is requested for a batch of hashes + and cached locally. *) +let get_predecessor = + let max_cached = 1023 and max_read = 8 in + let module HM = + Aches.Vache.Map (Aches.Vache.FIFO_Precise) (Aches.Vache.Strong) (Block_hash) + in + let cache = HM.create max_cached in + fun cctxt (chain : Tezos_shell_services.Chain_services.chain) ancestor -> + let open Lwt_result_syntax in + match HM.find_opt cache ancestor with + | Some pred -> return_some pred + | None -> ( + let* blocks = + Tezos_shell_services.Chain_services.Blocks.list + cctxt + ~chain + ~heads:[ancestor] + ~length:max_read + () + in + match blocks with + | [ancestors] -> ( + List.iter + (fun (h, p) -> HM.replace cache h p) + (predecessors_of_blocks ancestors) ; + match HM.find_opt cache ancestor with + | None -> + (* This could happen if ancestors was empty, but it shouldn't be. *) + return_none + | Some predecessor -> return_some predecessor) + | _ -> return_none) + +let get_predecessor_opt state (hash, level) = + let open Lwt_result_syntax in + if level = 0l then return_none + else + let level = Int32.pred level in + let+ hash = get_predecessor state.cctxt state.cctxt#chain hash in + Option.map (fun hash -> (hash, level)) hash + +let get_predecessor state ((hash, _) as head) = + let open Lwt_result_syntax in + let* pred = get_predecessor_opt state head in + match pred with + | None -> tzfail (Cannot_find_predecessor hash) + | Some pred -> return pred + +let nth_predecessor l1_state n block = + let open Lwt_result_syntax in + assert (n >= 0) ; + let rec aux acc n block = + if n = 0 then return (block, acc) + else + let* pred = get_predecessor l1_state block in + (aux [@tailcall]) (block :: acc) (n - 1) pred + in + aux [] n block + +let get_tezos_reorg_for_new_head l1_state old_head new_head = + let open Lwt_result_syntax in + (* old_head and new_head must have the same level when calling aux *) + let rec aux reorg old_head new_head = + let old_head_hash, _ = old_head in + let new_head_hash, _ = new_head in + if Block_hash.(old_head_hash = new_head_hash) then return reorg + else + let* old_head_pred = get_predecessor l1_state old_head in + let* new_head_pred = get_predecessor l1_state new_head in + let reorg = + Reorg. + { + old_chain = old_head :: reorg.old_chain; + new_chain = new_head :: reorg.new_chain; + } + in + (aux [@tailcall]) reorg old_head_pred new_head_pred + in + (* computing partial reorganization to make old_head and new_head at same + level *) + let _, old_head_level = old_head in + let _, new_head_level = new_head in + let distance = Int32.(to_int @@ abs @@ sub new_head_level old_head_level) in + let* old_head, new_head, reorg = + if old_head_level = new_head_level then + return (old_head, new_head, Reorg.no_reorg) + else if old_head_level < new_head_level then + let+ new_head, new_chain = nth_predecessor l1_state distance new_head in + (old_head, new_head, {Reorg.no_reorg with new_chain}) + else + let+ old_head, old_chain = nth_predecessor l1_state distance old_head in + (old_head, new_head, {Reorg.no_reorg with old_chain}) + in + assert (snd old_head = snd new_head) ; + aux reorg old_head new_head + +(** Returns the reorganization of L1 blocks (if any) for [new_head]. *) +let get_tezos_reorg_for_new_head l1_state old_head new_head = + let open Lwt_result_syntax in + match old_head with + | `Level l -> + let _, new_head_level = new_head in + (* No known tezos head, we want all blocks from l. *) + if new_head_level < l then return Reorg.no_reorg + else + let* _block_at_l, new_chain = + nth_predecessor + l1_state + (Int32.sub new_head_level l |> Int32.to_int) + new_head + in + return Reorg.{old_chain = []; new_chain} + | `Head old_head -> get_tezos_reorg_for_new_head l1_state old_head new_head diff --git a/src/lib_crawler/layer_1.mli b/src/lib_crawler/layer_1.mli new file mode 100644 index 0000000000000000000000000000000000000000..f3f549bc551d696fee053555a73b18244993c418 --- /dev/null +++ b/src/lib_crawler/layer_1.mli @@ -0,0 +1,86 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, *) +(* Copyright (c) Functori, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** This module allow to follow the layer 1 chain by subscribing to the head + monitoring RPC offered by the Tezos node, reconnecting, etc. *) + +(** The type of layer 1 followers. *) +type t + +(** {2 Monitoring the Layer 1 chain} *) + +(** [start ~name ~reconnection_delay cctxt] connects to a Tezos node and starts + monitoring new heads. One can iterate on the heads by calling {!iter_heads} + on its result. [reconnection_delay] gives an initial delay for the + reconnection which is used in an exponential backoff. The [name] is used to + differentiate events. *) +val start : + name:string -> + reconnection_delay:float -> + #Client_context.full -> + t tzresult Lwt.t + +(** [shutdown t] properly shuts the layer 1 down. *) +val shutdown : t -> unit Lwt.t + +(** [iter_heads t f] calls [f] on all new heads appearing in the layer 1 + chain. In case of a disconnection with the layer 1 node, it reconnects + automatically. If [f] returns an error (other than a disconnection), + [iter_heads] terminates and returns the error. *) +val iter_heads : + t -> + (Block_hash.t * Block_header.t -> unit tzresult Lwt.t) -> + unit tzresult Lwt.t + +(** {2 Helper functions for the Layer 1 chain} *) + +(** [get_predecessor_opt state head] returns the predecessor of block [head], + when [head] is not the genesis block. *) +val get_predecessor_opt : + t -> Block_hash.t * int32 -> (Block_hash.t * int32) option tzresult Lwt.t + +(** [get_predecessor state head] returns the predecessor block of [head]. *) +val get_predecessor : + t -> Block_hash.t * int32 -> (Block_hash.t * int32) tzresult Lwt.t + +(** [nth_predecessor l1_ctxt n head] returns [block, history] where [block] is + the nth predecessor of [head] and [history] is the list of blocks between + [block] (excluded) and [head] (included) on the chain *) +val nth_predecessor : + t -> + int -> + Block_hash.t * int32 -> + ((Block_hash.t * int32) * (Block_hash.t * int32) list) tzresult Lwt.t + +(** [get_tezos_reorg_for_new_head l1_ctxt old_head new_head] returns the + reorganization of L1 blocks between [old_head] and [new_head]. If [old_head] + is [`Level l], then it returns the reorganization between [new_head] and + level [l] on the same chain. *) +val get_tezos_reorg_for_new_head : + t -> + [`Head of Block_hash.t * int32 | `Level of int32] -> + Block_hash.t * int32 -> + (Block_hash.t * int32) Reorg.t tzresult Lwt.t diff --git a/src/lib_crawler/reorg.ml b/src/lib_crawler/reorg.ml new file mode 100644 index 0000000000000000000000000000000000000000..0a7e6aad3066aa566c5a25e23b30bf66f0b49742 --- /dev/null +++ b/src/lib_crawler/reorg.ml @@ -0,0 +1,41 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, *) +(* Copyright (c) Functori, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +type 'block t = {old_chain : 'block list; new_chain : 'block list} + +let no_reorg = {old_chain = []; new_chain = []} + +let encoding block_encoding = + let open Data_encoding in + conv + (fun {old_chain; new_chain} -> (old_chain, new_chain)) + (fun (old_chain, new_chain) -> {old_chain; new_chain}) + @@ obj2 + (req "old_chain" (list block_encoding)) + (req "new_chain" (list block_encoding)) + +let map f {old_chain; new_chain} = + {old_chain = List.map f old_chain; new_chain = List.map f new_chain} diff --git a/src/lib_crawler/reorg.mli b/src/lib_crawler/reorg.mli new file mode 100644 index 0000000000000000000000000000000000000000..0cbc0f8e03231876aacdb7a599ca2c8446ec09c0 --- /dev/null +++ b/src/lib_crawler/reorg.mli @@ -0,0 +1,40 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) Nomadic Labs, *) +(* Copyright (c) Functori, *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Type of chain reorganizations. *) +type 'block t = { + old_chain : 'block list; + (** The blocks that were in the old chain and which are not in the new one. *) + new_chain : 'block list; + (** The blocks that are now in the new chain. The length of [old_chain] and + [new_chain] may be different. *) +} + +val no_reorg : 'a t + +val encoding : 'a Data_encoding.t -> 'a t Data_encoding.t + +val map : ('a -> 'b) -> 'a t -> 'b t diff --git a/src/lib_injector/dune b/src/lib_injector/dune index a59a37c0396b72efaf0c46b4410eb8e2647e5d89..29183b8723669486bef56517cd5e2a63e4b99ed9 100644 --- a/src/lib_injector/dune +++ b/src/lib_injector/dune @@ -14,7 +14,8 @@ tezos-micheline tezos-client-base tezos-workers - tezos-shell) + tezos-shell + octez-crawler) (flags (:standard) -open Tezos_base.TzPervasives @@ -23,4 +24,5 @@ -open Tezos_stdlib_unix -open Tezos_micheline -open Tezos_client_base - -open Tezos_workers)) + -open Tezos_workers + -open Octez_crawler)) diff --git a/src/lib_injector/injector_common.ml b/src/lib_injector/injector_common.ml index 397fcb9afd1fe4b1059c76aee4c33134ba6a269d..90af6ba9e56600785324ae74e16f5b7b562aad18 100644 --- a/src/lib_injector/injector_common.ml +++ b/src/lib_injector/injector_common.ml @@ -34,16 +34,3 @@ let get_signer cctxt pkh = let open Lwt_result_syntax in let* alias, pk, sk = Client_keys.get_key cctxt pkh in return {alias; pkh; pk; sk} - -type 'block reorg = {old_chain : 'block list; new_chain : 'block list} - -let no_reorg = {old_chain = []; new_chain = []} - -let reorg_encoding block_encoding = - let open Data_encoding in - conv - (fun {old_chain; new_chain} -> (old_chain, new_chain)) - (fun (old_chain, new_chain) -> {old_chain; new_chain}) - @@ obj2 - (req "old_chain" (list block_encoding)) - (req "new_chain" (list block_encoding)) diff --git a/src/lib_injector/injector_common.mli b/src/lib_injector/injector_common.mli index a329f09343f05462939432ee61358ef0668a3933..35230b7767bdebfd6a7efe4ddc0e7cf3fc9718e1 100644 --- a/src/lib_injector/injector_common.mli +++ b/src/lib_injector/injector_common.mli @@ -31,19 +31,6 @@ type signer = { sk : Client_keys.sk_uri; } -(** Type of chain reorganizations. *) -type 'block reorg = { - old_chain : 'block list; - (** The blocks that were in the old chain and which are not in the new one. *) - new_chain : 'block list; - (** The blocks that are now in the new chain. The length of [old_chain] and - [new_chain] may be different. *) -} - (** Retrieve a signer from the client wallet. *) val get_signer : #Client_context.wallet -> Signature.public_key_hash -> signer tzresult Lwt.t - -val no_reorg : 'a reorg - -val reorg_encoding : 'a Data_encoding.t -> 'a reorg Data_encoding.t diff --git a/src/lib_injector/injector_functor.ml b/src/lib_injector/injector_functor.ml index 731981bac6072199e5173d5e9b3628d695774b8f..3b734933b92c1d2ab268c3e2ecdcd350951f5e32 100644 --- a/src/lib_injector/injector_functor.ml +++ b/src/lib_injector/injector_functor.ml @@ -876,7 +876,7 @@ struct the effect of the new branch (the newly included operation and confirmed operations). *) let on_new_tezos_head state (head_hash, head_level) - (reorg : (Block_hash.t * int32) reorg) = + (reorg : (Block_hash.t * int32) Reorg.t) = let open Lwt_result_syntax in let*! () = Event.(emit1 new_tezos_head) state head_hash in let* () = diff --git a/src/lib_injector/injector_sigs.ml b/src/lib_injector/injector_sigs.ml index 684606ad49fc32fd5fdbde283639e4bb03a4c76a..0cb295f810c01cb6afea6bcbeab0647716f88b11 100644 --- a/src/lib_injector/injector_sigs.ml +++ b/src/lib_injector/injector_sigs.ml @@ -306,9 +306,7 @@ module type S = sig reorganization are put back in the pending queue). When an operation is considered as {e confirmed}, it disappears from the injector. *) val new_tezos_head : - Block_hash.t * int32 -> - (Block_hash.t * int32) Injector_common.reorg -> - unit Lwt.t + Block_hash.t * int32 -> (Block_hash.t * int32) Reorg.t -> unit Lwt.t (** Trigger an injection of the pending operations for all workers. If [tags] is given, only the workers which have a tag in [tags] inject their pending diff --git a/src/lib_injector/injector_worker_types.ml b/src/lib_injector/injector_worker_types.ml index 4774790ec242c8bc9e0d0a4af5ad9abdda0d2bce..3d11e2e53dbc005b4226de2a2ad62f4772d1aea6 100644 --- a/src/lib_injector/injector_worker_types.ml +++ b/src/lib_injector/injector_worker_types.ml @@ -23,14 +23,13 @@ (* *) (*****************************************************************************) -open Injector_common open Injector_sigs module Request (L1_operation : INJECTOR_OPERATION) = struct type ('a, 'b) t = | Add_pending : L1_operation.t -> (unit, error trace) t | New_tezos_head : - (Block_hash.t * int32) * (Block_hash.t * int32) reorg + (Block_hash.t * int32) * (Block_hash.t * int32) Reorg.t -> (unit, error trace) t | Inject : (unit, error trace) t @@ -59,7 +58,7 @@ module Request (L1_operation : INJECTOR_OPERATION) = struct obj3 (req "request" (constant "new_tezos_head")) (req "head" block_level) - (req "reorg" (reorg_encoding block_level))) + (req "reorg" (Reorg.encoding block_level))) (function | View (New_tezos_head (b, r)) -> Some ((), b, r) | _ -> None) (fun ((), b, r) -> View (New_tezos_head (b, r))); diff --git a/src/lib_injector/injector_worker_types.mli b/src/lib_injector/injector_worker_types.mli index 1f70c5a48df06fd834f04e85949008b5b7cafe6e..a9a227828710bc4c7b6379dfb39156ffda0c1875 100644 --- a/src/lib_injector/injector_worker_types.mli +++ b/src/lib_injector/injector_worker_types.mli @@ -23,14 +23,13 @@ (* *) (*****************************************************************************) -open Injector_common open Injector_sigs module Request (Inj_operation : INJECTOR_OPERATION) : sig type ('a, 'b) t = | Add_pending : Inj_operation.t -> (unit, error trace) t | New_tezos_head : - (Block_hash.t * int32) * (Block_hash.t * int32) reorg + (Block_hash.t * int32) * (Block_hash.t * int32) Reorg.t -> (unit, error trace) t | Inject : (unit, error trace) t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml index 9920d6a87fc6c6b4eb1e836cf8005ea3981d47df..22c27b36840e70c81089f2a70100de8d2c6e1125 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml @@ -1,8 +1,9 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2021 Nomadic Labs, *) -(* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 TriliTech *) +(* Copyright (c) 2023 Functori, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -263,7 +264,7 @@ module Make (PVM : Pvm.S) = struct let process_l1_block_operations ~finalized node_ctxt (Layer1.{hash; _} as head) = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.l1_ctxt hash in + let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in let apply (type kind) accu ~source (operation : kind manager_operation) result = let open Lwt_result_syntax in @@ -371,7 +372,7 @@ module Make (PVM : Pvm.S) = struct in return_unit - let notify_injector new_head (reorg : Layer1.head Injector_common.reorg) = + let notify_injector new_head (reorg : Layer1.head Reorg.t) = let open Lwt_result_syntax in let open Layer1 in let new_chain = @@ -417,9 +418,7 @@ module Make (PVM : Pvm.S) = struct (* TODO: https://gitlab.com/tezos/tezos/-/issues/3348 Rollback state information on reorganization, i.e. for reorg.old_chain. *) - let* new_head = - Layer1.fetch_tezos_block node_ctxt.l1_ctxt head.Layer1.hash - in + let* new_head = Layer1.fetch_tezos_block node_ctxt.cctxt head.Layer1.hash in let header = Block_header.( raw @@ -440,49 +439,8 @@ module Make (PVM : Pvm.S) = struct let*! () = Injector.inject ~header () in return_unit - let is_connection_error trace = - TzTrace.fold - (fun yes error -> - yes - || - match error with - | Tezos_rpc_http.RPC_client_errors.( - Request_failed {error = Connection_failed _; _}) -> - true - | _ -> false) - false - trace - - (* TODO: https://gitlab.com/tezos/tezos/-/issues/2895 - Use Lwt_stream.fold_es once it is exposed. *) - let daemonize configuration (node_ctxt : _ Node_context.t) = - let open Lwt_result_syntax in - let rec loop (l1_ctxt : Layer1.t) = - let*! () = - Lwt_stream.iter_s - (fun head -> - let open Lwt_syntax in - let* res = on_layer_1_head node_ctxt head in - match res with - | Ok () -> return_unit - | Error trace when is_connection_error trace -> - Format.eprintf - "@[Connection error:@ %a@]@." - pp_print_trace - trace ; - l1_ctxt.stopper () ; - return_unit - | Error e -> - Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; - let* _ = Lwt_exit.exit_and_wait 1 in - return_unit) - l1_ctxt.heads - in - let*! () = Event.connection_lost () in - let* l1_ctxt = Layer1.reconnect configuration node_ctxt.l1_ctxt in - loop l1_ctxt - in - protect @@ fun () -> Lwt.no_cancel @@ loop node_ctxt.l1_ctxt + let daemonize (node_ctxt : _ Node_context.t) = + Layer1.iter_heads node_ctxt.l1_ctxt (on_layer_1_head node_ctxt) let install_finalizer node_ctxt rpc_server = let open Lwt_syntax in @@ -526,11 +484,11 @@ module Make (PVM : Pvm.S) = struct let run node_ctxt configuration = let open Lwt_result_syntax in let* () = check_initial_state_hash node_ctxt in + let* rpc_server = Components.RPC_server.start node_ctxt configuration in + let (_ : Lwt_exit.clean_up_callback_id) = + install_finalizer node_ctxt rpc_server + in let start () = - let* rpc_server = Components.RPC_server.start node_ctxt configuration in - let (_ : Lwt_exit.clean_up_callback_id) = - install_finalizer node_ctxt rpc_server - in let*! () = Inbox.start () in let signers = Configuration.Operator_purpose_map.bindings node_ctxt.operators @@ -590,7 +548,7 @@ module Make (PVM : Pvm.S) = struct ~rpc_addr:configuration.rpc_addr ~rpc_port:configuration.rpc_port in - daemonize configuration node_ctxt + daemonize node_ctxt in Metrics.Info.init_rollup_node_info ~id:node_ctxt.rollup_address @@ -598,7 +556,10 @@ module Make (PVM : Pvm.S) = struct ~genesis_level:node_ctxt.genesis_info.level ~genesis_hash:node_ctxt.genesis_info.commitment_hash ~pvm_kind:node_ctxt.kind ; - start () + protect start ~on_error:(fun e -> + Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; + let*! _ = Lwt_exit.exit_and_wait 1 in + return_unit) end let run ~data_dir (configuration : Configuration.t) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker.ml index 9bfe16292a84cd0d164b7353aa806b5807dca045..26239d7de02276888fa6ae2fffb1d5470f83282d 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/dal_slots_tracker.ml @@ -84,7 +84,7 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = return None | Some published_block_hash -> let* {metadata; _} = - Layer1.fetch_tezos_block node_ctxt.Node_context.l1_ctxt hash + Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in let*? metadata = Option.to_result @@ -234,7 +234,7 @@ module Confirmed_slots_history = struct slot_index) relevant_slots_indexes - let read_slots_history_from_l1 {Node_context.l1_ctxt = {cctxt; _}; _} block = + let read_slots_history_from_l1 {Node_context.cctxt; _} block = let open Lwt_result_syntax in (* We return the empty Slots_history if DAL is not enabled. *) let* slots_list_opt = diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/dune b/src/proto_016_PtMumbai/lib_sc_rollup_node/dune index 6377f8f580514660593d7e365827dd860005e168..5827d992e67884ea58321900b4ccd38e0b9f2c37 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/dune +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/dune @@ -27,6 +27,7 @@ tezos-smart-rollup-layer2-016-PtMumbai tezos-layer2-utils-016-PtMumbai tezos_layer2_store + octez-crawler tezos-tree-encoding data-encoding irmin-pack @@ -59,5 +60,6 @@ -open Tezos_smart_rollup_layer2_016_PtMumbai -open Tezos_layer2_utils_016_PtMumbai -open Tezos_layer2_store + -open Octez_crawler -open Octez_injector -open Tezos_crypto_dal)) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml index fc92ac73074d79b09be97da302d232eb368b274d..572791baf5c3a159007d28fbb059b3b704b7b466 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml @@ -31,9 +31,9 @@ open Alpha_context let lift promise = Lwt.map Environment.wrap_tzresult promise -let get_messages Node_context.{l1_ctxt; _} head = +let get_messages Node_context.{cctxt; _} head = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block l1_ctxt head in + let* block = Layer1.fetch_tezos_block cctxt head in let apply (type kind) accu ~source:_ (operation : kind manager_operation) _result = let open Result_syntax in @@ -80,7 +80,7 @@ let get_messages Node_context.{l1_ctxt; _} head = in let ({predecessor; _} : Block_header.shell_header) = block.header.shell in let* {timestamp = predecessor_timestamp; _} = - Layer1.fetch_tezos_shell_header l1_ctxt predecessor + Layer1.fetch_tezos_shell_header cctxt predecessor in return (List.rev rev_messages, predecessor_timestamp, predecessor) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml index 8de11af5eddb3af70cd53a42ee7474b543cfcb31..47242dd61817eff65379d72fe856eeb0c24fde7b 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml @@ -69,7 +69,7 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct let get_boot_sector block_hash (node_ctxt : _ Node_context.t) = let open Lwt_result_syntax in let exception Found_boot_sector of string in - let* block = Layer1.fetch_tezos_block node_ctxt.l1_ctxt block_hash in + let* block = Layer1.fetch_tezos_block node_ctxt.cctxt block_hash in let missing_boot_sector () = failwith "Boot sector not found in Tezos block %a" diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml index 419e1dcd784c2d8ae3fd9b39614b851c798f930e..d11aa61d64c00e7f5974c871508f410e27247098 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.ml @@ -1,7 +1,8 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 Functori, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,9 +24,6 @@ (* *) (*****************************************************************************) -open Configuration -open Protocol.Alpha_context -open Plugin open Protocol_client_context (** @@ -53,24 +51,6 @@ let () = (function Cannot_find_block hash -> Some hash | _ -> None) (fun hash -> Cannot_find_block hash) -type error += Cannot_find_predecessor of Block_hash.t - -let () = - register_error_kind - ~id:"sc_rollup.node.cannot_find_predecessor" - ~title:"Cannot find block predecessor from L1" - ~description:"A predecessor couldn't be found from the L1 node" - ~pp:(fun ppf hash -> - Format.fprintf - ppf - "Block with hash %a has no predecessor on the L1 node." - Block_hash.pp - hash) - `Temporary - Data_encoding.(obj1 (req "hash" Block_hash.encoding)) - (function Cannot_find_predecessor hash -> Some hash | _ -> None) - (fun hash -> Cannot_find_predecessor hash) - (** State @@ -91,156 +71,58 @@ module Blocks_cache = Aches_lwt.Lache.Make_option (Aches.Rache.Transfer (Aches.Rache.LRU) (Block_hash)) -type blocks_cache = - Protocol_client_context.Alpha_block_services.block_info Blocks_cache.t - -type t = { - blocks_cache : blocks_cache; - heads : head Lwt_stream.t; - cctxt : Protocol_client_context.full; - stopper : Tezos_rpc.Context.stopper; - genesis_info : Sc_rollup.Commitment.genesis_info; -} +type blocks_cache = Alpha_block_services.block_info Blocks_cache.t -(** +(** Global blocks cache for the smart rollup node. *) +let blocks_cache : blocks_cache = Blocks_cache.create 32 - Helpers - ======= +include Octez_crawler.Layer_1 -*) +let head_of_block_level (hash, level) = {hash; level} -(** [predecessors_of_blocks hashes] given a list of successive hashes, - returns an associative list that associates a hash to its - predecessor in this list. *) -let predecessors_of_blocks hashes = - let rec aux next = function [] -> [] | x :: xs -> (next, x) :: aux x xs in - match hashes with [] -> [] | x :: xs -> aux x xs +let block_level_of_head {hash; level} = (hash, level) -(** [get_predecessor block_hash] returns the predecessor block hash of - some [block_hash] through an RPC to the Tezos node. To limit the - number of RPCs, this information is requested for a batch of hashes - and cached locally. *) -let get_predecessor = - let max_cached = 1023 and max_read = 8 in - let module HM = - Aches.Vache.Map (Aches.Vache.FIFO_Precise) (Aches.Vache.Strong) (Block_hash) - in - let cache = HM.create max_cached in - fun cctxt (chain : Tezos_shell_services.Chain_services.chain) ancestor -> - let open Lwt_result_syntax in - match HM.find_opt cache ancestor with - | Some pred -> return_some pred - | None -> ( - let* blocks = - Tezos_shell_services.Chain_services.Blocks.list - cctxt - ~chain - ~heads:[ancestor] - ~length:max_read - () - in - match blocks with - | [ancestors] -> ( - List.iter - (fun (h, p) -> HM.replace cache h p) - (predecessors_of_blocks ancestors) ; - match HM.find_opt cache ancestor with - | None -> - (* We have just updated the cache with that information. *) - assert false - | Some predecessor -> return_some predecessor) - | _ -> return_none) +let iter_heads l1_ctxt f = + iter_heads l1_ctxt @@ fun (hash, {shell = {level; _}; _}) -> f {hash; level} -let get_predecessor_opt state {level; hash} = +let get_predecessor_opt state head = let open Lwt_result_syntax in - if level = 0l then return_none - else - let level = Int32.pred level in - let+ hash = get_predecessor state.cctxt state.cctxt#chain hash in - Option.map (fun hash -> {level; hash}) hash + let+ res = get_predecessor_opt state (block_level_of_head head) in + Option.map head_of_block_level res -let get_predecessor state ({hash; _} as head) = +let get_predecessor state head = let open Lwt_result_syntax in - let* pred = get_predecessor_opt state head in - match pred with - | None -> tzfail (Cannot_find_predecessor hash) - | Some pred -> return pred + let+ res = get_predecessor state (block_level_of_head head) in + head_of_block_level res -let rec connect ?(count = 0) ~delay cctxt genesis_info = - let open Lwt_syntax in - let* () = - if count = 0 then return_unit - else - let fcount = float_of_int (count - 1) in - (* Randomized exponential backoff capped to 1.5h: 1.5^count * delay ± 50% *) - let delay = delay *. (1.5 ** fcount) in - let delay = min delay 3600. in - let randomization_factor = 0.5 (* 50% *) in - let delay = - delay - +. Random.float (delay *. 2. *. randomization_factor) - -. (delay *. randomization_factor) - in - let* () = Event.wait_reconnect delay in - Lwt_unix.sleep delay - in - let* res = Tezos_shell_services.Monitor_services.heads cctxt cctxt#chain in - match res with - | Ok (heads, stopper) -> - let heads = - Lwt_stream.map_s - (fun (hash, Tezos_base.Block_header.{shell = {level; _}; _}) -> - let+ () = Layer1_event.switched_new_head hash level in - {hash; level}) - heads - in - return_ok (heads, stopper) - | Error e -> - let* () = Event.cannot_connect ~count e in - connect ~delay ~count:(count + 1) cctxt genesis_info +let nth_predecessor l1_state n block = + let open Lwt_result_syntax in + let+ res, preds = nth_predecessor l1_state n (block_level_of_head block) in + (head_of_block_level res, List.map head_of_block_level preds) -let start configuration (cctxt : Protocol_client_context.full) = +let get_tezos_reorg_for_new_head l1_ctxt old_head new_head = let open Lwt_result_syntax in - let*! () = Layer1_event.starting () in - let* kind = - RPC.Sc_rollup.kind - cctxt - (cctxt#chain, cctxt#block) - configuration.sc_rollup_address - () - in - let*! () = Event.rollup_exists ~addr:configuration.sc_rollup_address ~kind in - let* genesis_info = - RPC.Sc_rollup.genesis_info - cctxt - (cctxt#chain, cctxt#block) - configuration.sc_rollup_address + let old_head = + match old_head with + | `Level l -> `Level l + | `Head h -> `Head (block_level_of_head h) in - let+ heads, stopper = - connect ~delay:configuration.reconnection_delay cctxt genesis_info + let+ reorg = + get_tezos_reorg_for_new_head l1_ctxt old_head (block_level_of_head new_head) in - ( {cctxt; heads; blocks_cache = Blocks_cache.create 32; stopper; genesis_info}, - kind ) + Reorg.map head_of_block_level reorg -let reconnect configuration l1_ctxt = - let open Lwt_result_syntax in - let* heads, stopper = - connect - ~count:1 - ~delay:configuration.reconnection_delay - l1_ctxt.cctxt - l1_ctxt.genesis_info - in - return {l1_ctxt with heads; stopper} +(** -let shutdown state = - state.stopper () ; - Lwt.return_unit + Helpers + ======= + +*) (** [fetch_tezos_block l1_ctxt hash] returns a block shell header of [hash]. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) -let fetch_tezos_shell_header l1_ctxt hash = +let fetch_tezos_shell_header cctxt hash = let open Lwt_syntax in trace (Cannot_find_block hash) @@ @@ -248,7 +130,7 @@ let fetch_tezos_shell_header l1_ctxt hash = let fetch hash = let* shell_header = Tezos_shell_services.Shell_services.Blocks.Header.shell_header - l1_ctxt.cctxt + cctxt ~chain:`Main ~block:(`Hash (hash, 0)) () @@ -261,7 +143,7 @@ let fetch_tezos_shell_header l1_ctxt hash = in let+ shell_header = let res = - Blocks_cache.bind l1_ctxt.blocks_cache hash (function + Blocks_cache.bind blocks_cache hash (function | Some block_info -> Lwt.return_some block_info.header.shell | None -> Lwt.return_none) in @@ -281,7 +163,7 @@ let fetch_tezos_shell_header l1_ctxt hash = (** [fetch_tezos_block l1_ctxt hash] returns a block info given a block hash. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) -let fetch_tezos_block l1_ctxt hash = +let fetch_tezos_block cctxt hash = let open Lwt_syntax in trace (Cannot_find_block hash) @@ @@ -289,7 +171,7 @@ let fetch_tezos_block l1_ctxt hash = let fetch hash = let* block = Alpha_block_services.info - l1_ctxt.cctxt + cctxt ~chain:`Main ~block:(`Hash (hash, 0)) ~metadata:`Always @@ -301,9 +183,7 @@ let fetch_tezos_block l1_ctxt hash = return_none | Ok block -> return_some block in - let+ block = - Blocks_cache.bind_or_put l1_ctxt.blocks_cache hash fetch Lwt.return - in + let+ block = Blocks_cache.bind_or_put blocks_cache hash fetch Lwt.return in match (block, !errors) with | None, None -> (* This should not happen if {!find_in_cache} behaves correctly, @@ -314,64 +194,3 @@ let fetch_tezos_block l1_ctxt hash = hash | None, Some errs -> Error errs | Some block, _ -> Ok block - -let nth_predecessor l1_state n block = - let open Lwt_result_syntax in - assert (n >= 0) ; - let rec aux acc n block = - if n = 0 then return (block, acc) - else - let* pred = get_predecessor l1_state block in - (aux [@tailcall]) (block :: acc) (n - 1) pred - in - aux [] n block - -let get_tezos_reorg_for_new_head l1_state old_head new_head = - let open Lwt_result_syntax in - (* old_head and new_head must have the same level when calling aux *) - let rec aux reorg old_head new_head = - if Block_hash.(old_head.hash = new_head.hash) then return reorg - else - let* old_head_pred = get_predecessor l1_state old_head in - let* new_head_pred = get_predecessor l1_state new_head in - let reorg = - Injector_common. - { - old_chain = old_head :: reorg.old_chain; - new_chain = new_head :: reorg.new_chain; - } - in - aux reorg old_head_pred new_head_pred - in - (* computing partial reorganization to make old_head and new_head at same - level *) - let distance = Int32.(to_int @@ abs @@ sub new_head.level old_head.level) in - let* old_head, new_head, reorg = - if old_head.level = new_head.level then - return (old_head, new_head, Injector_common.no_reorg) - else if old_head.level < new_head.level then - let+ new_head, new_chain = nth_predecessor l1_state distance new_head in - (old_head, new_head, {Injector_common.no_reorg with new_chain}) - else - let+ old_head, old_chain = nth_predecessor l1_state distance old_head in - (old_head, new_head, {Injector_common.no_reorg with old_chain}) - in - assert (old_head.level = new_head.level) ; - aux reorg old_head new_head - -(** Returns the reorganization of L1 blocks (if any) for [new_head]. *) -let get_tezos_reorg_for_new_head l1_state old_head new_head = - let open Lwt_result_syntax in - match old_head with - | `Level l -> - (* No known tezos head, we want all blocks from l. *) - if new_head.level < l then return Injector_common.no_reorg - else - let* _block_at_l, new_chain = - nth_predecessor - l1_state - (Int32.sub new_head.level l |> Int32.to_int) - new_head - in - return Injector_common.{old_chain = []; new_chain} - | `Head old_head -> get_tezos_reorg_for_new_head l1_state old_head new_head diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli index 1127f09957eae353021912c6fb5f49fa81ccd4b4..1580d1b26f023c61852796180b044bdbc0fbd25f 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1.mli @@ -1,7 +1,8 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 Functori, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -33,33 +34,18 @@ type head = {hash : Block_hash.t; level : int32} val head_encoding : head Data_encoding.t -(** Type of cache holding the last 32 blocks, with their operations. *) -type blocks_cache - -type t = private { - blocks_cache : blocks_cache; - heads : head Lwt_stream.t; - cctxt : Protocol_client_context.full; - stopper : Tezos_rpc.Context.stopper; - genesis_info : Protocol.Alpha_context.Sc_rollup.Commitment.genesis_info; -} +include module type of Octez_crawler.Layer_1 (* TODO: https://gitlab.com/tezos/tezos/-/issues/3311 Allow to retrieve L1 blocks through Tezos node storage locally. *) -(** [start configuration cctxt] returns a stream of [chain_event] obtained - from the monitoring of the Tezos node set up by the client [cctxt]. The - layer 1 state is stored in the data directory declared in - [configuration]. *) -val start : - Configuration.t -> - Protocol_client_context.full -> - (t * Protocol.Alpha_context.Sc_rollup.Kind.t) tzresult Lwt.t +(** [iter_heads t f] calls [f] on all new heads appearing in the layer 1 + chain. In case of a disconnection with the layer 1 node, it reconnects + automatically. If [f] returns an error (other than a disconnection) it, + [iter_heads] terminates and returns the error. *) +val iter_heads : t -> (head -> unit tzresult Lwt.t) -> unit tzresult Lwt.t -(** [reconnect cfg l1_ctxt] reconnects (and retries with delay) to the - Tezos node. The delay for each reconnection is increased with a randomized - exponential backoff (capped to 1.5h) . *) -val reconnect : Configuration.t -> t -> t tzresult Lwt.t +(** {2 Helpers } *) (** [get_predecessor_opt state head] returns the predecessor of block [head], when [head] is not the genesis block. *) @@ -68,23 +54,6 @@ val get_predecessor_opt : t -> head -> head option tzresult Lwt.t (** [get_predecessor state head] returns the predecessor block of [head]. *) val get_predecessor : t -> head -> head tzresult Lwt.t -(** [shutdown store] properly shut the layer 1 down. *) -val shutdown : t -> unit Lwt.t - -(** [fetch_tezos_shell_header l1_ctxt hash] returns the block shell header of - [hash]. Looks for the block in the blocks cache first, and fetches it from - the L1 node otherwise. *) -val fetch_tezos_shell_header : - t -> Block_hash.t -> Block_header.shell_header tzresult Lwt.t - -(** [fetch_tezos_block l1_ctxt hash] returns a block info given a block hash. - Looks for the block in the blocks cache first, and fetches it from the L1 - node otherwise. *) -val fetch_tezos_block : - t -> - Block_hash.t -> - Protocol_client_context.Alpha_block_services.block_info tzresult Lwt.t - (** [nth_predecessor l1_ctxt n head] return [block, history] where [block] is the nth predecessor of [head] and [history] is the list of blocks between [block] (excluded) and [head] (included) on the chain *) @@ -95,7 +64,20 @@ val nth_predecessor : t -> int -> head -> (head * head list) tzresult Lwt.t is [`Level l], then it returns the reorganization between [new_head] and level [l] on the same chain. *) val get_tezos_reorg_for_new_head : - t -> - [`Head of head | `Level of int32] -> - head -> - head Injector_common.reorg tzresult Lwt.t + t -> [`Head of head | `Level of int32] -> head -> head Reorg.t tzresult Lwt.t + +(** [fetch_tezos_shell_header cctxt hash] returns the block shell header of + [hash]. Looks for the block in the blocks cache first, and fetches it from + the L1 node otherwise. *) +val fetch_tezos_shell_header : + #Tezos_rpc.Context.simple -> + Block_hash.t -> + Block_header.shell_header tzresult Lwt.t + +(** [fetch_tezos_block cctxt hash] returns a block info given a block hash. + Looks for the block in the blocks cache first, and fetches it from the L1 + node otherwise. *) +val fetch_tezos_block : + #Tezos_rpc.Context.simple -> + Block_hash.t -> + Protocol_client_context.Alpha_block_services.block_info tzresult Lwt.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml index c9802326b7d207ac250eb457442462f0edb1e6e7..d17a6af774ad525aa46b1537490890da8d82db48 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/node_context.ml @@ -124,6 +124,7 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode loser_mode; l2_blocks_cache_size; dal_node_endpoint; + reconnection_delay; _; } as configuration) = let open Lwt_result_syntax in @@ -139,7 +140,9 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode let*! context = Context.load mode (Configuration.default_context_dir data_dir) in - let* l1_ctxt, kind = Layer1.start configuration cctxt in + let* l1_ctxt = + Layer1.start ~name:"sc_rollup_node" ~reconnection_delay cctxt + in let publisher = Configuration.Operator_purpose_map.find Publish operators in let* protocol_constants = retrieve_constants cctxt and* lcc = get_last_cemented_commitment cctxt rollup_address @@ -147,7 +150,12 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode Option.filter_map_es (get_last_published_commitment cctxt rollup_address) publisher + and* kind = + RPC.Sc_rollup.kind cctxt (cctxt#chain, cctxt#block) rollup_address () + and* genesis_info = + RPC.Sc_rollup.genesis_info cctxt (cctxt#chain, cctxt#block) rollup_address in + let*! () = Event.rollup_exists ~addr:configuration.sc_rollup_address ~kind in let*! () = if dal_cctxt = None && protocol_constants.parametric.dal.feature_enable then Event.warn_dal_enabled_no_node () @@ -162,7 +170,7 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode rollup_address; mode = operating_mode; operators; - genesis_info = l1_ctxt.Layer1.genesis_info; + genesis_info; lcc = Reference.new_ lcc; lpc = Reference.new_ lpc; kind; @@ -256,13 +264,13 @@ let hash_of_level node_ctxt level = | Some h -> return h | None -> failwith "Cannot retrieve hash of level %ld" level -let level_of_hash {l1_ctxt; store; _} hash = +let level_of_hash {cctxt; store; _} hash = let open Lwt_result_syntax in let* l2_header = Store.L2_blocks.header store.l2_blocks hash in match l2_header with | Some {level; _} -> return (Raw_level.to_int32 level) | None -> - let+ {level; _} = Layer1.fetch_tezos_shell_header l1_ctxt hash in + let+ {level; _} = Layer1.fetch_tezos_shell_header cctxt hash in level let save_level {store; _} Layer1.{hash; level} = diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/simulation.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/simulation.ml index 9072a881472507a9a8fb1786b4630982f5a6e361..c6facce09a1140a1e46adeb9006d92d9c16c37fe 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/simulation.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/simulation.ml @@ -89,7 +89,7 @@ module Make (Interpreter : Interpreter.S) : let simulate_info_per_level (node_ctxt : [`Read] Node_context.t) predecessor = let open Lwt_result_syntax in - let* block_info = Layer1.fetch_tezos_block node_ctxt.l1_ctxt predecessor in + let* block_info = Layer1.fetch_tezos_block node_ctxt.cctxt predecessor in let predecessor_timestamp = block_info.header.shell.timestamp in return {predecessor_timestamp; predecessor} diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon.ml b/src/proto_alpha/lib_sc_rollup_node/daemon.ml index ea86f054c3efd247da643a890b8e061b3d48710e..312f6832b21233268246f4ea8f0b7c2d965d795c 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon.ml @@ -1,8 +1,9 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2021 Nomadic Labs, *) -(* Copyright (c) 2022 TriliTech *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 TriliTech *) +(* Copyright (c) 2023 Functori, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -260,7 +261,7 @@ module Make (PVM : Pvm.S) = struct let process_l1_block_operations ~finalized node_ctxt (Layer1.{hash; _} as head) = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.l1_ctxt hash in + let* block = Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in let apply (type kind) accu ~source (operation : kind manager_operation) result = let open Lwt_result_syntax in @@ -368,7 +369,7 @@ module Make (PVM : Pvm.S) = struct in return_unit - let notify_injector new_head (reorg : Layer1.head Injector_common.reorg) = + let notify_injector new_head (reorg : Layer1.head Reorg.t) = let open Lwt_result_syntax in let open Layer1 in let new_chain = @@ -414,9 +415,7 @@ module Make (PVM : Pvm.S) = struct (* TODO: https://gitlab.com/tezos/tezos/-/issues/3348 Rollback state information on reorganization, i.e. for reorg.old_chain. *) - let* new_head = - Layer1.fetch_tezos_block node_ctxt.l1_ctxt head.Layer1.hash - in + let* new_head = Layer1.fetch_tezos_block node_ctxt.cctxt head.Layer1.hash in let header = Block_header.( raw @@ -437,49 +436,8 @@ module Make (PVM : Pvm.S) = struct let*! () = Injector.inject ~header () in return_unit - let is_connection_error trace = - TzTrace.fold - (fun yes error -> - yes - || - match error with - | Tezos_rpc_http.RPC_client_errors.( - Request_failed {error = Connection_failed _; _}) -> - true - | _ -> false) - false - trace - - (* TODO: https://gitlab.com/tezos/tezos/-/issues/2895 - Use Lwt_stream.fold_es once it is exposed. *) - let daemonize configuration (node_ctxt : _ Node_context.t) = - let open Lwt_result_syntax in - let rec loop (l1_ctxt : Layer1.t) = - let*! () = - Lwt_stream.iter_s - (fun head -> - let open Lwt_syntax in - let* res = on_layer_1_head node_ctxt head in - match res with - | Ok () -> return_unit - | Error trace when is_connection_error trace -> - Format.eprintf - "@[Connection error:@ %a@]@." - pp_print_trace - trace ; - l1_ctxt.stopper () ; - return_unit - | Error e -> - Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; - let* _ = Lwt_exit.exit_and_wait 1 in - return_unit) - l1_ctxt.heads - in - let*! () = Event.connection_lost () in - let* l1_ctxt = Layer1.reconnect configuration node_ctxt.l1_ctxt in - loop l1_ctxt - in - protect @@ fun () -> Lwt.no_cancel @@ loop node_ctxt.l1_ctxt + let daemonize (node_ctxt : _ Node_context.t) = + Layer1.iter_heads node_ctxt.l1_ctxt (on_layer_1_head node_ctxt) let install_finalizer node_ctxt rpc_server = let open Lwt_syntax in @@ -523,11 +481,11 @@ module Make (PVM : Pvm.S) = struct let run node_ctxt configuration = let open Lwt_result_syntax in let* () = check_initial_state_hash node_ctxt in + let* rpc_server = Components.RPC_server.start node_ctxt configuration in + let (_ : Lwt_exit.clean_up_callback_id) = + install_finalizer node_ctxt rpc_server + in let start () = - let* rpc_server = Components.RPC_server.start node_ctxt configuration in - let (_ : Lwt_exit.clean_up_callback_id) = - install_finalizer node_ctxt rpc_server - in let*! () = Inbox.start () in let signers = Configuration.Operator_purpose_map.bindings node_ctxt.operators @@ -582,7 +540,7 @@ module Make (PVM : Pvm.S) = struct ~rpc_addr:configuration.rpc_addr ~rpc_port:configuration.rpc_port in - daemonize configuration node_ctxt + daemonize node_ctxt in Metrics.Info.init_rollup_node_info ~id:node_ctxt.rollup_address @@ -590,7 +548,10 @@ module Make (PVM : Pvm.S) = struct ~genesis_level:node_ctxt.genesis_info.level ~genesis_hash:node_ctxt.genesis_info.commitment_hash ~pvm_kind:node_ctxt.kind ; - start () + protect start ~on_error:(fun e -> + Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; + let*! _ = Lwt_exit.exit_and_wait 1 in + return_unit) end let run ~data_dir (configuration : Configuration.t) diff --git a/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker.ml b/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker.ml index 80f95fa05f5950c164d8d19815721e6505260b8a..f372fad3e147d99c86ca983f258898e2bed9f0fa 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker.ml +++ b/src/proto_alpha/lib_sc_rollup_node/dal_slots_tracker.ml @@ -84,7 +84,7 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = return None | Some published_block_hash -> let* {metadata; _} = - Layer1.fetch_tezos_block node_ctxt.Node_context.l1_ctxt hash + Layer1.fetch_tezos_block node_ctxt.Node_context.cctxt hash in let*? metadata = Option.to_result @@ -203,7 +203,7 @@ module Confirmed_slots_history = struct slot_index) relevant_slots_indexes - let read_slots_history_from_l1 {Node_context.l1_ctxt = {cctxt; _}; _} block = + let read_slots_history_from_l1 {Node_context.cctxt; _} block = let open Lwt_result_syntax in (* We return the empty Slots_history if DAL is not enabled. *) let* slots_list_opt = diff --git a/src/proto_alpha/lib_sc_rollup_node/dune b/src/proto_alpha/lib_sc_rollup_node/dune index bbb1f5a0e6b80d023a464f9c1dff6cddd1d7fdeb..cf52914bd2cd88d74139eb1cdc7a0c9d4d99998d 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dune +++ b/src/proto_alpha/lib_sc_rollup_node/dune @@ -27,6 +27,7 @@ tezos-smart-rollup-layer2-alpha tezos-layer2-utils-alpha tezos_layer2_store + octez-crawler tezos-tree-encoding data-encoding irmin-pack @@ -59,5 +60,6 @@ -open Tezos_smart_rollup_layer2_alpha -open Tezos_layer2_utils_alpha -open Tezos_layer2_store + -open Octez_crawler -open Octez_injector -open Tezos_crypto_dal)) diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox.ml b/src/proto_alpha/lib_sc_rollup_node/inbox.ml index 8308d58e33283661f37f51485b4baeeb10e3cee6..c2415f9f5bbd4ba9477fe8f8c60dba56746c65dd 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/lib_sc_rollup_node/inbox.ml @@ -31,9 +31,9 @@ open Alpha_context let lift promise = Lwt.map Environment.wrap_tzresult promise -let get_messages Node_context.{l1_ctxt; _} head = +let get_messages Node_context.{cctxt; _} head = let open Lwt_result_syntax in - let* block = Layer1.fetch_tezos_block l1_ctxt head in + let* block = Layer1.fetch_tezos_block cctxt head in let apply (type kind) accu ~source:_ (operation : kind manager_operation) _result = let open Result_syntax in @@ -84,7 +84,7 @@ let get_messages Node_context.{l1_ctxt; _} head = proto_level = predecessor_proto_level; _; } = - Layer1.fetch_tezos_shell_header l1_ctxt predecessor + Layer1.fetch_tezos_shell_header cctxt predecessor in let is_migration_block = block.header.shell.proto_level <> predecessor_proto_level diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml index 52fc91285706f409a122c1d0cdeb26e08e3ad639..9a4890a7e852501396f4475f63f3821928010b03 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml @@ -69,7 +69,7 @@ module Make (PVM : Pvm.S) : S with module PVM = PVM = struct let get_boot_sector block_hash (node_ctxt : _ Node_context.t) = let open Lwt_result_syntax in let exception Found_boot_sector of string in - let* block = Layer1.fetch_tezos_block node_ctxt.l1_ctxt block_hash in + let* block = Layer1.fetch_tezos_block node_ctxt.cctxt block_hash in let missing_boot_sector () = failwith "Boot sector not found in Tezos block %a" diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1.ml b/src/proto_alpha/lib_sc_rollup_node/layer1.ml index 419e1dcd784c2d8ae3fd9b39614b851c798f930e..2bbaf1c0710e05c2ae7cbfec947376bef2438050 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1.ml +++ b/src/proto_alpha/lib_sc_rollup_node/layer1.ml @@ -1,7 +1,8 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 Functori, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -23,9 +24,6 @@ (* *) (*****************************************************************************) -open Configuration -open Protocol.Alpha_context -open Plugin open Protocol_client_context (** @@ -53,24 +51,6 @@ let () = (function Cannot_find_block hash -> Some hash | _ -> None) (fun hash -> Cannot_find_block hash) -type error += Cannot_find_predecessor of Block_hash.t - -let () = - register_error_kind - ~id:"sc_rollup.node.cannot_find_predecessor" - ~title:"Cannot find block predecessor from L1" - ~description:"A predecessor couldn't be found from the L1 node" - ~pp:(fun ppf hash -> - Format.fprintf - ppf - "Block with hash %a has no predecessor on the L1 node." - Block_hash.pp - hash) - `Temporary - Data_encoding.(obj1 (req "hash" Block_hash.encoding)) - (function Cannot_find_predecessor hash -> Some hash | _ -> None) - (fun hash -> Cannot_find_predecessor hash) - (** State @@ -91,156 +71,58 @@ module Blocks_cache = Aches_lwt.Lache.Make_option (Aches.Rache.Transfer (Aches.Rache.LRU) (Block_hash)) -type blocks_cache = - Protocol_client_context.Alpha_block_services.block_info Blocks_cache.t - -type t = { - blocks_cache : blocks_cache; - heads : head Lwt_stream.t; - cctxt : Protocol_client_context.full; - stopper : Tezos_rpc.Context.stopper; - genesis_info : Sc_rollup.Commitment.genesis_info; -} +type blocks_cache = Alpha_block_services.block_info Blocks_cache.t -(** +(** Global blocks cache for the smart rollup node. *) +let blocks_cache : blocks_cache = Blocks_cache.create 32 - Helpers - ======= +include Octez_crawler.Layer_1 -*) +let head_of_block_level (hash, level) = {hash; level} -(** [predecessors_of_blocks hashes] given a list of successive hashes, - returns an associative list that associates a hash to its - predecessor in this list. *) -let predecessors_of_blocks hashes = - let rec aux next = function [] -> [] | x :: xs -> (next, x) :: aux x xs in - match hashes with [] -> [] | x :: xs -> aux x xs +let block_level_of_head {hash; level} = (hash, level) -(** [get_predecessor block_hash] returns the predecessor block hash of - some [block_hash] through an RPC to the Tezos node. To limit the - number of RPCs, this information is requested for a batch of hashes - and cached locally. *) -let get_predecessor = - let max_cached = 1023 and max_read = 8 in - let module HM = - Aches.Vache.Map (Aches.Vache.FIFO_Precise) (Aches.Vache.Strong) (Block_hash) - in - let cache = HM.create max_cached in - fun cctxt (chain : Tezos_shell_services.Chain_services.chain) ancestor -> - let open Lwt_result_syntax in - match HM.find_opt cache ancestor with - | Some pred -> return_some pred - | None -> ( - let* blocks = - Tezos_shell_services.Chain_services.Blocks.list - cctxt - ~chain - ~heads:[ancestor] - ~length:max_read - () - in - match blocks with - | [ancestors] -> ( - List.iter - (fun (h, p) -> HM.replace cache h p) - (predecessors_of_blocks ancestors) ; - match HM.find_opt cache ancestor with - | None -> - (* We have just updated the cache with that information. *) - assert false - | Some predecessor -> return_some predecessor) - | _ -> return_none) +let iter_heads l1_ctxt f = + iter_heads l1_ctxt @@ fun (hash, {shell = {level; _}; _}) -> f {hash; level} -let get_predecessor_opt state {level; hash} = +let get_predecessor_opt state head = let open Lwt_result_syntax in - if level = 0l then return_none - else - let level = Int32.pred level in - let+ hash = get_predecessor state.cctxt state.cctxt#chain hash in - Option.map (fun hash -> {level; hash}) hash + let+ res = get_predecessor_opt state (block_level_of_head head) in + Option.map head_of_block_level res -let get_predecessor state ({hash; _} as head) = +let get_predecessor state head = let open Lwt_result_syntax in - let* pred = get_predecessor_opt state head in - match pred with - | None -> tzfail (Cannot_find_predecessor hash) - | Some pred -> return pred + let+ res = get_predecessor state (block_level_of_head head) in + head_of_block_level res -let rec connect ?(count = 0) ~delay cctxt genesis_info = - let open Lwt_syntax in - let* () = - if count = 0 then return_unit - else - let fcount = float_of_int (count - 1) in - (* Randomized exponential backoff capped to 1.5h: 1.5^count * delay ± 50% *) - let delay = delay *. (1.5 ** fcount) in - let delay = min delay 3600. in - let randomization_factor = 0.5 (* 50% *) in - let delay = - delay - +. Random.float (delay *. 2. *. randomization_factor) - -. (delay *. randomization_factor) - in - let* () = Event.wait_reconnect delay in - Lwt_unix.sleep delay - in - let* res = Tezos_shell_services.Monitor_services.heads cctxt cctxt#chain in - match res with - | Ok (heads, stopper) -> - let heads = - Lwt_stream.map_s - (fun (hash, Tezos_base.Block_header.{shell = {level; _}; _}) -> - let+ () = Layer1_event.switched_new_head hash level in - {hash; level}) - heads - in - return_ok (heads, stopper) - | Error e -> - let* () = Event.cannot_connect ~count e in - connect ~delay ~count:(count + 1) cctxt genesis_info +let nth_predecessor l1_state n block = + let open Lwt_result_syntax in + let+ res, preds = nth_predecessor l1_state n (block_level_of_head block) in + (head_of_block_level res, List.map head_of_block_level preds) -let start configuration (cctxt : Protocol_client_context.full) = +let get_tezos_reorg_for_new_head l1_ctxt old_head new_head = let open Lwt_result_syntax in - let*! () = Layer1_event.starting () in - let* kind = - RPC.Sc_rollup.kind - cctxt - (cctxt#chain, cctxt#block) - configuration.sc_rollup_address - () - in - let*! () = Event.rollup_exists ~addr:configuration.sc_rollup_address ~kind in - let* genesis_info = - RPC.Sc_rollup.genesis_info - cctxt - (cctxt#chain, cctxt#block) - configuration.sc_rollup_address + let old_head = + match old_head with + | `Level l -> `Level l + | `Head h -> `Head (block_level_of_head h) in - let+ heads, stopper = - connect ~delay:configuration.reconnection_delay cctxt genesis_info + let+ reorg = + get_tezos_reorg_for_new_head l1_ctxt old_head (block_level_of_head new_head) in - ( {cctxt; heads; blocks_cache = Blocks_cache.create 32; stopper; genesis_info}, - kind ) + Reorg.map head_of_block_level reorg -let reconnect configuration l1_ctxt = - let open Lwt_result_syntax in - let* heads, stopper = - connect - ~count:1 - ~delay:configuration.reconnection_delay - l1_ctxt.cctxt - l1_ctxt.genesis_info - in - return {l1_ctxt with heads; stopper} +(** -let shutdown state = - state.stopper () ; - Lwt.return_unit + Helpers + ======= -(** [fetch_tezos_block l1_ctxt hash] returns a block shell header of +*) + +(** [fetch_tezos_block cctxt hash] returns a block shell header of [hash]. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) -let fetch_tezos_shell_header l1_ctxt hash = +let fetch_tezos_shell_header cctxt hash = let open Lwt_syntax in trace (Cannot_find_block hash) @@ @@ -248,7 +130,7 @@ let fetch_tezos_shell_header l1_ctxt hash = let fetch hash = let* shell_header = Tezos_shell_services.Shell_services.Blocks.Header.shell_header - l1_ctxt.cctxt + cctxt ~chain:`Main ~block:(`Hash (hash, 0)) () @@ -261,7 +143,7 @@ let fetch_tezos_shell_header l1_ctxt hash = in let+ shell_header = let res = - Blocks_cache.bind l1_ctxt.blocks_cache hash (function + Blocks_cache.bind blocks_cache hash (function | Some block_info -> Lwt.return_some block_info.header.shell | None -> Lwt.return_none) in @@ -278,10 +160,10 @@ let fetch_tezos_shell_header l1_ctxt hash = | None, Some errs -> Error errs | Some shell_header, _ -> Ok shell_header -(** [fetch_tezos_block l1_ctxt hash] returns a block info given a block +(** [fetch_tezos_block cctxt hash] returns a block info given a block hash. Looks for the block in the blocks cache first, and fetches it from the L1 node otherwise. *) -let fetch_tezos_block l1_ctxt hash = +let fetch_tezos_block cctxt hash = let open Lwt_syntax in trace (Cannot_find_block hash) @@ @@ -289,7 +171,7 @@ let fetch_tezos_block l1_ctxt hash = let fetch hash = let* block = Alpha_block_services.info - l1_ctxt.cctxt + cctxt ~chain:`Main ~block:(`Hash (hash, 0)) ~metadata:`Always @@ -301,9 +183,7 @@ let fetch_tezos_block l1_ctxt hash = return_none | Ok block -> return_some block in - let+ block = - Blocks_cache.bind_or_put l1_ctxt.blocks_cache hash fetch Lwt.return - in + let+ block = Blocks_cache.bind_or_put blocks_cache hash fetch Lwt.return in match (block, !errors) with | None, None -> (* This should not happen if {!find_in_cache} behaves correctly, @@ -314,64 +194,3 @@ let fetch_tezos_block l1_ctxt hash = hash | None, Some errs -> Error errs | Some block, _ -> Ok block - -let nth_predecessor l1_state n block = - let open Lwt_result_syntax in - assert (n >= 0) ; - let rec aux acc n block = - if n = 0 then return (block, acc) - else - let* pred = get_predecessor l1_state block in - (aux [@tailcall]) (block :: acc) (n - 1) pred - in - aux [] n block - -let get_tezos_reorg_for_new_head l1_state old_head new_head = - let open Lwt_result_syntax in - (* old_head and new_head must have the same level when calling aux *) - let rec aux reorg old_head new_head = - if Block_hash.(old_head.hash = new_head.hash) then return reorg - else - let* old_head_pred = get_predecessor l1_state old_head in - let* new_head_pred = get_predecessor l1_state new_head in - let reorg = - Injector_common. - { - old_chain = old_head :: reorg.old_chain; - new_chain = new_head :: reorg.new_chain; - } - in - aux reorg old_head_pred new_head_pred - in - (* computing partial reorganization to make old_head and new_head at same - level *) - let distance = Int32.(to_int @@ abs @@ sub new_head.level old_head.level) in - let* old_head, new_head, reorg = - if old_head.level = new_head.level then - return (old_head, new_head, Injector_common.no_reorg) - else if old_head.level < new_head.level then - let+ new_head, new_chain = nth_predecessor l1_state distance new_head in - (old_head, new_head, {Injector_common.no_reorg with new_chain}) - else - let+ old_head, old_chain = nth_predecessor l1_state distance old_head in - (old_head, new_head, {Injector_common.no_reorg with old_chain}) - in - assert (old_head.level = new_head.level) ; - aux reorg old_head new_head - -(** Returns the reorganization of L1 blocks (if any) for [new_head]. *) -let get_tezos_reorg_for_new_head l1_state old_head new_head = - let open Lwt_result_syntax in - match old_head with - | `Level l -> - (* No known tezos head, we want all blocks from l. *) - if new_head.level < l then return Injector_common.no_reorg - else - let* _block_at_l, new_chain = - nth_predecessor - l1_state - (Int32.sub new_head.level l |> Int32.to_int) - new_head - in - return Injector_common.{old_chain = []; new_chain} - | `Head old_head -> get_tezos_reorg_for_new_head l1_state old_head new_head diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1.mli b/src/proto_alpha/lib_sc_rollup_node/layer1.mli index 1127f09957eae353021912c6fb5f49fa81ccd4b4..1580d1b26f023c61852796180b044bdbc0fbd25f 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1.mli +++ b/src/proto_alpha/lib_sc_rollup_node/layer1.mli @@ -1,7 +1,8 @@ (*****************************************************************************) (* *) (* Open Source License *) -(* Copyright (c) 2021 Nomadic Labs, *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 Functori, *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) @@ -33,33 +34,18 @@ type head = {hash : Block_hash.t; level : int32} val head_encoding : head Data_encoding.t -(** Type of cache holding the last 32 blocks, with their operations. *) -type blocks_cache - -type t = private { - blocks_cache : blocks_cache; - heads : head Lwt_stream.t; - cctxt : Protocol_client_context.full; - stopper : Tezos_rpc.Context.stopper; - genesis_info : Protocol.Alpha_context.Sc_rollup.Commitment.genesis_info; -} +include module type of Octez_crawler.Layer_1 (* TODO: https://gitlab.com/tezos/tezos/-/issues/3311 Allow to retrieve L1 blocks through Tezos node storage locally. *) -(** [start configuration cctxt] returns a stream of [chain_event] obtained - from the monitoring of the Tezos node set up by the client [cctxt]. The - layer 1 state is stored in the data directory declared in - [configuration]. *) -val start : - Configuration.t -> - Protocol_client_context.full -> - (t * Protocol.Alpha_context.Sc_rollup.Kind.t) tzresult Lwt.t +(** [iter_heads t f] calls [f] on all new heads appearing in the layer 1 + chain. In case of a disconnection with the layer 1 node, it reconnects + automatically. If [f] returns an error (other than a disconnection) it, + [iter_heads] terminates and returns the error. *) +val iter_heads : t -> (head -> unit tzresult Lwt.t) -> unit tzresult Lwt.t -(** [reconnect cfg l1_ctxt] reconnects (and retries with delay) to the - Tezos node. The delay for each reconnection is increased with a randomized - exponential backoff (capped to 1.5h) . *) -val reconnect : Configuration.t -> t -> t tzresult Lwt.t +(** {2 Helpers } *) (** [get_predecessor_opt state head] returns the predecessor of block [head], when [head] is not the genesis block. *) @@ -68,23 +54,6 @@ val get_predecessor_opt : t -> head -> head option tzresult Lwt.t (** [get_predecessor state head] returns the predecessor block of [head]. *) val get_predecessor : t -> head -> head tzresult Lwt.t -(** [shutdown store] properly shut the layer 1 down. *) -val shutdown : t -> unit Lwt.t - -(** [fetch_tezos_shell_header l1_ctxt hash] returns the block shell header of - [hash]. Looks for the block in the blocks cache first, and fetches it from - the L1 node otherwise. *) -val fetch_tezos_shell_header : - t -> Block_hash.t -> Block_header.shell_header tzresult Lwt.t - -(** [fetch_tezos_block l1_ctxt hash] returns a block info given a block hash. - Looks for the block in the blocks cache first, and fetches it from the L1 - node otherwise. *) -val fetch_tezos_block : - t -> - Block_hash.t -> - Protocol_client_context.Alpha_block_services.block_info tzresult Lwt.t - (** [nth_predecessor l1_ctxt n head] return [block, history] where [block] is the nth predecessor of [head] and [history] is the list of blocks between [block] (excluded) and [head] (included) on the chain *) @@ -95,7 +64,20 @@ val nth_predecessor : t -> int -> head -> (head * head list) tzresult Lwt.t is [`Level l], then it returns the reorganization between [new_head] and level [l] on the same chain. *) val get_tezos_reorg_for_new_head : - t -> - [`Head of head | `Level of int32] -> - head -> - head Injector_common.reorg tzresult Lwt.t + t -> [`Head of head | `Level of int32] -> head -> head Reorg.t tzresult Lwt.t + +(** [fetch_tezos_shell_header cctxt hash] returns the block shell header of + [hash]. Looks for the block in the blocks cache first, and fetches it from + the L1 node otherwise. *) +val fetch_tezos_shell_header : + #Tezos_rpc.Context.simple -> + Block_hash.t -> + Block_header.shell_header tzresult Lwt.t + +(** [fetch_tezos_block cctxt hash] returns a block info given a block hash. + Looks for the block in the blocks cache first, and fetches it from the L1 + node otherwise. *) +val fetch_tezos_block : + #Tezos_rpc.Context.simple -> + Block_hash.t -> + Protocol_client_context.Alpha_block_services.block_info tzresult Lwt.t diff --git a/src/proto_alpha/lib_sc_rollup_node/node_context.ml b/src/proto_alpha/lib_sc_rollup_node/node_context.ml index 828a1d2ec6b3f3c8b56ab1573f27509edbef0d3d..5827936498c5d0195035787925fd0567a4d070f5 100644 --- a/src/proto_alpha/lib_sc_rollup_node/node_context.ml +++ b/src/proto_alpha/lib_sc_rollup_node/node_context.ml @@ -124,6 +124,7 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode loser_mode; l2_blocks_cache_size; dal_node_endpoint; + reconnection_delay; _; } as configuration) = let open Lwt_result_syntax in @@ -139,7 +140,9 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode let*! context = Context.load mode (Configuration.default_context_dir data_dir) in - let* l1_ctxt, kind = Layer1.start configuration cctxt in + let* l1_ctxt = + Layer1.start ~name:"sc_rollup_node" ~reconnection_delay cctxt + in let publisher = Configuration.Operator_purpose_map.find Publish operators in let* protocol_constants = retrieve_constants cctxt and* lcc = get_last_cemented_commitment cctxt rollup_address @@ -147,7 +150,12 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode Option.filter_map_es (get_last_published_commitment cctxt rollup_address) publisher + and* kind = + RPC.Sc_rollup.kind cctxt (cctxt#chain, cctxt#block) rollup_address () + and* genesis_info = + RPC.Sc_rollup.genesis_info cctxt (cctxt#chain, cctxt#block) rollup_address in + let*! () = Event.rollup_exists ~addr:configuration.sc_rollup_address ~kind in let*! () = if dal_cctxt = None && protocol_constants.parametric.dal.feature_enable then Event.warn_dal_enabled_no_node () @@ -162,7 +170,7 @@ let init (cctxt : Protocol_client_context.full) ~data_dir mode rollup_address; mode = operating_mode; operators; - genesis_info = l1_ctxt.Layer1.genesis_info; + genesis_info; lcc = Reference.new_ lcc; lpc = Reference.new_ lpc; kind; @@ -256,13 +264,13 @@ let hash_of_level node_ctxt level = | Some h -> return h | None -> failwith "Cannot retrieve hash of level %ld" level -let level_of_hash {l1_ctxt; store; _} hash = +let level_of_hash {cctxt; store; _} hash = let open Lwt_result_syntax in let* l2_header = Store.L2_blocks.header store.l2_blocks hash in match l2_header with | Some {level; _} -> return (Raw_level.to_int32 level) | None -> - let+ {level; _} = Layer1.fetch_tezos_shell_header l1_ctxt hash in + let+ {level; _} = Layer1.fetch_tezos_shell_header cctxt hash in level let save_level {store; _} Layer1.{hash; level} = diff --git a/src/proto_alpha/lib_sc_rollup_node/simulation.ml b/src/proto_alpha/lib_sc_rollup_node/simulation.ml index 9072a881472507a9a8fb1786b4630982f5a6e361..c6facce09a1140a1e46adeb9006d92d9c16c37fe 100644 --- a/src/proto_alpha/lib_sc_rollup_node/simulation.ml +++ b/src/proto_alpha/lib_sc_rollup_node/simulation.ml @@ -89,7 +89,7 @@ module Make (Interpreter : Interpreter.S) : let simulate_info_per_level (node_ctxt : [`Read] Node_context.t) predecessor = let open Lwt_result_syntax in - let* block_info = Layer1.fetch_tezos_block node_ctxt.l1_ctxt predecessor in + let* block_info = Layer1.fetch_tezos_block node_ctxt.cctxt predecessor in let predecessor_timestamp = block_info.header.shell.timestamp in return {predecessor_timestamp; predecessor}