certora-cli-beta-mirror 7.31.0__py3-none-macosx_10_9_universal2.whl → 8.0.0__py3-none-macosx_10_9_universal2.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 (33) hide show
  1. certora_cli/CertoraProver/certoraBuild.py +19 -18
  2. certora_cli/CertoraProver/certoraBuildCacheManager.py +2 -0
  3. certora_cli/CertoraProver/certoraBuildRust.py +31 -20
  4. certora_cli/{Shared/rustProverCommon.py → CertoraProver/certoraBuildSui.py} +24 -18
  5. certora_cli/CertoraProver/certoraCloudIO.py +37 -8
  6. certora_cli/CertoraProver/certoraCollectRunMetadata.py +20 -5
  7. certora_cli/CertoraProver/certoraConfigIO.py +15 -13
  8. certora_cli/CertoraProver/certoraContext.py +33 -8
  9. certora_cli/CertoraProver/certoraContextAttributes.py +113 -195
  10. certora_cli/CertoraProver/certoraContextValidator.py +69 -40
  11. certora_cli/CertoraProver/certoraVerifyGenerator.py +9 -4
  12. certora_cli/CertoraProver/splitRules.py +2 -0
  13. certora_cli/CertoraProver/storageExtension.py +0 -35
  14. certora_cli/Mutate/mutateApp.py +16 -10
  15. certora_cli/Mutate/mutateAttributes.py +11 -0
  16. certora_cli/Shared/certoraAttrUtil.py +11 -5
  17. certora_cli/Shared/certoraUtils.py +29 -28
  18. certora_cli/Shared/certoraValidateFuncs.py +24 -12
  19. certora_cli/Shared/proverCommon.py +4 -2
  20. certora_cli/certoraCVLFormatter.py +76 -0
  21. certora_cli/certoraConcord.py +39 -0
  22. certora_cli/certoraRun.py +55 -95
  23. certora_cli/certoraSolanaProver.py +1 -1
  24. certora_cli/certoraSorobanProver.py +1 -1
  25. {certora_cli_beta_mirror-7.31.0.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/METADATA +3 -3
  26. {certora_cli_beta_mirror-7.31.0.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/RECORD +33 -30
  27. {certora_cli_beta_mirror-7.31.0.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/entry_points.txt +1 -0
  28. certora_jars/ASTExtraction.jar +0 -0
  29. certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
  30. certora_jars/Typechecker.jar +0 -0
  31. {certora_cli_beta_mirror-7.31.0.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/LICENSE +0 -0
  32. {certora_cli_beta_mirror-7.31.0.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/WHEEL +0 -0
  33. {certora_cli_beta_mirror-7.31.0.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/top_level.txt +0 -0
@@ -39,8 +39,6 @@ from CertoraProver.certoraSourceFinders import add_source_finders
39
39
  from CertoraProver.certoraVerifyGenerator import CertoraVerifyGenerator
40
40
  from CertoraProver.certoraContractFuncs import Func, InternalFunc, STATEMUT, SourceBytes
41
41
 
42
- from Shared.certoraUtils import is_relative_to
43
-
44
42
  scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
45
43
  sys.path.insert(0, str(scripts_dir_path))
46
44
  from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerCollector
@@ -454,8 +452,8 @@ def convert_pathname_to_posix(json_dict: Dict[str, Any], entry: str, smart_contr
454
452
  # protecting against long strings
455
453
  if len(json_dict_str) > 200:
456
454
  json_dict_str = json_dict_str[:200] + '...'
457
- fatal_error(compiler_logger, f"The path of the source file {file_path}"
458
- f"in the standard json file {json_dict_str} does not exist")
455
+ fatal_error(compiler_logger, f"The path of the source file {file_path} "
456
+ f"in the standard json file does not exist!\n{json_dict_str} ")
459
457
  json_dict[entry] = json_dict_posix_paths
460
458
 
461
459
 
@@ -1432,8 +1430,11 @@ class CertoraBuildGenerator:
1432
1430
  compiler_version = compiler_collector.compiler_version
1433
1431
  major, minor, patch = compiler_version
1434
1432
 
1435
- err_msg = "--finder_friendly_optimizer option supports solc versions 0.6.7 - 0.8.25 only, " \
1436
- f"got {major}.{minor}.{patch}"
1433
+ err_msg = \
1434
+ f"Unsupported solc version {major}.{minor}.{patch} for `solc_via_ir`. " \
1435
+ f"Supported versions: 0.6.7 – 0.8.25.\n" \
1436
+ f"Use `solc_via_ir_map` to disable `solc_via_ir` for specific files with older compiler versions."
1437
+
1437
1438
  yul_optimizer_steps = None
1438
1439
  if major != 0:
1439
1440
  raise Util.CertoraUserInputError(err_msg)
@@ -2690,7 +2691,7 @@ class CertoraBuildGenerator:
2690
2691
  self.handle_erc7201_annotations()
2691
2692
  self.handle_storage_extension_harnesses()
2692
2693
 
2693
- def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage]) -> NewStorageInfo:
2694
+ def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage], compiler_version: str) -> NewStorageInfo:
2694
2695
  """
2695
2696
  Given a file containing a contract with namespaced storage, extract the storage information
2696
2697
  corresponding to the namespaced types.
@@ -2715,7 +2716,9 @@ class CertoraBuildGenerator:
2715
2716
  # original file is accessed also in the actual code, and compiling it
2716
2717
  # directly can cause issues.
2717
2718
  tmp_file.write(f"import \"{original_file}\";\n\n")
2718
-
2719
+ rel_path = os.path.relpath(tmp_file.name, Path.cwd())
2720
+ if self.context.compiler_map:
2721
+ self.context.compiler_map.update({rel_path: compiler_version}, last=False)
2719
2722
  # Write the harness contract with dummy fields for each namespaced storage
2720
2723
  var_to_slot = storageExtension.write_harness_contract(tmp_file, harness_name, ns_storage)
2721
2724
  tmp_file.flush()
@@ -2728,13 +2731,6 @@ class CertoraBuildGenerator:
2728
2731
  build_dir / f"{Path(original_file).stem}_storage_extension.sol"
2729
2732
  )
2730
2733
 
2731
- # Add harness to compiler map
2732
- storageExtension.add_harness_to_compiler_map(
2733
- original_file,
2734
- tmp_file,
2735
- self.context
2736
- )
2737
-
2738
2734
  # normalize the path exactly the way collect_for_file expects it:
2739
2735
  abs_path = Util.abs_posix_path(tmp_file.name)
2740
2736
  self.context.file_to_contract[abs_path] = {harness_name}
@@ -2802,7 +2798,8 @@ class CertoraBuildGenerator:
2802
2798
  continue
2803
2799
 
2804
2800
  # Now that we have all the storage layout information, extract it once
2805
- slayouts[key] = self.extract_slayout(imported_file, ns_storage)
2801
+ slayouts[key] = self.extract_slayout(imported_file, ns_storage,
2802
+ get_relevant_compiler(Path(target_file), self.context))
2806
2803
 
2807
2804
  if self.context.test == str(Util.TestValue.STORAGE_EXTENSION_LAYOUT):
2808
2805
  raise Util.TestResultsReady(slayouts)
@@ -3583,7 +3580,11 @@ class CertoraBuildGenerator:
3583
3580
  return sources
3584
3581
 
3585
3582
  def __del__(self) -> None:
3586
- self.cleanup()
3583
+ try:
3584
+ self.cleanup()
3585
+ except ImportError:
3586
+ # Avoiding Python interpreter shutdown exceptions which are safe to ignore
3587
+ pass
3587
3588
 
3588
3589
 
3589
3590
  # make sure each source file exists and its path is in absolute format
@@ -3698,7 +3699,7 @@ def build_from_scratch(context: CertoraContext,
3698
3699
  # the contract files in SDCs are relative to .certora_sources. Which isn't good for us here.
3699
3700
  # Need to be relative to original paths
3700
3701
  for f in paths_set:
3701
- if is_relative_to(f, absolute_sources_dir):
3702
+ if f.is_relative_to(absolute_sources_dir):
3702
3703
  rel_f = f.relative_to(absolute_sources_dir)
3703
3704
  else:
3704
3705
  # may be an absolute path already outside .certora_sources, so we can keep it.
@@ -179,6 +179,8 @@ class CertoraBuildCacheManager:
179
179
  trg = trg_path_with_additional_included_files / post_autofinder_dir.name
180
180
  if post_autofinder_dir != trg:
181
181
  if post_autofinder_dir.is_dir():
182
+ if trg.exists():
183
+ shutil.rmtree(trg)
182
184
  Util.safe_copy_folder(post_autofinder_dir, trg, shutil.ignore_patterns())
183
185
  else:
184
186
  # highly unlikely .post_autofinder.[digit] will be a file and not a directory,
@@ -13,10 +13,20 @@
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
15
 
16
+ from __future__ import annotations
17
+
18
+ import sys
19
+ from pathlib import Path
20
+
21
+ scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
22
+ sys.path.insert(0, str(scripts_dir_path))
23
+
16
24
  import glob
17
25
  import shutil
26
+ import time
27
+ import logging
18
28
  from pathlib import Path
19
- from typing import Set
29
+ from typing import Set, Dict
20
30
 
21
31
  from CertoraProver.certoraBuild import build_source_tree
22
32
  from CertoraProver.certoraContextClass import CertoraContext
@@ -25,6 +35,25 @@ import CertoraProver.certoraContextAttributes as Attrs
25
35
  from Shared import certoraUtils as Util
26
36
 
27
37
 
38
+ log = logging.getLogger(__name__)
39
+
40
+
41
+ def build_rust_project(context: CertoraContext, timings: Dict) -> None:
42
+ """
43
+ Compile the Rust artefact and record elapsed time in *timings*.
44
+
45
+ Args:
46
+ context: The CertoraContext object containing the configuration.
47
+ timings: A dictionary to store timing information.
48
+ """
49
+ log.debug("Build Rust target")
50
+ start = time.perf_counter()
51
+ set_rust_build_directory(context)
52
+ timings["buildTime"] = round(time.perf_counter() - start, 4)
53
+ if context.test == str(Util.TestValue.AFTER_BUILD):
54
+ raise Util.TestResultsReady(context)
55
+
56
+
28
57
  def set_rust_build_directory(context: CertoraContext) -> None:
29
58
  if not context.files:
30
59
  build_rust_app(context)
@@ -41,6 +70,7 @@ def set_rust_build_directory(context: CertoraContext) -> None:
41
70
  except Exception as e:
42
71
  raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
43
72
 
73
+
44
74
  def build_rust_app(context: CertoraContext) -> None:
45
75
  assert not context.files, "build_rust_app: expecting files to be empty"
46
76
  if context.build_script:
@@ -62,24 +92,6 @@ def build_rust_app(context: CertoraContext) -> None:
62
92
 
63
93
  run_rust_build(context, build_command)
64
94
 
65
- def add_solana_files_from_prover_args(context: CertoraContext) -> None:
66
- if context.prover_args:
67
- inlining_file = False
68
- summaries_file = False
69
- for prover_arg in context.prover_args:
70
- for arg in prover_arg.split(' '):
71
- if inlining_file:
72
- context.solana_inlining = context.solana_inlining or [Path(arg)]
73
- inlining_file = False
74
- if summaries_file:
75
- context.solana_summaries = context.solana_summaries or [Path(arg)]
76
- summaries_file = False
77
-
78
- if arg == '-solanaInlining':
79
- inlining_file = True
80
- elif arg == '-solanaSummaries':
81
- summaries_file = True
82
-
83
95
 
84
96
  def add_solana_files_to_sources(context: CertoraContext, sources: Set[Path]) -> None:
85
97
  for attr in [Attrs.SolanaProverAttributes.SOLANA_INLINING,
@@ -126,7 +138,6 @@ def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path])
126
138
  if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
127
139
  sources.add(Path(context.conf_file).absolute())
128
140
 
129
- add_solana_files_from_prover_args(context)
130
141
  add_solana_files_to_sources(context, sources)
131
142
 
132
143
 
@@ -13,13 +13,6 @@
13
13
  # You should have received a copy of the GNU General Public License
14
14
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
15
 
16
- """
17
- Shared helpers for Rust-based targets (Solana & Soroban).
18
-
19
- Placing the build logic in one module removes
20
- duplication from the dedicated entry scripts.
21
- """
22
-
23
16
  from __future__ import annotations
24
17
 
25
18
  import sys
@@ -28,25 +21,21 @@ from pathlib import Path
28
21
  scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
29
22
  sys.path.insert(0, str(scripts_dir_path))
30
23
 
24
+ import shutil
31
25
  import time
32
26
  import logging
33
- from typing import Dict
34
-
35
- from Shared import certoraUtils as Util
27
+ from pathlib import Path
28
+ from typing import Set, Dict
36
29
 
30
+ from CertoraProver.certoraBuild import build_source_tree
37
31
  from CertoraProver.certoraContextClass import CertoraContext
38
-
39
- from CertoraProver.certoraBuildRust import set_rust_build_directory
32
+ from Shared import certoraUtils as Util
40
33
 
41
34
 
42
35
  log = logging.getLogger(__name__)
43
36
 
44
37
 
45
- # --------------------------------------------------------------------------- #
46
- # Build
47
- # --------------------------------------------------------------------------- #
48
-
49
- def build_rust_project(context: CertoraContext, timings: Dict) -> None:
38
+ def build_sui_project(context: CertoraContext, timings: Dict) -> None:
50
39
  """
51
40
  Compile the Rust artefact and record elapsed time in *timings*.
52
41
 
@@ -56,7 +45,24 @@ def build_rust_project(context: CertoraContext, timings: Dict) -> None:
56
45
  """
57
46
  log.debug("Build Rust target")
58
47
  start = time.perf_counter()
59
- set_rust_build_directory(context)
48
+ set_sui_build_directory(context)
60
49
  timings["buildTime"] = round(time.perf_counter() - start, 4)
61
50
  if context.test == str(Util.TestValue.AFTER_BUILD):
62
51
  raise Util.TestResultsReady(context)
52
+
53
+
54
+ def set_sui_build_directory(context: CertoraContext) -> None:
55
+ assert context.move_path, "build_sui_project: expecting move_path to link to a build directory"
56
+
57
+ shutil.copytree(context.move_path, Util.get_build_dir() / Path(context.move_path).name)
58
+
59
+ sources: Set[Path] = set()
60
+ if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
61
+ sources.add(Path(context.conf_file).absolute())
62
+
63
+ try:
64
+ # Create generators
65
+ build_source_tree(sources, context)
66
+
67
+ except Exception as e:
68
+ raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
@@ -43,7 +43,6 @@ from tqdm import tqdm
43
43
 
44
44
  import logging
45
45
 
46
-
47
46
  cloud_logger = logging.getLogger("cloud")
48
47
 
49
48
  MAX_FILE_SIZE = 25 * 1024 * 1024
@@ -74,11 +73,12 @@ class EcoEnum(Util.NoValEnum):
74
73
  EVM = Util.auto()
75
74
  SOROBAN = Util.auto()
76
75
  SOLANA = Util.auto()
76
+ SUI = Util.auto()
77
77
 
78
78
  class ProductEnum(Util.NoValEnum):
79
79
  PROVER = Util.auto()
80
80
  RANGER = Util.auto()
81
- SOPHY = Util.auto()
81
+ CONCORD = Util.auto()
82
82
 
83
83
 
84
84
  class TimeError(Exception):
@@ -587,7 +587,7 @@ class CloudVerification:
587
587
  """
588
588
 
589
589
  auth_data = {
590
- "process": self.context.process, "runName": self.runName,
590
+ "runName": self.runName,
591
591
  "run_source": self.context.run_source,
592
592
  "group_id": self.context.group_id,
593
593
  "branch": self.context.prover_version if self.context.prover_version else '',
@@ -624,6 +624,13 @@ class CloudVerification:
624
624
  rust_jar_settings.append(paths_in_source_dir(self.context.solana_inlining))
625
625
 
626
626
  auth_data["jarSettings"] = rust_jar_settings + jar_settings
627
+
628
+ elif Attrs.is_sui_app():
629
+ sui_jar_settings = ['-movePath', Path(self.context.move_path).name]
630
+ auth_data["jarSettings"] = sui_jar_settings + jar_settings
631
+
632
+ elif Attrs.is_concord_app():
633
+ auth_data["jarSettings"] = jar_settings + ["-equivalenceCheck", "true"]
627
634
  else:
628
635
  auth_data["jarSettings"] = jar_settings
629
636
 
@@ -645,13 +652,15 @@ class CloudVerification:
645
652
  auth_data["ecosystem"] = EcoEnum.SOLANA.name
646
653
  elif Attrs.is_soroban_app():
647
654
  auth_data["ecosystem"] = EcoEnum.SOROBAN.name
655
+ elif Attrs.is_sui_app():
656
+ auth_data["ecosystem"] = EcoEnum.SUI.name
648
657
  else:
649
658
  auth_data["ecosystem"] = EcoEnum.EVM.name
650
659
 
651
660
  if Attrs.is_ranger_app():
652
661
  auth_data["product"] = ProductEnum.RANGER.name
653
- elif Attrs.is_sophy_app():
654
- auth_data["product"] = ProductEnum.SOPHY.name
662
+ elif Attrs.is_concord_app():
663
+ auth_data["product"] = ProductEnum.CONCORD.name
655
664
  else:
656
665
  auth_data["product"] = ProductEnum.PROVER.name
657
666
 
@@ -686,12 +695,12 @@ class CloudVerification:
686
695
 
687
696
  def print_output_links(self) -> None:
688
697
  print(f"Manage your jobs at {self.get_domain()}")
689
- print(f"Follow your job and see verification results at {self.reportUrl}", flush=True)
698
+ print(f"Follow your job and see verification results at {self.url_print_format()}", flush=True)
690
699
 
691
700
  def print_verification_summary(self) -> None:
692
701
  report_exists = self.check_file_exists(params={"filename": "index.html", "certoraKey": self.context.key})
693
702
  if report_exists:
694
- print(f"Job is completed! View the results at {self.reportUrl}")
703
+ print(f"Job is completed! View the results at {self.url_print_format()}")
695
704
  print("Finished verification request", flush=True)
696
705
 
697
706
  def __send_verification_request(self, cl_args: str) -> bool:
@@ -774,6 +783,16 @@ class CloudVerification:
774
783
 
775
784
  result = compress_files(self.ZipFilePath, *files_list,
776
785
  short_output=Ctx.is_minimal_cli_output(self.context))
786
+ elif Attrs.is_sui_app():
787
+ files_list = [Util.get_certora_metadata_file(),
788
+ Util.get_configuration_layout_data_file(),
789
+ Util.get_build_dir() / Path(self.context.move_path).name]
790
+
791
+ if Util.get_certora_sources_dir().exists():
792
+ files_list.append(Util.get_certora_sources_dir())
793
+
794
+ result = compress_files(self.ZipFilePath, *files_list,
795
+ short_output=Ctx.is_minimal_cli_output(self.context))
777
796
 
778
797
  else:
779
798
  # Zip the log file first separately and again with the rest of the files, so it will not be decompressed
@@ -851,7 +870,7 @@ class CloudVerification:
851
870
  self.runName, self.reportUrl, self.msg, self.get_domain(), str(self.userId), self.anonymousKey)
852
871
 
853
872
  # Generate a json file for the VS Code extension with the relevant url
854
- self.vscode_extension_info_writer.add_field("verification_report_url", self.reportUrl)
873
+ self.vscode_extension_info_writer.add_field("verification_report_url", self.url_print_format())
855
874
  self.vscode_extension_info_writer.write_file()
856
875
 
857
876
  if not wait_for_results or wait_for_results == str(Vf.WaitForResultOptions.NONE):
@@ -1140,6 +1159,12 @@ class CloudVerification:
1140
1159
  Util.remove_file(self.ZipFilePath)
1141
1160
  Util.remove_file(self.logZipFilePath)
1142
1161
 
1162
+ def url_print_format(self) -> str:
1163
+ if self.reportUrl and self.context.url_visibility == str(Vf.UrlVisibilityOptions.PRIVATE):
1164
+ return self.privatize_url()
1165
+ else:
1166
+ return self.reportUrl
1167
+
1143
1168
  @lru_cache(maxsize=1, typed=False)
1144
1169
  def get_domain(self) -> str:
1145
1170
  """
@@ -1155,3 +1180,7 @@ class CloudVerification:
1155
1180
  domain = Util.SupportedServers.PRODUCTION.value
1156
1181
 
1157
1182
  return domain
1183
+
1184
+ @lru_cache(maxsize=1)
1185
+ def privatize_url(self) -> str:
1186
+ return self.reportUrl.split('?anonymousKey=')[0]
@@ -31,6 +31,7 @@ import CertoraProver.certoraContextAttributes as Attrs
31
31
 
32
32
  metadata_logger = logging.getLogger("metadata")
33
33
 
34
+
34
35
  def collect_args_with_jar_flags() -> List[str]:
35
36
  return_array = []
36
37
  for attr in Attrs.get_attribute_class().attribute_list():
@@ -70,10 +71,18 @@ class RunMetaData:
70
71
  dirty -- true iff the git repository has changes (git diff is not empty)
71
72
  main_spec -- the relative path to the main spec file that should be displayed by default at the web report
72
73
  conf_path -- the relative path form the cwd_relative to the configuration file
74
+ group_id -- optional identifier for grouping this run
75
+ java_version -- version of Java used during the run, if available
76
+ python_version -- version of Python running the process
77
+ certora_ci_client -- name of the CI client if available, derived from environment
78
+ timestamp -- UTC timestamp when the run metadata was generated
79
+ CLI_package_name -- name of the CLI package
80
+ CLI_version -- version of the CLI
81
+ jar_flag_info -- CLI attributes which are jarFlags
73
82
  """
74
83
  def __init__(self, raw_args: List[str], conf: Dict[str, Any], origin: str, revision: str,
75
84
  branch: str, cwd_relative: Path, dirty: bool, main_spec: Optional[str],
76
- conf_path: Optional[Path], group_id: Optional[str]):
85
+ conf_path: Optional[Path], group_id: Optional[str], java_version: str):
77
86
  self.raw_args = raw_args
78
87
  self.conf = conf
79
88
  self.origin = origin
@@ -85,6 +94,7 @@ class RunMetaData:
85
94
  self.conf_path = conf_path
86
95
  self.group_id = group_id
87
96
  self.python_version = ".".join(str(x) for x in sys.version_info[:3])
97
+ self.java_version = java_version
88
98
  self.certora_ci_client = Utils.get_certora_ci_name()
89
99
  self.timestamp = str(datetime.now(timezone.utc).timestamp())
90
100
  _, self.CLI_package_name, self.CLI_version = Utils.get_package_and_version()
@@ -105,6 +115,7 @@ class RunMetaData:
105
115
  f" conf_path: {self.conf_path}\n"
106
116
  f" group_id: {self.group_id}\n"
107
117
  f" python_version: {self.python_version}\n"
118
+ f" java_version: {self.java_version}\n"
108
119
  f" CertoraCI client: {self.certora_ci_client}\n"
109
120
  f" jar_flag_info: {self.jar_flag_info}\n"
110
121
  )
@@ -182,7 +193,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
182
193
  dirty=True,
183
194
  main_spec=None,
184
195
  conf_path=None,
185
- group_id=None)
196
+ group_id=None,
197
+ java_version=context.java_version)
186
198
 
187
199
  # collect information about current git snapshot
188
200
  cwd_abs = wd.absolute()
@@ -210,7 +222,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
210
222
  dirty=True,
211
223
  main_spec=get_main_spec(context),
212
224
  conf_path=conf_path,
213
- group_id=context.group_id)
225
+ group_id=context.group_id,
226
+ java_version=context.java_version)
214
227
 
215
228
  try:
216
229
  sha_out = subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=wd,
@@ -245,7 +258,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
245
258
  dirty=dirty,
246
259
  main_spec=get_main_spec(context),
247
260
  conf_path=conf_path,
248
- group_id=context.group_id)
261
+ group_id=context.group_id,
262
+ java_version=context.java_version)
249
263
 
250
264
  metadata_logger.debug(f' collected data:\n{str(data)}')
251
265
 
@@ -263,4 +277,5 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
263
277
  dirty=True,
264
278
  main_spec=get_main_spec(context),
265
279
  conf_path=conf_path,
266
- group_id=context.group_id)
280
+ group_id=context.group_id,
281
+ java_version=context.java_version)
@@ -80,7 +80,7 @@ def read_from_conf_file(context: CertoraContext) -> None:
80
80
  try:
81
81
  check_conf_content(context)
82
82
  except Util.CertoraUserInputError as e:
83
- raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {str(e)}", e) from None
83
+ raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {e}") from None
84
84
  context.conf_file = str(conf_file_path)
85
85
  except FileNotFoundError:
86
86
  raise Util.CertoraUserInputError(f"read_from_conf_file: {conf_file_path}: not found") from None
@@ -97,22 +97,24 @@ def handle_override_base_config(context: CertoraContext) -> None:
97
97
  """
98
98
 
99
99
  if context.override_base_config:
100
- with Path(context.override_base_config).open() as conf_file:
101
- try:
102
- override_base_config_attrs = json5.load(conf_file, allow_duplicate_keys=False)
100
+ try:
101
+ with Path(context.override_base_config).open() as conf_file:
102
+ override_base_config_attrs = json5.load(conf_file, allow_duplicate_keys=False,
103
+ object_pairs_hook=OrderedDict)
103
104
  context.conf_file_attr = {**override_base_config_attrs, **context.conf_file_attr}
104
105
 
105
106
  if 'override_base_config' in override_base_config_attrs:
106
107
  raise Util.CertoraUserInputError("base config cannot include 'override_base_config'")
107
- except Exception as e:
108
- raise Util.CertoraUserInputError(f"Cannot load base config: {context.override_base_config}\n{e}")
109
- for attr in override_base_config_attrs:
110
- if hasattr(context, attr):
111
- value = getattr(context, attr, False)
112
- if not value:
113
- setattr(context, attr, override_base_config_attrs.get(attr))
114
- else:
115
- raise Util.CertoraUserInputError(f"{attr} appears in the base conf file {context.override_base_config} but is not a known attribute.")
108
+ except Exception as e:
109
+ raise Util.CertoraUserInputError(f"Cannot load base config: {context.override_base_config}\n{e}")
110
+
111
+ for attr in override_base_config_attrs:
112
+ if hasattr(context, attr):
113
+ value = getattr(context, attr, False)
114
+ if not value:
115
+ setattr(context, attr, override_base_config_attrs.get(attr))
116
+ else:
117
+ raise Util.CertoraUserInputError(f"{attr} appears in the base conf file {context.override_base_config} but is not a known attribute.")
116
118
 
117
119
 
118
120
  def check_conf_content(context: CertoraContext) -> None:
@@ -122,7 +122,11 @@ def get_local_run_cmd(context: CertoraContext) -> List[str]:
122
122
  """
123
123
  run_args = []
124
124
 
125
- if Attrs.is_rust_app():
125
+ if Attrs.is_sui_app():
126
+ # For local runs, we want path to be relative to cwd instead of zip root.
127
+ move_rel_path = os.path.relpath(Path(context.move_path), os.getcwd())
128
+ run_args.extend(['-movePath', move_rel_path])
129
+ elif Attrs.is_rust_app():
126
130
  # For local runs, we want path to be relative to cwd instead of zip root.
127
131
  rust_rel_path = os.path.relpath(Path(context.files[0]), os.getcwd())
128
132
  run_args.append(rust_rel_path)
@@ -135,7 +139,8 @@ def get_local_run_cmd(context: CertoraContext) -> List[str]:
135
139
 
136
140
  if Attrs.is_evm_app() and context.cache is not None:
137
141
  run_args.extend(['-cache', context.cache])
138
-
142
+ if Attrs.is_concord_app():
143
+ run_args.extend(['-equivalenceCheck', 'true'])
139
144
  jar_args = collect_jar_args(context)
140
145
  run_args.extend(jar_args)
141
146
 
@@ -161,12 +166,17 @@ class ProverParser(AttrUtil.ContextAttributeParser):
161
166
 
162
167
  def format_help(self) -> str:
163
168
  console = Console()
164
- if Attrs.is_ranger_app():
169
+ if Attrs.is_concord_app():
170
+ console.print("\n\nConcord - Certora’s equivalence checker for smart contracts")
171
+ elif Attrs.is_ranger_app():
165
172
  console.print("\n\nRanger - Certora’s bounded model checker for smart contracts")
166
173
  else:
167
174
  console.print("\n\nThe Certora Prover - A formal verification tool for smart contracts")
168
175
  # Using sys.stdout.write() as print() would color some of the strings here
169
176
  sys.stdout.write(f"\n\nUsage: {sys.argv[0]} <Files> <Flags>\n\n")
177
+ if Attrs.is_concord_app():
178
+ sys.stdout.write("Concord supports only Solidity (.sol/.yul) and configuration (.conf) files.\n"
179
+ "Rust and Vyper contracts are not currently supported.\n\n")
170
180
  if Attrs.is_ranger_app():
171
181
  sys.stdout.write("Ranger supports only Solidity (.sol) and configuration (.conf) files.\n"
172
182
  "Rust and Vyper contracts are not currently supported.\n\n")
@@ -210,6 +220,16 @@ def __get_argparser() -> argparse.ArgumentParser:
210
220
  return parser
211
221
 
212
222
 
223
+ def set_apps_members(context: CertoraContext) -> None:
224
+ # in many cases accessing context is simpler than accessing Attrs
225
+ context.is_solana_app = Attrs.is_solana_app()
226
+ context.is_soroban_app = Attrs.is_soroban_app()
227
+ context.is_rust_app = Attrs.is_rust_app()
228
+ context.is_evm_app = Attrs.is_evm_app()
229
+ context.is_ranger_app = Attrs.is_ranger_app()
230
+ context.is_concord_app = Attrs.is_concord_app()
231
+
232
+
213
233
  def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
214
234
  """
215
235
  Compiles an argparse.Namespace from the given list of command line arguments.
@@ -244,6 +264,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
244
264
  args = parser.parse_args(args_list)
245
265
  context = CertoraContext(**vars(args))
246
266
  context.args_list = args_list
267
+ set_apps_members(context)
247
268
 
248
269
  __remove_parsing_whitespace(args_list)
249
270
  format_input(context)
@@ -251,7 +272,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
251
272
 
252
273
  if context.is_conf:
253
274
  read_from_conf_file(context)
254
-
275
+ context.process = None
255
276
  context.local = Util.is_local(context)
256
277
  context.is_tac = context.files and context.files[0].endswith('.tac')
257
278
  context.is_vyper = context.files and context.files[0].endswith('.vy')
@@ -260,8 +281,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
260
281
  Cv.check_mode_of_operation(context) # Here boolean run characteristics are set
261
282
 
262
283
  validator = Cv.CertoraContextValidator(context)
263
- if Attrs.is_evm_app():
264
- validator.handle_ranger_attrs()
284
+
265
285
  validator.validate()
266
286
  if Attrs.is_evm_app() or Attrs.is_rust_app():
267
287
  current_build_directory = Util.get_build_dir()
@@ -278,6 +298,8 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
278
298
  if Attrs.is_evm_app():
279
299
  validator.check_args_post_argparse()
280
300
  setup_cache(context) # Here context.cache, context.user_defined_cache are set
301
+ validator.handle_ranger_attrs()
302
+ validator.handle_concord_attrs()
281
303
  if Attrs.is_rust_app():
282
304
  validator.check_rust_args_post_argparse()
283
305
 
@@ -285,6 +307,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
285
307
  # Setup defaults (defaults are not recorded in conf file)
286
308
  context.expected_file = context.expected_file or "expected.json"
287
309
  context.run_source = context.run_source or Vf.RunSources.COMMAND.name.upper()
310
+ context.java_version = Util.get_java_version()
288
311
 
289
312
  context_logger.debug("parsed args successfully.")
290
313
  context_logger.debug(f"args= {context}")
@@ -403,7 +426,7 @@ def setup_cache(context: CertoraContext) -> None:
403
426
  # we have a user defined cache key if the user provided a cache key
404
427
  context.user_defined_cache = context.cache is not None
405
428
  if not context.disable_auto_cache_key_gen and not os.environ.get("CERTORA_DISABLE_AUTO_CACHE") is not None:
406
- if context.is_verify or context.is_assert or context.is_conf:
429
+ if context.is_verify or context.is_conf:
407
430
  # in local mode we don't want to create a cache key if not such is given
408
431
  if (context.cache is None) and (not context.local):
409
432
  optimistic_loop = context.optimistic_loop
@@ -524,6 +547,8 @@ def run_typechecker(typechecker_name: str, with_typechecking: bool, args: List[s
524
547
  if with_typechecking:
525
548
  cmd_str_list.append('-typeCheck')
526
549
 
550
+ context_logger.debug(f"typechecking cmd: {' '.join(cmd_str_list)}")
551
+
527
552
  exit_code = Util.run_jar_cmd(cmd_str_list, False,
528
553
  custom_error_message="Failed to run Certora Prover locally. Please check the errors "
529
554
  "below for problems in the specifications (.spec files) or the "
@@ -547,7 +572,7 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> No
547
572
  if context.disable_local_typechecking or Util.is_ci_or_git_action():
548
573
  return
549
574
  args = collect_jar_args(context)
550
- if Util.is_java_installed():
575
+ if Util.is_java_installed(context.java_version):
551
576
  run_typechecker("Typechecker.jar", with_typechecking, args)
552
577
  else:
553
578
  raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "