From 36ba84a02fc48cea5a1a5c6b1c688d07cbd0d0fd Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Tue, 2 Aug 2022 18:16:45 +0200 Subject: [PATCH 01/11] proto: create validate_errors file --- .../lib_client/michelson_v1_error_reporter.ml | 2 +- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 1 + src/proto_alpha/lib_protocol/dune | 4 + .../operations/test_combined_operations.ml | 4 +- .../integration/operations/test_reveal.ml | 12 +- .../integration/operations/test_sc_rollup.ml | 2 +- .../integration/operations/test_tx_rollup.ml | 4 +- .../validate/test_1m_restriction.ml | 4 +- ...st_batched_manager_operation_validation.ml | 14 +- .../test_manager_operation_validation.ml | 8 +- .../lib_protocol/validate_errors.ml | 155 ++++++++++++++++++ .../lib_protocol/validate_errors.mli | 37 +++++ .../lib_protocol/validate_operation.ml | 127 +------------- .../lib_protocol/validate_operation.mli | 13 -- 14 files changed, 221 insertions(+), 166 deletions(-) create mode 100644 src/proto_alpha/lib_protocol/validate_errors.ml create mode 100644 src/proto_alpha/lib_protocol/validate_errors.mli diff --git a/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml b/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml index 993a5981d8dd..b842cd9e82a7 100644 --- a/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml +++ b/src/proto_alpha/lib_client/michelson_v1_error_reporter.ml @@ -320,7 +320,7 @@ let report_errors ~details ~show_source ?parsed ppf errs = if rest <> [] then Format.fprintf ppf "@," ; print_trace (parsed_locations parsed) rest | Environment.Ecoproto_error - Validate_operation.Manager.Gas_quota_exceeded_init_deserialize + Validate_errors.Manager.Gas_quota_exceeded_init_deserialize :: rest -> Format.fprintf ppf diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 71c7e7b36aac..fa227940b60b 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -216,6 +216,7 @@ "Baking", "Amendment", + "Validate_errors", "Validate_operation", "Apply", diff --git a/src/proto_alpha/lib_protocol/dune b/src/proto_alpha/lib_protocol/dune index 07fb0c871a86..23c5ed1a208d 100644 --- a/src/proto_alpha/lib_protocol/dune +++ b/src/proto_alpha/lib_protocol/dune @@ -227,6 +227,7 @@ Dal_apply Baking Amendment + Validate_errors Validate_operation Apply Services_registration @@ -471,6 +472,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_errors.ml validate_errors.mli validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli @@ -695,6 +697,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_errors.ml validate_errors.mli validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli @@ -924,6 +927,7 @@ dal_apply.ml dal_apply.mli baking.ml baking.mli amendment.ml amendment.mli + validate_errors.ml validate_errors.mli validate_operation.ml validate_operation.mli apply.ml apply.mli services_registration.ml services_registration.mli diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml index 976ede73da83..9a02d8673294 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_combined_operations.ml @@ -290,7 +290,7 @@ let test_wrong_signature_in_the_middle () = >>=? fun operation -> let expect_failure = function | Environment.Ecoproto_error - (Validate_operation.Manager.Inconsistent_sources as err) + (Validate_errors.Manager.Inconsistent_sources as err) :: _ -> Assert.test_error_encodings err ; return_unit @@ -308,7 +308,7 @@ let expect_inconsistent_counters list = List.exists (function | Environment.Ecoproto_error - Validate_operation.Manager.Inconsistent_counters -> + Validate_errors.Manager.Inconsistent_counters -> true | _ -> false) list diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml index a6f16e28c19a..d6ea32c30912 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_reveal.ml @@ -444,7 +444,7 @@ let test_no_reveal_when_gas_exhausted () = let expect_failure = function | [ Environment.Ecoproto_error - Validate_operation.Manager.Insufficient_gas_for_manager; + Validate_errors.Manager.Insufficient_gas_for_manager; Environment.Ecoproto_error Raw_context.Operation_quota_exceeded; ] -> return_unit @@ -509,7 +509,7 @@ let test_reveal_incorrect_position_in_batch () = let expect_failure = function | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | _ -> assert false @@ -548,7 +548,7 @@ let test_duplicate_valid_reveals () = let expect_failure = function | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | _ -> assert false @@ -591,7 +591,7 @@ let test_valid_reveal_after_gas_exhausted_one () = let expect_failure = function | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | _ -> assert false @@ -638,7 +638,7 @@ let test_valid_reveal_after_insolvent_one () = let expect_failure = function | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | _ -> assert false @@ -681,7 +681,7 @@ let test_valid_reveal_after_emptying_balance () = let expect_failure = function | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | _ -> assert false diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml index bc14314945df..ccc1cbf57de4 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_sc_rollup.ml @@ -109,7 +109,7 @@ let test_disable_feature_flag () = in let expect_apply_failure = function | Environment.Ecoproto_error - (Validate_operation.Manager.Sc_rollup_feature_disabled as e) + (Validate_errors.Manager.Sc_rollup_feature_disabled as e) :: _ -> Assert.test_error_encodings e ; return_unit diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml index a5f892ed38df..60876263147f 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_tx_rollup.ml @@ -86,7 +86,7 @@ let test_disable_feature_flag () = Op.tx_rollup_origination (I i) contract >>=? fun (op, _tx_rollup) -> Incremental.add_operation ~expect_failure: - (check_proto_error Validate_operation.Manager.Tx_rollup_feature_disabled) + (check_proto_error Validate_errors.Manager.Tx_rollup_feature_disabled) i op >>=? fun _i -> return_unit @@ -109,7 +109,7 @@ let test_sunset () = Op.tx_rollup_origination (I i) contract >>=? fun (op, _tx_rollup) -> Incremental.add_operation ~expect_failure: - (check_proto_error Validate_operation.Manager.Tx_rollup_feature_disabled) + (check_proto_error Validate_errors.Manager.Tx_rollup_feature_disabled) i op >>=? fun _i -> return_unit diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/test_1m_restriction.ml b/src/proto_alpha/lib_protocol/test/integration/validate/test_1m_restriction.ml index 18c261fb070d..7d6a1825e2b4 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/test_1m_restriction.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/test_1m_restriction.ml @@ -121,14 +121,14 @@ let negative_validated_two_ops_of_same_manager = let expect_failure = function | [ Environment.Ecoproto_error - (Validate_operation.Manager.Manager_restriction _); + (Validate_errors.Manager.Manager_restriction _); ] -> return_unit | err -> failwith "Error trace:@,\ \ %a does not match the \ - [Validate_operation.Manager.Manager_restriction] error" + [Validate_errors.Manager.Manager_restriction] error" Error_monad.pp_print_trace err in diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/test_batched_manager_operation_validation.ml b/src/proto_alpha/lib_protocol/test/integration/validate/test_batched_manager_operation_validation.ml index f53573b9eb1f..b97bdf5e1641 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/test_batched_manager_operation_validation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/test_batched_manager_operation_validation.ml @@ -45,7 +45,7 @@ let batch_reveal_in_the_middle_diagnostic (infos : infos) op = match errs with | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | err -> @@ -115,7 +115,7 @@ let batch_two_reveals_diagnostic (infos : infos) op = match errs with | [ Environment.Ecoproto_error - Validate_operation.Manager.Incorrect_reveal_position; + Validate_errors.Manager.Incorrect_reveal_position; ] -> return_unit | err -> @@ -183,9 +183,8 @@ let generate_tests_batches_two_reveals () = let batch_two_sources_diagnostic (infos : infos) op = let expect_failure errs = match errs with - | [ - Environment.Ecoproto_error Validate_operation.Manager.Inconsistent_sources; - ] -> + | [Environment.Ecoproto_error Validate_errors.Manager.Inconsistent_sources] + -> return_unit | err -> failwith @@ -311,9 +310,8 @@ let test_batch_inconsistent_counters kind1 kind2 () = in let expect_failure errs = match errs with - | [ - Environment.Ecoproto_error Validate_operation.Manager.Inconsistent_counters; - ] -> + | [Environment.Ecoproto_error Validate_errors.Manager.Inconsistent_counters] + -> return_unit | err -> failwith diff --git a/src/proto_alpha/lib_protocol/test/integration/validate/test_manager_operation_validation.ml b/src/proto_alpha/lib_protocol/test/integration/validate/test_manager_operation_validation.ml index 83108d0de5d6..9e4a5a5f4c73 100644 --- a/src/proto_alpha/lib_protocol/test/integration/validate/test_manager_operation_validation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/validate/test_manager_operation_validation.ml @@ -126,7 +126,7 @@ let low_gas_limit_diagnostic (infos : infos) op = match errs with | [ Environment.Ecoproto_error - Validate_operation.Manager.Gas_quota_exceeded_init_deserialize; + Validate_errors.Manager.Gas_quota_exceeded_init_deserialize; Environment.Ecoproto_error Raw_context.Operation_quota_exceeded; ] -> return_unit @@ -671,14 +671,12 @@ let generate_tests_validate () = let flag_expect_failure flags errs = match errs with | [ - Environment.Ecoproto_error - Validate_operation.Manager.Sc_rollup_feature_disabled; + Environment.Ecoproto_error Validate_errors.Manager.Sc_rollup_feature_disabled; ] when flags.scoru = false -> return_unit | [ - Environment.Ecoproto_error - Validate_operation.Manager.Tx_rollup_feature_disabled; + Environment.Ecoproto_error Validate_errors.Manager.Tx_rollup_feature_disabled; ] when flags.toru = false -> return_unit diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml new file mode 100644 index 000000000000..4a61bbd815e7 --- /dev/null +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -0,0 +1,155 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +open Alpha_context + +module Manager = struct + type error += + | Manager_restriction of Signature.Public_key_hash.t * Operation_hash.t + | Inconsistent_sources + | Inconsistent_counters + | Incorrect_reveal_position + | Insufficient_gas_for_manager + | Gas_quota_exceeded_init_deserialize + | Tx_rollup_feature_disabled + | Sc_rollup_feature_disabled + + let () = + register_error_kind + `Temporary + ~id:"validate_operation.manager_restriction" + ~title:"Manager restriction" + ~description: + "An operation with the same manager has already been validated in the \ + current block." + ~pp:(fun ppf (d, hash) -> + Format.fprintf + ppf + "Manager %a already has the operation %a in the current block." + Signature.Public_key_hash.pp + d + Operation_hash.pp + hash) + Data_encoding.( + obj2 + (req "manager" Signature.Public_key_hash.encoding) + (req "hash" Operation_hash.encoding)) + (function + | Manager_restriction (manager, hash) -> Some (manager, hash) + | _ -> None) + (fun (manager, hash) -> Manager_restriction (manager, hash)) ; + let inconsistent_sources_description = + "The operation batch includes operations from different sources." + in + register_error_kind + `Permanent + ~id:"validate_operation.inconsistent_sources" + ~title:"Inconsistent sources in operation batch" + ~description:inconsistent_sources_description + ~pp:(fun ppf () -> + Format.fprintf ppf "%s" inconsistent_sources_description) + Data_encoding.empty + (function Inconsistent_sources -> Some () | _ -> None) + (fun () -> Inconsistent_sources) ; + let inconsistent_counters_description = + "Inconsistent counters in operation. Counters of an operation must be \ + successive." + in + register_error_kind + `Permanent + ~id:"validate_operation.inconsistent_counters" + ~title:"Inconsistent counters in operation" + ~description:inconsistent_counters_description + ~pp:(fun ppf () -> + Format.fprintf ppf "%s" inconsistent_counters_description) + Data_encoding.empty + (function Inconsistent_counters -> Some () | _ -> None) + (fun () -> Inconsistent_counters) ; + let incorrect_reveal_description = + "Incorrect reveal operation position in batch: only allowed in first \ + position." + in + register_error_kind + `Permanent + ~id:"validate_operation.incorrect_reveal_position" + ~title:"Incorrect reveal position" + ~description:incorrect_reveal_description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" incorrect_reveal_description) + Data_encoding.empty + (function Incorrect_reveal_position -> Some () | _ -> None) + (fun () -> Incorrect_reveal_position) ; + register_error_kind + `Permanent + ~id:"validate_operation.insufficient_gas_for_manager" + ~title:"Not enough gas for initial manager cost" + ~description: + (Format.asprintf + "Gas limit is too low to cover the initial cost of manager \ + operations: at least %a gas required." + Gas.pp_cost + Michelson_v1_gas.Cost_of.manager_operation) + Data_encoding.empty + (function Insufficient_gas_for_manager -> Some () | _ -> None) + (fun () -> Insufficient_gas_for_manager) ; + let gas_deserialize_description = + "Gas limit was not high enough to deserialize the transaction parameters \ + or origination script code or initial storage etc., making the \ + operation impossible to parse within the provided gas bounds." + in + register_error_kind + `Permanent + ~id:"validate_operation.gas_quota_exceeded_init_deserialize" + ~title:"Not enough gas for initial deserialization of script expressions" + ~description:gas_deserialize_description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" gas_deserialize_description) + Data_encoding.empty + (function Gas_quota_exceeded_init_deserialize -> Some () | _ -> None) + (fun () -> Gas_quota_exceeded_init_deserialize) ; + register_error_kind + `Permanent + ~id:"validate_operation.tx_rollup_is_disabled" + ~title:"Tx rollup is disabled" + ~description:"Cannot originate a tx rollup as it is disabled." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "Cannot apply a tx rollup operation as it is disabled. This feature \ + will be enabled in a future proposal") + Data_encoding.unit + (function Tx_rollup_feature_disabled -> Some () | _ -> None) + (fun () -> Tx_rollup_feature_disabled) ; + let scoru_disabled_description = + "Smart contract rollups will be enabled in a future proposal." + in + register_error_kind + `Permanent + ~id:"validate_operation.sc_rollup_disabled" + ~title:"Smart contract rollups are disabled" + ~description:scoru_disabled_description + ~pp:(fun ppf () -> Format.fprintf ppf "%s" scoru_disabled_description) + Data_encoding.unit + (function Sc_rollup_feature_disabled -> Some () | _ -> None) + (fun () -> Sc_rollup_feature_disabled) +end diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli new file mode 100644 index 000000000000..48347e68597d --- /dev/null +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -0,0 +1,37 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2022 Nomadic Labs *) +(* *) +(* Permission is hereby granted, free of charge, to any person obtaining a *) +(* copy of this software and associated documentation files (the "Software"),*) +(* to deal in the Software without restriction, including without limitation *) +(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) +(* and/or sell copies of the Software, and to permit persons to whom the *) +(* Software is furnished to do so, subject to the following conditions: *) +(* *) +(* The above copyright notice and this permission notice shall be included *) +(* in all copies or substantial portions of the Software. *) +(* *) +(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) +(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) +(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) +(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) +(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) +(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) +(* DEALINGS IN THE SOFTWARE. *) +(* *) +(*****************************************************************************) + +(** Errors that may arise while validating a manager operation. *) +module Manager : sig + type error += + | Manager_restriction of Signature.Public_key_hash.t * Operation_hash.t + | Inconsistent_sources + | Inconsistent_counters + | Incorrect_reveal_position + | Insufficient_gas_for_manager + | Gas_quota_exceeded_init_deserialize + | Tx_rollup_feature_disabled + | Sc_rollup_feature_disabled +end diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index 1f118bea3a67..aee7b5cf7324 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -106,132 +106,7 @@ let init_info_and_state ctxt mode chain_id = type stamp = Operation_validated_stamp module Manager = struct - type error += - | Manager_restriction of Signature.Public_key_hash.t * Operation_hash.t - | Inconsistent_sources - | Inconsistent_counters - | Incorrect_reveal_position - | Insufficient_gas_for_manager - | Gas_quota_exceeded_init_deserialize - | Tx_rollup_feature_disabled - | Sc_rollup_feature_disabled - - let () = - register_error_kind - `Temporary - ~id:"validate_operation.manager_restriction" - ~title:"Manager restriction" - ~description: - "An operation with the same manager has already been validated in the \ - current block." - ~pp:(fun ppf (d, hash) -> - Format.fprintf - ppf - "Manager %a already has the operation %a in the current block." - Signature.Public_key_hash.pp - d - Operation_hash.pp - hash) - Data_encoding.( - obj2 - (req "manager" Signature.Public_key_hash.encoding) - (req "hash" Operation_hash.encoding)) - (function - | Manager_restriction (manager, hash) -> Some (manager, hash) - | _ -> None) - (fun (manager, hash) -> Manager_restriction (manager, hash)) ; - let inconsistent_sources_description = - "The operation batch includes operations from different sources." - in - register_error_kind - `Permanent - ~id:"validate_operation.inconsistent_sources" - ~title:"Inconsistent sources in operation batch" - ~description:inconsistent_sources_description - ~pp:(fun ppf () -> - Format.fprintf ppf "%s" inconsistent_sources_description) - Data_encoding.empty - (function Inconsistent_sources -> Some () | _ -> None) - (fun () -> Inconsistent_sources) ; - let inconsistent_counters_description = - "Inconsistent counters in operation. Counters of an operation must be \ - successive." - in - register_error_kind - `Permanent - ~id:"validate_operation.inconsistent_counters" - ~title:"Inconsistent counters in operation" - ~description:inconsistent_counters_description - ~pp:(fun ppf () -> - Format.fprintf ppf "%s" inconsistent_counters_description) - Data_encoding.empty - (function Inconsistent_counters -> Some () | _ -> None) - (fun () -> Inconsistent_counters) ; - let incorrect_reveal_description = - "Incorrect reveal operation position in batch: only allowed in first \ - position." - in - register_error_kind - `Permanent - ~id:"validate_operation.incorrect_reveal_position" - ~title:"Incorrect reveal position" - ~description:incorrect_reveal_description - ~pp:(fun ppf () -> Format.fprintf ppf "%s" incorrect_reveal_description) - Data_encoding.empty - (function Incorrect_reveal_position -> Some () | _ -> None) - (fun () -> Incorrect_reveal_position) ; - register_error_kind - `Permanent - ~id:"validate_operation.insufficient_gas_for_manager" - ~title:"Not enough gas for initial manager cost" - ~description: - (Format.asprintf - "Gas limit is too low to cover the initial cost of manager \ - operations: at least %a gas required." - Gas.pp_cost - Michelson_v1_gas.Cost_of.manager_operation) - Data_encoding.empty - (function Insufficient_gas_for_manager -> Some () | _ -> None) - (fun () -> Insufficient_gas_for_manager) ; - let gas_deserialize_description = - "Gas limit was not high enough to deserialize the transaction parameters \ - or origination script code or initial storage etc., making the \ - operation impossible to parse within the provided gas bounds." - in - register_error_kind - `Permanent - ~id:"validate_operation.gas_quota_exceeded_init_deserialize" - ~title:"Not enough gas for initial deserialization of script expressions" - ~description:gas_deserialize_description - ~pp:(fun ppf () -> Format.fprintf ppf "%s" gas_deserialize_description) - Data_encoding.empty - (function Gas_quota_exceeded_init_deserialize -> Some () | _ -> None) - (fun () -> Gas_quota_exceeded_init_deserialize) ; - register_error_kind - `Permanent - ~id:"validate_operation.tx_rollup_is_disabled" - ~title:"Tx rollup is disabled" - ~description:"Cannot originate a tx rollup as it is disabled." - ~pp:(fun ppf () -> - Format.fprintf - ppf - "Cannot apply a tx rollup operation as it is disabled. This feature \ - will be enabled in a future proposal") - Data_encoding.unit - (function Tx_rollup_feature_disabled -> Some () | _ -> None) - (fun () -> Tx_rollup_feature_disabled) ; - let scoru_disabled_description = - "Smart contract rollups will be enabled in a future proposal." - in - register_error_kind - `Permanent - ~id:"validate_operation.sc_rollup_disabled" - ~title:"Smart contract rollups are disabled" - ~description:scoru_disabled_description - ~pp:(fun ppf () -> Format.fprintf ppf "%s" scoru_disabled_description) - Data_encoding.unit - (function Sc_rollup_feature_disabled -> Some () | _ -> None) - (fun () -> Sc_rollup_feature_disabled) + open Validate_errors.Manager (** State that simulates changes from individual operations that have an effect on future operations inside the same batch. *) diff --git a/src/proto_alpha/lib_protocol/validate_operation.mli b/src/proto_alpha/lib_protocol/validate_operation.mli index 7bd3b065ef8c..4e06e54a392b 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.mli +++ b/src/proto_alpha/lib_protocol/validate_operation.mli @@ -70,19 +70,6 @@ val init_info_and_state : function in {!TMP_for_plugin}). *) type stamp -(** Errors that may arise while validating a manager operation. *) -module Manager : sig - type error += - | Manager_restriction of Signature.Public_key_hash.t * Operation_hash.t - | Inconsistent_sources - | Inconsistent_counters - | Incorrect_reveal_position - | Insufficient_gas_for_manager - | Gas_quota_exceeded_init_deserialize - | Tx_rollup_feature_disabled - | Sc_rollup_feature_disabled -end - (** Check the validity of the given operation; return an updated {!validate_operation_state}, and a {!stamp} attesting that the operation has been validated. -- GitLab From ab349f37201489116731bfc82ffcbb73d35bac2d Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 13:53:42 +0200 Subject: [PATCH 02/11] proto: add anonymous validity information and state Co-authored-by: Diane Gallois-Wong --- .../lib_protocol/validate_operation.ml | 45 +++++++++++++++++-- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index aee7b5cf7324..7790d43a7da8 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -32,6 +32,39 @@ open Alpha_context change of head block in mempool mode; they are never put in the storage. *) +module Double_evidence = Map.Make (struct + type t = Signature.Public_key_hash.t * Level.t + + let compare (d1, l1) (d2, l2) = + let res = Signature.Public_key_hash.compare d1 d2 in + if Compare.Int.equal res 0 then Level.compare l1 l2 else res +end) + +(** State used and modified when validating anonymous operations. + These fields are used to enforce that we do not validate the same + operation multiple times. + + Note that as part of {!validate_operation_state}, these maps live + in memory. They are not explicitly bounded here, however: + + - In block validation mode, they are bounded by the number of + anonymous operations allowed in the block. + + - In mempool mode, bounding the number of operations in this map + is the responsability of the prevalidator on the shell side. *) +type anonymous_state = { + blinded_pkhs_seen : Operation_hash.t Blinded_public_key_hash.Map.t; + double_baking_evidences_seen : Operation_hash.t Double_evidence.t; + double_consensus_evidences_seen : Operation_hash.t Double_evidence.t; +} + +let empty_anonymous_state = + { + blinded_pkhs_seen = Blinded_public_key_hash.Map.empty; + double_baking_evidences_seen = Double_evidence.empty; + double_consensus_evidences_seen = Double_evidence.empty; + } + (** Static information used to validate manager operations. *) type manager_info = { hard_storage_limit_per_operation : Z.t; @@ -83,7 +116,10 @@ type validate_operation_info = { manager_info : manager_info; } -type validate_operation_state = {manager_state : manager_state} +type validate_operation_state = { + anonymous_state : anonymous_state; + manager_state : manager_state; +} let init_validate_operation_info ctxt mode chain_id = { @@ -95,7 +131,10 @@ let init_validate_operation_info ctxt mode chain_id = } let init_validate_operation_state ctxt = - {manager_state = init_manager_state ctxt} + { + 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 @@ -555,7 +594,7 @@ module Manager = struct let remaining_block_gas = maybe_update_remaining_block_gas vi vs batch_state in - return {manager_state = {managers_seen; remaining_block_gas}} + return {vs with manager_state = {managers_seen; remaining_block_gas}} end let validate_operation (vi : validate_operation_info) -- GitLab From d4fbc7b23c79a01263ee099a42f1390aed023512 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 14:04:15 +0200 Subject: [PATCH 03/11] proto: add anonymous validation signature Co-authored-by: Diane Gallois-Wong --- .../lib_protocol/validate_operation.ml | 53 ++++++++++++++++--- 1 file changed, 47 insertions(+), 6 deletions(-) diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index 7790d43a7da8..b6490f4b8004 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -144,6 +144,41 @@ let init_info_and_state ctxt mode chain_id = (* See mli file. *) type stamp = Operation_validated_stamp +module Anonymous = struct + type denunciation_kind = Preendorsement | Endorsement + + let validate_activate_account _vi vs _oph + (Activate_account {id = _; activation_code = _}) = + return vs + + let validate_double_consensus ~consensus_operation:_ _vi vs _oph _op1 _op2 = + return vs + + let validate_double_preendorsement_evidence vi vs oph + (Double_preendorsement_evidence {op1; op2}) = + validate_double_consensus + ~consensus_operation:Preendorsement + vi + vs + oph + op1 + op2 + + let validate_double_endorsement_evidence vi vs oph + (Double_endorsement_evidence {op1; op2}) = + validate_double_consensus ~consensus_operation:Endorsement vi vs oph op1 op2 + + let validate_double_baking_evidence _vi vs _oph + (Double_baking_evidence {bh1 = _; bh2 = _}) = + return vs + + let validate_seed_nonce_revelation _vi vs + (Seed_nonce_revelation {level = _; nonce = _}) = + return vs + + let validate_vdf_revelation _vi vs (Vdf_revelation {solution = _}) = return vs +end + module Manager = struct open Validate_errors.Manager @@ -603,6 +638,18 @@ let validate_operation (vi : validate_operation_info) let open Lwt_result_syntax in let* vs = match operation.protocol_data.contents with + | Single (Activate_account _ as contents) -> + Anonymous.validate_activate_account vi vs oph contents + | Single (Double_preendorsement_evidence _ as contents) -> + Anonymous.validate_double_preendorsement_evidence vi vs oph contents + | Single (Double_endorsement_evidence _ as contents) -> + Anonymous.validate_double_endorsement_evidence vi vs oph contents + | Single (Double_baking_evidence _ as contents) -> + Anonymous.validate_double_baking_evidence vi vs oph contents + | Single (Seed_nonce_revelation _ as contents) -> + Anonymous.validate_seed_nonce_revelation vi vs contents + | Single (Vdf_revelation _ as contents) -> + Anonymous.validate_vdf_revelation vi vs contents | Single (Manager_operation {source; _}) -> Manager.validate_manager_operation vi @@ -622,14 +669,8 @@ let validate_operation (vi : validate_operation_info) | Single (Preendorsement _) | Single (Endorsement _) | Single (Dal_slot_availability _) - | Single (Seed_nonce_revelation _) - | Single (Vdf_revelation _) | Single (Proposals _) | Single (Ballot _) - | Single (Activate_account _) - | Single (Double_preendorsement_evidence _) - | Single (Double_endorsement_evidence _) - | Single (Double_baking_evidence _) | Single (Failing_noop _) -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/2603 -- GitLab From e0c8455973994f350dcede4e78c89d7dbbca3686 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 15:25:59 +0200 Subject: [PATCH 04/11] proto: add validate activate account Co-authored-by: Diane Gallois-Wong --- .../lib_protocol/alpha_context.mli | 3 ++ src/proto_alpha/lib_protocol/apply.ml | 19 -------- .../integration/operations/test_activation.ml | 16 +++++-- .../lib_protocol/validate_errors.ml | 48 +++++++++++++++++++ .../lib_protocol/validate_errors.mli | 7 +++ .../lib_protocol/validate_operation.ml | 30 ++++++++++-- .../lib_protocol/validate_operation.mli | 11 +++-- 7 files changed, 103 insertions(+), 31 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 47aa310300b1..93aee27ee492 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -4336,6 +4336,9 @@ module Commitment : sig amount : Tez.tez; } + (** See {!Commitment_storage.exists}. *) + val exists : context -> Blinded_public_key_hash.t -> bool Lwt.t + val encoding : t Data_encoding.t end diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 632217f41e97..28c62c7daa2f 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -120,7 +120,6 @@ type error += level : Raw_level.t; last_cycle : Cycle.t; } - | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} | Multiple_revelation | Failing_noop_error | Zero_frozen_deposits of Signature.Public_key_hash.t @@ -685,22 +684,6 @@ let () = | _ -> None) (fun (kind, level, last_cycle) -> Outdated_denunciation {kind; level; last_cycle}) ; - register_error_kind - `Permanent - ~id:"operation.invalid_activation" - ~title:"Invalid activation" - ~description: - "The given key and secret do not correspond to any existing preallocated \ - contract" - ~pp:(fun ppf pkh -> - Format.fprintf - ppf - "Invalid activation. The public key %a does not match any commitment." - Ed25519.Public_key_hash.pp - pkh) - Data_encoding.(obj1 (req "pkh" Ed25519.Public_key_hash.encoding)) - (function Invalid_activation {pkh} -> Some pkh | _ -> None) - (fun pkh -> Invalid_activation {pkh}) ; register_error_kind `Permanent ~id:"block.multiple_revelation" @@ -2971,8 +2954,6 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) Blinded_public_key_hash.of_ed25519_pkh activation_code pkh in let src = `Collected_commitments blinded_pkh in - Token.allocated ctxt src >>=? fun (ctxt, src_exists) -> - fail_unless src_exists (Invalid_activation {pkh}) >>=? fun () -> let contract = Contract.Implicit (Signature.Ed25519 pkh) in Token.balance ctxt src >>=? fun (ctxt, amount) -> Token.transfer ctxt src (`Contract contract) amount diff --git a/src/proto_alpha/lib_protocol/test/integration/operations/test_activation.ml b/src/proto_alpha/lib_protocol/test/integration/operations/test_activation.ml index c96449bb68fd..5e995ec1c01a 100644 --- a/src/proto_alpha/lib_protocol/test/integration/operations/test_activation.ml +++ b/src/proto_alpha/lib_protocol/test/integration/operations/test_activation.ml @@ -464,7 +464,9 @@ let test_invalid_activation_with_no_commitments () = in Op.activation (B blk) account activation_code >>=? fun operation -> Block.bake ~operation blk >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid activation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_activation _ -> true + | _ -> false) (** Wrong activation: wrong secret given in the operation. *) let test_invalid_activation_wrong_secret () = @@ -477,7 +479,9 @@ let test_invalid_activation_wrong_secret () = in Op.activation (B blk) account activation_code >>=? fun operation -> Block.bake ~operation blk >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid activation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_activation _ -> true + | _ -> false) (** Invalid pkh activation : expected to fail as the context does not contain an associated commitment. *) @@ -492,7 +496,9 @@ let test_invalid_activation_inexistent_pkh () = in Op.activation (B blk) inexistent_pkh activation_code >>=? fun operation -> Block.bake ~operation blk >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid activation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_activation _ -> true + | _ -> false) (** Invalid pkh activation : expected to fail as the commitment has already been claimed. *) @@ -506,7 +512,9 @@ let test_invalid_double_activation () = Incremental.add_operation inc op >>=? fun inc -> Op.activation (I inc) account activation_code >>=? fun op' -> Incremental.add_operation inc op' >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid activation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Conflicting_activation _ -> true + | _ -> false) (** Transfer from an unactivated commitment account. *) let test_invalid_transfer_from_unactivated_account () = diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index 4a61bbd815e7..484a0b432053 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -25,6 +25,54 @@ open Alpha_context +module Anonymous = struct + type error += + | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} + | Conflicting_activation of Ed25519.Public_key_hash.t * Operation_hash.t + + let () = + register_error_kind + `Permanent + ~id:"validate_operation.invalid_activation" + ~title:"Invalid activation" + ~description: + "The given key and secret do not correspond to any existing \ + preallocated contract." + ~pp:(fun ppf pkh -> + Format.fprintf + ppf + "Invalid activation. The public key %a and accompanying secret do \ + not match any commitment." + Ed25519.Public_key_hash.pp + pkh) + Data_encoding.(obj1 (req "pkh" Ed25519.Public_key_hash.encoding)) + (function Invalid_activation {pkh} -> Some pkh | _ -> None) + (fun pkh -> Invalid_activation {pkh}) ; + register_error_kind + `Branch + ~id:"validate_operation.conflicting_activation" + ~title:"Account already activated in current validation_state" + ~description: + "The account has already been activated by a previous operation in the \ + current validation state." + ~pp:(fun ppf (edpkh, oph) -> + Format.fprintf + ppf + "Invalid activation: the account %a has already been activated in \ + the current validation state by operation %a." + Ed25519.Public_key_hash.pp + edpkh + Operation_hash.pp + oph) + Data_encoding.( + obj2 + (req "account_edpkh" Ed25519.Public_key_hash.encoding) + (req "conflicting_op_hash" Operation_hash.encoding)) + (function + | Conflicting_activation (edpkh, oph) -> Some (edpkh, oph) | _ -> None) + (fun (edpkh, oph) -> Conflicting_activation (edpkh, oph)) +end + module Manager = struct type error += | Manager_restriction of Signature.Public_key_hash.t * Operation_hash.t diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 48347e68597d..8e46329b5b78 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -23,6 +23,13 @@ (* *) (*****************************************************************************) +(** Errors that may arise while validating an anonymous operation. *) +module Anonymous : sig + type error += + | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} + | Conflicting_activation of Ed25519.Public_key_hash.t * Operation_hash.t +end + (** Errors that may arise while validating a manager operation. *) module Manager : sig type error += diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index b6490f4b8004..b0fdfef2d7d5 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -145,11 +145,35 @@ let init_info_and_state ctxt mode chain_id = type stamp = Operation_validated_stamp module Anonymous = struct + open Validate_errors.Anonymous + type denunciation_kind = Preendorsement | Endorsement - let validate_activate_account _vi vs _oph - (Activate_account {id = _; activation_code = _}) = - return vs + let validate_activate_account vi vs oph + (Activate_account {id = edpkh; activation_code}) = + let open Lwt_result_syntax in + let blinded_pkh = + Blinded_public_key_hash.of_ed25519_pkh activation_code edpkh + in + let*? () = + match + Blinded_public_key_hash.Map.find + blinded_pkh + vs.anonymous_state.blinded_pkhs_seen + with + | None -> ok () + | Some oph' -> error (Conflicting_activation (edpkh, oph')) + in + let*! exists = Commitment.exists vi.ctxt blinded_pkh in + let*? () = error_unless exists (Invalid_activation {pkh = edpkh}) in + let blinded_pkhs_seen = + Blinded_public_key_hash.Map.add + blinded_pkh + oph + vs.anonymous_state.blinded_pkhs_seen + in + return + {vs with anonymous_state = {vs.anonymous_state with blinded_pkhs_seen}} let validate_double_consensus ~consensus_operation:_ _vi vs _oph _op1 _op2 = return vs diff --git a/src/proto_alpha/lib_protocol/validate_operation.mli b/src/proto_alpha/lib_protocol/validate_operation.mli index 4e06e54a392b..024962788951 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.mli +++ b/src/proto_alpha/lib_protocol/validate_operation.mli @@ -133,11 +133,12 @@ type stamp TODO: https://gitlab.com/tezos/tezos/-/issues/2603 - This function currently does nothing for non-manager operations - (instead, the validity of a non-manager 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. *) + 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 + {!Apply.apply_operation} to check whether it returns an error). + We should specify and implement the validation of every kind of + operation. *) val validate_operation : validate_operation_info -> validate_operation_state -> -- GitLab From b6f8642ae11aabe0a3157c6c2fa062998a84753b Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Tue, 12 Jul 2022 10:29:31 +0200 Subject: [PATCH 05/11] proto: add unrequired endorsement and preendorsement tests --- src/proto_alpha/lib_protocol/apply.mli | 1 + .../consensus/test_double_endorsement.ml | 35 +++++++++++++ .../consensus/test_double_preendorsement.ml | 51 +++++++++++++++++++ 3 files changed, 87 insertions(+) diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index dca373aa8f8d..9efa71676548 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -43,6 +43,7 @@ type error += | Tx_rollup_invalid_transaction_ticket_amount | Sc_rollup_feature_disabled | Empty_transaction of Contract.t + | Unrequired_denunciation val begin_partial_construction : context -> diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml index b91a39c9d528..06645950b9e3 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml @@ -430,6 +430,37 @@ let test_freeze_more_with_low_balance = Context.Delegate.info (B e1) account1 >>=? fun info6 -> Assert.equal_bool ~loc:__LOC__ info6.deactivated true +(** Injecting a valid double endorsement multiple times raises an error. *) +let test_two_double_endorsement_evidences_leads_to_unreqired_denunciation () = + Context.init2 ~consensus_threshold:0 () >>=? fun (genesis, _contracts) -> + block_fork genesis >>=? fun (blk_1, blk_2) -> + Block.bake blk_1 >>=? fun blk_a -> + Block.bake blk_2 >>=? fun blk_b -> + Context.get_endorser (B blk_a) >>=? fun (delegate, _) -> + Op.endorsement ~endorsed_block:blk_a (B blk_1) () >>=? fun endorsement_a -> + Op.endorsement ~endorsed_block:blk_b (B blk_2) () >>=? fun endorsement_b -> + let operation = double_endorsement (B genesis) endorsement_a endorsement_b in + let operation2 = double_endorsement (B genesis) endorsement_b endorsement_a in + Context.get_bakers (B blk_a) >>=? fun bakers -> + let baker = Context.get_first_different_baker delegate bakers in + Context.Delegate.full_balance (B blk_a) baker >>=? fun _full_balance -> + Block.bake + ~policy:(By_account baker) + ~operations:[operation; operation2] + blk_a + >>= fun e -> + Assert.proto_error ~loc:__LOC__ e (function + | Apply.Unrequired_denunciation -> true + | _ -> false) + >>=? fun () -> + Block.bake ~policy:(By_account baker) ~operation blk_a + >>=? fun blk_with_evidence1 -> + Block.bake ~policy:(By_account baker) ~operation blk_with_evidence1 + >>= fun e -> + Assert.proto_error ~loc:__LOC__ e (function + | Apply.Unrequired_denunciation -> true + | _ -> false) + let tests = [ Tztest.tztest @@ -440,6 +471,10 @@ let tests = "2 valid double endorsement evidences lead to not being able to bake" `Quick test_two_double_endorsement_evidences_leadsto_no_bake; + Tztest.tztest + "valid double endorsement injected multiple time" + `Quick + test_two_double_endorsement_evidences_leads_to_unreqired_denunciation; Tztest.tztest "invalid double endorsement evidence" `Quick diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml index 1f844fe614b8..8cad8e7368d9 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml @@ -283,6 +283,53 @@ end = struct ~loc:__LOC__ () + let double_preendorsement ctxt ?(correct_order = true) op1 op2 = + let e1, e2 = order_preendorsements ~correct_order op1 op2 in + Op.double_preendorsement ctxt e1 e2 + + let block_fork b = + Context.get_first_different_bakers (B b) >>=? fun (baker_1, baker_2) -> + Block.bake ~policy:(By_account baker_1) b >>=? fun blk_a -> + Block.bake ~policy:(By_account baker_2) b >|=? fun blk_b -> (blk_a, blk_b) + + (** Injecting a valid double preendorsement multiple time raises an error. *) + let test_two_double_preendorsement_evidences_leads_to_unreqired_denunciation + () = + Context.init2 ~consensus_threshold:0 () >>=? fun (genesis, _contracts) -> + block_fork genesis >>=? fun (blk_1, blk_2) -> + Block.bake blk_1 >>=? fun blk_a -> + Block.bake blk_2 >>=? fun blk_b -> + Context.get_endorser (B blk_a) >>=? fun (delegate, _) -> + Op.preendorsement ~endorsed_block:blk_a (B blk_1) () + >>=? fun preendorsement_a -> + Op.preendorsement ~endorsed_block:blk_b (B blk_2) () + >>=? fun preendorsement_b -> + let operation = + double_preendorsement (B genesis) preendorsement_a preendorsement_b + in + let operation2 = + double_preendorsement (B genesis) preendorsement_b preendorsement_a + in + Context.get_bakers (B blk_a) >>=? fun bakers -> + let baker = Context.get_first_different_baker delegate bakers in + Context.Delegate.full_balance (B blk_a) baker >>=? fun _full_balance -> + Block.bake + ~policy:(By_account baker) + ~operations:[operation; operation2] + blk_a + >>= fun e -> + Assert.proto_error ~loc:__LOC__ e (function + | Apply.Unrequired_denunciation -> true + | _ -> false) + >>=? fun () -> + Block.bake ~policy:(By_account baker) ~operation blk_a + >>=? fun blk_with_evidence1 -> + Block.bake ~policy:(By_account baker) ~operation blk_with_evidence1 + >>= fun e -> + Assert.proto_error ~loc:__LOC__ e (function + | Apply.Unrequired_denunciation -> true + | _ -> false) + let my_tztest title test = Tztest.tztest (Format.sprintf "%s: %s" name title) test @@ -322,6 +369,10 @@ end = struct "double_preendorsement_denunciation_after_slashing_period" `Quick double_preendorsement_denunciation_after_slashing_period; + my_tztest + "valid double preendorsement injected multiple times" + `Quick + test_two_double_preendorsement_evidences_leads_to_unreqired_denunciation; ] end -- GitLab From 2feba566b609adcd72c4c75fb4d020f249cbf328 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 16:49:22 +0200 Subject: [PATCH 06/11] proto: add validate double consensus Co-authored-by: Diane Gallois-Wong --- src/proto_alpha/lib_protocol/apply.ml | 58 +----- .../consensus/test_double_endorsement.ml | 48 ++++- .../consensus/test_double_preendorsement.ml | 41 ++-- .../lib_protocol/validate_errors.ml | 194 ++++++++++++++++++ .../lib_protocol/validate_errors.mli | 29 +++ .../lib_protocol/validate_operation.ml | 94 ++++++++- 6 files changed, 388 insertions(+), 76 deletions(-) diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 28c62c7daa2f..d51d65d5f642 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2698,9 +2698,8 @@ let punish_delegate ctxt delegate level mistake mk_result ~payload_producer = let balance_updates = reward_balance_updates @ punish_balance_updates in (ctxt, Single_result (mk_result balance_updates)) -let punish_double_endorsement_or_preendorsement (type kind) ctxt ~chain_id - ~preendorsement ~(op1 : kind Kind.consensus Operation.t) - ~(op2 : kind Kind.consensus Operation.t) ~payload_producer : +let punish_double_endorsement_or_preendorsement (type kind) ctxt + ~(op1 : kind Kind.consensus Operation.t) ~payload_producer : (context * kind Kind.double_consensus_operation_evidence contents_result_list) tzresult @@ -2713,38 +2712,11 @@ let punish_double_endorsement_or_preendorsement (type kind) ctxt ~chain_id | Single (Endorsement _) -> Double_endorsement_evidence_result balance_updates in - match (op1.protocol_data.contents, op2.protocol_data.contents) with - | Single (Preendorsement e1), Single (Preendorsement e2) - | Single (Endorsement e1), Single (Endorsement e2) -> - let kind = if preendorsement then Preendorsement else Endorsement in - let op1_hash = Operation.hash op1 in - let op2_hash = Operation.hash op2 in - fail_unless - (Raw_level.(e1.level = e2.level) - && Round.(e1.round = e2.round) - && (not - (Block_payload_hash.equal - e1.block_payload_hash - e2.block_payload_hash)) - && (* we require an order on hashes to avoid the existence of - equivalent evidences *) - Operation_hash.(op1_hash < op2_hash)) - (Invalid_denunciation kind) - >>=? fun () -> - (* Disambiguate: levels are equal *) + match op1.protocol_data.contents with + | Single (Preendorsement e1) | Single (Endorsement e1) -> let level = Level.from_raw ctxt e1.level in - check_denunciation_age ctxt kind level.level >>=? fun () -> Stake_distribution.slot_owner ctxt level e1.slot - >>=? fun (ctxt, (delegate1_pk, delegate1)) -> - Stake_distribution.slot_owner ctxt level e2.slot - >>=? fun (ctxt, (_delegate2_pk, delegate2)) -> - fail_unless - (Signature.Public_key_hash.equal delegate1 delegate2) - (Inconsistent_denunciation {kind; delegate1; delegate2}) - >>=? fun () -> - let delegate_pk, delegate = (delegate1_pk, delegate1) in - Operation.check_signature delegate_pk chain_id op1 >>?= fun () -> - Operation.check_signature delegate_pk chain_id op2 >>?= fun () -> + >>=? fun (ctxt, (_delegate_pk, delegate)) -> punish_delegate ctxt delegate @@ -2931,22 +2903,10 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) Token.transfer ctxt `Revelation_rewards (`Contract contract) tip >|=? fun (ctxt, balance_updates) -> (ctxt, Single_result (Vdf_revelation_result balance_updates)) - | Single (Double_preendorsement_evidence {op1; op2}) -> - punish_double_endorsement_or_preendorsement - ctxt - ~preendorsement:true - ~chain_id - ~op1 - ~op2 - ~payload_producer - | Single (Double_endorsement_evidence {op1; op2}) -> - punish_double_endorsement_or_preendorsement - ctxt - ~preendorsement:false - ~chain_id - ~op1 - ~op2 - ~payload_producer + | Single (Double_preendorsement_evidence {op1; op2 = _}) -> + punish_double_endorsement_or_preendorsement ctxt ~op1 ~payload_producer + | Single (Double_endorsement_evidence {op1; op2 = _}) -> + punish_double_endorsement_or_preendorsement ctxt ~op1 ~payload_producer | Single (Double_baking_evidence {bh1; bh2}) -> punish_double_baking ctxt chain_id bh1 bh2 ~payload_producer | Single (Activate_account {id = pkh; activation_code}) -> diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml index 06645950b9e3..c6e9e3510366 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_endorsement.ml @@ -173,7 +173,11 @@ let test_invalid_double_endorsement () = Block.bake ~operation:(Operation.pack endorsement) b >>=? fun b -> Op.double_endorsement (B b) endorsement endorsement |> fun operation -> Block.bake ~operation b >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_denunciation kind + when kind = Validate_errors.Anonymous.Endorsement -> + true + | _ -> false) (** Check that an double endorsement operation that is invalid due to incorrect ordering of the endorsements fails. *) @@ -192,7 +196,11 @@ let test_invalid_double_endorsement_variant () = endorsement_b |> fun operation -> Block.bake ~operation genesis >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_denunciation kind + when kind = Validate_errors.Anonymous.Endorsement -> + true + | _ -> false) (** Check that a future-cycle double endorsement fails. *) let test_too_early_double_endorsement_evidence () = @@ -205,7 +213,11 @@ let test_too_early_double_endorsement_evidence () = Op.endorsement ~endorsed_block:blk_b (B blk_2) () >>=? fun endorsement_b -> double_endorsement (B genesis) endorsement_a endorsement_b |> fun operation -> Block.bake ~operation genesis >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Too early denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Too_early_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Endorsement -> + true + | _ -> false) (** Check that after [max_slashing_period * blocks_per_cycle + 1], it is not possible to create a double_endorsement anymore. *) @@ -223,7 +235,11 @@ let test_too_late_double_endorsement_evidence () = >>=? fun blk -> double_endorsement (B blk) endorsement_a endorsement_b |> fun operation -> Block.bake ~operation blk >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Outdated denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Outdated_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Endorsement -> + true + | _ -> false) (** Check that an invalid double endorsement evidence that exposes two endorsements made by two different endorsers fails. *) @@ -256,7 +272,11 @@ let test_different_delegates () = Block.bake ~operation:(Operation.pack e_b) blk_b >>=? fun _ -> double_endorsement (B blk_b) e_a e_b |> fun operation -> Block.bake ~operation blk_b >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Inconsistent denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Inconsistent_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Endorsement -> + true + | _ -> false) (** Check that a double endorsement evidence that exposes a ill-formed endorsement fails. *) @@ -287,7 +307,11 @@ let test_wrong_delegate () = >>=? fun endorsement_b -> double_endorsement (B blk_b) endorsement_a endorsement_b |> fun operation -> Block.bake ~operation blk_b >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Inconsistent denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Inconsistent_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Endorsement -> + true + | _ -> false) let test_freeze_more_with_low_balance = let get_endorsing_slots_for_account ctxt account = @@ -431,7 +455,7 @@ let test_freeze_more_with_low_balance = Assert.equal_bool ~loc:__LOC__ info6.deactivated true (** Injecting a valid double endorsement multiple times raises an error. *) -let test_two_double_endorsement_evidences_leads_to_unreqired_denunciation () = +let test_two_double_endorsement_evidences_leads_to_duplicate_denunciation () = Context.init2 ~consensus_threshold:0 () >>=? fun (genesis, _contracts) -> block_fork genesis >>=? fun (blk_1, blk_2) -> Block.bake blk_1 >>=? fun blk_a -> @@ -450,7 +474,9 @@ let test_two_double_endorsement_evidences_leads_to_unreqired_denunciation () = blk_a >>= fun e -> Assert.proto_error ~loc:__LOC__ e (function - | Apply.Unrequired_denunciation -> true + | Validate_errors.Anonymous.Conflicting_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Endorsement -> + true | _ -> false) >>=? fun () -> Block.bake ~policy:(By_account baker) ~operation blk_a @@ -458,7 +484,9 @@ let test_two_double_endorsement_evidences_leads_to_unreqired_denunciation () = Block.bake ~policy:(By_account baker) ~operation blk_with_evidence1 >>= fun e -> Assert.proto_error ~loc:__LOC__ e (function - | Apply.Unrequired_denunciation -> true + | Validate_errors.Anonymous.Already_denounced {kind; _} + when kind = Validate_errors.Anonymous.Endorsement -> + true | _ -> false) let tests = @@ -474,7 +502,7 @@ let tests = Tztest.tztest "valid double endorsement injected multiple time" `Quick - test_two_double_endorsement_evidences_leads_to_unreqired_denunciation; + test_two_double_endorsement_evidences_leads_to_duplicate_denunciation; Tztest.tztest "invalid double endorsement evidence" `Quick diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml index 8cad8e7368d9..a4e2db412204 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_preendorsement.ml @@ -63,7 +63,11 @@ end = struct | _ -> assert false let invalid_denunciation loc res = - Assert.proto_error_with_info ~loc res "Invalid denunciation" + Assert.proto_error ~loc res (function + | Validate_errors.Anonymous.Invalid_denunciation kind + when kind = Validate_errors.Anonymous.Preendorsement -> + true + | _ -> false) let malformed_double_preendorsement_denunciation ?(include_endorsement = false) ?(block_round = 0) @@ -87,14 +91,26 @@ end = struct >>=? fun {parametric = {max_slashing_period; blocks_per_cycle; _}; _} -> return (max_slashing_period * Int32.to_int blocks_per_cycle) - let unrequired_denunciation loc res = - Assert.proto_error_with_info ~loc res "Unrequired denunciation" + let already_denounced loc res = + Assert.proto_error ~loc res (function + | Validate_errors.Anonymous.Already_denounced {kind; _} + when kind = Validate_errors.Anonymous.Preendorsement -> + true + | _ -> false) let inconsistent_denunciation loc res = - Assert.proto_error_with_info ~loc res "Inconsistent denunciation" + Assert.proto_error ~loc res (function + | Validate_errors.Anonymous.Inconsistent_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Preendorsement -> + true + | _ -> false) let outdated_denunciation loc res = - Assert.proto_error_with_info ~loc res "Outdated denunciation" + Assert.proto_error ~loc res (function + | Validate_errors.Anonymous.Outdated_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Preendorsement -> + true + | _ -> false) let unexpected_failure loc res = (* no error is expected *) @@ -205,7 +221,7 @@ end = struct let op : Operation.packed = Op.double_preendorsement (B new_head) op1 op2 in - bake new_head ~operations:[op] >>= unrequired_denunciation loc + bake new_head ~operations:[op] >>= already_denounced loc | Error _ as res -> test_expected_ko loc res (****************************************************************) @@ -293,7 +309,7 @@ end = struct Block.bake ~policy:(By_account baker_2) b >|=? fun blk_b -> (blk_a, blk_b) (** Injecting a valid double preendorsement multiple time raises an error. *) - let test_two_double_preendorsement_evidences_leads_to_unreqired_denunciation + let test_two_double_preendorsement_evidences_leads_to_duplicate_denunciation () = Context.init2 ~consensus_threshold:0 () >>=? fun (genesis, _contracts) -> block_fork genesis >>=? fun (blk_1, blk_2) -> @@ -319,16 +335,15 @@ end = struct blk_a >>= fun e -> Assert.proto_error ~loc:__LOC__ e (function - | Apply.Unrequired_denunciation -> true + | Validate_errors.Anonymous.Conflicting_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Preendorsement -> + true | _ -> false) >>=? fun () -> Block.bake ~policy:(By_account baker) ~operation blk_a >>=? fun blk_with_evidence1 -> Block.bake ~policy:(By_account baker) ~operation blk_with_evidence1 - >>= fun e -> - Assert.proto_error ~loc:__LOC__ e (function - | Apply.Unrequired_denunciation -> true - | _ -> false) + >>= fun e -> already_denounced __LOC__ e let my_tztest title test = Tztest.tztest (Format.sprintf "%s: %s" name title) test @@ -372,7 +387,7 @@ end = struct my_tztest "valid double preendorsement injected multiple times" `Quick - test_two_double_preendorsement_evidences_leads_to_unreqired_denunciation; + test_two_double_preendorsement_evidences_leads_to_duplicate_denunciation; ] end diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index 484a0b432053..be5cc6d5487e 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -71,6 +71,200 @@ module Anonymous = struct (function | Conflicting_activation (edpkh, oph) -> Some (edpkh, oph) | _ -> None) (fun (edpkh, oph) -> Conflicting_activation (edpkh, oph)) + + type denunciation_kind = Preendorsement | Endorsement + + let denunciation_kind_encoding = + let open Data_encoding in + string_enum + [("preendorsement", Preendorsement); ("endorsement", Endorsement)] + + let pp_denunciation_kind fmt : denunciation_kind -> unit = function + | Preendorsement -> Format.fprintf fmt "preendorsement" + | Endorsement -> Format.fprintf fmt "endorsement" + + type error += + | Invalid_denunciation of denunciation_kind + | Inconsistent_denunciation of { + kind : denunciation_kind; + delegate1 : Signature.Public_key_hash.t; + delegate2 : Signature.Public_key_hash.t; + } + | Already_denounced of { + kind : denunciation_kind; + delegate : Signature.Public_key_hash.t; + level : Level.t; + } + | Conflicting_denunciation of { + kind : denunciation_kind; + delegate : Signature.Public_key_hash.t; + level : Level.t; + hash : Operation_hash.t; + } + | Too_early_denunciation of { + kind : denunciation_kind; + level : Raw_level.t; + current : Raw_level.t; + } + | Outdated_denunciation of { + kind : denunciation_kind; + level : Raw_level.t; + last_cycle : Cycle.t; + } + + let () = + register_error_kind + `Permanent + ~id:"validate_operation.block.invalid_denunciation" + ~title:"Invalid denunciation" + ~description:"A denunciation is malformed" + ~pp:(fun ppf kind -> + Format.fprintf + ppf + "Malformed double-%a evidence" + pp_denunciation_kind + kind) + Data_encoding.(obj1 (req "kind" denunciation_kind_encoding)) + (function Invalid_denunciation kind -> Some kind | _ -> None) + (fun kind -> Invalid_denunciation kind) ; + register_error_kind + `Permanent + ~id:"validate_operation.block.inconsistent_denunciation" + ~title:"Inconsistent denunciation" + ~description: + "A denunciation operation is inconsistent (two distinct delegates)" + ~pp:(fun ppf (kind, delegate1, delegate2) -> + Format.fprintf + ppf + "Inconsistent double-%a evidence (distinct delegate: %a and %a)" + pp_denunciation_kind + kind + Signature.Public_key_hash.pp_short + delegate1 + Signature.Public_key_hash.pp_short + delegate2) + Data_encoding.( + obj3 + (req "kind" denunciation_kind_encoding) + (req "delegate1" Signature.Public_key_hash.encoding) + (req "delegate2" Signature.Public_key_hash.encoding)) + (function + | Inconsistent_denunciation {kind; delegate1; delegate2} -> + Some (kind, delegate1, delegate2) + | _ -> None) + (fun (kind, delegate1, delegate2) -> + Inconsistent_denunciation {kind; delegate1; delegate2}) ; + register_error_kind + `Branch + ~id:"validate_operation.already_denounced" + ~title:"Already denounced" + ~description:"The same denunciation has already been validated." + ~pp:(fun ppf (kind, delegate, level) -> + Format.fprintf + ppf + "Delegate %a at level %a has already been denounced for a double %a." + pp_denunciation_kind + kind + Signature.Public_key_hash.pp + delegate + Level.pp + level) + Data_encoding.( + obj3 + (req "denunciation_kind" denunciation_kind_encoding) + (req "delegate" Signature.Public_key_hash.encoding) + (req "level" Level.encoding)) + (function + | Already_denounced {kind; delegate; level} -> + Some (kind, delegate, level) + | _ -> None) + (fun (kind, delegate, level) -> Already_denounced {kind; delegate; level}) ; + register_error_kind + `Branch + ~id:"validate_operation.conflicting_denunciation" + ~title:"Conflicting denunciation in current validation state" + ~description: + "The same denunciation has already been validated in the current \ + validation state." + ~pp:(fun ppf (kind, delegate, level, oph) -> + Format.fprintf + ppf + "Double %a evidence for the delegate %a at level %a already exists \ + in the current validation state as operation %a." + pp_denunciation_kind + kind + Signature.Public_key_hash.pp + delegate + Level.pp + level + Operation_hash.pp + oph) + Data_encoding.( + obj4 + (req "denunciation_kind" denunciation_kind_encoding) + (req "delegate" Signature.Public_key_hash.encoding) + (req "level" Level.encoding) + (req "hash" Operation_hash.encoding)) + (function + | Conflicting_denunciation {kind; delegate; level; hash} -> + Some (kind, delegate, level, hash) + | _ -> None) + (fun (kind, delegate, level, hash) -> + Conflicting_denunciation {kind; delegate; level; hash}) ; + register_error_kind + `Temporary + ~id:"validate_operation.block.too_early_denunciation" + ~title:"Too early denunciation" + ~description:"A denunciation is too far in the future" + ~pp:(fun ppf (kind, level, current) -> + Format.fprintf + ppf + "A double-%a denunciation is too far in the future (current level: \ + %a, given level: %a)" + pp_denunciation_kind + kind + Raw_level.pp + current + Raw_level.pp + level) + Data_encoding.( + obj3 + (req "kind" denunciation_kind_encoding) + (req "level" Raw_level.encoding) + (req "current" Raw_level.encoding)) + (function + | Too_early_denunciation {kind; level; current} -> + Some (kind, level, current) + | _ -> None) + (fun (kind, level, current) -> + Too_early_denunciation {kind; level; current}) ; + register_error_kind + `Permanent + ~id:"validate_operation.block.outdated_denunciation" + ~title:"Outdated denunciation" + ~description:"A denunciation is outdated." + ~pp:(fun ppf (kind, level, last_cycle) -> + Format.fprintf + ppf + "A double-%a denunciation is outdated (last acceptable cycle: %a, \ + given level: %a)." + pp_denunciation_kind + kind + Cycle.pp + last_cycle + Raw_level.pp + level) + Data_encoding.( + obj3 + (req "kind" denunciation_kind_encoding) + (req "level" Raw_level.encoding) + (req "last" Cycle.encoding)) + (function + | Outdated_denunciation {kind; level; last_cycle} -> + Some (kind, level, last_cycle) + | _ -> None) + (fun (kind, level, last_cycle) -> + Outdated_denunciation {kind; level; last_cycle}) end module Manager = struct diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 8e46329b5b78..7f85d614d7ff 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -25,9 +25,38 @@ (** Errors that may arise while validating an anonymous operation. *) module Anonymous : sig + type denunciation_kind = Preendorsement | Endorsement + type error += | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} | Conflicting_activation of Ed25519.Public_key_hash.t * Operation_hash.t + | Invalid_denunciation of denunciation_kind + | Inconsistent_denunciation of { + kind : denunciation_kind; + delegate1 : Signature.Public_key_hash.t; + delegate2 : Signature.Public_key_hash.t; + } + | Already_denounced of { + kind : denunciation_kind; + delegate : Signature.Public_key_hash.t; + level : Alpha_context.Level.t; + } + | Conflicting_denunciation of { + kind : denunciation_kind; + delegate : Signature.Public_key_hash.t; + level : Alpha_context.Level.t; + hash : Operation_hash.t; + } + | Too_early_denunciation of { + kind : denunciation_kind; + level : Alpha_context.Raw_level.t; + current : Alpha_context.Raw_level.t; + } + | Outdated_denunciation of { + kind : denunciation_kind; + level : Alpha_context.Raw_level.t; + last_cycle : Alpha_context.Cycle.t; + } end (** Errors that may arise while validating a manager operation. *) diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index b0fdfef2d7d5..62d0f26d7662 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -147,8 +147,6 @@ type stamp = Operation_validated_stamp module Anonymous = struct open Validate_errors.Anonymous - type denunciation_kind = Preendorsement | Endorsement - let validate_activate_account vi vs oph (Activate_account {id = edpkh; activation_code}) = let open Lwt_result_syntax in @@ -175,8 +173,96 @@ module Anonymous = struct return {vs with anonymous_state = {vs.anonymous_state with blinded_pkhs_seen}} - let validate_double_consensus ~consensus_operation:_ _vi vs _oph _op1 _op2 = - return vs + let check_denunciation_age vi kind given_level = + let open Result_syntax in + let current_cycle = vi.current_level.cycle in + let given_cycle = (Level.from_raw vi.ctxt given_level).cycle in + let max_slashing_period = Constants.max_slashing_period vi.ctxt in + let last_slashable_cycle = Cycle.add given_cycle max_slashing_period in + let* () = + error_unless + Cycle.(given_cycle <= current_cycle) + (Too_early_denunciation + {kind; level = given_level; current = vi.current_level.level}) + in + error_unless + Cycle.(last_slashable_cycle > current_cycle) + (Outdated_denunciation + {kind; level = given_level; last_cycle = last_slashable_cycle}) + + let validate_double_consensus (type kind) + ~consensus_operation:denunciation_kind vi vs oph + (op1 : kind Kind.consensus Operation.t) + (op2 : kind Kind.consensus Operation.t) = + let open Lwt_result_syntax in + match (op1.protocol_data.contents, op2.protocol_data.contents) with + | Single (Preendorsement e1), Single (Preendorsement e2) + | Single (Endorsement e1), Single (Endorsement e2) -> + let op1_hash = Operation.hash op1 in + let op2_hash = Operation.hash op2 in + let*? () = + error_unless + (Raw_level.(e1.level = e2.level) + && Round.(e1.round = e2.round) + && (not + (Block_payload_hash.equal + e1.block_payload_hash + e2.block_payload_hash)) + && (* we require an order on hashes to avoid the existence of + equivalent evidences *) + Operation_hash.(op1_hash < op2_hash)) + (Invalid_denunciation denunciation_kind) + in + (* Disambiguate: levels are equal *) + let level = Level.from_raw vi.ctxt e1.level in + let*? () = check_denunciation_age vi denunciation_kind level.level in + let* ctxt, (delegate1_pk, delegate1) = + Stake_distribution.slot_owner vi.ctxt level e1.slot + in + let* ctxt, (_delegate2_pk, delegate2) = + Stake_distribution.slot_owner ctxt level e2.slot + in + let*? () = + error_unless + (Signature.Public_key_hash.equal delegate1 delegate2) + (Inconsistent_denunciation + {kind = denunciation_kind; delegate1; delegate2}) + in + let delegate_pk, delegate = (delegate1_pk, delegate1) in + let*? () = + match + Double_evidence.find + (delegate, level) + vs.anonymous_state.double_consensus_evidences_seen + with + | None -> ok () + | Some oph' -> + error + (Conflicting_denunciation + {kind = denunciation_kind; delegate; level; hash = oph'}) + in + let* already_slashed = + Delegate.already_slashed_for_double_endorsing ctxt delegate level + in + let*? () = + error_unless + (not already_slashed) + (Already_denounced {kind = denunciation_kind; delegate; level}) + in + let*? () = Operation.check_signature delegate_pk vi.chain_id op1 in + let*? () = Operation.check_signature delegate_pk vi.chain_id op2 in + let double_consensus_evidences_seen = + Double_evidence.add + (delegate, level) + oph + vs.anonymous_state.double_consensus_evidences_seen + in + return + { + vs with + anonymous_state = + {vs.anonymous_state with double_consensus_evidences_seen}; + } let validate_double_preendorsement_evidence vi vs oph (Double_preendorsement_evidence {op1; op2}) = -- GitLab From 63acf2e003e625d297d41c3308ce9bf7d1587529 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Tue, 12 Jul 2022 10:53:47 +0200 Subject: [PATCH 07/11] proto: add unrequired double baking in block test --- .../test/integration/consensus/test_double_baking.ml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml index 3eb68159fc42..de07aca60066 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml @@ -401,6 +401,9 @@ let test_double_evidence () = block_fork (c1, c2) blk >>=? fun (blk_a, blk_b) -> Block.bake_until_cycle_end blk_a >>=? fun blk -> double_baking (B blk) blk_a.header blk_b.header |> fun evidence -> + Block.bake ~operations:[evidence; evidence] blk >>= fun e -> + Assert.proto_error_with_info ~loc:__LOC__ e "Unrequired denunciation" + >>=? fun () -> Block.bake ~operation:evidence blk >>=? fun blk -> double_baking (B blk) blk_b.header blk_a.header |> fun evidence -> Block.bake ~operation:evidence blk >>= fun e -> -- GitLab From b3c7095413fe5b47d905f7a7115603ec320bfd7c Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 17:01:04 +0200 Subject: [PATCH 08/11] proto: add validate double baking evidence Co-authored-by: Diane Gallois-Wong --- src/proto_alpha/lib_protocol/apply.ml | 244 +----------------- src/proto_alpha/lib_protocol/apply.mli | 1 - .../consensus/test_double_baking.ml | 38 ++- .../lib_protocol/validate_errors.ml | 56 +++- .../lib_protocol/validate_errors.mli | 10 +- .../lib_protocol/validate_operation.ml | 74 +++++- 6 files changed, 169 insertions(+), 254 deletions(-) diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index d51d65d5f642..8b81ba292f35 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -29,33 +29,9 @@ open Alpha_context -type denunciation_kind = Preendorsement | Endorsement | Block - -let denunciation_kind_encoding = - let open Data_encoding in - string_enum - [ - ("preendorsement", Preendorsement); - ("endorsement", Endorsement); - ("block", Block); - ] - -let pp_denunciation_kind fmt : denunciation_kind -> unit = function - | Preendorsement -> Format.fprintf fmt "preendorsement" - | Endorsement -> Format.fprintf fmt "endorsement" - | Block -> Format.fprintf fmt "baking" - type error += | Not_enough_endorsements of {required : int; provided : int} | Wrong_consensus_operation_branch of Block_hash.t * Block_hash.t - | Invalid_double_baking_evidence of { - hash1 : Block_hash.t; - level1 : Raw_level.t; - round1 : Round.t; - hash2 : Block_hash.t; - level2 : Raw_level.t; - round2 : Round.t; - } | Wrong_level_for_consensus_operation of { expected : Raw_level.t; provided : Raw_level.t; @@ -103,23 +79,6 @@ type error += | Wrong_voting_period of {expected : int32; provided : int32} | Internal_operation_replay of Apply_internal_results.packed_internal_operation - | Invalid_denunciation of denunciation_kind - | Inconsistent_denunciation of { - kind : denunciation_kind; - delegate1 : Signature.Public_key_hash.t; - delegate2 : Signature.Public_key_hash.t; - } - | Unrequired_denunciation - | Too_early_denunciation of { - kind : denunciation_kind; - level : Raw_level.t; - current : Raw_level.t; - } - | Outdated_denunciation of { - kind : denunciation_kind; - level : Raw_level.t; - last_cycle : Cycle.t; - } | Multiple_revelation | Failing_noop_error | Zero_frozen_deposits of Signature.Public_key_hash.t @@ -178,41 +137,6 @@ let () = (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:"block.invalid_double_baking_evidence" - ~title:"Invalid double baking evidence" - ~description: - "A double-baking evidence is inconsistent (two distinct level)" - ~pp:(fun ppf (hash1, level1, round1, hash2, level2, round2) -> - Format.fprintf - ppf - "Invalid double-baking evidence (hash: %a and %a, levels/rounds: \ - (%ld,%ld) and (%ld,%ld))" - Block_hash.pp - hash1 - Block_hash.pp - hash2 - (Raw_level.to_int32 level1) - (Round.to_int32 round1) - (Raw_level.to_int32 level2) - (Round.to_int32 round2)) - Data_encoding.( - obj6 - (req "hash1" Block_hash.encoding) - (req "level1" Raw_level.encoding) - (req "round1" Round.encoding) - (req "hash2" Block_hash.encoding) - (req "level2" Raw_level.encoding) - (req "round2" Round.encoding)) - (function - | Invalid_double_baking_evidence - {hash1; level1; round1; hash2; level2; round2} -> - Some (hash1, level1, round1, hash2, level2, round2) - | _ -> None) - (fun (hash1, level1, round1, hash2, level2, round2) -> - Invalid_double_baking_evidence - {hash1; level1; round1; hash2; level2; round2}) ; register_error_kind `Permanent ~id:"wrong_level_for_consensus_operation" @@ -577,113 +501,6 @@ let () = Apply_internal_results.internal_operation_encoding (function Internal_operation_replay op -> Some op | _ -> None) (fun op -> Internal_operation_replay op) ; - register_error_kind - `Permanent - ~id:"block.invalid_denunciation" - ~title:"Invalid denunciation" - ~description:"A denunciation is malformed" - ~pp:(fun ppf kind -> - Format.fprintf - ppf - "Malformed double-%a evidence" - pp_denunciation_kind - kind) - Data_encoding.(obj1 (req "kind" denunciation_kind_encoding)) - (function Invalid_denunciation kind -> Some kind | _ -> None) - (fun kind -> Invalid_denunciation kind) ; - register_error_kind - `Permanent - ~id:"block.inconsistent_denunciation" - ~title:"Inconsistent denunciation" - ~description: - "A denunciation operation is inconsistent (two distinct delegates)" - ~pp:(fun ppf (kind, delegate1, delegate2) -> - Format.fprintf - ppf - "Inconsistent double-%a evidence (distinct delegate: %a and %a)" - pp_denunciation_kind - kind - Signature.Public_key_hash.pp_short - delegate1 - Signature.Public_key_hash.pp_short - delegate2) - Data_encoding.( - obj3 - (req "kind" denunciation_kind_encoding) - (req "delegate1" Signature.Public_key_hash.encoding) - (req "delegate2" Signature.Public_key_hash.encoding)) - (function - | Inconsistent_denunciation {kind; delegate1; delegate2} -> - Some (kind, delegate1, delegate2) - | _ -> None) - (fun (kind, delegate1, delegate2) -> - Inconsistent_denunciation {kind; delegate1; delegate2}) ; - register_error_kind - `Branch - ~id:"block.unrequired_denunciation" - ~title:"Unrequired denunciation" - ~description:"A denunciation is unrequired" - ~pp:(fun ppf _ -> - Format.fprintf - ppf - "A valid denunciation cannot be applied: the associated delegate has \ - already been denounced for this level.") - Data_encoding.unit - (function Unrequired_denunciation -> Some () | _ -> None) - (fun () -> Unrequired_denunciation) ; - register_error_kind - `Temporary - ~id:"block.too_early_denunciation" - ~title:"Too early denunciation" - ~description:"A denunciation is too far in the future" - ~pp:(fun ppf (kind, level, current) -> - Format.fprintf - ppf - "A double-%a denunciation is too far in the future (current level: %a, \ - given level: %a)" - pp_denunciation_kind - kind - Raw_level.pp - current - Raw_level.pp - level) - Data_encoding.( - obj3 - (req "kind" denunciation_kind_encoding) - (req "level" Raw_level.encoding) - (req "current" Raw_level.encoding)) - (function - | Too_early_denunciation {kind; level; current} -> - Some (kind, level, current) - | _ -> None) - (fun (kind, level, current) -> - Too_early_denunciation {kind; level; current}) ; - register_error_kind - `Permanent - ~id:"block.outdated_denunciation" - ~title:"Outdated denunciation" - ~description:"A denunciation is outdated." - ~pp:(fun ppf (kind, level, last_cycle) -> - Format.fprintf - ppf - "A double-%a is outdated (last acceptable cycle: %a, given level: %a)" - pp_denunciation_kind - kind - Cycle.pp - last_cycle - Raw_level.pp - level) - Data_encoding.( - obj3 - (req "kind" denunciation_kind_encoding) - (req "level" Raw_level.encoding) - (req "last" Cycle.encoding)) - (function - | Outdated_denunciation {kind; level; last_cycle} -> - Some (kind, level, last_cycle) - | _ -> None) - (fun (kind, level, last_cycle) -> - Outdated_denunciation {kind; level; last_cycle}) ; register_error_kind `Permanent ~id:"block.multiple_revelation" @@ -2658,33 +2475,12 @@ let apply_manager_operations ctxt ~payload_producer chain_id ~mempool_mode in return (ctxt, contents_result_list) -let check_denunciation_age ctxt kind given_level = - let max_slashing_period = Constants.max_slashing_period ctxt in - let current_cycle = (Level.current ctxt).cycle in - let given_cycle = (Level.from_raw ctxt given_level).cycle in - let last_slashable_cycle = Cycle.add given_cycle max_slashing_period in - fail_when - Cycle.(given_cycle > current_cycle) - (Too_early_denunciation - {kind; level = given_level; current = (Level.current ctxt).level}) - >>=? fun () -> - fail_unless - Cycle.(last_slashable_cycle > current_cycle) - (Outdated_denunciation - {kind; level = given_level; last_cycle = last_slashable_cycle}) - let punish_delegate ctxt delegate level mistake mk_result ~payload_producer = - let already_slashed, punish = + let punish = match mistake with - | `Double_baking -> - ( Delegate.already_slashed_for_double_baking, - Delegate.punish_double_baking ) - | `Double_endorsing -> - ( Delegate.already_slashed_for_double_endorsing, - Delegate.punish_double_endorsing ) + | `Double_baking -> Delegate.punish_double_baking + | `Double_endorsing -> Delegate.punish_double_endorsing in - already_slashed ctxt delegate level >>=? fun slashed -> - fail_when slashed Unrequired_denunciation >>=? fun () -> punish ctxt delegate level >>=? fun (ctxt, burned, punish_balance_updates) -> (match Tez.(burned /? 2L) with | Ok reward -> @@ -2725,41 +2521,15 @@ let punish_double_endorsement_or_preendorsement (type kind) ctxt mk_result ~payload_producer -let punish_double_baking ctxt chain_id bh1 bh2 ~payload_producer = - let hash1 = Block_header.hash bh1 in - let hash2 = Block_header.hash bh2 in +let punish_double_baking ctxt (bh1 : Block_header.t) ~payload_producer = Fitness.from_raw bh1.shell.fitness >>?= fun bh1_fitness -> let round1 = Fitness.round bh1_fitness in - Fitness.from_raw bh2.shell.fitness >>?= fun bh2_fitness -> - let round2 = Fitness.round bh2_fitness in - ( Raw_level.of_int32 bh1.shell.level >>?= fun level1 -> - Raw_level.of_int32 bh2.shell.level >>?= fun level2 -> - fail_unless - (Compare.Int32.(bh1.shell.level = bh2.shell.level) - && Round.(round1 = round2) - && (* we require an order on hashes to avoid the existence of - equivalent evidences *) - Block_hash.(hash1 < hash2)) - (Invalid_double_baking_evidence - {hash1; level1; round1; hash2; level2; round2}) ) - >>=? fun () -> Raw_level.of_int32 bh1.shell.level >>?= fun raw_level -> - check_denunciation_age ctxt Block raw_level >>=? fun () -> let level = Level.from_raw ctxt raw_level in let committee_size = Constants.consensus_committee_size ctxt in Round.to_slot round1 ~committee_size >>?= fun slot1 -> Stake_distribution.slot_owner ctxt level slot1 - >>=? fun (ctxt, (delegate1_pk, delegate1)) -> - Round.to_slot round2 ~committee_size >>?= fun slot2 -> - Stake_distribution.slot_owner ctxt level slot2 - >>=? fun (ctxt, (_delegate2_pk, delegate2)) -> - fail_unless - Signature.Public_key_hash.(delegate1 = delegate2) - (Inconsistent_denunciation {kind = Block; delegate1; delegate2}) - >>=? fun () -> - let delegate_pk, delegate = (delegate1_pk, delegate1) in - Block_header.check_signature bh1 chain_id delegate_pk >>?= fun () -> - Block_header.check_signature bh2 chain_id delegate_pk >>?= fun () -> + >>=? fun (ctxt, (_delegate_pk, delegate)) -> punish_delegate ctxt delegate @@ -2907,8 +2677,8 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) punish_double_endorsement_or_preendorsement ctxt ~op1 ~payload_producer | Single (Double_endorsement_evidence {op1; op2 = _}) -> punish_double_endorsement_or_preendorsement ctxt ~op1 ~payload_producer - | Single (Double_baking_evidence {bh1; bh2}) -> - punish_double_baking ctxt chain_id bh1 bh2 ~payload_producer + | Single (Double_baking_evidence {bh1; bh2 = _}) -> + punish_double_baking ctxt bh1 ~payload_producer | Single (Activate_account {id = pkh; activation_code}) -> let blinded_pkh = Blinded_public_key_hash.of_ed25519_pkh activation_code pkh diff --git a/src/proto_alpha/lib_protocol/apply.mli b/src/proto_alpha/lib_protocol/apply.mli index 9efa71676548..dca373aa8f8d 100644 --- a/src/proto_alpha/lib_protocol/apply.mli +++ b/src/proto_alpha/lib_protocol/apply.mli @@ -43,7 +43,6 @@ type error += | Tx_rollup_invalid_transaction_ticket_amount | Sc_rollup_feature_disabled | Empty_transaction of Contract.t - | Unrequired_denunciation val begin_partial_construction : context -> diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml index de07aca60066..0c6a76f98f7b 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_double_baking.ml @@ -291,7 +291,9 @@ let test_same_blocks () = Block.bake b >>=? fun ba -> double_baking (B ba) ba.header ba.header |> fun operation -> Block.bake ~operation ba >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid double baking evidence" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_double_baking_evidence _ -> true + | _ -> false) (** Check that an double baking operation that is invalid due to incorrect ordering of the block headers fails. *) @@ -301,7 +303,9 @@ let test_incorrect_order () = double_baking (B genesis) ~correct_order:false blk_a.header blk_b.header |> fun operation -> Block.bake ~operation genesis >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid double baking evidence" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_double_baking_evidence _ -> true + | _ -> false) (** Check that a double baking operation exposing two blocks with different levels fails. *) @@ -311,7 +315,9 @@ let test_different_levels () = Block.bake blk_b >>=? fun blk_b_2 -> double_baking (B blk_a) blk_a.header blk_b_2.header |> fun operation -> Block.bake ~operation blk_a >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Invalid double baking evidence" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Invalid_double_baking_evidence _ -> true + | _ -> false) (** Check that a double baking operation exposing two yet-to-be-baked blocks fails. *) @@ -321,7 +327,11 @@ let test_too_early_double_baking_evidence () = block_fork ~policy:(By_round 0) contracts b >>=? fun (blk_a, blk_b) -> double_baking (B b) blk_a.header blk_b.header |> fun operation -> Block.bake ~operation genesis >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Too early denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Too_early_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Block -> + true + | _ -> false) (** Check that after [max_slashing_period * blocks_per_cycle + 1] blocks -- corresponding to 2 cycles --, it is not possible to create a double baking operation anymore. *) @@ -333,7 +343,11 @@ let test_too_late_double_baking_evidence () = Block.bake_until_n_cycle_end max_slashing_period blk_a >>=? fun blk -> double_baking (B blk) blk_a.header blk_b.header |> fun operation -> Block.bake ~operation blk >>= fun res -> - Assert.proto_error_with_info ~loc:__LOC__ res "Outdated denunciation" + Assert.proto_error ~loc:__LOC__ res (function + | Validate_errors.Anonymous.Outdated_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Block -> + true + | _ -> false) (** Check that before [max_slashing_period * blocks_per_cycle] blocks -- corresponding to 2 cycles --, it is still possible to create a @@ -359,7 +373,9 @@ let test_different_delegates () = Block.bake ~policy:(By_account baker_2) b >>=? fun blk_b -> double_baking (B blk_a) blk_a.header blk_b.header |> fun operation -> Block.bake ~operation blk_a >>= fun e -> - Assert.proto_error_with_info ~loc:__LOC__ e "Invalid double baking evidence" + Assert.proto_error ~loc:__LOC__ e (function + | Validate_errors.Anonymous.Invalid_double_baking_evidence _ -> true + | _ -> false) (** This test is supposed to mimic that a block cannot be baked by one baker and signed by another. The way it tries to show this is by using a @@ -402,12 +418,18 @@ let test_double_evidence () = Block.bake_until_cycle_end blk_a >>=? fun blk -> double_baking (B blk) blk_a.header blk_b.header |> fun evidence -> Block.bake ~operations:[evidence; evidence] blk >>= fun e -> - Assert.proto_error_with_info ~loc:__LOC__ e "Unrequired denunciation" + Assert.proto_error ~loc:__LOC__ e (function + | Validate_errors.Anonymous.Conflicting_denunciation {kind; _} + when kind = Validate_errors.Anonymous.Block -> + true + | _ -> false) >>=? fun () -> Block.bake ~operation:evidence blk >>=? fun blk -> double_baking (B blk) blk_b.header blk_a.header |> fun evidence -> Block.bake ~operation:evidence blk >>= fun e -> - Assert.proto_error_with_info ~loc:__LOC__ e "Unrequired denunciation" + Assert.proto_error ~loc:__LOC__ e (function + | Validate_errors.Anonymous.Already_denounced _ -> true + | _ -> false) let tests = [ diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index be5cc6d5487e..f25a5b807385 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -72,18 +72,31 @@ module Anonymous = struct | Conflicting_activation (edpkh, oph) -> Some (edpkh, oph) | _ -> None) (fun (edpkh, oph) -> Conflicting_activation (edpkh, oph)) - type denunciation_kind = Preendorsement | Endorsement + type denunciation_kind = Preendorsement | Endorsement | Block let denunciation_kind_encoding = let open Data_encoding in string_enum - [("preendorsement", Preendorsement); ("endorsement", Endorsement)] + [ + ("preendorsement", Preendorsement); + ("endorsement", Endorsement); + ("block", Block); + ] let pp_denunciation_kind fmt : denunciation_kind -> unit = function | Preendorsement -> Format.fprintf fmt "preendorsement" | Endorsement -> Format.fprintf fmt "endorsement" + | Block -> Format.fprintf fmt "block" type error += + | Invalid_double_baking_evidence of { + hash1 : Block_hash.t; + level1 : Raw_level.t; + round1 : Round.t; + hash2 : Block_hash.t; + level2 : Raw_level.t; + round2 : Round.t; + } | Invalid_denunciation of denunciation_kind | Inconsistent_denunciation of { kind : denunciation_kind; @@ -113,6 +126,41 @@ module Anonymous = struct } let () = + register_error_kind + `Permanent + ~id:"validate.block.invalid_double_baking_evidence" + ~title:"Invalid double baking evidence" + ~description: + "A double-baking evidence is inconsistent (two distinct levels)" + ~pp:(fun ppf (hash1, level1, round1, hash2, level2, round2) -> + Format.fprintf + ppf + "Invalid double-baking evidence (hash: %a and %a, levels/rounds: \ + (%ld,%ld) and (%ld,%ld))" + Block_hash.pp + hash1 + Block_hash.pp + hash2 + (Raw_level.to_int32 level1) + (Round.to_int32 round1) + (Raw_level.to_int32 level2) + (Round.to_int32 round2)) + Data_encoding.( + obj6 + (req "hash1" Block_hash.encoding) + (req "level1" Raw_level.encoding) + (req "round1" Round.encoding) + (req "hash2" Block_hash.encoding) + (req "level2" Raw_level.encoding) + (req "round2" Round.encoding)) + (function + | Invalid_double_baking_evidence + {hash1; level1; round1; hash2; level2; round2} -> + Some (hash1, level1, round1, hash2, level2, round2) + | _ -> None) + (fun (hash1, level1, round1, hash2, level2, round2) -> + Invalid_double_baking_evidence + {hash1; level1; round1; hash2; level2; round2}) ; register_error_kind `Permanent ~id:"validate_operation.block.invalid_denunciation" @@ -186,7 +234,7 @@ module Anonymous = struct ~description: "The same denunciation has already been validated in the current \ validation state." - ~pp:(fun ppf (kind, delegate, level, oph) -> + ~pp:(fun ppf (kind, delegate, level, hash) -> Format.fprintf ppf "Double %a evidence for the delegate %a at level %a already exists \ @@ -198,7 +246,7 @@ module Anonymous = struct Level.pp level Operation_hash.pp - oph) + hash) Data_encoding.( obj4 (req "denunciation_kind" denunciation_kind_encoding) diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 7f85d614d7ff..58b752a6eace 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -25,12 +25,20 @@ (** Errors that may arise while validating an anonymous operation. *) module Anonymous : sig - type denunciation_kind = Preendorsement | Endorsement + type denunciation_kind = Preendorsement | Endorsement | Block type error += | Invalid_activation of {pkh : Ed25519.Public_key_hash.t} | Conflicting_activation of Ed25519.Public_key_hash.t * Operation_hash.t | Invalid_denunciation of denunciation_kind + | Invalid_double_baking_evidence of { + hash1 : Block_hash.t; + level1 : Alpha_context.Raw_level.t; + round1 : Alpha_context.Round.t; + hash2 : Block_hash.t; + level2 : Alpha_context.Raw_level.t; + round2 : Alpha_context.Round.t; + } | Inconsistent_denunciation of { kind : denunciation_kind; delegate1 : Signature.Public_key_hash.t; diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index 62d0f26d7662..9421aa1efd8b 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -278,9 +278,77 @@ module Anonymous = struct (Double_endorsement_evidence {op1; op2}) = validate_double_consensus ~consensus_operation:Endorsement vi vs oph op1 op2 - let validate_double_baking_evidence _vi vs _oph - (Double_baking_evidence {bh1 = _; bh2 = _}) = - return vs + let validate_double_baking_evidence vi vs oph + (Double_baking_evidence {bh1; bh2}) = + let open Lwt_result_syntax in + let hash1 = Block_header.hash bh1 in + let hash2 = Block_header.hash bh2 in + let*? bh1_fitness = Fitness.from_raw bh1.shell.fitness in + let round1 = Fitness.round bh1_fitness in + let*? bh2_fitness = Fitness.from_raw bh2.shell.fitness in + let round2 = Fitness.round bh2_fitness in + let*? level1 = Raw_level.of_int32 bh1.shell.level in + let*? level2 = Raw_level.of_int32 bh2.shell.level in + let*? () = + error_unless + (Raw_level.(level1 = level2) + && Round.(round1 = round2) + && (* we require an order on hashes to avoid the existence of + equivalent evidences *) + Block_hash.(hash1 < hash2)) + (Invalid_double_baking_evidence + {hash1; level1; round1; hash2; level2; round2}) + in + let*? () = check_denunciation_age vi Block level1 in + let level = Level.from_raw vi.ctxt level1 in + let committee_size = Constants.consensus_committee_size vi.ctxt in + let*? slot1 = Round.to_slot round1 ~committee_size in + let* ctxt, (delegate1_pk, delegate1) = + Stake_distribution.slot_owner vi.ctxt level slot1 + in + let*? slot2 = Round.to_slot round2 ~committee_size in + let* ctxt, (_delegate2_pk, delegate2) = + Stake_distribution.slot_owner ctxt level slot2 + in + let*? () = + error_unless + Signature.Public_key_hash.(delegate1 = delegate2) + (Inconsistent_denunciation {kind = Block; delegate1; delegate2}) + in + let delegate_pk, delegate = (delegate1_pk, delegate1) in + let*? () = + match + Double_evidence.find + (delegate, level) + vs.anonymous_state.double_baking_evidences_seen + with + | None -> ok () + | Some oph' -> + error + (Conflicting_denunciation + {kind = Block; delegate; level; hash = oph'}) + in + let* already_slashed = + Delegate.already_slashed_for_double_baking ctxt delegate level + in + let*? () = + error_unless + (not already_slashed) + (Already_denounced {kind = Block; delegate; level}) + in + let*? () = Block_header.check_signature bh1 vi.chain_id delegate_pk in + let*? () = Block_header.check_signature bh2 vi.chain_id delegate_pk in + let double_baking_evidences_seen = + Double_evidence.add + (delegate, level) + oph + vs.anonymous_state.double_baking_evidences_seen + in + return + { + vs with + anonymous_state = {vs.anonymous_state with double_baking_evidences_seen}; + } let validate_seed_nonce_revelation _vi vs (Seed_nonce_revelation {level = _; nonce = _}) = -- GitLab From e19592fe1f1b475a3417763c1e77ce65e6bc205c Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 18:40:12 +0200 Subject: [PATCH 09/11] proto: add validate seed nonce revelation Co-authored-by: Diane Gallois-Wong --- .../lib_protocol/alpha_context.mli | 5 +++ src/proto_alpha/lib_protocol/nonce_storage.ml | 22 ++++++------ .../lib_protocol/nonce_storage.mli | 26 ++++++++++++++ .../lib_protocol/raw_level_repr.ml | 2 ++ .../lib_protocol/raw_level_repr.mli | 2 ++ .../test/integration/consensus/test_seed.ml | 34 ++++++++++++++++--- .../lib_protocol/validate_errors.ml | 18 ++++++++++ .../lib_protocol/validate_errors.mli | 1 + .../lib_protocol/validate_operation.ml | 30 ++++++++++++++-- 9 files changed, 122 insertions(+), 18 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 93aee27ee492..4d0a34642f74 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -236,6 +236,8 @@ module Raw_level : sig val of_int32 : int32 -> raw_level tzresult val of_int32_exn : int32 -> raw_level + + module Set : Set.S with type elt = raw_level end (** This module re-exports definitions from {!Cycle_repr}. *) @@ -1286,6 +1288,9 @@ module Nonce : sig val record_hash : context -> unrevealed -> context tzresult Lwt.t + (** See {!Nonce_storage.check_unrevealed}. *) + val check_unrevealed : context -> Level.t -> nonce -> unit tzresult Lwt.t + val reveal : context -> Level.t -> nonce -> context tzresult Lwt.t type status = Unrevealed of unrevealed | Revealed of nonce diff --git a/src/proto_alpha/lib_protocol/nonce_storage.ml b/src/proto_alpha/lib_protocol/nonce_storage.ml index 9ffc286eaa5f..adfcc8eee003 100644 --- a/src/proto_alpha/lib_protocol/nonce_storage.ml +++ b/src/proto_alpha/lib_protocol/nonce_storage.ml @@ -32,7 +32,7 @@ let encoding = Seed_repr.nonce_encoding type error += | Too_late_revelation | Too_early_revelation - | Previously_revealed_nonce + | Already_revealed_nonce | Inconsistent_nonce let () = @@ -58,13 +58,13 @@ let () = (fun () -> Too_early_revelation) ; register_error_kind `Branch - ~id:"nonce.previously_revealed" - ~title:"Previously revealed nonce" + ~id:"nonce.already_revealed" + ~title:"Already revealed nonce" ~description:"Duplicated revelation for a nonce." - ~pp:(fun ppf () -> Format.fprintf ppf "This nonce was previously revealed") + ~pp:(fun ppf () -> Format.fprintf ppf "This nonce was already revealed") Data_encoding.unit - (function Previously_revealed_nonce -> Some () | _ -> None) - (fun () -> Previously_revealed_nonce) ; + (function Already_revealed_nonce -> Some () | _ -> None) + (fun () -> Already_revealed_nonce) ; register_error_kind `Branch ~id:"nonce.inconsistent" @@ -98,19 +98,21 @@ let get_unrevealed ctxt (level : Level_repr.t) = then fail Too_late_revelation else Storage.Seed.Nonce.get ctxt level >>=? function - | Revealed _ -> fail Previously_revealed_nonce + | Revealed _ -> fail Already_revealed_nonce | Unrevealed status -> return status) let record_hash ctxt unrevealed = let level = Level_storage.current ctxt in Storage.Seed.Nonce.init ctxt level (Unrevealed unrevealed) -let reveal ctxt level nonce = +let check_unrevealed ctxt (level : Level_repr.t) nonce = get_unrevealed ctxt level >>=? fun unrevealed -> - error_unless + fail_unless (Seed_repr.check_hash nonce unrevealed.nonce_hash) Inconsistent_nonce - >>?= fun () -> Storage.Seed.Nonce.update ctxt level (Revealed nonce) + +let reveal ctxt level nonce = + Storage.Seed.Nonce.update ctxt level (Revealed nonce) type unrevealed = Storage.Seed.unrevealed_nonce = { nonce_hash : Nonce_hash.t; diff --git a/src/proto_alpha/lib_protocol/nonce_storage.mli b/src/proto_alpha/lib_protocol/nonce_storage.mli index e85ce95de2e0..181682a41db9 100644 --- a/src/proto_alpha/lib_protocol/nonce_storage.mli +++ b/src/proto_alpha/lib_protocol/nonce_storage.mli @@ -34,6 +34,12 @@ type t = Seed_repr.nonce type nonce = t +type error += + | Too_late_revelation + | Too_early_revelation + | Already_revealed_nonce + | Inconsistent_nonce + val encoding : nonce Data_encoding.t type unrevealed = Storage.Seed.unrevealed_nonce = { @@ -51,6 +57,26 @@ val check : Raw_context.t -> Level_repr.t -> nonce_presence tzresult Lwt.t val record_hash : Raw_context.t -> unrevealed -> Raw_context.t tzresult Lwt.t +(** Checks that a nonce revelation operation can be safely applied. + + @return [Error Too_early_revelation] if the current cycle is the + cycle 0 or if the previous cycle is lesser than the cycle of the + input level. + + @return [Error Too_late_revelation] if the previous cycle is + greater than the cycle of the input level. This error is also + returned if the current level cycle position is greater or equal to + the nonce revelation threshold. + + @return [Error Already_revealed_nonce] if a nonce is already + revealed in the context for the input level. + + @return [Error Inconsistent_nonce] if the hash of the input nonce + does not correspond to the nonce recover from the context for the + given level. *) +val check_unrevealed : + Raw_context.t -> Level_repr.t -> nonce -> unit tzresult Lwt.t + val reveal : Raw_context.t -> Level_repr.t -> nonce -> Raw_context.t tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/raw_level_repr.ml b/src/proto_alpha/lib_protocol/raw_level_repr.ml index 8a888f0f2d3f..7cbc3fa42445 100644 --- a/src/proto_alpha/lib_protocol/raw_level_repr.ml +++ b/src/proto_alpha/lib_protocol/raw_level_repr.ml @@ -29,6 +29,8 @@ type raw_level = t include (Compare.Int32 : Compare.S with type t := t) +module Set = Set.Make (Compare.Int32) + let pp ppf level = Format.fprintf ppf "%ld" level let rpc_arg = diff --git a/src/proto_alpha/lib_protocol/raw_level_repr.mli b/src/proto_alpha/lib_protocol/raw_level_repr.mli index bc6540be1d64..2efca396ef53 100644 --- a/src/proto_alpha/lib_protocol/raw_level_repr.mli +++ b/src/proto_alpha/lib_protocol/raw_level_repr.mli @@ -30,6 +30,8 @@ type t type raw_level = t +module Set : Set.S with type elt = t + (** @raise Invalid_argument when the level to encode is not positive *) val encoding : raw_level Data_encoding.t diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml index 58ee25898137..d209b9ca1337 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml @@ -162,7 +162,9 @@ let test_revelation_early_wrong_right_twice () = in let*! e = Block.bake ~policy ~operation b in let* () = - Assert.proto_error_with_info ~loc:__LOC__ e "Too early nonce revelation" + Assert.proto_error ~loc:__LOC__ e (function + | Nonce_storage.Too_early_revelation -> true + | _ -> false) in (* finish the cycle excluding the committing baker, id *) let* b = Block.bake_until_cycle_end ~policy b in @@ -176,7 +178,11 @@ let test_revelation_early_wrong_right_twice () = (WithExceptions.Option.to_exn ~none:Not_found @@ Nonce.get wrong_hash) in let*! e = Block.bake ~operation b in - let* () = Assert.proto_error_with_info ~loc:__LOC__ e "Inconsistent nonce" in + let* () = + Assert.proto_error ~loc:__LOC__ e (function + | Nonce_storage.Inconsistent_nonce -> true + | _ -> false) + in (* reveals correctly *) let operation = Op.seed_nonce_revelation @@ -187,6 +193,18 @@ let test_revelation_early_wrong_right_twice () = let* baker_pkh, _, _ = Block.get_next_baker ~policy b in let baker = Alpha_context.Contract.Implicit baker_pkh in let* baker_bal = Context.Contract.balance (B b) baker in + (* test that revealing twice in a block produces an error *) + let*! e = + Block.bake + ~policy:(Block.By_account baker_pkh) + ~operations:[operation; operation] + b + in + let* () = + Assert.proto_error ~loc:__LOC__ e (function + | Validate_errors.Anonymous.Conflicting_nonce_revelation -> true + | _ -> false) + in let* b = Block.bake ~policy:(Block.By_account baker_pkh) ~operation b in (* test that the baker gets the tip reward plus the baking reward*) let* () = @@ -205,7 +223,9 @@ let test_revelation_early_wrong_right_twice () = (WithExceptions.Option.to_exn ~none:Not_found @@ Nonce.get wrong_hash) in let*! e = Block.bake ~operation ~policy b in - Assert.proto_error_with_info ~loc:__LOC__ e "Previously revealed nonce" + Assert.proto_error ~loc:__LOC__ e (function + | Nonce_storage.Already_revealed_nonce -> true + | _ -> false) (** Test that revealing too late produces an error. Note that a committer who doesn't reveal at cycle 1 is not punished.*) @@ -240,7 +260,9 @@ let test_revelation_missing_and_late () = in let*! e = Block.bake ~operation ~policy b in let* () = - Assert.proto_error_with_info ~loc:__LOC__ e "Too late nonce revelation" + Assert.proto_error ~loc:__LOC__ e (function + | Nonce_storage.Too_late_revelation -> true + | _ -> false) in (* finish cycle 1 excluding the committing baker [id] *) let* b = Block.bake_until_cycle_end ~policy b in @@ -252,7 +274,9 @@ let test_revelation_missing_and_late () = (WithExceptions.Option.to_exn ~none:Not_found @@ Nonce.get committed_hash) in let*! e = Block.bake ~operation b in - Assert.proto_error_with_info ~loc:__LOC__ e "Too late nonce revelation" + Assert.proto_error ~loc:__LOC__ e (function + | Nonce_storage.Too_late_revelation -> true + | _ -> false) let wrap e = e >|= Environment.wrap_tzresult diff --git a/src/proto_alpha/lib_protocol/validate_errors.ml b/src/proto_alpha/lib_protocol/validate_errors.ml index f25a5b807385..49fe041e0551 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.ml +++ b/src/proto_alpha/lib_protocol/validate_errors.ml @@ -313,6 +313,24 @@ module Anonymous = struct | _ -> None) (fun (kind, level, last_cycle) -> Outdated_denunciation {kind; level; last_cycle}) + + type error += Conflicting_nonce_revelation + + let () = + register_error_kind + `Branch + ~id:"validate_operation.conflicting_nonce_revelation" + ~title:"Conflicting nonce revelation in the current validation state)." + ~description: + "A revelation for the same nonce has already been validated for the \ + current validation state." + ~pp:(fun ppf () -> + Format.fprintf + ppf + "This nonce was previously revealed in the current block") + Data_encoding.unit + (function Conflicting_nonce_revelation -> Some () | _ -> None) + (fun () -> Conflicting_nonce_revelation) end module Manager = struct diff --git a/src/proto_alpha/lib_protocol/validate_errors.mli b/src/proto_alpha/lib_protocol/validate_errors.mli index 58b752a6eace..ace0fa19004d 100644 --- a/src/proto_alpha/lib_protocol/validate_errors.mli +++ b/src/proto_alpha/lib_protocol/validate_errors.mli @@ -65,6 +65,7 @@ module Anonymous : sig level : Alpha_context.Raw_level.t; last_cycle : Alpha_context.Cycle.t; } + | Conflicting_nonce_revelation end (** Errors that may arise while validating a manager operation. *) diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index 9421aa1efd8b..1bef1348c99f 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -56,6 +56,7 @@ type anonymous_state = { blinded_pkhs_seen : Operation_hash.t Blinded_public_key_hash.Map.t; double_baking_evidences_seen : Operation_hash.t Double_evidence.t; double_consensus_evidences_seen : Operation_hash.t Double_evidence.t; + seed_nonce_levels_seen : Raw_level.Set.t; } let empty_anonymous_state = @@ -63,6 +64,7 @@ let empty_anonymous_state = blinded_pkhs_seen = Blinded_public_key_hash.Map.empty; double_baking_evidences_seen = Double_evidence.empty; double_consensus_evidences_seen = Double_evidence.empty; + seed_nonce_levels_seen = Raw_level.Set.empty; } (** Static information used to validate manager operations. *) @@ -350,9 +352,31 @@ module Anonymous = struct anonymous_state = {vs.anonymous_state with double_baking_evidences_seen}; } - let validate_seed_nonce_revelation _vi vs - (Seed_nonce_revelation {level = _; nonce = _}) = - return vs + let validate_seed_nonce_revelation vi vs + (Seed_nonce_revelation {level = commitment_raw_level; nonce}) = + let open Lwt_result_syntax in + let commitment_level = Level.from_raw vi.ctxt commitment_raw_level in + let*? () = + error_unless + (not + (Raw_level.Set.mem + commitment_raw_level + vs.anonymous_state.seed_nonce_levels_seen)) + Conflicting_nonce_revelation + in + let* () = Nonce.check_unrevealed vi.ctxt commitment_level nonce in + let seed_nonce_levels_seen = + Raw_level.Set.add + commitment_raw_level + vs.anonymous_state.seed_nonce_levels_seen + in + let new_vs = + { + vs with + anonymous_state = {vs.anonymous_state with seed_nonce_levels_seen}; + } + in + return new_vs let validate_vdf_revelation _vi vs (Vdf_revelation {solution = _}) = return vs end -- GitLab From 9558f074f5f7afe4fde3905fc8635b9b35934ae6 Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 11 Jul 2022 19:44:10 +0200 Subject: [PATCH 10/11] proto: add validate vdf revelation --- .../lib_protocol/alpha_context.mli | 7 ++-- src/proto_alpha/lib_protocol/apply.ml | 2 +- src/proto_alpha/lib_protocol/seed_repr.ml | 7 ++-- src/proto_alpha/lib_protocol/seed_storage.ml | 13 ++++--- src/proto_alpha/lib_protocol/seed_storage.mli | 17 ++++++++-- .../test/integration/consensus/test_seed.ml | 34 +++++++++++++++---- .../lib_protocol/validate_operation.ml | 16 ++++++++- 7 files changed, 75 insertions(+), 21 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 4d0a34642f74..4491ff61a680 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -1327,8 +1327,11 @@ module Seed : sig val generate_vdf_setup : seed_discriminant:seed -> seed_challenge:seed -> vdf_setup - val check_vdf_and_update_seed : - context -> vdf_solution -> context tzresult Lwt.t + (** See {!Seed_storage.check_vdf}. *) + val check_vdf : context -> vdf_solution -> unit tzresult Lwt.t + + (** See {!Seed_storage.update_seed}. *) + val update_seed : context -> vdf_solution -> context tzresult Lwt.t val compute_randao : context -> context tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 8b81ba292f35..4eabeca9c535 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -2667,7 +2667,7 @@ let apply_contents_list (type kind) ctxt chain_id (apply_mode : apply_mode) >|=? fun (ctxt, balance_updates) -> (ctxt, Single_result (Seed_nonce_revelation_result balance_updates)) | Single (Vdf_revelation {solution}) -> - Seed.check_vdf_and_update_seed ctxt solution >>=? fun ctxt -> + Seed.update_seed ctxt solution >>=? fun ctxt -> let tip = Constants.seed_nonce_revelation_tip ctxt in let contract = Contract.Implicit payload_producer in Token.transfer ctxt `Revelation_rewards (`Contract contract) tip diff --git a/src/proto_alpha/lib_protocol/seed_repr.ml b/src/proto_alpha/lib_protocol/seed_repr.ml index f06d2fe82dd3..dac874d51e22 100644 --- a/src/proto_alpha/lib_protocol/seed_repr.ml +++ b/src/proto_alpha/lib_protocol/seed_repr.ml @@ -243,9 +243,10 @@ let generate_vdf_setup ~seed_discriminant ~seed_challenge = let verify (discriminant, challenge) vdf_difficulty solution = (* We return false when getting non group elements as input *) let result, proof = solution in - Option.catch - ~catch_only:(function Invalid_argument _ -> true | _ -> false) - (fun () -> Vdf.verify discriminant challenge vdf_difficulty result proof) + (* Note: external library call must be wrapped to ensure that + exceptions are caught. *) + Option.catch (fun () -> + Vdf.verify discriminant challenge vdf_difficulty result proof) let vdf_to_seed seed_challenge solution = let result, _ = solution in diff --git a/src/proto_alpha/lib_protocol/seed_storage.ml b/src/proto_alpha/lib_protocol/seed_storage.ml index 99d6396575a7..79c789c1abdb 100644 --- a/src/proto_alpha/lib_protocol/seed_storage.ml +++ b/src/proto_alpha/lib_protocol/seed_storage.ml @@ -168,7 +168,7 @@ let get_seed_computation_status ctxt = return (Vdf_revelation_stage {seed_discriminant; seed_challenge}) | VDF_seed -> return Computation_finished -let check_vdf_and_update_seed ctxt vdf_solution = +let check_vdf ctxt vdf_solution = let* r = get_seed_computation_status ctxt in let*? seed_discriminant, seed_challenge = match r with @@ -190,8 +190,6 @@ let check_vdf_and_update_seed ctxt vdf_solution = return (ctxt, setup) | Some setup -> return (ctxt, setup) in - (* If VDF is valid, compute and update seed and change seed status from - * RANDAO to VDF *) let*? () = error_unless (Option.value @@ -202,10 +200,17 @@ let check_vdf_and_update_seed ctxt vdf_solution = vdf_solution)) Unverified_vdf in - let new_seed = Seed_repr.vdf_to_seed seed_challenge vdf_solution in + return () + +let update_seed ctxt vdf_solution = + let open Lwt_result_syntax in + (* compute and update seed and change seed status from RANDAO to + VDF *) let current_cycle = (Level_storage.current ctxt).cycle in let preserved = Constants_storage.preserved_cycles ctxt in let cycle_computed = Cycle_repr.add current_cycle (preserved + 1) in + let* seed_challenge = Storage.Seed.For_cycle.get ctxt cycle_computed in + let new_seed = Seed_repr.vdf_to_seed seed_challenge vdf_solution in Storage.Seed.For_cycle.update ctxt cycle_computed new_seed Seed_repr.VDF_seed let for_cycle ctxt cycle = diff --git a/src/proto_alpha/lib_protocol/seed_storage.mli b/src/proto_alpha/lib_protocol/seed_storage.mli index 8942a010c9fc..d9488ed981b9 100644 --- a/src/proto_alpha/lib_protocol/seed_storage.mli +++ b/src/proto_alpha/lib_protocol/seed_storage.mli @@ -48,9 +48,20 @@ type error += val init : ?initial_seed:State_hash.t -> Raw_context.t -> Raw_context.t tzresult Lwt.t -(** Verifies if a VDF (result, proof) is valid, if so updates the seed with a - function of the VDF result. *) -val check_vdf_and_update_seed : +(** Verifies if a VDF (result, proof) is valid. + + @return [Error Too_early_revelation] if the nonce revelation + threshold is greater than the current level cycle position. + + @return [Error Already_accepted] if a VDF seed has already been + recorded. + + @return [Error Unverified_vdf] if the {!Seed_repr.vdf_solution} is + not verified. *) +val check_vdf : Raw_context.t -> Seed_repr.vdf_solution -> unit tzresult Lwt.t + +(** Updates the seed with a function of the VDF result. *) +val update_seed : Raw_context.t -> Seed_repr.vdf_solution -> Raw_context.t tzresult Lwt.t val for_cycle : Raw_context.t -> Cycle_repr.t -> Seed_repr.seed tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml index d209b9ca1337..0f7d505e66f6 100644 --- a/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml +++ b/src/proto_alpha/lib_protocol/test/integration/consensus/test_seed.ml @@ -433,14 +433,20 @@ let test_early_incorrect_unverified_correct_already_vdf () = let operation = Op.vdf_revelation (B b) dummy_solution in let*! e = Block.bake ~operation b in let* () = - Assert.proto_error_with_info ~loc:__LOC__ e "Too early VDF revelation" + Assert.proto_error ~loc:__LOC__ e (function + | Seed_storage.Too_early_revelation -> true + | _ -> false) in (* bake until nonce reveal period finishes *) let* b = Block.bake_n ~policy nonce_revelation_threshold b in (* test that revealing non group elements produces an error *) let operation = Op.vdf_revelation (B b) dummy_solution in let*! e = Block.bake ~operation b in - let* () = Assert.proto_error_with_info ~loc:__LOC__ e "Unverified VDF" in + let* () = + Assert.proto_error ~loc:__LOC__ e (function + | Seed_storage.Unverified_vdf -> true + | _ -> false) + in let* seed_status = Context.get_seed_computation (B b) in match seed_status with | Nonce_revelation_stage -> assert false @@ -462,7 +468,11 @@ let test_early_incorrect_unverified_correct_already_vdf () = in let operation = Op.vdf_revelation (B b) wrong_solution in let*! e = Block.bake ~operation b in - let* () = Assert.proto_error_with_info ~loc:__LOC__ e "Unverified VDF" in + let* () = + Assert.proto_error ~loc:__LOC__ e (function + | Seed_storage.Unverified_vdf -> true + | _ -> false) + in (* test with correct input *) (* compute the VDF solution (the result and the proof ) *) let solution = @@ -474,6 +484,17 @@ let test_early_incorrect_unverified_correct_already_vdf () = in let* baker_bal = Context.Contract.balance (B b) baker in let operation = Op.vdf_revelation (B b) solution in + let*! e = + Block.bake + ~policy:(Block.By_account baker_pkh) + ~operations:[operation; operation] + b + in + let* () = + Assert.proto_error ~loc:__LOC__ e (function + | Seed_storage.Already_accepted -> true + | _ -> false) + in (* verify the balance was credited following operation inclusion *) let* b = Block.bake ~policy:(Block.By_account baker_pkh) ~operation b in let* () = @@ -494,10 +515,9 @@ let test_early_incorrect_unverified_correct_already_vdf () = let operation = Op.vdf_revelation (B b) solution in let*! e = Block.bake ~operation b in let* () = - Assert.proto_error_with_info - ~loc:__LOC__ - e - "Previously revealed VDF" + Assert.proto_error ~loc:__LOC__ e (function + | Seed_storage.Already_accepted -> true + | _ -> false) in (* verify the stored seed has the expected value *) let open Data_encoding.Binary in diff --git a/src/proto_alpha/lib_protocol/validate_operation.ml b/src/proto_alpha/lib_protocol/validate_operation.ml index 1bef1348c99f..771ecf99e2f0 100644 --- a/src/proto_alpha/lib_protocol/validate_operation.ml +++ b/src/proto_alpha/lib_protocol/validate_operation.ml @@ -57,6 +57,7 @@ type anonymous_state = { double_baking_evidences_seen : Operation_hash.t Double_evidence.t; double_consensus_evidences_seen : Operation_hash.t Double_evidence.t; seed_nonce_levels_seen : Raw_level.Set.t; + vdf_solution_seen : bool; } let empty_anonymous_state = @@ -65,6 +66,7 @@ let empty_anonymous_state = double_baking_evidences_seen = Double_evidence.empty; double_consensus_evidences_seen = Double_evidence.empty; seed_nonce_levels_seen = Raw_level.Set.empty; + vdf_solution_seen = false; } (** Static information used to validate manager operations. *) @@ -378,7 +380,19 @@ module Anonymous = struct in return new_vs - let validate_vdf_revelation _vi vs (Vdf_revelation {solution = _}) = return vs + let validate_vdf_revelation vi vs (Vdf_revelation {solution}) = + let open Lwt_result_syntax in + let*? () = + error_unless + (not vs.anonymous_state.vdf_solution_seen) + Seed_storage.Already_accepted + in + let* () = Seed.check_vdf vi.ctxt solution in + return + { + vs with + anonymous_state = {vs.anonymous_state with vdf_solution_seen = true}; + } end module Manager = struct -- GitLab From 0a9b35422f927972379ca857a5eec2b2d331b89c Mon Sep 17 00:00:00 2001 From: Albin Coquereau Date: Mon, 1 Aug 2022 17:46:15 +0200 Subject: [PATCH 11/11] changes: add newly introduced errors in minor changes section and add an entry for the validation of anonymous operation and the splitting of `check_vdf_and_update_seed` function in the internal section --- docs/protocols/alpha.rst | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/protocols/alpha.rst b/docs/protocols/alpha.rst index 4bd5f526b752..c9e7528cbed2 100644 --- a/docs/protocols/alpha.rst +++ b/docs/protocols/alpha.rst @@ -55,7 +55,21 @@ Bug Fixes Minor Changes ------------- -- Split internal transactions. (MR :gl:`!5585`) +- Split internal transactions. (MR :gl:`!5585`) + +- Rename error ``Previously_revealed_nonce`` in + ``Already_revealed_nonce`` (MR :gl:`!5849`) + +- New error ``Conflicting_activation`` (MR :gl:`!5849`) + +- New error ``Already_denounced`` replace ``Unrequired_denunciation`` + (MR :gl:`!5849`) + +- New error ``Conflicting_denunciation`` (MR + :gl:`!5849`) + +- New error ``Conflicting_nonce_revelation`` (MR + :gl:`!5849`) Internal -------- @@ -78,3 +92,11 @@ Internal it return a list of key-value pairs. Also change the name of the signature ``Non_iterable_indexed_carbonated_data_storage_with_values`` to ``Indexed_carbonated_data_storage``. (MR :gl:`!3491`) + +- Move the checks part of anonymous operation to + ``validate_operation.ml``. The effects part remains in + ``apply_operation``. (MR :gl:`!5849`) + +- 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`) -- GitLab