certora-cli 7.25.3__tar.gz → 7.28.0__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 (78) hide show
  1. {certora-cli-7.25.3 → certora-cli-7.28.0}/LICENSE +1 -1
  2. {certora-cli-7.25.3/certora_cli.egg-info → certora-cli-7.28.0}/PKG-INFO +2 -2
  3. certora-cli-7.28.0/README.md +1 -0
  4. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +6 -3
  5. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuild.py +173 -28
  6. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildDataClasses.py +14 -2
  7. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildRust.py +34 -16
  8. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraCloudIO.py +6 -5
  9. certora-cli-7.28.0/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +242 -0
  10. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +10 -0
  11. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraConfigIO.py +52 -16
  12. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContext.py +1 -1
  13. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContextAttributes.py +147 -51
  14. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContextValidator.py +67 -45
  15. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraParseBuildScript.py +8 -2
  16. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/equivCheck.py +5 -4
  17. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateApp.py +21 -12
  18. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateConstants.py +0 -1
  19. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraAttrUtil.py +4 -2
  20. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraUtils.py +23 -7
  21. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraValidateFuncs.py +31 -14
  22. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraRun.py +8 -1
  23. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraSolanaProver.py +8 -1
  24. {certora-cli-7.25.3 → certora-cli-7.28.0/certora_cli.egg-info}/PKG-INFO +2 -2
  25. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/SOURCES.txt +1 -0
  26. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/requires.txt +0 -1
  27. certora-cli-7.28.0/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  28. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_jars/Typechecker.jar +0 -0
  29. {certora-cli-7.25.3 → certora-cli-7.28.0}/setup.py +3 -3
  30. certora-cli-7.25.3/README.md +0 -1
  31. certora-cli-7.25.3/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  32. {certora-cli-7.25.3 → certora-cli-7.28.0}/MANIFEST.in +0 -0
  33. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_bins/__init__.py +0 -0
  34. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
  35. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
  36. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
  37. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
  38. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
  39. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
  40. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/__init__.py +0 -0
  41. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildCacheManager.py +0 -0
  42. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
  43. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
  44. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
  45. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
  46. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraJobList.py +0 -0
  47. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
  48. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
  49. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
  50. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
  51. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraType.py +0 -0
  52. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
  53. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/splitRules.py +0 -0
  54. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  55. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  56. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  57. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  58. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  59. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  60. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  61. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/__init__.py +0 -0
  62. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateAttributes.py +0 -0
  63. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateUtil.py +0 -0
  64. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateValidate.py +0 -0
  65. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/ExpectedComparator.py +0 -0
  66. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/__init__.py +0 -0
  67. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraLogging.py +0 -0
  68. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/__init__.py +0 -0
  69. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraEVMProver.py +0 -0
  70. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraEqCheck.py +0 -0
  71. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraMutate.py +0 -0
  72. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraSorobanProver.py +0 -0
  73. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/rustMutator.py +0 -0
  74. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/dependency_links.txt +0 -0
  75. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/entry_points.txt +0 -0
  76. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/top_level.txt +0 -0
  77. {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_jars/__init__.py +0 -0
  78. {certora-cli-7.25.3 → certora-cli-7.28.0}/setup.cfg +0 -0
@@ -1,7 +1,7 @@
1
1
 
2
2
  MIT License
3
3
 
4
- Copyright (c) 2020 Certora
4
+ Copyright (c) 2025 Certora
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: certora-cli
3
- Version: 7.25.3
3
+ Version: 7.28.0
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli
6
6
  Author: Certora
@@ -12,4 +12,4 @@ Requires-Python: >=3.8
12
12
  Description-Content-Type: text/markdown
13
13
  License-File: LICENSE
14
14
 
15
- Commit 97ac73d. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
15
+ Commit 489a4d6. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit 489a4d6. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -39,12 +39,15 @@ def get_relevant_compiler(contract_file_path: Path, context: CertoraContext) ->
39
39
  @return: the name of the compiler executable we want to run on this file (as a string, could be a path
40
40
  or a resolvable executable name)
41
41
  """
42
- if context.solc_map:
43
- match = match_path_to_mapping_key(contract_file_path, context.solc_map)
42
+
43
+ if contract_file_path.is_absolute():
44
+ contract_file_path = Path(os.path.relpath(contract_file_path, Path.cwd()))
45
+ if context.compiler_map:
46
+ match = match_path_to_mapping_key(contract_file_path, context.compiler_map)
44
47
  if match:
45
48
  return match
46
49
  else:
47
- raise RuntimeError(f'cannot match compiler to {contract_file_path} from solc_map')
50
+ raise RuntimeError(f'cannot match compiler to {contract_file_path} from compiler_map')
48
51
 
49
52
  if Util.is_vyper_file(str(contract_file_path)):
50
53
  if context.vyper:
@@ -21,7 +21,7 @@ import re
21
21
  import shutil
22
22
  import sys
23
23
  import typing
24
- from collections import OrderedDict
24
+ from collections import OrderedDict, defaultdict
25
25
  from enum import Enum
26
26
  from functools import lru_cache
27
27
  from pathlib import Path
@@ -825,7 +825,7 @@ class CertoraBuildGenerator:
825
825
  ast_logger.debug(f"No body for {func_def} but ast claims it is implemented")
826
826
 
827
827
  if original_contract is not None:
828
- if method := original_contract.find_method(func_name):
828
+ if method := original_contract.find_method(func_name, solidity_type_args):
829
829
  source_bytes = method.source_bytes
830
830
  original_file = method.original_file
831
831
  else:
@@ -1389,7 +1389,7 @@ class CertoraBuildGenerator:
1389
1389
  if not self.context.strict_solc_optimizer and self.context.solc_via_ir:
1390
1390
  # The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
1391
1391
  # full inliner step removed
1392
- solc0_8_26_to_0_8_28 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
1392
+ solc0_8_26_to_0_8_29 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
1393
1393
  "IulcscCTUtx[scCTUt]TOntnfDIuljmul[jul]VcTOculjmul")
1394
1394
  solc0_8_13_to_0_8_25 = "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]T" + \
1395
1395
  "peulxa[rul]xa[r]cLgvifCTUca[r]LSsTFOtfDnca[r]Iulc]jmul[jul]VcTOculjmul"
@@ -1433,8 +1433,8 @@ class CertoraBuildGenerator:
1433
1433
  yul_optimizer_steps = solc0_8_12
1434
1434
  elif minor == 8 and 13 <= patch <= 25:
1435
1435
  yul_optimizer_steps = solc0_8_13_to_0_8_25
1436
- elif minor == 8 and 26 <= patch <= 28:
1437
- yul_optimizer_steps = solc0_8_26_to_0_8_28
1436
+ elif minor == 8 and 26 <= patch <= 29:
1437
+ yul_optimizer_steps = solc0_8_26_to_0_8_29
1438
1438
  assert yul_optimizer_steps is not None, \
1439
1439
  'Yul Optimizer steps missing for requested Solidity version. Please contact Certora team.'
1440
1440
 
@@ -2652,6 +2652,158 @@ class CertoraBuildGenerator:
2652
2652
  self.handle_links()
2653
2653
  self.handle_struct_links()
2654
2654
  self.handle_contract_extensions()
2655
+ self.handle_storage_extension_harnesses()
2656
+
2657
+ def handle_storage_extension_harnesses(self) -> None:
2658
+ def new_field_of_node(ext_instance: Any, node: Dict[str, Any]) -> Optional[Dict[str, Any]]:
2659
+ """
2660
+ If this node is a storage variable declaration like the following:
2661
+
2662
+ /** @custom:certoralink 0x12345 */
2663
+ T x;
2664
+
2665
+ and if so, return a storage field matching the type of x but using 0x12345 as the slot
2666
+ """
2667
+
2668
+ if node.get("nodeType") != "VariableDeclaration":
2669
+ return None
2670
+ if not node.get("stateVariable"):
2671
+ return None
2672
+ if "documentation" not in node:
2673
+ return None
2674
+ if node["documentation"].get("nodeType") != "StructuredDocumentation":
2675
+ return None
2676
+ annot_link = node["documentation"].get("text")
2677
+ var_name = node.get("name")
2678
+ if var_name is None or not isinstance(annot_link, str):
2679
+ return None
2680
+
2681
+ link_regex = r'@custom:certoralink\s+(0x[0-9a-fA-F]+)\s*'
2682
+ link_match = re.search(link_regex, annot_link)
2683
+
2684
+ if link_match is None:
2685
+ raise Util.CertoraUserInputError(f"Couldn't parse annotation in on "
2686
+ f"{ext_instance.original_file_name}.{var_name}: {annot_link}")
2687
+ try:
2688
+ link_slot = int(link_match.group(1), 16)
2689
+ except ValueError:
2690
+ raise Util.CertoraUserInputError(f"Invalid certoralink slot value in annotation on"
2691
+ f"{ext_instance.original_file_name}.{var_name}: {annot_link}")
2692
+ storage_field_info = None
2693
+ for field in ext_instance.storage_layout["storage"]:
2694
+ if field["label"] == var_name:
2695
+ storage_field_info = field
2696
+ break
2697
+ if storage_field_info is None:
2698
+ return None
2699
+ cloned_field = storage_field_info.copy()
2700
+ cloned_field["slot"] = str(link_slot)
2701
+ # Try to uniquify the name
2702
+ cloned_field["label"] = f"certoralink_{ext_instance.name}_{var_name}"
2703
+ return cloned_field
2704
+
2705
+ def handle_one_extension(storage_ext: str) -> tuple[Any, str, List[Dict[str, Any]]] :
2706
+ """
2707
+ Get the extension contract plus the new fields
2708
+ """
2709
+ extension_sdc_cands = self.get_matching_sdc_names_from_SDCs(storage_ext)
2710
+ assert len(extension_sdc_cands) == 1, f"Zero or multiple possible contracts for {storage_ext}"
2711
+ extension_sdc_name = extension_sdc_cands[0]
2712
+ extension_sdc = self.SDCs.get(extension_sdc_name)
2713
+ assert extension_sdc and extension_sdc.primary_contract == storage_ext, f"Could not find SDC for {extension_sdc_name}"
2714
+ ext_instance = extension_sdc.find_contract(storage_ext)
2715
+ assert ext_instance is not None, f"Could not find extension contract {storage_ext}"
2716
+
2717
+ # let's try to find the AST now
2718
+ build_file_ast = None
2719
+ for (k, v) in self.asts.items():
2720
+ if ext_instance.original_file in v:
2721
+ build_file_ast = k
2722
+ break
2723
+ if build_file_ast is None:
2724
+ raise RuntimeError(f"Couldn't find file for storage extension file {storage_ext}")
2725
+
2726
+ contract_def_node = self.get_contract_def_node_ref(
2727
+ build_file_ast,
2728
+ ext_instance.original_file,
2729
+ storage_ext
2730
+ )
2731
+ def_node = self.asts[build_file_ast][ext_instance.original_file][contract_def_node]
2732
+ nodes = def_node.get("nodes")
2733
+ if not isinstance(nodes, list):
2734
+ raise RuntimeError(f"Couldn't find nodes for body of {storage_ext}")
2735
+
2736
+ new_fields = []
2737
+ for node in nodes:
2738
+ new_field = new_field_of_node(ext_instance, node)
2739
+ if new_field is not None:
2740
+ new_fields.append(new_field)
2741
+
2742
+ return (ext_instance, extension_sdc_name, new_fields)
2743
+
2744
+ def apply_extensions(target_contract: Any, extensions: Set[str], to_add: Dict[str, Tuple[List[Dict[str, Any]], Dict[str, Any]]]) -> None:
2745
+ """
2746
+ Apply the fields from each extension to the target contract,
2747
+ """
2748
+ if target_contract.storage_layout.get("storage") is None:
2749
+ target_contract.storage_layout["storage"] = []
2750
+ if target_contract.storage_layout.get("types") is None:
2751
+ target_contract.storage_layout["types"] = {}
2752
+ target_slots = {storage["slot"] for storage in target_contract.storage_layout["storage"]}
2753
+ # Keep track of slots we've added, and error if we
2754
+ # find two extensions extending the same slot
2755
+ added_slots: Dict[str, str] = {}
2756
+ for ext in extensions:
2757
+ (new_fields, new_types) = to_add[ext]
2758
+
2759
+ for f in new_fields:
2760
+ # See if any of the new fields is a slot we've already added
2761
+ slot = f["slot"]
2762
+ if slot in added_slots:
2763
+ seen = added_slots[slot]
2764
+ raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} already added by {seen}")
2765
+
2766
+ if slot in target_slots:
2767
+ raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} already mapped by {target_contract.name}")
2768
+
2769
+ added_slots[slot] = ext
2770
+
2771
+ target_contract.storage_layout["storage"].extend(new_fields)
2772
+
2773
+ for (new_id, new_ty) in new_types.items():
2774
+ if new_id in target_contract.storage_layout["types"]:
2775
+ continue
2776
+ target_contract.storage_layout["types"][new_id] = new_ty
2777
+
2778
+ extension_contracts: Set[str] = set()
2779
+ storage_extensions: Dict[str, Set[str]] = defaultdict(set)
2780
+ storage_ext = self.context.storage_extension_harnesses
2781
+
2782
+ if storage_ext is None:
2783
+ return
2784
+
2785
+ for i in storage_ext:
2786
+ target_contract, extension_contract = i.split("=")
2787
+ extension_contracts.add(extension_contract)
2788
+ storage_extensions[target_contract].add(extension_contract)
2789
+
2790
+ # These are the new fields to add for each contract
2791
+ extension_to_fields_and_types: Dict[str, Tuple[List[Dict[str, Any]], Dict[str, Any]]] = {}
2792
+
2793
+ for storage_ext in extension_contracts:
2794
+ (ext_instance, extension_sdc_name, new_fields) = handle_one_extension(storage_ext)
2795
+ new_types = ext_instance.storage_layout["types"]
2796
+ extension_to_fields_and_types[storage_ext] = (new_fields, {} if new_types is None else new_types)
2797
+ del self.SDCs[extension_sdc_name]
2798
+
2799
+ for (target, extensions) in storage_extensions.items():
2800
+ target_sdc_names = self.get_matching_sdc_names_from_SDCs(target)
2801
+ assert len(target_sdc_names) == 1, f"no matching sdc for {target}"
2802
+ target_sdc = target_sdc_names[0]
2803
+ sdc = self.SDCs[target_sdc]
2804
+ target_contract = sdc.find_contract(target)
2805
+ assert target_contract is not None, f"could not find contract for {target}"
2806
+ apply_extensions(target_contract, extensions, extension_to_fields_and_types)
2655
2807
 
2656
2808
  def finders_compilation_round(self,
2657
2809
  build_arg_contract_file: str,
@@ -2985,7 +3137,7 @@ class CertoraBuildGenerator:
2985
3137
  f"for contract extension")
2986
3138
  extension_contract = contracts_by_name[extension]
2987
3139
  for f in ext["exclude"]:
2988
- if extension_contract.find_method(f) is None:
3140
+ if not extension_contract.has_method_with_name(f):
2989
3141
  raise Util.CertoraUserInputError(f"Can't find a method named {f} in contract "
2990
3142
  f"{extension_contract.name}")
2991
3143
  extended_contract.add_extension(ContractExtension(extension_contract, ext["exclude"]))
@@ -3267,6 +3419,8 @@ class CertoraBuildGenerator:
3267
3419
  sources.add(Path(context.bytecode_spec))
3268
3420
  if context.yul_abi:
3269
3421
  sources.add(Path(context.yul_abi))
3422
+ if context.override_base_config:
3423
+ sources.add(Path(context.override_base_config))
3270
3424
 
3271
3425
  if hasattr(context, Attrs.EvmProverAttributes.PROVER_RESOURCE_FILES.get_conf_key()) \
3272
3426
  and context.prover_resource_files:
@@ -3337,28 +3491,19 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
3337
3491
  raise
3338
3492
 
3339
3493
  # the empty file .cwd is written in the source tree to denote the current working directory
3340
- if cwd_rel_in_sources != '.':
3341
- file_path = Util.get_certora_sources_dir() / cwd_rel_in_sources / Util.CWD_FILE
3342
- file_path.parent.mkdir(parents=True, exist_ok=True)
3343
- file_path.touch()
3344
-
3345
- """
3346
- Once the resource files are copied to the source tree, the paths in the value of the 'prover_resource_files'
3347
- attribute are replaced with the relative path of the resource file from the source tree root.
3348
- This way The server can easily find the resource files in the source tree. The path from the source tree is the
3349
- relative path of cwd from source tree root (most cases '.') concatenated with the relative path of the resource
3350
- file from cwd
3351
- """
3352
- if context.prover_resource_files:
3353
- new_value = []
3354
- len_orig = len(context.prover_resource_files)
3355
- for value in context.prover_resource_files:
3356
- label, file_path = value.split(':')
3357
- rel_path = Path(os.path.relpath(file_path, '.'))
3358
- new_value.append(':'.join([label, os.path.normpath(rel_path)]))
3359
- if len_orig != len(new_value):
3360
- raise RuntimeError(f"fail to process prover_resource_files {len_orig} out of {len(new_value)}")
3361
- context.prover_resource_files = new_value
3494
+ cwd_file_path = Util.get_certora_sources_dir() / cwd_rel_in_sources / Util.CWD_FILE
3495
+ cwd_file_path.parent.mkdir(parents=True, exist_ok=True)
3496
+ cwd_file_path.touch()
3497
+
3498
+ # the empty file .project_directory is written in the source tree to denote the projecct directory
3499
+ rust_proj_dir = getattr(context, 'rust_project_directory', None)
3500
+ if rust_proj_dir:
3501
+ proj_dir_parent_relative = os.path.relpath(rust_proj_dir, os.getcwd())
3502
+ assert Path(rust_proj_dir).is_dir(), f"build_source_tree: not a directory {rust_proj_dir}"
3503
+ proj_dir_parent_file = (Util.get_certora_sources_dir() / cwd_rel_in_sources / proj_dir_parent_relative /
3504
+ Util.PROJECT_DIR_FILE)
3505
+ proj_dir_parent_file.parent.mkdir(parents=True, exist_ok=True)
3506
+ proj_dir_parent_file.touch()
3362
3507
 
3363
3508
  # Copy the repro conf file to the sources as the overall way to reproduce this run easily.
3364
3509
  # The file is being copied in here and not added through collect_sources, because we want
@@ -218,12 +218,24 @@ class ContractInSDC:
218
218
  "localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()}
219
219
  }
220
220
 
221
- def find_method(self, name: str) -> Optional[Func]:
221
+ def find_method(self, name: str, fullArgs: List[CT.TypeInstance]) -> Optional[Func]:
222
+ """
223
+ attempts to find and return a matching method. from Solidity documentation,
224
+ the combination of the method name and (ordered) parameter types
225
+ is sufficient to disambiguate method overloading.
226
+ """
222
227
  for method in self.methods:
223
228
  if method.name == name:
224
- return method
229
+ if Util.eq_by(CT.TypeInstance.matches, method.fullArgs, fullArgs):
230
+ return method
225
231
  return None
226
232
 
233
+ def has_method_with_name(self, name: str) -> bool:
234
+ for method in self.methods:
235
+ if method.name == name:
236
+ return True
237
+ return False
238
+
227
239
  def __repr__(self) -> str:
228
240
  return repr(self.as_printable_dict())
229
241
 
@@ -21,6 +21,7 @@ from typing import Set
21
21
  from CertoraProver.certoraBuild import build_source_tree
22
22
  from CertoraProver.certoraContextClass import CertoraContext
23
23
  from CertoraProver.certoraParseBuildScript import run_script_and_parse_json
24
+ import CertoraProver.certoraContextAttributes as Attrs
24
25
  from Shared import certoraUtils as Util
25
26
 
26
27
 
@@ -31,8 +32,7 @@ def build_rust_app(context: CertoraContext) -> None:
31
32
  raise Util.CertoraUserInputError("failed to get target executable")
32
33
 
33
34
  sources: Set[Path] = set()
34
- root_directory = Path(context.rust_project_directory)
35
- collect_files_from_rust_sources(context, sources, root_directory)
35
+ collect_files_from_rust_sources(context, sources)
36
36
 
37
37
  try:
38
38
  # Create generators
@@ -47,25 +47,42 @@ def build_rust_app(context: CertoraContext) -> None:
47
47
  raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
48
48
  if len(context.files) > 1:
49
49
  raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
50
- context.rust_executables = context.files[0]
51
50
 
52
- def add_solana_files(context: CertoraContext, sources: Set[Path], attr_name: str) -> None:
53
- attr_value = getattr(context, attr_name, None)
54
- if not attr_value or not isinstance(attr_value, list):
55
- return
56
- file_paths = [Path(s) for s in attr_value]
57
- for file_path in file_paths:
58
- if not file_path.exists():
59
- raise Util.CertoraUserInputError(f"in {attr_name} file {file_path} does not exist")
60
- sources.add(file_path.absolute())
51
+ try:
52
+ Util.get_certora_sources_dir().mkdir(parents=True, exist_ok=True)
53
+ shutil.copy(Util.get_last_conf_file(), Util.get_certora_sources_dir() / Util.LAST_CONF_FILE)
54
+ except Exception as e:
55
+ raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
56
+
57
+ context.rust_executables = context.files[0]
61
58
 
62
59
 
63
- def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path], root_directory: Path) -> None:
60
+ def add_solana_files(context: CertoraContext, sources: Set[Path]) -> None:
61
+ for attr in [Attrs.SolanaProverAttributes.SOLANA_INLINING, Attrs.SolanaProverAttributes.SOLANA_SUMMARIES]:
62
+ attr_name = attr.get_conf_key()
63
+ attr_value = getattr(context, attr_name, None)
64
+ if not attr_value:
65
+ continue
66
+ if isinstance(attr_value, str):
67
+ attr_value = [str]
68
+ if not isinstance(attr_value, list):
69
+ raise Util.CertoraUserInputError(f"{attr_value} is not a valid value for {attr_name} {attr_value}. Value "
70
+ f"must be a string or a llist ")
71
+ file_paths = [Path(s) for s in attr_value]
72
+ for file_path in file_paths:
73
+ if not file_path.exists():
74
+ raise Util.CertoraUserInputError(f"in {attr_name} file {file_path} does not exist")
75
+ sources.add(file_path.absolute())
76
+
77
+
78
+ def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path]) -> None:
64
79
  patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
65
80
  exclude_dirs = [".certora_internal"]
66
81
 
82
+ root_directory = Path(context.rust_project_directory)
83
+
67
84
  if not root_directory.is_dir():
68
- raise ValueError(f"The given directory {root_directory} is not valid.")
85
+ raise ValueError(f"The given directory '{root_directory}' is not valid.")
69
86
 
70
87
  for source in context.rust_sources:
71
88
  for file in glob.glob(f'{root_directory.joinpath(source)}', recursive=True):
@@ -75,12 +92,13 @@ def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path],
75
92
  if file_path.is_file() and any(file_path.match(pattern) for pattern in patterns):
76
93
  sources.add(file_path)
77
94
 
95
+ sources.add(Path(context.rust_project_directory).absolute())
78
96
  if Path(context.build_script).exists():
79
97
  sources.add(Path(context.build_script).resolve())
80
98
  if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
81
99
  sources.add(Path(context.conf_file).absolute())
82
- add_solana_files(context, sources, 'solana_inlining')
83
- add_solana_files(context, sources, 'solana_summaries')
100
+ add_solana_files(context, sources)
101
+
84
102
 
85
103
  def copy_files_to_build_dir(context: CertoraContext) -> None:
86
104
  rust_executable = Path(context.rust_project_directory) / context.rust_executables
@@ -737,7 +737,7 @@ class CloudVerification:
737
737
  elif Attrs.is_evm_app() and self.context.is_bytecode:
738
738
  # We zip the bytecode jsons and the spec
739
739
  paths = [Util.get_certora_build_file(), Util.get_certora_verify_file(),
740
- Util.get_certora_metadata_file()]
740
+ Util.get_certora_metadata_file(), Util.get_configuration_layout_data_file()]
741
741
  if Util.get_certora_sources_dir().exists():
742
742
  paths.append(Util.get_certora_sources_dir())
743
743
 
@@ -747,7 +747,9 @@ class CloudVerification:
747
747
  result = compress_files(self.ZipFilePath, *paths,
748
748
  short_output=Ctx.is_minimal_cli_output(self.context))
749
749
  elif Attrs.is_rust_app():
750
- files_list = [Util.get_certora_metadata_file()]
750
+ files_list = [Util.get_certora_metadata_file(), Util.get_configuration_layout_data_file()]
751
+ if Util.get_certora_sources_dir().exists():
752
+ files_list.append(Util.get_certora_sources_dir())
751
753
 
752
754
  if hasattr(self.context, 'build_script') and self.context.build_script:
753
755
  result = compress_files(self.logZipFilePath, Util.get_debug_log_file(),
@@ -756,8 +758,6 @@ class CloudVerification:
756
758
  if not result:
757
759
  return False
758
760
  files_list.append(self.logZipFilePath)
759
- if Util.get_certora_sources_dir().exists():
760
- files_list.append(Util.get_certora_sources_dir())
761
761
 
762
762
  files_list.append(Util.get_build_dir() / Path(self.context.rust_executables).name)
763
763
 
@@ -820,7 +820,8 @@ class CloudVerification:
820
820
  if not result:
821
821
  return False
822
822
  files_list = [Util.get_certora_build_file(), Util.get_certora_verify_file(),
823
- Util.get_certora_metadata_file(), self.logZipFilePath]
823
+ Util.get_certora_metadata_file(), Util.get_configuration_layout_data_file(),
824
+ self.logZipFilePath]
824
825
  if Util.get_certora_sources_dir().exists():
825
826
  files_list.append(Util.get_certora_sources_dir())
826
827
  result = compress_files(self.ZipFilePath, *files_list, short_output=Ctx.is_minimal_cli_output(self.context))