certora-cli 7.6.3__tar.gz → 7.10.1__tar.gz

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 (68) hide show
  1. {certora-cli-7.6.3/certora_cli.egg-info → certora_cli-7.10.1}/PKG-INFO +2 -2
  2. certora_cli-7.10.1/README.md +1 -0
  3. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSol.py +1 -1
  4. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraBuild.py +108 -8
  5. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraBuildCacheManager.py +22 -1
  6. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraCloudIO.py +31 -1
  7. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraCollectRunMetadata.py +25 -16
  8. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraConfigIO.py +0 -12
  9. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraContext.py +8 -8
  10. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraContextAttribute.py +35 -15
  11. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraContextValidator.py +23 -12
  12. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/equivCheck.py +1 -1
  13. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Mutate/mutateApp.py +148 -69
  14. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Mutate/mutateAttribute.py +1 -1
  15. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Mutate/mutateConstants.py +4 -3
  16. certora_cli-7.10.1/certora_cli/Mutate/mutateUtil.py +47 -0
  17. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Mutate/mutateValidate.py +19 -4
  18. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Shared/certoraUtils.py +47 -13
  19. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Shared/certoraValidateFuncs.py +16 -2
  20. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/certoraMutate.py +6 -2
  21. certora_cli-7.10.1/certora_cli/certoraRun.py +254 -0
  22. {certora-cli-7.6.3 → certora_cli-7.10.1/certora_cli.egg-info}/PKG-INFO +2 -2
  23. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli.egg-info/SOURCES.txt +1 -0
  24. certora_cli-7.10.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  25. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_jars/Typechecker.jar +0 -0
  26. {certora-cli-7.6.3 → certora_cli-7.10.1}/setup.py +2 -2
  27. certora-cli-7.6.3/README.md +0 -1
  28. certora-cli-7.6.3/certora_cli/certoraRun.py +0 -293
  29. certora-cli-7.6.3/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  30. {certora-cli-7.6.3 → certora_cli-7.10.1}/LICENSE +0 -0
  31. {certora-cli-7.6.3 → certora_cli-7.10.1}/MANIFEST.in +0 -0
  32. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_bins/__init__.py +0 -0
  33. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/CompilerCollector.py +0 -0
  34. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/CompilerCollectorFactory.py +0 -0
  35. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSolBased.py +0 -0
  36. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/CompilerCollectorVy.py +0 -0
  37. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/CompilerCollectorYul.py +0 -0
  38. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/Compiler/__init__.py +0 -0
  39. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/__init__.py +0 -0
  40. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraCompilerParameters.py +0 -0
  41. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraContextClass.py +0 -0
  42. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraContractFuncs.py +0 -0
  43. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraExtensionInfo.py +0 -0
  44. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraJobList.py +0 -0
  45. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraMiniSpecParser.py +0 -0
  46. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraNodeFilters.py +0 -0
  47. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraType.py +0 -0
  48. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EVMVerifier/certoraVerifyGenerator.py +0 -0
  49. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  50. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  51. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  52. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  53. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  54. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  55. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  56. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Mutate/__init__.py +0 -0
  57. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Shared/ExpectedComparator.py +0 -0
  58. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Shared/__init__.py +0 -0
  59. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Shared/certoraAttrUtil.py +0 -0
  60. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/Shared/certoraLogging.py +0 -0
  61. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/__init__.py +0 -0
  62. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli/certoraEqCheck.py +0 -0
  63. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli.egg-info/dependency_links.txt +0 -0
  64. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli.egg-info/entry_points.txt +0 -0
  65. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli.egg-info/requires.txt +0 -0
  66. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_cli.egg-info/top_level.txt +0 -0
  67. {certora-cli-7.6.3 → certora_cli-7.10.1}/certora_jars/__init__.py +0 -0
  68. {certora-cli-7.6.3 → certora_cli-7.10.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: certora-cli
3
- Version: 7.6.3
3
+ Version: 7.10.1
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli
6
6
  Author: Certora
@@ -21,4 +21,4 @@ Requires-Dist: tabulate
21
21
  Requires-Dist: tqdm
22
22
  Requires-Dist: StrEnum
23
23
 
24
- Commit 8d9362c. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
24
+ Commit 3dd11d6. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit 3dd11d6. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -35,7 +35,7 @@ class CompilerLangSol(CompilerLang, metaclass=Singleton):
35
35
 
36
36
  @staticmethod
37
37
  def compilation_output_path(sdc_name: str) -> Path:
38
- return get_certora_config_dir() / f"{sdc_name}.standard.json.stdout"
38
+ return get_certora_config_dir() / f"{sdc_name}.standard.stdout.json"
39
39
 
40
40
  @staticmethod
41
41
  def get_supports_imports() -> bool:
@@ -543,8 +543,79 @@ def generate_full_finder(f: Func, internal_id: int, sym: int,
543
543
  return used_symbols, finder_gen.renaming() + to_ret
544
544
 
545
545
 
546
+ def reprint_ast_type(node: Dict[str, Any]) -> Optional[str]:
547
+ """
548
+ Tries to pretty print the source code that was passed as node. That is, produce the concrete syntax that
549
+ was parsed by the compiler and then represented by node. This can differ from the "canonical" printing, as it
550
+ may not include the contract qualifier, as this is not always in scope.
551
+ """
552
+ if "nodeType" not in node:
553
+ return None
554
+ typ = node["nodeType"]
555
+ if typ == "ArrayTypeName":
556
+ if "baseType" not in node:
557
+ return None
558
+ reprint_base = reprint_ast_type(node["baseType"])
559
+ if reprint_base is None:
560
+ return None
561
+ if "length" in node:
562
+ lnode = node["length"]
563
+ if type(lnode) is not dict or "nodeType" not in lnode or lnode["nodeType"] != "Literal" or \
564
+ "value" not in lnode:
565
+ return None
566
+ len_str = lnode["value"]
567
+ else:
568
+ len_str = ""
569
+ return f"{reprint_base}[{len_str}]"
570
+ elif typ == "UserDefinedTypeName":
571
+ if "pathNode" not in node:
572
+ return None
573
+ p_node = node["pathNode"]
574
+ if type(p_node) is not dict or "nodeType" not in p_node or \
575
+ p_node["nodeType"] != "IdentifierPath" or "name" not in p_node:
576
+ return None
577
+ return p_node["name"]
578
+ else:
579
+ return None
580
+
581
+
582
+ def get_modifier_param_type_name(ind: int, def_node: Dict[str, Any], f: Func) -> str:
583
+ """
584
+ Get the type name to use in the modifier for paramter ind. Attempts to reprint the
585
+ literal source code used in the original contract (via reprint_ast_type) or falls back
586
+ to the canonical type name (which may not be valid syntax, see https://certora.atlassian.net/browse/CERT-6284
587
+ """
588
+ fallback = f.fullArgs[ind].get_source_and_location_str()
589
+ d = def_node.get("parameters", None)
590
+ if type(d) is not dict or d["nodeType"] != "ParameterList" or "parameters" not in d:
591
+ return fallback
592
+ params = d["parameters"]
593
+ if type(params) is not list or len(params) != len(f.fullArgs):
594
+ return fallback
595
+ param_def_node = params[ind]
596
+ if type(param_def_node) is not dict or param_def_node["nodeType"] != "VariableDeclaration" or \
597
+ "typeName" not in param_def_node:
598
+ return fallback
599
+ param_type_node = param_def_node["typeName"]
600
+ if type(param_type_node) is not dict:
601
+ return fallback
602
+ reprint = reprint_ast_type(param_type_node)
603
+ if reprint is None:
604
+ return fallback
605
+ loc_string = ""
606
+ if "storageLocation" in param_def_node:
607
+ if param_def_node["storageLocation"] == "memory":
608
+ loc_string = " memory"
609
+ elif param_def_node["storageLocation"] == "calldata":
610
+ loc_string = " calldata"
611
+ elif param_def_node["storageLocation"] == "storage":
612
+ loc_string = " storage"
613
+ return f"{reprint}{loc_string}"
614
+
615
+
546
616
  def generate_modifier_finder(f: Func, internal_id: int, sym: int,
547
- compiler_collector: CompilerCollectorSol, compress: bool) -> Optional[Tuple[str, str]]:
617
+ compiler_collector: CompilerCollectorSol, def_node: Dict[str, Any],
618
+ compress: bool) -> Optional[Tuple[str, str]]:
548
619
  compressed = generate_finder_body(f, internal_id, sym, compiler_collector, compressed=compress)
549
620
  if compressed is None:
550
621
  return None
@@ -552,13 +623,13 @@ def generate_modifier_finder(f: Func, internal_id: int, sym: int,
552
623
  if len(compressed[0]) == 0:
553
624
  return f"{modifier_name}()", f"modifier {modifier_name}() {{ {compressed[1]} _; }}"
554
625
  else:
555
- loggable_types = map(lambda e: f.fullArgs[e], compressed[0])
626
+ loggable_types = map(lambda e: get_modifier_param_type_name(e, def_node, f), compressed[0])
556
627
  loggable_names = map(lambda e: f.paramNames[e], compressed[0])
557
628
  formal_strings = []
558
629
  arg_strings = []
559
630
  for (logged_ty, logged_name) in zip(loggable_types, loggable_names):
560
631
  arg_strings.append(logged_name)
561
- formal_strings.append(f"{logged_ty.get_source_and_location_str()} {logged_name}")
632
+ formal_strings.append(f"{logged_ty} {logged_name}")
562
633
  modifier_body = f"modifier {modifier_name}"
563
634
  modifier_body += f'({",".join(formal_strings)}) {{ '
564
635
  modifier_body += compressed[1]
@@ -1510,6 +1581,8 @@ class CertoraBuildGenerator:
1510
1581
  self.context.solc_via_ir or self.context.solc_experimental_via_ir):
1511
1582
  # The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
1512
1583
  # full inliner step removed
1584
+ solc0_8_26 = "dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" + \
1585
+ "IulcscCTUtx[scCTUt]TOntnfDIuljmul[jul]VcTOculjmul"
1513
1586
  solc0_8_13_to_0_8_25 = "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]T" + \
1514
1587
  "peulxa[rul]xa[r]cLgvifCTUca[r]LSsTFOtfDnca[r]Iulc]jmul[jul]VcTOculjmul"
1515
1588
  solc0_8_12 = "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]T" + \
@@ -1552,6 +1625,8 @@ class CertoraBuildGenerator:
1552
1625
  yul_optimizer_steps = solc0_8_12
1553
1626
  elif minor == 8 and 13 <= patch <= 25:
1554
1627
  yul_optimizer_steps = solc0_8_13_to_0_8_25
1628
+ elif minor == 8 and patch == 26:
1629
+ yul_optimizer_steps = solc0_8_26
1555
1630
  assert yul_optimizer_steps is not None
1556
1631
 
1557
1632
  yul_optimizer_steps = standard_replacement(yul_optimizer_steps)
@@ -1566,6 +1641,11 @@ class CertoraBuildGenerator:
1566
1641
  raise Util.CertoraUserInputError(f"Unrecognized version {major}.{minor}.{patch}")
1567
1642
  if (minor == 8 and patch >= 5) or (minor > 8):
1568
1643
  settings_dict["optimizer"]["details"] = {"inliner": False}
1644
+ if self.context.disable_solc_optimizers is not None:
1645
+ if "details" not in settings_dict["optimizer"]:
1646
+ settings_dict["optimizer"]["details"] = {}
1647
+ for opt_pass in self.context.disable_solc_optimizers:
1648
+ settings_dict["optimizer"]["details"][opt_pass] = False
1569
1649
 
1570
1650
  def _fill_codegen_related_options(self, contract_file_path: str, settings_dict: Dict[str, Any],
1571
1651
  compiler_collector: CompilerCollector) -> None:
@@ -1863,7 +1943,7 @@ class CertoraBuildGenerator:
1863
1943
  is equivalent to `main_path = self.context.solc_allow_path`, and that's the best we can do in that
1864
1944
  case anyway.
1865
1945
  """
1866
- main_path = compile_wd / self.context.solc_allow_path
1946
+ main_path = (compile_wd / self.context.solc_allow_path).resolve()
1867
1947
  else:
1868
1948
  main_path = self.context.solc_allow_path
1869
1949
  self.context.solc_cmd_allow_paths = [f'"{main_path}"', '.']
@@ -2484,7 +2564,7 @@ class CertoraBuildGenerator:
2484
2564
  # least one modifier already present, and then insert before the first modifier's location in the
2485
2565
  # source code
2486
2566
  mod_inst = generate_modifier_finder(f, internal_id, function_symbol, sdc.compiler_collector,
2487
- compress=should_compress)
2567
+ def_node, compress=should_compress)
2488
2568
  if mod_inst is None:
2489
2569
  instrumentation_logger.debug(f"Modifier generation for {f.name} @ {f.where()} failed")
2490
2570
  return None
@@ -2716,7 +2796,16 @@ class CertoraBuildGenerator:
2716
2796
  sdc_with_verified_contract_name = next(
2717
2797
  curr_sdc for index, curr_sdc in enumerate(sdc_pre_finders) if
2718
2798
  curr_sdc.primary_contract == verified_contract_name)
2719
- self.__verify_method(context.method, sdc_with_verified_contract_name)
2799
+ errs = []
2800
+ for method in context.method:
2801
+ try:
2802
+ self.__verify_method(method, sdc_with_verified_contract_name)
2803
+ except Util.CertoraUserInputError as e:
2804
+ errs.append(e)
2805
+ if len(errs) > 0:
2806
+ raise Util.CertoraUserInputError(
2807
+ f"Some methods as specified via the `method` flag could not be found!{Util.NEW_LINE}{Util.NEW_LINE.join(str(e) for e in errs)}"
2808
+ )
2720
2809
 
2721
2810
  # Build sources tree
2722
2811
  build_logger.debug("Building source tree")
@@ -3041,7 +3130,7 @@ class CertoraBuildGenerator:
3041
3130
  for k, v in autofinder_remappings.items():
3042
3131
  self.function_finder_file_remappings[Util.abs_posix_path(k)] = Util.abs_posix_path(v)
3043
3132
  new_sdcs = self.collect_for_file(new_file, i, get_compiler_lang(build_arg_contract_file),
3044
- Util.get_certora_sources_dir(),
3133
+ Util.get_certora_sources_dir() / self.cwd_rel_in_sources,
3045
3134
  path_for_compiler_collector_file,
3046
3135
  sdc_pre_finder,
3047
3136
  fail_on_compilation_error=False,
@@ -3394,6 +3483,8 @@ class CertoraBuildGenerator:
3394
3483
  add_to_sources(Path(file))
3395
3484
  if context.bytecode_spec:
3396
3485
  sources.add(Path(context.bytecode_spec))
3486
+ if context.yul_abi:
3487
+ sources.add(Path(context.yul_abi))
3397
3488
 
3398
3489
  if hasattr(context,
3399
3490
  Attr.ContextAttribute.PROVER_RESOURCE_FILES.get_conf_key()) and context.prover_resource_files:
@@ -3478,7 +3569,8 @@ def build_from_scratch(certora_build_generator: CertoraBuildGenerator,
3478
3569
  indent=4,
3479
3570
  sort_keys=True)
3480
3571
 
3481
- return CachedFiles(certora_build_file, all_contract_files, build_output_props_file, may_store_in_build_cache)
3572
+ return CachedFiles(certora_build_file, all_contract_files, build_output_props_file, may_store_in_build_cache,
3573
+ path_with_additional_included_files=absolute_sources_dir)
3482
3574
 
3483
3575
 
3484
3576
  def build_from_cache_or_scratch(context: CertoraContext,
@@ -3522,6 +3614,14 @@ def build_from_cache_or_scratch(context: CertoraContext,
3522
3614
  # write build_cache indicator file
3523
3615
  with open(Util.get_build_cache_indicator_file(), "w+") as indicator_handle:
3524
3616
  json.dump({"build_cache_hit": True}, indicator_handle)
3617
+ # write in sources all the additional paths found
3618
+ for p in cached_files.path_with_additional_included_files.glob("*"):
3619
+ if p.is_dir():
3620
+ Util.safe_copy_folder(p,
3621
+ Util.get_certora_sources_dir() / p.name,
3622
+ shutil.ignore_patterns())
3623
+ else:
3624
+ shutil.copyfile(p, Util.get_certora_sources_dir() / p.name)
3525
3625
  cache_hit = True
3526
3626
  else:
3527
3627
  # rebuild
@@ -23,6 +23,9 @@ class CachedFiles:
23
23
  build_output_props_file: Path
24
24
  may_store_in_build_cache: bool
25
25
 
26
+ # to keep .post_autofinders
27
+ path_with_additional_included_files: Path
28
+
26
29
  def all_exist(self, main_cache_entry_dir: Path, sub_cache_key: str) -> bool:
27
30
  exist = self.certora_build_file.exists() and \
28
31
  self.certora_build_file == main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.certora_build_suffix}"
@@ -36,6 +39,7 @@ class CachedFiles:
36
39
  certora_build_suffix = "certora_build.json"
37
40
  file_list_suffix = "file_list.json"
38
41
  build_output_props_suffix = "build_output_props.json"
42
+ additional_included_files = "additional_included_files"
39
43
 
40
44
 
41
45
  class CertoraBuildCacheManager:
@@ -105,8 +109,9 @@ class CertoraBuildCacheManager:
105
109
  f"but {CachedFiles.build_output_props_suffix} file does not exist")
106
110
  return None
107
111
 
112
+ additional_included_files = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.additional_included_files}"
108
113
  return CachedFiles(certora_build_file, all_contract_files, build_output_props_file,
109
- may_store_in_build_cache=True)
114
+ may_store_in_build_cache=True, path_with_additional_included_files=additional_included_files)
110
115
 
111
116
  @staticmethod
112
117
  def save_build_cache(context: CertoraContext, cached_files: CachedFiles) -> None:
@@ -151,6 +156,22 @@ class CertoraBuildCacheManager:
151
156
  # save additional props file
152
157
  shutil.copyfile(cached_files.build_output_props_file,
153
158
  main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.build_output_props_suffix}")
159
+ # save .post_autofinders from additional included files
160
+ trg_path_with_additional_included_files = \
161
+ main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.additional_included_files}"
162
+ post_autofinder_dirs = list(
163
+ cached_files.path_with_additional_included_files.glob(f"{Util.POST_AUTOFINDER_BACKUP_DIR}.*"))
164
+ for post_autofinder_dir in post_autofinder_dirs:
165
+ trg = trg_path_with_additional_included_files / post_autofinder_dir.name
166
+ if post_autofinder_dir != trg:
167
+ if post_autofinder_dir.is_dir():
168
+ Util.safe_copy_folder(post_autofinder_dir, trg, shutil.ignore_patterns())
169
+ else:
170
+ # highly unlikely .post_autofinder.[digit] will be a file and not a directory,
171
+ # but would rather not crash and future-proof instead
172
+ # (more likely, we may include other types of files and not just autofinder directories, in which
173
+ # case we'll change the variable name accordingly)
174
+ shutil.copyfile(post_autofinder_dir, trg)
154
175
 
155
176
  @staticmethod
156
177
  def get_main_cache_key(context: CertoraContext) -> str:
@@ -21,6 +21,7 @@ from Shared import certoraUtils as Util
21
21
  import EVMVerifier.certoraContextAttribute as Attr
22
22
  import EVMVerifier.certoraContext as Ctx
23
23
  from Shared import certoraValidateFuncs as Vf
24
+ from Mutate import mutateUtil as MutUtil
24
25
 
25
26
  from typing import Optional, Dict, Any, List, Union, cast
26
27
  from tqdm import tqdm
@@ -560,6 +561,7 @@ class CloudVerification:
560
561
  auth_data = {
561
562
  "process": self.context.process, "runName": self.runName,
562
563
  "run_source": self.context.run_source,
564
+ "group_id": self.context.group_id,
563
565
  "branch": self.context.prover_version if self.context.prover_version else '',
564
566
  'ci': Util.is_ci_or_git_action()
565
567
  }
@@ -595,6 +597,15 @@ class CloudVerification:
595
597
  elif arg == '-solanaSummaries':
596
598
  is_file = True
597
599
  auth_data["jarSettings"] = solana_jar_settings
600
+ elif self.context.is_wasm:
601
+ # We need to strip "../" path component from all file paths because
602
+ # unzip will also do that.
603
+ wasm_jar_settings = []
604
+ for file in self.context.files:
605
+ wasm_jar_settings.append(file.split('../')[-1])
606
+ for arg in jar_settings:
607
+ wasm_jar_settings.append(arg)
608
+ auth_data["jarSettings"] = wasm_jar_settings
598
609
  else:
599
610
  auth_data["jarSettings"] = jar_settings
600
611
 
@@ -728,6 +739,12 @@ class CloudVerification:
728
739
  elif arg == '-solanaSummaries':
729
740
  is_file = True
730
741
  result = compress_files(self.ZipFilePath, *paths, short_output=Ctx.is_minimal_cli_output(self.context))
742
+ elif self.context.is_wasm:
743
+ # We zip the wat file
744
+ paths = []
745
+ for file in self.context.files:
746
+ paths.append(Path(file))
747
+ result = compress_files(self.ZipFilePath, *paths, short_output=Ctx.is_minimal_cli_output(self.context))
731
748
  else:
732
749
  # Zip the log file first separately and again with the rest of the files, so it will not be decompressed
733
750
  # on each run in order to save space
@@ -748,7 +765,17 @@ class CloudVerification:
748
765
 
749
766
  cloud_logger.debug("Uploading files...")
750
767
  if self.upload(self.presigned_url, self.ZipFilePath):
751
- Util.print_completion_message("Job submitted to server")
768
+ try:
769
+ if self.context.mutation_test_id:
770
+ counter = MutUtil.mutant_counter(self.context.mutation_test_id)
771
+ total = MutUtil.TOTAL_MUTANTS
772
+ Util.print_completion_message(f"Mutation {counter}/{total} submitted to server")
773
+ else:
774
+ Util.print_completion_message("Job submitted to server")
775
+
776
+ except Exception:
777
+ Util.print_completion_message("Job submitted to server")
778
+
752
779
  print()
753
780
  else: # upload error
754
781
  return False
@@ -881,6 +908,9 @@ class CloudVerification:
881
908
  print("Please note - failure may be related to using the server"
882
909
  f" <{self.context.server.lower()}> which is not a standard server")
883
910
  return None
911
+ elif status == 401:
912
+ print("Invalid key or key is missing. Please, make sure you entered a valid key.")
913
+ return None
884
914
  elif status >= 500:
885
915
  cloud_logger.debug(f'{status} received. Retry...')
886
916
  if i < self.verification_request_retries:
@@ -45,7 +45,7 @@ class RunMetaData:
45
45
  """
46
46
  def __init__(self, raw_args: List[str], conf: Dict[str, Any], origin: str, revision: str,
47
47
  branch: str, cwd_relative: Path, dirty: bool, main_spec: Optional[str],
48
- conf_path: Optional[Path]):
48
+ conf_path: Optional[Path], group_id: Optional[str]):
49
49
  self.raw_args = raw_args
50
50
  self.conf = conf
51
51
  self.origin = origin
@@ -55,22 +55,27 @@ class RunMetaData:
55
55
  self.dirty = dirty
56
56
  self.main_spec = main_spec
57
57
  self.conf_path = conf_path
58
+ self.group_id = group_id
59
+ self.python_version = ".".join(str(x) for x in sys.version_info[:3])
58
60
  self.certora_ci_client = Utils.get_certora_ci_name()
59
61
  self.timestamp = str(datetime.utcnow().timestamp())
60
- _, _, self.CLI_version = Utils.get_package_and_version()
62
+ _, self.CLI_package_name, self.CLI_version = Utils.get_package_and_version()
61
63
 
62
64
  def __repr__(self) -> str:
63
65
  return (
64
- f" raw_args: {self.raw_args}\n" +
65
- f" conf: {self.conf}\n" +
66
- f" origin: {self.origin}\n" +
67
- f" revision: {self.revision}\n" +
68
- f" branch: {self.branch}\n" +
69
- f" cwd_relative: {self.cwd_relative}\n" +
70
- f" dirty: {self.dirty}\n" +
71
- f" CLI_version: {self.CLI_version}\n" +
72
- f" main_spec: {self.main_spec}\n" +
73
- f" conf_path: {self.conf_path}\n" +
66
+ f" raw_args: {self.raw_args}\n"
67
+ f" conf: {self.conf}\n"
68
+ f" origin: {self.origin}\n"
69
+ f" revision: {self.revision}\n"
70
+ f" branch: {self.branch}\n"
71
+ f" cwd_relative: {self.cwd_relative}\n"
72
+ f" dirty: {self.dirty}\n"
73
+ f" CLI_version: {self.CLI_version}\n"
74
+ f" CLI_package_name: {self.CLI_package_name}\n"
75
+ f" main_spec: {self.main_spec}\n"
76
+ f" conf_path: {self.conf_path}\n"
77
+ f" group_id: {self.group_id}\n"
78
+ f" python_version: {self.python_version}\n"
74
79
  f" CertoraCI client: {self.certora_ci_client}\n"
75
80
  )
76
81
 
@@ -137,7 +142,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], raw_input_conf_dict: Dic
137
142
  cwd_relative=wd,
138
143
  dirty=True,
139
144
  main_spec=None,
140
- conf_path=None)
145
+ conf_path=None,
146
+ group_id=None)
141
147
 
142
148
  # collect information about current git snapshot
143
149
  cwd_abs = wd.resolve()
@@ -164,7 +170,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], raw_input_conf_dict: Dic
164
170
  cwd_relative=cwd_relative,
165
171
  dirty=True,
166
172
  main_spec=context.main_spec,
167
- conf_path=conf_path)
173
+ conf_path=conf_path,
174
+ group_id=context.group_id)
168
175
 
169
176
  try:
170
177
  sha_out = subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=wd,
@@ -198,7 +205,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], raw_input_conf_dict: Dic
198
205
  cwd_relative=cwd_relative,
199
206
  dirty=dirty,
200
207
  main_spec=context.main_spec,
201
- conf_path=conf_path)
208
+ conf_path=conf_path,
209
+ group_id=context.group_id)
202
210
 
203
211
  metadata_logger.debug(f' collected data:\n{str(data)}')
204
212
 
@@ -215,4 +223,5 @@ def collect_run_metadata(wd: Path, raw_args: List[str], raw_input_conf_dict: Dic
215
223
  cwd_relative=cwd_relative,
216
224
  dirty=True,
217
225
  main_spec=context.main_spec,
218
- conf_path=conf_path)
226
+ conf_path=conf_path,
227
+ group_id=context.group_id)
@@ -1,6 +1,5 @@
1
1
  import json5
2
2
  import logging
3
- import sys
4
3
  from pathlib import Path
5
4
  from typing import Dict, Any, Optional
6
5
 
@@ -43,17 +42,6 @@ def current_conf_to_file(context: CertoraContext) -> Dict[str, Any]:
43
42
  run_logger.debug(f"Saving last configuration file to {out_file_path}")
44
43
  Ctx.write_output_conf_to_path(context_to_save, out_file_path)
45
44
 
46
- # for dumping the conf file and exit user can either call with --conf_output_file or by setting the
47
- # environment variable CERTORA_DUMP_CONFIG. Using CERTORA_DUMP_CONFIG let the user change the conf file path
48
- # without tempering with .sh files.
49
- # NOTE: if you want to run multiple CVT instances simultaneously,
50
- # you should use consider the --conf_output_file flag and not CERTORA_DUMP_CONFIG.
51
-
52
- conf_key = Attr.ContextAttribute.CONF_OUTPUT_FILE.get_conf_key()
53
- conf_output_file = getattr(context, conf_key, None) or Util.get_certora_dump_config()
54
- if conf_output_file:
55
- Ctx.write_output_conf_to_path(context_to_save, Path(conf_output_file))
56
- sys.exit(0)
57
45
  return context_to_save
58
46
 
59
47
 
@@ -114,7 +114,9 @@ def get_typechecker_arguments(context: CertoraContext, typechecker_args: Optiona
114
114
  typecheck_cmd = f"{typecheck_cmd} {' '.join(typechecker_args)}"
115
115
 
116
116
  relevant_args = filter(lambda arg: get_attr_value(context, arg),
117
- [Attr.ContextAttribute.RULE, Attr.ContextAttribute.EXCLUDE_RULE])
117
+ [Attr.ContextAttribute.RULE,
118
+ Attr.ContextAttribute.EXCLUDE_RULE,
119
+ Attr.ContextAttribute.FOUNDRY_TESTS_MODE])
118
120
  args_list = create_jar_flag_list(context, list(relevant_args))
119
121
 
120
122
  return f"{typecheck_cmd} {' '.join(args_list)}"
@@ -127,7 +129,7 @@ def get_local_run_cmd(context: CertoraContext) -> str:
127
129
  @return: A command for running the prover locally
128
130
  """
129
131
  run_args = []
130
- if context.is_tac or context.is_solana:
132
+ if context.is_tac or context.is_solana or context.is_wasm:
131
133
  run_args.append(context.files[0])
132
134
  if context.cache is not None:
133
135
  run_args.extend(['-cache', context.cache])
@@ -140,7 +142,7 @@ def get_local_run_cmd(context: CertoraContext) -> str:
140
142
  jar_path = context.jar
141
143
  else:
142
144
  certora_root_dir = Util.get_certora_root_directory().as_posix()
143
- jar_path = f"{certora_root_dir}/emv.jar"
145
+ jar_path = f"{certora_root_dir}/{Util.EMV_JAR}"
144
146
 
145
147
  if context.allow_solidity_calls_in_quantifiers:
146
148
  run_args.extend(['-allowSolidityCallsInQuantifiers true'])
@@ -184,7 +186,7 @@ class ProverParser(AttrUtil.CertoraArgumentParser):
184
186
 
185
187
  Attr.ContextAttribute.print_attr_help()
186
188
  console.print("\n\nYou can find detailed documentation of the supported flags here: "
187
- f"[link={CLI_DOCUMENTATION_URL}]{CLI_DOCUMENTATION_URL}[/link]\n\n")
189
+ f"{Util.print_rich_link(CLI_DOCUMENTATION_URL)}\n\n")
188
190
 
189
191
  return ''
190
192
 
@@ -263,10 +265,8 @@ def get_args(args_list: Optional[List[str]] = None) -> Tuple[CertoraContext, Dic
263
265
  setup_cache(context) # Here context.cache, context.user_defined_cache are set
264
266
 
265
267
  # Setup defaults (defaults are not recorded in conf file)
266
- if context.expected_file is None:
267
- context.expected_file = "expected.json"
268
- if context.run_source is None:
269
- context.run_source = Vf.RunSources.COMMAND.name.upper()
268
+ context.expected_file = context.expected_file or "expected.json"
269
+ context.run_source = context.run_source or Vf.RunSources.COMMAND.name.upper()
270
270
 
271
271
  context_logger.debug("parsed args successfully.")
272
272
  context_logger.debug(f"args= {context}")
@@ -254,7 +254,8 @@ class ContextAttribute(AttrUtil.BaseAttribute):
254
254
 
255
255
  MULTI_EXAMPLE = CertoraArgument(
256
256
  attr_validation_func=Vf.validate_multi_example_value,
257
- help_msg="Show several examples for failed 'assert' or verified 'satisfy' statements",
257
+ help_msg="Show several counter examples for failed `assert` statements "
258
+ "and several witnesses for verified `satisfy` statements",
258
259
  jar_flag='-multipleCEX',
259
260
  default_desc="Shows a single example",
260
261
  argparse_args={
@@ -269,8 +270,8 @@ class ContextAttribute(AttrUtil.BaseAttribute):
269
270
 
270
271
  FUNCTION_FINDER_MODE = CertoraArgument(
271
272
  attr_validation_func=Vf.validate_function_finder_mode,
272
- help_msg="Use 'relaxed' mode to increase internal function finders precision, "
273
- "but may cause 'stack too deep' errors unless using 'via-ir'",
273
+ help_msg="Use `relaxed` mode to increase internal function finders precision, "
274
+ "but may cause `stack too deep` errors unless using `via-ir`",
274
275
  default_desc="Takes less stack space but internal functions may be missed",
275
276
  argparse_args={
276
277
  'nargs': AttrUtil.SINGLE_OR_NONE_OCCURRENCES,
@@ -588,6 +589,16 @@ class ContextAttribute(AttrUtil.BaseAttribute):
588
589
  disables_build_cache=False
589
590
  )
590
591
 
592
+ DISABLE_SOLC_OPTIMIZERS = CertoraArgument(
593
+ arg_type=AttrUtil.AttrArgType.LIST,
594
+ argparse_args={
595
+ 'nargs': AttrUtil.ONE_OR_MORE_OCCURRENCES,
596
+ 'action': AttrUtil.APPEND
597
+ },
598
+ affects_build_cache_key=True,
599
+ disables_build_cache=False
600
+ )
601
+
591
602
  YUL_ABI = CertoraArgument(
592
603
  attr_validation_func=Vf.validate_json_file,
593
604
  help_msg="An auxiliary ABI file for yul contracts",
@@ -715,10 +726,12 @@ class ContextAttribute(AttrUtil.BaseAttribute):
715
726
 
716
727
  METHOD = CertoraArgument(
717
728
  jar_flag='-method',
729
+ arg_type=AttrUtil.AttrArgType.LIST,
718
730
  help_msg="Filter methods to be verified by their signature",
719
731
  default_desc="Verifies all public or external methods. In invariants pure and view functions are ignored",
720
732
  argparse_args={
721
- 'action': AttrUtil.UniqueStore
733
+ 'nargs': AttrUtil.ONE_OR_MORE_OCCURRENCES,
734
+ 'action': AttrUtil.APPEND
722
735
  },
723
736
  affects_build_cache_key=False,
724
737
  disables_build_cache=False
@@ -866,7 +879,7 @@ class ContextAttribute(AttrUtil.BaseAttribute):
866
879
  NONDET_MINIMAL_DIFFICULTY = CertoraArgument(
867
880
  attr_validation_func=Vf.validate_non_negative_integer,
868
881
  jar_flag='-autoNondetMinimalDifficulty',
869
- help_msg="Set the minimal 'difficulty' threshold for summarization by `nondet_difficult_funcs`",
882
+ help_msg="Set the minimal `difficulty` threshold for summarization by `nondet_difficult_funcs`",
870
883
  default_desc="50",
871
884
  argparse_args={
872
885
  'action': AttrUtil.UniqueStore
@@ -1172,15 +1185,6 @@ class ContextAttribute(AttrUtil.BaseAttribute):
1172
1185
  disables_build_cache=False
1173
1186
  )
1174
1187
 
1175
- CONF_OUTPUT_FILE = CertoraArgument(
1176
- flag='--conf_output_file',
1177
- argparse_args={
1178
- 'action': AttrUtil.UniqueStore
1179
- },
1180
- affects_build_cache_key=False,
1181
- disables_build_cache=False
1182
- )
1183
-
1184
1188
  RUN_SOURCE = CertoraArgument(
1185
1189
  attr_validation_func=Vf.validate_run_source,
1186
1190
  argparse_args={
@@ -1404,6 +1408,22 @@ class ContextAttribute(AttrUtil.BaseAttribute):
1404
1408
  disables_build_cache=True
1405
1409
  )
1406
1410
 
1411
+ GROUP_ID = CertoraArgument(
1412
+ attr_validation_func=Vf.validate_uuid,
1413
+ affects_build_cache_key=False,
1414
+ disables_build_cache=False
1415
+ )
1416
+
1417
+ FOUNDRY_TESTS_MODE = CertoraArgument(
1418
+ arg_type=AttrUtil.AttrArgType.BOOLEAN,
1419
+ jar_flag='-foundry',
1420
+ argparse_args={
1421
+ 'action': AttrUtil.STORE_TRUE
1422
+ },
1423
+ affects_build_cache_key=False,
1424
+ disables_build_cache=False
1425
+ )
1426
+
1407
1427
  def validate_value(self, value: str, cli_flag: bool = True) -> None:
1408
1428
  if self.value.attr_validation_func is not None:
1409
1429
  try:
@@ -1426,4 +1446,4 @@ class ContextAttribute(AttrUtil.BaseAttribute):
1426
1446
 
1427
1447
  @lru_cache(maxsize=1, typed=False)
1428
1448
  def all_context_keys() -> List[str]:
1429
- return [attr.get_conf_key() for attr in ContextAttribute if attr is not ContextAttribute.CONF_OUTPUT_FILE]
1449
+ return [attr.get_conf_key() for attr in ContextAttribute]