https://gitlab.com/tezos/tezos
Tip revision: cb44fe210c08b8a1bb8da6ec4fd17c11fb75b6c0 authored by martoon on 29 October 2023, 16:52:10 UTC
MIR: fixup! MIR: Add high-level storage operations
MIR: fixup! MIR: Add high-level storage operations
Tip revision: cb44fe2
refutation_game_helpers.ml
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2023 Nomadic Labs, <contact@nomadic-labs.com> *)
(* Copyright (c) 2023 TriliTech <contact@trili.tech> *)
(* Copyright (c) 2023 Functori, <contact@functori.com> *)
(* *)
(* 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 Protocol
open Alpha_context
(** This function computes the inclusion/membership proof of the page
identified by [page_id] in the slot whose data are provided in
[slot_data]. *)
let page_membership_proof params page_index slot_data =
(* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/4048
Rely on DAL node to compute page membership proof and drop
the dal-crypto dependency from the rollup node. *)
let proof =
let open Result_syntax in
(* The computation of the page's proof below can be a bit costly. In fact,
it involves initialising a cryptobox environment and some non-trivial
crypto processing. *)
let* dal = Cryptobox.make params in
let* polynomial = Cryptobox.polynomial_from_slot dal slot_data in
Cryptobox.prove_page dal polynomial page_index
in
let open Lwt_result_syntax in
match proof with
| Ok proof -> return proof
| Error e ->
failwith
"%s"
(match e with
| `Fail s -> "Fail " ^ s
| `Page_index_out_of_range -> "Page_index_out_of_range"
| `Slot_wrong_size s -> "Slot_wrong_size: " ^ s
| `Invalid_degree_strictly_less_than_expected _ as commit_error ->
Cryptobox.string_of_commit_error commit_error)
(** When the PVM is waiting for a Dal page input, this function attempts to
retrieve the page's content from the store, the data of its slot. Then it
computes the proof that the page is part of the slot and returns the
content along with the proof.
If the PVM is not waiting for a Dal page input, or if the slot is known to
be unconfirmed on L1, this function returns [None]. If the data of the
slot are not saved to the store, the function returns a failure
in the error monad. *)
let page_info_from_pvm_state constants (node_ctxt : _ Node_context.t)
~inbox_level (dal_params : Dal.parameters) start_state =
let open Lwt_result_syntax in
let module PVM = (val Pvm.of_kind node_ctxt.kind) in
let dal_attestation_lag = constants.Rollup_constants.dal.attestation_lag in
let is_reveal_enabled =
match constants.sc_rollup.reveal_activation_level with
| Some reveal_activation_level ->
Sc_rollup.is_reveal_enabled_predicate
(Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez
reveal_activation_level)
| None ->
(* For older protocol, constants don't have the notion of reveal
activation level. *)
fun ~current_block_level:_ _ -> true
in
let*! input_request = PVM.is_input_state ~is_reveal_enabled start_state in
match input_request with
| Sc_rollup.(Needs_reveal (Request_dal_page page_id)) -> (
let Dal.Page.{slot_id; page_index} = page_id in
let* pages =
Dal_pages_request.slot_pages
~dal_attestation_lag
~inbox_level
node_ctxt
slot_id
in
match pages with
| None -> return_none (* The slot is not confirmed. *)
| Some pages -> (
let pages_per_slot = dal_params.slot_size / dal_params.page_size in
(* check invariant that pages' length is correct. *)
(* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/4031
It's better to do the check when the slots are saved into disk. *)
(* FIXME/DAL: https://gitlab.com/tezos/tezos/-/issues/3997
This check is not resilient to dal parameters change. *)
match List.nth_opt pages page_index with
| Some content ->
let* page_proof =
page_membership_proof dal_params page_index
@@ Bytes.concat Bytes.empty pages
in
return_some (content, page_proof)
| None ->
failwith
"Page index %d too big or negative.\n\
Number of pages in a slot is %d."
page_index
pages_per_slot))
| _ -> return_none
let metadata (node_ctxt : _ Node_context.t) =
let address = node_ctxt.rollup_address in
let origination_level = Raw_level.of_int32_exn node_ctxt.genesis_info.level in
Sc_rollup.Metadata.{address; origination_level}
let generate_proof (node_ctxt : _ Node_context.t)
(game : Octez_smart_rollup.Game.t) start_state =
let open Lwt_result_syntax in
let module PVM = (val Pvm.of_kind node_ctxt.kind) in
let snapshot =
Sc_rollup_proto_types.Inbox.history_proof_of_octez game.inbox_snapshot
in
(* NOTE: [snapshot_level_int32] below refers to the level of the snapshotted
inbox (from the skip list) which also matches [game.start_level - 1]. *)
let snapshot_level_int32 =
(Octez_smart_rollup.Inbox.Skip_list.content game.inbox_snapshot).level
in
let get_snapshot_head () =
let+ hash = Node_context.hash_of_level node_ctxt snapshot_level_int32 in
Layer1.{hash; level = snapshot_level_int32}
in
let* context =
let* start_hash = Node_context.hash_of_level node_ctxt game.inbox_level in
let+ context = Node_context.checkout_context node_ctxt start_hash in
Context.index context
in
let* dal_slots_history =
if Node_context.dal_supported node_ctxt then
let* snapshot_head = get_snapshot_head () in
Dal_slots_tracker.slots_history_of_hash node_ctxt snapshot_head
else return Dal.Slots_history.genesis
in
let* dal_slots_history_cache =
if Node_context.dal_supported node_ctxt then
let* snapshot_head = get_snapshot_head () in
Dal_slots_tracker.slots_history_cache_of_hash node_ctxt snapshot_head
else return (Dal.Slots_history.History_cache.empty ~capacity:0L)
in
(* We fetch the value of protocol constants at block snapshot level
where the game started. *)
let* constants =
Protocol_plugins.get_constants_of_level node_ctxt snapshot_level_int32
in
let dal_l1_parameters = constants.dal in
let dal_parameters = dal_l1_parameters.cryptobox_parameters in
let dal_attestation_lag = dal_l1_parameters.attestation_lag in
let* page_info =
page_info_from_pvm_state
constants
~inbox_level:game.inbox_level
node_ctxt
dal_parameters
start_state
in
let module P = struct
include PVM
let context = context
let state = start_state
let reveal hash =
let open Lwt_syntax in
let* res =
Reveals.get
~dac_client:node_ctxt.dac_client
~data_dir:node_ctxt.data_dir
~pvm_kind:(Sc_rollup_proto_types.Kind.to_octez PVM.kind)
hash
in
match res with Ok data -> return @@ Some data | Error _ -> return None
module Inbox_with_history = struct
let inbox = snapshot
let get_history inbox_hash =
let open Lwt_syntax in
let+ inbox = Node_context.find_inbox node_ctxt inbox_hash in
match inbox with
| Error err ->
Format.kasprintf
Stdlib.failwith
"Refutation game: Cannot get inbox history for %a, %a"
Sc_rollup.Inbox.Hash.pp
inbox_hash
pp_print_trace
err
| Ok inbox ->
Option.map
(fun i ->
Sc_rollup.Inbox.take_snapshot
(Sc_rollup_proto_types.Inbox.of_octez i))
inbox
let get_payloads_history witness =
Lwt.map
(WithExceptions.Result.to_exn_f
~error:(Format.kasprintf Stdlib.failwith "%a" pp_print_trace))
@@
let open Lwt_result_syntax in
let* {is_first_block; predecessor; predecessor_timestamp; messages} =
Node_context.get_messages node_ctxt witness
in
let*? hist =
Inbox.payloads_history_of_messages
~is_first_block
~predecessor
~predecessor_timestamp
messages
in
return hist
end
module Dal_with_history = struct
let confirmed_slots_history = dal_slots_history
let get_history ptr =
Dal.Slots_history.History_cache.find ptr dal_slots_history_cache
|> Lwt.return
let dal_attestation_lag = dal_attestation_lag
let dal_parameters = dal_parameters
let page_info = page_info
end
end in
let metadata = metadata node_ctxt in
let*! start_tick = PVM.get_tick start_state in
let is_reveal_enabled =
match constants.sc_rollup.reveal_activation_level with
| Some reveal_activation_level ->
Sc_rollup.is_reveal_enabled_predicate
(Sc_rollup_proto_types.Constants.reveal_activation_level_of_octez
reveal_activation_level)
| None ->
(* Constants for an older protocol, there is no notion of reveal
activation level for those. *)
(* TODO: https://gitlab.com/tezos/tezos/-/issues/6247
default value for is_reveal_enabled that returns true for all reveal
supported in protocols <= 18. *)
fun ~current_block_level:_ _ -> true
in
let* proof =
trace
(Sc_rollup_node_errors.Cannot_produce_proof
{
inbox_level = game.inbox_level;
start_tick = Sc_rollup.Tick.to_z start_tick;
})
@@ (Sc_rollup.Proof.produce
~metadata
(module P)
(Raw_level.of_int32_exn game.inbox_level)
~is_reveal_enabled
>|= Environment.wrap_tzresult)
in
let*? pvm_step =
Sc_rollup.Proof.unserialize_pvm_step ~pvm:(module PVM) proof.pvm_step
|> Environment.wrap_tzresult
in
let unserialized_proof = {proof with pvm_step} in
let*! res =
Sc_rollup.Proof.valid
~metadata
snapshot
(Raw_level.of_int32_exn game.inbox_level)
dal_slots_history
dal_parameters
~dal_attestation_lag
~pvm:(module PVM)
unserialized_proof
~is_reveal_enabled
>|= Environment.wrap_tzresult
in
assert (Result.is_ok res) ;
let proof =
Data_encoding.Binary.to_string_exn Sc_rollup.Proof.encoding proof
in
return proof
let make_dissection plugin (node_ctxt : _ Node_context.t) ~start_state
~start_chunk ~our_stop_chunk ~default_number_of_sections ~last_level =
let open Lwt_result_syntax in
let module PVM = (val Pvm.of_kind node_ctxt.kind) in
let state_of_tick ?start_state tick =
Interpreter.state_of_tick
plugin
node_ctxt
?start_state
~tick:(Sc_rollup.Tick.to_z tick)
last_level
in
let state_hash_of_eval_state Pvm_plugin_sig.{state_hash; _} = state_hash in
let start_chunk =
Sc_rollup_proto_types.Game.dissection_chunk_of_octez start_chunk
in
let our_stop_chunk =
Sc_rollup_proto_types.Game.dissection_chunk_of_octez our_stop_chunk
in
let+ dissection =
Game_helpers.make_dissection
~state_of_tick
~state_hash_of_eval_state
?start_state
~start_chunk
~our_stop_chunk
@@ PVM.new_dissection
~start_chunk
~our_stop_chunk
~default_number_of_sections
in
List.map Sc_rollup_proto_types.Game.dissection_chunk_to_octez dissection
let timeout_reached node_ctxt ~self ~opponent =
let open Lwt_result_syntax in
let Node_context.{rollup_address; cctxt; _} = node_ctxt in
let+ game_result =
Plugin.RPC.Sc_rollup.timeout_reached
(new Protocol_client_context.wrap_full cctxt)
(cctxt#chain, `Head 0)
rollup_address
self
opponent
in
let open Sc_rollup.Game in
match game_result with
| Some (Loser {loser; _}) ->
let is_it_me = Signature.Public_key_hash.(self = loser) in
not is_it_me
| _ -> false
let get_conflicts cctxt rollup staker =
let open Lwt_result_syntax in
let cctxt = new Protocol_client_context.wrap_full cctxt in
let+ conflicts =
Plugin.RPC.Sc_rollup.conflicts cctxt (cctxt#chain, `Head 0) rollup staker
in
List.map Sc_rollup_proto_types.Game.conflict_to_octez conflicts
let get_ongoing_games cctxt rollup staker =
let open Lwt_result_syntax in
let cctxt = new Protocol_client_context.wrap_full cctxt in
let+ games =
Plugin.RPC.Sc_rollup.ongoing_refutation_games
cctxt
(cctxt#chain, `Head 0)
rollup
staker
in
List.map
(fun (game, staker1, staker2) ->
(Sc_rollup_proto_types.Game.to_octez game, staker1, staker2))
games