diff --git a/.dockerignore b/.dockerignore index 309f545c6e554355297885a9d16a19021a4b5cdd..f84a78de1cd7e6fd0a036d807e1f1ff882a86696 100644 --- a/.dockerignore +++ b/.dockerignore @@ -35,7 +35,7 @@ octez-endorser-* octez-accuser-* octez-proxy-server octez-signer -octez-smart-rollup-node-* +octez-smart-rollup-node* octez-smart-rollup-client-* scripts/opam-test-all.sh.DONE diff --git a/.gitignore b/.gitignore index be90d2bce40278f51c05f19c967a2007f6cc1b5f..c888daac6e421b86e234c4919e9bcb884cca6d3e 100644 --- a/.gitignore +++ b/.gitignore @@ -47,7 +47,7 @@ __pycache__ /tezos-tps-evaluation-* /octez-tps-evaluation-* /tezos-smart-rollup-node-* -/octez-smart-rollup-node-* +/octez-smart-rollup-node* /octez-smart-rollup-sequencer-node /tezos-smart-rollup-client-* /octez-smart-rollup-client-* diff --git a/.gitlab/ci/jobs/packaging/opam_package.yml b/.gitlab/ci/jobs/packaging/opam_package.yml index eb5078b6b4b5199cb85674b40774670bacbf3d4c..58135c2c52e52682347df4e851dae8532c5a77bb 100644 --- a/.gitlab/ci/jobs/packaging/opam_package.yml +++ b/.gitlab/ci/jobs/packaging/opam_package.yml @@ -648,12 +648,7 @@ opam:octez-smart-rollup-client-PtNairob: # Ignoring unreleased package octez-smart-rollup-client-alpha. -opam:octez-smart-rollup-node: - extends: - - .opam_template - - .rules_template__trigger_all_opam_batch_1 - variables: - package: octez-smart-rollup-node +# Ignoring unreleased package octez-smart-rollup-node. opam:octez-smart-rollup-node-Proxford: extends: @@ -678,6 +673,13 @@ opam:octez-smart-rollup-node-PtNairob: # Ignoring unreleased package octez-smart-rollup-node-alpha. +opam:octez-smart-rollup-node-lib: + extends: + - .opam_template + - .rules_template__trigger_all_opam_batch_1 + variables: + package: octez-smart-rollup-node-lib + # Ignoring unreleased package octez-smart-rollup-sequencer. # Ignoring unreleased package octez-smart-rollup-sequencer-node. diff --git a/CHANGES.rst b/CHANGES.rst index 9cba792c8aa22dd2f7dabc2dcb430fcefe8bda3c..a44d7b2b6bd2c35d31e1ac1ddaaec72aa423fc6c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -269,6 +269,10 @@ Smart Rollup node ----------------- - Faster bootstrapping process. (MR :gl:`!8618`, MR :gl:`!8767`) +- Single, protocol-agnostic, rollup node binary. The rollup node + ``octez-smart-rollup-node`` works with any protocol and supports protocol + upgrades. The other protocol specific rollup nodes still exist but will be + deprecated. (MR :gl:`!9105`) - Added a new metrics ``head_inbox_process_time`` to report the time the rollup node spent to process a new Layer 1 head. (MR :gl:`!8971`) diff --git a/docs/Makefile b/docs/Makefile index b2d833811b2bb933fc82595599a8a42255676e98..223c6786bda1959f7015488af32f60f73025e835 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -71,7 +71,9 @@ manuals: \ ./scripts/man2txt.py > api/octez-node.txt @../octez-proxy-server --help | grep -v '^Octez-proxy-server' | grep -v 'OCTEZ-PROXY-SERVER(1)' | \ ./scripts/man2txt.py > api/octez-proxy-server.txt - + @../octez-smart-rollup-node man -verbosity 3 -format html | \ + sed "s#${HOME}#\$$HOME#g" > api/octez-smart-rollup-node.html + # artificially depend on docexes to avoid concurrent dunes .PHONY: odoc odoc: docexes diff --git a/docs/alpha/smart_rollups.rst b/docs/alpha/smart_rollups.rst index 3c903a77031aecfd165e9c5011736cf9068225e5..5ed4f4a686a3a1f4911a4bf09586e767ab74019e 100644 --- a/docs/alpha/smart_rollups.rst +++ b/docs/alpha/smart_rollups.rst @@ -472,7 +472,7 @@ The rollup node can then be run with: .. code:: sh - octez-smart-rollup-node-alpha --base-dir "${OCLIENT_DIR}" \ + octez-smart-rollup-node --base-dir "${OCLIENT_DIR}" \ run operator for "${SOR_ALIAS_OR_ADDR}" \ with operators "${OPERATOR_ADDR}" \ --data-dir "${ROLLUP_NODE_DIR}" @@ -545,7 +545,7 @@ uses the same arguments as the ``run`` command: .. code:: sh - octez-smart-rollup-node-alpha --base-dir "${OCLIENT_DIR}" \ + octez-smart-rollup-node --base-dir "${OCLIENT_DIR}" \ init operator config for "${SOR_ALIAS_OR_ADDR}" \ with operators "${OPERATOR_ADDR}" \ --data-dir "${ROLLUP_NODE_DIR}" @@ -577,7 +577,7 @@ The rollup node can now be run with just: .. code:: sh - octez-smart-rollup-node-alpha -d "${OCLIENT_DIR}" run --data-dir ${ROLLUP_NODE_DIR} + octez-smart-rollup-node -d "${OCLIENT_DIR}" run --data-dir ${ROLLUP_NODE_DIR} The configuration will be read from ``${ROLLUP_NODE_DIR}/config.json``. @@ -586,7 +586,7 @@ Rollup node in a sandbox The node can also be tested locally with a sandbox environment. (See :doc:`sandbox documentation <../user/sandbox>`.) -Once you initialized the "sandboxed" client data with ``./src/bin_client/octez-init-sandboxed-client.sh``, you can run a sandboxed rollup node with ``octez-smart-rollup-node-alpha run``. +Once you initialized the "sandboxed" client data with ``./src/bin_client/octez-init-sandboxed-client.sh``, you can run a sandboxed rollup node with ``octez-smart-rollup-node run``. A temporary directory ``/tmp/tezos-smart-rollup-node.xxxxxxxx`` will be used. However, a specific data directory can be set with the environment variable ``SCORU_DATA_DIR``. diff --git a/docs/nairobi/smart_rollups.rst b/docs/nairobi/smart_rollups.rst index d022abc2bc43884375d782d7f5c26af9335d68df..abe441b60fbf17139f8433fde9c2d53119ef456b 100644 --- a/docs/nairobi/smart_rollups.rst +++ b/docs/nairobi/smart_rollups.rst @@ -456,38 +456,23 @@ Now that the rollup is originated, anyone can make it progress by deploying a rollup node. First, we need to decide on a directory where the rollup node stores -its data. Let us assign ``${ROLLUP_NODE_DIR}`` with this path. The -rollup node is configured with the following command: +its data. Let us assign ``${ROLLUP_NODE_DIR}`` with this path, by default +``~/.tezos-smart-rollup-node``. + + +The rollup node can then be run with: .. code:: sh - octez-smart-rollup-node-alpha --base-dir "${OCLIENT_DIR}" \ - init operator config for "${SOR_ALIAS_OR_ADDR}" \ + octez-smart-rollup-node --base-dir "${OCLIENT_DIR}" \ + run operator for "${SOR_ALIAS_OR_ADDR}" \ with operators "${OPERATOR_ADDR}" \ --data-dir "${ROLLUP_NODE_DIR}" -This creates a configuration file: - -:: - - Smart rollup node configuration written in ${ROLLUP_NODE_DIR}/config.json +where ``${OCLIENT_DIR}`` is the data directory of the Octez client, by default ``~/.tezos-client``. -Here is the content of the file: - -:: - - { - "data-dir": "${ROLLUP_NODE_DIR}", - "smart-rollup-address": "${SOR_ADDR}", - "smart-rollup-node-operator": { - "publish": "${OPERATOR_ADDR}", - "add_messages": "${OPERATOR_ADDR}", - "cement": "${OPERATOR_ADDR}", - "refute": "${OPERATOR_ADDR}" - }, - "fee-parameters": {}, - "mode": "operator" - } +The log should show that the rollup node follows the Layer 1 chain and +processes the inbox of each level. Notice that distinct Layer 1 adresses could be used for the Layer 1 operations issued by the rollup node simply by editing the @@ -543,23 +528,57 @@ operations which are injected by the rollup node in each mode. .. [*] An accuser node will publish commitments only when it detects conflicts; for such cases it must make a deposit of 10,000 tez. -Second, the configured rollup node can be run: + +Configuration file +"""""""""""""""""" + +The rollup node can also be configured with the following command that +uses the same arguments as the ``run`` command: .. code:: sh - octez-smart-rollup-node-alpha -d "${OCLIENT_DIR}" run --data-dir ${ROLLUP_NODE_DIR} + octez-smart-rollup-node --base-dir "${OCLIENT_DIR}" \ + init operator config for "${SOR_ALIAS_OR_ADDR}" \ + with operators "${OPERATOR_ADDR}" \ + --data-dir "${ROLLUP_NODE_DIR}" -where ``${OCLIENT_DIR}`` is the data directory of the Octez client. +This creates a configuration file: -The log should show that the rollup node follows the Layer 1 chain and -processes the inbox of each level. +:: + + Smart rollup node configuration written in ${ROLLUP_NODE_DIR}/config.json + +Here is the content of the file: + +:: + + { + "data-dir": "${ROLLUP_NODE_DIR}", + "smart-rollup-address": "${SOR_ADDR}", + "smart-rollup-node-operator": { + "publish": "${OPERATOR_ADDR}", + "add_messages": "${OPERATOR_ADDR}", + "cement": "${OPERATOR_ADDR}", + "refute": "${OPERATOR_ADDR}" + }, + "fee-parameters": {}, + "mode": "operator" + } + +The rollup node can now be run with just: + +.. code:: sh + + octez-smart-rollup-node -d "${OCLIENT_DIR}" run --data-dir ${ROLLUP_NODE_DIR} + +The configuration will be read from ``${ROLLUP_NODE_DIR}/config.json``. Rollup node in a sandbox """""""""""""""""""""""" The node can also be tested locally with a sandbox environment. (See :doc:`sandbox documentation <../user/sandbox>`.) -Once you initialized the "sandboxed" client data with ``./src/bin_client/octez-init-sandboxed-client.sh``, you can run a sandboxed rollup node with ``octez-smart-rollup-node-alpha run``. +Once you initialized the "sandboxed" client data with ``./src/bin_client/octez-init-sandboxed-client.sh``, you can run a sandboxed rollup node with ``octez-smart-rollup-node run``. A temporary directory ``/tmp/tezos-smart-rollup-node.xxxxxxxx`` will be used. However, a specific data directory can be set with the environment variable ``SCORU_DATA_DIR``. @@ -606,7 +625,7 @@ can encode an outbox transaction using the Octez rollup client as follows: "entrypoint" : "%default" } ]' - EMESSAGE=$(octez-smart-rollup-client-alpha encode outbox message "${MESSAGE}") + EMESSAGE=$(octez-smart-rollup-client-PtNairob encode outbox message "${MESSAGE}") .. _triggering_execution_outbox_message: @@ -626,7 +645,7 @@ populated as follows: .. code:: sh - octez-smart-rollup-client-alpha rpc get \ + octez-smart-rollup-client-PtNairob rpc get \ /global/block/cemented/outbox/${L}/messages Here is the output for this command: @@ -648,7 +667,7 @@ proof is retrieved as follows: .. code:: sh - PROOF=$(octez-smart-rollup-client-alpha get proof for message 0 \ + PROOF=$(octez-smart-rollup-client-PtNairob get proof for message 0 \ of outbox at level "${L}" \ transferring "${MESSAGE}") diff --git a/docs/oxford/smart_rollups.rst b/docs/oxford/smart_rollups.rst index 113e6d291c41d0ddcc4345eec6816fc281a60741..519f4f50dcfa44c4dbe19528a64d16ed5ed1f740 100644 --- a/docs/oxford/smart_rollups.rst +++ b/docs/oxford/smart_rollups.rst @@ -472,7 +472,7 @@ The rollup node can then be run with: .. code:: sh - octez-smart-rollup-node-alpha --base-dir "${OCLIENT_DIR}" \ + octez-smart-rollup-node --base-dir "${OCLIENT_DIR}" \ run operator for "${SOR_ALIAS_OR_ADDR}" \ with operators "${OPERATOR_ADDR}" \ --data-dir "${ROLLUP_NODE_DIR}" @@ -545,7 +545,7 @@ uses the same arguments as the ``run`` command: .. code:: sh - octez-smart-rollup-node-alpha --base-dir "${OCLIENT_DIR}" \ + octez-smart-rollup-node --base-dir "${OCLIENT_DIR}" \ init operator config for "${SOR_ALIAS_OR_ADDR}" \ with operators "${OPERATOR_ADDR}" \ --data-dir "${ROLLUP_NODE_DIR}" @@ -577,7 +577,7 @@ The rollup node can now be run with just: .. code:: sh - octez-smart-rollup-node-alpha -d "${OCLIENT_DIR}" run --data-dir ${ROLLUP_NODE_DIR} + octez-smart-rollup-node -d "${OCLIENT_DIR}" run --data-dir ${ROLLUP_NODE_DIR} The configuration will be read from ``${ROLLUP_NODE_DIR}/config.json``. @@ -586,7 +586,7 @@ Rollup node in a sandbox The node can also be tested locally with a sandbox environment. (See :doc:`sandbox documentation <../user/sandbox>`.) -Once you initialized the "sandboxed" client data with ``./src/bin_client/octez-init-sandboxed-client.sh``, you can run a sandboxed rollup node with ``octez-smart-rollup-node-alpha run``. +Once you initialized the "sandboxed" client data with ``./src/bin_client/octez-init-sandboxed-client.sh``, you can run a sandboxed rollup node with ``octez-smart-rollup-node run``. A temporary directory ``/tmp/tezos-smart-rollup-node.xxxxxxxx`` will be used. However, a specific data directory can be set with the environment variable ``SCORU_DATA_DIR``. @@ -633,7 +633,7 @@ outbox transaction using the Octez rollup client as follows: "entrypoint" : "%default" } ]' - EMESSAGE=$(octez-smart-rollup-client-alpha encode outbox message "${MESSAGE}") + EMESSAGE=$(octez-smart-rollup-client-Proxford encode outbox message "${MESSAGE}") .. _triggering_execution_outbox_message_oxford: @@ -652,7 +652,7 @@ populated as follows: .. code:: sh - octez-smart-rollup-client-alpha rpc get \ + octez-smart-rollup-client-Proxford rpc get \ /global/block/cemented/outbox/${L}/messages Here is the output for this command: @@ -674,7 +674,7 @@ proof is retrieved as follows: .. code:: sh - PROOF=$(octez-smart-rollup-client-alpha get proof for message 0 \ + PROOF=$(octez-smart-rollup-client-Proxford get proof for message 0 \ of outbox at level "${L}" \ transferring "${MESSAGE}") diff --git a/docs/shell/cli-commands.rst b/docs/shell/cli-commands.rst index b04acfa7eb1907121e189a603da19912b1e9ab35..67936e60d6e861686219d933468534062385d7b0 100644 --- a/docs/shell/cli-commands.rst +++ b/docs/shell/cli-commands.rst @@ -85,3 +85,12 @@ DAC client manual (Experimental) ================================ .. raw:: html :file: ../api/octez-dac-client.html + + +.. _smart_rollup_node_manual: + +Smart rollup node manual +======================== + +.. raw:: html + :file: ../api/octez-smart-rollup-node.html diff --git a/dune-project b/dune-project index 3764355cc79efa7e1aed2ae9166ca2ab07783cde..28971740631d49cf26306d8fde6a4154b1d920bd 100644 --- a/dune-project +++ b/dune-project @@ -50,6 +50,7 @@ (package (name octez-smart-rollup-node-PtMumbai)) (package (name octez-smart-rollup-node-PtNairob)) (package (name octez-smart-rollup-node-alpha)) +(package (name octez-smart-rollup-node-lib)) (package (name octez-smart-rollup-sequencer)(allow_empty)) (package (name octez-smart-rollup-sequencer-node)) (package (name octez-smart-rollup-wasm-benchmark)(allow_empty)) diff --git a/manifest/main.ml b/manifest/main.ml index 260064f7093306e18e160b9a8e18080102799f63..8bf90160c657c3d4a70e49a8dbb008712e9292ec 100644 --- a/manifest/main.ml +++ b/manifest/main.ml @@ -4111,7 +4111,8 @@ let octez_smart_rollup_lib = let octez_smart_rollup_node_lib = public_lib - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" + ~internal_name:"octez_smart_rollup_node" ~path:"src/lib_smart_rollup_node" ~synopsis:"Octez: library for Smart Rollup node" ~deps: @@ -7419,6 +7420,44 @@ let _octez_dac_client = ] @ protocol_deps) +let _octez_smart_rollup_node = + let protocol_deps = + let deps_for_protocol protocol = + let is_optional = + match (Protocol.status protocol, Protocol.number protocol) with + | Active, V _ -> false + | (Frozen | Overridden | Not_mainnet), _ | Active, (Alpha | Other) -> + true + in + let targets = + List.filter_map Fun.id [Protocol.octez_sc_rollup_node protocol] + in + if is_optional then List.map optional targets else targets + in + List.map deps_for_protocol Protocol.all |> List.flatten + in + public_exe + "octez-smart-rollup-node" + ~internal_name:"main_smart_rollup_node" + ~path:"src/bin_smart_rollup_node" + ~synopsis:"Octez: Smart rollup node" + ~release_status:Experimental + ~linkall:true + ~with_macos_security_framework:true + ~deps: + ([ + octez_base |> open_ |> open_ ~m:"TzPervasives" + |> open_ ~m:"TzPervasives.Error_monad.Legacy_monad_globals"; + octez_clic; + octez_shell_services |> open_; + octez_client_base |> open_; + octez_client_base_unix |> open_; + octez_client_commands |> open_; + octez_smart_rollup_lib |> open_; + octez_smart_rollup_node_lib |> open_; + ] + @ protocol_deps) + let _octez_scoru_wasm_debugger = public_exe (sf "octez-smart-rollup-wasm-debugger") diff --git a/opam/octez-smart-rollup-node-Proxford.opam b/opam/octez-smart-rollup-node-Proxford.opam index 1ae2c113a93015d4faf7dce5449209e4e51b2060..4454f585f7a0e62053cb316aaa313c6d690cdf1b 100644 --- a/opam/octez-smart-rollup-node-Proxford.opam +++ b/opam/octez-smart-rollup-node-Proxford.opam @@ -41,7 +41,7 @@ depends: [ "aches" { >= "1.0.0" } "aches-lwt" { >= "1.0.0" } "octez-injector" - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" "tezos-scoru-wasm" "tezos-scoru-wasm-fast" "tezos-crypto-dal" diff --git a/opam/octez-smart-rollup-node-PtMumbai.opam b/opam/octez-smart-rollup-node-PtMumbai.opam index 177faba01d7de419d703d74540ab07675787cc0d..9087c4c147ad42af584b089edc9dd7e505ca1f6e 100644 --- a/opam/octez-smart-rollup-node-PtMumbai.opam +++ b/opam/octez-smart-rollup-node-PtMumbai.opam @@ -38,7 +38,7 @@ depends: [ "aches" { >= "1.0.0" } "aches-lwt" { >= "1.0.0" } "octez-injector" - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" "tezos-scoru-wasm" "tezos-scoru-wasm-fast" "tezos-crypto-dal" diff --git a/opam/octez-smart-rollup-node-PtNairob.opam b/opam/octez-smart-rollup-node-PtNairob.opam index 4a17b4d5e925faaa85a4daf7c6ca151747217a11..1d964adac6d4bdd5a4f19c02124bae6bb855b38c 100644 --- a/opam/octez-smart-rollup-node-PtNairob.opam +++ b/opam/octez-smart-rollup-node-PtNairob.opam @@ -38,7 +38,7 @@ depends: [ "aches" { >= "1.0.0" } "aches-lwt" { >= "1.0.0" } "octez-injector" - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" "tezos-scoru-wasm" "tezos-scoru-wasm-fast" "tezos-crypto-dal" diff --git a/opam/octez-smart-rollup-node-alpha.opam b/opam/octez-smart-rollup-node-alpha.opam index 8cda3456f7d26a1d46a7242801d4f217629fc932..07ef110daf6ecb8dc0cb3d2b4850f01cbbc6a032 100644 --- a/opam/octez-smart-rollup-node-alpha.opam +++ b/opam/octez-smart-rollup-node-alpha.opam @@ -41,7 +41,7 @@ depends: [ "aches" { >= "1.0.0" } "aches-lwt" { >= "1.0.0" } "octez-injector" - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" "tezos-scoru-wasm" "tezos-scoru-wasm-fast" "tezos-crypto-dal" diff --git a/opam/octez-smart-rollup-node-lib.opam b/opam/octez-smart-rollup-node-lib.opam new file mode 100644 index 0000000000000000000000000000000000000000..12c31d0dcd7bcfdfe2cc596eec7d73f393cc4469 --- /dev/null +++ b/opam/octez-smart-rollup-node-lib.opam @@ -0,0 +1,34 @@ +# 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-stdlib-unix" + "tezos-crypto" + "tezos-client-base" + "tezos-client-base-unix" + "cohttp-lwt-unix" { >= "4.0.0" & != "5.1.0" } + "octez-node-config" + "prometheus-app" { >= "1.2" } + "tezos-dal-node-lib" + "tezos-dac-client-lib" + "octez-injector" + "tezos-version" + "tezos-layer2-store" + "octez-crawler" + "octez-smart-rollup" +] +build: [ + ["rm" "-r" "vendors" "contrib"] + ["dune" "build" "-p" name "-j" jobs] + ["dune" "runtest" "-p" name "-j" jobs] {with-test} +] +synopsis: "Octez: library for Smart Rollup node" diff --git a/opam/octez-smart-rollup-node.opam b/opam/octez-smart-rollup-node.opam index 12c31d0dcd7bcfdfe2cc596eec7d73f393cc4469..2c59232452f2efb3ead0f60d69094ed965fe35fd 100644 --- a/opam/octez-smart-rollup-node.opam +++ b/opam/octez-smart-rollup-node.opam @@ -11,24 +11,23 @@ depends: [ "dune" { >= "3.0" } "ocaml" { >= "4.14" } "tezos-base" - "tezos-stdlib-unix" - "tezos-crypto" + "tezos-clic" + "tezos-shell-services" "tezos-client-base" "tezos-client-base-unix" - "cohttp-lwt-unix" { >= "4.0.0" & != "5.1.0" } - "octez-node-config" - "prometheus-app" { >= "1.2" } - "tezos-dal-node-lib" - "tezos-dac-client-lib" - "octez-injector" - "tezos-version" - "tezos-layer2-store" - "octez-crawler" + "tezos-client-commands" "octez-smart-rollup" + "octez-smart-rollup-node-lib" + "octez-smart-rollup-node-PtMumbai" + "octez-smart-rollup-node-PtNairob" + "octez-smart-rollup-node-Proxford" +] +depopts: [ + "octez-smart-rollup-node-alpha" ] build: [ ["rm" "-r" "vendors" "contrib"] ["dune" "build" "-p" name "-j" jobs] ["dune" "runtest" "-p" name "-j" jobs] {with-test} ] -synopsis: "Octez: library for Smart Rollup node" +synopsis: "Octez: Smart rollup node" diff --git a/opam/octez-smart-rollup-sequencer-node.opam b/opam/octez-smart-rollup-sequencer-node.opam index eb37c844e4c8b5276fde18a3f61d721d1084e172..2030794637a848293097b6c18d678c0197357d1b 100644 --- a/opam/octez-smart-rollup-sequencer-node.opam +++ b/opam/octez-smart-rollup-sequencer-node.opam @@ -15,7 +15,7 @@ depends: [ "tezos-client-base" "tezos-client-base-unix" "tezos-client-commands" - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" "tezos-client-alpha" "octez-smart-rollup-node-alpha" "octez-smart-rollup-sequencer" diff --git a/opam/octez-smart-rollup-sequencer.opam b/opam/octez-smart-rollup-sequencer.opam index 3cd84df45810df3727c58f6601b9e6418e966d39..974b5b44db3d9f1949a5ada3ac9b332df499fb27 100644 --- a/opam/octez-smart-rollup-sequencer.opam +++ b/opam/octez-smart-rollup-sequencer.opam @@ -15,7 +15,7 @@ depends: [ "tezos-protocol-alpha" "octez-smart-rollup-node-alpha" "tezos-workers" - "octez-smart-rollup-node" + "octez-smart-rollup-node-lib" "octez-smart-rollup" "tezos-rpc" "tezos-rpc-http" diff --git a/script-inputs/experimental-executables b/script-inputs/experimental-executables index d6513465432f4b33a6df89197d9dfd40e15c7f46..b7271fd0a69115686da282b2913fff6dcd816b23 100644 --- a/script-inputs/experimental-executables +++ b/script-inputs/experimental-executables @@ -1,5 +1,6 @@ octez-evm-proxy-server octez-smart-rollup-sequencer-node +octez-smart-rollup-node octez-dal-node octez-smart-rollup-node-alpha octez-smart-rollup-client-alpha diff --git a/src/bin_sequencer_node/dune b/src/bin_sequencer_node/dune index ab8b306e86307b5d4da7e2f9f23d9552ed180949..d9a05e903d3bad9e56b346865d0cd0bc9f4bc645 100644 --- a/src/bin_sequencer_node/dune +++ b/src/bin_sequencer_node/dune @@ -12,7 +12,7 @@ tezos-client-base tezos-client-base-unix tezos-client-commands - octez-smart-rollup-node + octez-smart-rollup-node-lib tezos-client-alpha octez_smart_rollup_node_alpha octez_smart_rollup_sequencer) diff --git a/src/bin_smart_rollup_node/dune b/src/bin_smart_rollup_node/dune new file mode 100644 index 0000000000000000000000000000000000000000..332dfba77a550784779a0a20b7bc9a85ca061f42 --- /dev/null +++ b/src/bin_smart_rollup_node/dune @@ -0,0 +1,43 @@ +; This file was automatically generated, do not edit. +; Edit file manifest/main.ml instead. + +(executable + (name main_smart_rollup_node) + (public_name octez-smart-rollup-node) + (package octez-smart-rollup-node) + (instrumentation (backend bisect_ppx)) + (libraries + tezos-base + tezos-clic + tezos-shell-services + tezos-client-base + tezos-client-base-unix + tezos-client-commands + octez-smart-rollup + octez-smart-rollup-node-lib + octez_smart_rollup_node_PtMumbai + octez_smart_rollup_node_PtNairob + octez_smart_rollup_node_Proxford + (select void_for_linking-octez_smart_rollup_node_alpha from + (octez_smart_rollup_node_alpha -> void_for_linking-octez_smart_rollup_node_alpha.empty) + (-> void_for_linking-octez_smart_rollup_node_alpha.empty))) + (link_flags + (:standard) + (:include %{workspace_root}/static-link-flags.sexp) + (:include %{workspace_root}/macos-link-flags.sexp) + (-linkall)) + (flags + (:standard) + -open Tezos_base + -open Tezos_base.TzPervasives + -open Tezos_base.TzPervasives.Error_monad.Legacy_monad_globals + -open Tezos_shell_services + -open Tezos_client_base + -open Tezos_client_base_unix + -open Tezos_client_commands + -open Octez_smart_rollup + -open Octez_smart_rollup_node)) + +(rule + (action + (progn (write-file void_for_linking-octez_smart_rollup_node_alpha.empty "")))) diff --git a/src/bin_smart_rollup_node/main_smart_rollup_node.ml b/src/bin_smart_rollup_node/main_smart_rollup_node.ml new file mode 100644 index 0000000000000000000000000000000000000000..6d5acf80dfb3027046a2d61a7e5bd6106ceafd8f --- /dev/null +++ b/src/bin_smart_rollup_node/main_smart_rollup_node.ml @@ -0,0 +1,306 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2023 Nomadic Labs, *) +(* Copyright (c) 2023 Trili Tech, *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +include Cli.Binary_dependent_args (struct + let binary_name = "smart rollup node" +end) + +let group = + { + Tezos_clic.name = "sc_rollup.node"; + title = "Commands related to the smart rollup node."; + } + +let config_init_command = + let open Lwt_result_syntax in + let open Tezos_clic in + let open Cli in + command + ~group + ~desc:"Configure the smart rollup node." + (args15 + force_switch + data_dir_arg + rpc_addr_arg + rpc_port_arg + metrics_addr_arg + loser_mode_arg + reconnection_delay_arg + dal_node_endpoint_arg + dac_observer_endpoint_arg + dac_timeout_arg + injector_retention_period_arg + injector_attempts_arg + injection_ttl_arg + log_kernel_debug_arg + boot_sector_file_arg) + (prefix "init" @@ mode_param + @@ prefixes ["config"; "for"] + @@ sc_rollup_address_param + @@ prefixes ["with"; "operators"] + @@ seq_of_param @@ sc_rollup_node_operator_param) + (fun ( force, + data_dir, + rpc_addr, + rpc_port, + metrics_addr, + loser_mode, + reconnection_delay, + dal_node_endpoint, + dac_observer_endpoint, + dac_timeout, + injector_retention_period, + injector_attempts, + injection_ttl, + log_kernel_debug, + boot_sector_file ) + mode + sc_rollup_address + sc_rollup_node_operators + cctxt -> + let*? config = + Configuration.Cli.configuration_from_args + ~rpc_addr + ~rpc_port + ~metrics_addr + ~loser_mode + ~reconnection_delay + ~dal_node_endpoint + ~dac_observer_endpoint + ~dac_timeout + ~injector_retention_period + ~injector_attempts + ~injection_ttl + ~mode + ~sc_rollup_address + ~boot_sector_file + ~sc_rollup_node_operators + ~log_kernel_debug + in + let* () = Configuration.save ~force ~data_dir config in + let*! () = + cctxt#message + "Smart rollup node configuration written in %s" + (Configuration.config_filename ~data_dir) + in + return_unit) + +let legacy_run_command = + let open Tezos_clic in + let open Lwt_result_syntax in + let open Cli in + command + ~group + ~desc:"Run the rollup node daemon (deprecated)." + (args17 + data_dir_arg + mode_arg + sc_rollup_address_arg + rpc_addr_arg + rpc_port_arg + metrics_addr_arg + loser_mode_arg + reconnection_delay_arg + dal_node_endpoint_arg + dac_observer_endpoint_arg + dac_timeout_arg + injector_retention_period_arg + injector_attempts_arg + injection_ttl_arg + log_kernel_debug_arg + log_kernel_debug_file_arg + boot_sector_file_arg) + (prefixes ["run"] @@ stop) + (fun ( data_dir, + mode, + sc_rollup_address, + rpc_addr, + rpc_port, + metrics_addr, + loser_mode, + reconnection_delay, + dal_node_endpoint, + dac_observer_endpoint, + dac_timeout, + injector_retention_period, + injector_attempts, + injection_ttl, + log_kernel_debug, + log_kernel_debug_file, + boot_sector_file ) + cctxt -> + let* configuration = + Configuration.Cli.create_or_read_config + ~data_dir + ~rpc_addr + ~rpc_port + ~metrics_addr + ~loser_mode + ~reconnection_delay + ~dal_node_endpoint + ~dac_observer_endpoint + ~dac_timeout + ~injector_retention_period + ~injector_attempts + ~injection_ttl + ~mode + ~sc_rollup_address + ~boot_sector_file + ~sc_rollup_node_operators:[] + ~log_kernel_debug + in + Rollup_node_daemon.run + ~data_dir + ?log_kernel_debug_file + configuration + cctxt) + +let run_command = + let open Tezos_clic in + let open Lwt_result_syntax in + let open Cli in + command + ~group + ~desc: + "Run the rollup node daemon. Arguments overwrite values provided in the \ + configuration file." + (args15 + data_dir_arg + rpc_addr_arg + rpc_port_arg + metrics_addr_arg + loser_mode_arg + reconnection_delay_arg + dal_node_endpoint_arg + dac_observer_endpoint_arg + dac_timeout_arg + injector_retention_period_arg + injector_attempts_arg + injection_ttl_arg + log_kernel_debug_arg + log_kernel_debug_file_arg + boot_sector_file_arg) + (prefixes ["run"] @@ mode_param @@ prefixes ["for"] + @@ sc_rollup_address_param + @@ prefixes ["with"; "operators"] + @@ seq_of_param @@ sc_rollup_node_operator_param) + (fun ( data_dir, + rpc_addr, + rpc_port, + metrics_addr, + loser_mode, + reconnection_delay, + dal_node_endpoint, + dac_observer_endpoint, + dac_timeout, + injector_retention_period, + injector_attempts, + injection_ttl, + log_kernel_debug, + log_kernel_debug_file, + boot_sector_file ) + mode + sc_rollup_address + sc_rollup_node_operators + cctxt -> + let* configuration = + Configuration.Cli.create_or_read_config + ~data_dir + ~rpc_addr + ~rpc_port + ~metrics_addr + ~loser_mode + ~reconnection_delay + ~dal_node_endpoint + ~dac_observer_endpoint + ~dac_timeout + ~injector_retention_period + ~injector_attempts + ~injection_ttl + ~mode:(Some mode) + ~sc_rollup_address:(Some sc_rollup_address) + ~sc_rollup_node_operators + ~log_kernel_debug + ~boot_sector_file + in + Rollup_node_daemon.run + ~data_dir + ?log_kernel_debug_file + configuration + cctxt) + +let protocols_command = + let open Tezos_clic in + let open Lwt_result_syntax in + command + ~group + ~desc:"Shows the protocols supported by this rollup node." + no_options + (prefixes ["show"; "supported"; "protocols"] @@ stop) + (fun () (cctxt : #Client_context.full) -> + let protocols = Protocol_plugins.registered_protocols () in + let*! () = + match protocols with + | [] -> cctxt#error "No protocols supported by rollup node!" + | _ -> + cctxt#message + "@[%a@]" + (Format.pp_print_list Protocol_hash.pp) + protocols + in + return_unit) + +(** Command to dump the rollup node metrics. *) +let dump_metrics = + let open Tezos_clic in + let open Lwt_result_syntax in + command + ~group + ~desc:"dump the rollup node available metrics in CSV format." + no_options + (prefixes ["dump-metrics"] @@ stop) + (fun () (cctxt : Client_context.full) -> + let*! metrics = + Prometheus.CollectorRegistry.collect Metrics.sc_rollup_node_registry + in + let*! () = cctxt#message "%a@." Metrics.print_csv_metrics metrics in + return_unit) + +let sc_rollup_commands () = + [ + config_init_command; + run_command; + legacy_run_command; + protocols_command; + dump_metrics; + ] + +let select_commands _ctxt _ = + Lwt_result_syntax.return + (sc_rollup_commands () @ Client_helpers_commands.commands ()) + +let () = Client_main_run.run (module Daemon_config) ~select_commands diff --git a/src/bin_testnet_scenarios/evm_rollup.ml b/src/bin_testnet_scenarios/evm_rollup.ml index ac225070a578a7344a2956ed6bae1dbd1281f840..9baa5b0143c1bc370f8ac91c26de3d4b494899d7 100644 --- a/src/bin_testnet_scenarios/evm_rollup.ml +++ b/src/bin_testnet_scenarios/evm_rollup.ml @@ -30,13 +30,12 @@ let preset_preimages ~rollup_preimages_dir ~preimages_dir = let* () = Process.run "mkdir" ["-p"; preimages_dir] in Process.run "cp" ["-rT"; rollup_preimages_dir; preimages_dir] -let setup_evm_infra ~mode ~(testnet : Testnet.t) ~operator ?runner - ?preexisting_rollup ?rollup_node_name ?loser_mode node client = +let setup_evm_infra ~mode ~operator ?runner ?preexisting_rollup + ?rollup_node_name ?loser_mode node client = let rollup_node = Sc_rollup_node.create ?runner ?name:rollup_node_name - ~protocol:testnet.protocol ~base_dir:(Client.base_dir client) ~default_operator:operator.Account.alias mode @@ -112,7 +111,7 @@ let deploy_evm_rollup ~(testnet : Testnet.t) () = in let* () = check_operator_balance ~node ~client ~mode ~operator in let* _rollup_address, _rollup_node, _evm_proxy_server = - setup_evm_infra ~mode ~testnet ~operator node client + setup_evm_infra ~mode ~operator node client in stop_or_keep_going ~node diff --git a/src/bin_testnet_scenarios/evm_rollup_upgrade.ml b/src/bin_testnet_scenarios/evm_rollup_upgrade.ml index 04b55e36f206a2432f3cb44a36f6dc5bb3e2775f..9fc52b5b83d3dd42c1b45d711dfb46d9ef502868 100644 --- a/src/bin_testnet_scenarios/evm_rollup_upgrade.ml +++ b/src/bin_testnet_scenarios/evm_rollup_upgrade.ml @@ -175,7 +175,6 @@ let upgrade_kernel ~testnet () = let* smart_rollup_address, smart_rollup_node, _evm_proxy_server = Evm_rollup.setup_evm_infra ~mode - ~testnet ~operator ?preexisting_rollup:upgrade_config.smart_rollup node diff --git a/src/bin_testnet_scenarios/sc_rollup.ml b/src/bin_testnet_scenarios/sc_rollup.ml index ff22ec10782d25aabc8f77bae1ead32aafbbc8e9..38daeb06afabadbffd32ced3fecb731880d284fb 100644 --- a/src/bin_testnet_scenarios/sc_rollup.ml +++ b/src/bin_testnet_scenarios/sc_rollup.ml @@ -40,15 +40,13 @@ let originate_new_rollup ?(alias = "rollup") Log.info "Rollup %s originated" rollup ; return rollup -let setup_l2_node ~(testnet : Testnet.t) ?runner ?name ?loser_mode ~operator - client node rollup = +let setup_l2_node ?runner ?name ?loser_mode ~operator client node rollup = let rollup_node = Sc_rollup_node.create ?runner ?name ~base_dir:(Client.base_dir client) ~default_operator:operator - ~protocol:testnet.protocol Operator node in @@ -114,14 +112,12 @@ let rejection_with_proof ~(testnet : Testnet.t) () = Lwt.all [ setup_l2_node - ~testnet ~name:"honest-node" ~operator:honest_operator.alias client node rollup_address; setup_l2_node - ~testnet ~name:"dishonest-node" ~loser_mode:Format.(sprintf "%d 0 0" fault_level) ~operator:dishonest_operator.alias diff --git a/src/lib_scoru_sequencer/dune b/src/lib_scoru_sequencer/dune index cddccfa20fbf2511a14c58843c87c030043a387d..05361fbd177d17d3890bf150091143af68f190ea 100644 --- a/src/lib_scoru_sequencer/dune +++ b/src/lib_scoru_sequencer/dune @@ -11,7 +11,7 @@ tezos-protocol-alpha octez_smart_rollup_node_alpha tezos-workers - octez-smart-rollup-node + octez-smart-rollup-node-lib octez-smart-rollup tezos-rpc tezos-rpc-http diff --git a/src/lib_smart_rollup_node/daemon_event.ml b/src/lib_smart_rollup_node/daemon_event.ml index eb97aef08e520a40f3e58d20717e49581118af12..66fbe0e2489e8c8fb44586d5eb552390d7dace12 100644 --- a/src/lib_smart_rollup_node/daemon_event.ml +++ b/src/lib_smart_rollup_node/daemon_event.ml @@ -106,6 +106,23 @@ module Simple = struct | None -> Format.pp_print_string ppf "none" | Some e -> Error_monad.pp_print_trace ppf e) + let migration = + declare_5 + ~name:"sc_rollup_daemon_protocol_migration" + ~msg: + "{catching_up} from {old_protocol} ({old_protocol_level}) to \ + {new_protocol} ({new_protocol_level}) " + ~level:Notice + ("catching_up", Data_encoding.bool) + ("old_protocol", Protocol_hash.encoding) + ("old_protocol_level", Data_encoding.int31) + ("new_protocol", Protocol_hash.encoding) + ("new_protocol_level", Data_encoding.int31) + ~pp1:(fun ppf catching_up -> + Format.pp_print_string + ppf + (if catching_up then "Catching up on migration" else "Migration")) + let error = declare_1 ~section @@ -154,6 +171,15 @@ let included_operation ?errors status operation = | `Failed | `Backtracked | `Skipped -> Simple.(emit included_failed_operation) (operation, status, errors) +let migration ~catching_up (old_protocol, old_protocol_level) + (new_protocol, new_protocol_level) = + Simple.(emit migration) + ( catching_up, + old_protocol, + old_protocol_level, + new_protocol, + new_protocol_level ) + let error e = Simple.(emit error) e let degraded_mode () = Simple.(emit degraded_mode) () diff --git a/src/lib_smart_rollup_node/daemon_event.mli b/src/lib_smart_rollup_node/daemon_event.mli index 6598f34a86be59327ff5a29b8f3eca79a8557eaa..a5beb5c9feb2cc2066eee69af2f3e483daffa497 100644 --- a/src/lib_smart_rollup_node/daemon_event.mli +++ b/src/lib_smart_rollup_node/daemon_event.mli @@ -53,6 +53,15 @@ val included_operation : L1_operation.t -> unit Lwt.t +(** [migration ~catching_up (old_protocol, old_protocol_level) (new_protocol, + new_protocol_level)] emits and event for when the rollup node detects and + handles a protocol migration. *) +val migration : + catching_up:bool -> + Protocol_hash.t * int -> + Protocol_hash.t * int -> + unit Lwt.t + (** Emit a fatal error for the daemon. *) val error : tztrace -> unit Lwt.t diff --git a/src/lib_smart_rollup_node/dune b/src/lib_smart_rollup_node/dune index d57b6e625ece68011e0c39ffd2028eb47ed2de0b..4940f27ce14ac56ff8837f3ab2cdc9967f57a6c4 100644 --- a/src/lib_smart_rollup_node/dune +++ b/src/lib_smart_rollup_node/dune @@ -3,7 +3,7 @@ (library (name octez_smart_rollup_node) - (public_name octez-smart-rollup-node) + (public_name octez-smart-rollup-node-lib) (instrumentation (backend bisect_ppx)) (libraries tezos-base diff --git a/src/lib_smart_rollup_node/layer1.ml b/src/lib_smart_rollup_node/layer1.ml index 7764afd7b36758accba01d3c4dcb378034d597f3..7727c1f5b1150fe68bae21b9c001f68d5ad74061 100644 --- a/src/lib_smart_rollup_node/layer1.ml +++ b/src/lib_smart_rollup_node/layer1.ml @@ -207,38 +207,64 @@ let fetch_tezos_shell_header {cctxt; headers_cache; _} hash = | None, Some errs -> Error errs | Some shell_header, _ -> Ok shell_header +let fetch_block_no_cache (fetch : fetch_block_rpc) extract_header + ({cctxt; blocks_cache; _} as l1_ctxt) hash = + let open Lwt_result_syntax in + trace (Cannot_find_block hash) + @@ let* block = + fetch cctxt ~chain:`Main ~block:(`Hash (hash, 0)) ~metadata:`Always () + in + Blocks_cache.put blocks_cache hash (Lwt.return_some block) ; + cache_shell_header l1_ctxt hash (extract_header block) ; + return block + (** [fetch_tezos_block cctxt fetch extract_header 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 (fetch : fetch_block_rpc) extract_header +let fetch_tezos_block (fetch_rpc : fetch_block_rpc) extract_header ({cctxt; blocks_cache; _} as l1_ctxt) hash = - let open Lwt_syntax in trace (Cannot_find_block hash) @@ let errors = ref None in let fetch hash = + let open Lwt_syntax in let* block = - fetch cctxt ~chain:`Main ~block:(`Hash (hash, 0)) ~metadata:`Always () + fetch_rpc cctxt ~chain:`Main ~block:(`Hash (hash, 0)) ~metadata:`Always () in match block with | Error errs -> errors := Some errs ; return_none - | Ok block -> return_some block + | Ok block -> + cache_shell_header l1_ctxt hash (extract_header block) ; + return_some block in - let+ block = Blocks_cache.bind_or_put blocks_cache hash fetch Lwt.return in + let open Lwt_result_syntax 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, i.e. calls {!fetch} for cache misses. *) - error_with - "Fetching Tezos block %a failed unexpectedly" - Block_hash.pp - hash - | None, Some errs -> Error errs + failwith "Fetching Tezos block %a failed unexpectedly" Block_hash.pp hash + | None, Some errs -> Lwt.return_error errs | Some block, _ -> - cache_shell_header l1_ctxt hash (extract_header block) ; - Ok block + let is_of_expected_protocol = + try + let (_ : Block_header.shell_header) = extract_header block in + true + with _ -> false + in + if is_of_expected_protocol then return block + else + (* It is possible for a value stored in the cache to have been parsed + with the wrong protocol code because: + 1. There is no protocol information in binary blocs + 2. We pre-fetch blocks eagerly. + + If we realize a posteriori that the bloc we cached is for another + protocol then we overwrite it with the correctly parsed one. + *) + fetch_block_no_cache fetch_rpc extract_header l1_ctxt hash let make_prefetching_schedule {prefetch_blocks; _} blocks = let blocks_with_prefetching, _, first_prefetch = diff --git a/src/lib_smart_rollup_node/node_context.ml b/src/lib_smart_rollup_node/node_context.ml index 2e34b972c4d6bf323d508aa50b2ad08b5526be6a..477c124b2690ca1b1acd7583777096f80a5d69a6 100644 --- a/src/lib_smart_rollup_node/node_context.ml +++ b/src/lib_smart_rollup_node/node_context.ml @@ -32,6 +32,12 @@ type 'a store = 'a Store.t type debug_logger = string -> unit Lwt.t +type current_protocol = { + hash : Protocol_hash.t; + proto_level : int; + constants : Rollup_constants.protocol_constants; +} + type 'a t = { cctxt : Client_context.full; dal_cctxt : Dal_node_client.cctxt option; @@ -47,8 +53,6 @@ type 'a t = { block_finality_time : int; kind : Kind.t; fee_parameters : Configuration.fee_parameters; - protocol_constants : Rollup_constants.protocol_constants; - proto_level : int; loser_mode : Loser_mode.t; lockfile : Lwt_unix.file_descr; store : 'a store; @@ -57,6 +61,7 @@ type 'a t = { lpc : ('a, Commitment.t option) Reference.t; kernel_debug_logger : debug_logger; finaliser : unit -> unit Lwt.t; + mutable current_protocol : current_protocol; } type rw = [`Read | `Write] t @@ -124,8 +129,7 @@ let make_kernel_logger ?log_kernel_debug_file data_dir = Lwt_io.of_fd ~close:(fun () -> Lwt_unix.close fd) ~mode:Lwt_io.Output fd let init (cctxt : #Client_context.full) ~data_dir ?log_kernel_debug_file mode - l1_ctxt (protocol_constants : Rollup_constants.protocol_constants) - genesis_info ~lcc ~lpc kind ~proto_level + l1_ctxt genesis_info ~lcc ~lpc kind current_protocol Configuration.( { sc_rollup_address = rollup_address; @@ -159,7 +163,7 @@ let init (cctxt : #Client_context.full) ~data_dir ?log_kernel_debug_file mode let* () = Context.Rollup.check_or_set_address mode context rollup_address in let*! () = Event.rollup_exists ~addr:rollup_address ~kind in let*! () = - if dal_cctxt = None && protocol_constants.dal.feature_enable then + if dal_cctxt = None && current_protocol.constants.dal.feature_enable then Event.warn_dal_enabled_no_node () else Lwt.return_unit in @@ -206,14 +210,13 @@ let init (cctxt : #Client_context.full) ~data_dir ?log_kernel_debug_file mode injector_retention_period = 0; block_finality_time = 2; fee_parameters; - protocol_constants; - proto_level; loser_mode; lockfile; store; context; kernel_debug_logger; finaliser = kernel_debug_finaliser; + current_protocol; } let close ({cctxt; store; context; l1_ctxt; finaliser; _} as node_ctxt) = @@ -252,7 +255,8 @@ let checkout_context node_ctxt block_hash = | Some ctxt -> return ctxt let dal_supported node_ctxt = - node_ctxt.dal_cctxt <> None && node_ctxt.protocol_constants.dal.feature_enable + node_ctxt.dal_cctxt <> None + && node_ctxt.current_protocol.constants.dal.feature_enable let readonly (node_ctxt : _ t) = { @@ -922,7 +926,7 @@ let save_confirmed_slots_histories {store; _} block hist = Store.Dal_confirmed_slots_histories.add store.irmin_store block hist module Internal_for_tests = struct - let create_node_context cctxt protocol_constants ~data_dir kind = + let create_node_context cctxt current_protocol ~data_dir kind = let open Lwt_result_syntax in let l2_blocks_cache_size = Configuration.default_l2_blocks_cache_size in let* lockfile = lock ~data_dir in @@ -957,8 +961,7 @@ module Internal_for_tests = struct injector_retention_period = 0; block_finality_time = 2; fee_parameters = Configuration.default_fee_parameters; - protocol_constants; - proto_level = 0; + current_protocol; loser_mode = Loser_mode.no_failures; lockfile; store; diff --git a/src/lib_smart_rollup_node/node_context.mli b/src/lib_smart_rollup_node/node_context.mli index 0b4fcc19c5a60ef61240276eb8761a1bd2b1d942..7554562347b94757b396a3e81cf46b32b279e769 100644 --- a/src/lib_smart_rollup_node/node_context.mli +++ b/src/lib_smart_rollup_node/node_context.mli @@ -35,6 +35,15 @@ type 'a store constraint 'a = [< `Read | `Write > `Read] type debug_logger = string -> unit Lwt.t +type current_protocol = { + hash : Protocol_hash.t; (** Hash of the current protocol. *) + proto_level : int; + (** Protocol supported by this rollup node (represented as a protocol + level). *) + constants : Rollup_constants.protocol_constants; + (** Protocol constants retrieved from the Tezos node. *) +} + type 'a t = { cctxt : Client_context.full; (** Client context used by the rollup node. *) dal_cctxt : Dal_node_client.cctxt option; @@ -63,11 +72,6 @@ type 'a t = { kind : Kind.t; (** Kind of the smart rollup. *) fee_parameters : Configuration.fee_parameters; (** Fee parameters to use when injecting operations in layer 1. *) - protocol_constants : Rollup_constants.protocol_constants; - (** Protocol constants retrieved from the Tezos node. *) - proto_level : int; - (** Protocol supported by this rollup node (represented as a protocol - level). *) loser_mode : Loser_mode.t; (** If different from [Loser_mode.no_failures], the rollup node issues wrong commitments (for tests). *) @@ -84,6 +88,9 @@ type 'a t = { (** Logger used for writing [kernel_debug] messages *) finaliser : unit -> unit Lwt.t; (** Aggregation of finalisers to run when the node context closes *) + mutable current_protocol : current_protocol; + (** Information about the current protocol. This value is changed in place + on protocol upgrades. *) } (** Read/write node context {!t}. *) @@ -118,10 +125,10 @@ val is_loser : _ t -> bool val get_fee_parameter : _ t -> Configuration.purpose -> Injector_sigs.fee_parameter -(** [init cctxt ~data_dir mode l1_ctxt constants genesis_info ~proto_level - configuration] initializes the rollup representation. The rollup origination - level and kind are fetched via an RPC call to the layer1 node that [cctxt] - uses for RPC requests. +(** [init cctxt ~data_dir mode l1_ctxt genesis_info protocol configuration] + initializes the rollup representation. The rollup origination level and kind + are fetched via an RPC call to the layer1 node that [cctxt] uses for RPC + requests. *) val init : #Client_context.full -> @@ -129,12 +136,11 @@ val init : ?log_kernel_debug_file:string -> 'a Store_sigs.mode -> Layer1.t -> - Rollup_constants.protocol_constants -> genesis_info -> lcc:lcc -> lpc:Commitment.t option -> Kind.t -> - proto_level:int -> + current_protocol -> Configuration.t -> 'a t tzresult Lwt.t @@ -469,7 +475,7 @@ module Internal_for_tests : sig rollup node functions. *) val create_node_context : #Client_context.full -> - Rollup_constants.protocol_constants -> + current_protocol -> data_dir:string -> Kind.t -> Store_sigs.rw t tzresult Lwt.t diff --git a/src/lib_smart_rollup_node/protocol_plugin_sig.ml b/src/lib_smart_rollup_node/protocol_plugin_sig.ml new file mode 100644 index 0000000000000000000000000000000000000000..9ae5a2329d6073db4cd69266e264254ba9cd7c7a --- /dev/null +++ b/src/lib_smart_rollup_node/protocol_plugin_sig.ml @@ -0,0 +1,273 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +(** Protocol specific RPC directory. *) +module type RPC_DIRECTORY = sig + (** The RPC directory for the rollup node of this protocol *) + val directory : Node_context.rw -> unit Tezos_rpc.Directory.t +end + +(** Protocol specific functions to track endorsed DAL slots of L1 blocks. *) +module type DAL_SLOTS_TRACKER = sig + (** [process_head node_ctxt head] performs the following operations for a + given protocol: + {ul + {li it reads the endorsements for headers published attestation_lag + levels preceding [head] from the block metadata, determines which + ones the rollup node will download, and stores the results in + [Store.Dal_confirmed_slots].} + } *) + val process_head : Node_context.rw -> Layer1.head -> unit tzresult Lwt.t +end + +(** Protocol specific functions to reconstruct inboxes from L1 blocks. *) +module type INBOX = sig + (** [process_head node_ctxt ~predecessor head] changes the state of + the inbox on disk to react to the operations contained in the block [head] + (where [predecessor] is the predecessor of [head] in the L1 chain). It + returns a tuple [(inbox_hash, inbox, payload_hash, messages)] where + [inbox] is the new inbox and [inbox_hash] its hash, [payload_hash] is the + hash of the merkelized payload for this inbox and [messages] are the + serialized messages present in the block (with the internal messages added + by the protocol). *) + val process_head : + Node_context.rw -> + predecessor:Layer1.header -> + Layer1.header -> + (Octez_smart_rollup.Inbox.Hash.t + * Octez_smart_rollup.Inbox.t + * Merkelized_payload_hashes_hash.t + * string list) + tzresult + Lwt.t + + (** [same_as_layer_1 node_ctxt block node_inbox] ensures that the rollup node + agrees with the L1 node that inbox for [block] is [node_inbox]. *) + val same_as_layer_1 : + _ Node_context.t -> + Block_hash.t -> + Octez_smart_rollup.Inbox.t -> + unit tzresult Lwt.t + + (**/**) + + module Internal_for_tests : sig + (** Only for tests. [process_messages node_ctxt ~is_first_block ~predecessor + head messages] reconstructs the inbox on disk for the [messages] as if + they appeared in [head]. See {!val:process_head} for the return + values. *) + val process_messages : + Node_context.rw -> + is_first_block:bool -> + predecessor:Layer1.header -> + Layer1.header -> + string list -> + (Octez_smart_rollup.Inbox.Hash.t + * Octez_smart_rollup.Inbox.t + * Merkelized_payload_hashes_hash.t + * string list) + tzresult + Lwt.t + end +end + +(** Protocol specific functions to interpret inbox and messages of L1 + blocks. *) +module type INTERPRETER = sig + (** [process_head node_ctxt ctxt ~predecessor head (inbox, messages)] interprets + the [messages] associated with a [head] (where [predecessor] is the + predecessor of [head] in the L1 chain). This requires the [inbox] to be + updated beforehand. It returns [(ctxt, num_messages, num_ticks, tick)] + where [ctxt] is the updated layer 2 context (with the new PVM state), + [num_messages] is the number of [messages], [num_ticks] is the number of + ticks taken by the PVM for the evaluation and [tick] is the tick reached + by the PVM after the evaluation. *) + val process_head : + Node_context.rw -> + 'a Context.t -> + predecessor:Layer1.header -> + Layer1.header -> + Octez_smart_rollup.Inbox.t * string trace -> + ('a Context.t * int * int64 * Z.t) tzresult Lwt.t + + (** [state_of_head node_ctxt ctxt head] returns the state corresponding to the + block [head], or the state at rollup genesis if the block is before the + rollup origination. *) + val state_of_head : + 'a Node_context.t -> + 'a Context.t -> + Layer1.head -> + ('a Context.t * Context.tree) tzresult Lwt.t +end + +(** Protocol specific commitments publisher. NOTE: The publisher has to be + stopped and the new one restarted on protocol change. *) +module type PUBLISHER = sig + (** [process_head node_ctxt ~predecessor head ctxt] builds a new commitment if + needed, by looking at the level of [head] and checking whether it is a + multiple of `Commitment.sc_rollup_commitment_period` levels away from + [node_ctxt.initial_level]. It uses the functionalities of [PVM] to compute + the hash to be included in the commitment. *) + val process_head : + Node_context.rw -> + predecessor:Block_hash.t -> + Layer1.header -> + Context.rw -> + Commitment.Hash.t option tzresult Lwt.t + + (** [init node_ctxt] initializes the worker for publishing and cementing + commitments. *) + val init : _ Node_context.t -> unit tzresult Lwt.t + + (** [publish_commitments ()] publishes the commitments that were not + yet published up to the finalized head and which are after the last + cemented commitment. *) + val publish_commitments : unit -> unit tzresult Lwt.t + + (** [cement_commitments ()] cements the commitments that can be + cemented, i.e. the commitments that are after the current last cemented + commitment and which have [sc_rollup_challenge_period] levels on top of + them since they were originally published. *) + val cement_commitments : unit -> unit tzresult Lwt.t + + (** [shutdown ()] stops the worker for publishing and cementing + commitments. *) + val shutdown : unit -> unit Lwt.t +end + +(** Protocol specific refutation coordinator. NOTE: The refutation coordinator + has to be stopped and the new one restarted on protocol change. *) +module type REFUTATION_COORDINATOR = sig + (** [init node_ctxt] initializes the refutation coordinator worker. *) + val init : Node_context.rw -> unit tzresult Lwt.t + + (** [process head] processes a new l1 head. This means that the coordinator + will: + {ol + {li Gather all existing conflicts} + {li Launch new refutation players for each conflict concerning + the operator that doesn't have a player in this node} + {li Kill all players whose conflict has disappeared from L1} + {li Make all players play a step in the refutation} + } + *) + val process : Layer1.head -> unit tzresult Lwt.t + + (** [shutdown ()] stops the refutation coordinator. *) + val shutdown : unit -> unit Lwt.t +end + +(** Protocol specific batcher. NOTE: The batcher has to be stopped and the new + one restarted on protocol change. *) +module type BATCHER = sig + (** [init config ~signer node_ctxt] initializes and starts the batcher for + [signer]. If [config.simulation] is [true] (the default), messages added + to the batcher are simulated in an incremental simulation context. *) + val init : + Configuration.batcher -> + signer:Signature.public_key_hash -> + _ Node_context.t -> + unit tzresult Lwt.t + + (** [new_head head] create batches of L2 messages from the queue and pack each + batch in an L1 operation. The L1 operations (i.e. L2 batches) are queued + in the injector for injection on the Tezos node. *) + val new_head : Layer1.head -> unit tzresult Lwt.t + + (** [shutdown ()] stops the batcher, waiting for the ongoing request to be + processed. *) + val shutdown : unit -> unit Lwt.t +end + +(** Protocol specific functions to interact with the L1 node. *) +module type LAYER1_HELPERS = sig + (** [prefetch_tezos_blocks l1_ctxt blocks] prefetches the blocks + asynchronously. NOTE: the number of blocks to prefetch must not be greater + than the size of the blocks cache otherwise they will be lost. *) + val prefetch_tezos_blocks : Layer1.t -> Layer1.head list -> unit + + val get_last_cemented_commitment : + #Client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t + + val get_last_published_commitment : + #Client_context.full -> + Address.t -> + Signature.public_key_hash -> + Commitment.t option tzresult Lwt.t + + val get_kind : #Client_context.full -> Address.t -> Kind.t tzresult Lwt.t + + val genesis_inbox : + #Client_context.full -> + genesis_level:int32 -> + Octez_smart_rollup.Inbox.t tzresult Lwt.t + + (** Retrieve protocol agnotic constants for the head of the chain. *) + val retrieve_constants : + ?block:Tezos_shell_services.Block_services.block -> + #Client_context.full -> + Rollup_constants.protocol_constants tzresult Lwt.t + + val retrieve_genesis_info : + #Client_context.full -> + Address.t -> + Node_context.genesis_info tzresult Lwt.t +end + +(** Protocol specific functions for processing L1 blocks. *) +module type L1_PROCESSING = sig + (** Ensure that the initial state hash of the PVM as defined by the rollup + node matches the one of the PVM on the L1 node. *) + val check_pvm_initial_state_hash : _ Node_context.t -> unit tzresult Lwt.t + + (** React to L1 operations included in a block of the chain. *) + val process_l1_block_operations : + Node_context.rw -> Layer1.header -> unit tzresult Lwt.t +end + +(** Signature of protocol plugins for the rollup node. NOTE: the plugins have to + be registered to be made available to the rollup node. *) +module type S = sig + (** The protocol for which this plugin is. *) + val protocol : Protocol_hash.t + + module RPC_directory : RPC_DIRECTORY + + module Dal_slots_tracker : DAL_SLOTS_TRACKER + + module Inbox : INBOX + + module Interpreter : INTERPRETER + + module Publisher : PUBLISHER + + module Refutation_coordinator : REFUTATION_COORDINATOR + + module Batcher : BATCHER + + module Layer1_helpers : LAYER1_HELPERS + + module L1_processing : L1_PROCESSING +end diff --git a/src/lib_smart_rollup_node/protocol_plugins.ml b/src/lib_smart_rollup_node/protocol_plugins.ml new file mode 100644 index 0000000000000000000000000000000000000000..01571759461c06f527ab4b642bf938e502853333 --- /dev/null +++ b/src/lib_smart_rollup_node/protocol_plugins.ml @@ -0,0 +1,65 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 error += Unsupported_protocol of Protocol_hash.t + +let () = + register_error_kind + ~id:"smart_rollup.node.unsupported_protocol" + ~title:"Protocol not supported by rollup node" + ~description:"Protocol not supported by rollup node." + ~pp:(fun ppf proto -> + Format.fprintf + ppf + "Protocol %a is not supported by the rollup node." + Protocol_hash.pp + proto) + `Permanent + Data_encoding.(obj1 (req "protocol" Protocol_hash.encoding)) + (function Unsupported_protocol p -> Some p | _ -> None) + (fun p -> Unsupported_protocol p) + +type proto_plugin = (module Protocol_plugin_sig.S) + +let proto_plugins : proto_plugin Protocol_hash.Table.t = + Protocol_hash.Table.create 7 + +let register (plugin : proto_plugin) = + let module Plugin = (val plugin) in + if Protocol_hash.Table.mem proto_plugins Plugin.protocol then + Format.kasprintf + invalid_arg + "The rollup node protocol plugin for protocol %a is already registered. \ + Did you register it manually multiple times?" + Protocol_hash.pp + Plugin.protocol ; + Protocol_hash.Table.add proto_plugins Plugin.protocol plugin + +let proto_plugin_for_protocol protocol = + Protocol_hash.Table.find proto_plugins protocol + |> Option.to_result ~none:[Unsupported_protocol protocol] + +let registered_protocols () = + Protocol_hash.Table.to_seq_keys proto_plugins |> List.of_seq diff --git a/src/lib_smart_rollup_node/protocol_plugins.mli b/src/lib_smart_rollup_node/protocol_plugins.mli new file mode 100644 index 0000000000000000000000000000000000000000..8da839659113af836e8ddcd51f3edfd6382d131c --- /dev/null +++ b/src/lib_smart_rollup_node/protocol_plugins.mli @@ -0,0 +1,37 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 proto_plugin = (module Protocol_plugin_sig.S) + +(** Register a protocol plugin for a specific protocol to be used by the + rollup node. *) +val register : proto_plugin -> unit + +(** Return the protocol plugin for a given protocol (or an error if not + supported). *) +val proto_plugin_for_protocol : Protocol_hash.t -> proto_plugin tzresult + +(** Returns the list of registered protocols. *) +val registered_protocols : unit -> Protocol_hash.t list diff --git a/src/lib_smart_rollup_node/rollup_node_daemon.ml b/src/lib_smart_rollup_node/rollup_node_daemon.ml new file mode 100644 index 0000000000000000000000000000000000000000..fe5e4e2b5ea97a3d434f0a57a4129d64803bced8 --- /dev/null +++ b/src/lib_smart_rollup_node/rollup_node_daemon.ml @@ -0,0 +1,585 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 state = { + mutable plugin : (module Protocol_plugin_sig.S); + mutable rpc_server : Rpc_server.t; + configuration : Configuration.t; + node_ctxt : Node_context.rw; +} + +let protocol_info cctxt protocol block_hash = + let open Result_syntax in + let+ plugin = Protocol_plugins.proto_plugin_for_protocol protocol in + let module Plugin = (val plugin) in + let constants = + Plugin.Layer1_helpers.retrieve_constants + ~block:(`Hash (block_hash, 0)) + cctxt + in + (constants, plugin) + +let is_before_origination (node_ctxt : _ Node_context.t) + (header : Layer1.header) = + let origination_level = node_ctxt.genesis_info.level in + header.level < origination_level + +let previous_context (node_ctxt : _ Node_context.t) + ~(predecessor : Layer1.header) = + let open Lwt_result_syntax in + if is_before_origination node_ctxt predecessor then + (* This is before we have interpreted the boot sector, so we start + with an empty context in genesis *) + return (Context.empty node_ctxt.context) + else Node_context.checkout_context node_ctxt predecessor.Layer1.hash + +let start_workers ?(degraded = false) (configuration : Configuration.t) + (module Plugin : Protocol_plugin_sig.S) (node_ctxt : _ Node_context.t) = + let open Lwt_result_syntax in + let* () = + unless degraded @@ fun () -> + let* () = Plugin.Publisher.init node_ctxt in + match + Configuration.Operator_purpose_map.find Add_messages node_ctxt.operators + with + | None -> return_unit + | Some signer -> Plugin.Batcher.init configuration.batcher ~signer node_ctxt + in + let* () = Plugin.Refutation_coordinator.init node_ctxt in + return_unit + +let handle_protocol_migration ?degraded ~catching_up state + (head : Layer1.header) = + let open Lwt_result_syntax in + let* head_proto = Node_context.protocol_of_level state.node_ctxt head.level in + let new_protocol = head_proto.protocol in + when_ Protocol_hash.(new_protocol <> state.node_ctxt.current_protocol.hash) + @@ fun () -> + let*! () = + Daemon_event.migration + ~catching_up + ( state.node_ctxt.current_protocol.hash, + state.node_ctxt.current_protocol.proto_level ) + (new_protocol, head_proto.proto_level) + in + let*? constants, new_plugin = + protocol_info state.node_ctxt.cctxt new_protocol head.hash + in + let module New_plugin = (val new_plugin) in + let* rpc_server = + Rpc_server.change_directory + state.rpc_server + (New_plugin.RPC_directory.directory state.node_ctxt) + and* constants in + let* () = + start_workers ?degraded state.configuration new_plugin state.node_ctxt + in + let new_protocol = + { + Node_context.hash = new_protocol; + proto_level = head_proto.proto_level; + constants; + } + in + state.plugin <- new_plugin ; + state.rpc_server <- rpc_server ; + state.node_ctxt.current_protocol <- new_protocol ; + return_unit + +(* Process a L1 that we have never seen and for which we have processed the + predecessor. *) +let process_new_head ({node_ctxt; _} as state) ~catching_up ~predecessor + (head : Layer1.header) = + let open Lwt_result_syntax in + let* () = + Node_context.save_level + node_ctxt + {Layer1.hash = head.hash; level = head.level} + and* () = Node_context.save_protocol_info node_ctxt head ~predecessor in + let* () = handle_protocol_migration ~catching_up state head in + let* rollup_ctxt = previous_context node_ctxt ~predecessor in + let module Plugin = (val state.plugin) in + let* inbox_hash, inbox, inbox_witness, messages = + Plugin.Inbox.process_head node_ctxt ~predecessor head + in + let* () = + when_ (Node_context.dal_supported node_ctxt) @@ fun () -> + Plugin.Dal_slots_tracker.process_head node_ctxt (Layer1.head_of_header head) + in + let* () = Plugin.L1_processing.process_l1_block_operations node_ctxt head in + (* Avoid storing and publishing commitments if the head is not final. *) + (* Avoid triggering the pvm execution if this has been done before for + this head. *) + let* ctxt, _num_messages, num_ticks, initial_tick = + Plugin.Interpreter.process_head + node_ctxt + rollup_ctxt + ~predecessor + head + (inbox, messages) + in + let*! context_hash = Context.commit ctxt in + let* commitment_hash = + Plugin.Publisher.process_head + node_ctxt + ~predecessor:predecessor.hash + head + ctxt + in + let* () = + unless (catching_up && Option.is_none commitment_hash) @@ fun () -> + Plugin.Inbox.same_as_layer_1 node_ctxt head.hash inbox + in + let level = head.level in + let* previous_commitment_hash = + if level = node_ctxt.genesis_info.level then + (* Previous commitment for rollup genesis is itself. *) + return node_ctxt.genesis_info.commitment_hash + else + let+ pred = Node_context.get_l2_block node_ctxt predecessor.hash in + Sc_rollup_block.most_recent_commitment pred.header + in + let header = + Sc_rollup_block. + { + block_hash = head.hash; + level; + predecessor = predecessor.hash; + commitment_hash; + previous_commitment_hash; + context = context_hash; + inbox_witness; + inbox_hash; + } + in + let l2_block = + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} + in + let* () = + Node_context.mark_finalized_level + node_ctxt + Int32.(sub head.level (of_int node_ctxt.block_finality_time)) + in + let* () = Node_context.save_l2_head node_ctxt l2_block in + return_unit + +let rec process_head ({node_ctxt; _} as state) ~catching_up + (head : Layer1.header) = + let open Lwt_result_syntax in + let start_timestamp = Time.System.now () in + let* already_processed = Node_context.is_processed node_ctxt head.hash in + unless (already_processed || is_before_origination node_ctxt head) + @@ fun () -> + let*! () = Daemon_event.head_processing head.hash head.level in + let* predecessor = Node_context.get_predecessor_header_opt node_ctxt head in + match predecessor with + | None -> + (* Predecessor not available on the L1, which means the block does not + exist in the chain. *) + return_unit + | Some predecessor -> + let* () = process_head state ~catching_up:true predecessor in + let* () = process_new_head state ~catching_up ~predecessor head in + let stop_timestamp = Time.System.now () in + let process_time = Ptime.diff stop_timestamp start_timestamp in + Metrics.Inbox.set_process_time process_time ; + let*! () = + Daemon_event.new_head_processed head.hash head.level process_time + in + return_unit + +(* [on_layer_1_head node_ctxt head] processes a new head from the L1. It + also processes any missing blocks that were not processed. *) +let on_layer_1_head ({node_ctxt; _} as state) (head : Layer1.header) = + let open Lwt_result_syntax in + let* old_head = Node_context.last_processed_head_opt node_ctxt in + let old_head = + match old_head with + | Some h -> + `Head Layer1.{hash = h.header.block_hash; level = h.header.level} + | None -> + (* if no head has been processed yet, we want to handle all blocks + since, and including, the rollup origination. *) + let origination_level = node_ctxt.genesis_info.level in + `Level (Int32.pred origination_level) + in + let stripped_head = Layer1.head_of_header head in + let*! reorg = + Node_context.get_tezos_reorg_for_new_head node_ctxt old_head stripped_head + in + let*? reorg = + match reorg with + | Error trace + when TzTrace.fold + (fun yes error -> + yes + || + match error with + | Octez_crawler.Layer_1.Cannot_find_predecessor _ -> true + | _ -> false) + false + trace -> + (* The reorganization could not be computed entirely because of missing + info on the Layer 1. We fallback to a recursive process_head. *) + Ok {Reorg.no_reorg with new_chain = [stripped_head]} + | _ -> reorg + in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/3348 + Rollback state information on reorganization, i.e. for + reorg.old_chain. *) + let*! () = Daemon_event.processing_heads_iteration reorg.new_chain in + let get_header Layer1.{hash; level} = + if Block_hash.equal hash head.hash then return head + else + let+ header = Layer1.fetch_tezos_shell_header node_ctxt.l1_ctxt hash in + {Layer1.hash; level; header} + in + let new_chain_prefetching = + Layer1.make_prefetching_schedule node_ctxt.l1_ctxt reorg.new_chain + in + let* () = + List.iter_es + (fun (block, to_prefetch) -> + let module Plugin = (val state.plugin) in + Plugin.Layer1_helpers.prefetch_tezos_blocks + node_ctxt.l1_ctxt + to_prefetch ; + let* header = get_header block in + let catching_up = block.level < head.level in + process_head state ~catching_up header) + new_chain_prefetching + in + let module Plugin = (val state.plugin) in + let* () = Plugin.Publisher.publish_commitments () in + let* () = Plugin.Publisher.cement_commitments () in + let*! () = Daemon_event.new_heads_processed reorg.new_chain in + let* () = Plugin.Refutation_coordinator.process stripped_head in + let* () = Plugin.Batcher.new_head stripped_head in + let*! () = Injector.inject ~header:head.header () in + return_unit + +let daemonize state = + Layer1.iter_heads state.node_ctxt.l1_ctxt (on_layer_1_head state) + +let degraded_refutation_mode state = + let open Lwt_result_syntax in + let*! () = Daemon_event.degraded_mode () in + let message = state.node_ctxt.Node_context.cctxt#message in + let module Plugin = (val state.plugin) in + let*! () = message "Shutting down Batcher@." in + let*! () = Plugin.Batcher.shutdown () in + let*! () = message "Shutting down Commitment Publisher@." in + let*! () = Plugin.Publisher.shutdown () in + Layer1.iter_heads state.node_ctxt.l1_ctxt @@ fun head -> + let* () = + handle_protocol_migration ~degraded:true ~catching_up:false state head + in + let module Plugin = (val state.plugin) in + let* () = + Plugin.Refutation_coordinator.process (Layer1.head_of_header head) + in + let*! () = Injector.inject () in + return_unit + +let install_finalizer state = + let open Lwt_syntax in + Lwt_exit.register_clean_up_callback ~loc:__LOC__ @@ fun exit_status -> + let message = state.node_ctxt.Node_context.cctxt#message in + let module Plugin = (val state.plugin) in + let* () = message "Shutting down RPC server@." in + let* () = Rpc_server.shutdown state.rpc_server in + let* () = message "Shutting down Injector@." in + let* () = Injector.shutdown () in + let* () = message "Shutting down Batcher@." in + let* () = Plugin.Batcher.shutdown () in + let* () = message "Shutting down Commitment Publisher@." in + let* () = Plugin.Publisher.shutdown () in + let* () = message "Shutting down Refutation Coordinator@." in + let* () = Plugin.Refutation_coordinator.shutdown () in + let* (_ : unit tzresult) = Node_context.close state.node_ctxt in + let* () = Event.shutdown_node exit_status in + Tezos_base_unix.Internal_event_unix.close () + +let run ({node_ctxt; configuration; plugin; _} as state) = + let open Lwt_result_syntax in + let module Plugin = (val state.plugin) in + let start () = + let signers = + Configuration.Operator_purpose_map.bindings node_ctxt.operators + |> List.fold_left + (fun acc (purpose, operator) -> + let purposes = + match Signature.Public_key_hash.Map.find operator acc with + | None -> [purpose] + | Some ps -> purpose :: ps + in + Signature.Public_key_hash.Map.add operator purposes acc) + Signature.Public_key_hash.Map.empty + |> Signature.Public_key_hash.Map.bindings + |> List.map (fun (operator, purposes) -> + let strategy = + match purposes with + | [Configuration.Add_messages] -> `Delay_block 0.5 + | _ -> `Each_block + in + (operator, strategy, purposes)) + in + let* () = + unless (signers = []) @@ fun () -> + Injector.init + node_ctxt.cctxt + { + cctxt = (node_ctxt.cctxt :> Client_context.full); + fee_parameters = configuration.fee_parameters; + minimal_block_delay = + node_ctxt.current_protocol.constants.minimal_block_delay; + delay_increment_per_round = + node_ctxt.current_protocol.constants.delay_increment_per_round; + } + ~data_dir:node_ctxt.data_dir + ~signers + ~retention_period:configuration.injector.retention_period + ~allowed_attempts:configuration.injector.attempts + in + let* () = start_workers configuration plugin node_ctxt in + Lwt.dont_wait + (fun () -> + let*! r = Metrics.metrics_serve configuration.metrics_addr in + match r with + | Ok () -> Lwt.return_unit + | Error err -> + Event.(metrics_ended (Format.asprintf "%a" pp_print_trace err))) + (fun exn -> Event.(metrics_ended_dont_wait (Printexc.to_string exn))) ; + + let*! () = + Event.node_is_ready + ~rpc_addr:configuration.rpc_addr + ~rpc_port:configuration.rpc_port + in + daemonize state + in + Metrics.Info.init_rollup_node_info + ~id:configuration.sc_rollup_address + ~mode:configuration.mode + ~genesis_level:node_ctxt.genesis_info.level + ~pvm_kind:(Octez_smart_rollup.Kind.to_string node_ctxt.kind) ; + let fatal_error_exit e = + Format.eprintf "%!%a@.Exiting.@." pp_print_trace e ; + let*! _ = Lwt_exit.exit_and_wait 1 in + return_unit + in + let error_to_degraded_mode e = + let*! () = Daemon_event.error e in + degraded_refutation_mode state + in + let handle_preimage_not_found e = + (* When running/initialising a rollup node with missing preimages + the rollup node enter in a degraded mode where actually there + isn't much that can be done with a non initialised rollup node, + hence it should exit after printing the error logs. + + A safe way to do this is to check if there was a processed head. + If not we can exit safely. If there was a processed head, we + go deeper and we check if the most recent commitment is actually + the genesis' one. If that's the case it means we're still on the + initialisation phase which means we can exit safely as well, if + not it means there is potential commitment(s) where refutation + can be played so we enter in degraded mode. *) + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | Some head -> + if + Commitment.Hash.( + Sc_rollup_block.most_recent_commitment head.header + = node_ctxt.genesis_info.commitment_hash) + then fatal_error_exit e + else error_to_degraded_mode e + | None -> fatal_error_exit e + in + protect start ~on_error:(function + | Rollup_node_errors.( + Lost_game _ | Unparsable_boot_sector _ | Invalid_genesis_state _) + :: _ as e -> + fatal_error_exit e + | Rollup_node_errors.Could_not_open_preimage_file _ :: _ as e -> + handle_preimage_not_found e + | e -> error_to_degraded_mode e) + +module Internal_for_tests = struct + (** Same as {!process_head} but only builds and stores the L2 block + corresponding to [messages]. It is used by the unit tests to build an L2 + chain. *) + let process_messages (module Plugin : Protocol_plugin_sig.S) + (node_ctxt : _ Node_context.t) ~is_first_block ~predecessor head messages + = + let open Lwt_result_syntax in + let* ctxt = previous_context node_ctxt ~predecessor in + let* () = Node_context.save_level node_ctxt (Layer1.head_of_header head) in + let* inbox_hash, inbox, inbox_witness, messages = + Plugin.Inbox.Internal_for_tests.process_messages + node_ctxt + ~is_first_block + ~predecessor + head + messages + in + let* ctxt, _num_messages, num_ticks, initial_tick = + Plugin.Interpreter.process_head + node_ctxt + ctxt + ~predecessor + head + (inbox, messages) + in + let*! context_hash = Context.commit ctxt in + let* commitment_hash = + Plugin.Publisher.process_head + node_ctxt + ~predecessor:predecessor.Layer1.hash + head + ctxt + in + let level = head.level in + let* previous_commitment_hash = + if level = node_ctxt.genesis_info.level then + (* Previous commitment for rollup genesis is itself. *) + return node_ctxt.genesis_info.commitment_hash + else + let+ pred = Node_context.get_l2_block node_ctxt predecessor.hash in + Sc_rollup_block.most_recent_commitment pred.header + in + let header = + Sc_rollup_block. + { + block_hash = head.hash; + level; + predecessor = predecessor.hash; + commitment_hash; + previous_commitment_hash; + context = context_hash; + inbox_witness; + inbox_hash; + } + in + let l2_block = + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} + in + let* () = Node_context.save_l2_head node_ctxt l2_block in + return l2_block +end + +let plugin_of_first_block cctxt (block : Layer1.header) = + let open Lwt_result_syntax in + let* {current_protocol; _} = + Tezos_shell_services.Shell_services.Blocks.protocols + cctxt + ~block:(`Hash (block.hash, 0)) + () + in + let*? constants, plugin = protocol_info cctxt current_protocol block.hash in + return (constants, current_protocol, plugin) + +let run ~data_dir ?log_kernel_debug_file (configuration : Configuration.t) + (cctxt : Client_context.full) = + let open Lwt_result_syntax in + Random.self_init () (* Initialize random state (for reconnection delays) *) ; + let*! () = Event.starting_node () in + let open Configuration in + let* () = + (* Check that the operators are valid keys. *) + Operator_purpose_map.iter_es + (fun _purpose operator -> + let+ _pkh, _pk, _skh = Client_keys.get_key cctxt operator in + ()) + configuration.sc_rollup_node_operators + in + let* l1_ctxt = + Layer1.start + ~name:"sc_rollup_node" + ~reconnection_delay:configuration.reconnection_delay + ~l1_blocks_cache_size:configuration.l1_blocks_cache_size + ?prefetch_blocks:configuration.prefetch_blocks + cctxt + in + let*! head = Layer1.wait_first l1_ctxt in + let* predecessor = + Layer1.fetch_tezos_shell_header l1_ctxt head.header.predecessor + in + let publisher = + Configuration.Operator_purpose_map.find + Publish + configuration.sc_rollup_node_operators + in + + let* constants, protocol, plugin = plugin_of_first_block cctxt head in + let module Plugin = (val plugin) in + let* constants + and* genesis_info = + Plugin.Layer1_helpers.retrieve_genesis_info + cctxt + configuration.sc_rollup_address + and* lcc = + Plugin.Layer1_helpers.get_last_cemented_commitment + cctxt + configuration.sc_rollup_address + and* lpc = + Option.filter_map_es + (Plugin.Layer1_helpers.get_last_published_commitment + cctxt + configuration.sc_rollup_address) + publisher + and* kind = + Plugin.Layer1_helpers.get_kind cctxt configuration.sc_rollup_address + in + let current_protocol = + { + Node_context.hash = protocol; + proto_level = predecessor.proto_level; + constants; + } + in + let* node_ctxt = + Node_context.init + cctxt + ~data_dir + ?log_kernel_debug_file + Read_write + l1_ctxt + genesis_info + ~lcc + ~lpc + kind + current_protocol + configuration + in + let* () = Plugin.L1_processing.check_pvm_initial_state_hash node_ctxt in + let* rpc_server = + Rpc_server.start configuration (Plugin.RPC_directory.directory node_ctxt) + in + let state = {node_ctxt; rpc_server; configuration; plugin} in + let (_ : Lwt_exit.clean_up_callback_id) = install_finalizer state in + run state diff --git a/src/lib_smart_rollup_node/rpc_server.ml b/src/lib_smart_rollup_node/rpc_server.ml new file mode 100644 index 0000000000000000000000000000000000000000..2d90478b2febe452c25d8005d238413ee51f2dde --- /dev/null +++ b/src/lib_smart_rollup_node/rpc_server.ml @@ -0,0 +1,76 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2022-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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +open Tezos_rpc_http +open Tezos_rpc_http_server + +type t = { + server : RPC_server.server; + host : string; + node : Conduit_lwt_unix.server; + acl : Resto_acl.Acl.t; +} + +let start configuration dir = + let open Lwt_result_syntax in + let Configuration.{rpc_addr; rpc_port; _} = configuration in + let rpc_addr = P2p_addr.of_string_exn rpc_addr in + let host = Ipaddr.V6.to_string rpc_addr in + let node = `TCP (`Port rpc_port) in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/2885 + More restrictive access control. *) + let acl = RPC_server.Acl.allow_all in + let server = + RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types + in + protect @@ fun () -> + let*! () = + RPC_server.launch + ~host + server + ~callback:(RPC_server.resto_callback server) + node + in + return {server; host; node; acl} + +let shutdown {server; _} = RPC_server.shutdown server + +let change_directory {server; host; node; acl} dir = + let open Lwt_result_syntax in + let*! () = RPC_server.shutdown server in + let server = + RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types + in + protect @@ fun () -> + let*! () = + RPC_server.launch + ~host + server + ~callback:(RPC_server.resto_callback server) + node + in + return {server; host; node; acl} diff --git a/src/lib_smart_rollup_node/rpc_server.mli b/src/lib_smart_rollup_node/rpc_server.mli new file mode 100644 index 0000000000000000000000000000000000000000..047182b620d6a8c36a434ba1e6eb5f075098bc5e --- /dev/null +++ b/src/lib_smart_rollup_node/rpc_server.mli @@ -0,0 +1,40 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2022-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"),*) +(* 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 t + +(** [start node_ctxt config] starts an RPC server listening for requests on the + port [config.rpc_port] and address [config.rpc_addr]. *) +val start : Configuration.t -> unit Tezos_rpc.Directory.t -> t tzresult Lwt.t + +(** Shutdown a running RPC server. When this function is called, the rollup node + will stop listening to incoming requests. *) +val shutdown : t -> unit Lwt.t + +(** Changes the directory for the RPC server. WARNING: this function stops and + restarts the RPC server. *) +val change_directory : t -> unit Tezos_rpc.Directory.t -> t tzresult Lwt.t diff --git a/src/proto_016_PtMumbai/bin_sc_rollup_node/dune b/src/proto_016_PtMumbai/bin_sc_rollup_node/dune index 486f1b53bf69aec6c1b8a534ba60f98d7ebabfa5..628052615626023ba19b02c4c935af4640c013ac 100644 --- a/src/proto_016_PtMumbai/bin_sc_rollup_node/dune +++ b/src/proto_016_PtMumbai/bin_sc_rollup_node/dune @@ -15,7 +15,7 @@ tezos-client-base-unix tezos-client-commands tezos-client-016-PtMumbai - octez-smart-rollup-node + octez-smart-rollup-node-lib octez_smart_rollup_node_PtMumbai) (link_flags (:standard) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_directory.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_directory.ml new file mode 100644 index 0000000000000000000000000000000000000000..67b128d608a7571afee0328ed05d48075711009a --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_directory.ml @@ -0,0 +1,516 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2022-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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +open RPC_directory_helpers +open Protocol + +let get_head_hash_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) + res + +let get_head_level_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res + +module Slot_pages_map = struct + open Protocol + open Alpha_context + include Map.Make (Dal.Slot_index) +end + +let get_dal_processed_slots node_ctxt block = + Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block + +module Global_directory = Make_directory (struct + include Sc_rollup_services.Global + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Proof_helpers_directory = Make_directory (struct + include Sc_rollup_services.Global.Helpers + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type context = Node_context.rw + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type subcontext = Node_context.rw + + let context_of_prefix node_ctxt () = return node_ctxt +end) + +module Local_directory = Make_directory (struct + include Sc_rollup_services.Local + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Block_directory = Make_directory (struct + include Sc_rollup_services.Global.Block + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t + + let context_of_prefix node_ctxt (((), block) : prefix) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block) +end) + +module Outbox_directory = Make_directory (struct + include Sc_rollup_services.Global.Block.Outbox + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t + + let context_of_prefix node_ctxt (((), block), level) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block, level) +end) + +module Common = struct + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.block + @@ fun (node_ctxt, block) () () -> + Node_context.get_full_l2_block node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.num_messages + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* l2_block = Node_context.get_l2_block node_ctxt block in + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness + in + Z.of_int num_messages + + let () = + Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address + @@ fun node_ctxt () () -> + return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_head + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_level + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.hash + @@ fun (_node_ctxt, block) () () -> return block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.level + @@ fun (node_ctxt, block) () () -> + Node_context.level_of_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.inbox + @@ fun (node_ctxt, block) () () -> + Node_context.get_inbox_by_block_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ l2_block = Node_context.get_l2_block node_ctxt block in + Z.of_int64 l2_block.num_ticks +end + +let get_state (node_ctxt : _ Node_context.t) block_hash = + let open Lwt_result_syntax in + let* ctxt = Node_context.checkout_context node_ctxt block_hash in + let*! state = Context.PVMState.find ctxt in + match state with None -> failwith "No state" | Some state -> return state + +let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages + ~insight_requests messages = + let open Lwt_result_syntax in + let open Alpha_context in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let reveal_map = + match reveal_pages with + | Some pages -> + let map = + List.fold_left + (fun map page -> + let hash = + Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) + in + Sc_rollup_reveal_hash.Map.add hash page map) + Sc_rollup_reveal_hash.Map.empty + pages + in + Some map + | None -> None + in + let* level = Node_context.level_of_hash node_ctxt block in + let* sim = + Simulation.start_simulation + node_ctxt + ~reveal_map + Layer1.{hash = block; level} + in + let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in + let* {state; inbox_level; _}, num_ticks_end = + Simulation.end_simulation node_ctxt sim + in + let*! insights = + List.map_p + (function + | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key + | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) + insight_requests + in + let num_ticks = Z.(num_ticks_0 + num_ticks_end) in + let level = Raw_level.of_int32_exn inbox_level in + let*! outbox = PVM.get_outbox level state in + let output = + List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox + in + let*! state_hash = PVM.state_hash state in + let*! status = PVM.get_status state in + let status = PVM.string_of_status status in + return + Sc_rollup_services. + {state_hash; status; output; inbox_level; num_ticks; insights} + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! tick = PVM.get_tick state in + return tick + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_hash + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! hash = PVM.state_hash state in + return hash + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! current_level = PVM.get_current_level state in + return current_level + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_value + @@ fun (node_ctxt, block) {key} () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let path = String.split_on_char '/' key in + let*! value = Context.PVMState.lookup state path in + match value with + | None -> failwith "No such key in PVM state" + | Some value -> + Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; + return value + +let () = + Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | None -> return_none + | Some head -> + let commitment_hash = + Sc_rollup_block.most_recent_commitment head.header + in + let+ commitment = + Node_context.find_commitment node_ctxt commitment_hash + in + Option.map (fun c -> (c, commitment_hash)) commitment + +let () = + Local_directory.register0 Sc_rollup_services.Local.last_published_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + match Reference.get node_ctxt.lpc with + | None -> return_none + | Some commitment -> + let hash = Octez_smart_rollup.Commitment.hash commitment in + (* The corresponding level in Store.Commitments.published_at_level is + available only when the commitment has been published and included + in a block. *) + let* published_at_level_info = + Node_context.commitment_published_at_level node_ctxt hash + in + let first_published, published = + match published_at_level_info with + | None -> (None, None) + | Some {first_published_at_level; published_at_level} -> + (Some first_published_at_level, published_at_level) + in + return_some (commitment, hash, first_published, published) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.status + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! status = PVM.get_status state in + return (PVM.string_of_status status) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ slots = + Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block + in + List.rev_map Sc_rollup_proto_types.Dal.Slot_header.of_octez slots |> List.rev + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots + @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block + +let () = + Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages + @@ fun (node_ctxt, block, outbox_level) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! outbox = PVM.get_outbox outbox_level state in + return outbox + +let () = + Proof_helpers_directory.register0 + Sc_rollup_services.Global.Helpers.outbox_proof + @@ fun node_ctxt output () -> + let open Lwt_result_syntax in + let+ commitment, proof = Outbox.proof_of_output node_ctxt output in + (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.simulate + @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> + simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.injection + @@ fun _node_ctxt () messages -> Batcher.register_messages messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.batcher_queue + @@ fun _node_ctxt () () -> + let open Lwt_result_syntax in + let*? queue = Batcher.get_queue () in + return queue + +(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level + of the commitment which should include the inbox of level + [inbox_level]. + + It is computed with the following formula: + {v + commitment_level(inbox_level) = + last_commitment - + ((last_commitment - inbox_level) / commitment_period + * commitment_period) + v} + *) +let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = + let open Alpha_context in + let open Option_syntax in + let+ last_published_commitment = Reference.get node_ctxt.lpc in + let commitment_period = + Int32.of_int + node_ctxt.current_protocol.constants.sc_rollup.commitment_period_in_blocks + in + let last_published = last_published_commitment.inbox_level in + let open Int32 in + div (sub last_published inbox_level) commitment_period + |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn + +let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = + let open Lwt_result_syntax in + let+ finalized_level = Node_context.get_finalized_level node_ctxt in + let finalized = Compare.Int32.(inbox_level <= finalized_level) in + let lcc = Reference.get node_ctxt.lcc in + let cemented = Compare.Int32.(inbox_level <= lcc.level) in + (finalized, cemented) + +let () = + Local_directory.register1 Sc_rollup_services.Local.batcher_message + @@ fun node_ctxt hash () () -> + let open Lwt_result_syntax in + let*? batch_status = Batcher.message_status hash in + let* status = + match batch_status with + | None -> return (None, Sc_rollup_services.Unknown) + | Some (batch_status, msg) -> ( + let return status = return (Some msg, status) in + match batch_status with + | Pending_batch -> return Sc_rollup_services.Pending_batch + | Batched l1_hash -> ( + match Injector.operation_status l1_hash with + | None -> return Sc_rollup_services.Unknown + | Some (Pending op) -> + return (Sc_rollup_services.Pending_injection op) + | Some (Injected {op; oph; op_index}) -> + return + (Sc_rollup_services.Injected + {op = op.operation; oph; op_index}) + | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( + let* finalized, cemented = + inbox_info_of_level node_ctxt l1_level + in + let commitment_level = + commitment_level_of_inbox_level node_ctxt l1_level + in + match commitment_level with + | None -> + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some commitment_level -> ( + let* block = + Node_context.find_l2_block_by_level + node_ctxt + (Alpha_context.Raw_level.to_int32 commitment_level) + in + match block with + | None -> + (* Commitment not computed yet for inbox *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some block -> ( + let commitment_hash = + WithExceptions.Option.get + ~loc:__LOC__ + block.header.commitment_hash + in + (* Commitment computed *) + let* published_at = + Node_context.commitment_published_at_level + node_ctxt + commitment_hash + in + match published_at with + | None | Some {published_at_level = None; _} -> + (* Commitment not published yet *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some + { + first_published_at_level; + published_at_level = Some published_at_level; + } -> + (* Commitment published *) + let* commitment = + Node_context.get_commitment + node_ctxt + commitment_hash + in + return + (Sc_rollup_services.Committed + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + commitment; + commitment_hash; + first_published_at_level; + published_at_level; + })))))) + in + + return status + +let directory (node_ctxt : _ Node_context.t) = + let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in + List.fold_left + (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) + Tezos_rpc.Directory.empty + [ + Global_directory.build_directory; + Local_directory.build_directory; + Block_directory.build_directory; + Proof_helpers_directory.build_directory; + Outbox_directory.build_directory; + PVM.build_directory; + ] diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_directory.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_directory.mli new file mode 100644 index 0000000000000000000000000000000000000000..206e38547b0c0036ae3003795244070e7063cf71 --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_directory.mli @@ -0,0 +1,27 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +(** The RPC directory for this rollup node. *) +val directory : Node_context.rw -> unit Tezos_rpc.Directory.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml index 6777c7e83d39edc98ee24941407aff192c47e7a7..bbc8af4ea919b8814d58a1e896af439c61b86208 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2022 Nomadic Labs, *) (* Copyright (c) 2022-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"),*) @@ -26,495 +27,6 @@ open Tezos_rpc_http open Tezos_rpc_http_server -open RPC_directory_helpers -open Protocol - -let get_head_hash_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map - (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) - res - -let get_head_level_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res - -module Slot_pages_map = struct - open Protocol - open Alpha_context - include Map.Make (Dal.Slot_index) -end - -let get_dal_processed_slots node_ctxt block = - Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block - -module Global_directory = Make_directory (struct - include Sc_rollup_services.Global - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Proof_helpers_directory = Make_directory (struct - include Sc_rollup_services.Global.Helpers - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type context = Node_context.rw - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type subcontext = Node_context.rw - - let context_of_prefix node_ctxt () = return node_ctxt -end) - -module Local_directory = Make_directory (struct - include Sc_rollup_services.Local - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Block_directory = Make_directory (struct - include Sc_rollup_services.Global.Block - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t - - let context_of_prefix node_ctxt (((), block) : prefix) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block) -end) - -module Outbox_directory = Make_directory (struct - include Sc_rollup_services.Global.Block.Outbox - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t - - let context_of_prefix node_ctxt (((), block), level) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block, level) -end) - -module Common = struct - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.block - @@ fun (node_ctxt, block) () () -> - Node_context.get_full_l2_block node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.num_messages - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* l2_block = Node_context.get_l2_block node_ctxt block in - let+ num_messages = - Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness - in - Z.of_int num_messages - - let () = - Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address - @@ fun node_ctxt () () -> - return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.hash - @@ fun (_node_ctxt, block) () () -> return block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.level - @@ fun (node_ctxt, block) () () -> - Node_context.level_of_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.inbox - @@ fun (node_ctxt, block) () () -> - Node_context.get_inbox_by_block_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ l2_block = Node_context.get_l2_block node_ctxt block in - Z.of_int64 l2_block.num_ticks -end - -let get_state (node_ctxt : _ Node_context.t) block_hash = - let open Lwt_result_syntax in - let* ctxt = Node_context.checkout_context node_ctxt block_hash in - let*! state = Context.PVMState.find ctxt in - match state with None -> failwith "No state" | Some state -> return state - -let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages - ~insight_requests messages = - let open Lwt_result_syntax in - let open Alpha_context in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let reveal_map = - match reveal_pages with - | Some pages -> - let map = - List.fold_left - (fun map page -> - let hash = - Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) - in - Sc_rollup_reveal_hash.Map.add hash page map) - Sc_rollup_reveal_hash.Map.empty - pages - in - Some map - | None -> None - in - let* level = Node_context.level_of_hash node_ctxt block in - let* sim = - Simulation.start_simulation - node_ctxt - ~reveal_map - Layer1.{hash = block; level} - in - let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in - let* {state; inbox_level; _}, num_ticks_end = - Simulation.end_simulation node_ctxt sim - in - let*! insights = - List.map_p - (function - | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key - | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) - insight_requests - in - let num_ticks = Z.(num_ticks_0 + num_ticks_end) in - let level = Raw_level.of_int32_exn inbox_level in - let*! outbox = PVM.get_outbox level state in - let output = - List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox - in - let*! state_hash = PVM.state_hash state in - let*! status = PVM.get_status state in - let status = PVM.string_of_status status in - return - Sc_rollup_services. - {state_hash; status; output; inbox_level; num_ticks; insights} - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! tick = PVM.get_tick state in - return tick - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_hash - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! hash = PVM.state_hash state in - return hash - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! current_level = PVM.get_current_level state in - return current_level - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_value - @@ fun (node_ctxt, block) {key} () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let path = String.split_on_char '/' key in - let*! value = Context.PVMState.lookup state path in - match value with - | None -> failwith "No such key in PVM state" - | Some value -> - Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; - return value - -let () = - Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - let* head = Node_context.last_processed_head_opt node_ctxt in - match head with - | None -> return_none - | Some head -> - let commitment_hash = - Sc_rollup_block.most_recent_commitment head.header - in - let+ commitment = - Node_context.find_commitment node_ctxt commitment_hash - in - Option.map (fun c -> (c, commitment_hash)) commitment - -let () = - Local_directory.register0 Sc_rollup_services.Local.last_published_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - match Reference.get node_ctxt.lpc with - | None -> return_none - | Some commitment -> - let hash = Octez_smart_rollup.Commitment.hash commitment in - (* The corresponding level in Store.Commitments.published_at_level is - available only when the commitment has been published and included - in a block. *) - let* published_at_level_info = - Node_context.commitment_published_at_level node_ctxt hash - in - let first_published, published = - match published_at_level_info with - | None -> (None, None) - | Some {first_published_at_level; published_at_level} -> - (Some first_published_at_level, published_at_level) - in - return_some (commitment, hash, first_published, published) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.status - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! status = PVM.get_status state in - return (PVM.string_of_status status) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ slots = - Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block - in - List.rev_map Sc_rollup_proto_types.Dal.Slot_header.of_octez slots |> List.rev - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots - @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block - -let () = - Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages - @@ fun (node_ctxt, block, outbox_level) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! outbox = PVM.get_outbox outbox_level state in - return outbox - -let () = - Proof_helpers_directory.register0 - Sc_rollup_services.Global.Helpers.outbox_proof - @@ fun node_ctxt output () -> - let open Lwt_result_syntax in - let+ commitment, proof = Outbox.proof_of_output node_ctxt output in - (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.simulate - @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> - simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.injection - @@ fun _node_ctxt () messages -> Batcher.register_messages messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.batcher_queue - @@ fun _node_ctxt () () -> - let open Lwt_result_syntax in - let*? queue = Batcher.get_queue () in - return queue - -(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level - of the commitment which should include the inbox of level - [inbox_level]. - - It is computed with the following formula: - {v - commitment_level(inbox_level) = - last_commitment - - ((last_commitment - inbox_level) / commitment_period - * commitment_period) - v} - *) -let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = - let open Alpha_context in - let open Option_syntax in - let+ last_published_commitment = Reference.get node_ctxt.lpc in - let commitment_period = - Int32.of_int - node_ctxt.protocol_constants.sc_rollup.commitment_period_in_blocks - in - let last_published = last_published_commitment.inbox_level in - let open Int32 in - div (sub last_published inbox_level) commitment_period - |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn - -let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = - let open Lwt_result_syntax in - let+ finalized_level = Node_context.get_finalized_level node_ctxt in - let finalized = Compare.Int32.(inbox_level <= finalized_level) in - let lcc = Reference.get node_ctxt.lcc in - let cemented = Compare.Int32.(inbox_level <= lcc.level) in - (finalized, cemented) - -let () = - Local_directory.register1 Sc_rollup_services.Local.batcher_message - @@ fun node_ctxt hash () () -> - let open Lwt_result_syntax in - let*? batch_status = Batcher.message_status hash in - let* status = - match batch_status with - | None -> return (None, Sc_rollup_services.Unknown) - | Some (batch_status, msg) -> ( - let return status = return (Some msg, status) in - match batch_status with - | Pending_batch -> return Sc_rollup_services.Pending_batch - | Batched l1_hash -> ( - match Injector.operation_status l1_hash with - | None -> return Sc_rollup_services.Unknown - | Some (Pending op) -> - return (Sc_rollup_services.Pending_injection op) - | Some (Injected {op; oph; op_index}) -> - return - (Sc_rollup_services.Injected - {op = op.operation; oph; op_index}) - | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( - let* finalized, cemented = - inbox_info_of_level node_ctxt l1_level - in - let commitment_level = - commitment_level_of_inbox_level node_ctxt l1_level - in - match commitment_level with - | None -> - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some commitment_level -> ( - let* block = - Node_context.find_l2_block_by_level - node_ctxt - (Alpha_context.Raw_level.to_int32 commitment_level) - in - match block with - | None -> - (* Commitment not computed yet for inbox *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some block -> ( - let commitment_hash = - WithExceptions.Option.get - ~loc:__LOC__ - block.header.commitment_hash - in - (* Commitment computed *) - let* published_at = - Node_context.commitment_published_at_level - node_ctxt - commitment_hash - in - match published_at with - | None | Some {published_at_level = None; _} -> - (* Commitment not published yet *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some - { - first_published_at_level; - published_at_level = Some published_at_level; - } -> - (* Commitment published *) - let* commitment = - Node_context.get_commitment - node_ctxt - commitment_hash - in - return - (Sc_rollup_services.Committed - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - commitment; - commitment_hash; - first_published_at_level; - published_at_level; - })))))) - in - - return status - -let register (node_ctxt : _ Node_context.t) = - let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in - List.fold_left - (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) - Tezos_rpc.Directory.empty - [ - Global_directory.build_directory; - Local_directory.build_directory; - Block_directory.build_directory; - Proof_helpers_directory.build_directory; - Outbox_directory.build_directory; - PVM.build_directory; - ] let start node_ctxt configuration = let open Lwt_result_syntax in @@ -523,7 +35,7 @@ let start node_ctxt configuration = let host = Ipaddr.V6.to_string rpc_addr in let node = `TCP (`Port rpc_port) in let acl = RPC_server.Acl.allow_all in - let dir = register node_ctxt in + let dir = RPC_directory.directory node_ctxt in let server = RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types in diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.mli index 6ce3fd2f3ec98de44ba3d2e209aaf903e50a1b0c..a7bcde269132a8713231c8dadbeb6283186d6b68 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/RPC_server.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 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"),*) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml index fccf828b61bb86d458e2de869bdaa3bc6650fdd9..e2eed2c345ebadf904558d3b09e0f812493f27e9 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/batcher.ml @@ -370,7 +370,7 @@ let check_batcher_config Configuration.{max_batch_size; _} = protocol_max_batch_size | _ -> Ok () -let init conf ~signer node_ctxt = +let start conf ~signer node_ctxt = let open Lwt_result_syntax in let*? () = check_batcher_config conf in let node_ctxt = Node_context.readonly node_ctxt in @@ -379,6 +379,19 @@ let init conf ~signer node_ctxt = in Lwt.wakeup worker_waker worker +let init conf ~signer node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_batcher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start conf ~signer node_ctxt + (* This is a batcher worker for a single scoru *) let worker = lazy 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 0bc4e1b6ee9c14e51e360f39fceed6a6d31816f8..2238a82fa858c3ea6e7e4ab8a5b00991bcb81568 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/daemon.ml @@ -324,16 +324,18 @@ let previous_context (node_ctxt : _ Node_context.t) let classify_head (node_ctxt : _ Node_context.t) ?(predecessor : Layer1.header option) (head : Layer1.header) = let open Lwt_result_syntax in - if head.header.proto_level = node_ctxt.proto_level then + if head.header.proto_level = node_ctxt.current_protocol.proto_level then (* Same protocol as supported one, ok *) return `Supported_proto - else if head.header.proto_level = node_ctxt.proto_level + 1 then + else if head.header.proto_level = node_ctxt.current_protocol.proto_level + 1 + then let* predecessor = match predecessor with | Some p -> return p | None -> Node_context.get_predecessor_header node_ctxt head in - if predecessor.header.proto_level = node_ctxt.proto_level then + if predecessor.header.proto_level = node_ctxt.current_protocol.proto_level + then (* Migration block from supported protocol to the next one, ok *) return `Supported_migration else return `Unsupported_proto @@ -433,13 +435,7 @@ let rec process_head (daemon_components : (module Daemon_components.S)) } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.mark_finalized_level @@ -636,9 +632,10 @@ let run node_ctxt configuration { cctxt = (node_ctxt.cctxt :> Client_context.full); fee_parameters = configuration.fee_parameters; - minimal_block_delay = node_ctxt.protocol_constants.minimal_block_delay; + minimal_block_delay = + node_ctxt.current_protocol.constants.minimal_block_delay; delay_increment_per_round = - node_ctxt.protocol_constants.delay_increment_per_round; + node_ctxt.current_protocol.constants.delay_increment_per_round; } ~data_dir:node_ctxt.data_dir ~signers @@ -727,6 +724,7 @@ module Internal_for_tests = struct let* inbox_hash, inbox, inbox_witness, messages = Inbox.Internal_for_tests.process_messages node_ctxt + ~is_first_block:false ~predecessor head messages @@ -765,13 +763,7 @@ module Internal_for_tests = struct } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.save_l2_head node_ctxt l2_block in return l2_block @@ -832,6 +824,13 @@ let run configuration.sc_rollup_address) publisher and* kind = Layer1_helpers.get_kind cctxt configuration.sc_rollup_address in + let current_protocol = + { + Node_context.hash = Protocol.hash; + proto_level = predecessor.proto_level; + constants; + } + in let* node_ctxt = Node_context.init cctxt @@ -839,12 +838,11 @@ let run ?log_kernel_debug_file Read_write l1_ctxt - constants genesis_info ~lcc ~lpc kind - ~proto_level:predecessor.proto_level + current_protocol configuration in run node_ctxt configuration daemon_components 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 f7418051577f803269d84b13d05c635791c8da32..ab702873c37da4411dc1d154043357562094082a 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 @@ -25,8 +25,6 @@ open Protocol open Alpha_context -module Lifted_protocol = Tezos_protocol_016_PtMumbai_lifted.Lifted_protocol -module Block_services = Block_services.Make (Lifted_protocol) (Lifted_protocol) let ancestor_hash ~number_of_levels ({Node_context.genesis_info; _} as node_ctxt) head = @@ -69,7 +67,9 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = we reduce the lag to 1, then the slots header will never be confirmed. *) let open Lwt_result_syntax in - let lag = node_ctxt.Node_context.protocol_constants.dal.attestation_lag in + let lag = + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag + in (* we are downloading endorsemented for slots at level [level], so we need to download the data at level [level - lag]. *) @@ -127,22 +127,24 @@ let to_slot_index_list (constants : Rollup_constants.protocol_constants) bitset Use a shared storage between dal and rollup node to store slots data. *) -let download_and_save_slots - ({Node_context.protocol_constants; _} as node_context) ~current_block_hash - {published_block_hash; confirmed_slots_indexes} = +let download_and_save_slots (node_context : _ Node_context.t) + ~current_block_hash {published_block_hash; confirmed_slots_indexes} = let open Lwt_result_syntax in let*? all_slots = - Bitset.fill ~length:protocol_constants.dal.number_of_slots + Bitset.fill + ~length:node_context.current_protocol.constants.dal.number_of_slots |> Environment.wrap_tzresult in let*? not_confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants + @@ to_slot_index_list node_context.current_protocol.constants @@ Bitset.diff all_slots confirmed_slots_indexes in let*? confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants confirmed_slots_indexes + @@ to_slot_index_list + node_context.current_protocol.constants + confirmed_slots_indexes in (* The contents of each slot index are written to a different location on disk, therefore calls to store contents for different slot indexes can @@ -185,7 +187,7 @@ module Confirmed_slots_history = struct let*? relevant_slots_indexes = Environment.wrap_tzresult @@ to_slot_index_list - node_ctxt.Node_context.protocol_constants + node_ctxt.Node_context.current_protocol.constants confirmed_slots_indexes in List.map_ep @@ -217,7 +219,8 @@ module Confirmed_slots_history = struct let should_process_dal_slots node_ctxt block_level = let open Node_context in let lag = - Int32.of_int node_ctxt.Node_context.protocol_constants.dal.attestation_lag + Int32.of_int + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag in let block_level = Raw_level.to_int32 block_level in let genesis_level = node_ctxt.genesis_info.level in @@ -280,7 +283,7 @@ module Confirmed_slots_history = struct ~find ~default:(fun node_ctxt _block -> let num_slots = - node_ctxt.Node_context.protocol_constants.dal.number_of_slots + node_ctxt.Node_context.current_protocol.constants.dal.number_of_slots in (* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/3788 Put an accurate value for capacity. The value diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/dune b/src/proto_016_PtMumbai/lib_sc_rollup_node/dune index f2c0eedf96e747877ffbcf05da9b9d4b347ffc37..ff86dd4c55b05bfe74985b4158a3ebd0247e504f 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/dune +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/dune @@ -37,7 +37,7 @@ aches aches-lwt octez-injector - octez-smart-rollup-node + octez-smart-rollup-node-lib tezos-scoru-wasm tezos-scoru-wasm-fast tezos-crypto-dal diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/fueled_pvm.ml index b56a7d48c8fccb0e6df2008e80a2bd59c16daff1..bb35157ecaeb38f4162a7469f75985381fefba1d 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/fueled_pvm.ml @@ -141,7 +141,7 @@ module Make_fueled (F : Fuel.S) : S with type fuel = F.t = struct let module PVM = (val Pvm.of_kind node_ctxt.kind) in let metadata = metadata node_ctxt in let dal_attestation_lag = - node_ctxt.protocol_constants.dal.attestation_lag + node_ctxt.current_protocol.constants.dal.attestation_lag in let reveal_builtins = Tezos_scoru_wasm.Builtins. 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 704b2826b13826d8137df685c89f80ba2d928eb8..dbac01ee0f715c0b9f46561c60128150e5c71514 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.ml @@ -204,7 +204,7 @@ let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) else let* inbox = Layer1_helpers.genesis_inbox - (new Protocol_client_context.wrap_full node_ctxt.cctxt) + node_ctxt.cctxt ~genesis_level:node_ctxt.genesis_info.level in let Octez_smart_rollup.Inbox.{hash = witness; _} = @@ -249,5 +249,9 @@ let payloads_history_of_messages ~predecessor ~predecessor_timestamp messages = payloads_history module Internal_for_tests = struct - let process_messages = process_messages + let process_messages node_ctxt ~is_first_block ~predecessor head messages = + assert (not is_first_block) ; + (* Rollups cannot be originated before Mumbai, so an inbox cannot appear in + the first block of Mumbai. *) + process_messages node_ctxt ~predecessor head messages end diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli index a5f3a461128fa733595df1cce03d8b4b3bd7a7e3..7b8e851b547687e06d69f4c24e72183cb2f4262e 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/inbox.mli @@ -91,6 +91,7 @@ val same_as_layer_1 : module Internal_for_tests : sig val process_messages : Node_context.rw -> + is_first_block:bool -> predecessor:Layer1.header -> Layer1.header -> string list -> 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 e85973339fe871ed2ce51e9381bc15d26322f4e0..b246c1777d1f7acf2f48aa76ea11202e099357ce 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.ml @@ -119,7 +119,8 @@ let transition_pvm node_ctxt ctxt predecessor Layer1.{hash = _; _} let*! () = Interpreter_event.transitioned_pvm inbox_level state_hash tick num_messages in - return (ctxt, num_messages, Z.to_int64 num_ticks, initial_tick) + return + (ctxt, num_messages, Z.to_int64 num_ticks, Sc_rollup.Tick.to_z initial_tick) (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) @@ -137,8 +138,8 @@ let process_head (node_ctxt : _ Node_context.t) ctxt else if head.Layer1.level = node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in let*! ctxt = Context.PVMState.set ctxt state in - return (ctxt, 0, 0L, Sc_rollup.Tick.initial) - else return (ctxt, 0, 0L, Sc_rollup.Tick.initial) + return (ctxt, 0, 0L, Z.zero) + else return (ctxt, 0, 0L, Z.zero) (** Returns the starting evaluation before the evaluation of the block. It contains the PVM state at the end of the execution of the previous block and diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli index 48cff9f6ceb5366c4bcdb7a69b922fd390102b24..124eaa96b53d4ba69484ba4dcc81688166aba0b2 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/interpreter.mli @@ -39,7 +39,7 @@ val process_head : predecessor:Layer1.header -> Layer1.header -> Octez_smart_rollup.Inbox.t * string list -> - ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t + ('a Context.t * int * int64 * Z.t) tzresult Lwt.t (** [state_of_tick node_ctxt ?start_state tick level] returns [Some (state, hash)] for a given [tick] if this [tick] happened before [level]. Otherwise, diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.ml index 22da00e3554946644ff9f9d75695d87b4ca1aa4d..cf934fb1b3e7cb8f7e70db74800b64bd9566bfad 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.ml @@ -53,9 +53,12 @@ let fetch_tezos_block l1_ctxt hash = let prefetch_tezos_blocks = Layer1.prefetch_tezos_blocks fetch extract_header -let get_last_cemented_commitment (cctxt : Protocol_client_context.full) - rollup_address : Node_context.lcc tzresult Lwt.t = +let get_last_cemented_commitment (cctxt : #Client_context.full) rollup_address : + Node_context.lcc tzresult Lwt.t = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ commitment, level = Plugin.RPC.Sc_rollup.last_cemented_commitment_hash_with_level @@ -69,9 +72,12 @@ let get_last_cemented_commitment (cctxt : Protocol_client_context.full) level = Protocol.Alpha_context.Raw_level.to_int32 level; } -let get_last_published_commitment (cctxt : Protocol_client_context.full) - rollup_address operator = +let get_last_published_commitment (cctxt : #Client_context.full) rollup_address + operator = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let*! res = Plugin.RPC.Sc_rollup.staked_on_commitment @@ -98,6 +104,9 @@ let get_last_published_commitment (cctxt : Protocol_client_context.full) let get_kind cctxt rollup_address = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ kind = RPC.Sc_rollup.kind cctxt (cctxt#chain, cctxt#block) rollup_address () @@ -106,6 +115,9 @@ let get_kind cctxt rollup_address = let genesis_inbox cctxt ~genesis_level = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ inbox = Plugin.RPC.Sc_rollup.inbox cctxt (cctxt#chain, `Level genesis_level) in @@ -142,6 +154,9 @@ let constants_of_parametric *) let retrieve_constants ?(block = `Head 0) cctxt = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {parametric; _} = Protocol.Constants_services.all cctxt (cctxt#chain, block) in @@ -150,6 +165,9 @@ let retrieve_constants ?(block = `Head 0) cctxt = let retrieve_genesis_info cctxt rollup_address = let open Lwt_result_syntax in let open Protocol.Alpha_context in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {level; commitment_hash} = RPC.Sc_rollup.genesis_info cctxt diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.mli b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.mli index 605c80fcbdd0757210fabb5b06a5ca6edbb02154..c82972f95ba0d8f23472993bc7451a0655999f68 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.mli +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/layer1_helpers.mli @@ -40,19 +40,18 @@ val fetch_tezos_block : val prefetch_tezos_blocks : t -> head list -> unit val get_last_cemented_commitment : - Protocol_client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t val get_last_published_commitment : - Protocol_client_context.full -> + #Client_context.full -> Address.t -> Signature.public_key_hash -> Commitment.t option tzresult Lwt.t -val get_kind : - Protocol_client_context.full -> Address.t -> Kind.t tzresult Lwt.t +val get_kind : #Client_context.full -> Address.t -> Kind.t tzresult Lwt.t val genesis_inbox : - Protocol_client_context.full -> + #Client_context.full -> genesis_level:int32 -> Octez_smart_rollup.Inbox.t tzresult Lwt.t @@ -64,10 +63,8 @@ val constants_of_parametric : (** Retrieve protocol agnotic constants for the head of the chain. *) val retrieve_constants : ?block:Block_services.block -> - Protocol_client_context.full -> + #Client_context.full -> Rollup_constants.protocol_constants tzresult Lwt.t val retrieve_genesis_info : - Protocol_client_context.full -> - Address.t -> - Node_context.genesis_info tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.genesis_info tzresult Lwt.t diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher.ml index eb26ecbd21ce1cefcaf3e8a7f75804a585c6ce96..3305d3a16b5c26c1271b8bfeb8a1b8ce36992411 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/publisher.ml @@ -72,11 +72,12 @@ let sub_level level decrement = if r < 0l then None else Some r let sc_rollup_commitment_period node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup + node_ctxt.Node_context.current_protocol.constants.sc_rollup .commitment_period_in_blocks let sc_rollup_challenge_window node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup.challenge_window_in_blocks + node_ctxt.Node_context.current_protocol.constants.sc_rollup + .challenge_window_in_blocks let next_commitment_level node_ctxt last_commitment_level = add_level last_commitment_level (sc_rollup_commitment_period node_ctxt) @@ -478,13 +479,26 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Commitment_event.starting () in let node_ctxt = Node_context.readonly node_ctxt in let+ worker = Worker.launch table () {node_ctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_publisher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a publisher worker for a single scoru *) let worker = lazy diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_coordinator.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_coordinator.ml index 5762f752126c9814d1d882e6a540ca5c81085dec..627b7399a5e91e1ed02a84efdca2cc0ab74abdf1 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_coordinator.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/refutation_coordinator.ml @@ -201,7 +201,7 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Refutation_game_event.Coordinator.starting () in let cctxt = @@ -210,6 +210,19 @@ let init node_ctxt = let+ worker = Worker.launch table () {node_ctxt; cctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_refutation_coordinator; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a refutation coordinator for a single scoru *) let worker = lazy diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/rollup_node_plugin.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/rollup_node_plugin.ml new file mode 100644 index 0000000000000000000000000000000000000000..6d1642bf8f58d15e8da5aaa7e91613cda1a8d4b5 --- /dev/null +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/rollup_node_plugin.ml @@ -0,0 +1,45 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 Plugin : Protocol_plugin_sig.S = struct + let protocol = Protocol.hash + + module RPC_directory = RPC_directory + module Dal_slots_tracker = Dal_slots_tracker + module Inbox = Inbox + module Interpreter = Interpreter + module Publisher = Publisher + module Refutation_coordinator = Refutation_coordinator + module Batcher = Batcher + module Layer1_helpers = Layer1_helpers + + module L1_processing = struct + let check_pvm_initial_state_hash = Daemon.check_initial_state_hash + + let process_l1_block_operations = Daemon.process_l1_block_operations + end +end + +let () = Protocol_plugins.register (module Plugin) diff --git a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml index 123d317b3cf3a2f26a2636ba677aa53ddc391284..41e44f245a7f8d9b99279871e29030e5041c6920 100644 --- a/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_016_PtMumbai/lib_sc_rollup_node/test/helpers/helpers.ml @@ -143,10 +143,13 @@ let initialize_node_context ?(constants = default_constants) kind ~boot_sector = new Protocol_client_context.wrap_full (new Faked_client_context.unix_faked ~base_dir ~filesystem) in + let current_protocol = + {Node_context.hash = Protocol.hash; proto_level = 0; constants} + in let* ctxt = Node_context.Internal_for_tests.create_node_context cctxt - constants + current_protocol ~data_dir kind in diff --git a/src/proto_017_PtNairob/bin_sc_rollup_node/dune b/src/proto_017_PtNairob/bin_sc_rollup_node/dune index 2c9fb67fd74aeab1680f9a4922bd1d35b8e0a79f..5df876d3682a1ea861ec7f97148f779628f13198 100644 --- a/src/proto_017_PtNairob/bin_sc_rollup_node/dune +++ b/src/proto_017_PtNairob/bin_sc_rollup_node/dune @@ -15,7 +15,7 @@ tezos-client-base-unix tezos-client-commands tezos-client-017-PtNairob - octez-smart-rollup-node + octez-smart-rollup-node-lib octez_smart_rollup_node_PtNairob) (link_flags (:standard) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_directory.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_directory.ml new file mode 100644 index 0000000000000000000000000000000000000000..67b128d608a7571afee0328ed05d48075711009a --- /dev/null +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_directory.ml @@ -0,0 +1,516 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2022-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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +open RPC_directory_helpers +open Protocol + +let get_head_hash_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) + res + +let get_head_level_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res + +module Slot_pages_map = struct + open Protocol + open Alpha_context + include Map.Make (Dal.Slot_index) +end + +let get_dal_processed_slots node_ctxt block = + Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block + +module Global_directory = Make_directory (struct + include Sc_rollup_services.Global + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Proof_helpers_directory = Make_directory (struct + include Sc_rollup_services.Global.Helpers + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type context = Node_context.rw + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type subcontext = Node_context.rw + + let context_of_prefix node_ctxt () = return node_ctxt +end) + +module Local_directory = Make_directory (struct + include Sc_rollup_services.Local + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Block_directory = Make_directory (struct + include Sc_rollup_services.Global.Block + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t + + let context_of_prefix node_ctxt (((), block) : prefix) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block) +end) + +module Outbox_directory = Make_directory (struct + include Sc_rollup_services.Global.Block.Outbox + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t + + let context_of_prefix node_ctxt (((), block), level) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block, level) +end) + +module Common = struct + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.block + @@ fun (node_ctxt, block) () () -> + Node_context.get_full_l2_block node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.num_messages + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* l2_block = Node_context.get_l2_block node_ctxt block in + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness + in + Z.of_int num_messages + + let () = + Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address + @@ fun node_ctxt () () -> + return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_head + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_level + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.hash + @@ fun (_node_ctxt, block) () () -> return block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.level + @@ fun (node_ctxt, block) () () -> + Node_context.level_of_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.inbox + @@ fun (node_ctxt, block) () () -> + Node_context.get_inbox_by_block_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ l2_block = Node_context.get_l2_block node_ctxt block in + Z.of_int64 l2_block.num_ticks +end + +let get_state (node_ctxt : _ Node_context.t) block_hash = + let open Lwt_result_syntax in + let* ctxt = Node_context.checkout_context node_ctxt block_hash in + let*! state = Context.PVMState.find ctxt in + match state with None -> failwith "No state" | Some state -> return state + +let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages + ~insight_requests messages = + let open Lwt_result_syntax in + let open Alpha_context in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let reveal_map = + match reveal_pages with + | Some pages -> + let map = + List.fold_left + (fun map page -> + let hash = + Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) + in + Sc_rollup_reveal_hash.Map.add hash page map) + Sc_rollup_reveal_hash.Map.empty + pages + in + Some map + | None -> None + in + let* level = Node_context.level_of_hash node_ctxt block in + let* sim = + Simulation.start_simulation + node_ctxt + ~reveal_map + Layer1.{hash = block; level} + in + let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in + let* {state; inbox_level; _}, num_ticks_end = + Simulation.end_simulation node_ctxt sim + in + let*! insights = + List.map_p + (function + | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key + | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) + insight_requests + in + let num_ticks = Z.(num_ticks_0 + num_ticks_end) in + let level = Raw_level.of_int32_exn inbox_level in + let*! outbox = PVM.get_outbox level state in + let output = + List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox + in + let*! state_hash = PVM.state_hash state in + let*! status = PVM.get_status state in + let status = PVM.string_of_status status in + return + Sc_rollup_services. + {state_hash; status; output; inbox_level; num_ticks; insights} + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! tick = PVM.get_tick state in + return tick + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_hash + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! hash = PVM.state_hash state in + return hash + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! current_level = PVM.get_current_level state in + return current_level + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_value + @@ fun (node_ctxt, block) {key} () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let path = String.split_on_char '/' key in + let*! value = Context.PVMState.lookup state path in + match value with + | None -> failwith "No such key in PVM state" + | Some value -> + Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; + return value + +let () = + Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | None -> return_none + | Some head -> + let commitment_hash = + Sc_rollup_block.most_recent_commitment head.header + in + let+ commitment = + Node_context.find_commitment node_ctxt commitment_hash + in + Option.map (fun c -> (c, commitment_hash)) commitment + +let () = + Local_directory.register0 Sc_rollup_services.Local.last_published_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + match Reference.get node_ctxt.lpc with + | None -> return_none + | Some commitment -> + let hash = Octez_smart_rollup.Commitment.hash commitment in + (* The corresponding level in Store.Commitments.published_at_level is + available only when the commitment has been published and included + in a block. *) + let* published_at_level_info = + Node_context.commitment_published_at_level node_ctxt hash + in + let first_published, published = + match published_at_level_info with + | None -> (None, None) + | Some {first_published_at_level; published_at_level} -> + (Some first_published_at_level, published_at_level) + in + return_some (commitment, hash, first_published, published) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.status + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! status = PVM.get_status state in + return (PVM.string_of_status status) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ slots = + Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block + in + List.rev_map Sc_rollup_proto_types.Dal.Slot_header.of_octez slots |> List.rev + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots + @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block + +let () = + Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages + @@ fun (node_ctxt, block, outbox_level) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! outbox = PVM.get_outbox outbox_level state in + return outbox + +let () = + Proof_helpers_directory.register0 + Sc_rollup_services.Global.Helpers.outbox_proof + @@ fun node_ctxt output () -> + let open Lwt_result_syntax in + let+ commitment, proof = Outbox.proof_of_output node_ctxt output in + (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.simulate + @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> + simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.injection + @@ fun _node_ctxt () messages -> Batcher.register_messages messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.batcher_queue + @@ fun _node_ctxt () () -> + let open Lwt_result_syntax in + let*? queue = Batcher.get_queue () in + return queue + +(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level + of the commitment which should include the inbox of level + [inbox_level]. + + It is computed with the following formula: + {v + commitment_level(inbox_level) = + last_commitment - + ((last_commitment - inbox_level) / commitment_period + * commitment_period) + v} + *) +let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = + let open Alpha_context in + let open Option_syntax in + let+ last_published_commitment = Reference.get node_ctxt.lpc in + let commitment_period = + Int32.of_int + node_ctxt.current_protocol.constants.sc_rollup.commitment_period_in_blocks + in + let last_published = last_published_commitment.inbox_level in + let open Int32 in + div (sub last_published inbox_level) commitment_period + |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn + +let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = + let open Lwt_result_syntax in + let+ finalized_level = Node_context.get_finalized_level node_ctxt in + let finalized = Compare.Int32.(inbox_level <= finalized_level) in + let lcc = Reference.get node_ctxt.lcc in + let cemented = Compare.Int32.(inbox_level <= lcc.level) in + (finalized, cemented) + +let () = + Local_directory.register1 Sc_rollup_services.Local.batcher_message + @@ fun node_ctxt hash () () -> + let open Lwt_result_syntax in + let*? batch_status = Batcher.message_status hash in + let* status = + match batch_status with + | None -> return (None, Sc_rollup_services.Unknown) + | Some (batch_status, msg) -> ( + let return status = return (Some msg, status) in + match batch_status with + | Pending_batch -> return Sc_rollup_services.Pending_batch + | Batched l1_hash -> ( + match Injector.operation_status l1_hash with + | None -> return Sc_rollup_services.Unknown + | Some (Pending op) -> + return (Sc_rollup_services.Pending_injection op) + | Some (Injected {op; oph; op_index}) -> + return + (Sc_rollup_services.Injected + {op = op.operation; oph; op_index}) + | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( + let* finalized, cemented = + inbox_info_of_level node_ctxt l1_level + in + let commitment_level = + commitment_level_of_inbox_level node_ctxt l1_level + in + match commitment_level with + | None -> + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some commitment_level -> ( + let* block = + Node_context.find_l2_block_by_level + node_ctxt + (Alpha_context.Raw_level.to_int32 commitment_level) + in + match block with + | None -> + (* Commitment not computed yet for inbox *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some block -> ( + let commitment_hash = + WithExceptions.Option.get + ~loc:__LOC__ + block.header.commitment_hash + in + (* Commitment computed *) + let* published_at = + Node_context.commitment_published_at_level + node_ctxt + commitment_hash + in + match published_at with + | None | Some {published_at_level = None; _} -> + (* Commitment not published yet *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some + { + first_published_at_level; + published_at_level = Some published_at_level; + } -> + (* Commitment published *) + let* commitment = + Node_context.get_commitment + node_ctxt + commitment_hash + in + return + (Sc_rollup_services.Committed + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + commitment; + commitment_hash; + first_published_at_level; + published_at_level; + })))))) + in + + return status + +let directory (node_ctxt : _ Node_context.t) = + let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in + List.fold_left + (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) + Tezos_rpc.Directory.empty + [ + Global_directory.build_directory; + Local_directory.build_directory; + Block_directory.build_directory; + Proof_helpers_directory.build_directory; + Outbox_directory.build_directory; + PVM.build_directory; + ] diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_directory.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_directory.mli new file mode 100644 index 0000000000000000000000000000000000000000..206e38547b0c0036ae3003795244070e7063cf71 --- /dev/null +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_directory.mli @@ -0,0 +1,27 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +(** The RPC directory for this rollup node. *) +val directory : Node_context.rw -> unit Tezos_rpc.Directory.t diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.ml index 6777c7e83d39edc98ee24941407aff192c47e7a7..bbc8af4ea919b8814d58a1e896af439c61b86208 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2022 Nomadic Labs, *) (* Copyright (c) 2022-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"),*) @@ -26,495 +27,6 @@ open Tezos_rpc_http open Tezos_rpc_http_server -open RPC_directory_helpers -open Protocol - -let get_head_hash_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map - (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) - res - -let get_head_level_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res - -module Slot_pages_map = struct - open Protocol - open Alpha_context - include Map.Make (Dal.Slot_index) -end - -let get_dal_processed_slots node_ctxt block = - Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block - -module Global_directory = Make_directory (struct - include Sc_rollup_services.Global - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Proof_helpers_directory = Make_directory (struct - include Sc_rollup_services.Global.Helpers - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type context = Node_context.rw - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type subcontext = Node_context.rw - - let context_of_prefix node_ctxt () = return node_ctxt -end) - -module Local_directory = Make_directory (struct - include Sc_rollup_services.Local - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Block_directory = Make_directory (struct - include Sc_rollup_services.Global.Block - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t - - let context_of_prefix node_ctxt (((), block) : prefix) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block) -end) - -module Outbox_directory = Make_directory (struct - include Sc_rollup_services.Global.Block.Outbox - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t - - let context_of_prefix node_ctxt (((), block), level) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block, level) -end) - -module Common = struct - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.block - @@ fun (node_ctxt, block) () () -> - Node_context.get_full_l2_block node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.num_messages - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* l2_block = Node_context.get_l2_block node_ctxt block in - let+ num_messages = - Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness - in - Z.of_int num_messages - - let () = - Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address - @@ fun node_ctxt () () -> - return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.hash - @@ fun (_node_ctxt, block) () () -> return block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.level - @@ fun (node_ctxt, block) () () -> - Node_context.level_of_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.inbox - @@ fun (node_ctxt, block) () () -> - Node_context.get_inbox_by_block_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ l2_block = Node_context.get_l2_block node_ctxt block in - Z.of_int64 l2_block.num_ticks -end - -let get_state (node_ctxt : _ Node_context.t) block_hash = - let open Lwt_result_syntax in - let* ctxt = Node_context.checkout_context node_ctxt block_hash in - let*! state = Context.PVMState.find ctxt in - match state with None -> failwith "No state" | Some state -> return state - -let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages - ~insight_requests messages = - let open Lwt_result_syntax in - let open Alpha_context in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let reveal_map = - match reveal_pages with - | Some pages -> - let map = - List.fold_left - (fun map page -> - let hash = - Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) - in - Sc_rollup_reveal_hash.Map.add hash page map) - Sc_rollup_reveal_hash.Map.empty - pages - in - Some map - | None -> None - in - let* level = Node_context.level_of_hash node_ctxt block in - let* sim = - Simulation.start_simulation - node_ctxt - ~reveal_map - Layer1.{hash = block; level} - in - let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in - let* {state; inbox_level; _}, num_ticks_end = - Simulation.end_simulation node_ctxt sim - in - let*! insights = - List.map_p - (function - | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key - | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) - insight_requests - in - let num_ticks = Z.(num_ticks_0 + num_ticks_end) in - let level = Raw_level.of_int32_exn inbox_level in - let*! outbox = PVM.get_outbox level state in - let output = - List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox - in - let*! state_hash = PVM.state_hash state in - let*! status = PVM.get_status state in - let status = PVM.string_of_status status in - return - Sc_rollup_services. - {state_hash; status; output; inbox_level; num_ticks; insights} - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! tick = PVM.get_tick state in - return tick - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_hash - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! hash = PVM.state_hash state in - return hash - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! current_level = PVM.get_current_level state in - return current_level - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_value - @@ fun (node_ctxt, block) {key} () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let path = String.split_on_char '/' key in - let*! value = Context.PVMState.lookup state path in - match value with - | None -> failwith "No such key in PVM state" - | Some value -> - Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; - return value - -let () = - Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - let* head = Node_context.last_processed_head_opt node_ctxt in - match head with - | None -> return_none - | Some head -> - let commitment_hash = - Sc_rollup_block.most_recent_commitment head.header - in - let+ commitment = - Node_context.find_commitment node_ctxt commitment_hash - in - Option.map (fun c -> (c, commitment_hash)) commitment - -let () = - Local_directory.register0 Sc_rollup_services.Local.last_published_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - match Reference.get node_ctxt.lpc with - | None -> return_none - | Some commitment -> - let hash = Octez_smart_rollup.Commitment.hash commitment in - (* The corresponding level in Store.Commitments.published_at_level is - available only when the commitment has been published and included - in a block. *) - let* published_at_level_info = - Node_context.commitment_published_at_level node_ctxt hash - in - let first_published, published = - match published_at_level_info with - | None -> (None, None) - | Some {first_published_at_level; published_at_level} -> - (Some first_published_at_level, published_at_level) - in - return_some (commitment, hash, first_published, published) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.status - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! status = PVM.get_status state in - return (PVM.string_of_status status) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ slots = - Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block - in - List.rev_map Sc_rollup_proto_types.Dal.Slot_header.of_octez slots |> List.rev - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots - @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block - -let () = - Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages - @@ fun (node_ctxt, block, outbox_level) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! outbox = PVM.get_outbox outbox_level state in - return outbox - -let () = - Proof_helpers_directory.register0 - Sc_rollup_services.Global.Helpers.outbox_proof - @@ fun node_ctxt output () -> - let open Lwt_result_syntax in - let+ commitment, proof = Outbox.proof_of_output node_ctxt output in - (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.simulate - @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> - simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.injection - @@ fun _node_ctxt () messages -> Batcher.register_messages messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.batcher_queue - @@ fun _node_ctxt () () -> - let open Lwt_result_syntax in - let*? queue = Batcher.get_queue () in - return queue - -(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level - of the commitment which should include the inbox of level - [inbox_level]. - - It is computed with the following formula: - {v - commitment_level(inbox_level) = - last_commitment - - ((last_commitment - inbox_level) / commitment_period - * commitment_period) - v} - *) -let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = - let open Alpha_context in - let open Option_syntax in - let+ last_published_commitment = Reference.get node_ctxt.lpc in - let commitment_period = - Int32.of_int - node_ctxt.protocol_constants.sc_rollup.commitment_period_in_blocks - in - let last_published = last_published_commitment.inbox_level in - let open Int32 in - div (sub last_published inbox_level) commitment_period - |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn - -let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = - let open Lwt_result_syntax in - let+ finalized_level = Node_context.get_finalized_level node_ctxt in - let finalized = Compare.Int32.(inbox_level <= finalized_level) in - let lcc = Reference.get node_ctxt.lcc in - let cemented = Compare.Int32.(inbox_level <= lcc.level) in - (finalized, cemented) - -let () = - Local_directory.register1 Sc_rollup_services.Local.batcher_message - @@ fun node_ctxt hash () () -> - let open Lwt_result_syntax in - let*? batch_status = Batcher.message_status hash in - let* status = - match batch_status with - | None -> return (None, Sc_rollup_services.Unknown) - | Some (batch_status, msg) -> ( - let return status = return (Some msg, status) in - match batch_status with - | Pending_batch -> return Sc_rollup_services.Pending_batch - | Batched l1_hash -> ( - match Injector.operation_status l1_hash with - | None -> return Sc_rollup_services.Unknown - | Some (Pending op) -> - return (Sc_rollup_services.Pending_injection op) - | Some (Injected {op; oph; op_index}) -> - return - (Sc_rollup_services.Injected - {op = op.operation; oph; op_index}) - | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( - let* finalized, cemented = - inbox_info_of_level node_ctxt l1_level - in - let commitment_level = - commitment_level_of_inbox_level node_ctxt l1_level - in - match commitment_level with - | None -> - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some commitment_level -> ( - let* block = - Node_context.find_l2_block_by_level - node_ctxt - (Alpha_context.Raw_level.to_int32 commitment_level) - in - match block with - | None -> - (* Commitment not computed yet for inbox *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some block -> ( - let commitment_hash = - WithExceptions.Option.get - ~loc:__LOC__ - block.header.commitment_hash - in - (* Commitment computed *) - let* published_at = - Node_context.commitment_published_at_level - node_ctxt - commitment_hash - in - match published_at with - | None | Some {published_at_level = None; _} -> - (* Commitment not published yet *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some - { - first_published_at_level; - published_at_level = Some published_at_level; - } -> - (* Commitment published *) - let* commitment = - Node_context.get_commitment - node_ctxt - commitment_hash - in - return - (Sc_rollup_services.Committed - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - commitment; - commitment_hash; - first_published_at_level; - published_at_level; - })))))) - in - - return status - -let register (node_ctxt : _ Node_context.t) = - let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in - List.fold_left - (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) - Tezos_rpc.Directory.empty - [ - Global_directory.build_directory; - Local_directory.build_directory; - Block_directory.build_directory; - Proof_helpers_directory.build_directory; - Outbox_directory.build_directory; - PVM.build_directory; - ] let start node_ctxt configuration = let open Lwt_result_syntax in @@ -523,7 +35,7 @@ let start node_ctxt configuration = let host = Ipaddr.V6.to_string rpc_addr in let node = `TCP (`Port rpc_port) in let acl = RPC_server.Acl.allow_all in - let dir = register node_ctxt in + let dir = RPC_directory.directory node_ctxt in let server = RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types in diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.mli index 6ce3fd2f3ec98de44ba3d2e209aaf903e50a1b0c..a7bcde269132a8713231c8dadbeb6283186d6b68 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/RPC_server.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 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"),*) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/batcher.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/batcher.ml index fccf828b61bb86d458e2de869bdaa3bc6650fdd9..e2eed2c345ebadf904558d3b09e0f812493f27e9 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/batcher.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/batcher.ml @@ -370,7 +370,7 @@ let check_batcher_config Configuration.{max_batch_size; _} = protocol_max_batch_size | _ -> Ok () -let init conf ~signer node_ctxt = +let start conf ~signer node_ctxt = let open Lwt_result_syntax in let*? () = check_batcher_config conf in let node_ctxt = Node_context.readonly node_ctxt in @@ -379,6 +379,19 @@ let init conf ~signer node_ctxt = in Lwt.wakeup worker_waker worker +let init conf ~signer node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_batcher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start conf ~signer node_ctxt + (* This is a batcher worker for a single scoru *) let worker = lazy diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml index 003433875727dcb59567f51eab2e9534f085cd45..0320a9683f18fb5dd20f2caea286a5bea7d2d0aa 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/daemon.ml @@ -320,16 +320,18 @@ let previous_context (node_ctxt : _ Node_context.t) let classify_head (node_ctxt : _ Node_context.t) ?(predecessor : Layer1.header option) (head : Layer1.header) = let open Lwt_result_syntax in - if head.header.proto_level = node_ctxt.proto_level then + if head.header.proto_level = node_ctxt.current_protocol.proto_level then (* Same protocol as supported one, ok *) return `Supported_proto - else if head.header.proto_level = node_ctxt.proto_level + 1 then + else if head.header.proto_level = node_ctxt.current_protocol.proto_level + 1 + then let* predecessor = match predecessor with | Some p -> return p | None -> Node_context.get_predecessor_header node_ctxt head in - if predecessor.header.proto_level = node_ctxt.proto_level then + if predecessor.header.proto_level = node_ctxt.current_protocol.proto_level + then (* Migration block from supported protocol to the next one, ok *) return `Supported_migration else return `Unsupported_proto @@ -429,13 +431,7 @@ let rec process_head (daemon_components : (module Daemon_components.S)) } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.mark_finalized_level @@ -627,9 +623,10 @@ let run node_ctxt configuration { cctxt = (node_ctxt.cctxt :> Client_context.full); fee_parameters = configuration.fee_parameters; - minimal_block_delay = node_ctxt.protocol_constants.minimal_block_delay; + minimal_block_delay = + node_ctxt.current_protocol.constants.minimal_block_delay; delay_increment_per_round = - node_ctxt.protocol_constants.delay_increment_per_round; + node_ctxt.current_protocol.constants.delay_increment_per_round; } ~data_dir:node_ctxt.data_dir ~signers @@ -757,13 +754,7 @@ module Internal_for_tests = struct } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.save_l2_head node_ctxt l2_block in return l2_block @@ -824,6 +815,13 @@ let run configuration.sc_rollup_address) publisher and* kind = Layer1_helpers.get_kind cctxt configuration.sc_rollup_address in + let current_protocol = + { + Node_context.hash = Protocol.hash; + proto_level = predecessor.proto_level; + constants; + } + in let* node_ctxt = Node_context.init cctxt @@ -831,12 +829,11 @@ let run ?log_kernel_debug_file Read_write l1_ctxt - constants genesis_info ~lcc ~lpc kind - ~proto_level:predecessor.proto_level + current_protocol configuration in run node_ctxt configuration daemon_components diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/dal_slots_tracker.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/dal_slots_tracker.ml index e8f011e00c7faf12a61b25ef68af96ef69089d00..868e7a6692d2dd5a5ef772ba776b18ccd94a99b5 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/dal_slots_tracker.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/dal_slots_tracker.ml @@ -25,8 +25,6 @@ open Protocol open Alpha_context -module Lifted_protocol = Tezos_protocol_017_PtNairob_lifted.Lifted_protocol -module Block_services = Block_services.Make (Lifted_protocol) (Lifted_protocol) let ancestor_hash ~number_of_levels ({Node_context.genesis_info; _} as node_ctxt) head = @@ -69,7 +67,9 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = we reduce the lag to 1, then the slots header will never be confirmed. *) let open Lwt_result_syntax in - let lag = node_ctxt.Node_context.protocol_constants.dal.attestation_lag in + let lag = + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag + in (* we are downloading endorsemented for slots at level [level], so we need to download the data at level [level - lag]. *) @@ -127,22 +127,24 @@ let to_slot_index_list (constants : Rollup_constants.protocol_constants) bitset Use a shared storage between dal and rollup node to store slots data. *) -let download_and_save_slots - ({Node_context.protocol_constants; _} as node_context) ~current_block_hash - {published_block_hash; confirmed_slots_indexes} = +let download_and_save_slots (node_context : _ Node_context.t) + ~current_block_hash {published_block_hash; confirmed_slots_indexes} = let open Lwt_result_syntax in let*? all_slots = - Bitset.fill ~length:protocol_constants.dal.number_of_slots + Bitset.fill + ~length:node_context.current_protocol.constants.dal.number_of_slots |> Environment.wrap_tzresult in let*? not_confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants + @@ to_slot_index_list node_context.current_protocol.constants @@ Bitset.diff all_slots confirmed_slots_indexes in let*? confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants confirmed_slots_indexes + @@ to_slot_index_list + node_context.current_protocol.constants + confirmed_slots_indexes in (* The contents of each slot index are written to a different location on disk, therefore calls to store contents for different slot indexes can @@ -188,7 +190,7 @@ module Confirmed_slots_history = struct let*? relevant_slots_indexes = Environment.wrap_tzresult @@ to_slot_index_list - node_ctxt.Node_context.protocol_constants + node_ctxt.Node_context.current_protocol.constants confirmed_slots_indexes in List.map_ep @@ -220,7 +222,8 @@ module Confirmed_slots_history = struct let should_process_dal_slots node_ctxt block_level = let open Node_context in let lag = - Int32.of_int node_ctxt.Node_context.protocol_constants.dal.attestation_lag + Int32.of_int + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag in let block_level = Raw_level.to_int32 block_level in let genesis_level = node_ctxt.genesis_info.level in @@ -283,7 +286,7 @@ module Confirmed_slots_history = struct ~find ~default:(fun node_ctxt _block -> let num_slots = - node_ctxt.Node_context.protocol_constants.dal.number_of_slots + node_ctxt.Node_context.current_protocol.constants.dal.number_of_slots in (* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/3788 Put an accurate value for capacity. The value diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/dune b/src/proto_017_PtNairob/lib_sc_rollup_node/dune index 6eeca21991dfd29054f9689b4fe1d2a45e737406..00cf5b4771dc1295a5682e62fd43578d26f0af56 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/dune +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/dune @@ -37,7 +37,7 @@ aches aches-lwt octez-injector - octez-smart-rollup-node + octez-smart-rollup-node-lib tezos-scoru-wasm tezos-scoru-wasm-fast tezos-crypto-dal diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/fueled_pvm.ml index b56a7d48c8fccb0e6df2008e80a2bd59c16daff1..bb35157ecaeb38f4162a7469f75985381fefba1d 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/fueled_pvm.ml @@ -141,7 +141,7 @@ module Make_fueled (F : Fuel.S) : S with type fuel = F.t = struct let module PVM = (val Pvm.of_kind node_ctxt.kind) in let metadata = metadata node_ctxt in let dal_attestation_lag = - node_ctxt.protocol_constants.dal.attestation_lag + node_ctxt.current_protocol.constants.dal.attestation_lag in let reveal_builtins = Tezos_scoru_wasm.Builtins. diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml index 7715bd437a89c5ba5460476533caca9aa85cec4b..267bb98a99ae3d2e4976186cf9e51137bd1efe1c 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/inbox.ml @@ -214,7 +214,7 @@ let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) else let* inbox = Layer1_helpers.genesis_inbox - (new Protocol_client_context.wrap_full node_ctxt.cctxt) + node_ctxt.cctxt ~genesis_level:node_ctxt.genesis_info.level in let Octez_smart_rollup.Inbox.{hash = witness; _} = diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml index dee1f6fdb114975a20babde498d670a0ed0b255d..c4be2d729b1e6bcd36cecb450795483fc7839143 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.ml @@ -119,7 +119,8 @@ let transition_pvm node_ctxt ctxt predecessor Layer1.{hash = _; _} let*! () = Interpreter_event.transitioned_pvm inbox_level state_hash tick num_messages in - return (ctxt, num_messages, Z.to_int64 num_ticks, initial_tick) + return + (ctxt, num_messages, Z.to_int64 num_ticks, Sc_rollup.Tick.to_z initial_tick) (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) @@ -137,8 +138,8 @@ let process_head (node_ctxt : _ Node_context.t) ctxt else if head.Layer1.level = node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in let*! ctxt = Context.PVMState.set ctxt state in - return (ctxt, 0, 0L, Sc_rollup.Tick.initial) - else return (ctxt, 0, 0L, Sc_rollup.Tick.initial) + return (ctxt, 0, 0L, Z.zero) + else return (ctxt, 0, 0L, Z.zero) (** Returns the starting evaluation before the evaluation of the block. It contains the PVM state at the end of the execution of the previous block and diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli index 48cff9f6ceb5366c4bcdb7a69b922fd390102b24..124eaa96b53d4ba69484ba4dcc81688166aba0b2 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/interpreter.mli @@ -39,7 +39,7 @@ val process_head : predecessor:Layer1.header -> Layer1.header -> Octez_smart_rollup.Inbox.t * string list -> - ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t + ('a Context.t * int * int64 * Z.t) tzresult Lwt.t (** [state_of_tick node_ctxt ?start_state tick level] returns [Some (state, hash)] for a given [tick] if this [tick] happened before [level]. Otherwise, diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.ml index f15e34e99d15fe5a8c6514b1f2510f0126525ad9..254e41b733b4ac425a75404cf4e4484a8586c61b 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.ml @@ -53,9 +53,12 @@ let fetch_tezos_block l1_ctxt hash = let prefetch_tezos_blocks = Layer1.prefetch_tezos_blocks fetch extract_header -let get_last_cemented_commitment (cctxt : Protocol_client_context.full) - rollup_address : Node_context.lcc tzresult Lwt.t = +let get_last_cemented_commitment (cctxt : #Client_context.full) rollup_address : + Node_context.lcc tzresult Lwt.t = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ commitment, level = Plugin.RPC.Sc_rollup.last_cemented_commitment_hash_with_level @@ -69,9 +72,12 @@ let get_last_cemented_commitment (cctxt : Protocol_client_context.full) level = Protocol.Alpha_context.Raw_level.to_int32 level; } -let get_last_published_commitment (cctxt : Protocol_client_context.full) - rollup_address operator = +let get_last_published_commitment (cctxt : #Client_context.full) rollup_address + operator = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let*! res = Plugin.RPC.Sc_rollup.staked_on_commitment @@ -98,6 +104,9 @@ let get_last_published_commitment (cctxt : Protocol_client_context.full) let get_kind cctxt rollup_address = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ kind = RPC.Sc_rollup.kind cctxt (cctxt#chain, cctxt#block) rollup_address () @@ -106,6 +115,9 @@ let get_kind cctxt rollup_address = let genesis_inbox cctxt ~genesis_level = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ inbox = Plugin.RPC.Sc_rollup.inbox cctxt (cctxt#chain, `Level genesis_level) in @@ -142,6 +154,9 @@ let constants_of_parametric *) let retrieve_constants ?(block = `Head 0) cctxt = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {parametric; _} = Protocol.Constants_services.all cctxt (cctxt#chain, block) in @@ -150,6 +165,9 @@ let retrieve_constants ?(block = `Head 0) cctxt = let retrieve_genesis_info cctxt rollup_address = let open Lwt_result_syntax in let open Protocol.Alpha_context in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {level; commitment_hash} = RPC.Sc_rollup.genesis_info cctxt diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.mli b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.mli index 605c80fcbdd0757210fabb5b06a5ca6edbb02154..c82972f95ba0d8f23472993bc7451a0655999f68 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.mli +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/layer1_helpers.mli @@ -40,19 +40,18 @@ val fetch_tezos_block : val prefetch_tezos_blocks : t -> head list -> unit val get_last_cemented_commitment : - Protocol_client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t val get_last_published_commitment : - Protocol_client_context.full -> + #Client_context.full -> Address.t -> Signature.public_key_hash -> Commitment.t option tzresult Lwt.t -val get_kind : - Protocol_client_context.full -> Address.t -> Kind.t tzresult Lwt.t +val get_kind : #Client_context.full -> Address.t -> Kind.t tzresult Lwt.t val genesis_inbox : - Protocol_client_context.full -> + #Client_context.full -> genesis_level:int32 -> Octez_smart_rollup.Inbox.t tzresult Lwt.t @@ -64,10 +63,8 @@ val constants_of_parametric : (** Retrieve protocol agnotic constants for the head of the chain. *) val retrieve_constants : ?block:Block_services.block -> - Protocol_client_context.full -> + #Client_context.full -> Rollup_constants.protocol_constants tzresult Lwt.t val retrieve_genesis_info : - Protocol_client_context.full -> - Address.t -> - Node_context.genesis_info tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.genesis_info tzresult Lwt.t diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/publisher.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/publisher.ml index eb26ecbd21ce1cefcaf3e8a7f75804a585c6ce96..3305d3a16b5c26c1271b8bfeb8a1b8ce36992411 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/publisher.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/publisher.ml @@ -72,11 +72,12 @@ let sub_level level decrement = if r < 0l then None else Some r let sc_rollup_commitment_period node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup + node_ctxt.Node_context.current_protocol.constants.sc_rollup .commitment_period_in_blocks let sc_rollup_challenge_window node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup.challenge_window_in_blocks + node_ctxt.Node_context.current_protocol.constants.sc_rollup + .challenge_window_in_blocks let next_commitment_level node_ctxt last_commitment_level = add_level last_commitment_level (sc_rollup_commitment_period node_ctxt) @@ -478,13 +479,26 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Commitment_event.starting () in let node_ctxt = Node_context.readonly node_ctxt in let+ worker = Worker.launch table () {node_ctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_publisher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a publisher worker for a single scoru *) let worker = lazy diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/refutation_coordinator.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/refutation_coordinator.ml index 5762f752126c9814d1d882e6a540ca5c81085dec..627b7399a5e91e1ed02a84efdca2cc0ab74abdf1 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/refutation_coordinator.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/refutation_coordinator.ml @@ -201,7 +201,7 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Refutation_game_event.Coordinator.starting () in let cctxt = @@ -210,6 +210,19 @@ let init node_ctxt = let+ worker = Worker.launch table () {node_ctxt; cctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_refutation_coordinator; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a refutation coordinator for a single scoru *) let worker = lazy diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/rollup_node_plugin.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/rollup_node_plugin.ml new file mode 100644 index 0000000000000000000000000000000000000000..6d1642bf8f58d15e8da5aaa7e91613cda1a8d4b5 --- /dev/null +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/rollup_node_plugin.ml @@ -0,0 +1,45 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 Plugin : Protocol_plugin_sig.S = struct + let protocol = Protocol.hash + + module RPC_directory = RPC_directory + module Dal_slots_tracker = Dal_slots_tracker + module Inbox = Inbox + module Interpreter = Interpreter + module Publisher = Publisher + module Refutation_coordinator = Refutation_coordinator + module Batcher = Batcher + module Layer1_helpers = Layer1_helpers + + module L1_processing = struct + let check_pvm_initial_state_hash = Daemon.check_initial_state_hash + + let process_l1_block_operations = Daemon.process_l1_block_operations + end +end + +let () = Protocol_plugins.register (module Plugin) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml index 6b669b98306a1642c001f60616f2613bb5e711cd..ceed3e3bcc559290447f60338be739a18e9b296d 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/test/helpers/helpers.ml @@ -142,10 +142,13 @@ let initialize_node_context ?(constants = default_constants) kind ~boot_sector = new Protocol_client_context.wrap_full (new Faked_client_context.unix_faked ~base_dir ~filesystem) in + let current_protocol = + {Node_context.hash = Protocol.hash; proto_level = 0; constants} + in let* ctxt = Node_context.Internal_for_tests.create_node_context cctxt - constants + current_protocol ~data_dir kind in diff --git a/src/proto_018_Proxford/bin_sc_rollup_node/dune b/src/proto_018_Proxford/bin_sc_rollup_node/dune index ed35d2a0a5e711202faba7042634d39d91ea33d2..4c897d4884539adcc945b700c91d875ddd494c3b 100644 --- a/src/proto_018_Proxford/bin_sc_rollup_node/dune +++ b/src/proto_018_Proxford/bin_sc_rollup_node/dune @@ -15,7 +15,7 @@ tezos-client-base-unix tezos-client-commands tezos-client-018-Proxford - octez-smart-rollup-node + octez-smart-rollup-node-lib octez_smart_rollup_node_Proxford) (link_flags (:standard) diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/RPC_directory.ml b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_directory.ml new file mode 100644 index 0000000000000000000000000000000000000000..67b128d608a7571afee0328ed05d48075711009a --- /dev/null +++ b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_directory.ml @@ -0,0 +1,516 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2022-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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +open RPC_directory_helpers +open Protocol + +let get_head_hash_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) + res + +let get_head_level_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res + +module Slot_pages_map = struct + open Protocol + open Alpha_context + include Map.Make (Dal.Slot_index) +end + +let get_dal_processed_slots node_ctxt block = + Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block + +module Global_directory = Make_directory (struct + include Sc_rollup_services.Global + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Proof_helpers_directory = Make_directory (struct + include Sc_rollup_services.Global.Helpers + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type context = Node_context.rw + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type subcontext = Node_context.rw + + let context_of_prefix node_ctxt () = return node_ctxt +end) + +module Local_directory = Make_directory (struct + include Sc_rollup_services.Local + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Block_directory = Make_directory (struct + include Sc_rollup_services.Global.Block + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t + + let context_of_prefix node_ctxt (((), block) : prefix) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block) +end) + +module Outbox_directory = Make_directory (struct + include Sc_rollup_services.Global.Block.Outbox + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t + + let context_of_prefix node_ctxt (((), block), level) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block, level) +end) + +module Common = struct + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.block + @@ fun (node_ctxt, block) () () -> + Node_context.get_full_l2_block node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.num_messages + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* l2_block = Node_context.get_l2_block node_ctxt block in + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness + in + Z.of_int num_messages + + let () = + Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address + @@ fun node_ctxt () () -> + return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_head + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_level + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.hash + @@ fun (_node_ctxt, block) () () -> return block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.level + @@ fun (node_ctxt, block) () () -> + Node_context.level_of_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.inbox + @@ fun (node_ctxt, block) () () -> + Node_context.get_inbox_by_block_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ l2_block = Node_context.get_l2_block node_ctxt block in + Z.of_int64 l2_block.num_ticks +end + +let get_state (node_ctxt : _ Node_context.t) block_hash = + let open Lwt_result_syntax in + let* ctxt = Node_context.checkout_context node_ctxt block_hash in + let*! state = Context.PVMState.find ctxt in + match state with None -> failwith "No state" | Some state -> return state + +let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages + ~insight_requests messages = + let open Lwt_result_syntax in + let open Alpha_context in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let reveal_map = + match reveal_pages with + | Some pages -> + let map = + List.fold_left + (fun map page -> + let hash = + Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) + in + Sc_rollup_reveal_hash.Map.add hash page map) + Sc_rollup_reveal_hash.Map.empty + pages + in + Some map + | None -> None + in + let* level = Node_context.level_of_hash node_ctxt block in + let* sim = + Simulation.start_simulation + node_ctxt + ~reveal_map + Layer1.{hash = block; level} + in + let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in + let* {state; inbox_level; _}, num_ticks_end = + Simulation.end_simulation node_ctxt sim + in + let*! insights = + List.map_p + (function + | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key + | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) + insight_requests + in + let num_ticks = Z.(num_ticks_0 + num_ticks_end) in + let level = Raw_level.of_int32_exn inbox_level in + let*! outbox = PVM.get_outbox level state in + let output = + List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox + in + let*! state_hash = PVM.state_hash state in + let*! status = PVM.get_status state in + let status = PVM.string_of_status status in + return + Sc_rollup_services. + {state_hash; status; output; inbox_level; num_ticks; insights} + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! tick = PVM.get_tick state in + return tick + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_hash + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! hash = PVM.state_hash state in + return hash + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! current_level = PVM.get_current_level state in + return current_level + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_value + @@ fun (node_ctxt, block) {key} () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let path = String.split_on_char '/' key in + let*! value = Context.PVMState.lookup state path in + match value with + | None -> failwith "No such key in PVM state" + | Some value -> + Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; + return value + +let () = + Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | None -> return_none + | Some head -> + let commitment_hash = + Sc_rollup_block.most_recent_commitment head.header + in + let+ commitment = + Node_context.find_commitment node_ctxt commitment_hash + in + Option.map (fun c -> (c, commitment_hash)) commitment + +let () = + Local_directory.register0 Sc_rollup_services.Local.last_published_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + match Reference.get node_ctxt.lpc with + | None -> return_none + | Some commitment -> + let hash = Octez_smart_rollup.Commitment.hash commitment in + (* The corresponding level in Store.Commitments.published_at_level is + available only when the commitment has been published and included + in a block. *) + let* published_at_level_info = + Node_context.commitment_published_at_level node_ctxt hash + in + let first_published, published = + match published_at_level_info with + | None -> (None, None) + | Some {first_published_at_level; published_at_level} -> + (Some first_published_at_level, published_at_level) + in + return_some (commitment, hash, first_published, published) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.status + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! status = PVM.get_status state in + return (PVM.string_of_status status) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ slots = + Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block + in + List.rev_map Sc_rollup_proto_types.Dal.Slot_header.of_octez slots |> List.rev + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots + @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block + +let () = + Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages + @@ fun (node_ctxt, block, outbox_level) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! outbox = PVM.get_outbox outbox_level state in + return outbox + +let () = + Proof_helpers_directory.register0 + Sc_rollup_services.Global.Helpers.outbox_proof + @@ fun node_ctxt output () -> + let open Lwt_result_syntax in + let+ commitment, proof = Outbox.proof_of_output node_ctxt output in + (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.simulate + @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> + simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.injection + @@ fun _node_ctxt () messages -> Batcher.register_messages messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.batcher_queue + @@ fun _node_ctxt () () -> + let open Lwt_result_syntax in + let*? queue = Batcher.get_queue () in + return queue + +(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level + of the commitment which should include the inbox of level + [inbox_level]. + + It is computed with the following formula: + {v + commitment_level(inbox_level) = + last_commitment - + ((last_commitment - inbox_level) / commitment_period + * commitment_period) + v} + *) +let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = + let open Alpha_context in + let open Option_syntax in + let+ last_published_commitment = Reference.get node_ctxt.lpc in + let commitment_period = + Int32.of_int + node_ctxt.current_protocol.constants.sc_rollup.commitment_period_in_blocks + in + let last_published = last_published_commitment.inbox_level in + let open Int32 in + div (sub last_published inbox_level) commitment_period + |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn + +let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = + let open Lwt_result_syntax in + let+ finalized_level = Node_context.get_finalized_level node_ctxt in + let finalized = Compare.Int32.(inbox_level <= finalized_level) in + let lcc = Reference.get node_ctxt.lcc in + let cemented = Compare.Int32.(inbox_level <= lcc.level) in + (finalized, cemented) + +let () = + Local_directory.register1 Sc_rollup_services.Local.batcher_message + @@ fun node_ctxt hash () () -> + let open Lwt_result_syntax in + let*? batch_status = Batcher.message_status hash in + let* status = + match batch_status with + | None -> return (None, Sc_rollup_services.Unknown) + | Some (batch_status, msg) -> ( + let return status = return (Some msg, status) in + match batch_status with + | Pending_batch -> return Sc_rollup_services.Pending_batch + | Batched l1_hash -> ( + match Injector.operation_status l1_hash with + | None -> return Sc_rollup_services.Unknown + | Some (Pending op) -> + return (Sc_rollup_services.Pending_injection op) + | Some (Injected {op; oph; op_index}) -> + return + (Sc_rollup_services.Injected + {op = op.operation; oph; op_index}) + | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( + let* finalized, cemented = + inbox_info_of_level node_ctxt l1_level + in + let commitment_level = + commitment_level_of_inbox_level node_ctxt l1_level + in + match commitment_level with + | None -> + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some commitment_level -> ( + let* block = + Node_context.find_l2_block_by_level + node_ctxt + (Alpha_context.Raw_level.to_int32 commitment_level) + in + match block with + | None -> + (* Commitment not computed yet for inbox *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some block -> ( + let commitment_hash = + WithExceptions.Option.get + ~loc:__LOC__ + block.header.commitment_hash + in + (* Commitment computed *) + let* published_at = + Node_context.commitment_published_at_level + node_ctxt + commitment_hash + in + match published_at with + | None | Some {published_at_level = None; _} -> + (* Commitment not published yet *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some + { + first_published_at_level; + published_at_level = Some published_at_level; + } -> + (* Commitment published *) + let* commitment = + Node_context.get_commitment + node_ctxt + commitment_hash + in + return + (Sc_rollup_services.Committed + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + commitment; + commitment_hash; + first_published_at_level; + published_at_level; + })))))) + in + + return status + +let directory (node_ctxt : _ Node_context.t) = + let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in + List.fold_left + (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) + Tezos_rpc.Directory.empty + [ + Global_directory.build_directory; + Local_directory.build_directory; + Block_directory.build_directory; + Proof_helpers_directory.build_directory; + Outbox_directory.build_directory; + PVM.build_directory; + ] diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/RPC_directory.mli b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_directory.mli new file mode 100644 index 0000000000000000000000000000000000000000..206e38547b0c0036ae3003795244070e7063cf71 --- /dev/null +++ b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_directory.mli @@ -0,0 +1,27 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +(** The RPC directory for this rollup node. *) +val directory : Node_context.rw -> unit Tezos_rpc.Directory.t diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.ml b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.ml index 6777c7e83d39edc98ee24941407aff192c47e7a7..bbc8af4ea919b8814d58a1e896af439c61b86208 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2022 Nomadic Labs, *) (* Copyright (c) 2022-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"),*) @@ -26,495 +27,6 @@ open Tezos_rpc_http open Tezos_rpc_http_server -open RPC_directory_helpers -open Protocol - -let get_head_hash_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map - (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) - res - -let get_head_level_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res - -module Slot_pages_map = struct - open Protocol - open Alpha_context - include Map.Make (Dal.Slot_index) -end - -let get_dal_processed_slots node_ctxt block = - Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block - -module Global_directory = Make_directory (struct - include Sc_rollup_services.Global - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Proof_helpers_directory = Make_directory (struct - include Sc_rollup_services.Global.Helpers - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type context = Node_context.rw - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type subcontext = Node_context.rw - - let context_of_prefix node_ctxt () = return node_ctxt -end) - -module Local_directory = Make_directory (struct - include Sc_rollup_services.Local - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Block_directory = Make_directory (struct - include Sc_rollup_services.Global.Block - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t - - let context_of_prefix node_ctxt (((), block) : prefix) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block) -end) - -module Outbox_directory = Make_directory (struct - include Sc_rollup_services.Global.Block.Outbox - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t - - let context_of_prefix node_ctxt (((), block), level) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block, level) -end) - -module Common = struct - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.block - @@ fun (node_ctxt, block) () () -> - Node_context.get_full_l2_block node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.num_messages - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* l2_block = Node_context.get_l2_block node_ctxt block in - let+ num_messages = - Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness - in - Z.of_int num_messages - - let () = - Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address - @@ fun node_ctxt () () -> - return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.hash - @@ fun (_node_ctxt, block) () () -> return block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.level - @@ fun (node_ctxt, block) () () -> - Node_context.level_of_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.inbox - @@ fun (node_ctxt, block) () () -> - Node_context.get_inbox_by_block_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ l2_block = Node_context.get_l2_block node_ctxt block in - Z.of_int64 l2_block.num_ticks -end - -let get_state (node_ctxt : _ Node_context.t) block_hash = - let open Lwt_result_syntax in - let* ctxt = Node_context.checkout_context node_ctxt block_hash in - let*! state = Context.PVMState.find ctxt in - match state with None -> failwith "No state" | Some state -> return state - -let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages - ~insight_requests messages = - let open Lwt_result_syntax in - let open Alpha_context in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let reveal_map = - match reveal_pages with - | Some pages -> - let map = - List.fold_left - (fun map page -> - let hash = - Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) - in - Sc_rollup_reveal_hash.Map.add hash page map) - Sc_rollup_reveal_hash.Map.empty - pages - in - Some map - | None -> None - in - let* level = Node_context.level_of_hash node_ctxt block in - let* sim = - Simulation.start_simulation - node_ctxt - ~reveal_map - Layer1.{hash = block; level} - in - let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in - let* {state; inbox_level; _}, num_ticks_end = - Simulation.end_simulation node_ctxt sim - in - let*! insights = - List.map_p - (function - | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key - | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) - insight_requests - in - let num_ticks = Z.(num_ticks_0 + num_ticks_end) in - let level = Raw_level.of_int32_exn inbox_level in - let*! outbox = PVM.get_outbox level state in - let output = - List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox - in - let*! state_hash = PVM.state_hash state in - let*! status = PVM.get_status state in - let status = PVM.string_of_status status in - return - Sc_rollup_services. - {state_hash; status; output; inbox_level; num_ticks; insights} - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! tick = PVM.get_tick state in - return tick - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_hash - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! hash = PVM.state_hash state in - return hash - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! current_level = PVM.get_current_level state in - return current_level - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_value - @@ fun (node_ctxt, block) {key} () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let path = String.split_on_char '/' key in - let*! value = Context.PVMState.lookup state path in - match value with - | None -> failwith "No such key in PVM state" - | Some value -> - Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; - return value - -let () = - Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - let* head = Node_context.last_processed_head_opt node_ctxt in - match head with - | None -> return_none - | Some head -> - let commitment_hash = - Sc_rollup_block.most_recent_commitment head.header - in - let+ commitment = - Node_context.find_commitment node_ctxt commitment_hash - in - Option.map (fun c -> (c, commitment_hash)) commitment - -let () = - Local_directory.register0 Sc_rollup_services.Local.last_published_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - match Reference.get node_ctxt.lpc with - | None -> return_none - | Some commitment -> - let hash = Octez_smart_rollup.Commitment.hash commitment in - (* The corresponding level in Store.Commitments.published_at_level is - available only when the commitment has been published and included - in a block. *) - let* published_at_level_info = - Node_context.commitment_published_at_level node_ctxt hash - in - let first_published, published = - match published_at_level_info with - | None -> (None, None) - | Some {first_published_at_level; published_at_level} -> - (Some first_published_at_level, published_at_level) - in - return_some (commitment, hash, first_published, published) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.status - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! status = PVM.get_status state in - return (PVM.string_of_status status) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ slots = - Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block - in - List.rev_map Sc_rollup_proto_types.Dal.Slot_header.of_octez slots |> List.rev - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots - @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block - -let () = - Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages - @@ fun (node_ctxt, block, outbox_level) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! outbox = PVM.get_outbox outbox_level state in - return outbox - -let () = - Proof_helpers_directory.register0 - Sc_rollup_services.Global.Helpers.outbox_proof - @@ fun node_ctxt output () -> - let open Lwt_result_syntax in - let+ commitment, proof = Outbox.proof_of_output node_ctxt output in - (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.simulate - @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> - simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.injection - @@ fun _node_ctxt () messages -> Batcher.register_messages messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.batcher_queue - @@ fun _node_ctxt () () -> - let open Lwt_result_syntax in - let*? queue = Batcher.get_queue () in - return queue - -(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level - of the commitment which should include the inbox of level - [inbox_level]. - - It is computed with the following formula: - {v - commitment_level(inbox_level) = - last_commitment - - ((last_commitment - inbox_level) / commitment_period - * commitment_period) - v} - *) -let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = - let open Alpha_context in - let open Option_syntax in - let+ last_published_commitment = Reference.get node_ctxt.lpc in - let commitment_period = - Int32.of_int - node_ctxt.protocol_constants.sc_rollup.commitment_period_in_blocks - in - let last_published = last_published_commitment.inbox_level in - let open Int32 in - div (sub last_published inbox_level) commitment_period - |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn - -let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = - let open Lwt_result_syntax in - let+ finalized_level = Node_context.get_finalized_level node_ctxt in - let finalized = Compare.Int32.(inbox_level <= finalized_level) in - let lcc = Reference.get node_ctxt.lcc in - let cemented = Compare.Int32.(inbox_level <= lcc.level) in - (finalized, cemented) - -let () = - Local_directory.register1 Sc_rollup_services.Local.batcher_message - @@ fun node_ctxt hash () () -> - let open Lwt_result_syntax in - let*? batch_status = Batcher.message_status hash in - let* status = - match batch_status with - | None -> return (None, Sc_rollup_services.Unknown) - | Some (batch_status, msg) -> ( - let return status = return (Some msg, status) in - match batch_status with - | Pending_batch -> return Sc_rollup_services.Pending_batch - | Batched l1_hash -> ( - match Injector.operation_status l1_hash with - | None -> return Sc_rollup_services.Unknown - | Some (Pending op) -> - return (Sc_rollup_services.Pending_injection op) - | Some (Injected {op; oph; op_index}) -> - return - (Sc_rollup_services.Injected - {op = op.operation; oph; op_index}) - | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( - let* finalized, cemented = - inbox_info_of_level node_ctxt l1_level - in - let commitment_level = - commitment_level_of_inbox_level node_ctxt l1_level - in - match commitment_level with - | None -> - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some commitment_level -> ( - let* block = - Node_context.find_l2_block_by_level - node_ctxt - (Alpha_context.Raw_level.to_int32 commitment_level) - in - match block with - | None -> - (* Commitment not computed yet for inbox *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some block -> ( - let commitment_hash = - WithExceptions.Option.get - ~loc:__LOC__ - block.header.commitment_hash - in - (* Commitment computed *) - let* published_at = - Node_context.commitment_published_at_level - node_ctxt - commitment_hash - in - match published_at with - | None | Some {published_at_level = None; _} -> - (* Commitment not published yet *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some - { - first_published_at_level; - published_at_level = Some published_at_level; - } -> - (* Commitment published *) - let* commitment = - Node_context.get_commitment - node_ctxt - commitment_hash - in - return - (Sc_rollup_services.Committed - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - commitment; - commitment_hash; - first_published_at_level; - published_at_level; - })))))) - in - - return status - -let register (node_ctxt : _ Node_context.t) = - let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in - List.fold_left - (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) - Tezos_rpc.Directory.empty - [ - Global_directory.build_directory; - Local_directory.build_directory; - Block_directory.build_directory; - Proof_helpers_directory.build_directory; - Outbox_directory.build_directory; - PVM.build_directory; - ] let start node_ctxt configuration = let open Lwt_result_syntax in @@ -523,7 +35,7 @@ let start node_ctxt configuration = let host = Ipaddr.V6.to_string rpc_addr in let node = `TCP (`Port rpc_port) in let acl = RPC_server.Acl.allow_all in - let dir = register node_ctxt in + let dir = RPC_directory.directory node_ctxt in let server = RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types in diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.mli b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.mli index 425bba0d9e7c269d67799eaa28b53ae9b9214e4f..48149ab632740020c623b333f6a3411298fd5f73 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.mli +++ b/src/proto_018_Proxford/lib_sc_rollup_node/RPC_server.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 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"),*) diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/batcher.ml b/src/proto_018_Proxford/lib_sc_rollup_node/batcher.ml index fccf828b61bb86d458e2de869bdaa3bc6650fdd9..e2eed2c345ebadf904558d3b09e0f812493f27e9 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/batcher.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/batcher.ml @@ -370,7 +370,7 @@ let check_batcher_config Configuration.{max_batch_size; _} = protocol_max_batch_size | _ -> Ok () -let init conf ~signer node_ctxt = +let start conf ~signer node_ctxt = let open Lwt_result_syntax in let*? () = check_batcher_config conf in let node_ctxt = Node_context.readonly node_ctxt in @@ -379,6 +379,19 @@ let init conf ~signer node_ctxt = in Lwt.wakeup worker_waker worker +let init conf ~signer node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_batcher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start conf ~signer node_ctxt + (* This is a batcher worker for a single scoru *) let worker = lazy diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/daemon.ml b/src/proto_018_Proxford/lib_sc_rollup_node/daemon.ml index a5e7d3e9158559370115e8d60d36c4687122e865..47e7201f25a0fa3d5bc40b2bb94d17a79a20b9fe 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/daemon.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/daemon.ml @@ -319,16 +319,18 @@ let previous_context (node_ctxt : _ Node_context.t) let classify_head (node_ctxt : _ Node_context.t) ?(predecessor : Layer1.header option) (head : Layer1.header) = let open Lwt_result_syntax in - if head.header.proto_level = node_ctxt.proto_level then + if head.header.proto_level = node_ctxt.current_protocol.proto_level then (* Same protocol as supported one, ok *) return `Supported_proto - else if head.header.proto_level = node_ctxt.proto_level + 1 then + else if head.header.proto_level = node_ctxt.current_protocol.proto_level + 1 + then let* predecessor = match predecessor with | Some p -> return p | None -> Node_context.get_predecessor_header node_ctxt head in - if predecessor.header.proto_level = node_ctxt.proto_level then + if predecessor.header.proto_level = node_ctxt.current_protocol.proto_level + then (* Migration block from supported protocol to the next one, ok *) return `Supported_migration else return `Unsupported_proto @@ -428,13 +430,7 @@ let rec process_head (daemon_components : (module Daemon_components.S)) } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.mark_finalized_level @@ -627,9 +623,10 @@ let run node_ctxt configuration { cctxt = (node_ctxt.cctxt :> Client_context.full); fee_parameters = configuration.fee_parameters; - minimal_block_delay = node_ctxt.protocol_constants.minimal_block_delay; + minimal_block_delay = + node_ctxt.current_protocol.constants.minimal_block_delay; delay_increment_per_round = - node_ctxt.protocol_constants.delay_increment_per_round; + node_ctxt.current_protocol.constants.delay_increment_per_round; } ~data_dir:node_ctxt.data_dir ~signers @@ -758,13 +755,7 @@ module Internal_for_tests = struct } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.save_l2_head node_ctxt l2_block in return l2_block @@ -825,6 +816,13 @@ let run configuration.sc_rollup_address) publisher and* kind = Layer1_helpers.get_kind cctxt configuration.sc_rollup_address in + let current_protocol = + { + Node_context.hash = Protocol.hash; + proto_level = predecessor.proto_level; + constants; + } + in let* node_ctxt = Node_context.init cctxt @@ -832,12 +830,11 @@ let run ?log_kernel_debug_file Read_write l1_ctxt - constants genesis_info ~lcc ~lpc kind - ~proto_level:predecessor.proto_level + current_protocol configuration in run node_ctxt configuration daemon_components diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/dal_slots_tracker.ml b/src/proto_018_Proxford/lib_sc_rollup_node/dal_slots_tracker.ml index 45da0efc367c60c1b0301bbc4de7b5ab23cb2484..6cb095c0819709f49a82f686d8e2fc4f2699d2ee 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/dal_slots_tracker.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/dal_slots_tracker.ml @@ -68,7 +68,9 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = we reduce the lag to 1, then the slots header will never be confirmed. *) let open Lwt_result_syntax in - let lag = node_ctxt.Node_context.protocol_constants.dal.attestation_lag in + let lag = + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag + in (* we are downloading endorsemented for slots at level [level], so we need to download the data at level [level - lag]. *) @@ -126,22 +128,24 @@ let to_slot_index_list (constants : Rollup_constants.protocol_constants) bitset Use a shared storage between dal and rollup node to store slots data. *) -let download_and_save_slots - ({Node_context.protocol_constants; _} as node_context) ~current_block_hash - {published_block_hash; confirmed_slots_indexes} = +let download_and_save_slots (node_context : _ Node_context.t) + ~current_block_hash {published_block_hash; confirmed_slots_indexes} = let open Lwt_result_syntax in let*? all_slots = - Bitset.fill ~length:protocol_constants.dal.number_of_slots + Bitset.fill + ~length:node_context.current_protocol.constants.dal.number_of_slots |> Environment.wrap_tzresult in let*? not_confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants + @@ to_slot_index_list node_context.current_protocol.constants @@ Bitset.diff all_slots confirmed_slots_indexes in let*? confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants confirmed_slots_indexes + @@ to_slot_index_list + node_context.current_protocol.constants + confirmed_slots_indexes in (* The contents of each slot index are written to a different location on disk, therefore calls to store contents for different slot indexes can @@ -187,7 +191,7 @@ module Confirmed_slots_history = struct let*? relevant_slots_indexes = Environment.wrap_tzresult @@ to_slot_index_list - node_ctxt.Node_context.protocol_constants + node_ctxt.Node_context.current_protocol.constants confirmed_slots_indexes in List.map_ep @@ -219,7 +223,8 @@ module Confirmed_slots_history = struct let should_process_dal_slots node_ctxt block_level = let open Node_context in let lag = - Int32.of_int node_ctxt.Node_context.protocol_constants.dal.attestation_lag + Int32.of_int + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag in let block_level = Raw_level.to_int32 block_level in let genesis_level = node_ctxt.genesis_info.level in @@ -282,7 +287,7 @@ module Confirmed_slots_history = struct ~find ~default:(fun node_ctxt _block -> let num_slots = - node_ctxt.Node_context.protocol_constants.dal.number_of_slots + node_ctxt.Node_context.current_protocol.constants.dal.number_of_slots in (* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/3788 Put an accurate value for capacity. The value diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/dune b/src/proto_018_Proxford/lib_sc_rollup_node/dune index f5cb1fe4d5e12ae1d9e944c8bf9bbd2e4b82016d..e3750e9a4768476531870834e09fa014d747da63 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/dune +++ b/src/proto_018_Proxford/lib_sc_rollup_node/dune @@ -40,7 +40,7 @@ aches aches-lwt octez-injector - octez-smart-rollup-node + octez-smart-rollup-node-lib tezos-scoru-wasm tezos-scoru-wasm-fast tezos-crypto-dal diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_018_Proxford/lib_sc_rollup_node/fueled_pvm.ml index c2309035fe9c577ac30745c0406c6314772959eb..370778a2c41f26e3be6b5a0e1ef59fce7e527bd2 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/fueled_pvm.ml @@ -141,7 +141,7 @@ module Make_fueled (F : Fuel.S) : S with type fuel = F.t = struct let module PVM = (val Pvm.of_kind node_ctxt.kind) in let metadata = metadata node_ctxt in let dal_attestation_lag = - node_ctxt.protocol_constants.dal.attestation_lag + node_ctxt.current_protocol.constants.dal.attestation_lag in let reveal_builtins = Tezos_scoru_wasm.Builtins. diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/inbox.ml b/src/proto_018_Proxford/lib_sc_rollup_node/inbox.ml index 621a72a12b023b360cad9b06347c89c8fc11f038..14c5137459d5743498b6933309eb1cfd2bb9a53f 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/inbox.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/inbox.ml @@ -214,7 +214,7 @@ let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) else let* inbox = Layer1_helpers.genesis_inbox - (new Protocol_client_context.wrap_full node_ctxt.cctxt) + node_ctxt.cctxt ~genesis_level:node_ctxt.genesis_info.level in let Octez_smart_rollup.Inbox.{hash = witness; _} = diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.ml b/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.ml index b306b2badaea47c610bfbc27425a7be1c88036a0..59e4ea454b82db73d6fbced17bf4a68d6b37f98c 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.ml @@ -137,7 +137,8 @@ let transition_pvm node_ctxt ctxt predecessor Layer1.{hash = _; _} let*! () = Interpreter_event.transitioned_pvm inbox_level state_hash tick num_messages in - return (ctxt, num_messages, Z.to_int64 num_ticks, initial_tick) + return + (ctxt, num_messages, Z.to_int64 num_ticks, Sc_rollup.Tick.to_z initial_tick) (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) @@ -155,8 +156,8 @@ let process_head (node_ctxt : _ Node_context.t) ctxt else if head.Layer1.level = node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in let*! ctxt = Context.PVMState.set ctxt state in - return (ctxt, 0, 0L, Sc_rollup.Tick.initial) - else return (ctxt, 0, 0L, Sc_rollup.Tick.initial) + return (ctxt, 0, 0L, Z.zero) + else return (ctxt, 0, 0L, Z.zero) (** Returns the starting evaluation before the evaluation of the block. It contains the PVM state at the end of the execution of the previous block and diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.mli b/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.mli index 48cff9f6ceb5366c4bcdb7a69b922fd390102b24..124eaa96b53d4ba69484ba4dcc81688166aba0b2 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_018_Proxford/lib_sc_rollup_node/interpreter.mli @@ -39,7 +39,7 @@ val process_head : predecessor:Layer1.header -> Layer1.header -> Octez_smart_rollup.Inbox.t * string list -> - ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t + ('a Context.t * int * int64 * Z.t) tzresult Lwt.t (** [state_of_tick node_ctxt ?start_state tick level] returns [Some (state, hash)] for a given [tick] if this [tick] happened before [level]. Otherwise, diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.ml b/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.ml index 12b477a68c835b351054cb9d4d3c3603c12cb84e..52a6238f1636ae87c6b989c04e3ac5549b374920 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.ml @@ -53,9 +53,12 @@ let fetch_tezos_block l1_ctxt hash = let prefetch_tezos_blocks = Layer1.prefetch_tezos_blocks fetch extract_header -let get_last_cemented_commitment (cctxt : Protocol_client_context.full) - rollup_address : Node_context.lcc tzresult Lwt.t = +let get_last_cemented_commitment (cctxt : #Client_context.full) rollup_address : + Node_context.lcc tzresult Lwt.t = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ commitment, level = Plugin.RPC.Sc_rollup.last_cemented_commitment_hash_with_level @@ -69,9 +72,12 @@ let get_last_cemented_commitment (cctxt : Protocol_client_context.full) level = Protocol.Alpha_context.Raw_level.to_int32 level; } -let get_last_published_commitment (cctxt : Protocol_client_context.full) - rollup_address operator = +let get_last_published_commitment (cctxt : #Client_context.full) rollup_address + operator = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let*! res = Plugin.RPC.Sc_rollup.staked_on_commitment @@ -98,6 +104,9 @@ let get_last_published_commitment (cctxt : Protocol_client_context.full) let get_kind cctxt rollup_address = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ kind = RPC.Sc_rollup.kind cctxt (cctxt#chain, cctxt#block) rollup_address () @@ -106,6 +115,9 @@ let get_kind cctxt rollup_address = let genesis_inbox cctxt ~genesis_level = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ inbox = Plugin.RPC.Sc_rollup.inbox cctxt (cctxt#chain, `Level genesis_level) in @@ -142,6 +154,9 @@ let constants_of_parametric *) let retrieve_constants ?(block = `Head 0) cctxt = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {parametric; _} = Protocol.Constants_services.all cctxt (cctxt#chain, block) in @@ -150,6 +165,9 @@ let retrieve_constants ?(block = `Head 0) cctxt = let retrieve_genesis_info cctxt rollup_address = let open Lwt_result_syntax in let open Protocol.Alpha_context in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {level; commitment_hash} = RPC.Sc_rollup.genesis_info cctxt (cctxt#chain, `Head 0) rollup_address in diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.mli b/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.mli index 605c80fcbdd0757210fabb5b06a5ca6edbb02154..c82972f95ba0d8f23472993bc7451a0655999f68 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.mli +++ b/src/proto_018_Proxford/lib_sc_rollup_node/layer1_helpers.mli @@ -40,19 +40,18 @@ val fetch_tezos_block : val prefetch_tezos_blocks : t -> head list -> unit val get_last_cemented_commitment : - Protocol_client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t val get_last_published_commitment : - Protocol_client_context.full -> + #Client_context.full -> Address.t -> Signature.public_key_hash -> Commitment.t option tzresult Lwt.t -val get_kind : - Protocol_client_context.full -> Address.t -> Kind.t tzresult Lwt.t +val get_kind : #Client_context.full -> Address.t -> Kind.t tzresult Lwt.t val genesis_inbox : - Protocol_client_context.full -> + #Client_context.full -> genesis_level:int32 -> Octez_smart_rollup.Inbox.t tzresult Lwt.t @@ -64,10 +63,8 @@ val constants_of_parametric : (** Retrieve protocol agnotic constants for the head of the chain. *) val retrieve_constants : ?block:Block_services.block -> - Protocol_client_context.full -> + #Client_context.full -> Rollup_constants.protocol_constants tzresult Lwt.t val retrieve_genesis_info : - Protocol_client_context.full -> - Address.t -> - Node_context.genesis_info tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.genesis_info tzresult Lwt.t diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/publisher.ml b/src/proto_018_Proxford/lib_sc_rollup_node/publisher.ml index f984a811cfc91b5410dbc1dd3a9dc293833caf0b..b20d68e38b0ac484156d098c684e3e1ee243e866 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/publisher.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/publisher.ml @@ -72,11 +72,12 @@ let sub_level level decrement = if r < 0l then None else Some r let sc_rollup_commitment_period node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup + node_ctxt.Node_context.current_protocol.constants.sc_rollup .commitment_period_in_blocks let sc_rollup_challenge_window node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup.challenge_window_in_blocks + node_ctxt.Node_context.current_protocol.constants.sc_rollup + .challenge_window_in_blocks let next_commitment_level node_ctxt last_commitment_level = add_level last_commitment_level (sc_rollup_commitment_period node_ctxt) @@ -478,13 +479,26 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Commitment_event.starting () in let node_ctxt = Node_context.readonly node_ctxt in let+ worker = Worker.launch table () {node_ctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_publisher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a publisher worker for a single scoru *) let worker = lazy diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/refutation_coordinator.ml b/src/proto_018_Proxford/lib_sc_rollup_node/refutation_coordinator.ml index 5762f752126c9814d1d882e6a540ca5c81085dec..627b7399a5e91e1ed02a84efdca2cc0ab74abdf1 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/refutation_coordinator.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/refutation_coordinator.ml @@ -201,7 +201,7 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Refutation_game_event.Coordinator.starting () in let cctxt = @@ -210,6 +210,19 @@ let init node_ctxt = let+ worker = Worker.launch table () {node_ctxt; cctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_refutation_coordinator; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a refutation coordinator for a single scoru *) let worker = lazy diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/rollup_node_plugin.ml b/src/proto_018_Proxford/lib_sc_rollup_node/rollup_node_plugin.ml new file mode 100644 index 0000000000000000000000000000000000000000..6d1642bf8f58d15e8da5aaa7e91613cda1a8d4b5 --- /dev/null +++ b/src/proto_018_Proxford/lib_sc_rollup_node/rollup_node_plugin.ml @@ -0,0 +1,45 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 Plugin : Protocol_plugin_sig.S = struct + let protocol = Protocol.hash + + module RPC_directory = RPC_directory + module Dal_slots_tracker = Dal_slots_tracker + module Inbox = Inbox + module Interpreter = Interpreter + module Publisher = Publisher + module Refutation_coordinator = Refutation_coordinator + module Batcher = Batcher + module Layer1_helpers = Layer1_helpers + + module L1_processing = struct + let check_pvm_initial_state_hash = Daemon.check_initial_state_hash + + let process_l1_block_operations = Daemon.process_l1_block_operations + end +end + +let () = Protocol_plugins.register (module Plugin) diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_018_Proxford/lib_sc_rollup_node/test/helpers/helpers.ml index 6b669b98306a1642c001f60616f2613bb5e711cd..ceed3e3bcc559290447f60338be739a18e9b296d 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/test/helpers/helpers.ml @@ -142,10 +142,13 @@ let initialize_node_context ?(constants = default_constants) kind ~boot_sector = new Protocol_client_context.wrap_full (new Faked_client_context.unix_faked ~base_dir ~filesystem) in + let current_protocol = + {Node_context.hash = Protocol.hash; proto_level = 0; constants} + in let* ctxt = Node_context.Internal_for_tests.create_node_context cctxt - constants + current_protocol ~data_dir kind in diff --git a/src/proto_alpha/bin_sc_rollup_node/dune b/src/proto_alpha/bin_sc_rollup_node/dune index 9cae4b7bc5dea9444efebec7339f346223ecb683..8d3f7eea389ff91097e372b2907eb3b4c2fc23d4 100644 --- a/src/proto_alpha/bin_sc_rollup_node/dune +++ b/src/proto_alpha/bin_sc_rollup_node/dune @@ -15,7 +15,7 @@ tezos-client-base-unix tezos-client-commands tezos-client-alpha - octez-smart-rollup-node + octez-smart-rollup-node-lib octez_smart_rollup_node_alpha) (link_flags (:standard) diff --git a/src/proto_alpha/lib_sc_rollup_node/RPC_directory.ml b/src/proto_alpha/lib_sc_rollup_node/RPC_directory.ml new file mode 100644 index 0000000000000000000000000000000000000000..ed7c2dc254c081f19ef09bed1525ca8c7a736080 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/RPC_directory.ml @@ -0,0 +1,536 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs, *) +(* Copyright (c) 2022-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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +open RPC_directory_helpers +open Protocol + +let get_head_hash_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map + (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) + res + +let get_head_level_opt node_ctxt = + let open Lwt_result_syntax in + let+ res = Node_context.last_processed_head_opt node_ctxt in + Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res + +module Slot_pages_map = struct + open Protocol + open Alpha_context + include Map.Make (Dal.Slot_index) +end + +let get_dal_processed_slots node_ctxt block = + Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block + +module Global_directory = Make_directory (struct + include Sc_rollup_services.Global + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Proof_helpers_directory = Make_directory (struct + include Sc_rollup_services.Global.Helpers + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type context = Node_context.rw + + (* The context needs to be accessed with write permissions because we need to + commit on disk to generate the proofs. *) + type subcontext = Node_context.rw + + let context_of_prefix node_ctxt () = return node_ctxt +end) + +module Local_directory = Make_directory (struct + include Sc_rollup_services.Local + + type context = Node_context.rw + + type subcontext = Node_context.ro + + let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) +end) + +module Block_directory = Make_directory (struct + include Sc_rollup_services.Global.Block + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t + + let context_of_prefix node_ctxt (((), block) : prefix) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block) +end) + +module Outbox_directory = Make_directory (struct + include Sc_rollup_services.Global.Block.Outbox + + type context = Node_context.rw + + type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t + + let context_of_prefix node_ctxt (((), block), level) = + let open Lwt_result_syntax in + let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in + (Node_context.readonly node_ctxt, block, level) +end) + +module Common = struct + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.block + @@ fun (node_ctxt, block) () () -> + Node_context.get_full_l2_block node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.num_messages + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* l2_block = Node_context.get_l2_block node_ctxt block in + let+ num_messages = + Node_context.get_num_messages node_ctxt l2_block.header.inbox_witness + in + Z.of_int num_messages + + let () = + Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address + @@ fun node_ctxt () () -> + return @@ Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_head + @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt + + let () = + Global_directory.register0 Sc_rollup_services.Global.current_tezos_level + @@ fun node_ctxt () () -> get_head_level_opt node_ctxt + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.hash + @@ fun (_node_ctxt, block) () () -> return block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.level + @@ fun (node_ctxt, block) () () -> + Node_context.level_of_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.inbox + @@ fun (node_ctxt, block) () () -> + Node_context.get_inbox_by_block_hash node_ctxt block + + let () = + Block_directory.register0 Sc_rollup_services.Global.Block.ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ l2_block = Node_context.get_l2_block node_ctxt block in + Z.of_int64 l2_block.num_ticks +end + +let get_state (node_ctxt : _ Node_context.t) block_hash = + let open Lwt_result_syntax in + let* ctxt = Node_context.checkout_context node_ctxt block_hash in + let*! state = Context.PVMState.find ctxt in + match state with None -> failwith "No state" | Some state -> return state + +let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages + ~insight_requests messages = + let open Lwt_result_syntax in + let open Alpha_context in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let reveal_map = + match reveal_pages with + | Some pages -> + let map = + List.fold_left + (fun map page -> + let hash = + Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) + in + Sc_rollup_reveal_hash.Map.add hash page map) + Sc_rollup_reveal_hash.Map.empty + pages + in + Some map + | None -> None + in + let* level = Node_context.level_of_hash node_ctxt block in + let* sim = + Simulation.start_simulation + node_ctxt + ~reveal_map + Layer1.{hash = block; level} + in + let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in + let* {state; inbox_level; _}, num_ticks_end = + Simulation.end_simulation node_ctxt sim + in + let*! insights = + List.map_p + (function + | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key + | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) + insight_requests + in + let num_ticks = Z.(num_ticks_0 + num_ticks_end) in + let level = Raw_level.of_int32_exn inbox_level in + let*! outbox = PVM.get_outbox level state in + let output = + List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox + in + let*! state_hash = PVM.state_hash state in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/5871 + Use constants for correct protocol. *) + let is_reveal_enabled = + node_ctxt.current_protocol.constants.sc_rollup.reveal_activation_level + |> WithExceptions.Option.get ~loc:__LOC__ + |> Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez + |> Protocol.Alpha_context.Sc_rollup.is_reveal_enabled_predicate + in + let*! status = PVM.get_status ~is_reveal_enabled state in + let status = PVM.string_of_status status in + return + Sc_rollup_services. + {state_hash; status; output; inbox_level; num_ticks; insights} + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! tick = PVM.get_tick state in + return tick + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_hash + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! hash = PVM.state_hash state in + return hash + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! current_level = PVM.get_current_level state in + return current_level + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.state_value + @@ fun (node_ctxt, block) {key} () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let path = String.split_on_char '/' key in + let*! value = Context.PVMState.lookup state path in + match value with + | None -> failwith "No such key in PVM state" + | Some value -> + Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; + return value + +let () = + Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + let* head = Node_context.last_processed_head_opt node_ctxt in + match head with + | None -> return_none + | Some head -> + let commitment_hash = + Sc_rollup_block.most_recent_commitment head.header + in + let+ commitment = + Node_context.find_commitment node_ctxt commitment_hash + in + Option.map (fun c -> (c, commitment_hash)) commitment + +let () = + Local_directory.register0 Sc_rollup_services.Local.last_published_commitment + @@ fun node_ctxt () () -> + let open Lwt_result_syntax in + match Reference.get node_ctxt.lpc with + | None -> return_none + | Some commitment -> + let hash = Octez_smart_rollup.Commitment.hash commitment in + (* The corresponding level in Store.Commitments.published_at_level is + available only when the commitment has been published and included + in a block. *) + let* published_at_level_info = + Node_context.commitment_published_at_level node_ctxt hash + in + let first_published, published = + match published_at_level_info with + | None -> (None, None) + | Some {first_published_at_level; published_at_level} -> + (Some first_published_at_level, published_at_level) + in + return_some (commitment, hash, first_published, published) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.status + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + (* TODO: https://gitlab.com/tezos/tezos/-/issues/5871 + Use constants for correct protocol. *) + let is_reveal_enabled = + node_ctxt.current_protocol.constants.sc_rollup.reveal_activation_level + |> WithExceptions.Option.get ~loc:__LOC__ + |> Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez + |> Protocol.Alpha_context.Sc_rollup.is_reveal_enabled_predicate + in + let*! status = PVM.get_status ~is_reveal_enabled state in + return (PVM.string_of_status status) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots + @@ fun (node_ctxt, block) () () -> + let open Lwt_result_syntax in + let+ slots = + Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block + in + List.rev_map + (Sc_rollup_proto_types.Dal.Slot_header.of_octez + ~number_of_slots:node_ctxt.current_protocol.constants.dal.number_of_slots) + slots + |> List.rev + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots + @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block + +let () = + Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages + @@ fun (node_ctxt, block, outbox_level) () () -> + let open Lwt_result_syntax in + let* state = get_state node_ctxt block in + let module PVM = (val Pvm.of_kind node_ctxt.kind) in + let*! outbox = PVM.get_outbox outbox_level state in + return outbox + +let () = + Proof_helpers_directory.register0 + Sc_rollup_services.Global.Helpers.outbox_proof + @@ fun node_ctxt output () -> + let open Lwt_result_syntax in + let+ commitment, proof = Outbox.proof_of_output node_ctxt output in + (Sc_rollup_proto_types.Commitment_hash.of_octez commitment, proof) + +let () = + Block_directory.register0 Sc_rollup_services.Global.Block.simulate + @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> + simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.injection + @@ fun _node_ctxt () messages -> Batcher.register_messages messages + +let () = + Local_directory.register0 Sc_rollup_services.Local.batcher_queue + @@ fun _node_ctxt () () -> + let open Lwt_result_syntax in + let*? queue = Batcher.get_queue () in + return queue + +(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level + of the commitment which should include the inbox of level + [inbox_level]. + + It is computed with the following formula: + {v + commitment_level(inbox_level) = + last_commitment - + ((last_commitment - inbox_level) / commitment_period + * commitment_period) + v} + *) +let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = + let open Alpha_context in + let open Option_syntax in + let+ last_published_commitment = Reference.get node_ctxt.lpc in + let commitment_period = + Int32.of_int + node_ctxt.current_protocol.constants.sc_rollup.commitment_period_in_blocks + in + let last_published = last_published_commitment.inbox_level in + let open Int32 in + div (sub last_published inbox_level) commitment_period + |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn + +let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = + let open Lwt_result_syntax in + let+ finalized_level = Node_context.get_finalized_level node_ctxt in + let finalized = Compare.Int32.(inbox_level <= finalized_level) in + let lcc = Reference.get node_ctxt.lcc in + let cemented = Compare.Int32.(inbox_level <= lcc.level) in + (finalized, cemented) + +let () = + Local_directory.register1 Sc_rollup_services.Local.batcher_message + @@ fun node_ctxt hash () () -> + let open Lwt_result_syntax in + let*? batch_status = Batcher.message_status hash in + let* status = + match batch_status with + | None -> return (None, Sc_rollup_services.Unknown) + | Some (batch_status, msg) -> ( + let return status = return (Some msg, status) in + match batch_status with + | Pending_batch -> return Sc_rollup_services.Pending_batch + | Batched l1_hash -> ( + match Injector.operation_status l1_hash with + | None -> return Sc_rollup_services.Unknown + | Some (Pending op) -> + return (Sc_rollup_services.Pending_injection op) + | Some (Injected {op; oph; op_index}) -> + return + (Sc_rollup_services.Injected + {op = op.operation; oph; op_index}) + | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( + let* finalized, cemented = + inbox_info_of_level node_ctxt l1_level + in + let commitment_level = + commitment_level_of_inbox_level node_ctxt l1_level + in + match commitment_level with + | None -> + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some commitment_level -> ( + let* block = + Node_context.find_l2_block_by_level + node_ctxt + (Alpha_context.Raw_level.to_int32 commitment_level) + in + match block with + | None -> + (* Commitment not computed yet for inbox *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some block -> ( + let commitment_hash = + WithExceptions.Option.get + ~loc:__LOC__ + block.header.commitment_hash + in + (* Commitment computed *) + let* published_at = + Node_context.commitment_published_at_level + node_ctxt + commitment_hash + in + match published_at with + | None | Some {published_at_level = None; _} -> + (* Commitment not published yet *) + return + (Sc_rollup_services.Included + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + }) + | Some + { + first_published_at_level; + published_at_level = Some published_at_level; + } -> + (* Commitment published *) + let* commitment = + Node_context.get_commitment + node_ctxt + commitment_hash + in + return + (Sc_rollup_services.Committed + { + op = op.operation; + oph; + op_index; + l1_block; + l1_level; + finalized; + cemented; + commitment; + commitment_hash; + first_published_at_level; + published_at_level; + })))))) + in + + return status + +let directory (node_ctxt : _ Node_context.t) = + let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in + List.fold_left + (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) + Tezos_rpc.Directory.empty + [ + Global_directory.build_directory; + Local_directory.build_directory; + Block_directory.build_directory; + Proof_helpers_directory.build_directory; + Outbox_directory.build_directory; + PVM.build_directory; + ] diff --git a/src/proto_alpha/lib_sc_rollup_node/RPC_directory.mli b/src/proto_alpha/lib_sc_rollup_node/RPC_directory.mli new file mode 100644 index 0000000000000000000000000000000000000000..206e38547b0c0036ae3003795244070e7063cf71 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/RPC_directory.mli @@ -0,0 +1,27 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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. *) +(* *) +(*****************************************************************************) + +(** The RPC directory for this rollup node. *) +val directory : Node_context.rw -> unit Tezos_rpc.Directory.t diff --git a/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml b/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml index 5227b3609bb125efb84fa96792059bacb04dd4a2..bbc8af4ea919b8814d58a1e896af439c61b86208 100644 --- a/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml +++ b/src/proto_alpha/lib_sc_rollup_node/RPC_server.ml @@ -3,6 +3,7 @@ (* Open Source License *) (* Copyright (c) 2022 Nomadic Labs, *) (* Copyright (c) 2022-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"),*) @@ -26,515 +27,6 @@ open Tezos_rpc_http open Tezos_rpc_http_server -open RPC_directory_helpers -open Protocol - -let get_head_hash_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map - (fun Sc_rollup_block.{header = {block_hash; _}; _} -> block_hash) - res - -let get_head_level_opt node_ctxt = - let open Lwt_result_syntax in - let+ res = Node_context.last_processed_head_opt node_ctxt in - Option.map (fun Sc_rollup_block.{header = {level; _}; _} -> level) res - -module Slot_pages_map = struct - open Protocol - open Alpha_context - include Map.Make (Dal.Slot_index) -end - -let get_dal_processed_slots node_ctxt block = - Node_context.list_slots_statuses node_ctxt ~confirmed_in_block_hash:block - -module Global_directory = Make_directory (struct - include Sc_rollup_services.Global - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Proof_helpers_directory = Make_directory (struct - include Sc_rollup_services.Global.Helpers - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type context = Node_context.rw - - (* The context needs to be accessed with write permissions because we need to - commit on disk to generate the proofs. *) - type subcontext = Node_context.rw - - let context_of_prefix node_ctxt () = return node_ctxt -end) - -module Local_directory = Make_directory (struct - include Sc_rollup_services.Local - - type context = Node_context.rw - - type subcontext = Node_context.ro - - let context_of_prefix node_ctxt () = return (Node_context.readonly node_ctxt) -end) - -module Block_directory = Make_directory (struct - include Sc_rollup_services.Global.Block - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t - - let context_of_prefix node_ctxt (((), block) : prefix) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block) -end) - -module Outbox_directory = Make_directory (struct - include Sc_rollup_services.Global.Block.Outbox - - type context = Node_context.rw - - type subcontext = Node_context.ro * Block_hash.t * Alpha_context.Raw_level.t - - let context_of_prefix node_ctxt (((), block), level) = - let open Lwt_result_syntax in - let+ block = Block_directory_helpers.block_of_prefix node_ctxt block in - (Node_context.readonly node_ctxt, block, level) -end) - -module Common = struct - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.block - @@ fun (node_ctxt, block) () () -> - Node_context.get_full_l2_block node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.num_messages - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* l2_block = Node_context.get_l2_block node_ctxt block in - let+ num_messages = - Node_context.get_num_messages - node_ctxt - (Sc_rollup_proto_types.Merkelized_payload_hashes_hash.of_octez - l2_block.header.inbox_witness) - in - Z.of_int num_messages - - let () = - Global_directory.register0 Sc_rollup_services.Global.sc_rollup_address - @@ fun node_ctxt () () -> return @@ node_ctxt.rollup_address - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_head - @@ fun node_ctxt () () -> get_head_hash_opt node_ctxt - - let () = - Global_directory.register0 Sc_rollup_services.Global.current_tezos_level - @@ fun node_ctxt () () -> get_head_level_opt node_ctxt - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.hash - @@ fun (_node_ctxt, block) () () -> return block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.level - @@ fun (node_ctxt, block) () () -> - Node_context.level_of_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.inbox - @@ fun (node_ctxt, block) () () -> - Node_context.get_inbox_by_block_hash node_ctxt block - - let () = - Block_directory.register0 Sc_rollup_services.Global.Block.ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ l2_block = Node_context.get_l2_block node_ctxt block in - Z.of_int64 l2_block.num_ticks -end - -let get_state (node_ctxt : _ Node_context.t) block_hash = - let open Lwt_result_syntax in - let* ctxt = Node_context.checkout_context node_ctxt block_hash in - let*! state = Context.PVMState.find ctxt in - match state with None -> failwith "No state" | Some state -> return state - -let simulate_messages (node_ctxt : Node_context.ro) block ~reveal_pages - ~insight_requests messages = - let open Lwt_result_syntax in - let open Alpha_context in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let reveal_map = - match reveal_pages with - | Some pages -> - let map = - List.fold_left - (fun map page -> - let hash = - Sc_rollup_reveal_hash.(hash_string ~scheme:Blake2B [page]) - in - Sc_rollup_reveal_hash.Map.add hash page map) - Sc_rollup_reveal_hash.Map.empty - pages - in - Some map - | None -> None - in - let* level = Node_context.level_of_hash node_ctxt block in - let* sim = - Simulation.start_simulation - node_ctxt - ~reveal_map - Layer1.{hash = block; level} - in - let* sim, num_ticks_0 = Simulation.simulate_messages node_ctxt sim messages in - let* {state; inbox_level; _}, num_ticks_end = - Simulation.end_simulation node_ctxt sim - in - let*! insights = - List.map_p - (function - | Sc_rollup_services.Pvm_state_key key -> PVM.State.lookup state key - | Durable_storage_key key -> PVM.Inspect_durable_state.lookup state key) - insight_requests - in - let num_ticks = Z.(num_ticks_0 + num_ticks_end) in - let level = Raw_level.of_int32_exn inbox_level in - let*! outbox = PVM.get_outbox level state in - let output = - List.filter (fun Sc_rollup.{outbox_level; _} -> outbox_level = level) outbox - in - let*! state_hash = PVM.state_hash state in - (* TODO: https://gitlab.com/tezos/tezos/-/issues/5871 - Use constants for correct protocol. *) - let is_reveal_enabled = - node_ctxt.protocol_constants.sc_rollup.reveal_activation_level - |> WithExceptions.Option.get ~loc:__LOC__ - |> Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez - |> Protocol.Alpha_context.Sc_rollup.is_reveal_enabled_predicate - in - let*! status = PVM.get_status ~is_reveal_enabled state in - let status = PVM.string_of_status status in - return - Sc_rollup_services. - {state_hash; status; output; inbox_level; num_ticks; insights} - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.total_ticks - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! tick = PVM.get_tick state in - return tick - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_hash - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! hash = PVM.state_hash state in - return hash - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_current_level - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! current_level = PVM.get_current_level state in - return current_level - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.state_value - @@ fun (node_ctxt, block) {key} () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let path = String.split_on_char '/' key in - let*! value = Context.PVMState.lookup state path in - match value with - | None -> failwith "No such key in PVM state" - | Some value -> - Format.eprintf "Encoded %S\n@.%!" (Bytes.to_string value) ; - return value - -let () = - Global_directory.register0 Sc_rollup_services.Global.last_stored_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - let* head = Node_context.last_processed_head_opt node_ctxt in - match head with - | None -> return_none - | Some head -> - let commitment_hash = - Sc_rollup_block.most_recent_commitment head.header - in - let+ commitment = - Node_context.find_commitment node_ctxt commitment_hash - in - Option.map (fun c -> (c, commitment_hash)) commitment - -let () = - Local_directory.register0 Sc_rollup_services.Local.last_published_commitment - @@ fun node_ctxt () () -> - let open Lwt_result_syntax in - match Reference.get node_ctxt.lpc with - | None -> return_none - | Some commitment -> - let hash = Octez_smart_rollup.Commitment.hash commitment in - (* The corresponding level in Store.Commitments.published_at_level is - available only when the commitment has been published and included - in a block. *) - let* published_at_level_info = - Node_context.commitment_published_at_level node_ctxt hash - in - let first_published, published = - match published_at_level_info with - | None -> (None, None) - | Some {first_published_at_level; published_at_level} -> - (Some first_published_at_level, published_at_level) - in - return_some (commitment, hash, first_published, published) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.status - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - (* TODO: https://gitlab.com/tezos/tezos/-/issues/5871 - Use constants for correct protocol. *) - let is_reveal_enabled = - node_ctxt.protocol_constants.sc_rollup.reveal_activation_level - |> WithExceptions.Option.get ~loc:__LOC__ - |> Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez - |> Protocol.Alpha_context.Sc_rollup.is_reveal_enabled_predicate - in - let*! status = PVM.get_status ~is_reveal_enabled state in - return (PVM.string_of_status status) - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_slots - @@ fun (node_ctxt, block) () () -> - let open Lwt_result_syntax in - let+ slots = - Node_context.get_all_slot_headers node_ctxt ~published_in_block_hash:block - in - List.rev_map - (Sc_rollup_proto_types.Dal.Slot_header.of_octez - ~number_of_slots:node_ctxt.protocol_constants.dal.number_of_slots) - slots - |> List.rev - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.dal_processed_slots - @@ fun (node_ctxt, block) () () -> get_dal_processed_slots node_ctxt block - -let () = - Outbox_directory.register0 Sc_rollup_services.Global.Block.Outbox.messages - @@ fun (node_ctxt, block, outbox_level) () () -> - let open Lwt_result_syntax in - let* state = get_state node_ctxt block in - let module PVM = (val Pvm.of_kind node_ctxt.kind) in - let*! outbox = PVM.get_outbox outbox_level state in - return outbox - -let () = - Proof_helpers_directory.register0 - Sc_rollup_services.Global.Helpers.outbox_proof - @@ fun node_ctxt output () -> Outbox.proof_of_output node_ctxt output - -let () = - Block_directory.register0 Sc_rollup_services.Global.Block.simulate - @@ fun (node_ctxt, block) () {messages; reveal_pages; insight_requests} -> - simulate_messages node_ctxt block ~reveal_pages ~insight_requests messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.injection - @@ fun _node_ctxt () messages -> Batcher.register_messages messages - -let () = - Local_directory.register0 Sc_rollup_services.Local.batcher_queue - @@ fun _node_ctxt () () -> - let open Lwt_result_syntax in - let*? queue = Batcher.get_queue () in - return queue - -(** [commitment_level_of_inbox_level node_ctxt inbox_level] returns the level - of the commitment which should include the inbox of level - [inbox_level]. - - It is computed with the following formula: - {v - commitment_level(inbox_level) = - last_commitment - - ((last_commitment - inbox_level) / commitment_period - * commitment_period) - v} - *) -let commitment_level_of_inbox_level (node_ctxt : _ Node_context.t) inbox_level = - let open Alpha_context in - let open Option_syntax in - let+ last_published_commitment = Reference.get node_ctxt.lpc in - let commitment_period = - Int32.of_int - node_ctxt.protocol_constants.sc_rollup.commitment_period_in_blocks - in - let last_published = last_published_commitment.inbox_level in - let open Int32 in - div (sub last_published inbox_level) commitment_period - |> mul commitment_period |> sub last_published |> Raw_level.of_int32_exn - -let inbox_info_of_level (node_ctxt : _ Node_context.t) inbox_level = - let open Lwt_result_syntax in - let+ finalized_level = Node_context.get_finalized_level node_ctxt in - let finalized = Compare.Int32.(inbox_level <= finalized_level) in - let lcc = Reference.get node_ctxt.lcc in - let cemented = Compare.Int32.(inbox_level <= lcc.level) in - (finalized, cemented) - -let () = - Local_directory.register1 Sc_rollup_services.Local.batcher_message - @@ fun node_ctxt hash () () -> - let open Lwt_result_syntax in - let*? batch_status = Batcher.message_status hash in - let* status = - match batch_status with - | None -> return (None, Sc_rollup_services.Unknown) - | Some (batch_status, msg) -> ( - let return status = return (Some msg, status) in - match batch_status with - | Pending_batch -> return Sc_rollup_services.Pending_batch - | Batched l1_hash -> ( - match Injector.operation_status l1_hash with - | None -> return Sc_rollup_services.Unknown - | Some (Pending op) -> - return (Sc_rollup_services.Pending_injection op) - | Some (Injected {op; oph; op_index}) -> - return - (Sc_rollup_services.Injected - {op = op.operation; oph; op_index}) - | Some (Included {op; oph; op_index; l1_block; l1_level}) -> ( - let* finalized, cemented = - inbox_info_of_level node_ctxt l1_level - in - let commitment_level = - commitment_level_of_inbox_level node_ctxt l1_level - in - match commitment_level with - | None -> - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some commitment_level -> ( - let* block = - Node_context.find_l2_block_by_level - node_ctxt - (Alpha_context.Raw_level.to_int32 commitment_level) - in - match block with - | None -> - (* Commitment not computed yet for inbox *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some block -> ( - let commitment_hash = - WithExceptions.Option.get - ~loc:__LOC__ - block.header.commitment_hash - |> Sc_rollup_proto_types.Commitment_hash.of_octez - in - (* Commitment computed *) - let* published_at = - Node_context.commitment_published_at_level - node_ctxt - commitment_hash - in - match published_at with - | None | Some {published_at_level = None; _} -> - (* Commitment not published yet *) - return - (Sc_rollup_services.Included - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - }) - | Some - { - first_published_at_level; - published_at_level = Some published_at_level; - } -> - (* Commitment published *) - let* commitment = - Node_context.get_commitment - node_ctxt - commitment_hash - in - return - (Sc_rollup_services.Committed - { - op = op.operation; - oph; - op_index; - l1_block; - l1_level; - finalized; - cemented; - commitment; - commitment_hash; - first_published_at_level; - published_at_level; - })))))) - in - - return status - -let register (node_ctxt : _ Node_context.t) = - let module PVM = (val Pvm_rpc.of_kind node_ctxt.kind) in - List.fold_left - (fun dir f -> Tezos_rpc.Directory.merge dir (f node_ctxt)) - Tezos_rpc.Directory.empty - [ - Global_directory.build_directory; - Local_directory.build_directory; - Block_directory.build_directory; - Proof_helpers_directory.build_directory; - Outbox_directory.build_directory; - PVM.build_directory; - ] let start node_ctxt configuration = let open Lwt_result_syntax in @@ -543,7 +35,7 @@ let start node_ctxt configuration = let host = Ipaddr.V6.to_string rpc_addr in let node = `TCP (`Port rpc_port) in let acl = RPC_server.Acl.allow_all in - let dir = register node_ctxt in + let dir = RPC_directory.directory node_ctxt in let server = RPC_server.init_server dir ~acl ~media_types:Media_type.all_media_types in diff --git a/src/proto_alpha/lib_sc_rollup_node/RPC_server.mli b/src/proto_alpha/lib_sc_rollup_node/RPC_server.mli index 6ce3fd2f3ec98de44ba3d2e209aaf903e50a1b0c..a7bcde269132a8713231c8dadbeb6283186d6b68 100644 --- a/src/proto_alpha/lib_sc_rollup_node/RPC_server.mli +++ b/src/proto_alpha/lib_sc_rollup_node/RPC_server.mli @@ -2,6 +2,7 @@ (* *) (* Open Source License *) (* Copyright (c) 2022 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"),*) diff --git a/src/proto_alpha/lib_sc_rollup_node/batcher.ml b/src/proto_alpha/lib_sc_rollup_node/batcher.ml index eaf5b902f150afe2068cc7dc06fbeb32128ef284..1e9f8967a9cb36c21ee5668657de66110b69a0af 100644 --- a/src/proto_alpha/lib_sc_rollup_node/batcher.ml +++ b/src/proto_alpha/lib_sc_rollup_node/batcher.ml @@ -370,7 +370,7 @@ let check_batcher_config Configuration.{max_batch_size; _} = protocol_max_batch_size | _ -> Ok () -let init conf ~signer node_ctxt = +let start conf ~signer node_ctxt = let open Lwt_result_syntax in let*? () = check_batcher_config conf in let node_ctxt = Node_context.readonly node_ctxt in @@ -379,6 +379,19 @@ let init conf ~signer node_ctxt = in Lwt.wakeup worker_waker worker +let init conf ~signer node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_batcher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start conf ~signer node_ctxt + (* This is a batcher worker for a single scoru *) let worker = lazy diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon.ml b/src/proto_alpha/lib_sc_rollup_node/daemon.ml index bf2509aa9a8f213da6c5d12a7ad54da8f83a7523..ce639a407cff766237c0dbd58539b48c9a127177 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon.ml @@ -315,16 +315,18 @@ let previous_context (node_ctxt : _ Node_context.t) let classify_head (node_ctxt : _ Node_context.t) ?(predecessor : Layer1.header option) (head : Layer1.header) = let open Lwt_result_syntax in - if head.header.proto_level = node_ctxt.proto_level then + if head.header.proto_level = node_ctxt.current_protocol.proto_level then (* Same protocol as supported one, ok *) return `Supported_proto - else if head.header.proto_level = node_ctxt.proto_level + 1 then + else if head.header.proto_level = node_ctxt.current_protocol.proto_level + 1 + then let* predecessor = match predecessor with | Some p -> return p | None -> Node_context.get_predecessor_header node_ctxt head in - if predecessor.header.proto_level = node_ctxt.proto_level then + if predecessor.header.proto_level = node_ctxt.current_protocol.proto_level + then (* Migration block from supported protocol to the next one, ok *) return `Supported_migration else return `Unsupported_proto @@ -434,13 +436,7 @@ let rec process_head (daemon_components : (module Daemon_components.S)) } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.mark_finalized_level @@ -633,9 +629,10 @@ let run node_ctxt configuration { cctxt = (node_ctxt.cctxt :> Client_context.full); fee_parameters = configuration.fee_parameters; - minimal_block_delay = node_ctxt.protocol_constants.minimal_block_delay; + minimal_block_delay = + node_ctxt.current_protocol.constants.minimal_block_delay; delay_increment_per_round = - node_ctxt.protocol_constants.delay_increment_per_round; + node_ctxt.current_protocol.constants.delay_increment_per_round; } ~data_dir:node_ctxt.data_dir ~signers @@ -772,13 +769,7 @@ module Internal_for_tests = struct } in let l2_block = - Sc_rollup_block. - { - header; - content = (); - num_ticks; - initial_tick = Sc_rollup.Tick.to_z initial_tick; - } + Sc_rollup_block.{header; content = (); num_ticks; initial_tick} in let* () = Node_context.save_l2_head node_ctxt l2_block in return l2_block @@ -839,6 +830,13 @@ let run configuration.sc_rollup_address) publisher and* kind = Layer1_helpers.get_kind cctxt configuration.sc_rollup_address in + let current_protocol = + { + Node_context.hash = Protocol.hash; + proto_level = predecessor.proto_level; + constants; + } + in let* node_ctxt = Node_context.init cctxt @@ -846,12 +844,11 @@ let run ?log_kernel_debug_file Read_write l1_ctxt - constants genesis_info ~lcc ~lpc kind - ~proto_level:predecessor.proto_level + current_protocol configuration in run node_ctxt configuration daemon_components 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 d48444cd9513abcea90f67c41eaaee96cbd10bff..511f7d4129ff85fc0af5a20f079c834e923a1ef5 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 @@ -25,7 +25,6 @@ open Protocol open Alpha_context -module Block_services = Block_services.Make (Protocol) (Protocol) let ancestor_hash ~number_of_levels ({Node_context.genesis_info; _} as node_ctxt) head = @@ -68,7 +67,9 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = we reduce the lag to 1, then the slots header will never be confirmed. *) let open Lwt_result_syntax in - let lag = node_ctxt.Node_context.protocol_constants.dal.attestation_lag in + let lag = + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag + in (* we are downloading endorsemented for slots at level [level], so we need to download the data at level [level - lag]. *) @@ -103,7 +104,7 @@ let slots_info node_ctxt (Layer1.{hash; _} as head) = ~published_in_block_hash:published_block_hash in let number_of_slots = - node_ctxt.Node_context.protocol_constants.dal.number_of_slots + node_ctxt.Node_context.current_protocol.constants.dal.number_of_slots in let confirmed_slots_indexes_list = List.filter @@ -131,22 +132,24 @@ let to_slot_index_list (constants : Rollup_constants.protocol_constants) bitset Use a shared storage between dal and rollup node to store slots data. *) -let download_and_save_slots - ({Node_context.protocol_constants; _} as node_context) ~current_block_hash - {published_block_hash; confirmed_slots_indexes} = +let download_and_save_slots (node_context : _ Node_context.t) + ~current_block_hash {published_block_hash; confirmed_slots_indexes} = let open Lwt_result_syntax in let*? all_slots = - Bitset.fill ~length:protocol_constants.dal.number_of_slots + Bitset.fill + ~length:node_context.current_protocol.constants.dal.number_of_slots |> Environment.wrap_tzresult in let*? not_confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants + @@ to_slot_index_list node_context.current_protocol.constants @@ Bitset.diff all_slots confirmed_slots_indexes in let*? confirmed = Environment.wrap_tzresult - @@ to_slot_index_list protocol_constants confirmed_slots_indexes + @@ to_slot_index_list + node_context.current_protocol.constants + confirmed_slots_indexes in (* The contents of each slot index are written to a different location on disk, therefore calls to store contents for different slot indexes can @@ -173,7 +176,8 @@ let download_and_save_slots let*! () = Dal_slots_tracker_event.slot_has_been_confirmed (Sc_rollup_proto_types.Dal.Slot_index.of_octez - ~number_of_slots:protocol_constants.dal.number_of_slots + ~number_of_slots: + node_context.current_protocol.constants.dal.number_of_slots s_slot) published_block_hash current_block_hash @@ -191,7 +195,7 @@ module Confirmed_slots_history = struct let*? relevant_slots_indexes = Environment.wrap_tzresult @@ to_slot_index_list - node_ctxt.Node_context.protocol_constants + node_ctxt.Node_context.current_protocol.constants confirmed_slots_indexes in List.map_ep @@ -203,7 +207,8 @@ module Confirmed_slots_history = struct slot_index in Sc_rollup_proto_types.Dal.Slot_header.of_octez - ~number_of_slots:node_ctxt.protocol_constants.dal.number_of_slots + ~number_of_slots: + node_ctxt.current_protocol.constants.dal.number_of_slots h) relevant_slots_indexes @@ -225,7 +230,8 @@ module Confirmed_slots_history = struct let should_process_dal_slots node_ctxt block_level = let open Node_context in let lag = - Int32.of_int node_ctxt.Node_context.protocol_constants.dal.attestation_lag + Int32.of_int + node_ctxt.Node_context.current_protocol.constants.dal.attestation_lag in let block_level = Raw_level.to_int32 block_level in let genesis_level = node_ctxt.genesis_info.level in @@ -288,7 +294,7 @@ module Confirmed_slots_history = struct ~find ~default:(fun node_ctxt _block -> let num_slots = - node_ctxt.Node_context.protocol_constants.dal.number_of_slots + node_ctxt.Node_context.current_protocol.constants.dal.number_of_slots in (* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/3788 Put an accurate value for capacity. The value diff --git a/src/proto_alpha/lib_sc_rollup_node/dune b/src/proto_alpha/lib_sc_rollup_node/dune index cd16eb7b141770c5e53f2396874846925cbfead4..7a3c56138a20aaa1cbc65b4919f6830905ec77b2 100644 --- a/src/proto_alpha/lib_sc_rollup_node/dune +++ b/src/proto_alpha/lib_sc_rollup_node/dune @@ -40,7 +40,7 @@ aches aches-lwt octez-injector - octez-smart-rollup-node + octez-smart-rollup-node-lib tezos-scoru-wasm tezos-scoru-wasm-fast tezos-crypto-dal diff --git a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml index 15fc0a48c4d7f3f247eb6768ed90d947dad12078..a88e0ed416197f9d7b2958dff3d1d684980d2ddf 100644 --- a/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml +++ b/src/proto_alpha/lib_sc_rollup_node/fueled_pvm.ml @@ -141,7 +141,7 @@ module Make_fueled (F : Fuel.S) : S with type fuel = F.t = struct (* TODO: https://gitlab.com/tezos/tezos/-/issues/5871 Use constants for correct protocol. *) let is_reveal_enabled = - node_ctxt.protocol_constants.sc_rollup.reveal_activation_level + node_ctxt.current_protocol.constants.sc_rollup.reveal_activation_level |> WithExceptions.Option.get ~loc:__LOC__ |> Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez |> Sc_rollup.is_reveal_enabled_predicate @@ -149,7 +149,7 @@ module Make_fueled (F : Fuel.S) : S with type fuel = F.t = struct let module PVM = (val Pvm.of_kind node_ctxt.kind) in let metadata = metadata node_ctxt in let dal_attestation_lag = - node_ctxt.protocol_constants.dal.attestation_lag + node_ctxt.current_protocol.constants.dal.attestation_lag in let reveal_builtins = Tezos_scoru_wasm.Builtins. diff --git a/src/proto_alpha/lib_sc_rollup_node/inbox.ml b/src/proto_alpha/lib_sc_rollup_node/inbox.ml index 108334f2544efcfe67468ee85b796bbff638b246..3262407a4ac4e703ef7ac79f49d2217d3ca5ba67 100644 --- a/src/proto_alpha/lib_sc_rollup_node/inbox.ml +++ b/src/proto_alpha/lib_sc_rollup_node/inbox.ml @@ -214,7 +214,7 @@ let process_head (node_ctxt : _ Node_context.t) ~(predecessor : Layer1.header) else let* inbox = Layer1_helpers.genesis_inbox - (new Protocol_client_context.wrap_full node_ctxt.cctxt) + node_ctxt.cctxt ~genesis_level:node_ctxt.genesis_info.level in let Octez_smart_rollup.Inbox.{hash = witness; _} = diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml index 98bdca5ffbf27b30959a561f4a2e903c25a910e9..d4edff9d2b161f1b3a3b5a5476a35c83a1b67da8 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter.ml +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter.ml @@ -133,7 +133,8 @@ let transition_pvm node_ctxt ctxt predecessor Layer1.{hash = _; _} let*! () = Interpreter_event.transitioned_pvm inbox_level state_hash tick num_messages in - return (ctxt, num_messages, Z.to_int64 num_ticks, initial_tick) + return + (ctxt, num_messages, Z.to_int64 num_ticks, Sc_rollup.Tick.to_z initial_tick) (** [process_head node_ctxt ctxt ~predecessor head] runs the PVM for the given head. *) @@ -151,8 +152,8 @@ let process_head (node_ctxt : _ Node_context.t) ctxt else if head.Layer1.level = node_ctxt.genesis_info.level then let* ctxt, state = genesis_state head.hash node_ctxt ctxt in let*! ctxt = Context.PVMState.set ctxt state in - return (ctxt, 0, 0L, Sc_rollup.Tick.initial) - else return (ctxt, 0, 0L, Sc_rollup.Tick.initial) + return (ctxt, 0, 0L, Z.zero) + else return (ctxt, 0, 0L, Z.zero) (** Returns the starting evaluation before the evaluation of the block. It contains the PVM state at the end of the execution of the previous block and diff --git a/src/proto_alpha/lib_sc_rollup_node/interpreter.mli b/src/proto_alpha/lib_sc_rollup_node/interpreter.mli index 48cff9f6ceb5366c4bcdb7a69b922fd390102b24..124eaa96b53d4ba69484ba4dcc81688166aba0b2 100644 --- a/src/proto_alpha/lib_sc_rollup_node/interpreter.mli +++ b/src/proto_alpha/lib_sc_rollup_node/interpreter.mli @@ -39,7 +39,7 @@ val process_head : predecessor:Layer1.header -> Layer1.header -> Octez_smart_rollup.Inbox.t * string list -> - ('a Context.t * int * int64 * Sc_rollup.Tick.t) tzresult Lwt.t + ('a Context.t * int * int64 * Z.t) tzresult Lwt.t (** [state_of_tick node_ctxt ?start_state tick level] returns [Some (state, hash)] for a given [tick] if this [tick] happened before [level]. Otherwise, diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.ml b/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.ml index 6e8fcdf54076c6b1fdc567f62397e89398cdc4f1..b2e0c93631770c05d1c92d02d71c7852693e0b57 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.ml +++ b/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.ml @@ -53,9 +53,12 @@ let fetch_tezos_block l1_ctxt hash = let prefetch_tezos_blocks = Layer1.prefetch_tezos_blocks fetch extract_header -let get_last_cemented_commitment (cctxt : Protocol_client_context.full) - rollup_address : Node_context.lcc tzresult Lwt.t = +let get_last_cemented_commitment (cctxt : #Client_context.full) rollup_address : + Node_context.lcc tzresult Lwt.t = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ commitment, level = Plugin.RPC.Sc_rollup.last_cemented_commitment_hash_with_level @@ -69,9 +72,12 @@ let get_last_cemented_commitment (cctxt : Protocol_client_context.full) level = Protocol.Alpha_context.Raw_level.to_int32 level; } -let get_last_published_commitment (cctxt : Protocol_client_context.full) - rollup_address operator = +let get_last_published_commitment (cctxt : #Client_context.full) rollup_address + operator = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let*! res = Plugin.RPC.Sc_rollup.staked_on_commitment @@ -98,6 +104,9 @@ let get_last_published_commitment (cctxt : Protocol_client_context.full) let get_kind cctxt rollup_address = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let rollup_address = Sc_rollup_proto_types.Address.of_octez rollup_address in let+ kind = RPC.Sc_rollup.kind cctxt (cctxt#chain, cctxt#block) rollup_address () @@ -106,6 +115,9 @@ let get_kind cctxt rollup_address = let genesis_inbox cctxt ~genesis_level = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ inbox = Plugin.RPC.Sc_rollup.inbox cctxt (cctxt#chain, `Level genesis_level) in @@ -151,6 +163,9 @@ let constants_of_parametric *) let retrieve_constants ?(block = `Head 0) cctxt = let open Lwt_result_syntax in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {parametric; _} = Protocol.Constants_services.all cctxt (cctxt#chain, block) in @@ -159,6 +174,9 @@ let retrieve_constants ?(block = `Head 0) cctxt = let retrieve_genesis_info cctxt rollup_address = let open Lwt_result_syntax in let open Protocol.Alpha_context in + let cctxt = + new Protocol_client_context.wrap_full (cctxt :> Client_context.full) + in let+ {level; commitment_hash} = RPC.Sc_rollup.genesis_info cctxt (cctxt#chain, `Head 0) rollup_address in diff --git a/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.mli b/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.mli index 605c80fcbdd0757210fabb5b06a5ca6edbb02154..c82972f95ba0d8f23472993bc7451a0655999f68 100644 --- a/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.mli +++ b/src/proto_alpha/lib_sc_rollup_node/layer1_helpers.mli @@ -40,19 +40,18 @@ val fetch_tezos_block : val prefetch_tezos_blocks : t -> head list -> unit val get_last_cemented_commitment : - Protocol_client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.lcc tzresult Lwt.t val get_last_published_commitment : - Protocol_client_context.full -> + #Client_context.full -> Address.t -> Signature.public_key_hash -> Commitment.t option tzresult Lwt.t -val get_kind : - Protocol_client_context.full -> Address.t -> Kind.t tzresult Lwt.t +val get_kind : #Client_context.full -> Address.t -> Kind.t tzresult Lwt.t val genesis_inbox : - Protocol_client_context.full -> + #Client_context.full -> genesis_level:int32 -> Octez_smart_rollup.Inbox.t tzresult Lwt.t @@ -64,10 +63,8 @@ val constants_of_parametric : (** Retrieve protocol agnotic constants for the head of the chain. *) val retrieve_constants : ?block:Block_services.block -> - Protocol_client_context.full -> + #Client_context.full -> Rollup_constants.protocol_constants tzresult Lwt.t val retrieve_genesis_info : - Protocol_client_context.full -> - Address.t -> - Node_context.genesis_info tzresult Lwt.t + #Client_context.full -> Address.t -> Node_context.genesis_info tzresult Lwt.t diff --git a/src/proto_alpha/lib_sc_rollup_node/publisher.ml b/src/proto_alpha/lib_sc_rollup_node/publisher.ml index e1180ebffafcc045be416d3e71c550f79118fa82..857b3ea306fe66652a4a5bf5ff891a14cee860cd 100644 --- a/src/proto_alpha/lib_sc_rollup_node/publisher.ml +++ b/src/proto_alpha/lib_sc_rollup_node/publisher.ml @@ -72,11 +72,12 @@ let sub_level level decrement = if r < 0l then None else Some r let sc_rollup_commitment_period node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup + node_ctxt.Node_context.current_protocol.constants.sc_rollup .commitment_period_in_blocks let sc_rollup_challenge_window node_ctxt = - node_ctxt.Node_context.protocol_constants.sc_rollup.challenge_window_in_blocks + node_ctxt.Node_context.current_protocol.constants.sc_rollup + .challenge_window_in_blocks let next_commitment_level node_ctxt last_commitment_level = add_level last_commitment_level (sc_rollup_commitment_period node_ctxt) @@ -482,13 +483,26 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Commitment_event.starting () in let node_ctxt = Node_context.readonly node_ctxt in let+ worker = Worker.launch table () {node_ctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_publisher; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a publisher worker for a single scoru *) let worker = lazy diff --git a/src/proto_alpha/lib_sc_rollup_node/refutation_coordinator.ml b/src/proto_alpha/lib_sc_rollup_node/refutation_coordinator.ml index d78834ff4e3418d7f2697f16a51b26243b1d2bd6..21403e6efef25151bbd555dd43b0bbc7afbe13e3 100644 --- a/src/proto_alpha/lib_sc_rollup_node/refutation_coordinator.ml +++ b/src/proto_alpha/lib_sc_rollup_node/refutation_coordinator.ml @@ -193,7 +193,7 @@ let table = Worker.create_table Queue let worker_promise, worker_waker = Lwt.task () -let init node_ctxt = +let start node_ctxt = let open Lwt_result_syntax in let*! () = Refutation_game_event.Coordinator.starting () in let cctxt = @@ -202,6 +202,19 @@ let init node_ctxt = let+ worker = Worker.launch table () {node_ctxt; cctxt} (module Handlers) in Lwt.wakeup worker_waker worker +let init node_ctxt = + let open Lwt_result_syntax in + match Lwt.state worker_promise with + | Lwt.Return _ -> + (* Worker already started, nothing to do. *) + return_unit + | Lwt.Fail exn -> + (* Worker crashed, not recoverable. *) + fail [Sc_rollup_node_errors.No_refutation_coordinator; Exn exn] + | Lwt.Sleep -> + (* Never started, start it. *) + start node_ctxt + (* This is a refutation coordinator for a single scoru *) let worker = lazy diff --git a/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml b/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml index a5031126122f7d852bf3bf15ccc5966c738d32b0..908b37937384a61761666b51189623abdce4fce9 100644 --- a/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml +++ b/src/proto_alpha/lib_sc_rollup_node/refutation_game.ml @@ -124,7 +124,7 @@ let page_info_from_pvm_state (node_ctxt : _ Node_context.t) ~dal_attestation_lag (* TODO: https://gitlab.com/tezos/tezos/-/issues/5871 Use constants for correct protocol. *) let is_reveal_enabled = - node_ctxt.protocol_constants.sc_rollup.reveal_activation_level + node_ctxt.current_protocol.constants.sc_rollup.reveal_activation_level |> WithExceptions.Option.get ~loc:__LOC__ |> Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez |> Sc_rollup.is_reveal_enabled_predicate diff --git a/src/proto_alpha/lib_sc_rollup_node/rollup_node_plugin.ml b/src/proto_alpha/lib_sc_rollup_node/rollup_node_plugin.ml new file mode 100644 index 0000000000000000000000000000000000000000..6d1642bf8f58d15e8da5aaa7e91613cda1a8d4b5 --- /dev/null +++ b/src/proto_alpha/lib_sc_rollup_node/rollup_node_plugin.ml @@ -0,0 +1,45 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* 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"),*) +(* 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 Plugin : Protocol_plugin_sig.S = struct + let protocol = Protocol.hash + + module RPC_directory = RPC_directory + module Dal_slots_tracker = Dal_slots_tracker + module Inbox = Inbox + module Interpreter = Interpreter + module Publisher = Publisher + module Refutation_coordinator = Refutation_coordinator + module Batcher = Batcher + module Layer1_helpers = Layer1_helpers + + module L1_processing = struct + let check_pvm_initial_state_hash = Daemon.check_initial_state_hash + + let process_l1_block_operations = Daemon.process_l1_block_operations + end +end + +let () = Protocol_plugins.register (module Plugin) diff --git a/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml index 6aeab3d04a78f1bc3ff2ad19ccde8ca0c9e96006..90604b57c821502f1a16d93beebb925b3172a82f 100644 --- a/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml +++ b/src/proto_alpha/lib_sc_rollup_node/test/helpers/helpers.ml @@ -139,10 +139,13 @@ let initialize_node_context ?(constants = default_constants) kind ~boot_sector = new Protocol_client_context.wrap_full (new Faked_client_context.unix_faked ~base_dir ~filesystem) in + let current_protocol = + {Node_context.hash = Protocol.hash; proto_level = 0; constants} + in let* ctxt = Node_context.Internal_for_tests.create_node_context cctxt - constants + current_protocol ~data_dir kind in diff --git a/tezt/lib_tezos/constant.ml b/tezt/lib_tezos/constant.ml index ba10534ca90d20447d29fc1b8f687844bf5c0913..e1c4cb6fb76b679e7ae25e7f4a681d234bab8f98 100644 --- a/tezt/lib_tezos/constant.ml +++ b/tezt/lib_tezos/constant.ml @@ -42,6 +42,8 @@ let dal_node = "./octez-dal-node" let dac_node = "./octez-dac-node" +let smart_rollup_node = "./octez-smart-rollup-node" + (* TODO: tezos/tezos#4803 Can we do better than to depend on script-inputs? *) diff --git a/tezt/lib_tezos/dac_helper.ml b/tezt/lib_tezos/dac_helper.ml index b957cc2eb747e6fcd6f901b31aed122bd6b95cc8..c6674fe2120a3a955cf50f6ad90d8c660f29961e 100644 --- a/tezt/lib_tezos/dac_helper.ml +++ b/tezt/lib_tezos/dac_helper.ml @@ -233,11 +233,10 @@ let with_observer ?name ?sc_rollup_node ?(pvm_name = "arith") (* TODO: https://gitlab.com/tezos/tezos/-/issues/4706 Keep pvm name value in Sc_rollup.t. *) -let with_fresh_rollup ?(pvm_name = "arith") ?hooks ~protocol tezos_node - tezos_client bootstrap1_key f = +let with_fresh_rollup ?(pvm_name = "arith") ?hooks tezos_node tezos_client + bootstrap1_key f = let sc_rollup_node = Sc_rollup_node.create - ~protocol Operator tezos_node ~base_dir:(Client.base_dir tezos_client) @@ -275,7 +274,7 @@ let scenario_with_full_dac_infrastructure ?(tags = ["dac"; "full"]) ?node_arguments ~protocol @@ fun node client key -> - with_fresh_rollup ~protocol ~pvm_name node client key + with_fresh_rollup ~pvm_name node client key @@ fun sc_rollup_address sc_rollup_node -> let range i = List.init i Fun.id in let* committee_members = @@ -339,7 +338,6 @@ let scenario_with_full_dac_infrastructure ?(tags = ["dac"; "full"]) let rollup_node_i = Sc_rollup_node.create ~name:("observer-" ^ Int.to_string i ^ "-rollup-node") - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -432,7 +430,6 @@ let scenario_with_layer1_legacy_and_rollup_nodes node client key - ~protocol ~pvm_name (fun sc_rollup_address sc_rollup_node -> with_legacy_dac_node diff --git a/tezt/lib_tezos/dac_helper.mli b/tezt/lib_tezos/dac_helper.mli index 9a30ac39a4e773c09188f4d2377b3d8a21a6005f..b670e04766dfbe4db38f2fb7f885d6ebd3432b5f 100644 --- a/tezt/lib_tezos/dac_helper.mli +++ b/tezt/lib_tezos/dac_helper.mli @@ -110,7 +110,6 @@ val with_observer : val with_fresh_rollup : ?pvm_name:string -> ?hooks:Process_hooks.t -> - protocol:Protocol.t -> Node.t -> Client.t -> string -> diff --git a/tezt/lib_tezos/protocol.ml b/tezt/lib_tezos/protocol.ml index 20fd56f2bdfeacfad3db9849c4e0ba181930a64a..7be38edeeb7966b604f3f8b6c7ced044e964190a 100644 --- a/tezt/lib_tezos/protocol.ml +++ b/tezt/lib_tezos/protocol.ml @@ -81,8 +81,6 @@ let accuser proto = "./octez-accuser-" ^ daemon_name proto let baker proto = "./octez-baker-" ^ daemon_name proto -let sc_rollup_node proto = "./octez-smart-rollup-node-" ^ daemon_name proto - let sc_rollup_client proto = "./octez-smart-rollup-client-" ^ daemon_name proto let encoding_prefix = function diff --git a/tezt/lib_tezos/protocol.mli b/tezt/lib_tezos/protocol.mli index 2dcb5efae778545f1132ca87badd967ed70b8c85..f8348e3624a425caedbfd4c4a9327e678670dc17 100644 --- a/tezt/lib_tezos/protocol.mli +++ b/tezt/lib_tezos/protocol.mli @@ -80,10 +80,6 @@ val accuser : t -> string (** Get the path of the baker of a protocol, such as ["./octez-baker-alpha"]. *) val baker : t -> string -(** Get the path of the smart rollup node of a protocol, such as - ["./octez-smart-rollup-node-alpha"]. *) -val sc_rollup_node : t -> string - (** Get the path of the smart rollup client of a protocol, such as ["./octez-smart-rollup-client-alpha"]. *) val sc_rollup_client : t -> string diff --git a/tezt/lib_tezos/sc_rollup_node.ml b/tezt/lib_tezos/sc_rollup_node.ml index 6c0aa6758c9c8cd96f39353036066f4558dea92a..f14af4155d5d8d2f52481d2370b3f4e6a93efc3c 100644 --- a/tezt/lib_tezos/sc_rollup_node.ml +++ b/tezt/lib_tezos/sc_rollup_node.ml @@ -294,8 +294,8 @@ let handle_event sc_node {name; value; timestamp = _} = update_level sc_node level | _ -> () -let create_with_endpoint ?protocol ?runner ?path ?name ?color ?data_dir - ~base_dir ?event_pipe ?(rpc_host = "127.0.0.1") ?rpc_port ?(operators = []) +let create_with_endpoint ?runner ?path ?name ?color ?data_dir ~base_dir + ?event_pipe ?(rpc_host = "127.0.0.1") ?rpc_port ?(operators = []) ?default_operator ?(dal_node : Dal_node.t option) mode endpoint = let name = match name with None -> fresh_name () | Some name -> name in let data_dir = @@ -304,11 +304,7 @@ let create_with_endpoint ?protocol ?runner ?path ?name ?color ?data_dir let rpc_port = match rpc_port with None -> Port.fresh () | Some port -> port in - let path = - match path with - | Some path -> path - | None -> Protocol.sc_rollup_node (Option.get protocol) - in + let path = Option.value ~default:Constant.smart_rollup_node path in let sc_node = create ~path @@ -334,11 +330,9 @@ let create_with_endpoint ?protocol ?runner ?path ?name ?color ?data_dir on_event sc_node (handle_event sc_node) ; sc_node -let create ~protocol ?runner ?path ?name ?color ?data_dir ~base_dir ?event_pipe - ?rpc_host ?rpc_port ?operators ?default_operator ?dal_node mode - (node : Node.t) = +let create ?runner ?path ?name ?color ?data_dir ~base_dir ?event_pipe ?rpc_host + ?rpc_port ?operators ?default_operator ?dal_node mode (node : Node.t) = create_with_endpoint - ~protocol ?runner ?path ?name diff --git a/tezt/lib_tezos/sc_rollup_node.mli b/tezt/lib_tezos/sc_rollup_node.mli index 0fcd52c5676336d82116b4ddadaea5232c8ac8ed..5db40c7f74eb4c642e35a2535cb183e535c04d8d 100644 --- a/tezt/lib_tezos/sc_rollup_node.mli +++ b/tezt/lib_tezos/sc_rollup_node.mli @@ -63,7 +63,6 @@ val mode_of_string : string -> mode *) val create : - protocol:Protocol.t -> ?runner:Runner.t -> ?path:string -> ?name:string -> @@ -82,7 +81,6 @@ val create : (** Do not assume we are running the rollup node against a local octez node. *) val create_with_endpoint : - ?protocol:Protocol.t -> ?runner:Runner.t -> ?path:string -> ?name:string -> diff --git a/tezt/long_tests/sc_rollup.ml b/tezt/long_tests/sc_rollup.ml index 3df406b9d6eb32a6def69d5f400693174f8d838d..74333f4e08e5240519fe0414b8fae64a53fc6c82 100644 --- a/tezt/long_tests/sc_rollup.ml +++ b/tezt/long_tests/sc_rollup.ml @@ -113,8 +113,8 @@ let send_message client msg = in Client.bake_for_and_wait client -let with_fresh_rollup ~protocol ?(kind = "arith") ~boot_sector f tezos_node - tezos_client operator = +let with_fresh_rollup ?(kind = "arith") ~boot_sector f tezos_node tezos_client + operator = let* sc_rollup = Sc_rollup_helpers.originate_sc_rollup ~kind @@ -124,7 +124,6 @@ let with_fresh_rollup ~protocol ?(kind = "arith") ~boot_sector f tezos_node in let sc_rollup_node = Sc_rollup_node.create - ~protocol Operator tezos_node ~base_dir:(Client.base_dir tezos_client) @@ -263,7 +262,6 @@ let test_rollup_node_advances_pvm_state protocols ~test_name ~boot_sector (fun protocol -> setup ~protocol @@ fun node client -> with_fresh_rollup - ~protocol ~kind ~boot_sector (fun sc_rollup_address sc_rollup_node _filename -> @@ -279,7 +277,6 @@ let test_rollup_node_advances_pvm_state protocols ~test_name ~boot_sector (fun protocol -> setup ~protocol @@ fun node client -> with_fresh_rollup - ~protocol ~kind ~boot_sector (fun sc_rollup_address sc_rollup_node _filename -> diff --git a/tezt/tests/dal.ml b/tezt/tests/dal.ml index b2710f8add884d8452688958fdfdcead2e65dfba..571103746e175929e628949b655835177a708c60 100644 --- a/tezt/tests/dal.ml +++ b/tezt/tests/dal.ml @@ -156,8 +156,8 @@ let with_layer1 ?custom_constants ?additional_bootstrap_accounts let bootstrap1_key = Constant.bootstrap1.public_key_hash in f dal_parameters cryptobox node client bootstrap1_key -let with_fresh_rollup ~protocol ?(pvm_name = "arith") ?dal_node f tezos_node - tezos_client bootstrap1_key = +let with_fresh_rollup ?(pvm_name = "arith") ?dal_node f tezos_node tezos_client + bootstrap1_key = let* rollup_address = Client.Sc_rollup.originate ~hooks @@ -171,7 +171,6 @@ let with_fresh_rollup ~protocol ?(pvm_name = "arith") ?dal_node f tezos_node in let sc_rollup_node = Sc_rollup_node.create - ~protocol ?dal_node Operator tezos_node @@ -265,7 +264,7 @@ let scenario_with_all_nodes ?custom_constants ?node_arguments ?attestation_lag ~dal_enable @@ fun parameters _cryptobox node client -> with_dal_node node client @@ fun key dal_node -> - ( with_fresh_rollup ~protocol ~pvm_name ~dal_node + ( with_fresh_rollup ~pvm_name ~dal_node @@ fun sc_rollup_address sc_rollup_node -> scenario protocol @@ -2186,7 +2185,6 @@ let create_additional_nodes ~protocol ~extra_node_operators rollup_address in let sc_rollup_node = Sc_rollup_node.create - ~protocol ~dal_node:fresh_dal_node rollup_mode l1_node diff --git a/tezt/tests/evm_rollup.ml b/tezt/tests/evm_rollup.ml index 129307940a40100acb9f0f57015569675d1a2aa5..931234fd0cbfd9c5006beec1efa5c62f68a13462 100644 --- a/tezt/tests/evm_rollup.ml +++ b/tezt/tests/evm_rollup.ml @@ -311,7 +311,6 @@ let setup_evm_kernel ?config in let sc_rollup_node = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -457,7 +456,6 @@ let test_evm_proxy_server_connection = in let sc_rollup_node = Sc_rollup_node.create - ~protocol Observer tezos_node ~base_dir:(Client.base_dir tezos_client) diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index e6ef16693380b013e0352f6eabb3e80dedfda395..f001ebf38d29398738cb715f8103a803afc82d41 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -257,7 +257,6 @@ let setup_rollup ~protocol ~kind ?hooks ?alias ?(mode = Sc_rollup_node.Operator) in let sc_rollup_node = Sc_rollup_node.create - ~protocol mode tezos_node ?data_dir @@ -380,7 +379,7 @@ let test_l2_migration_scenario ?parameters_ty ?(mode = Sc_rollup_node.Operator) let* tezos_node, tezos_client = setup_l1 ?commitment_period ?challenge_window ?timeout migrate_from in - let* rollup_node_from, rollup_client_from, sc_rollup = + let* rollup_node, rollup_client, sc_rollup = setup_rollup ~protocol:migrate_from ?parameters_ty @@ -391,35 +390,16 @@ let test_l2_migration_scenario ?parameters_ty ?(mode = Sc_rollup_node.Operator) tezos_node tezos_client in - (* Rollup node and client for other protocol *) - let data_dir = Sc_rollup_node.data_dir rollup_node_from in - let rollup_node_to = - Sc_rollup_node.create - ~protocol:migrate_to - ~data_dir - ~base_dir:(Client.base_dir tezos_client) - ~default_operator:operator - mode - tezos_node - in - let rollup_client_to = - Sc_rollup_client.create ~protocol:migrate_to rollup_node_to - in - + let* () = Sc_rollup_node.run rollup_node sc_rollup [] in let* prior_res = scenario_prior ~sc_rollup - ~rollup_node_from - ~rollup_client_from - ~rollup_node_to - ~rollup_client_to + ~rollup_node + ~rollup_client tezos_node tezos_client in let migration_level = Node.get_level tezos_node + 1 in - let rollup_node_from_processed_migration_level = - Sc_rollup_node.wait_for_level ~timeout:10. rollup_node_from migration_level - in let patch_config = Node.Config_file.set_sandbox_network_with_user_activated_upgrades [(migration_level, migrate_to)] @@ -435,13 +415,10 @@ let test_l2_migration_scenario ?parameters_ty ?(mode = Sc_rollup_node.Operator) let* () = Node.run ~patch_config tezos_node nodes_args in let* () = Node.wait_for_ready tezos_node in let* () = Client.bake_for_and_wait tezos_client in - let* _ = rollup_node_from_processed_migration_level in scenario_after ~sc_rollup - ~rollup_node_from - ~rollup_client_from - ~rollup_node_to - ~rollup_client_to + ~rollup_node + ~rollup_client tezos_node tezos_client prior_res @@ -1480,28 +1457,69 @@ let test_rollup_node_simple_migration ~kind ~migrate_from ~migrate_to = let description = "node can read data after store migration" in let commitment_period = 10 in let challenge_window = 10 in - let scenario_prior ~sc_rollup ~rollup_node_from ~rollup_client_from:_ - ~rollup_node_to ~rollup_client_to:_ _tezos_node tezos_client = - let* () = Sc_rollup_node.run rollup_node_from sc_rollup [] - and* () = - Sc_rollup_node.run ~wait_ready:false rollup_node_to sc_rollup [] - in + let scenario_prior ~sc_rollup:_ ~rollup_node ~rollup_client:_ _tezos_node + tezos_client = let* () = send_messages commitment_period tezos_client in - let* _ = Sc_rollup_node.wait_sync rollup_node_from ~timeout:10. in + let* _ = Sc_rollup_node.wait_sync rollup_node ~timeout:10. in unit in - let scenario_after ~sc_rollup:_ ~rollup_node_from:_ ~rollup_client_from:_ - ~rollup_node_to ~rollup_client_to tezos_node tezos_client () = + let scenario_after ~sc_rollup:_ ~rollup_node ~rollup_client tezos_node + tezos_client () = let migration_level = Node.get_level tezos_node in let* () = send_messages 1 tezos_client in - let* _ = Sc_rollup_node.wait_sync rollup_node_to ~timeout:10. in + let* _ = Sc_rollup_node.wait_sync rollup_node ~timeout:10. in let*! _l2_block = Sc_rollup_client.rpc_get - rollup_client_to + rollup_client ["global"; "block"; string_of_int (migration_level - 1)] in let*! _l2_block = - Sc_rollup_client.rpc_get rollup_client_to ["global"; "block"; "head"] + Sc_rollup_client.rpc_get rollup_client ["global"; "block"; "head"] + in + unit + in + test_l2_migration_scenario + ~tags + ~kind + ~commitment_period + ~challenge_window + ~migrate_from + ~migrate_to + ~scenario_prior + ~scenario_after + ~description + () + +let test_rollup_node_catchup_migration ~kind ~migrate_from ~migrate_to = + let tags = ["catchup"] in + let description = "node can catch up on protocol migration" in + let commitment_period = 10 in + let challenge_window = 10 in + let scenario_prior ~sc_rollup:_ ~rollup_node ~rollup_client:_ _tezos_node + tezos_client = + let* () = send_messages 1 tezos_client in + let* _ = Sc_rollup_node.wait_sync rollup_node ~timeout:10. in + Log.info "Stopping rollup node before protocol migration." ; + let* () = Sc_rollup_node.terminate rollup_node in + Log.info "Sending more messages on L1." ; + send_messages (commitment_period - 1) tezos_client + in + let scenario_after ~sc_rollup ~rollup_node ~rollup_client tezos_node + tezos_client () = + let migration_level = Node.get_level tezos_node in + let* () = send_messages 1 tezos_client in + Log.info "Restarting rollup node after migration." ; + let* () = Sc_rollup_node.run rollup_node sc_rollup [] in + Log.info "Waiting for rollup node to catch up." ; + let* _ = Sc_rollup_node.wait_sync rollup_node ~timeout:100. in + Log.info "Rollup node has caught up!" ; + let*! _l2_block = + Sc_rollup_client.rpc_get + rollup_client + ["global"; "block"; string_of_int (migration_level - 1)] + in + let*! _l2_block = + Sc_rollup_client.rpc_get rollup_client ["global"; "block"; "head"] in unit in @@ -1719,7 +1737,6 @@ let mode_publish mode publishes protocol sc_rollup_node sc_rollup_client let sc_rollup_other_node = (* Other rollup node *) Sc_rollup_node.create - ~protocol mode node' ~base_dir:(Client.base_dir client') @@ -1936,7 +1953,6 @@ let commitment_stored_robust_to_failures protocol sc_rollup_node let* client' = Client.init ?endpoint:(Some (Node node)) () in let sc_rollup_node' = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client') @@ -2319,7 +2335,6 @@ let commitment_before_lcc_not_published protocol sc_rollup_node sc_rollup_client let* client' = Client.init ?endpoint:(Some (Node node)) () in let sc_rollup_node' = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client') @@ -2449,7 +2464,6 @@ let first_published_level_is_global protocol sc_rollup_node sc_rollup_client let* client' = Client.init ?endpoint:(Some (Node node)) () in let sc_rollup_node' = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client') @@ -2498,7 +2512,7 @@ let first_published_level_is_global protocol sc_rollup_node sc_rollup_client unit (* TODO: https://gitlab.com/tezos/tezos/-/issues/4373 *) -let _test_reinject_failed_commitment ~protocol ~kind = +let _test_reinject_failed_commitment ~protocol:_ ~kind = let commitment_period = 3 in test_full_scenario ~kind @@ -2511,7 +2525,6 @@ let _test_reinject_failed_commitment ~protocol ~kind = @@ fun _protocol sc_rollup_node1 _sc_rollup_client1 sc_rollup node client -> let sc_rollup_node2 = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -2960,7 +2973,6 @@ let test_cement_ignore_commitment ~kind = @@ fun protocol _sc_rollup_node _sc_rollup_client sc_rollup node client -> let sc_rollup_node = Sc_rollup_node.create - ~protocol Custom node ~base_dir:(Client.base_dir client) @@ -3130,7 +3142,6 @@ let test_refutation_scenario ?commitment_period ?challenge_window ~variant ~mode List.map2 (fun default_operator _ -> Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -3878,7 +3889,8 @@ let test_late_rollup_node = variant = None; description = "a late rollup should catch up"; } - @@ fun protocol sc_rollup_node _rollup_client sc_rollup_address node client -> + @@ fun _protocol sc_rollup_node _rollup_client sc_rollup_address node client + -> let* () = bake_levels 65 client in let* () = Sc_rollup_node.run sc_rollup_node sc_rollup_address [] in let* () = bake_levels 30 client in @@ -3886,7 +3898,6 @@ let test_late_rollup_node = Log.info "First rollup node synchronized." ; let sc_rollup_node2 = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -3929,7 +3940,8 @@ let test_late_rollup_node_2 = variant = None; description = "a late alternative rollup should catch up"; } - @@ fun protocol sc_rollup_node _rollup_client sc_rollup_address node client -> + @@ fun _protocol sc_rollup_node _rollup_client sc_rollup_address node client + -> let* () = bake_levels 65 client in let* () = Sc_rollup_node.run sc_rollup_node sc_rollup_address [] in let* () = bake_levels 30 client in @@ -3937,7 +3949,6 @@ let test_late_rollup_node_2 = Log.info "First rollup node synchronized." ; let sc_rollup_node2 = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -5326,7 +5337,6 @@ let test_injector_auto_discard = (* Change operator and only batch messages *) let sc_rollup_node = Sc_rollup_node.create - ~protocol Batcher tezos_node ~base_dir:(Client.base_dir client) @@ -5468,7 +5478,6 @@ let test_rollup_node_missing_preimage_exit_at_initialisation = let* node, client = setup_l1 protocol in let rollup_node = Sc_rollup_node.create - ~protocol ~base_dir:(Client.base_dir client) ~default_operator:Constant.bootstrap1.alias Operator @@ -5685,6 +5694,7 @@ let register_migration ~kind ~migrate_from ~migrate_to = test_migration_refute ~kind ~migrate_from ~migrate_to ; test_cont_refute_pre_migration ~kind ~migrate_from ~migrate_to ; test_rollup_node_simple_migration ~kind ~migrate_from ~migrate_to ; + test_rollup_node_catchup_migration ~kind ~migrate_from ~migrate_to ; test_migration_removes_dead_games ~kind ~migrate_from ~migrate_to let register_migration ~migrate_from ~migrate_to = diff --git a/tezt/tests/tx_sc_rollup.ml b/tezt/tests/tx_sc_rollup.ml index 5e8b704f82399a85762210f6474a0164dfeaf0f5..dc7ddf10de1fb8be687433b9619861a0d3f5212e 100644 --- a/tezt/tests/tx_sc_rollup.ml +++ b/tezt/tests/tx_sc_rollup.ml @@ -300,7 +300,6 @@ let setup_classic ~commitment_period ~challenge_window protocol = let bootstrap1_key = Constant.bootstrap1.alias in let sc_rollup_node = Sc_rollup_node.create - ~protocol Operator node ~base_dir:(Client.base_dir client) @@ -358,7 +357,6 @@ let setup_bootstrap ~commitment_period ~challenge_window protocol = in let sc_rollup_node = Sc_rollup_node.create - ~protocol Operator node ~data_dir