certora-cli-beta-mirror 7.30.1__py3-none-any.whl → 8.0.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 (36) hide show
  1. certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +11 -2
  2. certora_cli/CertoraProver/certoraBuild.py +68 -62
  3. certora_cli/CertoraProver/certoraBuildCacheManager.py +17 -16
  4. certora_cli/CertoraProver/certoraBuildRust.py +33 -21
  5. certora_cli/{Shared/rustProverCommon.py → CertoraProver/certoraBuildSui.py} +24 -18
  6. certora_cli/CertoraProver/certoraCloudIO.py +42 -33
  7. certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +62 -51
  8. certora_cli/CertoraProver/certoraCollectRunMetadata.py +20 -5
  9. certora_cli/CertoraProver/certoraConfigIO.py +17 -14
  10. certora_cli/CertoraProver/certoraContext.py +62 -10
  11. certora_cli/CertoraProver/certoraContextAttributes.py +132 -203
  12. certora_cli/CertoraProver/certoraContextValidator.py +108 -101
  13. certora_cli/CertoraProver/certoraParseBuildScript.py +4 -3
  14. certora_cli/CertoraProver/certoraVerifyGenerator.py +9 -4
  15. certora_cli/CertoraProver/splitRules.py +2 -0
  16. certora_cli/CertoraProver/storageExtension.py +0 -35
  17. certora_cli/Mutate/mutateApp.py +16 -10
  18. certora_cli/Mutate/mutateAttributes.py +11 -0
  19. certora_cli/Shared/certoraAttrUtil.py +11 -5
  20. certora_cli/Shared/certoraUtils.py +50 -47
  21. certora_cli/Shared/certoraValidateFuncs.py +29 -15
  22. certora_cli/Shared/proverCommon.py +6 -2
  23. certora_cli/certoraCVLFormatter.py +76 -0
  24. certora_cli/certoraConcord.py +39 -0
  25. certora_cli/certoraRun.py +53 -91
  26. certora_cli/certoraSolanaProver.py +1 -1
  27. certora_cli/certoraSorobanProver.py +1 -1
  28. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/METADATA +4 -3
  29. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/RECORD +36 -33
  30. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/entry_points.txt +1 -0
  31. certora_jars/ASTExtraction.jar +0 -0
  32. certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
  33. certora_jars/Typechecker.jar +0 -0
  34. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/LICENSE +0 -0
  35. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/WHEEL +0 -0
  36. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/top_level.txt +0 -0
@@ -14,7 +14,6 @@
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
15
 
16
16
  import csv
17
- import fnmatch
18
17
  import json
19
18
  import os
20
19
  import subprocess
@@ -97,6 +96,7 @@ EMV_JAR = Path("emv.jar")
97
96
  CERTORA_SOURCES = Path(".certora_sources")
98
97
  SOLANA_INLINING = "solana_inlining"
99
98
  SOLANA_SUMMARIES = "solana_summaries"
99
+ CARGO_TOML_FILE = "cargo.toml"
100
100
 
101
101
  ALPHA_PACKAGE_NAME = 'certora-cli-alpha'
102
102
  ALPHA_PACKAGE_MASTER_NAME = ALPHA_PACKAGE_NAME + '-master'
@@ -114,7 +114,8 @@ EVM_SOURCE_EXTENSIONS = (SOL_EXT, VY_EXT, YUL_EXT)
114
114
  EVM_EXTENSIONS = EVM_SOURCE_EXTENSIONS + ('.tac', '.json')
115
115
  SOLANA_EXEC_EXTENSION = '.so'
116
116
  SOROBAN_EXEC_EXTENSION = '.wasm'
117
- VALID_FILE_EXTENSIONS = ['.conf'] + list(EVM_EXTENSIONS) + [SOLANA_EXEC_EXTENSION, SOROBAN_EXEC_EXTENSION]
117
+ VALID_EVM_EXTENSIONS = list(EVM_EXTENSIONS) + ['.conf']
118
+ VALID_FILE_EXTENSIONS = VALID_EVM_EXTENSIONS + [SOLANA_EXEC_EXTENSION, SOROBAN_EXEC_EXTENSION]
118
119
  # Type alias definition, not a variable
119
120
  CompilerVersion = Tuple[int, int, int]
120
121
  MAP_SUFFIX = '_map'
@@ -368,11 +369,8 @@ def remove_file(file_path: Union[str, Path]) -> None: # TODO - accept only Path
368
369
  except OSError:
369
370
  pass
370
371
  else:
371
- try:
372
- # When we upgrade to Python 3.8, we can use unlink(missing_ok=True) and remove the try/except clauses
373
- file_path.unlink()
374
- except FileNotFoundError:
375
- pass
372
+ file_path.unlink(missing_ok=True)
373
+
376
374
 
377
375
  def abs_norm_path(file_path: Union[str, Path]) -> Path:
378
376
  """
@@ -959,18 +957,6 @@ def flatten_set_list(set_list: List[Set[Any]]) -> List[Any]:
959
957
  return list(ret_set)
960
958
 
961
959
 
962
- def is_relative_to(path1: Path, path2: Path) -> bool:
963
- """certora-cli currently requires python3.8 and it's the last version without support for is_relative_to.
964
- Shamelessly copying.
965
- """
966
- # return path1.is_relative_to(path2)
967
- try:
968
- path1.relative_to(path2)
969
- return True
970
- except ValueError:
971
- return False
972
-
973
-
974
960
  def find_jar(jar_name: str) -> Path:
975
961
  # if we are a dev running certoraRun.py (local version), we want to get the local jar
976
962
  # if we are a dev running an installed version of certoraRun, we want to get the installed jar
@@ -982,7 +968,7 @@ def find_jar(jar_name: str) -> Path:
982
968
 
983
969
  if certora_home != "":
984
970
  local_certora_path = Path(certora_home) / CERTORA_JARS / jar_name
985
- if is_relative_to(Path(__file__), Path(certora_home)) and local_certora_path.is_file():
971
+ if Path(__file__).is_relative_to(Path(certora_home)) and local_certora_path.is_file():
986
972
  return local_certora_path
987
973
 
988
974
  return get_package_resource(CERTORA_JARS / jar_name)
@@ -997,31 +983,46 @@ def get_package_resource(resource: Path) -> Path:
997
983
  return Path(__file__).parents[2] / resource
998
984
 
999
985
 
1000
- def is_java_installed() -> bool:
986
+ def get_java_version() -> str:
1001
987
  """
1002
- Check that java is installed and with a version that is suitable for running certora jars
1003
- @return True on success
988
+ Retrieve installed java version
989
+ @return installed java version on success or empty string
1004
990
  """
1005
991
  # Check if java exists on the machine
1006
992
  java = which("java")
1007
993
  if java is None:
994
+ return ''
995
+
996
+ try:
997
+ java_version_str = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT).decode()
998
+ java_version = re.search(r'version \"([\d\.]+)\"', java_version_str).groups()[0] # type: ignore[union-attr]
999
+
1000
+ return java_version
1001
+ except (subprocess.CalledProcessError, AttributeError):
1002
+ typecheck_logger.debug("Couldn't find the installed Java version.")
1003
+ return ''
1004
+
1005
+
1006
+ def is_java_installed(java_version: str) -> bool:
1007
+ """
1008
+ Check that java is installed and with a version that is suitable for running certora jars
1009
+ @return True on success
1010
+ """
1011
+ if not java_version:
1008
1012
  typecheck_logger.warning(
1009
1013
  f"`java` is not installed. Installing Java version {MIN_JAVA_VERSION} or later will enable faster "
1010
1014
  f"CVL specification syntax checking before uploading to the cloud.")
1011
1015
  return False
1012
1016
 
1013
- try:
1014
- java_version_str = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT).decode()
1015
- major_java_version = re.search(r'version \"(\d+).*', java_version_str).groups()[0] # type: ignore[union-attr]
1017
+ else:
1018
+ major_java_version = java_version.split('.')[0]
1016
1019
  if int(major_java_version) < MIN_JAVA_VERSION:
1017
1020
  typecheck_logger.warning("Installed Java version is too old to check CVL specification files locally. "
1018
1021
  f" Java version should be at least {MIN_JAVA_VERSION} to allow local java-based "
1019
1022
  "type checking")
1020
1023
 
1021
1024
  return False
1022
- except (subprocess.CalledProcessError, AttributeError):
1023
- typecheck_logger.warning("Couldn't find the installed Java version.")
1024
- return False
1025
+
1025
1026
  return True
1026
1027
 
1027
1028
 
@@ -1171,26 +1172,9 @@ class Singleton(type):
1171
1172
  cls.__instancesinstances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
1172
1173
  return cls.__instancesinstances[cls]
1173
1174
 
1174
-
1175
1175
  class AbstractAndSingleton(Singleton, ABCMeta):
1176
1176
  pass
1177
1177
 
1178
-
1179
- def match_path_to_mapping_key(path: Path, m: Dict[str, str]) -> Optional[str]:
1180
- """
1181
- Matches the path to the best match in the dictionary's keys.
1182
- For example, given an absolute path `/Users/JohnDoe/Path/ToSolc/a.sol`, if the map contains
1183
- `b/a.sol` and `ToSolc/a.sol`, it will match on `ToSolc/a.sol`.
1184
- @param path: the path to match against
1185
- @param m: the map whose keys we're searching
1186
- @return: the value from the map that best matches the path, None if not found.
1187
- """
1188
- for k, v in m.items():
1189
- if fnmatch.fnmatch(str(path), k):
1190
- return v
1191
- return None
1192
-
1193
-
1194
1178
  def find_in(dir_path: Path, file_to_find: Path) -> Optional[Path]:
1195
1179
  """
1196
1180
  Given a directory dir_path and a file we wish to find within that directory,
@@ -1262,7 +1246,7 @@ class TestValue(NoValEnum):
1262
1246
  determines the chekpoint where the execution will halt. The exception TestResultsReady will be thrown. The value
1263
1247
  will also determine what object will be attached to the exception for inspection by the caller
1264
1248
  """
1265
- LOCAL_JAR = auto()
1249
+ BEFORE_LOCAL_PROVER_CALL = auto()
1266
1250
  CHECK_ARGS = auto()
1267
1251
  AFTER_BUILD = auto()
1268
1252
  CHECK_SOLC_OPTIONS = auto()
@@ -1452,3 +1436,22 @@ def eq_by(f: Callable[[T, T], bool], a: Sequence[T], b: Sequence[T]) -> bool:
1452
1436
  check if Sequences a and b are equal according to function f.
1453
1437
  """
1454
1438
  return len(a) == len(b) and all(map(f, a, b))
1439
+
1440
+ def find_nearest_cargo_toml() -> Optional[Path]:
1441
+ current = Path.cwd()
1442
+ for parent in [current] + list(current.parents):
1443
+ candidate = parent / CARGO_TOML_FILE
1444
+ if candidate.is_file():
1445
+ return candidate
1446
+ return None
1447
+
1448
+ def file_in_source_tree(file_path: Path) -> bool:
1449
+ # if the file is under .certora_source, return True
1450
+ file_path = Path(file_path).absolute()
1451
+ parent_dir = get_certora_sources_dir().absolute()
1452
+
1453
+ try:
1454
+ file_path.relative_to(parent_dir)
1455
+ return True
1456
+ except ValueError:
1457
+ return False
@@ -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,13 +550,6 @@ 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):
548
- raise Util.CertoraUserInputError(
549
- f"Contract name {contract} can include only alphanumeric characters, dollar signs or underscores")
550
- return contract
551
-
552
-
553
553
  def validate_equivalence_contracts(equiv_string: str) -> str:
554
554
  if not re.match(f'^{Util.SOLIDITY_ID_SUBSTRING_RE}={Util.SOLIDITY_ID_SUBSTRING_RE}$', equiv_string):
555
555
  raise Util.CertoraUserInputError(
@@ -684,7 +684,7 @@ def validate_solc_via_ir_map(args: Dict[str, bool]) -> None:
684
684
  if first:
685
685
  validation_logger.warning("all via_ir values are set to True '--solc_via_ir' can be used instead")
686
686
  else:
687
- validation_logger.warning("all via_ir values are set to False, this flag/attribute can be omitted")
687
+ validation_logger.warning("all via_ir values are set to False, this flag/attribute can be omitted")
688
688
 
689
689
  def validate_solc_evm_version_map(args: Dict[str, str]) -> None:
690
690
  if not isinstance(args, dict):
@@ -758,6 +758,16 @@ def validate_git_hash(git_hash: str) -> str:
758
758
  raise Util.CertoraUserInputError("Git hash must consist of between 1 and 40 characters")
759
759
  return git_hash
760
760
 
761
+ def validate_check_method_flag(method: str) -> str:
762
+ if '.' in method:
763
+ raise Util.CertoraUserInputError(f"Malformed `check_mathod` argument '{method}': checked method cannot contain a dot. Use only the method name without the contract prefix."
764
+ "the contract part is not allowed in `--check_method`")
765
+ if ' ' in method:
766
+ raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--check_method`: remove all whitespace")
767
+
768
+ if not __validate_matching_parens(method):
769
+ raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--check_method`: unmatched parenthesis")
770
+ return method
761
771
 
762
772
  def validate_method_flag(method: str) -> str:
763
773
  contract_and_method = method.split('.')
@@ -938,7 +948,7 @@ def validate_on_off(value: str) -> str:
938
948
  return __validate_enum_value(value, OnOffValue)
939
949
 
940
950
 
941
- def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict[str, Union[str, bool]]:
951
+ def parse_ordered_dict(conf_key: str, input_string: str, value_type: Type = str) -> OrderedDict[str, Union[str, bool]]:
942
952
  """
943
953
  convert CLI flag value string of the form <key>=<value>,<key>=<value>,.. to a Dict.
944
954
  Keys with different values raise an exception
@@ -971,7 +981,7 @@ def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict
971
981
  raise argparse.ArgumentTypeError(f"{conf_key} argument {input_string} is of wrong format. Must be of format:"
972
982
  f"<key>=<value>[,..]")
973
983
 
974
- return_dict = {} # type: Dict[str, Union[str, bool]]
984
+ return_dict = OrderedDict() # type: OrderedDict[str, Union[str, bool]]
975
985
 
976
986
  for match in input_string.split(','):
977
987
  key, value = match.split('=')
@@ -993,6 +1003,10 @@ def validate_wait_for_results(value: str) -> str:
993
1003
  return __validate_enum_value(value, WaitForResultOptions)
994
1004
 
995
1005
 
1006
+ def validate_url_visibility(value: str) -> str:
1007
+ return __validate_enum_value(value, UrlVisibilityOptions)
1008
+
1009
+
996
1010
  def validate_json5_file(file: str) -> str:
997
1011
  file_exists_and_readable(file)
998
1012
 
@@ -156,11 +156,13 @@ def ensure_version_compatibility(context: CertoraContext) -> None:
156
156
  """
157
157
  validate_version_and_branch(context)
158
158
 
159
+
159
160
  # --------------------------------------------------------------------------- #
160
161
  # Verification helpers
161
162
  # --------------------------------------------------------------------------- #
162
163
 
163
- def run_local(context: CertoraContext, timings: Dict, additional_commands: Optional[List[str]] = None, compare_with_expected_file: bool = False) -> int:
164
+ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optional[List[str]] = None,
165
+ compare_with_expected_file: bool = False) -> int:
164
166
  """
165
167
  Run the verifier locally and return its exit code (0 = success).
166
168
  Args:
@@ -176,6 +178,8 @@ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optio
176
178
  cmd.extend(additional_commands)
177
179
 
178
180
  print(f'Verifier run command:\n {" ".join(cmd)}')
181
+ if context.test == str(Util.TestValue.BEFORE_LOCAL_PROVER_CALL):
182
+ raise Util.TestResultsReady(' '.join(cmd))
179
183
  rc = Util.run_jar_cmd(
180
184
  cmd,
181
185
  override_exit_code=compare_with_expected_file,
@@ -185,7 +189,7 @@ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optio
185
189
 
186
190
  if rc == 0:
187
191
  Util.print_completion_message("Finished running verifier:")
188
- print("\t%s", " ".join(cmd))
192
+ print(f'\t{" ".join(cmd)}')
189
193
  timings.setdefault("buildTime", 0.0) # ensure key exists
190
194
  return 0
191
195
 
@@ -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.certoraContextAttributes as Attrs
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, Attrs.ConcordAttributes, 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()
certora_cli/certoraRun.py CHANGED
@@ -24,10 +24,9 @@ scripts_dir_path = Path(__file__).parent.resolve() # containing directory
24
24
  sys.path.insert(0, str(scripts_dir_path))
25
25
  from Shared import certoraUtils as Util
26
26
 
27
- from CertoraProver.certoraCloudIO import CloudVerification, validate_version_and_branch
27
+ from CertoraProver.certoraCloudIO import CloudVerification
28
28
 
29
29
  from CertoraProver.certoraBuild import build
30
- from CertoraProver.certoraBuildRust import set_rust_build_directory
31
30
  import CertoraProver.certoraContext as Ctx
32
31
 
33
32
  from CertoraProver import certoraContextValidator as Cv
@@ -52,7 +51,7 @@ BUILD_SCRIPT_PATH = Path("CertoraProver/certoraBuild.py")
52
51
  # Also serves as the default logger for errors originating from unexpected places.
53
52
  run_logger = logging.getLogger("run")
54
53
 
55
- def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]] = None,
54
+ def run_certora(args: List[str], attrs_class: Type[AttrUtil.Attributes] = Attrs.EvmProverAttributes,
56
55
  prover_cmd: Optional[str] = None) -> Optional[CertoraRunResult]:
57
56
  """
58
57
  The main function that is responsible for the general flow of the script.
@@ -61,9 +60,6 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
61
60
  2. Run the necessary steps (type checking/ build/ cloud verification/ local verification)
62
61
 
63
62
  """
64
- if not attrs_class:
65
- attrs_class = Attrs.detect_application_class(args)
66
-
67
63
  context, logging_manager = build_context(args, attrs_class)
68
64
 
69
65
  if prover_cmd:
@@ -78,7 +74,7 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
78
74
  # Collect and dump configuration layout
79
75
  collect_and_dump_config_layout(context)
80
76
 
81
- if Attrs.is_evm_app() and context.split_rules:
77
+ if context.split_rules:
82
78
  context.build_only = True
83
79
  build(context)
84
80
  context.build_only = False
@@ -90,94 +86,60 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
90
86
  else:
91
87
  raise Util.ExitException("Split rules failed", exit_code)
92
88
 
93
- if Attrs.is_rust_app():
94
- set_rust_build_directory(context)
89
+ # Version validation
90
+ ensure_version_compatibility(context)
95
91
 
96
- if context.local:
97
- check_cmd = Ctx.get_local_run_cmd(context)
98
- check_cmd_string = " ".join(check_cmd)
99
- print(f"Verifier run command:\n {check_cmd_string}", flush=True)
92
+ # When a TAC file is provided, no build arguments will be processed
93
+ if not context.is_tac:
94
+ run_logger.debug(f"There is no TAC file. Going to script {BUILD_SCRIPT_PATH} to main_with_args()")
95
+ build_start = time.perf_counter()
100
96
 
101
- compare_with_tool_output = False
102
- run_result = Util.run_jar_cmd(check_cmd, compare_with_tool_output, logger_topic="verification",
103
- print_output=True)
104
- # For solana and wasm, we don't check types so build time is zero.
105
- timings["buildTime"] = 0.0
106
- if run_result != 0:
107
- exit_code = 1
108
- else:
109
- Util.print_completion_message("Finished running verifier:")
110
- print(f"\t{check_cmd_string}")
111
- else:
112
- validate_version_and_branch(context)
113
- context.key = Cv.validate_certora_key()
114
- cloud_verifier = CloudVerification(context, timings)
115
- # Wrap strings with space with ' so it can be copied and pasted to shell
116
- pretty_args = [f"'{arg}'" if ' ' in arg else arg for arg in args]
117
- cl_args = ' '.join(pretty_args)
118
- logging_manager.remove_debug_logger()
119
- result = cloud_verifier.cli_verify_and_report(cl_args, context.wait_for_results)
120
- if cloud_verifier.statusUrl:
121
- return_value = CertoraRunResult(cloud_verifier.statusUrl, False,
122
- Util.get_certora_sources_dir(), cloud_verifier.reportUrl)
97
+ # If we are not in CI, we also check the spec for Syntax errors.
98
+ build(context)
99
+ build_end = time.perf_counter()
100
+
101
+ timings["buildTime"] = round(build_end - build_start, 4)
102
+ if context.test == str(Util.TestValue.AFTER_BUILD):
103
+ raise Util.TestResultsReady(context)
104
+
105
+ if context.build_only:
106
+ return return_value
107
+
108
+ # either we skipped building (TAC MODE) or build succeeded
109
+ if context.local:
110
+ compare_with_expected_file = Path(context.expected_file).exists()
111
+
112
+ run_result = run_local(context, timings, compare_with_expected_file=compare_with_expected_file)
113
+ emv_dir = latest_emv_dir()
114
+ return_value = CertoraRunResult(str(emv_dir) if emv_dir else None, True,
115
+ Util.get_certora_sources_dir(), None)
116
+ if run_result != 0:
117
+ exit_code = run_result
118
+ elif compare_with_expected_file:
119
+ print("Comparing tool output to the expected output:")
120
+ output_path = context.tool_output or (
121
+ 'tmpOutput.json' if emv_dir is None else
122
+ str(emv_dir / 'Reports/output.json')
123
+ )
124
+ result = Util.check_results_from_file(output_path, context.expected_file)
123
125
  if not result:
124
126
  exit_code = 1
125
- else:
126
-
127
- # Version validation
128
- ensure_version_compatibility(context)
129
-
130
- # When a TAC file is provided, no build arguments will be processed
131
- if not context.is_tac:
132
- run_logger.debug(f"There is no TAC file. Going to script {BUILD_SCRIPT_PATH} to main_with_args()")
133
- build_start = time.perf_counter()
134
-
135
- # If we are not in CI, we also check the spec for Syntax errors.
136
- build(context)
137
- build_end = time.perf_counter()
138
-
139
- timings["buildTime"] = round(build_end - build_start, 4)
140
- if context.test == str(Util.TestValue.AFTER_BUILD):
141
- raise Util.TestResultsReady(context)
142
-
143
- if context.build_only:
144
- return return_value
145
-
146
- # either we skipped building (TAC MODE) or build succeeded
147
- if context.local:
148
- compare_with_expected_file = Path(context.expected_file).exists()
149
-
150
- run_result = run_local(context, timings, compare_with_expected_file=compare_with_expected_file)
151
- emv_dir = latest_emv_dir()
152
- return_value = CertoraRunResult(str(emv_dir) if emv_dir else None, True,
153
- Util.get_certora_sources_dir(), None)
154
- if run_result != 0:
155
- exit_code = run_result
156
- elif compare_with_expected_file:
157
- print("Comparing tool output to the expected output:")
158
- output_path = context.tool_output or (
159
- 'tmpOutput.json' if emv_dir is None else
160
- str(emv_dir / 'Reports/output.json')
161
- )
162
- result = Util.check_results_from_file(output_path, context.expected_file)
163
- if not result:
164
- exit_code = 1
165
- else: # Remote run
166
- # Syntax checking and typechecking
167
- if Cv.mode_has_spec_file(context):
168
- if context.disable_local_typechecking:
169
- run_logger.warning(
170
- "Local checks of CVL specification files disabled. It is recommended to enable "
171
- "the checks.")
172
- else:
173
- typechecking_start = time.perf_counter()
174
- Ctx.run_local_spec_check(True, context)
175
- typechecking_end = time.perf_counter()
176
- timings['typecheckingTime'] = round(typechecking_end - typechecking_start, 4)
177
-
178
- # Remove debug logger and run remote verification
179
- logging_manager.remove_debug_logger()
180
- exit_code, return_value = run_remote(context, args, timings)
127
+ else: # Remote run
128
+ # Syntax checking and typechecking
129
+ if Cv.mode_has_spec_file(context):
130
+ if context.disable_local_typechecking:
131
+ run_logger.warning(
132
+ "Local checks of CVL specification files disabled. It is recommended to enable "
133
+ "the checks.")
134
+ else:
135
+ typechecking_start = time.perf_counter()
136
+ Ctx.run_local_spec_check(True, context)
137
+ typechecking_end = time.perf_counter()
138
+ timings['typecheckingTime'] = round(typechecking_end - typechecking_start, 4)
139
+
140
+ # Remove debug logger and run remote verification
141
+ logging_manager.remove_debug_logger()
142
+ exit_code, return_value = run_remote(context, args, timings)
181
143
 
182
144
  # Handle exit codes and return
183
145
  return handle_exit(exit_code, return_value)
@@ -25,7 +25,7 @@ sys.path.insert(0, str(scripts_dir_path))
25
25
 
26
26
 
27
27
  import CertoraProver.certoraContextAttributes as Attrs
28
- from Shared.rustProverCommon import build_rust_project
28
+ from CertoraProver.certoraBuildRust import build_rust_project
29
29
  from Shared.proverCommon import (
30
30
  build_context,
31
31
  collect_and_dump_metadata,
@@ -26,7 +26,7 @@ from typing import List, Optional, Dict
26
26
 
27
27
  import CertoraProver.certoraContextAttributes as Attrs
28
28
 
29
- from Shared.rustProverCommon import build_rust_project
29
+ from CertoraProver.certoraBuildRust import build_rust_project
30
30
  from Shared.proverCommon import (
31
31
  build_context,
32
32
  collect_and_dump_metadata,