diff --git a/src/proto_alpha/lib_protocol/sc_rollup_proof_repr.ml b/src/proto_alpha/lib_protocol/sc_rollup_proof_repr.ml index a0b3b38530a97e939d04ee13e9f6c82b311c621d..6892a67f0051541ae6facf27783197c55a7662a8 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_proof_repr.ml +++ b/src/proto_alpha/lib_protocol/sc_rollup_proof_repr.ml @@ -367,31 +367,109 @@ let valid (type state proof output) ~dal_attestation_lag ~dal_number_of_slots ~is_reveal_enabled ~dal_attested_slots_validity_lag (proof : proof t) = let open Lwt_result_syntax in + let open Sc_rollup_PVM_sig in let (module P) = pvm in let origination_level = metadata.Sc_rollup_metadata_repr.origination_level in - let* input = - match proof.input_proof with - | None -> return_none - | Some (Inbox_proof {level; message_counter; proof}) -> - let*? inbox_message = - check_inbox_proof snapshot proof (level, Z.succ message_counter) - in - return - @@ Option.map (fun i -> Sc_rollup_PVM_sig.Inbox_message i) inbox_message - | Some First_inbox_message -> - let*? payload = - Sc_rollup_inbox_message_repr.(serialize (Internal Start_of_level)) - in - let inbox_level = Raw_level_repr.succ origination_level in - let message_counter = Z.zero in - return_some - Sc_rollup_PVM_sig.( - Inbox_message {inbox_level; message_counter; payload}) - | Some (Reveal_proof (Raw_data_proof data)) -> - return_some (Sc_rollup_PVM_sig.Reveal (Raw_data data)) - | Some (Reveal_proof Metadata_proof) -> - return_some (Sc_rollup_PVM_sig.Reveal (Metadata metadata)) - | Some (Reveal_proof (Dal_page_proof {proof; page_id})) -> + + (* Helper function to check that the requested input pretended by the given + proof matches the input request deduced from the PVM state. *) + let verify_proof_and_check_input_proof_against_input_request input_opt checker + = + let input_opt = + Option.bind + input_opt + (cut_at_level ~origination_level ~commit_inbox_level) + in + let* input_requested = + P.verify_proof ~is_reveal_enabled input_opt proof.pvm_step + in + let* () = checker input_requested in + return (input_opt, input_requested) + in + + let inbox_proof_and_input_request_are_dissociated () = + proof_error "Inbox proof and input request are dissociated." + in + + match proof.input_proof with + (* Case where the proof says that no input is needed: in this case, we check + that the PVM didn't request any input. *) + | None -> + verify_proof_and_check_input_proof_against_input_request None (function + | No_input_required -> return_unit + | _ -> inbox_proof_and_input_request_are_dissociated ()) + (* Case where the proof pretends that the PVM consumes the first inbox + message: we check in this case that the PVM is indeed requesting the + initial message. *) + | Some First_inbox_message -> + let*? payload = + Sc_rollup_inbox_message_repr.(serialize (Internal Start_of_level)) + in + let inbox_level = Raw_level_repr.succ origination_level in + let message_counter = Z.zero in + let input_opt = + Some (Inbox_message {inbox_level; message_counter; payload}) + in + verify_proof_and_check_input_proof_against_input_request + input_opt + (function + | Initial -> + (* If the state is [Initial], we don't need a proof of the input, + we know it's the [Start_of_level] after the origination. *) + return_unit + | _ -> inbox_proof_and_input_request_are_dissociated ()) + (* Case where the proof pretends that the PVM consumes an inbox message which + is not the first one: below, we check that the PVM is indeed requesting a + non intial inbox message corresponding to the given level and messages + counter. *) + | Some (Inbox_proof {level; message_counter; proof}) -> + let*? inbox_message = + check_inbox_proof snapshot proof (level, Z.succ message_counter) + in + let input_opt = Option.map (fun i -> Inbox_message i) inbox_message in + verify_proof_and_check_input_proof_against_input_request + input_opt + (function + | First_after (l, n) -> + check + (Raw_level_repr.(level = l) && Z.(equal message_counter n)) + "Level and index of inbox proof are not equal to the one \ + expected in input request." + | _ -> inbox_proof_and_input_request_are_dissociated ()) + (* Case where the proof pretends that the PVM consumes external data: + we check that the PVM is indeed requesting data and that the + provided data's hash corresponds to the one expected by the PVM. *) + | Some (Reveal_proof (Raw_data_proof data)) -> + let input_opt = Some (Reveal (Raw_data data)) in + verify_proof_and_check_input_proof_against_input_request + input_opt + (function + | Needs_reveal (Reveal_raw_data expected_hash) -> + let scheme = Sc_rollup_reveal_hash.scheme_of_hash expected_hash in + let data_hash = + Sc_rollup_reveal_hash.hash_string ~scheme [data] + in + check + (Sc_rollup_reveal_hash.equal data_hash expected_hash) + "Invalid reveal" + | _ -> inbox_proof_and_input_request_are_dissociated ()) + (* Case where the provided proof pretends that the PVM is asking for metadata + reveal: we check in this case that the PVM is actually requesting metadata + reveal. *) + | Some (Reveal_proof Metadata_proof) -> + let input_opt = Some (Reveal (Metadata metadata)) in + verify_proof_and_check_input_proof_against_input_request + input_opt + (function + | Needs_reveal Reveal_metadata -> return_unit + | _ -> inbox_proof_and_input_request_are_dissociated ()) + (* Case where the provided proof pretends that the PVM is asking for + (Adjustable) DAL page reveal: we check in this case that the DAL proof is + consistent t what's stored in the (DAL) skip list. We then check that the + PVM is actually requesting a DAL page whose ID coincides with the one of + the given proof. *) + | Some (Reveal_proof (Dal_page_proof {proof; page_id})) -> + let*? input_opt = Dal_helpers.verify ~protocol_activation_level ~dal_number_of_slots @@ -404,15 +482,31 @@ let valid (type state proof output) page_id dal_snapshot proof - |> Lwt.return - | Some (Reveal_proof Dal_parameters_proof) -> - (* FIXME: https://gitlab.com/tezos/tezos/-/issues/6562 - Support revealing historical DAL parameters. - - Currently, we do not support revealing DAL parameters for the past. - We ignore the given [published_level] and use the DAL parameters. *) - return_some - (Sc_rollup_PVM_sig.Reveal + in + verify_proof_and_check_input_proof_against_input_request + input_opt + (function + | Needs_reveal (Request_dal_page pid) -> + check + (Dal_slot_repr.Page.equal page_id pid) + "Dal proof's page ID is not the one expected in input request." + | Needs_reveal (Request_adal_page _pid) -> + (* ADAL/FIXME: https://gitlab.com/tezos/tezos/-/milestones/410 implement + refutation games for adaptive DAL. *) + assert false + | _ -> inbox_proof_and_input_request_are_dissociated ()) + (* Case where the provided proof pretends that the PVM is asking for DAL + parameters. In this case, we just check that the PVM is requesting DAL + parameters revelation. *) + | Some (Reveal_proof Dal_parameters_proof) -> + (* FIXME: https://gitlab.com/tezos/tezos/-/issues/6562 + Support revealing historical DAL parameters. + + Currently, we do not support revealing DAL parameters for the past. + We ignore the given [published_level] and use the DAL parameters. *) + let input_opt = + Some + (Reveal (Dal_parameters Sc_rollup_dal_parameters_repr. { @@ -421,58 +515,12 @@ let valid (type state proof output) slot_size = Int64.of_int dal_parameters.slot_size; page_size = Int64.of_int dal_parameters.page_size; })) - in - let input = - Option.bind input (cut_at_level ~origination_level ~commit_inbox_level) - in - let* input_requested = - P.verify_proof ~is_reveal_enabled input proof.pvm_step - in - let* () = - match (proof.input_proof, input_requested) with - | None, No_input_required -> return_unit - | Some First_inbox_message, Initial -> - (* If the state is [Initial], we don't need a proof of the input, - we know it's the [Start_of_level] after the origination. *) - return_unit - | Some (Inbox_proof {level; message_counter; proof = _}), First_after (l, n) - -> - check - (Raw_level_repr.(level = l) && Z.(equal message_counter n)) - "Level and index of inbox proof are not equal to the one expected in \ - input request." - | ( Some (Reveal_proof (Raw_data_proof data)), - Needs_reveal (Reveal_raw_data expected_hash) ) -> - let scheme = Sc_rollup_reveal_hash.scheme_of_hash expected_hash in - - let data_hash = Sc_rollup_reveal_hash.hash_string ~scheme [data] in - check - (Sc_rollup_reveal_hash.equal data_hash expected_hash) - "Invalid reveal" - | Some (Reveal_proof Metadata_proof), Needs_reveal Reveal_metadata -> - return_unit - | ( Some (Reveal_proof (Dal_page_proof {page_id; proof = _})), - Needs_reveal (Request_dal_page pid) ) -> - check - (Dal_slot_repr.Page.equal page_id pid) - "Dal proof's page ID is not the one expected in input request." - | ( Some (Reveal_proof Dal_parameters_proof), - Needs_reveal Reveal_dal_parameters ) -> - return_unit - | Some (Reveal_proof _), Needs_reveal (Request_dal_page _pid) -> - (* ADAL/FIXME: https://gitlab.com/tezos/tezos/-/milestones/410 implement - refutation games for adaptive DAL. *) - assert false - | None, (Initial | First_after _ | Needs_reveal _) - | Some _, No_input_required - | Some (Inbox_proof _), Needs_reveal _ - | _ -> - (* ADAL/TODO: https://gitlab.com/tezos/tezos/-/milestones/410 - - Remove this fragile pattern matching. *) - proof_error "Inbox proof and input request are dissociated." - in - return (input, input_requested) + in + verify_proof_and_check_input_proof_against_input_request + input_opt + (function + | Needs_reveal Reveal_dal_parameters -> return_unit + | _ -> inbox_proof_and_input_request_are_dissociated ()) module type PVM_with_context_and_state = sig include Sc_rollups.PVM.S