certora-cli-beta-mirror 7.28.0__py3-none-any.whl → 8.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +10 -3
  2. certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +51 -16
  3. certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +3 -0
  4. certora_cli/CertoraProver/castingInstrumenter.py +192 -0
  5. certora_cli/CertoraProver/certoraApp.py +52 -0
  6. certora_cli/CertoraProver/certoraBuild.py +694 -207
  7. certora_cli/CertoraProver/certoraBuildCacheManager.py +21 -17
  8. certora_cli/CertoraProver/certoraBuildDataClasses.py +8 -2
  9. certora_cli/CertoraProver/certoraBuildRust.py +88 -54
  10. certora_cli/CertoraProver/certoraBuildSui.py +112 -0
  11. certora_cli/CertoraProver/certoraCloudIO.py +97 -96
  12. certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +230 -84
  13. certora_cli/CertoraProver/certoraCollectRunMetadata.py +52 -6
  14. certora_cli/CertoraProver/certoraCompilerParameters.py +11 -0
  15. certora_cli/CertoraProver/certoraConfigIO.py +43 -35
  16. certora_cli/CertoraProver/certoraContext.py +128 -54
  17. certora_cli/CertoraProver/certoraContextAttributes.py +415 -234
  18. certora_cli/CertoraProver/certoraContextValidator.py +152 -105
  19. certora_cli/CertoraProver/certoraContractFuncs.py +34 -1
  20. certora_cli/CertoraProver/certoraParseBuildScript.py +8 -10
  21. certora_cli/CertoraProver/certoraType.py +10 -1
  22. certora_cli/CertoraProver/certoraVerifyGenerator.py +22 -4
  23. certora_cli/CertoraProver/erc7201.py +45 -0
  24. certora_cli/CertoraProver/splitRules.py +23 -18
  25. certora_cli/CertoraProver/storageExtension.py +351 -0
  26. certora_cli/EquivalenceCheck/Eq_default.conf +0 -1
  27. certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -1
  28. certora_cli/EquivalenceCheck/equivCheck.py +2 -1
  29. certora_cli/Mutate/mutateApp.py +41 -22
  30. certora_cli/Mutate/mutateAttributes.py +11 -0
  31. certora_cli/Mutate/mutateValidate.py +42 -2
  32. certora_cli/Shared/certoraAttrUtil.py +21 -5
  33. certora_cli/Shared/certoraUtils.py +180 -60
  34. certora_cli/Shared/certoraValidateFuncs.py +68 -26
  35. certora_cli/Shared/proverCommon.py +308 -0
  36. certora_cli/certoraCVLFormatter.py +76 -0
  37. certora_cli/certoraConcord.py +39 -0
  38. certora_cli/certoraEVMProver.py +4 -3
  39. certora_cli/certoraRanger.py +39 -0
  40. certora_cli/certoraRun.py +83 -223
  41. certora_cli/certoraSolanaProver.py +40 -128
  42. certora_cli/certoraSorobanProver.py +59 -4
  43. certora_cli/certoraSuiProver.py +93 -0
  44. certora_cli_beta_mirror-8.5.0.dist-info/LICENSE +15 -0
  45. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/METADATA +21 -5
  46. certora_cli_beta_mirror-8.5.0.dist-info/RECORD +81 -0
  47. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/WHEEL +1 -1
  48. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/entry_points.txt +3 -0
  49. certora_jars/ASTExtraction.jar +0 -0
  50. certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
  51. certora_jars/Typechecker.jar +0 -0
  52. certora_cli_beta_mirror-7.28.0.dist-info/LICENSE +0 -22
  53. certora_cli_beta_mirror-7.28.0.dist-info/RECORD +0 -70
  54. {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,8 @@ import sys
18
18
  import argparse
19
19
  import json
20
20
  import logging
21
+ from collections import OrderedDict
22
+
21
23
  import json5
22
24
  import re
23
25
  import urllib3.util
@@ -89,7 +91,7 @@ class RunSources(Util.NoValEnum):
89
91
  MUTATION = auto()
90
92
  BENCHMARK = auto()
91
93
  LIGHT_TEST = auto()
92
- RERUN = auto()
94
+ REPORT = auto()
93
95
 
94
96
 
95
97
  class WaitForResultOptions(Util.NoValEnum):
@@ -97,6 +99,11 @@ class WaitForResultOptions(Util.NoValEnum):
97
99
  ALL = auto()
98
100
 
99
101
 
102
+ class UrlVisibilityOptions(Util.NoValEnum):
103
+ PRIVATE = auto()
104
+ PUBLIC = auto()
105
+
106
+
100
107
  def is_solc_file_valid(orig_filename: Optional[str]) -> str:
101
108
  """
102
109
  Verifies that a given --solc argument is valid:
@@ -122,7 +129,7 @@ def is_solc_file_valid(orig_filename: Optional[str]) -> str:
122
129
  if filename.endswith(f".{suffix}"):
123
130
  raise Util.CertoraUserInputError(f"wrong Solidity executable given: {filename}")
124
131
 
125
- # see https://docs.python.org/3.8/library/shutil.html#shutil.which. We use no mask to give a precise error
132
+ # see https://docs.python.org/3.9/library/shutil.html#shutil.which. We use no mask to give a precise error
126
133
  solc_location = shutil.which(filename, os.F_OK)
127
134
  if solc_location is not None:
128
135
  solc_path = Path(solc_location)
@@ -423,7 +430,7 @@ def validate_contract_extension_attr(map: Any) -> Dict[str, List[Dict[str, Any]]
423
430
  return map
424
431
 
425
432
 
426
- def validate_input_file(file: str) -> str:
433
+ def validate_evm_input_file(file: str) -> str:
427
434
  # [file[:contractName] ...] or CONF_FILE.conf or TAC_FILE.tac
428
435
 
429
436
  if Util.SOL_EXT in file:
@@ -467,12 +474,12 @@ def validate_input_file(file: str) -> str:
467
474
  except Exception as e:
468
475
  raise Util.CertoraUserInputError(f"Cannot access file {file} : {e}")
469
476
  return file
470
- elif any(file.endswith(ext) for ext in Util.VALID_FILE_EXTENSIONS):
477
+ elif any(file.endswith(ext) for ext in Util.VALID_EVM_EXTENSIONS):
471
478
  validate_readable_file(file)
472
479
  return file
473
480
 
474
481
  raise Util.CertoraUserInputError(
475
- f"input file {file} is not in one of the supported types ({Util.VALID_FILE_EXTENSIONS})")
482
+ f"input file {file} is not in one of the supported types ({Util.VALID_EVM_EXTENSIONS})")
476
483
 
477
484
 
478
485
  def validate_json_file(file: str) -> str:
@@ -543,12 +550,12 @@ def validate_struct_link(link: str) -> str:
543
550
  return link
544
551
 
545
552
 
546
- def validate_assert_contracts(contract: str) -> str:
547
- if not re.match(Util.SOLIDITY_ID_STRING_RE, contract):
553
+ def validate_equivalence_contracts(equiv_string: str) -> str:
554
+ if not re.match(f'^{Util.SOLIDITY_ID_SUBSTRING_RE}={Util.SOLIDITY_ID_SUBSTRING_RE}$', equiv_string):
548
555
  raise Util.CertoraUserInputError(
549
- f"Contract name {contract} can include only alphanumeric characters, dollar signs or underscores")
550
- return contract
551
-
556
+ f"Equivalence check must be ContractA=ContractB, got {equiv_string}"
557
+ )
558
+ return equiv_string
552
559
 
553
560
  def validate_packages(package: str) -> str:
554
561
  if not re.search("^[^=]+=[^=]+$", package):
@@ -660,14 +667,14 @@ def validate_address(value: str) -> str:
660
667
  return value
661
668
 
662
669
 
663
- def validate_solc_via_ir_map(args: Dict[str, bool]) -> None:
670
+ def validate_boolean_map(args: Dict[str, bool], attr_name: str) -> None:
671
+ attr_map_name = attr_name + "_map"
664
672
  if not isinstance(args, dict):
665
- raise Util.CertoraUserInputError("'solc_via_ir_map' should be stored as a map "
666
- f"(type was {type(args).__name__})")
673
+ raise Util.CertoraUserInputError(f"'{attr_map_name}' should be stored as a map (type was {type(args).__name__})")
667
674
 
668
675
  for contract, value in args.items():
669
676
  if not isinstance(value, bool):
670
- raise Util.CertoraUserInputError(f"'solc_via_ir_map' should map {contract} to a boolean value, "
677
+ raise Util.CertoraUserInputError(f"'{attr_map_name}' should map {contract} to a boolean value, "
671
678
  f"got ({value}, type: {type(value).__name__})")
672
679
 
673
680
  values = args.values()
@@ -675,9 +682,15 @@ def validate_solc_via_ir_map(args: Dict[str, bool]) -> None:
675
682
 
676
683
  if all(x == first for x in values):
677
684
  if first:
678
- validation_logger.warning("all via_ir values are set to True '--solc_via_ir' can be used instead")
685
+ validation_logger.warning(f"all {attr_map_name} values are set to True '{attr_name}' can be used instead")
679
686
  else:
680
- validation_logger.warning("all via_ir values are set to False, this flag/attribute can be omitted")
687
+ validation_logger.warning(f"all {attr_map_name} values are set to False, this flag/attribute can be omitted")
688
+
689
+ def validate_solc_via_ir_map(args: Dict[str, bool]) -> None:
690
+ validate_boolean_map(args, 'solc_via_ir')
691
+
692
+ def validate_vyper_venom_map(args: Dict[str, bool]) -> None:
693
+ validate_boolean_map(args, 'vyper_venom')
681
694
 
682
695
  def validate_solc_evm_version_map(args: Dict[str, str]) -> None:
683
696
  if not isinstance(args, dict):
@@ -751,8 +764,18 @@ def validate_git_hash(git_hash: str) -> str:
751
764
  raise Util.CertoraUserInputError("Git hash must consist of between 1 and 40 characters")
752
765
  return git_hash
753
766
 
767
+ def validate_check_method_flag(method: str) -> str:
768
+ if '.' in method:
769
+ raise Util.CertoraUserInputError(f"Malformed `check_mathod` argument '{method}': checked method cannot contain a dot. Use only the method name without the contract prefix."
770
+ "the contract part is not allowed in `--check_method`")
771
+ if ' ' in method:
772
+ raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--check_method`: remove all whitespace")
773
+
774
+ if not __validate_matching_parens(method):
775
+ raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--check_method`: unmatched parenthesis")
776
+ return method
754
777
 
755
- def validate_method_flag(method: str) -> str:
778
+ def validate_evm_method_flag(method: str) -> str:
756
779
  contract_and_method = method.split('.')
757
780
  if len(contract_and_method) > 2:
758
781
  raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--method` list: a method should be of the form `[ContractName.]functionABISignature(...)`")
@@ -771,6 +794,8 @@ def validate_method_flag(method: str) -> str:
771
794
 
772
795
  return method
773
796
 
797
+ def validate_move_method_flag(method: str) -> str:
798
+ return validate_move_function_name(method)
774
799
 
775
800
  def __validate_matching_parens(s: str) -> bool:
776
801
  stack = []
@@ -806,17 +831,30 @@ def __validate_solidity_id(string: str, object: str) -> str:
806
831
  def validate_contract_name(contract_name: str) -> str:
807
832
  return __validate_solidity_id(contract_name, "contract")
808
833
 
809
-
810
- def validate_rule_name(rule_str: str) -> str:
811
- if ("*" not in rule_str):
812
- return __validate_solidity_id(rule_str, "rule")
813
-
814
- # we have a rule pattern string
834
+ def validate_rule_pattern_string(rule_str: str) -> str:
815
835
  if not re.match(r"^[a-zA-Z0-9_$*]+$", rule_str):
816
836
  raise Util.CertoraUserInputError(f"invalid rule pattern \"{rule_str}\": rule patterns must contain only "
817
837
  "letters, digits, dollar signs, underscores, or asterisks")
818
838
  return rule_str
819
839
 
840
+ def validate_evm_rule_name(rule_str: str) -> str:
841
+ if ("*" in rule_str):
842
+ return validate_rule_pattern_string(rule_str)
843
+ else:
844
+ return __validate_solidity_id(rule_str, "rule")
845
+
846
+ def validate_move_function_name(name: str) -> str:
847
+ if not re.match(r"^(0x[0-9a-fA-F]+|([a-zA-Z_][a-zA-Z0-9_]*))::[a-zA-Z_][a-zA-Z0-9_]*::[a-zA-Z_][a-zA-Z0-9_]*$", name):
848
+ raise Util.CertoraUserInputError(f"invalid Move function name \"{name}\": must be a fully-qualified Move "
849
+ "function name")
850
+ return name
851
+
852
+ def validate_move_rule_name(rule_str: str) -> str:
853
+ if ("*" in rule_str):
854
+ return validate_rule_pattern_string(rule_str)
855
+ else:
856
+ return validate_move_function_name(rule_str)
857
+
820
858
 
821
859
  MAX_MSG_LEN: int = 256
822
860
 
@@ -827,7 +865,7 @@ def validate_msg(msg: str) -> str:
827
865
  msg = (msg[:MAX_MSG_LEN - 3] + "...")
828
866
  validation_logger.warning(f"'msg' can't accept strings longer than {MAX_MSG_LEN} chars, string was truncated")
829
867
 
830
- additional_chars = {'(', ' ', ',', '/', '[', "'", '-', '"', '_', ']', '.', ')', ':', '\\', '='}
868
+ additional_chars = {'(', ' ', ',', '/', '[', "'", '-', '"', '_', ']', '.', ')', ':', '\\', '=', '*', '$'}
831
869
  valid_chars = set(string.ascii_letters) | set(string.digits) | additional_chars
832
870
  invalid_chars = set(msg) - valid_chars
833
871
  if len(invalid_chars) > 0:
@@ -931,7 +969,7 @@ def validate_on_off(value: str) -> str:
931
969
  return __validate_enum_value(value, OnOffValue)
932
970
 
933
971
 
934
- def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict[str, Union[str, bool]]:
972
+ def parse_ordered_dict(conf_key: str, input_string: str, value_type: Type = str) -> OrderedDict[str, Union[str, bool]]:
935
973
  """
936
974
  convert CLI flag value string of the form <key>=<value>,<key>=<value>,.. to a Dict.
937
975
  Keys with different values raise an exception
@@ -964,7 +1002,7 @@ def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict
964
1002
  raise argparse.ArgumentTypeError(f"{conf_key} argument {input_string} is of wrong format. Must be of format:"
965
1003
  f"<key>=<value>[,..]")
966
1004
 
967
- return_dict = {} # type: Dict[str, Union[str, bool]]
1005
+ return_dict = OrderedDict() # type: OrderedDict[str, Union[str, bool]]
968
1006
 
969
1007
  for match in input_string.split(','):
970
1008
  key, value = match.split('=')
@@ -986,6 +1024,10 @@ def validate_wait_for_results(value: str) -> str:
986
1024
  return __validate_enum_value(value, WaitForResultOptions)
987
1025
 
988
1026
 
1027
+ def validate_url_visibility(value: str) -> str:
1028
+ return __validate_enum_value(value, UrlVisibilityOptions)
1029
+
1030
+
989
1031
  def validate_json5_file(file: str) -> str:
990
1032
  file_exists_and_readable(file)
991
1033
 
@@ -0,0 +1,308 @@
1
+ # The Certora Prover
2
+ # Copyright (C) 2025 Certora Ltd.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, version 3 of the License.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ """
17
+ Shared helpers for certoraRun entry points (Solana & Soroban & EVM).
18
+
19
+ Placing the context_build environment preparation logic and verification helpers in one module
20
+ reduces duplication from the dedicated entry scripts.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import sys
26
+ import logging
27
+ import functools
28
+ from pathlib import Path
29
+ from dataclasses import dataclass
30
+ from rich.console import Console
31
+ from typing import List, Tuple, Type, Optional, Dict, NoReturn, Callable
32
+ from os import getenv
33
+
34
+ scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
35
+ sys.path.insert(0, str(scripts_dir_path))
36
+
37
+ from CertoraProver.certoraCloudIO import validate_version_and_branch
38
+ from Shared.certoraLogging import LoggingManager
39
+ from Shared import certoraUtils as Util
40
+ import CertoraProver.certoraContext as Ctx
41
+ from CertoraProver.certoraContextClass import CertoraContext
42
+ import CertoraProver.certoraContextAttributes as Attrs
43
+ from CertoraProver.certoraCollectRunMetadata import collect_run_metadata
44
+ from CertoraProver.certoraCollectConfigurationLayout import collect_configuration_layout
45
+ from CertoraProver import certoraContextValidator as Cv
46
+ from CertoraProver.certoraCloudIO import CloudVerification
47
+ import CertoraProver.certoraApp as App
48
+
49
+ log = logging.getLogger(__name__)
50
+
51
+
52
+ VIOLATIONS_EXIT_CODE = 100
53
+
54
+ @dataclass
55
+ class CertoraRunResult:
56
+ link: Optional[str] # Path to emv_dir if running locally, or the link to the job status page
57
+ is_local_link: bool
58
+ src_dir: Path
59
+ rule_report_link: Optional[str]
60
+
61
+ class CertoraFoundViolations(Exception):
62
+ def __init__(self, message: str, results: Optional[CertoraRunResult] = None) -> None:
63
+ super().__init__(message)
64
+ self.results = results
65
+
66
+ # --------------------------------------------------------------------------- #
67
+ # Setup Environment
68
+ # --------------------------------------------------------------------------- #
69
+ def build_context(args: List[str], app: Type[App.CertoraApp]) -> Tuple[CertoraContext, LoggingManager]:
70
+ """
71
+ Build the context for the Certora Prover.
72
+ This function is responsible for setting up the context and logging manager
73
+ for the Certora Prover. It handles the following tasks:
74
+ 1. Setting up the logging manager
75
+ 2. Parsing the command line arguments
76
+ 3. Setting up the context
77
+
78
+ Args:
79
+ args: The command line arguments to parse.
80
+ app: The application
81
+ Returns:
82
+ A tuple containing the CertoraContext object and the LoggingManager object.
83
+ """
84
+ Attrs.set_attribute_class(app.attr_class)
85
+ non_str_els = [x for x in args if not isinstance(x, str)]
86
+ if non_str_els:
87
+ print(f"args for run_certora that are not strings: {non_str_els}")
88
+ exit(1)
89
+
90
+ # If we are not in debug mode, we do not want to print the traceback in case of exceptions.
91
+ if '--debug' not in args: # We check manually, because we want no traceback in argument parsing exceptions
92
+ sys.tracebacklimit = 0
93
+
94
+ # creating the default internal dir, files may be copied to user defined build directory after
95
+ # parsing the input
96
+
97
+ if not ('--help' in args or '--version' in args):
98
+ Util.reset_certora_internal_dir()
99
+ Util.safe_create_dir(Util.get_build_dir())
100
+ logging_manager = LoggingManager()
101
+
102
+ Ctx.handle_flags_in_args(args, app)
103
+ context = Ctx.get_args(args, app) # Parse arguments
104
+
105
+ assert logging_manager, "logging manager was not set"
106
+ logging_manager.set_log_level_and_format(is_quiet=Ctx.is_minimal_cli_output(context),
107
+ debug=context.debug,
108
+ debug_topics=context.debug_topics,
109
+ show_debug_topics=context.show_debug_topics)
110
+
111
+ return context, logging_manager
112
+
113
+
114
+ def collect_and_dump_metadata(context: CertoraContext) -> None:
115
+ """
116
+ Collect and validate run metadata.
117
+
118
+ Args:
119
+ context: The Certora context containing verification settings
120
+
121
+ Raises:
122
+ Util.TestResultsReady: If this is a metadata test run
123
+ """
124
+ metadata = collect_run_metadata(wd=Path.cwd(), raw_args=sys.argv, context=context)
125
+
126
+ if context.test == str(Util.TestValue.CHECK_METADATA):
127
+ raise Util.TestResultsReady(metadata)
128
+
129
+ metadata.dump()
130
+
131
+
132
+ def collect_and_dump_config_layout(context: CertoraContext) -> None:
133
+ """
134
+ Collect and dump the configuration layout.
135
+
136
+ Args:
137
+ context: The Certora context containing verification settings
138
+
139
+ Raises:
140
+ Util.TestResultsReady: If this is a configuration layout test run
141
+ """
142
+ configuration_layout = collect_configuration_layout()
143
+
144
+ if context.test == str(Util.TestValue.CHECK_CONFIG_LAYOUT):
145
+ raise Util.TestResultsReady(configuration_layout)
146
+
147
+ configuration_layout.dump()
148
+
149
+
150
+ def ensure_version_compatibility(context: CertoraContext) -> None:
151
+ if not (context.local or context.build_only or context.compilation_steps_only):
152
+ """
153
+ Before running the local type checker, we see if the current package version is compatible with
154
+ the latest. We check it before running the local type checker, because local type checking
155
+ errors could be simply a result of syntax introduced in the newest version.
156
+ The line below will raise an exception if the local version is incompatible.
157
+ """
158
+ validate_version_and_branch(context)
159
+
160
+
161
+ # --------------------------------------------------------------------------- #
162
+ # Verification helpers
163
+ # --------------------------------------------------------------------------- #
164
+
165
+ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optional[List[str]] = None,
166
+ compare_with_expected_file: bool = False) -> int:
167
+ """
168
+ Run the verifier locally and return its exit code (0 = success).
169
+ Args:
170
+ context: The CertoraContext object containing the configuration.
171
+ timings: A dictionary to store timing information.
172
+ additional_commands: A list of additional commands to pass to the verifier.
173
+ Returns:
174
+ An integer representing the exit code of the verifier run.
175
+ """
176
+ cmd: List[str] = Ctx.get_local_run_cmd(context)
177
+
178
+ if additional_commands:
179
+ cmd.extend(additional_commands)
180
+
181
+ print(f'Verifier run command:\n {" ".join(cmd)}')
182
+ if context.test == str(Util.TestValue.BEFORE_LOCAL_PROVER_CALL):
183
+ raise Util.TestResultsReady(' '.join(cmd))
184
+ rc = Util.run_jar_cmd(
185
+ cmd,
186
+ override_exit_code=compare_with_expected_file,
187
+ logger_topic="verification",
188
+ print_output=True,
189
+ )
190
+
191
+ if rc == 0:
192
+ Util.print_completion_message("Finished running verifier:")
193
+ print(f'\t{" ".join(cmd)}')
194
+ timings.setdefault("buildTime", 0.0) # ensure key exists
195
+ return 0
196
+
197
+ return 1
198
+
199
+
200
+ def run_remote(
201
+ context: CertoraContext,
202
+ args: List[str],
203
+ timings: Dict,
204
+ ) -> Tuple[int, Optional[CertoraRunResult]]:
205
+ """
206
+ Run verification in Certora Cloud.
207
+
208
+ Args:
209
+ context: The CertoraContext object containing the configuration.
210
+ args: The command line arguments to pass to the cloud verification.
211
+ timings: A dictionary to store timing information.
212
+ Returns:
213
+ A tuple containing the exit code (0 = success) and an optional CertoraRunResult object.
214
+ """
215
+ if context.compilation_steps_only:
216
+ return 0, CertoraRunResult(None, False, Util.get_certora_sources_dir(), None)
217
+
218
+ context.key = Cv.validate_certora_key()
219
+ cloud = CloudVerification(context, timings)
220
+
221
+ pretty_args = [f"'{a}'" if " " in a else a for a in args]
222
+
223
+ ok = cloud.cli_verify_and_report(" ".join(pretty_args), context.wait_for_results)
224
+
225
+ exit_code = 0 if ok else VIOLATIONS_EXIT_CODE
226
+ result: Optional[CertoraRunResult] = None
227
+ if cloud.statusUrl:
228
+ result = CertoraRunResult(
229
+ cloud.statusUrl,
230
+ False,
231
+ Util.get_certora_sources_dir(),
232
+ cloud.reportUrl,
233
+ )
234
+ return exit_code, result
235
+
236
+
237
+ def handle_exit(exit_code: int, return_value: Optional[CertoraRunResult]) -> Optional[CertoraRunResult]:
238
+ """
239
+ Handle the exit code of the verification run.
240
+ Args:
241
+ exit_code: The exit code of the verification run.
242
+ return_value: The CertoraRunResult object containing the results of the verification run.
243
+ Raises:
244
+ CertoraFoundViolations: If violations were found during the verification run.
245
+ Util.CertoraUserInputError: If there was an error with the user input.
246
+ Returns:
247
+ The CertoraRunResult object containing the results of the verification run.
248
+ """
249
+ if exit_code == VIOLATIONS_EXIT_CODE:
250
+ raise CertoraFoundViolations("violations were found", return_value)
251
+ if exit_code != 0:
252
+ raise Util.CertoraUserInputError(f"run_certora failed (code {exit_code})")
253
+ return return_value
254
+
255
+
256
+ # --------------------------------------------------------------------------- #
257
+ # Entry point decorator
258
+ # --------------------------------------------------------------------------- #
259
+
260
+ console = Console()
261
+
262
+ def catch_exits(fn: Callable[..., None]) -> Callable[..., NoReturn]:
263
+ """
264
+ Wrap any entry-point in a standard try/except + sys.exit logic.
265
+ The wrapped function should do its work and then return normally;
266
+ this decorator will exit(0) on success or exit(1/other) on failure.
267
+ """
268
+ @functools.wraps(fn)
269
+ def wrapper(*args, **kwargs) -> NoReturn: # type: ignore
270
+ try:
271
+ fn(*args, **kwargs)
272
+ sys.exit(0)
273
+
274
+ except KeyboardInterrupt:
275
+ console.print("[bold red]\nInterrupted by user")
276
+ sys.exit(1)
277
+
278
+ except Util.TestResultsReady:
279
+ print("reached checkpoint")
280
+ sys.exit(0)
281
+
282
+ except CertoraFoundViolations as e:
283
+ link = getattr(e.results, "rule_report_link", None)
284
+ if link:
285
+ print(f"report url: {link}")
286
+ console.print("[bold red]\nViolations were found\n")
287
+ sys.exit(1)
288
+
289
+ except Util.CertoraUserInputError as e:
290
+ if e.orig:
291
+ print(f"\n{str(e.orig).strip()}")
292
+ if e.more_info:
293
+ print(f"\n{e.more_info.strip()}")
294
+ console.print(f"[bold red]\n{e}\n")
295
+ sys.exit(1)
296
+
297
+ except Util.ExitException as e:
298
+ console.print(f"[bold red]{e}")
299
+ sys.exit(e.exit_code)
300
+
301
+ except Exception as e:
302
+ console.print(f"[bold red]{e}")
303
+ if getenv('CERTORA_DEV_MODE'):
304
+ import traceback
305
+ console.print(f"Traceback: {traceback.format_exc()}")
306
+ sys.exit(1)
307
+
308
+ return wrapper
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env python3
2
+ # The Certora Prover
3
+ # Copyright (C) 2025 Certora Ltd.
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, version 3 of the License.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+
18
+ import os
19
+ import sys
20
+ import argparse
21
+ import subprocess
22
+ from pathlib import Path
23
+
24
+ scripts_dir_path = Path(__file__).parent.resolve() # containing directory
25
+ sys.path.insert(0, str(scripts_dir_path))
26
+
27
+ from Shared import certoraUtils as Util
28
+ from pathlib import Path
29
+
30
+ FORMATTER_JAR = "ASTExtraction.jar"
31
+
32
+ scripts_dir_path = Path(__file__).parent.resolve()
33
+ sys.path.insert(0, str(scripts_dir_path))
34
+
35
+ def spec_file_type(spec_file: str) -> str:
36
+ if not os.path.isfile(spec_file):
37
+ raise argparse.ArgumentTypeError(f"File {spec_file} does not exist")
38
+ return spec_file
39
+
40
+ def run_formatter_from_jar(spec_file: str, overwrite: bool) -> None:
41
+ path_to_typechecker = Util.find_jar(FORMATTER_JAR)
42
+ cmd = ['java', '-jar', str(path_to_typechecker), 'format', '--file', spec_file]
43
+
44
+ result = subprocess.run(cmd, text=True, capture_output=True)
45
+
46
+ if result.returncode != 0:
47
+ raise Util.CertoraUserInputError(f"Error running formatter on {spec_file}: {result.stderr.strip() if result.stderr else ''}")
48
+
49
+ if overwrite:
50
+ with open(spec_file, 'w', encoding='utf-8') as f:
51
+ f.write(result.stdout)
52
+ else:
53
+ print(result.stdout, end='', file=sys.stdout)
54
+
55
+ def parse_args() -> tuple[str, bool]:
56
+ parser = argparse.ArgumentParser()
57
+ parser.add_argument('spec_file', type=spec_file_type, help='Path to the .spec file')
58
+ parser.add_argument('-w', '--overwrite', action='store_true', help='If set, output is written to the spec file instead of stdout')
59
+
60
+ args = parser.parse_args()
61
+ return args.spec_file, args.overwrite
62
+
63
+
64
+ def check_java_version() -> None:
65
+ if not (Util.is_java_installed(Util.get_java_version())):
66
+ raise Util.CertoraUserInputError(f"Java {Util.MIN_JAVA_VERSION} or higher is required to run the formatter. "
67
+ f"Please install Java and try again.")
68
+
69
+
70
+ def entry_point() -> None:
71
+ spec_file, overwrite = parse_args()
72
+ check_java_version()
73
+ run_formatter_from_jar(spec_file, overwrite)
74
+
75
+ if __name__ == '__main__':
76
+ entry_point()
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+ # The Certora Prover
3
+ # Copyright (C) 2025 Certora Ltd.
4
+ #
5
+ # This program is free software: you can redistribute it and/or modify
6
+ # it under the terms of the GNU General Public License as published by
7
+ # the Free Software Foundation, version 3 of the License.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
16
+
17
+
18
+ import sys
19
+ from pathlib import Path
20
+
21
+ scripts_dir_path = Path(__file__).parent.resolve() # containing directory
22
+ sys.path.insert(0, str(scripts_dir_path))
23
+
24
+ import CertoraProver.certoraApp as App
25
+ from certoraRun import run_certora
26
+ from Shared.proverCommon import CertoraRunResult, catch_exits
27
+
28
+ from typing import List, Optional
29
+
30
+
31
+ def run_concord(args: List[str]) -> Optional[CertoraRunResult]:
32
+ return run_certora(args, App.ConcordApp, prover_cmd=sys.argv[0])
33
+
34
+ @catch_exits
35
+ def entry_point() -> None:
36
+ run_concord(sys.argv[1:])
37
+
38
+ if __name__ == '__main__':
39
+ entry_point()
@@ -21,13 +21,14 @@ from pathlib import Path
21
21
  scripts_dir_path = Path(__file__).parent.resolve() # containing directory
22
22
  sys.path.insert(0, str(scripts_dir_path))
23
23
 
24
- import CertoraProver.certoraContextAttributes as Attrs
25
- from certoraRun import run_certora, CertoraRunResult
24
+ import CertoraProver.certoraApp as App
25
+ from Shared.proverCommon import CertoraRunResult
26
+ from certoraRun import run_certora
26
27
  from typing import List, Optional
27
28
 
28
29
 
29
30
  def run_evm_prover(args: List[str]) -> Optional[CertoraRunResult]:
30
- return run_certora(args, Attrs.EvmProverAttributes)
31
+ return run_certora(args, App.EvmApp)
31
32
 
32
33
  def entry_point() -> None:
33
34
  run_evm_prover(sys.argv[1:])