diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index f31a5f35ed113f3d00bbf2c22a8dd8e8e2ee402b..de9a9886135455bdd927c0d055da8df083759aca 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -82,6 +82,9 @@ Minor Changes - Patch smart contracts containing deprecated annotations. (MR :gl:`!5752`) +- Errors related to consensus operations have been reworked. See + ``Validate_errors.Consensus``. (MR :gl:`!5927`) + Internal -------- @@ -117,3 +120,7 @@ Internal - split ``check_vdf_and_update_seed`` function from ``seed_storage.ml`` between the checks part, ``check_vdf``, and the application part, ``update_seed``. (MR :gl:`!5849`) + +- Move the checks part of consensus operation to + ``validate_operation.ml``. The effects part remains in + ``apply_operation``. (MR :gl:`!5927`) diff --git a/src/proto_alpha/lib_plugin/RPC.ml b/src/proto_alpha/lib_plugin/RPC.ml index 2eb1d9003d74195d1b22237391b6343bda758969..05a50d330f384226abda4bc86bf13e686967bc4d 100644 --- a/src/proto_alpha/lib_plugin/RPC.ml +++ b/src/proto_alpha/lib_plugin/RPC.ml @@ -851,10 +851,7 @@ module Scripts = struct let operation : _ operation = {shell; protocol_data} in let oph = Operation.hash operation in let validate_operation_info, validate_operation_state = - Validate_operation.init_info_and_state - ctxt - Validate_operation.Mempool - chain_id + Validate_operation.begin_no_predecessor_info ctxt chain_id in Validate_operation.validate_operation validate_operation_info @@ -863,19 +860,10 @@ module Scripts = struct oph operation >>=? fun (_validate_operation_state, op_validated_stamp) -> - let apply_mode = - (* To simulate the injection of an operation in the mempool, - we want a mode that behaves similarly to - {!Apply.Partial_construction}. However, we don't have - access to information such as the [predecessor_round]. So - we use a mode that doesn't need this information, and - consequently doesn't support consensus operations. *) - Apply.Mempool_no_consensus_op - in Apply.apply_operation ctxt chain_id - apply_mode + (Apply.Partial_construction {predecessor_level = None}) ~payload_producer:Signature.Public_key_hash.zero op_validated_stamp oph diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 4eabeca9c535fa7182802c1083b8ed2038e3a496..688cefe371dc368fd182550dc306550790358cca 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -31,43 +31,7 @@ open Alpha_context type error += | Not_enough_endorsements of {required : int; provided : int} - | Wrong_consensus_operation_branch of Block_hash.t * Block_hash.t - | Wrong_level_for_consensus_operation of { - expected : Raw_level.t; - provided : Raw_level.t; - } - | Wrong_round_for_consensus_operation of { - expected : Round.t; - provided : Round.t; - } - | Preendorsement_round_too_high of {block_round : Round.t; provided : Round.t} - | Unexpected_endorsement_in_block - | Unexpected_preendorsement_in_block - | Wrong_payload_hash_for_consensus_operation of { - expected : Block_payload_hash.t; - provided : Block_payload_hash.t; - } - | Wrong_slot_used_for_consensus_operation - | Consensus_operation_for_future_level of { - expected : Raw_level.t; - provided : Raw_level.t; - } - | Consensus_operation_for_future_round of { - expected : Round.t; - provided : Round.t; - } - | Consensus_operation_for_old_level of { - expected : Raw_level.t; - provided : Raw_level.t; - } - | Consensus_operation_for_old_round of { - expected : Round.t; - provided : Round.t; - } - | Consensus_operation_on_competing_proposal of { - expected : Block_payload_hash.t; - provided : Block_payload_hash.t; - } + | Faulty_validation_wrong_slot | Set_deposits_limit_on_unregistered_delegate of Signature.Public_key_hash.t | Set_deposits_limit_too_high of {limit : Tez.t; max_limit : Tez.t} | Error_while_taking_fees @@ -83,18 +47,8 @@ type error += | Failing_noop_error | Zero_frozen_deposits of Signature.Public_key_hash.t | Invalid_transfer_to_sc_rollup_from_implicit_account - | Mode_forbids_consensus_operations let () = - register_error_kind - `Permanent - ~id:"operations.wrong_slot" - ~title:"Wrong slot" - ~description:"wrong slot" - ~pp:(fun ppf () -> Format.fprintf ppf "wrong slot") - Data_encoding.empty - (function Wrong_slot_used_for_consensus_operation -> Some () | _ -> None) - (fun () -> Wrong_slot_used_for_consensus_operation) ; register_error_kind `Permanent ~id:"operation.not_enough_endorsements" @@ -113,250 +67,19 @@ let () = | Not_enough_endorsements {required; provided} -> Some (required, provided) | _ -> None) (fun (required, provided) -> Not_enough_endorsements {required; provided}) ; - register_error_kind - `Temporary - ~id:"operation.wrong_consensus_operation_branch" - ~title:"Wrong consensus operation branch" - ~description: - "Trying to include an endorsement or preendorsement which points to the \ - wrong block.\n\ - \ It should be the predecessor for preendorsements and the \ - grandfather for endorsements." - ~pp:(fun ppf (e, p) -> - Format.fprintf - ppf - "Wrong branch %a, expected %a" - Block_hash.pp - p - Block_hash.pp - e) - Data_encoding.( - obj2 - (req "expected" Block_hash.encoding) - (req "provided" Block_hash.encoding)) - (function - | Wrong_consensus_operation_branch (e, p) -> Some (e, p) | _ -> None) - (fun (e, p) -> Wrong_consensus_operation_branch (e, p)) ; - register_error_kind - `Permanent - ~id:"wrong_level_for_consensus_operation" - ~title:"Wrong level for consensus operation" - ~description:"Wrong level for consensus operation." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Wrong level for consensus operation (expected: %a, provided: %a)." - Raw_level.pp - expected - Raw_level.pp - provided) - Data_encoding.( - obj2 - (req "expected" Raw_level.encoding) - (req "provided" Raw_level.encoding)) - (function - | Wrong_level_for_consensus_operation {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Wrong_level_for_consensus_operation {expected; provided}) ; - register_error_kind - `Permanent - ~id:"wrong_round_for_consensus_operation" - ~title:"Wrong round for consensus operation" - ~description:"Wrong round for consensus operation." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Wrong round for consensus operation (expected: %a, provided: %a)." - Round.pp - expected - Round.pp - provided) - Data_encoding.( - obj2 (req "expected" Round.encoding) (req "provided" Round.encoding)) - (function - | Wrong_round_for_consensus_operation {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Wrong_round_for_consensus_operation {expected; provided}) ; - register_error_kind - `Permanent - ~id:"preendorsement_round_too_high" - ~title:"Preendorsement round too high" - ~description:"Preendorsement round too high." - ~pp:(fun ppf (block_round, provided) -> - Format.fprintf - ppf - "Preendorsement round too high (block_round: %a, provided: %a)." - Round.pp - block_round - Round.pp - provided) - Data_encoding.( - obj2 (req "block_round" Round.encoding) (req "provided" Round.encoding)) - (function - | Preendorsement_round_too_high {block_round; provided} -> - Some (block_round, provided) - | _ -> None) - (fun (block_round, provided) -> - Preendorsement_round_too_high {block_round; provided}) ; - register_error_kind - `Permanent - ~id:"wrong_payload_hash_for_consensus_operation" - ~title:"Wrong payload hash for consensus operation" - ~description:"Wrong payload hash for consensus operation." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Wrong payload hash for consensus operation (expected: %a, provided: \ - %a)." - Block_payload_hash.pp_short - expected - Block_payload_hash.pp_short - provided) - Data_encoding.( - obj2 - (req "expected" Block_payload_hash.encoding) - (req "provided" Block_payload_hash.encoding)) - (function - | Wrong_payload_hash_for_consensus_operation {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Wrong_payload_hash_for_consensus_operation {expected; provided}) ; - register_error_kind - `Permanent - ~id:"unexpected_endorsement_in_block" - ~title:"Unexpected endorsement in block" - ~description:"Unexpected endorsement in block." - ~pp:(fun ppf () -> Format.fprintf ppf "Unexpected endorsement in block.") - Data_encoding.empty - (function Unexpected_endorsement_in_block -> Some () | _ -> None) - (fun () -> Unexpected_endorsement_in_block) ; + let description = + "The consensus operation uses an invalid slot. This error should not \ + happen: the operation validation should have failed earlier." + in register_error_kind `Permanent - ~id:"unexpected_preendorsement_in_block" - ~title:"Unexpected preendorsement in block" - ~description:"Unexpected preendorsement in block." - ~pp:(fun ppf () -> Format.fprintf ppf "Unexpected preendorsement in block.") + ~id:"operation.faulty_validation_wrong_slot" + ~title:"Faulty validation (wrong slot for consensus operation)" + ~description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty - (function Unexpected_preendorsement_in_block -> Some () | _ -> None) - (fun () -> Unexpected_preendorsement_in_block) ; - register_error_kind - `Temporary - ~id:"consensus_operation_for_future_level" - ~title:"Consensus operation for future level" - ~description:"Consensus operation for future level." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Consensus operation for future level\n\ - \ (expected: %a, provided: %a)." - Raw_level.pp - expected - Raw_level.pp - provided) - Data_encoding.( - obj2 - (req "expected" Raw_level.encoding) - (req "provided" Raw_level.encoding)) - (function - | Consensus_operation_for_future_level {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Consensus_operation_for_future_level {expected; provided}) ; - register_error_kind - `Temporary - ~id:"consensus_operation_for_future_round" - ~title:"Consensus operation for future round" - ~description:"Consensus operation for future round." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Consensus operation for future round (expected: %a, provided: %a)." - Round.pp - expected - Round.pp - provided) - Data_encoding.( - obj2 (req "expected_max" Round.encoding) (req "provided" Round.encoding)) - (function - | Consensus_operation_for_future_round {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Consensus_operation_for_future_round {expected; provided}) ; - register_error_kind - `Outdated - ~id:"consensus_operation_for_old_level" - ~title:"Consensus operation for old level" - ~description:"Consensus operation for old level." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Consensus operation for old level (expected: %a, provided: %a)." - Raw_level.pp - expected - Raw_level.pp - provided) - Data_encoding.( - obj2 - (req "expected" Raw_level.encoding) - (req "provided" Raw_level.encoding)) - (function - | Consensus_operation_for_old_level {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Consensus_operation_for_old_level {expected; provided}) ; - register_error_kind - `Branch - ~id:"consensus_operation_for_old_round" - ~title:"Consensus operation for old round" - ~description:"Consensus operation for old round." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Consensus operation for old round (expected_min: %a, provided: %a)." - Round.pp - expected - Round.pp - provided) - Data_encoding.( - obj2 (req "expected_min" Round.encoding) (req "provided" Round.encoding)) - (function - | Consensus_operation_for_old_round {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Consensus_operation_for_old_round {expected; provided}) ; - register_error_kind - `Branch - ~id:"consensus_operation_on_competing_proposal" - ~title:"Consensus operation on competing proposal" - ~description:"Consensus operation on competing proposal." - ~pp:(fun ppf (expected, provided) -> - Format.fprintf - ppf - "Consensus operation on competing proposal (expected: %a, provided: \ - %a)." - Block_payload_hash.pp_short - expected - Block_payload_hash.pp_short - provided) - Data_encoding.( - obj2 - (req "expected" Block_payload_hash.encoding) - (req "provided" Block_payload_hash.encoding)) - (function - | Consensus_operation_on_competing_proposal {expected; provided} -> - Some (expected, provided) - | _ -> None) - (fun (expected, provided) -> - Consensus_operation_on_competing_proposal {expected; provided}) ; + (function Faulty_validation_wrong_slot -> Some () | _ -> None) + (fun () -> Faulty_validation_wrong_slot) ; register_error_kind `Temporary ~id:"operation.set_deposits_limit_on_unregistered_delegate" @@ -557,19 +280,7 @@ let () = (function | Invalid_transfer_to_sc_rollup_from_implicit_account -> Some () | _ -> None) - (fun () -> Invalid_transfer_to_sc_rollup_from_implicit_account) ; - let mode_forbids_consens_description = - "The application mode does not support consensus operations." - in - register_error_kind - `Permanent - ~id:"mode_forbids_consensus_operations" - ~title:"Mode forbids consensus operations" - ~description:mode_forbids_consens_description - ~pp:(fun ppf () -> Format.fprintf ppf "%s" mode_forbids_consens_description) - Data_encoding.empty - (function Mode_forbids_consensus_operations -> Some () | _ -> None) - (fun () -> Mode_forbids_consensus_operations) + (fun () -> Invalid_transfer_to_sc_rollup_from_implicit_account) open Apply_results open Apply_operation_result @@ -2071,7 +1782,7 @@ let rec mark_skipped : i.e. its fees can be taken, i.e. [take_fees] cannot return an error. *) let take_fees ctxt (_ : Validate_operation.stamp) contents_list = - let open Lwt_result_syntax in + let open Lwt_tzresult_syntax in let rec take_fees_rec : type kind. context -> @@ -2187,35 +1898,15 @@ let mark_backtracked results = traverse_apply_results results type apply_mode = - | Application of { - predecessor_block : Block_hash.t; - payload_hash : Block_payload_hash.t; - locked_round : Round.t option; - predecessor_level : Level.t; - predecessor_round : Round.t; - round : Round.t; - } - (* Both partial and normal *) - | Full_construction of { - predecessor_block : Block_hash.t; - payload_hash : Block_payload_hash.t; - predecessor_level : Level.t; - predecessor_round : Round.t; - round : Round.t; - } - | Partial_construction of { - predecessor_level : Level.t; - predecessor_round : Round.t; - grand_parent_round : Round.t; - } - | Mempool_no_consensus_op - -let get_predecessor_level = function - | Application {predecessor_level; _} - | Full_construction {predecessor_level; _} - | Partial_construction {predecessor_level; _} -> - ok predecessor_level - | Mempool_no_consensus_op -> error Mode_forbids_consensus_operations + | Application (** Both partial and normal *) + | Full_construction + | Partial_construction of {predecessor_level : Level.t option} + (** This mode is intended to be used by a mempool. In this case, + [predecessor_level] has a value and is used to identify + grandparent endorsements (see [record_endorsement] + below). However, the [option] allows this mode to be hijacked in + other situations with no access to the predecessor level, + e.g. during an RPC call. *) let record_operation (type kind) ctxt hash (operation : kind operation) : context = @@ -2231,220 +1922,67 @@ let record_operation (type kind) ctxt hash (operation : kind operation) : | Cons (Manager_operation _, _) -> record_non_consensus_operation_hash ctxt hash -type 'consensus_op_kind expected_consensus_content = { - payload_hash : Block_payload_hash.t; - branch : Block_hash.t; - level : Level.t; - round : Round.t; -} - -(* The [Alpha_context] is modified only in [Full_construction] mode - when we check a preendorsement if the [preendorsement_quorum_round] - was not set. *) -let compute_expected_consensus_content (type consensus_op_kind) - ~(current_level : Level.t) ~(proposal_level : Level.t) (ctxt : context) - (application_mode : apply_mode) - (operation_kind : consensus_op_kind consensus_operation_type) - (operation_round : Round.t) (operation_level : Raw_level.t) : - (context * consensus_op_kind expected_consensus_content) tzresult Lwt.t = - match operation_kind with - | Endorsement -> ( - match Consensus.endorsement_branch ctxt with - | None -> ( - match application_mode with - | Application _ | Full_construction _ -> - fail Unexpected_endorsement_in_block - | Partial_construction _ -> - fail - (Consensus_operation_for_future_level - {expected = proposal_level.level; provided = operation_level}) - | Mempool_no_consensus_op -> fail Mode_forbids_consensus_operations) - | Some (branch, payload_hash) -> ( - match application_mode with - | Application {predecessor_round; _} - | Full_construction {predecessor_round; _} - | Partial_construction {predecessor_round; _} -> - return - ( ctxt, - { - payload_hash; - branch; - level = proposal_level; - round = predecessor_round; - } ) - | Mempool_no_consensus_op -> fail Mode_forbids_consensus_operations)) - | Preendorsement -> ( - match application_mode with - | Application {locked_round = None; _} -> - fail Unexpected_preendorsement_in_block - | Application - { - payload_hash; - predecessor_block = branch; - locked_round = Some locked_round; - _; - } -> - return - ( ctxt, - { - payload_hash; - branch; - level = current_level; - round = locked_round; - } ) - | Partial_construction {predecessor_round; _} -> ( - match Consensus.endorsement_branch ctxt with - | None -> - fail - (Consensus_operation_for_future_level - {expected = proposal_level.level; provided = operation_level}) - | Some (branch, payload_hash) -> - return - ( ctxt, - { - payload_hash; - branch; - level = proposal_level; - round = predecessor_round; - } )) - | Full_construction {payload_hash; predecessor_block = branch; _} -> - let ctxt', round = - match Consensus.get_preendorsements_quorum_round ctxt with - | None -> - ( Consensus.set_preendorsements_quorum_round ctxt operation_round, - operation_round ) - | Some round -> (ctxt, round) - in - return (ctxt', {payload_hash; branch; level = current_level; round}) - | Mempool_no_consensus_op -> fail Mode_forbids_consensus_operations) - -let check_level (apply_mode : apply_mode) ~expected ~provided = - match apply_mode with - | Application _ | Full_construction _ -> - error_unless - (Raw_level.equal expected provided) - (Wrong_level_for_consensus_operation {expected; provided}) - | Partial_construction _ -> - (* Valid grand parent's endorsements were treated by - [validate_grand_parent_endorsement]. *) - error_when - Raw_level.(expected > provided) - (Consensus_operation_for_old_level {expected; provided}) - >>? fun () -> - error_when - Raw_level.(expected < provided) - (Consensus_operation_for_future_level {expected; provided}) - | Mempool_no_consensus_op -> error Mode_forbids_consensus_operations +let record_preendorsement ctxt (apply_mode : apply_mode) + (_ : Validate_operation.stamp) (content : consensus_content) : + (context * Kind.preendorsement contents_result_list) tzresult = + let open Tzresult_syntax in + let ctxt = + match apply_mode with + | Full_construction -> ( + match Consensus.get_preendorsements_quorum_round ctxt with + | None -> Consensus.set_preendorsements_quorum_round ctxt content.round + | Some _ -> ctxt) + | Application | Partial_construction _ -> ctxt + in + match Slot.Map.find content.slot (Consensus.allowed_preendorsements ctxt) with + | None -> + (* This should not happen: operation validation should have failed. *) + error Faulty_validation_wrong_slot + | Some (_delegate_pk, delegate, preendorsement_power) -> + let* ctxt = + Consensus.record_preendorsement + ctxt + ~initial_slot:content.slot + ~power:preendorsement_power + content.round + in + return + ( ctxt, + Single_result + (Preendorsement_result + {balance_updates = []; delegate; preendorsement_power}) ) -let check_payload_hash (apply_mode : apply_mode) ~expected ~provided = +let is_grandparent_endorsement apply_mode content = match apply_mode with - | Application _ | Full_construction _ -> - error_unless - (Block_payload_hash.equal expected provided) - (Wrong_payload_hash_for_consensus_operation {expected; provided}) - | Partial_construction _ -> - error_unless - (Block_payload_hash.equal expected provided) - (Consensus_operation_on_competing_proposal {expected; provided}) - | Mempool_no_consensus_op -> error Mode_forbids_consensus_operations - -let check_operation_branch ~expected ~provided = - error_unless - (Block_hash.equal expected provided) - (Wrong_consensus_operation_branch (expected, provided)) - -let check_round (type kind) (operation_kind : kind consensus_operation_type) - (apply_mode : apply_mode) ~(expected : Round.t) ~(provided : Round.t) : - unit tzresult = - match apply_mode with - | Partial_construction _ -> - error_when - Round.(expected > provided) - (Consensus_operation_for_old_round {expected; provided}) - >>? fun () -> - error_when - Round.(expected < provided) - (Consensus_operation_for_future_round {expected; provided}) - | Full_construction {round; _} | Application {round; _} -> - (match operation_kind with - | Preendorsement -> - error_when - Round.(round <= provided) - (Preendorsement_round_too_high {block_round = round; provided}) - | Endorsement -> Result.return_unit) - >>? fun () -> - error_unless - (Round.equal expected provided) - (Wrong_round_for_consensus_operation {expected; provided}) - | Mempool_no_consensus_op -> error Mode_forbids_consensus_operations - -let check_consensus_content (type kind) (apply_mode : apply_mode) - (content : consensus_content) (operation_branch : Block_hash.t) - (operation_kind : kind consensus_operation_type) - (expected_content : kind expected_consensus_content) : unit tzresult = - let expected_level = expected_content.level.level in - let provided_level = content.level in - let expected_round = expected_content.round in - let provided_round = content.round in - check_level apply_mode ~expected:expected_level ~provided:provided_level - >>? fun () -> - check_round - operation_kind - apply_mode - ~expected:expected_round - ~provided:provided_round - >>? fun () -> - check_operation_branch - ~expected:expected_content.branch - ~provided:operation_branch - >>? fun () -> - check_payload_hash - apply_mode - ~expected:expected_content.payload_hash - ~provided:content.block_payload_hash - -(* Validate the 'operation.shell.branch' field of the operation. It MUST point - to the grandfather: the block hash used in the payload_hash. Otherwise we could produce - a preendorsement pointing to the direct proposal. This preendorsement wouldn't be able to - propagate for a subsequent proposal using it as a locked_round evidence. *) -let validate_consensus_contents (type kind) ctxt chain_id - (operation_kind : kind consensus_operation_type) - (operation : kind operation) (apply_mode : apply_mode) - (content : consensus_content) : - (context * public_key_hash * int) tzresult Lwt.t = - let current_level = Level.current ctxt in - get_predecessor_level apply_mode >>?= fun proposal_level -> - let slot_map = - match operation_kind with - | Preendorsement -> Consensus.allowed_preendorsements ctxt - | Endorsement -> Consensus.allowed_endorsements ctxt + | Partial_construction {predecessor_level = Some predecessor_level} -> + Raw_level.(succ content.level = predecessor_level.Level.level) + | _ -> false + +let record_endorsement ctxt (apply_mode : apply_mode) + (_ : Validate_operation.stamp) (content : consensus_content) : + (context * Kind.endorsement contents_result_list) tzresult Lwt.t = + let open Lwt_tzresult_syntax in + let mk_endorsement_result delegate endorsement_power = + Single_result + (Endorsement_result {balance_updates = []; delegate; endorsement_power}) in - compute_expected_consensus_content - ~current_level - ~proposal_level - ctxt - apply_mode - operation_kind - content.round - content.level - >>=? fun (ctxt, expected_content) -> - check_consensus_content - apply_mode - content - operation.shell.branch - operation_kind - expected_content - >>?= fun () -> - match Slot.Map.find content.slot slot_map with - | None -> fail Wrong_slot_used_for_consensus_operation - | Some (delegate_pk, delegate_pkh, voting_power) -> - Delegate.frozen_deposits ctxt delegate_pkh >>=? fun frozen_deposits -> - fail_unless - Tez.(frozen_deposits.current_amount > zero) - (Zero_frozen_deposits delegate_pkh) - >>=? fun () -> - Operation.check_signature delegate_pk chain_id operation >>?= fun () -> - return (ctxt, delegate_pkh, voting_power) + if is_grandparent_endorsement apply_mode content then + let level = Level.from_raw ctxt content.level in + let* ctxt, (_delegate_pk, delegate) = + Stake_distribution.slot_owner ctxt level content.slot + in + let*? ctxt = Consensus.record_grand_parent_endorsement ctxt delegate in + return (ctxt, mk_endorsement_result delegate 0) + else + match Slot.Map.find content.slot (Consensus.allowed_endorsements ctxt) with + | None -> + (* This should not happen: operation validation should have failed. *) + fail Faulty_validation_wrong_slot + | Some (_delegate_pk, delegate, power) -> + let*? ctxt = + Consensus.record_endorsement ctxt ~initial_slot:content.slot ~power + in + return (ctxt, mk_endorsement_result delegate power) let apply_manager_contents_list ctxt ~payload_producer chain_id fees_updated_contents_list = @@ -2461,7 +1999,7 @@ let apply_manager_contents_list ctxt ~payload_producer chain_id let apply_manager_operations ctxt ~payload_producer chain_id ~mempool_mode op_validated_stamp contents_list = - let open Lwt_result_syntax in + let open Lwt_tzresult_syntax in let ctxt = if mempool_mode then Gas.reset_block_gas ctxt else ctxt in let* ctxt, fees_updated_contents_list = take_fees ctxt op_validated_stamp contents_list @@ -2538,109 +2076,21 @@ let punish_double_baking ctxt (bh1 : Block_header.t) ~payload_producer = ~payload_producer (fun balance_updates -> Double_baking_evidence_result balance_updates) -let is_parent_endorsement ctxt ~proposal_level ~grand_parent_round - (operation : 'a operation) (operation_content : consensus_content) = - match Consensus.grand_parent_branch ctxt with - | None -> false - | Some (great_grand_parent_hash, grand_parent_payload_hash) -> - (* Check level *) - Raw_level.(proposal_level.Level.level = succ operation_content.level) - (* Check round *) - && Round.(grand_parent_round = operation_content.round) - (* Check payload *) - && Block_payload_hash.( - grand_parent_payload_hash = operation_content.block_payload_hash) - && (* Check branch *) - Block_hash.(great_grand_parent_hash = operation.shell.branch) - -let validate_grand_parent_endorsement ctxt chain_id - (op : Kind.endorsement operation) = - match op.protocol_data.contents with - | Single (Endorsement e) -> - let level = Level.from_raw ctxt e.level in - Stake_distribution.slot_owner ctxt level e.slot - >>=? fun (ctxt, (delegate_pk, pkh)) -> - Operation.check_signature delegate_pk chain_id op >>?= fun () -> - Consensus.record_grand_parent_endorsement ctxt pkh >>?= fun ctxt -> - return - ( ctxt, - Single_result - (Endorsement_result - { - balance_updates = []; - delegate = pkh; - endorsement_power = - 0 (* dummy endorsement power: this will never be used *); - }) ) - let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) ~payload_producer op_validated_stamp (operation : kind operation) (contents_list : kind contents_list) : (context * kind contents_result_list) tzresult Lwt.t = let mempool_mode = match apply_mode with - | Partial_construction _ | Mempool_no_consensus_op -> true - | Full_construction _ | Application _ -> false + | Partial_construction _ -> true + | Full_construction | Application -> false in match contents_list with | Single (Preendorsement consensus_content) -> - validate_consensus_contents - ctxt - chain_id - Preendorsement - operation - apply_mode - consensus_content - >>=? fun (ctxt, delegate, voting_power) -> - Consensus.record_preendorsement - ctxt - ~initial_slot:consensus_content.slot - ~power:voting_power - consensus_content.round - >>?= fun ctxt -> - return - ( ctxt, - Single_result - (Preendorsement_result - { - balance_updates = []; - delegate; - preendorsement_power = voting_power; - }) ) - | Single (Endorsement consensus_content) -> ( - get_predecessor_level apply_mode >>?= fun proposal_level -> - match apply_mode with - | Partial_construction {grand_parent_round; _} - when is_parent_endorsement - ctxt - ~proposal_level - ~grand_parent_round - operation - consensus_content -> - validate_grand_parent_endorsement ctxt chain_id operation - | _ -> - validate_consensus_contents - ctxt - chain_id - Endorsement - operation - apply_mode - consensus_content - >>=? fun (ctxt, delegate, voting_power) -> - Consensus.record_endorsement - ctxt - ~initial_slot:consensus_content.slot - ~power:voting_power - >>?= fun ctxt -> - return - ( ctxt, - Single_result - (Endorsement_result - { - balance_updates = []; - delegate; - endorsement_power = voting_power; - }) )) + record_preendorsement ctxt apply_mode op_validated_stamp consensus_content + |> Lwt.return + | Single (Endorsement consensus_content) -> + record_endorsement ctxt apply_mode op_validated_stamp consensus_content | Single (Dal_slot_availability (endorser, slot_availability)) -> (* DAL/FIXME https://gitlab.com/tezos/tezos/-/issues/3115 @@ -2652,7 +2102,6 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) endorsement encoding. However, once the DAL will be ready, this operation should be merged with an endorsement or at least refined. *) - Dal_apply.validate_data_availability ctxt slot_availability >>?= fun () -> Dal_apply.apply_data_availability ctxt slot_availability ~endorser >>=? fun ctxt -> return diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index a72fdd8dcce9f3b7b5747b4e8fde5f82139105de..9c401388e4111ea74b62b73a6b35724f7f4dfd03 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -91,36 +91,12 @@ val begin_application : Lwt.t type apply_mode = - | Application of { - predecessor_block : Block_hash.t; - payload_hash : Block_payload_hash.t; - locked_round : Round.t option; - predecessor_level : Level.t; - predecessor_round : Round.t; - round : Round.t; - } - (* Both partial and normal *) - | Full_construction of { - predecessor_block : Block_hash.t; - payload_hash : Block_payload_hash.t; - predecessor_level : Level.t; - predecessor_round : Round.t; - round : Round.t; - } - | Partial_construction of { - predecessor_level : Level.t; - predecessor_round : Round.t; - grand_parent_round : Round.t; - } - | Mempool_no_consensus_op - (** Similar to [Partial_construction], but does not have access to - information such as the [predecessor_level]. Makes the operations - which need this information (preendorsements and endorsements as - of July 2022) return the [Mode_forbids_consensus_operations] - error. - - This mode is used by the [run_operation] RPC in - [lib_plugin/RPC.ml]. *) + | Application (** Both partial and normal *) + | Full_construction (** For a baker *) + | Partial_construction of {predecessor_level : Level.t option} + (** This mode is mainly intended to be used by a mempool, in which + case the [predecessor_level] should be provided. However, an RPC + might use it with [None]. *) (** Apply an operation, i.e. update the given context in accordance with the operation's semantic (or return an error if the operation @@ -130,10 +106,13 @@ type apply_mode = operation needs to be validated by {!Validate_operation} before it can be applied. + For non-manager operations, the application of a validated + operation should always fully succeed. + TODO: https://gitlab.com/tezos/tezos/-/issues/2603 Currently, {!Validate_operation.validate_operation} does nothing - on non-manager operations. The "validation" of these operations is + on voting operations. The "validation" of these operations is instead handled by [apply_operation], which may thus return an error if the operation is ill-formed. Once [validate_operation] has been extended to every kind of operation, [apply_operation] should diff --git a/src/proto_alpha/lib_protocol/main.ml b/src/proto_alpha/lib_protocol/main.ml index 3ce79a43dad7cf385b02568247862d1237aa08f9..3ea50d9d3733f3e9bcf7982ab8ee96797d8a2a81 100644 --- a/src/proto_alpha/lib_protocol/main.ml +++ b/src/proto_alpha/lib_protocol/main.ml @@ -180,7 +180,14 @@ let begin_partial_application ~chain_id ~ancestor_context:ctxt } in let validate_operation_info, validate_operation_state = - Validate_operation.init_info_and_state ctxt Block chain_id + Validate_operation.begin_block_validation + ctxt + chain_id + ~predecessor_level + ~predecessor_round + ~predecessor_hash:block_header.shell.predecessor + fitness + block_header.protocol_data.contents.payload_hash in return { @@ -241,7 +248,14 @@ let begin_application ~chain_id ~predecessor_context:ctxt ~predecessor_timestamp } in let validate_operation_info, validate_operation_state = - Validate_operation.init_info_and_state ctxt Block chain_id + Validate_operation.begin_block_validation + ctxt + chain_id + ~predecessor_level + ~predecessor_round + ~predecessor_hash:block_header.shell.predecessor + fitness + block_header.protocol_data.contents.payload_hash in return { @@ -288,8 +302,16 @@ let begin_construction ~chain_id ~predecessor_context:ctxt predecessor_round; } in + Alpha_context.Fitness.predecessor_round_from_raw predecessor_fitness + >>?= fun grandparent_round -> let validate_operation_info, validate_operation_state = - Validate_operation.init_info_and_state ctxt Mempool chain_id + Validate_operation.begin_mempool + ctxt + chain_id + ~predecessor_level + ~predecessor_round + ~predecessor_hash:predecessor + ~grandparent_round in return ( mode, @@ -341,7 +363,14 @@ let begin_construction ~chain_id ~predecessor_context:ctxt } in let validate_operation_info, validate_operation_state = - Validate_operation.init_info_and_state ctxt Block chain_id + Validate_operation.begin_block_construction + ctxt + chain_id + ~predecessor_level + ~predecessor_round + ~predecessor_hash:predecessor + round + proto_header.contents.payload_hash in return ( mode, @@ -405,98 +434,36 @@ let apply_operation ({mode; chain_id; ctxt; op_count; _} as data) (* Multipass validation only considers operations in pass 0. *) let op_count = op_count + 1 in return ({data with ctxt; op_count}, No_operation_metadata) - | Partial_application - { - block_header = - { - shell = {predecessor; _}; - protocol_data = {contents = {payload_hash; _}; _}; - }; - fitness; - payload_producer; - predecessor_round; - predecessor_level; - _; - } -> - let locked_round = Alpha_context.Fitness.locked_round fitness in + | Partial_application {payload_producer; _} -> apply_operation_with_mode - (Apply.Application - { - payload_hash; - predecessor_block = predecessor; - predecessor_round; - predecessor_level; - locked_round; - round = Alpha_context.Fitness.round fitness; - }) + Apply.Application ctxt chain_id data op_count operation ~payload_producer - | Application - { - block_header = - { - shell = {predecessor; _}; - protocol_data = {contents = {payload_hash; _}; _}; - }; - fitness; - payload_producer; - predecessor_round; - predecessor_level; - _; - } -> - let locked_round = Alpha_context.Fitness.locked_round fitness in + | Application {payload_producer; _} -> apply_operation_with_mode - (Apply.Application - { - payload_hash; - predecessor_block = predecessor; - predecessor_round; - predecessor_level; - locked_round; - round = Alpha_context.Fitness.round fitness; - }) + Apply.Application ctxt chain_id data op_count operation ~payload_producer - | Partial_construction - {predecessor_level; predecessor_round; predecessor_fitness; _} -> - Alpha_context.Fitness.predecessor_round_from_raw predecessor_fitness - >>?= fun grand_parent_round -> + | Partial_construction {predecessor_level; _} -> apply_operation_with_mode - (Apply.Partial_construction - {predecessor_round; predecessor_level; grand_parent_round}) + (Apply.Partial_construction {predecessor_level = Some predecessor_level}) ctxt chain_id data op_count operation ~payload_producer:Signature.Public_key_hash.zero - | Full_construction - { - payload_producer; - predecessor; - predecessor_round; - predecessor_level; - protocol_data_contents = {payload_hash; _}; - round; - _; - } -> + | Full_construction {payload_producer; _} -> apply_operation_with_mode - (Apply.Full_construction - { - payload_hash; - predecessor_block = predecessor; - predecessor_level; - predecessor_round; - round; - }) + Apply.Full_construction ctxt chain_id data diff --git a/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml b/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml index ad62da449a9b07e34db70a4118dd57ee727ca07d..2b22c3df9d4d010ed54197edb3d9cb78f7a71daf 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/consensus_helpers.ml @@ -27,7 +27,7 @@ open Protocol open Alpha_context let test_consensus_operation ?construction_mode ?level ?block_payload_hash ?slot - ?round ~endorsed_block ~error_title ~is_preendorsement ~context ~loc () = + ?round ~endorsed_block ~error ~is_preendorsement ~context ~loc () = (if is_preendorsement then Op.preendorsement ~endorsed_block @@ -49,17 +49,16 @@ let test_consensus_operation ?construction_mode ?level ?block_payload_hash ?slot () >|=? fun op -> Operation.pack op) >>=? fun op -> + let assert_error res = Assert.proto_error ~loc res error in match construction_mode with | None -> (* meaning Application mode *) - Block.bake ~operations:[op] endorsed_block >>= fun res -> - Assert.proto_error_with_info ~loc res error_title + Block.bake ~operations:[op] endorsed_block >>= assert_error | Some (pred, protocol_data) -> (* meaning partial construction or full construction mode, depending on [protocol_data] *) Block.get_construction_vstate ~protocol_data pred >>=? fun vstate -> - apply_operation vstate op >|= Environment.wrap_tzresult >>= fun res -> - Assert.proto_error_with_info ~loc res error_title + apply_operation vstate op >|= Environment.wrap_tzresult >>= assert_error let delegate_of_first_slot b = let module V = Plugin.RPC.Validators in @@ -67,12 +66,14 @@ let delegate_of_first_slot b = | {V.delegate; slots = s :: _ as slots; _} :: _ -> ((delegate, slots), s) | _ -> assert false -let delegate_of_slot slot b = +let delegate_of_slot ?(different_slot = false) slot b = let module V = Plugin.RPC.Validators in Context.get_endorsers b >|=? fun endorsers -> List.find_map (function - | {V.delegate; slots = s :: _ as slots; _} when Slot.equal s slot -> + | {V.delegate; slots = s :: _ as slots; _} + when if different_slot then not (Slot.equal s slot) + else Slot.equal s slot -> Some (delegate, slots) | _ -> None) endorsers @@ -97,7 +98,7 @@ let test_consensus_op_for_next ~genesis ~kind ~next = delegate_of_first_slot (B b1) >>=? fun (delegate, slot) -> dorsement ~endorsed_block:b1 ~delegate (B genesis) >>=? fun operation -> Incremental.add_operation inc operation >>=? fun inc -> - delegate_of_slot slot (B b2) >>=? fun delegate -> + delegate_of_slot ~different_slot:true slot (B b2) >>=? fun delegate -> dorsement ~endorsed_block:b2 ~delegate (B b1) >>=? fun operation -> Incremental.add_operation inc operation >>= fun res -> let error_title = diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_endorsement.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_endorsement.ml index 292e00945721ca89aabf94257fe196855593d7a6..af0603b63c849b273366d4dda9cb792ef43905b8 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_endorsement.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_endorsement.ml @@ -117,7 +117,12 @@ let test_non_normalized_slot () = >>=? fun op -> let policy = Block.Excluding [delegate] in Block.bake ~policy ~operations:[Operation.pack op] b >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Wrong slot" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Wrong_slot_used_for_consensus_operation + {kind} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) (** Wrong endorsement predecessor : apply an endorsement with an incorrect block predecessor. *) @@ -127,10 +132,11 @@ let test_wrong_endorsement_predecessor () = >>=? fun operation -> let operation = Operation.pack operation in Block.bake ~operation b >>= fun res -> - Assert.proto_error_with_info - ~loc:__LOC__ - res - "Wrong consensus operation branch" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Wrong_consensus_operation_branch {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) (** Invalid_endorsement_level: apply an endorsement with an incorrect level (i.e. the predecessor level). *) @@ -140,10 +146,11 @@ let test_invalid_endorsement_level () = Op.endorsement ~level:genesis_level ~endorsed_block:b (B genesis) () >>=? fun op -> Block.bake ~operations:[Operation.pack op] b >>= fun res -> - Assert.proto_error_with_info - ~loc:__LOC__ - res - "Wrong level for consensus operation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) (** Duplicate endorsement : apply an endorsement that has already been applied. *) let test_duplicate_endorsement () = @@ -155,10 +162,11 @@ let test_duplicate_endorsement () = Op.endorsement ~endorsed_block:b (B genesis) () >>=? fun operation -> let operation = Operation.pack operation in Incremental.add_operation inc operation >>= fun res -> - Assert.proto_error_with_info - ~loc:__LOC__ - res - "Double inclusion of consensus operation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Conflicting_consensus_operation {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) (** Consensus operation for future level : apply an endorsement with a level in the future *) let test_consensus_operation_endorsement_for_future_level () = @@ -170,7 +178,11 @@ let test_consensus_operation_endorsement_for_future_level () = ~is_preendorsement:false ~endorsed_block:pred ~level - ~error_title:"Consensus operation for future level" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_future_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -185,7 +197,11 @@ let test_consensus_operation_endorsement_for_predecessor_level () = ~is_preendorsement:false ~endorsed_block:pred ~level - ~error_title:"Endorsement for previous level" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -201,7 +217,11 @@ let test_consensus_operation_endorsement_for_old_level () = ~is_preendorsement:false ~endorsed_block:pred ~level - ~error_title:"Consensus operation for old level" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B next_block) ~construction_mode:(pred, None) () @@ -215,7 +235,11 @@ let test_consensus_operation_endorsement_for_future_round () = ~is_preendorsement:false ~endorsed_block:pred ~round - ~error_title:"Consensus operation for future round" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_future_round {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -229,7 +253,11 @@ let test_consensus_operation_endorsement_for_old_round () = ~is_preendorsement:false ~endorsed_block:pred ~round - ~error_title:"Consensus operation for old round" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_old_round {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -242,7 +270,12 @@ let test_consensus_operation_endorsement_on_competing_proposal () = ~is_preendorsement:false ~endorsed_block:pred ~block_payload_hash:Block_payload_hash.zero - ~error_title:"Consensus operation on competing proposal" + ~error:(function + | Validate_errors.Consensus.Wrong_payload_hash_for_consensus_operation + {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -256,7 +289,11 @@ let test_wrong_round () = ~is_preendorsement:false ~endorsed_block:b ~round - ~error_title:"Wrong round for consensus operation" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_future_round {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) () @@ -271,7 +308,11 @@ let test_wrong_level () = ~is_preendorsement:false ~endorsed_block:b ~level - ~error_title:"Wrong level for consensus operation" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context () @@ -283,7 +324,12 @@ let test_wrong_payload_hash () = ~is_preendorsement:false ~endorsed_block:b ~block_payload_hash:Block_payload_hash.zero - ~error_title:"Wrong payload hash for consensus operation" + ~error:(function + | Validate_errors.Consensus.Wrong_payload_hash_for_consensus_operation + {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) () @@ -299,7 +345,12 @@ let test_wrong_slot_used () = ~is_preendorsement:false ~endorsed_block:b ~slot - ~error_title:"Wrong slot" + ~error:(function + | Validate_errors.Consensus.Wrong_slot_used_for_consensus_operation + {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) ~context:(Context.B genesis) () @@ -389,7 +440,11 @@ let test_wrong_endorsement_slot_in_mempool_mode () = let endo = Operation.pack endo in Incremental.begin_construction ~mempool_mode:true b1 >>=? fun i -> Incremental.add_operation i endo >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Wrong slot" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Wrong_slot_used_for_consensus_operation {kind} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) (** Endorsement for next level *) let test_endorsement_for_next_level () = @@ -471,10 +526,11 @@ let test_endorsement_grandparent_application () = Block.bake b_gp >>=? fun b -> Op.endorsement ~endorsed_block:b_gp (B genesis) () >>=? fun op -> Block.bake ~operations:[Operation.pack op] b >>= fun res -> - Assert.proto_error_with_info - ~loc:__LOC__ - res - "Wrong level for consensus operation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) (** Endorsement of grandparent in full construction mode should be rejected *) let test_endorsement_grandparent_full_construction () = @@ -486,10 +542,11 @@ let test_endorsement_grandparent_full_construction () = Op.endorsement ~endorsed_block:b_gp (B genesis) () >>=? fun op1 -> let op1 = Alpha_context.Operation.pack op1 in Incremental.add_operation i op1 >>= fun res -> - Assert.proto_error_with_info - ~loc:__LOC__ - res - "Wrong level for consensus operation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Endorsement -> + true + | _ -> false) let tests = [ diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement.ml index 5cb18b2483ffbe1801c4cd8ed5542eba5b74c811..eada47989ad94d39ae3b1f9b3b4575c0260de2e2 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement.ml @@ -56,7 +56,11 @@ let test_consensus_operation_preendorsement_for_future_level () = ~is_preendorsement:true ~endorsed_block:pred ~level - ~error_title:"Consensus operation for future level" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_future_level {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -71,7 +75,11 @@ let test_consensus_operation_preendorsement_for_old_level () = ~is_preendorsement:true ~endorsed_block:pred ~level - ~error_title:"Consensus operation for old level" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_old_level {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -85,7 +93,11 @@ let test_consensus_operation_preendorsement_for_future_round () = ~is_preendorsement:true ~endorsed_block:pred ~round - ~error_title:"Consensus operation for future round" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_future_round {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -99,7 +111,11 @@ let test_consensus_operation_preendorsement_for_old_round () = ~is_preendorsement:true ~endorsed_block:pred ~round - ~error_title:"Consensus operation for old round" + ~error:(function + | Validate_errors.Consensus.Consensus_operation_for_old_round {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -112,7 +128,12 @@ let test_consensus_operation_preendorsement_on_competing_proposal () = ~is_preendorsement:true ~endorsed_block:pred ~block_payload_hash:Block_payload_hash.zero - ~error_title:"Consensus operation on competing proposal" + ~error:(function + | Validate_errors.Consensus.Wrong_payload_hash_for_consensus_operation + {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, None) () @@ -124,7 +145,9 @@ let test_unexpected_preendorsements_in_blocks () = ~loc:__LOC__ ~is_preendorsement:true ~endorsed_block:pred - ~error_title:"Unexpected preendorsement in block" + ~error:(function + | Validate_errors.Consensus.Unexpected_preendorsement_in_block -> true + | _ -> false) ~context:(Context.B genesis) () @@ -140,7 +163,9 @@ let test_too_high_round () = ~endorsed_block:pred ~round ~level - ~error_title:"Preendorsement round too high" + ~error:(function + | Validate_errors.Consensus.Preendorsement_round_too_high _ -> true + | _ -> false) ~context:(Context.B genesis) ~construction_mode:(pred, Some pred.header.protocol_data) () diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement_functor.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement_functor.ml index fe2123e8632915c5af5cde577c0b039eb43412d2..36aeeb88976bd9ce095743aa38d2e652cc0907b4 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement_functor.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_preendorsement_functor.ml @@ -83,8 +83,7 @@ end = struct >>= fun res -> match (res, post_process) with | Ok ok, Ok success_fun -> success_fun ok - | Error _, Error (error_title, _error_category) -> - Assert.proto_error_with_info ~loc res error_title + | Error _, Error error -> Assert.proto_error ~loc res error | Ok _, Error _ -> Assert.error ~loc res (fun _ -> false) | Error _, Ok _ -> Assert.error ~loc res (fun _ -> false) @@ -105,7 +104,14 @@ end = struct (* preendorsement should be on branch _pred to be valid *) ~preend_branch:(fun predpred _pred _curr -> predpred) ~loc:__LOC__ - ~post_process:(Error ("Wrong consensus operation branch", `Temporary)) + ~post_process: + (Error + (function + | Validate_errors.Consensus.Wrong_consensus_operation_branch + {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false)) () (** KO: The same preendorsement injected twice in the PQC *) @@ -113,7 +119,13 @@ end = struct aux_simple_preendorsement_inclusion (* inject the op twice *) ~mk_ops:(fun op -> [op; op]) ~loc:__LOC__ - ~post_process:(Error ("Double inclusion of consensus operation", `Branch)) + ~post_process: + (Error + (function + | Validate_errors.Consensus.Conflicting_consensus_operation {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false)) () (** KO: locked round declared in the block is not smaller than @@ -123,7 +135,11 @@ end = struct (* default locked_round = 0 < block_round = 1 for this aux function *) ~block_round:0 ~loc:__LOC__ - ~post_process:(Error ("Locked round not smaller than round", `Permanent)) + ~post_process: + (Error + (function + | Fitness_repr.Locked_round_not_less_than_round _ -> true + | _ -> false)) () (** KO: because we announce a locked_round, but we don't provide the @@ -135,7 +151,7 @@ end = struct *) let post_process = if Mode.baking_mode == Block.Application then - Error ("Wrong fitness", `Permanent) + Error (function Fitness_repr.Wrong_fitness -> true | _ -> false) else Ok (fun _ -> return_unit) in aux_simple_preendorsement_inclusion @@ -151,7 +167,14 @@ end = struct (* preendorsement should be for _curr block to be valid *) ~preendorsed_block:(fun _predpred pred _curr -> pred) ~loc:__LOC__ - ~post_process:(Error ("Wrong level for consensus operation", `Permanent)) + ~post_process: + (Error + (function + | Validate_errors.Consensus.Consensus_operation_for_old_level + {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false)) () (** OK: explicit the correct endorser and preendorsing slot in the test *) @@ -179,7 +202,14 @@ end = struct | _ -> assert false (* there is at least one endorser with a slot *)) ~loc:__LOC__ - ~post_process:(Error ("Wrong slot", `Permanent)) + ~post_process: + (Error + (function + | Validate_errors.Consensus.Wrong_slot_used_for_consensus_operation + {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false)) () (** KO: the delegate tries to injects with a canonical slot of another delegate *) @@ -194,7 +224,9 @@ end = struct | _ -> assert false (* there is at least one endorser with a slot *)) ~loc:__LOC__ - ~post_process:(Error ("Invalid operation signature", `Permanent)) + ~post_process: + (Error + (function Operation_repr.Invalid_signature -> true | _ -> false)) () (** KO: cannot have a locked_round higher than attached PQC's round *) @@ -205,7 +237,13 @@ end = struct *) let post_process = if Mode.baking_mode == Application then - Error ("Wrong round for consensus operation", `Permanent) + Error + (function + | Validate_errors.Consensus.Consensus_operation_for_old_round + {kind; _} + when kind = Validate_errors.Consensus.Preendorsement -> + true + | _ -> false) else Ok (fun _ -> return_unit) in aux_simple_preendorsement_inclusion diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index 49fe041e0551e6002128a9c667a9cd1b732133b5..0f4ab0674ff4751aa7bf5cf5a5dd68d1b21eacf3 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -25,6 +25,361 @@ open Alpha_context +module Consensus = struct + type error += Zero_frozen_deposits of Signature.Public_key_hash.t + + let () = + register_error_kind + `Permanent + ~id:"validate.zero_frozen_deposits" + ~title:"Zero frozen deposits" + ~description:"The delegate has zero frozen deposits." + ~pp:(fun ppf delegate -> + Format.fprintf + ppf + "Delegate %a has zero frozen deposits; it is not allowed to \ + bake/preendorse/endorse." + Signature.Public_key_hash.pp + delegate) + Data_encoding.(obj1 (req "delegate" Signature.Public_key_hash.encoding)) + (function Zero_frozen_deposits delegate -> Some delegate | _ -> None) + (fun delegate -> Zero_frozen_deposits delegate) + + (** This type is only used in consensus operation errors to make + them more informative. *) + type consensus_operation_kind = + | Preendorsement + | Endorsement + | Grandparent_endorsement + + let consensus_operation_kind_encoding = + Data_encoding.string_enum + [ + ("Preendorsement", Preendorsement); + ("Endorsement", Endorsement); + ("Grandparent_endorsement", Grandparent_endorsement); + ] + + let consensus_operation_kind_pp fmt = function + | Preendorsement -> Format.fprintf fmt "Preendorsement" + | Endorsement -> Format.fprintf fmt "Endorsement" + | Grandparent_endorsement -> Format.fprintf fmt "Grandparent endorsement" + + (** Errors for preendorsements and endorsements. *) + type error += + | Consensus_operation_for_old_level of { + kind : consensus_operation_kind; + expected : Raw_level.t; + provided : Raw_level.t; + } + | Consensus_operation_for_future_level of { + kind : consensus_operation_kind; + expected : Raw_level.t; + provided : Raw_level.t; + } + | Consensus_operation_for_old_round of { + kind : consensus_operation_kind; + expected : Round.t; + provided : Round.t; + } + | Consensus_operation_for_future_round of { + kind : consensus_operation_kind; + expected : Round.t; + provided : Round.t; + } + | Wrong_consensus_operation_branch of { + kind : consensus_operation_kind; + expected : Block_hash.t; + provided : Block_hash.t; + } + | Wrong_payload_hash_for_consensus_operation of { + kind : consensus_operation_kind; + expected : Block_payload_hash.t; + provided : Block_payload_hash.t; + } + | Unexpected_preendorsement_in_block + | Unexpected_endorsement_in_block + | Preendorsement_round_too_high of { + block_round : Round.t; + provided : Round.t; + } + | Wrong_slot_used_for_consensus_operation of { + kind : consensus_operation_kind; + } + | Conflicting_consensus_operation of {kind : consensus_operation_kind} + | Consensus_operation_not_allowed + + let () = + register_error_kind + `Outdated + ~id:"validate.consensus_operation_for_old_level" + ~title:"Consensus operation for old level" + ~description:"Consensus operation for old level." + ~pp:(fun ppf (kind, expected, provided) -> + Format.fprintf + ppf + "%a for old level (expected: %a, provided: %a)." + consensus_operation_kind_pp + kind + Raw_level.pp + expected + Raw_level.pp + provided) + Data_encoding.( + obj3 + (req "kind" consensus_operation_kind_encoding) + (req "expected" Raw_level.encoding) + (req "provided" Raw_level.encoding)) + (function + | Consensus_operation_for_old_level {kind; expected; provided} -> + Some (kind, expected, provided) + | _ -> None) + (fun (kind, expected, provided) -> + Consensus_operation_for_old_level {kind; expected; provided}) ; + register_error_kind + `Temporary + ~id:"validate.consensus_operation_for_future_level" + ~title:"Consensus operation for future level" + ~description:"Consensus operation for future level." + ~pp:(fun ppf (kind, expected, provided) -> + Format.fprintf + ppf + "%a for future level (expected: %a, provided: %a)." + consensus_operation_kind_pp + kind + Raw_level.pp + expected + Raw_level.pp + provided) + Data_encoding.( + obj3 + (req "kind" consensus_operation_kind_encoding) + (req "expected" Raw_level.encoding) + (req "provided" Raw_level.encoding)) + (function + | Consensus_operation_for_future_level {kind; expected; provided} -> + Some (kind, expected, provided) + | _ -> None) + (fun (kind, expected, provided) -> + Consensus_operation_for_future_level {kind; expected; provided}) ; + register_error_kind + `Branch + ~id:"validate.consensus_operation_for_old_round" + ~title:"Consensus operation for old round" + ~description:"Consensus operation for old round." + ~pp:(fun ppf (kind, expected, provided) -> + Format.fprintf + ppf + "%a for old round (expected_min: %a, provided: %a)." + consensus_operation_kind_pp + kind + Round.pp + expected + Round.pp + provided) + Data_encoding.( + obj3 + (req "kind" consensus_operation_kind_encoding) + (req "expected_min" Round.encoding) + (req "provided" Round.encoding)) + (function + | Consensus_operation_for_old_round {kind; expected; provided} -> + Some (kind, expected, provided) + | _ -> None) + (fun (kind, expected, provided) -> + Consensus_operation_for_old_round {kind; expected; provided}) ; + register_error_kind + `Temporary + ~id:"validate.consensus_operation_for_future_round" + ~title:"Consensus operation for future round" + ~description:"Consensus operation for future round." + ~pp:(fun ppf (kind, expected, provided) -> + Format.fprintf + ppf + "%a for future round (expected: %a, provided: %a)." + consensus_operation_kind_pp + kind + Round.pp + expected + Round.pp + provided) + Data_encoding.( + obj3 + (req "kind" consensus_operation_kind_encoding) + (req "expected_max" Round.encoding) + (req "provided" Round.encoding)) + (function + | Consensus_operation_for_future_round {kind; expected; provided} -> + Some (kind, expected, provided) + | _ -> None) + (fun (kind, expected, provided) -> + Consensus_operation_for_future_round {kind; expected; provided}) ; + register_error_kind + `Temporary + ~id:"validate.wrong_consensus_operation_branch" + ~title:"Wrong consensus operation branch" + ~description: + "Trying to include an endorsement or preendorsement which points to \ + the wrong block. It should be the predecessor for preendorsements and \ + the grandfather for endorsements." + ~pp:(fun ppf (kind, expected, provided) -> + Format.fprintf + ppf + "%a with wrong branch (expected: %a, provided: %a)." + consensus_operation_kind_pp + kind + Block_hash.pp + expected + Block_hash.pp + provided) + Data_encoding.( + obj3 + (req "kind" consensus_operation_kind_encoding) + (req "expected" Block_hash.encoding) + (req "provided" Block_hash.encoding)) + (function + | Wrong_consensus_operation_branch {kind; expected; provided} -> + Some (kind, expected, provided) + | _ -> None) + (fun (kind, expected, provided) -> + Wrong_consensus_operation_branch {kind; expected; provided}) ; + register_error_kind + (* Note: in Mempool mode this used to be + Consensus_operation_on_competing_proposal (which was + [`Branch] so we kept this classification). *) + `Branch + ~id:"validate.wrong_payload_hash_for_consensus_operation" + ~title:"Wrong payload hash for consensus operation" + ~description:"Wrong payload hash for consensus operation." + ~pp:(fun ppf (kind, expected, provided) -> + Format.fprintf + ppf + "%a with wrong payload hash (expected: %a, provided: %a)." + consensus_operation_kind_pp + kind + Block_payload_hash.pp_short + expected + Block_payload_hash.pp_short + provided) + Data_encoding.( + obj3 + (req "kind" consensus_operation_kind_encoding) + (req "expected" Block_payload_hash.encoding) + (req "provided" Block_payload_hash.encoding)) + (function + | Wrong_payload_hash_for_consensus_operation {kind; expected; provided} + -> + Some (kind, expected, provided) + | _ -> None) + (fun (kind, expected, provided) -> + Wrong_payload_hash_for_consensus_operation {kind; expected; provided}) ; + register_error_kind + `Permanent + ~id:"validate.unexpected_preendorsement_in_block" + ~title:"Unexpected preendorsement in block" + ~description:"Unexpected preendorsement in block." + ~pp:(fun ppf () -> + Format.fprintf ppf "Unexpected preendorsement in block.") + Data_encoding.empty + (function Unexpected_preendorsement_in_block -> Some () | _ -> None) + (fun () -> Unexpected_preendorsement_in_block) ; + register_error_kind + `Permanent + ~id:"validate.unexpected_endorsement_in_block" + ~title:"Unexpected endorsement in block" + ~description:"Unexpected endorsement in block." + ~pp:(fun ppf () -> Format.fprintf ppf "Unexpected endorsement in block.") + Data_encoding.empty + (function Unexpected_endorsement_in_block -> Some () | _ -> None) + (fun () -> Unexpected_endorsement_in_block) ; + register_error_kind + `Permanent + ~id:"validate.preendorsement_round_too_high" + ~title:"Preendorsement round too high" + ~description:"Preendorsement round too high." + ~pp:(fun ppf (block_round, provided) -> + Format.fprintf + ppf + "Preendorsement round too high (block_round: %a, provided: %a)." + Round.pp + block_round + Round.pp + provided) + Data_encoding.( + obj2 (req "block_round" Round.encoding) (req "provided" Round.encoding)) + (function + | Preendorsement_round_too_high {block_round; provided} -> + Some (block_round, provided) + | _ -> None) + (fun (block_round, provided) -> + Preendorsement_round_too_high {block_round; provided}) ; + register_error_kind + `Permanent + ~id:"validate.wrong_slot_for_consensus_operation" + ~title:"Wrong slot for consensus operation" + ~description:"Wrong slot used for a preendorsement or endorsement." + ~pp:(fun ppf kind -> + Format.fprintf + ppf + "Wrong slot used for a %a." + consensus_operation_kind_pp + kind) + Data_encoding.(obj1 (req "kind" consensus_operation_kind_encoding)) + (function + | Wrong_slot_used_for_consensus_operation {kind} -> Some kind + | _ -> None) + (fun kind -> Wrong_slot_used_for_consensus_operation {kind}) ; + register_error_kind + `Branch + ~id:"validate.double_inclusion_of_consensus_operation" + ~title:"Double inclusion of consensus operation" + ~description:"Double inclusion of consensus operation." + ~pp:(fun ppf kind -> + Format.fprintf + ppf + "Double inclusion of %a operation" + consensus_operation_kind_pp + kind) + Data_encoding.(obj1 (req "kind" consensus_operation_kind_encoding)) + (function + | Conflicting_consensus_operation {kind} -> Some kind | _ -> None) + (fun kind -> Conflicting_consensus_operation {kind}) ; + register_error_kind + `Branch + ~id:"validate.consensus_operation_not_allowed" + ~title:"Consensus operation not allowed" + ~description:"Consensus operation not allowed." + ~pp:(fun ppf () -> + Format.fprintf ppf "Validation of consensus operation if forbidden ") + Data_encoding.empty + (function Consensus_operation_not_allowed -> Some () | _ -> None) + (fun () -> Consensus_operation_not_allowed) + + type error += + | Conflicting_dal_slot_availability of { + endorser : Signature.Public_key_hash.t; + } + + let () = + register_error_kind + `Temporary + ~id:"validate.conflicting_dal_slot_availability" + ~title:"Conflicting Dal slot availability" + ~description:"Conflicting Dal slot availability." + ~pp:(fun ppf endorser -> + Format.fprintf + ppf + "Dal slot availability for %a has already been validated for the \ + current validation state." + Signature.Public_key_hash.pp + endorser) + Data_encoding.(obj1 (req "endorser" Signature.Public_key_hash.encoding)) + (function + | Conflicting_dal_slot_availability {endorser} -> Some endorser + | _ -> None) + (fun endorser -> Conflicting_dal_slot_availability {endorser}) +end + module Anonymous = struct type error += | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index ace0fa19004d55245b8ae10ba21e378aad84859a..424c8bd820bf6c212836a5005d95e80ddb52e27e 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -23,6 +23,62 @@ (* *) (*****************************************************************************) +(** Errors that may arise while validating a consensus operation. *) +module Consensus : sig + type consensus_operation_kind = + | Preendorsement + | Endorsement + | Grandparent_endorsement + + (** Errors for preendorsements and endorsements. *) + type error += + | Zero_frozen_deposits of Signature.Public_key_hash.t + | Consensus_operation_not_allowed + | Consensus_operation_for_old_level of { + kind : consensus_operation_kind; + expected : Alpha_context.Raw_level.t; + provided : Alpha_context.Raw_level.t; + } + | Consensus_operation_for_future_level of { + kind : consensus_operation_kind; + expected : Alpha_context.Raw_level.t; + provided : Alpha_context.Raw_level.t; + } + | Consensus_operation_for_old_round of { + kind : consensus_operation_kind; + expected : Alpha_context.Round.t; + provided : Alpha_context.Round.t; + } + | Consensus_operation_for_future_round of { + kind : consensus_operation_kind; + expected : Alpha_context.Round.t; + provided : Alpha_context.Round.t; + } + | Wrong_consensus_operation_branch of { + kind : consensus_operation_kind; + expected : Block_hash.t; + provided : Block_hash.t; + } + | Wrong_payload_hash_for_consensus_operation of { + kind : consensus_operation_kind; + expected : Block_payload_hash.t; + provided : Block_payload_hash.t; + } + | Unexpected_preendorsement_in_block + | Unexpected_endorsement_in_block + | Preendorsement_round_too_high of { + block_round : Alpha_context.Round.t; + provided : Alpha_context.Round.t; + } + | Wrong_slot_used_for_consensus_operation of { + kind : consensus_operation_kind; + } + | Conflicting_consensus_operation of {kind : consensus_operation_kind} + | Conflicting_dal_slot_availability of { + endorser : Signature.Public_key_hash.t; + } +end + (** Errors that may arise while validating an anonymous operation. *) module Anonymous : sig type denunciation_kind = Preendorsement | Endorsement | Block diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index 771ecf99e2f0adf5d8059da9b59fa9c243c131c8..6327d6ae871493887b1a68c7a14ee36ca03f34df 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -32,6 +32,104 @@ open Alpha_context change of head block in mempool mode; they are never put in the storage. *) +(** Since the expected features of preendorsement and endorsement are + the same for all operations in the considered block and mempool, we + compute them once and for all at the beginning of the + block/mempool. + + See [expected_features_for_block_validation], + [expected_features_for_block_construction], and + [expected_features_for_mempool] in the [Consensus] module below. *) + +type expected_features = { + level : Raw_level.t; + round : Round.t option; + (** This always contains a value, except for the case of + preendorsements during block construction. See + [Consensus.check_round_equal] below for its usage. *) + branch : Block_hash.t; + payload_hash : Block_payload_hash.t; +} + +type expected_preendorsement = + | Expected_preendorsement of { + expected_features : expected_features; + block_round : Round.t option; + (** During block validation or construction, we must also check + that the preendorsement round is lower than the block + round. In mempool mode, this field is [None]. *) + } + | No_locked_round_for_block_validation_preendorsement + (** A preexisting block whose fitness indicates no locked round + should contain no preendorsements. *) + | Fresh_proposal_for_block_construction_preendorsement + (** A constructed block with a fresh proposal should contain no + preendorsements. *) + | No_expected_branch_for_mempool_preendorsement of { + expected_level : Raw_level.t; + } (** See [No_expected_branch_for_mempool_endorsement] below. *) + | No_predecessor_info_cannot_validate_preendorsement + (** We do not have access to predecessor level, round, etc. so any + preendorsement validation will fail. *) + +type expected_endorsement = + | Expected_endorsement of {expected_features : expected_features} + | No_expected_branch_for_block_endorsement + (** The context contains no branch: this happens to the first block + that uses the Tenderbake consensus algorithm. This block contains + no endorsements. *) + | No_expected_branch_for_mempool_endorsement of {expected_level : Raw_level.t} + (** Same as [No_expected_branch_for_block_endorsement]. This has a + separate constructor because the error raised is distinct: in + mempool mode, we simply assume that we have received a + preendorsement for a future block to which we have not switched + yet. *) + | No_predecessor_info_cannot_validate_endorsement + (** We do not have access to predecessor level, round, etc. so any + endorsement validation will fail. *) + +type all_expected_consensus_features = { + expected_preendorsement : expected_preendorsement; + expected_endorsement : expected_endorsement; + expected_grandparent_endorsement_for_mempool : expected_features option; + (** This only has a value in Mempool mode and when the [ctxt] has a + [grand_parent_branch]; it is [None] in all other cases. *) +} + +type consensus_info = { + all_expected_features : all_expected_consensus_features; + preendorsement_slot_map : + (Signature.public_key * Signature.public_key_hash * int) Slot.Map.t; + endorsement_slot_map : + (Signature.public_key * Signature.public_key_hash * int) Slot.Map.t; +} + +let init_consensus_info ctxt all_expected_features = + { + all_expected_features; + preendorsement_slot_map = Consensus.allowed_preendorsements ctxt; + endorsement_slot_map = Consensus.allowed_endorsements ctxt; + } + +type consensus_state = { + preendorsements_seen : Slot.Set.t; + endorsements_seen : Slot.Set.t; + endorsement_power : int; + grandparent_endorsements_seen : Signature.Public_key_hash.Set.t; + locked_round_evidence : (Round.t * int) option; + dal_slot_availability_seen : Signature.Public_key_hash.Set.t; +} + +let empty_consensus_state = + { + preendorsements_seen = Slot.Set.empty; + endorsements_seen = Slot.Set.empty; + endorsement_power = 0; + grandparent_endorsements_seen = Signature.Public_key_hash.Set.empty; + locked_round_evidence = None; + dal_slot_availability_seen = Signature.Public_key_hash.Set.empty; + } + module Double_evidence = Map.Make (struct type t = Signature.Public_key_hash.t * Level.t @@ -108,8 +206,19 @@ let init_manager_state ctxt = remaining_block_gas = Gas.Arith.fp (Constants.hard_gas_limit_per_block ctxt); } -(* If you add a new mode, please make sure that it has a way to bound - the size of the map {!recfield:managers_seen}. *) +(** Circumstances in which operations are validated: + + - [Block]: covers both the (full or partial) validation of a + preexisting block, and the construction of a new block. Corresponds + to [Application], [Partial_application], and [Full_construction] of + {!Main.validation_mode}. + + - [Mempool]: is used by the mempool (either directly or through + the plugin). Corresponds to [Partial_construction] of + {!Main.validation_mode}. + + If you add a new mode, please make sure that it has a way to bound + the size of the map {!recfield:managers_seen}. *) type mode = Block | Mempool type validate_operation_info = { @@ -117,37 +226,507 @@ type validate_operation_info = { mode : mode; chain_id : Chain_id.t; (** Needed for signature checks. *) current_level : Level.t; + consensus_info : consensus_info; manager_info : manager_info; } type validate_operation_state = { + consensus_state : consensus_state; anonymous_state : anonymous_state; manager_state : manager_state; } -let init_validate_operation_info ctxt mode chain_id = +let init_validate_operation_info ctxt mode chain_id + all_expected_consensus_characteritics = { ctxt; mode; chain_id; current_level = Level.current ctxt; + consensus_info = + init_consensus_info ctxt all_expected_consensus_characteritics; manager_info = init_manager_info ctxt; } let init_validate_operation_state ctxt = { + consensus_state = empty_consensus_state; anonymous_state = empty_anonymous_state; manager_state = init_manager_state ctxt; } -let init_info_and_state ctxt mode chain_id = - let vi = init_validate_operation_info ctxt mode chain_id in - let vs = init_validate_operation_state ctxt in - (vi, vs) - (* See mli file. *) type stamp = Operation_validated_stamp +(** Validation of consensus operations (validation pass [0]): + preendorsement, endorsement, and dal_slot_availability. *) +module Consensus = struct + let expected_endorsement_features ~predecessor_level ~predecessor_round branch + payload_hash = + { + level = predecessor_level.Level.level; + round = Some predecessor_round; + branch; + payload_hash; + } + + let expected_endorsement_for_block ctxt ~predecessor_level ~predecessor_round + : expected_endorsement = + match Consensus.endorsement_branch ctxt with + | None -> No_expected_branch_for_block_endorsement + | Some (branch, payload_hash) -> + let expected_features = + expected_endorsement_features + ~predecessor_level + ~predecessor_round + branch + payload_hash + in + Expected_endorsement {expected_features} + + let expected_features_for_block_validation ctxt fitness payload_hash + ~predecessor_level ~predecessor_round ~predecessor_hash = + let expected_preendorsement = + match Fitness.locked_round fitness with + | None -> No_locked_round_for_block_validation_preendorsement + | Some locked_round -> + let expected_features = + { + level = (Level.current ctxt).level; + round = Some locked_round; + branch = predecessor_hash; + payload_hash; + } + in + let block_round = Some (Fitness.round fitness) in + Expected_preendorsement {expected_features; block_round} + in + let expected_endorsement = + expected_endorsement_for_block ctxt ~predecessor_level ~predecessor_round + in + { + expected_preendorsement; + expected_endorsement; + expected_grandparent_endorsement_for_mempool = None; + } + + let expected_features_for_block_construction ctxt round payload_hash + ~predecessor_level ~predecessor_round ~predecessor_hash = + let expected_preendorsement = + if Block_payload_hash.(payload_hash = zero) then + (* When the proposal is fresh, a fake [payload_hash] of [zero] + has been provided. In this case, the block should not + contain any preendorsements. *) + Fresh_proposal_for_block_construction_preendorsement + else + let expected_features = + { + level = (Level.current ctxt).level; + round = None; + branch = predecessor_hash; + payload_hash; + } + in + Expected_preendorsement {expected_features; block_round = Some round} + in + let expected_endorsement = + expected_endorsement_for_block ctxt ~predecessor_level ~predecessor_round + in + { + expected_preendorsement; + expected_endorsement; + expected_grandparent_endorsement_for_mempool = None; + } + + let expected_features_for_mempool ctxt ~predecessor_level ~predecessor_round + ~grandparent_round = + let expected_preendorsement, expected_endorsement = + match Consensus.endorsement_branch ctxt with + | None -> + let expected_level = predecessor_level.Level.level in + ( No_expected_branch_for_mempool_preendorsement {expected_level}, + No_expected_branch_for_mempool_endorsement {expected_level} ) + | Some (branch, payload_hash) -> + let expected_features = + expected_endorsement_features + ~predecessor_level + ~predecessor_round + branch + payload_hash + in + ( Expected_preendorsement {expected_features; block_round = None}, + Expected_endorsement {expected_features} ) + in + let expected_grandparent_endorsement_for_mempool = + match + ( Consensus.grand_parent_branch ctxt, + Raw_level.pred predecessor_level.level ) + with + | None, _ | _, None -> None + | Some (branch, payload_hash), Some level -> + Some {level; round = Some grandparent_round; branch; payload_hash} + in + { + expected_preendorsement; + expected_endorsement; + expected_grandparent_endorsement_for_mempool; + } + + open Validate_errors.Consensus + + let check_frozen_deposits_are_positive ctxt delegate_pkh = + let open Lwt_result_syntax in + let* frozen_deposits = Delegate.frozen_deposits ctxt delegate_pkh in + fail_unless + Tez.(frozen_deposits.current_amount > zero) + (Zero_frozen_deposits delegate_pkh) + + let check_level_equal kind expected_features + (consensus_content : consensus_content) = + let expected = expected_features.level in + let provided = consensus_content.level in + error_unless + (Raw_level.equal expected provided) + (if Raw_level.(expected > provided) then + Consensus_operation_for_old_level {kind; expected; provided} + else Consensus_operation_for_future_level {kind; expected; provided}) + + let check_round_equal vs kind expected_features + (consensus_content : consensus_content) = + let check expected = + let provided = consensus_content.round in + error_unless + (Round.equal expected provided) + (if Round.(expected > provided) then + Consensus_operation_for_old_round {kind; expected; provided} + else Consensus_operation_for_future_round {kind; expected; provided}) + in + match expected_features.round with + | Some expected -> check expected + | None -> ( + (* For preendorsements in block construction mode, + [expected_features.round] has been set to [None] because we + could not know yet whether there is a locked round. *) + match vs.consensus_state.locked_round_evidence with + | None -> + (* This is the first validated preendorsement in + construction mode: there is nothing to check. *) + ok () + | Some (expected, _power) -> + (* Other preendorsements have already been validated: we + check that the current operation has the same round as + them. *) + check expected) + + let check_branch_equal kind expected_features (operation : 'a operation) = + let expected = expected_features.branch in + let provided = operation.shell.branch in + error_unless + (Block_hash.equal expected provided) + (Wrong_consensus_operation_branch {kind; expected; provided}) + + let check_payload_hash_equal kind expected_features + (consensus_content : consensus_content) = + let expected = expected_features.payload_hash in + let provided = consensus_content.block_payload_hash in + error_unless + (Block_payload_hash.equal expected provided) + (Wrong_payload_hash_for_consensus_operation {kind; expected; provided}) + + let check_consensus_features vs kind (expected : expected_features) + (consensus_content : consensus_content) (operation : 'a operation) = + let open Result_syntax in + let* () = check_level_equal kind expected consensus_content in + let* () = check_round_equal vs kind expected consensus_content in + let* () = check_branch_equal kind expected operation in + check_payload_hash_equal kind expected consensus_content + + let ensure_conflict_free_preendorsement vs slot = + error_unless + (not (Slot.Set.mem slot vs.consensus_state.preendorsements_seen)) + (Conflicting_consensus_operation {kind = Preendorsement}) + + let update_validity_state_preendorsement vs slot round voting_power = + let locked_round_evidence = + match vs.consensus_state.locked_round_evidence with + | None -> Some (round, voting_power) + | Some (_stored_round, evidences) -> Some (round, evidences + voting_power) + (* In mempool mode, round and stored_round can be different when + one of them corresponds to a grandparent preendorsement; this + doesn't matter because quorum certificates are not used in + mempool mode. For other cases, {!check_round_equal} ensures + that all preendorsements have the same round. Indeed, during + block validation, they are all checked to be the same + {!recfield:expected_features.round}; and during block + construction, the round of the first validated preendorsement + is stored in [locked_round_evidence] then all subsequent + preendorsements are checked to have the same round in + {!check_round_equal}. *) + in + let preendorsements_seen = + Slot.Set.add slot vs.consensus_state.preendorsements_seen + in + { + vs with + consensus_state = + {vs.consensus_state with locked_round_evidence; preendorsements_seen}; + } + + let get_expected_preendorsements_features consensus_info consensus_content = + match consensus_info.all_expected_features.expected_preendorsement with + | Expected_preendorsement {expected_features; block_round} -> + ok (expected_features, block_round) + | No_locked_round_for_block_validation_preendorsement + | Fresh_proposal_for_block_construction_preendorsement -> + error Unexpected_preendorsement_in_block + | No_expected_branch_for_mempool_preendorsement {expected_level} -> + error + (Consensus_operation_for_future_level + { + kind = Preendorsement; + expected = expected_level; + provided = consensus_content.Alpha_context.level; + }) + | No_predecessor_info_cannot_validate_preendorsement -> + error Consensus_operation_not_allowed + + let check_round_not_too_high ~block_round ~provided = + match block_round with + | None -> ok () + | Some block_round -> + error_unless + Round.(provided < block_round) + (Preendorsement_round_too_high {block_round; provided}) + + let get_delegate_details slot_map kind consensus_content = + Result.of_option + (Slot.Map.find consensus_content.slot slot_map) + ~error:(trace_of_error (Wrong_slot_used_for_consensus_operation {kind})) + + let validate_preendorsement vi vs ~should_check_signature + (operation : Kind.preendorsement operation) = + let open Lwt_result_syntax in + let (Single (Preendorsement consensus_content)) = + operation.protocol_data.contents + in + let kind = Preendorsement in + let*? () = ensure_conflict_free_preendorsement vs consensus_content.slot in + let*? expected_features, block_round = + get_expected_preendorsements_features vi.consensus_info consensus_content + in + let*? () = + check_round_not_too_high ~block_round ~provided:consensus_content.round + in + let*? () = + check_consensus_features + vs + kind + expected_features + consensus_content + operation + in + let*? delegate_pk, delegate_pkh, voting_power = + get_delegate_details + vi.consensus_info.preendorsement_slot_map + kind + consensus_content + in + let* () = check_frozen_deposits_are_positive vi.ctxt delegate_pkh in + let*? () = + if should_check_signature then + Operation.check_signature delegate_pk vi.chain_id operation + else ok () + in + return + (update_validity_state_preendorsement + vs + consensus_content.slot + consensus_content.round + voting_power) + + let ensure_conflict_free_grandparent_endorsement vs delegate = + error_unless + (not + (Signature.Public_key_hash.Set.mem + delegate + vs.consensus_state.grandparent_endorsements_seen)) + (Conflicting_consensus_operation {kind = Grandparent_endorsement}) + + let update_validity_state_grandparent_endorsement vs delegate = + { + vs with + consensus_state = + { + vs.consensus_state with + grandparent_endorsements_seen = + Signature.Public_key_hash.Set.add + delegate + vs.consensus_state.grandparent_endorsements_seen; + }; + } + + (** Validate an endorsement pointing to the grandparent block. This + function will only be called in [Mempool] mode. *) + let validate_grandparent_endorsement vi vs ~should_check_signature expected + (consensus_content : consensus_content) (operation : 'kind operation) = + let open Lwt_result_syntax in + let kind = Grandparent_endorsement in + let level = Level.from_raw vi.ctxt consensus_content.level in + let* _ctxt, (delegate_pk, delegate_pkh) = + Stake_distribution.slot_owner vi.ctxt level consensus_content.slot + in + let*? () = ensure_conflict_free_grandparent_endorsement vs delegate_pkh in + let*? () = + check_consensus_features vs kind expected consensus_content operation + in + let*? () = + if should_check_signature then + Operation.check_signature delegate_pk vi.chain_id operation + else ok () + in + return (update_validity_state_grandparent_endorsement vs delegate_pkh) + + let ensure_conflict_free_endorsement vs slot = + error_unless + (not (Slot.Set.mem slot vs.consensus_state.endorsements_seen)) + (Conflicting_consensus_operation {kind = Endorsement}) + + let update_validity_state_endorsement vs slot voting_power = + { + vs with + consensus_state = + { + vs.consensus_state with + endorsements_seen = + Slot.Set.add slot vs.consensus_state.endorsements_seen; + endorsement_power = + vs.consensus_state.endorsement_power + voting_power; + }; + } + + let get_expected_endorsements_features consensus_info consensus_content = + match consensus_info.all_expected_features.expected_endorsement with + | Expected_endorsement {expected_features} -> ok expected_features + | No_expected_branch_for_block_endorsement -> + error Unexpected_endorsement_in_block + | No_expected_branch_for_mempool_endorsement {expected_level} -> + error + (Consensus_operation_for_future_level + { + kind = Endorsement; + expected = expected_level; + provided = consensus_content.Alpha_context.level; + }) + | No_predecessor_info_cannot_validate_endorsement -> + error Consensus_operation_not_allowed + + (** Validate an endorsement pointing to the predecessor, aka a + "normal" endorsement. Only this kind of endorsement may be found + during block validation or construction. *) + let validate_normal_endorsement vi vs ~should_check_signature + (consensus_content : consensus_content) (operation : 'kind operation) = + let open Lwt_result_syntax in + let kind = Endorsement in + let*? () = ensure_conflict_free_endorsement vs consensus_content.slot in + let*? expected_features = + get_expected_endorsements_features vi.consensus_info consensus_content + in + let*? () = + check_consensus_features + vs + kind + expected_features + consensus_content + operation + in + let*? delegate_pk, delegate_pkh, voting_power = + get_delegate_details + vi.consensus_info.endorsement_slot_map + kind + consensus_content + in + let* () = check_frozen_deposits_are_positive vi.ctxt delegate_pkh in + let*? () = + if should_check_signature then + Operation.check_signature delegate_pk vi.chain_id operation + else ok () + in + return + (update_validity_state_endorsement vs consensus_content.slot voting_power) + + let validate_endorsement vi vs ~should_check_signature + (operation : Kind.endorsement operation) = + let (Single (Endorsement consensus_content)) = + operation.protocol_data.contents + in + match + vi.consensus_info.all_expected_features + .expected_grandparent_endorsement_for_mempool + with + | Some expected_grandparent_endorsement + when Raw_level.( + consensus_content.level = expected_grandparent_endorsement.level) + -> + validate_grandparent_endorsement + vi + vs + ~should_check_signature + expected_grandparent_endorsement + consensus_content + operation + | _ -> + validate_normal_endorsement + vi + vs + ~should_check_signature + consensus_content + operation + + let ensure_conflict_free_dal_slot_availability vs endorser = + error_unless + (not + (Signature.Public_key_hash.Set.mem + endorser + vs.consensus_state.dal_slot_availability_seen)) + (Conflicting_dal_slot_availability {endorser}) + + let update_validity_state_dal_slot_availabitiy vs endorser = + { + vs with + consensus_state = + { + vs.consensus_state with + dal_slot_availability_seen = + Signature.Public_key_hash.Set.add + endorser + vs.consensus_state.dal_slot_availability_seen; + }; + } + + let validate_dal_slot_availability vi vs ~should_check_signature:_ + (operation : Kind.dal_slot_availability operation) = + (* DAL/FIXME https://gitlab.com/tezos/tezos/-/issues/3115 + + This is a temporary operation. Some checks are missing for the + moment. In particular, the signature is not + checked. Consequently, it is really important to ensure this + operation cannot be included into a block when the feature flag + is not set. This is done in order to avoid modifying the + endorsement encoding. However, once the DAL is ready, this + operation should be merged with an endorsement or at least + refined. *) + let open Lwt_result_syntax in + let (Single (Dal_slot_availability (endorser, slot_availability))) = + operation.protocol_data.contents + in + let*? () = ensure_conflict_free_dal_slot_availability vs endorser in + let*? () = + (* Note that this function checks the dal feature flag. *) + Dal_apply.validate_data_availability vi.ctxt slot_availability + in + return (update_validity_state_dal_slot_availabitiy vs endorser) +end + module Anonymous = struct open Validate_errors.Anonymous @@ -848,12 +1427,85 @@ module Manager = struct return {vs with manager_state = {managers_seen; remaining_block_gas}} end +let init_info_and_state ctxt mode chain_id all_expected_consensus_features = + let vi = + init_validate_operation_info + ctxt + mode + chain_id + all_expected_consensus_features + in + let vs = init_validate_operation_state ctxt in + (vi, vs) + +let begin_block_validation ctxt chain_id ~predecessor_level ~predecessor_round + ~predecessor_hash fitness payload_hash = + let all_expected_consensus_features = + Consensus.expected_features_for_block_validation + ctxt + fitness + payload_hash + ~predecessor_level + ~predecessor_round + ~predecessor_hash + in + init_info_and_state ctxt Block chain_id all_expected_consensus_features + +let begin_block_construction ctxt chain_id ~predecessor_level ~predecessor_round + ~predecessor_hash round payload_hash = + let all_expected_consensus_features = + Consensus.expected_features_for_block_construction + ctxt + round + payload_hash + ~predecessor_level + ~predecessor_round + ~predecessor_hash + in + init_info_and_state ctxt Block chain_id all_expected_consensus_features + +let begin_mempool ctxt chain_id ~predecessor_level ~predecessor_round + ~predecessor_hash:_ ~grandparent_round = + let all_expected_consensus_features = + Consensus.expected_features_for_mempool + ctxt + ~predecessor_level + ~predecessor_round + ~grandparent_round + in + init_info_and_state ctxt Mempool chain_id all_expected_consensus_features + +let begin_no_predecessor_info ctxt chain_id = + let all_expected_consensus_features = + { + expected_preendorsement = + No_predecessor_info_cannot_validate_preendorsement; + expected_endorsement = No_predecessor_info_cannot_validate_endorsement; + expected_grandparent_endorsement_for_mempool = None; + } + in + init_info_and_state ctxt Mempool chain_id all_expected_consensus_features + let validate_operation (vi : validate_operation_info) (vs : validate_operation_state) ?(should_check_signature = true) oph (type kind) (operation : kind operation) = let open Lwt_result_syntax in let* vs = match operation.protocol_data.contents with + | Single (Preendorsement _) -> + Consensus.validate_preendorsement + vi + vs + ~should_check_signature + operation + | Single (Endorsement _) -> + Consensus.validate_endorsement vi vs ~should_check_signature operation + | Single (Dal_slot_availability _) -> + Consensus.validate_dal_slot_availability + vi + vs + ~should_check_signature + operation | Single (Activate_account _ as contents) -> Anonymous.validate_activate_account vi vs oph contents | Single (Double_preendorsement_evidence _ as contents) -> @@ -882,12 +1534,7 @@ let validate_operation (vi : validate_operation_info) source oph operation - | Single (Preendorsement _) - | Single (Endorsement _) - | Single (Dal_slot_availability _) - | Single (Proposals _) - | Single (Ballot _) - | Single (Failing_noop _) -> + | Single (Proposals _) | Single (Ballot _) | Single (Failing_noop _) -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/2603 There is no separate validation phase for non-manager diff --git a/src/proto_alpha/lib_protocol/validate_operation.mli b/src/proto_alpha/lib_protocol/validate_operation.mli index 024962788951244af785fa80935e8123ac843a1d..58bddc900af31ed2a57f8f562139a050ec918cc5 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.mli +++ b/src/proto_alpha/lib_protocol/validate_operation.mli @@ -41,27 +41,53 @@ type validate_operation_info It lives in memory, not in the storage. *) type validate_operation_state -(** Circumstances of the call to {!validate_operation}: +open Alpha_context - - [Block]: called during the validation or application of a block - (received from a peer of freshly constructed). Corresponds to - [Application], [Partial_application], and [Full_construction] modes - of {!Main.validation_mode}. +(** Initialize the {!validate_operation_info} and + {!validate_operation_state} for the validation of an existing block + (in preparation for its future application). *) +val begin_block_validation : + context -> + Chain_id.t -> + predecessor_level:Level.t -> + predecessor_round:Round.t -> + predecessor_hash:Block_hash.t -> + Fitness.t -> + Block_payload_hash.t -> + validate_operation_info * validate_operation_state - - [Mempool]: called by the mempool (either directly or through the - plugin). Corresponds to [Partial_construction] of - {!Main.validation_mode}. *) -type mode = Block | Mempool +(** Initialize the {!validate_operation_info} and + {!validate_operation_state} for the construction of a fresh + block. *) +val begin_block_construction : + context -> + Chain_id.t -> + predecessor_level:Level.t -> + predecessor_round:Round.t -> + predecessor_hash:Block_hash.t -> + Round.t -> + Block_payload_hash.t -> + validate_operation_info * validate_operation_state (** Initialize the {!validate_operation_info} and - {!validate_operation_state} that are needed in - {!validate_operation}. *) -val init_info_and_state : - Alpha_context.t -> - mode -> + {!validate_operation_state} for a mempool. *) +val begin_mempool : + context -> Chain_id.t -> + predecessor_level:Level.t -> + predecessor_round:Round.t -> + predecessor_hash:Block_hash.t -> + grandparent_round:Round.t -> validate_operation_info * validate_operation_state +(** Initialize the {!validate_operation_info} and + {!validate_operation_state} without providing any predecessor + information. This will cause any preendorsement or endorsement + operation to fail, since we lack the information needed to validate + it. *) +val begin_no_predecessor_info : + context -> Chain_id.t -> validate_operation_info * validate_operation_state + (** A receipt to guarantee that an operation is always validated before it is applied. @@ -133,9 +159,8 @@ type stamp TODO: https://gitlab.com/tezos/tezos/-/issues/2603 - This function currently does nothing for operations other than - anonymous or manager operation. (instead, the validity of a - consensus or voting operation is decided by calling + This function currently does nothing for voting operations. + (instead, the validity of a voting operation is decided by calling {!Apply.apply_operation} to check whether it returns an error). We should specify and implement the validation of every kind of operation. *) @@ -144,7 +169,7 @@ val validate_operation : validate_operation_state -> ?should_check_signature:bool -> Operation_hash.t -> - 'kind Alpha_context.operation -> + 'kind operation -> (validate_operation_state * stamp) tzresult Lwt.t (** Functions for the plugin. @@ -161,7 +186,7 @@ module TMP_for_plugin : sig We could have used an [option], but this makes calls to {!precheck_manager} more readable. *) type 'a should_check_signature = - | Check_signature of 'a Alpha_context.operation + | Check_signature of 'a operation | Skip_signature_check (** Similar to {!validate_operation}, but do not check the @@ -187,7 +212,7 @@ module TMP_for_plugin : sig val precheck_manager : validate_operation_info -> validate_operation_state -> - 'a Alpha_context.Kind.manager Alpha_context.contents_list -> - 'a Alpha_context.Kind.manager should_check_signature -> + 'a Kind.manager contents_list -> + 'a Kind.manager should_check_signature -> stamp tzresult Lwt.t end