certora-cli-beta-mirror 7.30.1__py3-none-any.whl → 7.31.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 (23) hide show
  1. certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +11 -2
  2. certora_cli/CertoraProver/certoraBuild.py +50 -45
  3. certora_cli/CertoraProver/certoraBuildCacheManager.py +15 -16
  4. certora_cli/CertoraProver/certoraBuildRust.py +2 -1
  5. certora_cli/CertoraProver/certoraCloudIO.py +7 -27
  6. certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +62 -51
  7. certora_cli/CertoraProver/certoraConfigIO.py +2 -1
  8. certora_cli/CertoraProver/certoraContext.py +29 -2
  9. certora_cli/CertoraProver/certoraContextAttributes.py +20 -9
  10. certora_cli/CertoraProver/certoraContextValidator.py +39 -61
  11. certora_cli/CertoraProver/certoraParseBuildScript.py +4 -3
  12. certora_cli/Shared/certoraUtils.py +21 -19
  13. certora_cli/Shared/certoraValidateFuncs.py +5 -3
  14. certora_cli/Shared/proverCommon.py +2 -0
  15. certora_cli/certoraRun.py +2 -0
  16. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-7.31.0.dist-info}/METADATA +3 -2
  17. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-7.31.0.dist-info}/RECORD +23 -23
  18. certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
  19. certora_jars/Typechecker.jar +0 -0
  20. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-7.31.0.dist-info}/LICENSE +0 -0
  21. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-7.31.0.dist-info}/WHEEL +0 -0
  22. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-7.31.0.dist-info}/entry_points.txt +0 -0
  23. {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-7.31.0.dist-info}/top_level.txt +0 -0
@@ -25,8 +25,9 @@ from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerColle
25
25
  from CertoraProver.Compiler.CompilerCollectorSol import CompilerCollectorSol, CompilerLangSol
26
26
  from CertoraProver.Compiler.CompilerCollectorYul import CompilerLangYul, CompilerCollectorYul
27
27
  from CertoraProver.Compiler.CompilerCollectorVy import CompilerCollectorVy, CompilerLangVy
28
- from Shared.certoraUtils import match_path_to_mapping_key, remove_file, get_certora_config_dir
28
+ from Shared.certoraUtils import remove_file, get_certora_config_dir
29
29
  from CertoraProver.certoraContextClass import CertoraContext
30
+ import CertoraProver.certoraContext as Ctx
30
31
 
31
32
  # logger for running the Solidity compiler and reporting any errors it emits
32
33
  compiler_logger = logging.getLogger("compiler")
@@ -42,8 +43,16 @@ def get_relevant_compiler(contract_file_path: Path, context: CertoraContext) ->
42
43
 
43
44
  if contract_file_path.is_absolute():
44
45
  contract_file_path = Path(os.path.relpath(contract_file_path, Path.cwd()))
46
+
47
+ # If the contract file is in the sources directory, we want to use the relative path from the "cwd" in the source tree.
48
+ if Util.get_certora_sources_dir() in contract_file_path.parents:
49
+ cwd_in_source = Util.get_certora_sources_dir() / context.cwd_rel_in_sources
50
+ contract_file_path = Path(os.path.relpath(contract_file_path, cwd_in_source))
51
+
45
52
  if context.compiler_map:
46
- match = match_path_to_mapping_key(contract_file_path, context.compiler_map)
53
+ match = Ctx.get_map_attribute_value(context, contract_file_path, 'compiler')
54
+ assert isinstance(match, str), (f"In the attribute compiler_map, {contract_file_path} expected to be matched "
55
+ f"to a string, {match} is of type {type(match)} not a string")
47
56
  if match:
48
57
  return match
49
58
  else:
@@ -450,8 +450,12 @@ def convert_pathname_to_posix(json_dict: Dict[str, Any], entry: str, smart_contr
450
450
  if path_obj.is_file():
451
451
  json_dict_posix_paths[path_obj.as_posix()] = json_dict[entry][file_path]
452
452
  else:
453
+ json_dict_str = str(json_dict)
454
+ # protecting against long strings
455
+ if len(json_dict_str) > 200:
456
+ json_dict_str = json_dict_str[:200] + '...'
453
457
  fatal_error(compiler_logger, f"The path of the source file {file_path}"
454
- f"in the standard json file {json_dict} does not exist")
458
+ f"in the standard json file {json_dict_str} does not exist")
455
459
  json_dict[entry] = json_dict_posix_paths
456
460
 
457
461
 
@@ -1361,46 +1365,48 @@ class CertoraBuildGenerator:
1361
1365
 
1362
1366
  return bytecode
1363
1367
 
1364
- def _handle_via_ir(self, contract_file_path: str, settings_dict: Dict[str, Any]) -> None:
1365
- if not self.context.solc_via_ir_map and not self.context.solc_via_ir:
1366
- return
1367
- if self.context.solc_via_ir_map:
1368
- match = Util.match_path_to_mapping_key(Path(contract_file_path), self.context.solc_via_ir_map)
1369
- if match:
1370
- settings_dict["viaIR"] = match
1371
- elif self.context.solc_via_ir:
1368
+ def get_solc_via_ir_value(self, contract_file_path: Path) -> bool:
1369
+ match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_via_ir')
1370
+ assert isinstance(match, (bool, type(None))), f"Expected solc_via_ir to be bool or None, got {type(match)}"
1371
+ return bool(match)
1372
+
1373
+ def get_solc_evm_version_value(self, contract_file_path: Path) -> Optional[str]:
1374
+ match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_evm_version')
1375
+ assert isinstance(match, (str, type(None))), f"Expected solc_evm_version to be string or None, got {type(match)}"
1376
+ return match
1377
+
1378
+ def get_solc_optimize_value(self, contract_file_path: Path) -> Optional[str]:
1379
+ match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_optimize')
1380
+ assert isinstance(match, (str, type(None))), f"Expected solc_optimize to be string or None, got {type(match)}"
1381
+ return match
1382
+
1383
+ def _handle_via_ir(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
1384
+ if self.get_solc_via_ir_value(contract_file_path):
1372
1385
  settings_dict["viaIR"] = True
1373
1386
 
1374
- def _handle_evm_version(self, contract_file_path: str, settings_dict: Dict[str, Any]) -> None:
1375
- if self.context.solc_evm_version_map:
1376
- match = Util.match_path_to_mapping_key(Path(contract_file_path), self.context.solc_evm_version_map)
1377
- if match:
1378
- settings_dict["evmVersion"] = match
1379
- elif self.context.solc_evm_version:
1380
- settings_dict["evmVersion"] = self.context.solc_evm_version
1387
+ def _handle_evm_version(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
1388
+ match = self.get_solc_evm_version_value(contract_file_path)
1389
+ if match:
1390
+ settings_dict["evmVersion"] = match
1381
1391
 
1382
- def _handle_optimize(self, contract_file_path: str, settings_dict: Dict[str, Any],
1392
+ def _handle_optimize(self, contract_file_path: Path, settings_dict: Dict[str, Any],
1383
1393
  compiler_collector: CompilerCollector) -> None:
1384
1394
  """
1385
1395
  @param contract_file_path: the contract that we are working on
1386
1396
  @param settings_dict: data structure for sending to the solc compiler
1387
1397
  """
1388
- if self.context.solc_optimize_map:
1389
- match = Util.match_path_to_mapping_key(Path(contract_file_path), self.context.solc_optimize_map)
1390
- if match and int(match) > 0:
1391
- settings_dict["optimizer"] = {"enabled": True}
1392
- settings_dict["optimizer"]['runs'] = int(match)
1393
- elif self.context.solc_optimize:
1398
+ match = self.get_solc_optimize_value(contract_file_path)
1399
+ if match:
1394
1400
  settings_dict["optimizer"] = {"enabled": True}
1395
- if int(self.context.solc_optimize) > 0:
1396
- settings_dict["optimizer"]['runs'] = int(self.context.solc_optimize)
1401
+ if int(match) > 0:
1402
+ settings_dict["optimizer"]['runs'] = int(match)
1397
1403
 
1398
1404
  # if optimizer is true, we should also define settings_dict["optimizer"]["details"]
1399
1405
  # for both optimize map and optimize
1400
1406
  optimizer = settings_dict.get("optimizer")
1401
1407
  if optimizer and isinstance(optimizer, dict) and optimizer.get('enabled'):
1402
1408
  # if we are not disabling finder friendly optimizer specifically, enable it whenever viaIR is also enabled
1403
- if not self.context.strict_solc_optimizer and self.context.solc_via_ir:
1409
+ if not self.context.strict_solc_optimizer and self.get_solc_via_ir_value(contract_file_path):
1404
1410
  # The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
1405
1411
  # full inliner step removed
1406
1412
  solc0_8_26_to_0_8_30 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
@@ -1433,6 +1439,8 @@ class CertoraBuildGenerator:
1433
1439
  raise Util.CertoraUserInputError(err_msg)
1434
1440
  elif minor < 6 or (minor == 6 and patch < 7):
1435
1441
  raise Util.CertoraUserInputError(err_msg)
1442
+ elif self.context.yul_optimizer_steps:
1443
+ yul_optimizer_steps = self.context.yul_optimizer_steps
1436
1444
  elif (minor == 6 and patch >= 7) or (minor == 7 and 0 <= patch <= 1):
1437
1445
  yul_optimizer_steps = solc0_6_7_to_0_7_1
1438
1446
  elif minor == 7 and 2 <= patch <= 5:
@@ -1470,7 +1478,7 @@ class CertoraBuildGenerator:
1470
1478
  for opt_pass in self.context.disable_solc_optimizers:
1471
1479
  settings_dict["optimizer"]["details"][opt_pass] = False
1472
1480
 
1473
- def _fill_codegen_related_options(self, contract_file_path: str, settings_dict: Dict[str, Any],
1481
+ def _fill_codegen_related_options(self, contract_file_path: Path, settings_dict: Dict[str, Any],
1474
1482
  compiler_collector: CompilerCollector) -> None:
1475
1483
  """
1476
1484
  Fills options that control how we call solc and affect the bytecode in some way
@@ -1551,7 +1559,7 @@ class CertoraBuildGenerator:
1551
1559
  }
1552
1560
  }
1553
1561
 
1554
- self._fill_codegen_related_options(contract_file_as_provided, settings_dict, compiler_collector)
1562
+ self._fill_codegen_related_options(Path(contract_file_as_provided), settings_dict, compiler_collector)
1555
1563
 
1556
1564
  result_dict = {"language": compiler_collector_lang.name, "sources": sources_dict, "settings": settings_dict}
1557
1565
  # debug_print("Standard json input")
@@ -2286,7 +2294,7 @@ class CertoraBuildGenerator:
2286
2294
 
2287
2295
  if compiler_lang == CompilerLangSol():
2288
2296
  settings_dict: Dict[str, Any] = {}
2289
- self._fill_codegen_related_options(build_arg_contract_file, settings_dict,
2297
+ self._fill_codegen_related_options(Path(build_arg_contract_file), settings_dict,
2290
2298
  compiler_collector_for_contract_file)
2291
2299
  solc_optimizer_on, solc_optimizer_runs = self.solc_setting_optimizer_runs(settings_dict)
2292
2300
  solc_via_ir = self.solc_setting_via_ir(settings_dict)
@@ -2416,7 +2424,7 @@ class CertoraBuildGenerator:
2416
2424
  # if no function finder mode set, determine based on viaIR enabled or not:
2417
2425
  if self.context.function_finder_mode is None:
2418
2426
  # in via-ir, should not compress
2419
- if self.context.solc_via_ir:
2427
+ if self.get_solc_via_ir_value(Path(contract_file)):
2420
2428
  should_compress = False
2421
2429
  else:
2422
2430
  should_compress = True
@@ -3614,7 +3622,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
3614
3622
 
3615
3623
  try:
3616
3624
  if overwrite:
3617
- # expecting target path to exist.
3625
+ # expecting the target path to exist.
3618
3626
  if target_path.exists():
3619
3627
  build_logger.debug(f"Overwriting {target_path} by copying from {source_path}")
3620
3628
  else:
@@ -3632,7 +3640,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
3632
3640
  cwd_file_path.parent.mkdir(parents=True, exist_ok=True)
3633
3641
  cwd_file_path.touch()
3634
3642
 
3635
- # the empty file .project_directory is written in the source tree to denote the projecct directory
3643
+ # the empty file .project_directory is written in the source tree to denote the project directory
3636
3644
  rust_proj_dir = getattr(context, 'rust_project_directory', None)
3637
3645
  if rust_proj_dir:
3638
3646
  proj_dir_parent_relative = os.path.relpath(rust_proj_dir, os.getcwd())
@@ -3683,8 +3691,9 @@ def build_from_scratch(context: CertoraContext,
3683
3691
 
3684
3692
  # add to cache also source files that were found in the SDCs (e.g., storage extensions)
3685
3693
  paths_set = sdc.all_contract_files
3686
- for p in context.files:
3687
- paths_set.add(Path(p).absolute())
3694
+ for f in context.files:
3695
+ path = f.split(':')[0] # `f` is either 'path/to/file.sol' or 'path/to/file.sol:ContractName'
3696
+ paths_set.add(Path(path).absolute())
3688
3697
 
3689
3698
  # the contract files in SDCs are relative to .certora_sources. Which isn't good for us here.
3690
3699
  # Need to be relative to original paths
@@ -3724,8 +3733,7 @@ def build_from_scratch(context: CertoraContext,
3724
3733
 
3725
3734
  def build_from_cache_or_scratch(context: CertoraContext,
3726
3735
  certora_build_generator: CertoraBuildGenerator,
3727
- certora_verify_generator: CertoraVerifyGenerator,
3728
- certora_build_cache_manager: CertoraBuildCacheManager) \
3736
+ certora_verify_generator: CertoraVerifyGenerator) \
3729
3737
  -> Tuple[bool, bool, CachedFiles]:
3730
3738
  """
3731
3739
  Builds either from cache (fast path) or from scratch (slow path)
@@ -3742,10 +3750,10 @@ def build_from_cache_or_scratch(context: CertoraContext,
3742
3750
  False)
3743
3751
  return cache_hit, False, cached_files
3744
3752
 
3745
- build_cache_applicable = certora_build_cache_manager.cache_is_applicable(context)
3753
+ build_cache_applicable = CertoraBuildCacheManager.cache_is_applicable(context)
3746
3754
 
3747
3755
  if not build_cache_applicable:
3748
- build_cache_disabling_options = certora_build_cache_manager.cache_disabling_options(context)
3756
+ build_cache_disabling_options = CertoraBuildCacheManager.cache_disabling_options(context)
3749
3757
  build_logger.warning("Requested to enable the build cache, but the build cache is not applicable "
3750
3758
  f"to this run because of the given options: {build_cache_disabling_options}")
3751
3759
  cached_files = build_from_scratch(context, certora_build_generator,
@@ -3753,7 +3761,7 @@ def build_from_cache_or_scratch(context: CertoraContext,
3753
3761
  False)
3754
3762
  return cache_hit, False, cached_files
3755
3763
 
3756
- cached_files = certora_build_cache_manager.build_from_cache(context)
3764
+ cached_files = CertoraBuildCacheManager.build_from_cache(context)
3757
3765
  # if no match, will rebuild from scratch
3758
3766
  if cached_files is not None:
3759
3767
  # write to .certora_build.json
@@ -3792,7 +3800,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3792
3800
 
3793
3801
  try:
3794
3802
  input_config = InputConfig(context)
3795
-
3803
+ context.main_cache_key = CertoraBuildCacheManager.get_main_cache_key(context)
3796
3804
  # Create generators
3797
3805
  certora_build_generator = CertoraBuildGenerator(input_config, context)
3798
3806
 
@@ -3808,12 +3816,9 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3808
3816
  else:
3809
3817
  Ctx.run_local_spec_check(False, context)
3810
3818
 
3811
- certora_build_cache_manager = CertoraBuildCacheManager()
3812
-
3813
3819
  cache_hit, build_cache_enabled, cached_files = build_from_cache_or_scratch(context,
3814
3820
  certora_build_generator,
3815
- certora_verify_generator,
3816
- certora_build_cache_manager)
3821
+ certora_verify_generator)
3817
3822
 
3818
3823
  # avoid running the same test over and over again for each split run, context.split_rules is true only for
3819
3824
  # the first run and is set to [] for split runs
@@ -3833,7 +3838,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3833
3838
 
3834
3839
  # save in build cache
3835
3840
  if not cache_hit and build_cache_enabled and cached_files.may_store_in_build_cache:
3836
- certora_build_cache_manager.save_build_cache(context, cached_files)
3841
+ CertoraBuildCacheManager.save_build_cache(context, cached_files)
3837
3842
 
3838
3843
  certora_verify_generator.update_certora_verify_struct(True)
3839
3844
  certora_verify_generator.dump() # second dump with properly rooted specs
@@ -66,10 +66,9 @@ class CertoraBuildCacheManager:
66
66
  @returns None if no cache hit, otherwise - the matching `.certora_build.json` file, and a set of source files
67
67
  """
68
68
  build_cache_dir = Util.get_certora_build_cache_dir()
69
- main_cache_key = CertoraBuildCacheManager.get_main_cache_key(context)
70
- main_cache_entry_dir = build_cache_dir / main_cache_key
69
+ main_cache_entry_dir = build_cache_dir / context.main_cache_key
71
70
  if not main_cache_entry_dir.exists():
72
- build_cache_logger.info(f"cache miss on build cache key {main_cache_key}")
71
+ build_cache_logger.info(f"cache miss on build cache key {context.main_cache_key}")
73
72
  return None
74
73
 
75
74
  # here's the tricky matching part:
@@ -92,7 +91,7 @@ class CertoraBuildCacheManager:
92
91
  build_cache_logger.debug(f"Current sub build cache key computed for {file_list_file} is invalid")
93
92
  continue
94
93
  elif sub_cache_key_current_for_list == sub_cache_key_from_saved_entry:
95
- build_cache_logger.info(f"We have a match on build cache key {main_cache_key} and "
94
+ build_cache_logger.info(f"We have a match on build cache key {context.main_cache_key} and "
96
95
  f"{sub_cache_key_current_for_list}")
97
96
  sub_cache_key = sub_cache_key_current_for_list
98
97
  all_contract_files = set([Path(f) for f in file_list])
@@ -103,24 +102,24 @@ class CertoraBuildCacheManager:
103
102
 
104
103
  if sub_cache_key is None:
105
104
  build_cache_logger.info("All sub-cache-key file list files missed, cache miss on build cache key "
106
- f"{main_cache_key}")
105
+ f"{context.main_cache_key}")
107
106
  return None
108
107
 
109
108
  # cache hit
110
109
  certora_build_file = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.certora_build_suffix}"
111
110
  if not certora_build_file.exists():
112
- build_cache_logger.warning(f"Got a cache-hit on build cache key {main_cache_key} and {sub_cache_key} "
111
+ build_cache_logger.warning(f"Got a cache-hit on build cache key {context.main_cache_key} and {sub_cache_key} "
113
112
  "but .certora_build.json file does not exist")
114
113
  return None
115
114
 
116
115
  if all_contract_files is None: # should not be feasible
117
- build_cache_logger.warning(f"Got a cache-hit on build cache key {main_cache_key} and {sub_cache_key} "
118
- "but file list file does not exist")
116
+ build_cache_logger.warning(f"Got a cache-hit on build cache key {context.main_cache_key} and "
117
+ f"{sub_cache_key} but file list file does not exist")
119
118
  return None
120
119
 
121
120
  build_output_props_file = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.build_output_props_suffix}"
122
121
  if not build_output_props_file.exists():
123
- build_cache_logger.warning(f"Got a cache-hit on build cache key {main_cache_key} and {sub_cache_key} "
122
+ build_cache_logger.warning(f"Got a cache-hit on build cache key {context.main_cache_key} and {sub_cache_key} "
124
123
  f"but {CachedFiles.build_output_props_suffix} file does not exist")
125
124
  return None
126
125
 
@@ -131,26 +130,26 @@ class CertoraBuildCacheManager:
131
130
  @staticmethod
132
131
  def save_build_cache(context: CertoraContext, cached_files: CachedFiles) -> None:
133
132
  build_cache_dir = Util.get_certora_build_cache_dir()
134
- main_cache_key = CertoraBuildCacheManager.get_main_cache_key(context)
135
- main_cache_entry_dir = build_cache_dir / main_cache_key
133
+ main_cache_entry_dir = build_cache_dir / context.main_cache_key
136
134
  sub_cache_key = CertoraBuildCacheManager.get_sub_cache_key(cached_files.all_contract_files)
137
135
  if sub_cache_key is None:
138
- build_cache_logger.warning(f"Cannot save cache for main build cache key {main_cache_key} "
136
+ build_cache_logger.warning(f"Cannot save cache for main build cache key {context.main_cache_key} "
139
137
  "as sub-cache-key could not be computed")
140
138
  return
141
139
 
142
140
  if main_cache_entry_dir.exists():
143
- build_cache_logger.info(f"main build cache key already exists {main_cache_key}, "
141
+ build_cache_logger.info(f"main build cache key already exists {context.main_cache_key}, "
144
142
  f"saving sub build cache key {sub_cache_key}")
145
143
  if cached_files.all_exist(main_cache_entry_dir, sub_cache_key):
146
144
  build_cache_logger.debug("cache already saved under this build cache key, override")
147
145
  else:
148
- build_cache_logger.debug(f"cache was corrupted, need to re-save build cache key {main_cache_key} "
149
- f"and sub cache key {sub_cache_key}")
146
+ build_cache_logger.debug(f"cache was corrupted, need to re-save build cache key "
147
+ f"{context.main_cache_key} and sub cache key {sub_cache_key}")
150
148
  CertoraBuildCacheManager.save_files(cached_files, main_cache_entry_dir,
151
149
  sub_cache_key)
152
150
  else:
153
- build_cache_logger.info(f"saving main build cache key {main_cache_key} and sub cache key {sub_cache_key}")
151
+ build_cache_logger.info(f"saving main build cache key {context.main_cache_key} and sub cache key "
152
+ f"{sub_cache_key}")
154
153
  safe_create_dir(main_cache_entry_dir)
155
154
  CertoraBuildCacheManager.save_files(cached_files, main_cache_entry_dir,
156
155
  sub_cache_key)
@@ -51,6 +51,7 @@ def build_rust_app(context: CertoraContext) -> None:
51
51
  feature_flag = '--features'
52
52
  if context.cargo_tools_version:
53
53
  build_command.extend(["--tools-version", context.cargo_tools_version])
54
+ context.rust_project_directory = Util.find_nearest_cargo_toml()
54
55
 
55
56
  if context.cargo_features is not None:
56
57
  build_command.append(feature_flag)
@@ -102,7 +103,7 @@ def add_solana_files_to_sources(context: CertoraContext, sources: Set[Path]) ->
102
103
 
103
104
 
104
105
  def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path]) -> None:
105
- patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
106
+ patterns = ["*.rs", "*.so", "*.wasm", Util.CARGO_TOML_FILE, "Cargo.lock", "justfile"]
106
107
  exclude_dirs = [".certora_internal"]
107
108
 
108
109
  if hasattr(context, 'rust_project_directory'):
@@ -760,42 +760,20 @@ class CloudVerification:
760
760
  Util.get_configuration_layout_data_file(),
761
761
  Util.get_build_dir() / Path(self.context.files[0]).name]
762
762
 
763
+ if Util.get_debug_log_file().exists():
764
+ files_list.append(Util.get_debug_log_file())
765
+
763
766
  if Util.get_certora_sources_dir().exists():
764
767
  files_list.append(Util.get_certora_sources_dir())
765
768
 
766
769
  if hasattr(self.context, 'build_script') and self.context.build_script:
767
- result = compress_files(self.logZipFilePath, Util.get_debug_log_file(),
768
- short_output=Ctx.is_minimal_cli_output(self.context))
769
-
770
- if not result:
771
- return False
772
- files_list.append(self.logZipFilePath)
773
-
774
- # Create a .RustExecution file to classify zipInput as a rust source code
775
- rust_execution_file = Util.get_build_dir() / ".RustExecution"
776
- rust_execution_file.touch(exist_ok=True)
777
- files_list.append(rust_execution_file)
778
-
779
770
  if attr_file := getattr(self.context, 'rust_logs_stdout', None):
780
771
  files_list.append(Util.get_build_dir() / Path(attr_file).name)
781
772
  if attr_file := getattr(self.context, 'rust_logs_stderr', None):
782
773
  files_list.append(Util.get_build_dir() / Path(attr_file).name)
783
774
 
784
- result = compress_files(self.ZipFilePath, *files_list,
785
- short_output=Ctx.is_minimal_cli_output(self.context))
786
-
787
- elif Attrs.is_solana_app() or Attrs.is_soroban_app():
788
- for file in self.context.files:
789
- files_list.append(Path(file))
790
-
791
- result = compress_files(self.ZipFilePath, *files_list,
792
- short_output=Ctx.is_minimal_cli_output(self.context))
793
- else:
794
- raise Util.CertoraUserInputError(
795
- 'When running rust application, context should either have attribute "rust_executables" '
796
- 'provided by build_script execution, '
797
- 'or either is_solana_app(), is_soroban_app() should be set to True'
798
- )
775
+ result = compress_files(self.ZipFilePath, *files_list,
776
+ short_output=Ctx.is_minimal_cli_output(self.context))
799
777
 
800
778
  else:
801
779
  # Zip the log file first separately and again with the rest of the files, so it will not be decompressed
@@ -953,6 +931,8 @@ class CloudVerification:
953
931
  for i in range(self.verification_request_retries):
954
932
  try:
955
933
  response = requests.post(verify_url, json=auth_data, headers=headers, timeout=60)
934
+ cloud_logger.debug(f"\n\n=================\n\nresponse - Status Code: {response.status_code}\n\n"
935
+ f"Headers:\n {response.headers}\n\nText:\n {response.text}\n\n=================\n\n")
956
936
  if response is None:
957
937
  continue
958
938
  status = response.status_code
@@ -25,12 +25,13 @@ sys.path.insert(0, str(scripts_dir_path))
25
25
  import CertoraProver.certoraContextAttributes as Attrs
26
26
  from CertoraProver.certoraCollectRunMetadata import RunMetaData, MetadataEncoder
27
27
  import Shared.certoraUtils as Utils
28
+ from typing import List
28
29
 
29
30
 
30
31
  class MainSection(Enum):
31
32
  GENERAL = "GENERAL"
33
+ OPTIONS = "OPTIONS"
32
34
  SOLIDITY_COMPILER = "SOLIDITY_COMPILER"
33
- GIT = "GIT"
34
35
  NEW_SECTION = "NEW_SECTION"
35
36
 
36
37
 
@@ -47,13 +48,11 @@ class InnerContent:
47
48
  content: Any
48
49
  doc_link: str = ''
49
50
  tooltip: str = ''
50
- unsound: str = 'false'
51
+ unsound: bool = False
51
52
 
52
53
  def __post_init__(self) -> None:
53
54
  if isinstance(self.content, bool):
54
55
  self.content = 'true' if self.content else 'false'
55
- if isinstance(self.unsound, bool):
56
- self.unsound = 'true' if self.unsound else 'false'
57
56
 
58
57
 
59
58
  @dataclasses.dataclass
@@ -65,6 +64,7 @@ class CardContent:
65
64
 
66
65
  DOC_LINK_PREFIX = 'https://docs.certora.com/en/latest/docs/'
67
66
  GIT_ATTRIBUTES = ['origin', 'revision', 'branch', 'dirty']
67
+ ARG_LIST_ATTRIBUTES = ['prover_args', 'java_args']
68
68
 
69
69
 
70
70
  class AttributeJobConfigData:
@@ -73,13 +73,13 @@ class AttributeJobConfigData:
73
73
  This should be added to the AttributeDefinition and configured for every new attribute
74
74
  presented in the Rule report.
75
75
 
76
- Note: Attributes which do not contain specific information will be assigned as a Flag in the General main section!
76
+ Note: Attributes that do not contain specific information will be presented in the OPTIONS main section!
77
77
 
78
78
  arguments:
79
79
  - main_section : MainSection -- the main section inside the config tab
80
- default: MainSection.GENERAL
81
- - subsection : str -- the subsection within the main_section (e.g Flags)
82
- default: Flags
80
+ default: MainSection.OPTIONS
81
+ - subsection : str -- the subsection within the main_section
82
+ default: None - they will be presented inside the OPTIONS card
83
83
  - doc_link : Optional[str] -- a link to the Documentation page of this attribute (if exists)
84
84
  default: 'https://docs.certora.com/en/latest/docs/' + Solana/EVM path + #<attribute_name>
85
85
  - tooltip : Optional[str] -- a description of this attribute to present in the config tab
@@ -88,7 +88,7 @@ class AttributeJobConfigData:
88
88
  default: False
89
89
  """
90
90
 
91
- def __init__(self, main_section: MainSection = MainSection.GENERAL, subsection: str = '',
91
+ def __init__(self, main_section: MainSection = MainSection.OPTIONS, subsection: str = '',
92
92
  doc_link: Optional[str] = '', tooltip: Optional[str] = '', unsound: bool = False):
93
93
  self.main_section = main_section
94
94
  self.subsection = subsection
@@ -201,6 +201,40 @@ def create_or_get_card_content(output: list[CardContent], name: str) -> CardCont
201
201
  return main_section
202
202
 
203
203
 
204
+ def split_and_sort_arg_list_value(args_list: List[str]) -> List[str]:
205
+ """
206
+ Splits a unified CLI argument list of strings into a sorted list of flag+value groups.
207
+ This is useful mainly for --prover_args and --java_args.
208
+
209
+ For example:
210
+ "-depth 15 -adaptiveSolverConfig false" → ["-adaptiveSolverConfig false", "-depth 15"]
211
+
212
+ Assumes each flag starts with '-' and its value follows immediately, if exists.
213
+ Lines are sorted alphabetically.
214
+ """
215
+ unified_args = ''.join(args_list)
216
+
217
+ if not unified_args.strip():
218
+ return []
219
+
220
+ lines: List[str] = []
221
+ tokens = unified_args.split()
222
+ curr_line = ""
223
+
224
+ for token in tokens:
225
+ if token.startswith('-'):
226
+ if curr_line:
227
+ lines.append(curr_line)
228
+ curr_line = token
229
+ else:
230
+ curr_line += f" {token}"
231
+
232
+ if curr_line:
233
+ lines.append(curr_line)
234
+
235
+ return sorted(lines)
236
+
237
+
204
238
  def create_inner_content(name: str, content_type: ContentType, value: Any, doc_link: str,
205
239
  config_data: AttributeJobConfigData) -> InnerContent:
206
240
  return InnerContent(
@@ -209,7 +243,7 @@ def create_inner_content(name: str, content_type: ContentType, value: Any, doc_l
209
243
  content=value,
210
244
  doc_link=doc_link,
211
245
  tooltip=config_data.tooltip or '',
212
- unsound='true' if config_data.unsound else 'false'
246
+ unsound=config_data.unsound
213
247
  )
214
248
 
215
249
 
@@ -260,35 +294,23 @@ def collect_attribute_configs(metadata: dict) -> list[CardContent]:
260
294
  )
261
295
  continue
262
296
 
263
- # Find or create the subsection (if it exists)
297
+ # Find or create the subsection (if it doesn't exist)
264
298
  if isinstance(attr_value, list):
265
- current_section: Any = main_section
266
299
  content_type = ContentType.SIMPLE
300
+ if attr_name in ARG_LIST_ATTRIBUTES:
301
+ attr_value = split_and_sort_arg_list_value(attr_value)
267
302
 
268
303
  elif isinstance(attr_value, dict):
269
- current_section = main_section
270
304
  content_type = ContentType.COMPLEX
271
305
  attr_value = [
272
306
  create_inner_content(key, ContentType.FLAG, value, doc_link, config_data)
273
307
  for key, value in attr_value.items()
274
308
  ]
275
309
  else:
276
- # this attribute is a value attribute without a subsection, it will be placed in flags.
277
- subsection_key = config_data.subsection.lower() if config_data.subsection else 'flags'
278
- current_section = next((section for section in main_section.content
279
- if section.inner_title == subsection_key), None)
280
- if current_section is None:
281
- current_section = InnerContent(
282
- inner_title=subsection_key,
283
- content_type=ContentType.COMPLEX.value,
284
- content=[]
285
- )
286
- main_section.content.append(current_section)
287
-
288
310
  content_type = ContentType.FLAG
289
311
 
290
312
  # Update the current section with attribute details
291
- current_section.content.append(
313
+ main_section.content.append(
292
314
  create_inner_content(attr_name, content_type, attr_value, doc_link, config_data)
293
315
  )
294
316
 
@@ -300,37 +322,23 @@ def collect_run_config_from_metadata(attributes_configs: list[CardContent], meta
300
322
  Adding CLI and Git configuration from metadata
301
323
  """
302
324
 
303
- # Define a mapping of metadata attributes to their keys in general_section
304
- metadata_mappings = {
305
- 'CLI Version': metadata.get('CLI_version'),
306
- 'Verify': metadata.get('conf', {}).get('verify'),
307
- }
308
-
309
325
  general_section = create_or_get_card_content(attributes_configs, MainSection.GENERAL.value.lower())
310
326
 
311
- # Add metadata attributes dynamically if they exist
312
- for key, value in metadata_mappings.items():
313
- if value:
314
- general_section.content.append(InnerContent(
315
- inner_title=key,
316
- content_type=ContentType.FLAG.value,
317
- content=value,
318
- ))
327
+ if cli_version := metadata.get('CLI_version'):
328
+ general_section.content.append(InnerContent(
329
+ inner_title='CLI Version',
330
+ content_type=ContentType.FLAG.value,
331
+ content=cli_version,
332
+ ))
319
333
 
320
- # Adding GIT configuration from metadata only if attributes are found
321
- git_content = []
322
334
  for attr in GIT_ATTRIBUTES:
323
335
  if attr_value := metadata.get(attr):
324
- git_content.append(InnerContent(
336
+ general_section.content.append(InnerContent(
325
337
  inner_title=attr,
326
338
  content_type=ContentType.FLAG.value,
327
339
  content=attr_value,
328
340
  ))
329
341
 
330
- if git_content:
331
- git_section = create_or_get_card_content(attributes_configs, MainSection.GIT.value.lower())
332
- git_section.content = git_content
333
-
334
342
  return attributes_configs
335
343
 
336
344
 
@@ -338,14 +346,17 @@ def sort_configuration_layout(data: list[CardContent]) -> list[CardContent]:
338
346
  """
339
347
  Sorts a configuration layout:
340
348
  - Top-level sorted by 'card_title'
341
- - Nested content sorted by 'inner_title', with 'Verify' first
349
+ - Nested content sorted by 'inner_title', with 'verify' first
342
350
  """
343
351
  priority = {
344
- "Verify": 0,
352
+ # Priorities for top-level cards
345
353
  "general": 0,
354
+ "files": 1,
355
+ "options": 2,
356
+ # Top level items inside their respective cards
357
+ "verify": 0,
346
358
  "solc": 0,
347
- "CLI Version": 1,
348
- "flags": 2
359
+ "CLI Version": 0
349
360
  }
350
361
 
351
362
  def inner_sort_key(item: Any) -> Any:
@@ -17,6 +17,7 @@ import json5
17
17
  import logging
18
18
  from pathlib import Path
19
19
  from typing import Dict, Any
20
+ from collections import OrderedDict
20
21
 
21
22
  import CertoraProver.certoraContext as Ctx
22
23
  import CertoraProver.certoraContextAttributes as Attrs
@@ -75,7 +76,7 @@ def read_from_conf_file(context: CertoraContext) -> None:
75
76
 
76
77
  try:
77
78
  with conf_file_path.open() as conf_file:
78
- context.conf_file_attr = json5.load(conf_file, allow_duplicate_keys=False)
79
+ context.conf_file_attr = json5.load(conf_file, allow_duplicate_keys=False, object_pairs_hook=OrderedDict)
79
80
  try:
80
81
  check_conf_content(context)
81
82
  except Util.CertoraUserInputError as e:
@@ -20,6 +20,8 @@ import os
20
20
  import re
21
21
  import sys
22
22
  import logging
23
+ import fnmatch
24
+ from wcmatch import glob
23
25
 
24
26
 
25
27
  from pathlib import Path
@@ -150,8 +152,6 @@ def get_local_run_cmd(context: CertoraContext) -> List[str]:
150
152
  java_cmd.extend(context.java_args.strip().split(' '))
151
153
 
152
154
  cmd = java_cmd + ["-jar", jar_path] + run_args
153
- if context.test == str(Util.TestValue.LOCAL_JAR):
154
- raise Util.TestResultsReady(' '.join(cmd))
155
155
  return cmd
156
156
 
157
157
 
@@ -615,3 +615,30 @@ def attrs_to_relative(context: CertoraContext) -> None:
615
615
  packages_to_relative()
616
616
  prover_resource_file_to_relative()
617
617
  verify_to_relative()
618
+
619
+ def get_map_attribute_value(context: CertoraContext, path: Path, attr_name: str) -> Optional[Union[str, bool]]:
620
+
621
+ value = getattr(context, attr_name, None)
622
+ if value:
623
+ return value
624
+
625
+ map_value = getattr(context, f"{attr_name}_map", None)
626
+ if not map_value:
627
+ return None # No map value defined
628
+ for key, entry_value in map_value.items():
629
+ # Split key to handle contract:field syntax
630
+ key_parts = key.split(':')
631
+ pattern = key_parts[0]
632
+
633
+ if Path(pattern).suffix == "": # This is a contract pattern
634
+ # Find contracts that match the pattern
635
+ for contract_name, contract_file_path in context.contract_to_file.items():
636
+ if fnmatch.fnmatch(contract_name, pattern):
637
+ # Check if this contract's file matches our target path
638
+ if Path(contract_file_path) == path:
639
+ return entry_value
640
+ else: # This is a file pattern
641
+ # Match the file pattern against the path
642
+ if glob.globmatch(str(path), pattern, flags=glob.GLOBSTAR):
643
+ return entry_value
644
+ return None # No match
@@ -156,7 +156,9 @@ class CommonAttributes(AttrUtil.Attributes):
156
156
  'action': AttrUtil.NotAllowed
157
157
  },
158
158
  affects_build_cache_key=False,
159
- disables_build_cache=False
159
+ disables_build_cache=False,
160
+ # Avoiding presentation of this attribute in Config Tab
161
+ config_data=None
160
162
  )
161
163
 
162
164
  RUN_SOURCE = AttrUtil.AttributeDefinition(
@@ -340,7 +342,7 @@ class EvmAttributes(AttrUtil.Attributes):
340
342
  default_desc="do not set via_ir during compilation unless solc_via_ir is set",
341
343
  argparse_args={
342
344
  'action': AttrUtil.UniqueStore,
343
- 'type': lambda value: Vf.parse_dict('solc_via_ir_map', value, bool)
345
+ 'type': lambda value: Vf.parse_ordered_dict('solc_via_ir_map', value, bool)
344
346
  },
345
347
  affects_build_cache_key=True,
346
348
  disables_build_cache=False,
@@ -382,7 +384,7 @@ class EvmAttributes(AttrUtil.Attributes):
382
384
  default_desc="Uses the same Solidity EVM version for all contracts",
383
385
  argparse_args={
384
386
  'action': AttrUtil.UniqueStore,
385
- 'type': lambda value: Vf.parse_dict('solc_evm_version_map', value)
387
+ 'type': lambda value: Vf.parse_ordered_dict('solc_evm_version_map', value)
386
388
  },
387
389
  affects_build_cache_key=True,
388
390
  disables_build_cache=False,
@@ -405,7 +407,7 @@ class EvmAttributes(AttrUtil.Attributes):
405
407
  default_desc="Uses the same Solidity compiler version for all contracts",
406
408
  argparse_args={
407
409
  'action': AttrUtil.UniqueStore,
408
- 'type': lambda value: Vf.parse_dict('solc_map', value)
410
+ 'type': lambda value: Vf.parse_ordered_dict('solc_map', value)
409
411
  },
410
412
  affects_build_cache_key=True,
411
413
  disables_build_cache=False,
@@ -428,7 +430,7 @@ class EvmAttributes(AttrUtil.Attributes):
428
430
  default_desc="Uses the same compiler version for all contracts",
429
431
  argparse_args={
430
432
  'action': AttrUtil.UniqueStore,
431
- 'type': lambda value: Vf.parse_dict('compiler_map', value)
433
+ 'type': lambda value: Vf.parse_ordered_dict('compiler_map', value)
432
434
  },
433
435
  affects_build_cache_key=True,
434
436
  disables_build_cache=False,
@@ -481,7 +483,7 @@ class EvmAttributes(AttrUtil.Attributes):
481
483
  default_desc="Compiles all contracts with the same optimization settings",
482
484
  argparse_args={
483
485
  'action': AttrUtil.UniqueStore,
484
- 'type': lambda value: Vf.parse_dict('solc_optimize_map', value)
486
+ 'type': lambda value: Vf.parse_ordered_dict('solc_optimize_map', value)
485
487
  },
486
488
  affects_build_cache_key=True,
487
489
  disables_build_cache=False,
@@ -582,6 +584,16 @@ class EvmAttributes(AttrUtil.Attributes):
582
584
  affects_build_cache_key=True,
583
585
  disables_build_cache=True
584
586
  )
587
+
588
+ YUL_OPTIMIZER_STEPS = AttrUtil.AttributeDefinition(
589
+ # overrides the hardcoded yul optimizer steps, set in certoraBuild.py
590
+ argparse_args={
591
+ 'action': AttrUtil.UniqueStore
592
+ },
593
+ affects_build_cache_key=True,
594
+ disables_build_cache=False
595
+ )
596
+
585
597
  CACHE = AttrUtil.AttributeDefinition(
586
598
  argparse_args={
587
599
  'action': AttrUtil.UniqueStore
@@ -740,8 +752,7 @@ class EvmAttributes(AttrUtil.Attributes):
740
752
  'action': AttrUtil.UniqueStore
741
753
  },
742
754
  affects_build_cache_key=False,
743
- disables_build_cache=False,
744
- config_data=None
755
+ disables_build_cache=False
745
756
  )
746
757
 
747
758
  BUILD_CACHE = AttrUtil.AttributeDefinition(
@@ -1719,7 +1730,7 @@ class RangerAttributes(EvmProverAttributes):
1719
1730
  @classmethod
1720
1731
  def ranger_unsupported_attributes(cls) -> List[AttrUtil.AttributeDefinition]:
1721
1732
  return [cls.PROJECT_SANITY, cls.RULE_SANITY, cls.COVERAGE_INFO, cls.FOUNDRY, cls.INDEPENDENT_SATISFY,
1722
- cls.MULTI_ASSERT_CHECK, cls.MULTI_EXAMPLE]
1733
+ cls.MULTI_ASSERT_CHECK, cls.MULTI_EXAMPLE, cls.VYPER]
1723
1734
 
1724
1735
  @classmethod
1725
1736
  def ranger_true_by_default_attributes(cls) -> List[AttrUtil.AttributeDefinition]:
@@ -21,7 +21,8 @@ import itertools
21
21
  import tempfile
22
22
  import fnmatch
23
23
  from pathlib import Path
24
- from typing import Dict, List, Tuple, Set, Any, Union, Optional
24
+ from wcmatch import glob
25
+ from typing import Dict, List, Tuple, Set, Any, Union, OrderedDict
25
26
 
26
27
  import CertoraProver.certoraContext as Ctx
27
28
  import CertoraProver.certoraContextAttributes as Attrs
@@ -31,6 +32,7 @@ from Shared import certoraUtils as Util
31
32
  from Shared import certoraAttrUtil as AttrUtil
32
33
  from CertoraProver.certoraProjectScanner import scan_project
33
34
 
35
+
34
36
  scripts_dir_path = Path(__file__).parent.resolve() # containing directory
35
37
  sys.path.insert(0, str(scripts_dir_path))
36
38
 
@@ -70,10 +72,10 @@ class CertoraContextValidator:
70
72
 
71
73
  else:
72
74
  if self.context.range:
73
- # self.context.range = None
75
+ self.context.range = None
74
76
  validation_logger.info("the 'range' attribute is ignored when not running from the Ranger App")
75
77
  if self.context.ranger_failure_limit:
76
- # self.context.ranger_failure_limit = None
78
+ self.context.ranger_failure_limit = None
77
79
  validation_logger.info("the 'ranger_failure_limit' is ignored when not running from the Ranger App")
78
80
 
79
81
  def validate(self) -> None:
@@ -375,19 +377,15 @@ def convert_to_compiler_map(context: CertoraContext) -> None:
375
377
  if context.solc_map:
376
378
  context.compiler_map = context.solc_map
377
379
  context.solc_map = None
378
- if context.solc:
379
- context.compiler_map = {'*.sol': f'{context.solc}'}
380
- if context.vyper:
381
- context.compiler_map = {'*.vy': f'{context.vyper}'}
380
+
382
381
 
383
382
  def check_vyper_flag(context: CertoraContext) -> None:
384
383
  if context.vyper:
385
- non_vy_paths = [path for path in context.files if not path.endswith(".vy")]
386
- if non_vy_paths:
387
- raise Util.CertoraUserInputError(f"vyper attribute can only be set for Vyper files: {non_vy_paths}")
384
+ vy_paths = [path for path in context.files if path.endswith(".vy")]
385
+ if not vy_paths:
386
+ validation_logger.warning("vyper attribute was set but no Vyper files were set")
388
387
  if context.solc:
389
388
  raise Util.CertoraUserInputError("cannot set both vyper attribute and solc attribute")
390
- context.solc = context.vyper
391
389
 
392
390
  def check_contract_name_arg_inputs(context: CertoraContext) -> None:
393
391
  """
@@ -670,37 +668,20 @@ def check_mode_of_operation(context: CertoraContext) -> None:
670
668
  special_file_type]) and not context.build_only:
671
669
  raise Util.CertoraUserInputError("You must use 'verify' when running the Certora Prover")
672
670
 
671
+ def find_matching_contracts(context: CertoraContext, pattern: str) -> List[str]:
672
+ result = []
673
+ for contract in context.contract_to_file:
674
+ if fnmatch.fnmatch(contract, pattern):
675
+ result.append(context.contract_to_file[contract])
676
+ return result
673
677
 
674
- def _normalize_maps(context: CertoraContext, map_attr_name: str, map_attr: Dict) -> Dict[str, str]:
675
- """
676
-
677
- :param context:
678
- :param map_attr_name: name of the attribute e.g. solc_optimize_map
679
- :param map_attr: the value of the map attribute, a dictionary mapping contract/file to a value
680
- :return:
681
-
682
- all solc_XXX_map attributes are used to set attributes for the Solidity compiler. The value is based on the
683
- Solidity file to be compiled, therefore it translates all keys to a relative path to a file
684
- """
685
-
686
- def find_matching_files(context: CertoraContext, pattern: str) -> List[str]:
687
- file_part = pattern.split(':')[0]
688
- if Path(file_part).suffix == "": # no suffix means the key is a contract
689
- matching_contracts = fnmatch.filter(context.contracts, pattern) # find matching contracts
690
- return [context.contract_to_file[contract] for contract in matching_contracts]
691
- else:
692
- return [file_part]
693
-
694
- new_map_attr: Dict[str, str] = {}
695
- for (pattern, value) in map_attr.copy().items():
696
- matching_files = find_matching_files(context, pattern)
697
- for file in matching_files:
698
- file = str(Path(file).resolve(strict=False).relative_to(Path.cwd()))
699
- attr_value = new_map_attr.get(file)
700
- if attr_value is None: # key in file paths but no mapping yet
701
- new_map_attr[file] = value
702
- return new_map_attr
678
+ def find_matching_files(context: CertoraContext, pattern: str) -> List[str]:
679
+ result = []
703
680
 
681
+ for path in context.file_paths:
682
+ if glob.globmatch(path, pattern, flags=glob.GLOBSTAR):
683
+ result.append(path)
684
+ return result
704
685
 
705
686
  def check_map_attributes(context: CertoraContext) -> None:
706
687
 
@@ -718,27 +699,24 @@ def check_map_attributes(context: CertoraContext) -> None:
718
699
  # we also check the map value was not set to False explicitly in the conf file
719
700
  if base_attr and map_attr and not (base_attr is False and context.conf_options.get(base_attr) is not None):
720
701
  raise Util.CertoraUserInputError(f"You cannot use both '{attr_prefix}' and '{map_attr_name}' arguments")
721
- if not isinstance(map_attr, dict):
722
- raise RuntimeError(f"map_attr is not dictionary, got {map_attr}")
723
- map_attr = _normalize_maps(context, map_attr_name, map_attr)
724
- setattr(context, f"{map_attr_name}", map_attr)
725
-
726
- sources_mappings: Dict[str, Optional[str]] = {file_path: None for file_path in context.file_paths} # initially mapping files to None
727
- for file in sources_mappings.keys():
728
- match: Optional[str] = None
729
- for (pattern, value) in map_attr.items():
730
- if fnmatch.fnmatch(file, pattern):
731
- match = pattern
732
- break
733
- if not match:
734
- raise Util.CertoraUserInputError(f"No matching for {file} in {map_attr_name}. Pattens: {map_attr}")
735
- elif not sources_mappings.get(file): # key in file paths but no mapping yet
736
- sources_mappings[file] = match
737
-
738
- # Now we check if all sources were mapped
739
- paths_not_set = [path for path, value in sources_mappings.items() if value is None]
740
- if paths_not_set:
741
- raise Util.CertoraUserInputError(f"{' '.join(paths_not_set)} was not set in {map_attr_name}")
702
+ if not isinstance(map_attr, OrderedDict):
703
+ raise RuntimeError(f"`map_attr` is not an ordered dictionary, got {map_attr}")
704
+
705
+ # will check that all the contract files are matched
706
+ file_list: Dict[str, bool] = {path: False for path in context.file_paths}
707
+
708
+ for key, value in map_attr.items():
709
+ pattern = key.split(':')[0] # ignore the contract part
710
+ if Path(pattern).suffix == "":
711
+ for path in find_matching_contracts(context, pattern):
712
+ file_list[path] = True
713
+ else:
714
+ for path in find_matching_files(context, pattern):
715
+ file_list[path] = True
716
+
717
+ none_keys = [k for k, v in file_list.items() if v is False]
718
+ if none_keys:
719
+ raise Util.CertoraUserInputError(f"The following files are not matched in {map_attr_name}: {none_keys}")
742
720
 
743
721
 
744
722
  def check_parametric_contracts(context: CertoraContext) -> None:
@@ -60,7 +60,7 @@ def add_solana_files_to_context(context: CertoraContext, json_obj: dict) -> None
60
60
  def run_rust_build(context: CertoraContext, build_cmd: List[str]) -> None:
61
61
 
62
62
  try:
63
- build_script_logger.info(f"Building by calling {build_cmd}")
63
+ build_script_logger.info(f"Building by calling `{' '.join(build_cmd)}`")
64
64
  result = subprocess.run(build_cmd, capture_output=True, text=True)
65
65
 
66
66
  # Check if the script executed successfully
@@ -90,11 +90,12 @@ def run_rust_build(context: CertoraContext, build_cmd: List[str]) -> None:
90
90
 
91
91
  add_solana_files_to_context(context, json_obj)
92
92
 
93
- if context.test == str(Util.TestValue.AFTER_BUILD_RUST):
94
- raise Util.TestResultsReady(context)
95
93
  assert not context.files, f"run_rust_build: expecting files to be empty, got: {context.files}"
96
94
  context.files = [os.path.join(context.rust_project_directory, context.rust_executables)]
97
95
 
96
+ if context.test == str(Util.TestValue.AFTER_BUILD_RUST):
97
+ raise Util.TestResultsReady(context)
98
+
98
99
  except Util.TestResultsReady as e:
99
100
  raise e
100
101
  except FileNotFoundError as e:
@@ -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'
@@ -1171,26 +1171,9 @@ class Singleton(type):
1171
1171
  cls.__instancesinstances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
1172
1172
  return cls.__instancesinstances[cls]
1173
1173
 
1174
-
1175
1174
  class AbstractAndSingleton(Singleton, ABCMeta):
1176
1175
  pass
1177
1176
 
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
1177
  def find_in(dir_path: Path, file_to_find: Path) -> Optional[Path]:
1195
1178
  """
1196
1179
  Given a directory dir_path and a file we wish to find within that directory,
@@ -1262,7 +1245,7 @@ class TestValue(NoValEnum):
1262
1245
  determines the chekpoint where the execution will halt. The exception TestResultsReady will be thrown. The value
1263
1246
  will also determine what object will be attached to the exception for inspection by the caller
1264
1247
  """
1265
- LOCAL_JAR = auto()
1248
+ BEFORE_LOCAL_PROVER_CALL = auto()
1266
1249
  CHECK_ARGS = auto()
1267
1250
  AFTER_BUILD = auto()
1268
1251
  CHECK_SOLC_OPTIONS = auto()
@@ -1452,3 +1435,22 @@ def eq_by(f: Callable[[T, T], bool], a: Sequence[T], b: Sequence[T]) -> bool:
1452
1435
  check if Sequences a and b are equal according to function f.
1453
1436
  """
1454
1437
  return len(a) == len(b) and all(map(f, a, b))
1438
+
1439
+ def find_nearest_cargo_toml() -> Optional[Path]:
1440
+ current = Path.cwd()
1441
+ for parent in [current] + list(current.parents):
1442
+ candidate = parent / CARGO_TOML_FILE
1443
+ if candidate.is_file():
1444
+ return candidate
1445
+ return None
1446
+
1447
+ def file_in_source_tree(file_path: Path) -> bool:
1448
+ # if the file is under .certora_source, return True
1449
+ file_path = Path(file_path).absolute()
1450
+ parent_dir = get_certora_sources_dir().absolute()
1451
+
1452
+ try:
1453
+ file_path.relative_to(parent_dir)
1454
+ return True
1455
+ except ValueError:
1456
+ 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
@@ -684,7 +686,7 @@ def validate_solc_via_ir_map(args: Dict[str, bool]) -> None:
684
686
  if first:
685
687
  validation_logger.warning("all via_ir values are set to True '--solc_via_ir' can be used instead")
686
688
  else:
687
- validation_logger.warning("all via_ir values are set to False, this flag/attribute can be omitted")
689
+ validation_logger.warning("all via_ir values are set to False, this flag/attribute can be omitted")
688
690
 
689
691
  def validate_solc_evm_version_map(args: Dict[str, str]) -> None:
690
692
  if not isinstance(args, dict):
@@ -938,7 +940,7 @@ def validate_on_off(value: str) -> str:
938
940
  return __validate_enum_value(value, OnOffValue)
939
941
 
940
942
 
941
- def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict[str, Union[str, bool]]:
943
+ def parse_ordered_dict(conf_key: str, input_string: str, value_type: Type = str) -> OrderedDict[str, Union[str, bool]]:
942
944
  """
943
945
  convert CLI flag value string of the form <key>=<value>,<key>=<value>,.. to a Dict.
944
946
  Keys with different values raise an exception
@@ -971,7 +973,7 @@ def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict
971
973
  raise argparse.ArgumentTypeError(f"{conf_key} argument {input_string} is of wrong format. Must be of format:"
972
974
  f"<key>=<value>[,..]")
973
975
 
974
- return_dict = {} # type: Dict[str, Union[str, bool]]
976
+ return_dict = OrderedDict() # type: OrderedDict[str, Union[str, bool]]
975
977
 
976
978
  for match in input_string.split(','):
977
979
  key, value = match.split('=')
@@ -176,6 +176,8 @@ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optio
176
176
  cmd.extend(additional_commands)
177
177
 
178
178
  print(f'Verifier run command:\n {" ".join(cmd)}')
179
+ if context.test == str(Util.TestValue.BEFORE_LOCAL_PROVER_CALL):
180
+ raise Util.TestResultsReady(' '.join(cmd))
179
181
  rc = Util.run_jar_cmd(
180
182
  cmd,
181
183
  override_exit_code=compare_with_expected_file,
certora_cli/certoraRun.py CHANGED
@@ -99,6 +99,8 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
99
99
  print(f"Verifier run command:\n {check_cmd_string}", flush=True)
100
100
 
101
101
  compare_with_tool_output = False
102
+ if context.test == str(Util.TestValue.BEFORE_LOCAL_PROVER_CALL):
103
+ raise Util.TestResultsReady(' '.join(check_cmd))
102
104
  run_result = Util.run_jar_cmd(check_cmd, compare_with_tool_output, logger_topic="verification",
103
105
  print_output=True)
104
106
  # For solana and wasm, we don't check types so build time is zero.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: certora-cli-beta-mirror
3
- Version: 7.30.1
3
+ Version: 7.31.0
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli-beta-mirror
6
6
  Author: Certora
@@ -25,6 +25,7 @@ Requires-Dist: tqdm
25
25
  Requires-Dist: StrEnum
26
26
  Requires-Dist: universalmutator
27
27
  Requires-Dist: jinja2
28
+ Requires-Dist: wcmatch
28
29
  Dynamic: author
29
30
  Dynamic: author-email
30
31
  Dynamic: classifier
@@ -37,4 +38,4 @@ Dynamic: requires-dist
37
38
  Dynamic: requires-python
38
39
  Dynamic: summary
39
40
 
40
- Commit 5c9b8e0. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
41
+ Commit 89159ec. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -4,30 +4,30 @@ certora_cli/certoraEVMProver.py,sha256=gLxqPXG9jKlCdLdgfo0aNjvp09vbHGSs8wghY6RH0
4
4
  certora_cli/certoraEqCheck.py,sha256=qfZq7bpU1kbAIezC66W61VfKNZz7Uywg2Ygup62qYeo,1069
5
5
  certora_cli/certoraMutate.py,sha256=XhFHyNVP_sk-3XkY6AAV5fVliEFAVRq-JeDGsqE5IQQ,3333
6
6
  certora_cli/certoraRanger.py,sha256=cwejxWTNlHsbwtu6Lew0SNsynSeq_ZKJu1Cr9uu0DhE,1314
7
- certora_cli/certoraRun.py,sha256=IIPykXqarflxubpd7zJomoBMcCcj7-uLXIPiv7nrL_k,8675
7
+ certora_cli/certoraRun.py,sha256=mTBAi-SVCApy8BvfvKAL8Z4PdGcaBV3G5vNXPrFdgTI,8817
8
8
  certora_cli/certoraSolanaProver.py,sha256=7hu-YJJNA_P5eAJq_jYh6IGjiUf0PegGUBRCJ5AhE7s,3274
9
9
  certora_cli/certoraSorobanProver.py,sha256=qE6b_vicC8KgOvUz7UTOaDuXT3UW0MMhq3keQgUVvzs,2865
10
10
  certora_cli/rustMutator.py,sha256=6AvOGU8Ijz89zW_ZJCWlfXkeobJsk7EsqZhK7Eqwn-Y,14544
11
11
  certora_cli/CertoraProver/__init__.py,sha256=QHNr-PJQAoyuPgTkO7gg23GRchiWSXglWNG7yLSQZvs,849
12
- certora_cli/CertoraProver/certoraBuild.py,sha256=WMaRg4VNdH_1k-GTfv-0USfIkQ7Gtdg3Y23bZHQVLVQ,212071
13
- certora_cli/CertoraProver/certoraBuildCacheManager.py,sha256=dzXC8A-V9gr1GGsPYglQLlFLoR2Tk4dTJuMHaxXBfgw,13257
12
+ certora_cli/CertoraProver/certoraBuild.py,sha256=eAAaRcyF16XNjr9Es54cQPutErqjW6j8VUefrGAxumc,212517
13
+ certora_cli/CertoraProver/certoraBuildCacheManager.py,sha256=I60x0ykMFPzciD193cPXihsHh0fdV--UOmNKbIZW6Rc,13238
14
14
  certora_cli/CertoraProver/certoraBuildDataClasses.py,sha256=8tPny9-pasC7CCRKQclaVQ3qcEDNa6EdOUu1ZWh0MZY,14704
15
- certora_cli/CertoraProver/certoraBuildRust.py,sha256=l81mZHGOhfSL8jme6I_O_aAiu3wxdHAg-jPEFQvFFdg,5936
16
- certora_cli/CertoraProver/certoraCloudIO.py,sha256=oYhNAcyhQa97_5OrpFsewcD6sksoKD8aJz_Iw1-ihg8,53980
17
- certora_cli/CertoraProver/certoraCollectConfigurationLayout.py,sha256=waMo5_nzirDaK7aoPCfoS7jOqqi_6huRc-_jTrDcZO8,14252
15
+ certora_cli/CertoraProver/certoraBuildRust.py,sha256=ofKvmtqzUGTrgVm5crcybV7gbElbjiUc6Z4FphPEjz0,6016
16
+ certora_cli/CertoraProver/certoraCloudIO.py,sha256=w3mqA5ANgIX6dsb6Nxg4jl9zBRdAX5kw9xLeYmA-iHc,53062
17
+ certora_cli/CertoraProver/certoraCollectConfigurationLayout.py,sha256=6aHEGrhT_Y9aYfM5n_Mk--awgcQfdtc-chBPRl7FU4E,14095
18
18
  certora_cli/CertoraProver/certoraCollectRunMetadata.py,sha256=n67E7hjVdPlBXMh1FMzcWSgu3v5SfFM_HOO2JpbCeY0,11787
19
19
  certora_cli/CertoraProver/certoraCompilerParameters.py,sha256=r35y03IRwWIoz1GCNC7PuW3n8JPz9J1NGwhwUYKdYtI,1452
20
- certora_cli/CertoraProver/certoraConfigIO.py,sha256=H3ebbIBO8xXTf8ArBPnZV1TmDBExQVDF2zc9Yu1v0Xo,7493
21
- certora_cli/CertoraProver/certoraContext.py,sha256=0AihMYuUAGHWjbiSOMiDM6FVW5OyvEAxE51GPnMxSKg,25821
22
- certora_cli/CertoraProver/certoraContextAttributes.py,sha256=IhzGm46ToCIps-Eg04V90G4OUDcTm0dQqmtWr3rn-N4,69055
20
+ certora_cli/CertoraProver/certoraConfigIO.py,sha256=uq-uNnjVBzg9Kh41fDqn-lnwSJXe4k2_iy5GTnQIDa8,7560
21
+ certora_cli/CertoraProver/certoraContext.py,sha256=HyYQlzIEk3gzzEH2ilTC_Qxw1zXvsKU3IJuCLiV2Sro,26916
22
+ certora_cli/CertoraProver/certoraContextAttributes.py,sha256=d8ywqZrWfhzwS8w3VSduwIqwgSASML7k_guXzsoxIdU,69463
23
23
  certora_cli/CertoraProver/certoraContextClass.py,sha256=d7HDqM72K7YnswR7kEcAHGwkFNrTqRz5-_0m7cl2Mso,900
24
- certora_cli/CertoraProver/certoraContextValidator.py,sha256=iSB2byuHmi1G4xG-ViVvBrZv7e1mdxPmlWdV3AaN_l4,46492
24
+ certora_cli/CertoraProver/certoraContextValidator.py,sha256=SgdtQJFimBi51c9SUnpgEYow9FBEYU_7LUdZhISBxpo,45064
25
25
  certora_cli/CertoraProver/certoraContractFuncs.py,sha256=ipSwge5QQzp8qhUavY44bZ-eCR6szK_HWwSIWqQyuR0,6921
26
26
  certora_cli/CertoraProver/certoraExtensionInfo.py,sha256=YlShzdoqJQgXXj3r0TJ3fir1KntIR99Rk8JN5qii2lk,2026
27
27
  certora_cli/CertoraProver/certoraJobList.py,sha256=FBIYgJ60I0Ok7vchfTbcuJJbiXgnfAhrONoVeZoHti4,11464
28
28
  certora_cli/CertoraProver/certoraMiniSpecParser.py,sha256=NjjMwf5Rav3YWpoOJh4PZ-QOS8exC2cg4yIBSbZA6l0,9660
29
29
  certora_cli/CertoraProver/certoraNodeFilters.py,sha256=5Uk2mixZKeis_JVd3HkLgoEVklkAYBXAZiNHRlXOIfY,2830
30
- certora_cli/CertoraProver/certoraParseBuildScript.py,sha256=V_2lqlDnwdkXJzpdvXiMuOyupdrDTZnOyHKshm-_3rQ,4850
30
+ certora_cli/CertoraProver/certoraParseBuildScript.py,sha256=l7KQA1poEjmbmuYbMskz0jOQg6cW0lM3vk5ruAGPjPI,4863
31
31
  certora_cli/CertoraProver/certoraProjectScanner.py,sha256=jT7FeWzcy8o83LrZRwsg_L4x6im6Fm_0LZFKVbKr3Jk,6862
32
32
  certora_cli/CertoraProver/certoraSourceFinders.py,sha256=qwJtwrQq3NUNYmdmn1UmANN4lmJFIUh4M-St2x1FJ2Y,19038
33
33
  certora_cli/CertoraProver/certoraType.py,sha256=wD-Sr3xk_dJGtbvw33oIGu_lf15NCZuKWjUb4HlVcUM,29318
@@ -36,7 +36,7 @@ certora_cli/CertoraProver/erc7201.py,sha256=BME5kBZsDx6lgqLn7EE91I1cEOZtsnZ8BlRV
36
36
  certora_cli/CertoraProver/splitRules.py,sha256=HfSqsKeeLZDeOnv8TGgpPDHoaXgdVc0HOrLcGk-cU1Q,7681
37
37
  certora_cli/CertoraProver/storageExtension.py,sha256=BZC9HJygeLxqS5TmhNxbIrVNfH3kLEPjN1VjsWQi8s8,15922
38
38
  certora_cli/CertoraProver/Compiler/CompilerCollector.py,sha256=cr-PIl7LY9VfNs4s4H3-EnSnomPiCgXudfwP9-KenMk,6740
39
- certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py,sha256=L-LAH0UU7gB7wYvCcXrwdtLGpBI8MX3rPts0ufQ-X9s,8157
39
+ certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py,sha256=TOpHMSYGhfSmVQwJYZMVOI_Ws03k6cTffzaQUmhnNUo,8761
40
40
  certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py,sha256=7nAY2FLMUlGJn4f_YoZMqpa3rf7THqhJVjLwTaChcBc,5027
41
41
  certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py,sha256=UasYWyu8Of6R84vXsqRNGpscYcFQghmSIY_dyaAWDYA,1350
42
42
  certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py,sha256=e95xOHK5Bz8BbzjbCVLCrGSutVs8ejqOImh5wH9o3Jk,69918
@@ -60,16 +60,16 @@ certora_cli/Shared/ExpectedComparator.py,sha256=eyRR-jni4WJoa6j2TK2lnZ89Tyb8U99w
60
60
  certora_cli/Shared/__init__.py,sha256=s0dhvolFtsS4sRNzPVhC_rlw8mm194rCZ0WhOxInY40,1025
61
61
  certora_cli/Shared/certoraAttrUtil.py,sha256=ZsoS6xbSZnAjEoPEcfiJi6CvHU-1ySBKubvVKh78ohs,8373
62
62
  certora_cli/Shared/certoraLogging.py,sha256=cV2UQMhQ5j8crGXgeq9CEamI-Lk4HgdiA3HCrP-kSR4,14013
63
- certora_cli/Shared/certoraUtils.py,sha256=fWb0-zZMCT_lRvhaPhf8GeXUySW6I0enChv1aOYhoOY,55185
64
- certora_cli/Shared/certoraValidateFuncs.py,sha256=WG4UiyES8u49o3XmuRIvNf79rcpWFuCKtV__QUptOEQ,41852
65
- certora_cli/Shared/proverCommon.py,sha256=-Lw6_zEIlPA7i_n6xOUsmJeUKjqYWAnGW9GKWBB_GC0,11171
63
+ certora_cli/Shared/certoraUtils.py,sha256=5uxgeGyfq8jw4w9l7fxiyMsgw74GpEtS8Wy8u5MNBHs,55178
64
+ certora_cli/Shared/certoraValidateFuncs.py,sha256=Fn6GN_LEwlMjBdTiVO6WzCg8P5F6DhwkvP9Xl-XiQ6o,41921
65
+ certora_cli/Shared/proverCommon.py,sha256=PqkjycZ3TdZr0RNWoPuA_VZ5b7EAAsu9ewymNH6kIm4,11291
66
66
  certora_cli/Shared/rustProverCommon.py,sha256=NIZ5ECbhuiMegyRAl07CV3r68MFG2tBNKgUAQoV4uLI,2049
67
- certora_jars/CERTORA-CLI-VERSION-METADATA.json,sha256=j8bVz9zl2GrWRNLkCyJqxowmkL3ViT7lv_8gGapoAxw,146
68
- certora_jars/Typechecker.jar,sha256=F8N8aedlUn9WQ3KX_3I5QgzSRnuyNa0HmCYj1uWyHMo,17139024
67
+ certora_jars/CERTORA-CLI-VERSION-METADATA.json,sha256=YT2XfCbYZ3cwuV-9FyvRVu5GrruMtCHqT0n1kvOSlGY,145
68
+ certora_jars/Typechecker.jar,sha256=NmnUZaxhNvRthGr1tEh9FVlT0wEO8A-jj9J22gOtngY,17143207
69
69
  certora_jars/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- certora_cli_beta_mirror-7.30.1.dist-info/LICENSE,sha256=UGKSKIJSetF8m906JLKqNLkUS2CL60XfQdNvxBvpQXo,620
71
- certora_cli_beta_mirror-7.30.1.dist-info/METADATA,sha256=CFnS5p5BHGEdNORrYyCT4U27P_260PWpBD6s5UEaUvE,1231
72
- certora_cli_beta_mirror-7.30.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
73
- certora_cli_beta_mirror-7.30.1.dist-info/entry_points.txt,sha256=_SQ5_uYOAJXtqEW992nIvq7blW9cWFSUVEdbMGuy--4,443
74
- certora_cli_beta_mirror-7.30.1.dist-info/top_level.txt,sha256=8C77w3JLanY0-NW45vpJsjRssyCqVP-qmPiN9FjWiX4,38
75
- certora_cli_beta_mirror-7.30.1.dist-info/RECORD,,
70
+ certora_cli_beta_mirror-7.31.0.dist-info/LICENSE,sha256=UGKSKIJSetF8m906JLKqNLkUS2CL60XfQdNvxBvpQXo,620
71
+ certora_cli_beta_mirror-7.31.0.dist-info/METADATA,sha256=OGfFfEjLrMpnJSD69pQjP_yH9-2Jfvh1GviqnGpZTN0,1254
72
+ certora_cli_beta_mirror-7.31.0.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
73
+ certora_cli_beta_mirror-7.31.0.dist-info/entry_points.txt,sha256=_SQ5_uYOAJXtqEW992nIvq7blW9cWFSUVEdbMGuy--4,443
74
+ certora_cli_beta_mirror-7.31.0.dist-info/top_level.txt,sha256=8C77w3JLanY0-NW45vpJsjRssyCqVP-qmPiN9FjWiX4,38
75
+ certora_cli_beta_mirror-7.31.0.dist-info/RECORD,,
@@ -1 +1 @@
1
- {"name": "certora-cli-beta-mirror", "tag": "7.30.1", "branch": "", "commit": "5c9b8e0", "timestamp": "20250604.15.13.217266", "version": "7.30.1"}
1
+ {"name": "certora-cli-beta-mirror", "tag": "7.31.0", "branch": "", "commit": "89159ec", "timestamp": "20250624.9.18.557504", "version": "7.31.0"}
Binary file