certora-cli-alpha-master 20250508.17.30.185947__tar.gz → 20250519.9.10.855346__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 (81) hide show
  1. {certora_cli_alpha_master-20250508.17.30.185947/certora_cli_alpha_master.egg-info → certora_cli_alpha_master-20250519.9.10.855346}/PKG-INFO +2 -2
  2. certora_cli_alpha_master-20250519.9.10.855346/README.md +1 -0
  3. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuild.py +161 -55
  4. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuildRust.py +24 -38
  5. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCloudIO.py +24 -0
  6. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContext.py +2 -0
  7. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContextAttributes.py +21 -0
  8. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContextValidator.py +8 -0
  9. certora_cli_alpha_master-20250519.9.10.855346/certora_cli/CertoraProver/erc7201.py +45 -0
  10. certora_cli_alpha_master-20250519.9.10.855346/certora_cli/CertoraProver/storageExtension.py +386 -0
  11. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraUtils.py +1 -0
  12. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraRun.py +2 -2
  13. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraSolanaProver.py +2 -2
  14. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraSorobanProver.py +2 -2
  15. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346/certora_cli_alpha_master.egg-info}/PKG-INFO +2 -2
  16. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli_alpha_master.egg-info/SOURCES.txt +2 -0
  17. certora_cli_alpha_master-20250519.9.10.855346/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  18. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_jars/Typechecker.jar +0 -0
  19. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/setup.py +2 -2
  20. certora_cli_alpha_master-20250508.17.30.185947/README.md +0 -1
  21. certora_cli_alpha_master-20250508.17.30.185947/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  22. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/LICENSE +0 -0
  23. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/MANIFEST.in +0 -0
  24. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_bins/__init__.py +0 -0
  25. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
  26. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +0 -0
  27. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
  28. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
  29. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
  30. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
  31. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
  32. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/__init__.py +0 -0
  33. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuildCacheManager.py +0 -0
  34. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuildDataClasses.py +0 -0
  35. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +0 -0
  36. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +0 -0
  37. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
  38. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraConfigIO.py +0 -0
  39. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
  40. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
  41. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
  42. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraJobList.py +0 -0
  43. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
  44. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
  45. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraParseBuildScript.py +0 -0
  46. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
  47. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
  48. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraType.py +0 -0
  49. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
  50. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/splitRules.py +0 -0
  51. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  52. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  53. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  54. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  55. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  56. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  57. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
  58. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  59. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/__init__.py +0 -0
  60. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateApp.py +0 -0
  61. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateAttributes.py +0 -0
  62. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateConstants.py +0 -0
  63. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateUtil.py +0 -0
  64. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateValidate.py +0 -0
  65. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/ExpectedComparator.py +0 -0
  66. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/__init__.py +0 -0
  67. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraAttrUtil.py +0 -0
  68. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraLogging.py +0 -0
  69. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraValidateFuncs.py +0 -0
  70. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/__init__.py +0 -0
  71. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraEVMProver.py +0 -0
  72. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraEqCheck.py +0 -0
  73. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraMutate.py +0 -0
  74. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraRanger.py +0 -0
  75. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/rustMutator.py +0 -0
  76. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli_alpha_master.egg-info/dependency_links.txt +0 -0
  77. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli_alpha_master.egg-info/entry_points.txt +0 -0
  78. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli_alpha_master.egg-info/requires.txt +0 -0
  79. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli_alpha_master.egg-info/top_level.txt +0 -0
  80. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_jars/__init__.py +0 -0
  81. {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: certora-cli-alpha-master
3
- Version: 20250508.17.30.185947
3
+ Version: 20250519.9.10.855346
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli-alpha-master
6
6
  Author: Certora
@@ -37,4 +37,4 @@ Dynamic: requires-dist
37
37
  Dynamic: requires-python
38
38
  Dynamic: summary
39
39
 
40
- Commit 5d897cd. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
40
+ Commit 081395d. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit 081395d. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -25,8 +25,11 @@ from collections import OrderedDict, defaultdict
25
25
  from enum import Enum
26
26
  from functools import lru_cache
27
27
  from pathlib import Path
28
- from typing import Any, Dict, List, Tuple, Optional, Set, Iterator, NoReturn
29
28
  from Crypto.Hash import keccak
29
+ import tempfile
30
+
31
+ from typing import Any, Dict, List, Tuple, Optional, Set, Iterator, NoReturn
32
+
30
33
 
31
34
  from CertoraProver.certoraBuildCacheManager import CertoraBuildCacheManager, CachedFiles
32
35
  from CertoraProver.certoraBuildDataClasses import CONTRACTS, ImmutableReference, ContractExtension, ContractInSDC, SDC, \
@@ -35,6 +38,7 @@ from CertoraProver.certoraCompilerParameters import SolcParameters
35
38
  from CertoraProver.certoraSourceFinders import add_source_finders
36
39
  from CertoraProver.certoraVerifyGenerator import CertoraVerifyGenerator
37
40
  from CertoraProver.certoraContractFuncs import Func, InternalFunc, STATEMUT, SourceBytes
41
+
38
42
  from Shared.certoraUtils import is_relative_to
39
43
 
40
44
  scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
@@ -54,7 +58,11 @@ from Shared import certoraValidateFuncs as Vf
54
58
  from CertoraProver import certoraContextValidator as Cv
55
59
  from Shared import certoraUtils as Util
56
60
  import CertoraProver.certoraContext as Ctx
57
-
61
+ from CertoraProver import storageExtension
62
+ from CertoraProver.storageExtension import (
63
+ NameSpacedStorage,
64
+ NewStorageInfo,
65
+ )
58
66
 
59
67
  BUILD_IS_LIBRARY = False
60
68
  AUTO_FINDER_PREFIX = "autoFinder_"
@@ -72,6 +80,7 @@ MUTANTS_LOCATION = "mutants_location"
72
80
 
73
81
  FunctionSig = Tuple[str, List[str], List[str], str]
74
82
 
83
+
75
84
  # logger for building the abstract syntax tree
76
85
  ast_logger = logging.getLogger("ast")
77
86
  # logger for issues calling/shelling out to external functions
@@ -84,12 +93,10 @@ build_logger = logging.getLogger("build_conf")
84
93
  # logger of the build cache
85
94
  build_cache_logger = logging.getLogger("build_cache")
86
95
 
87
-
88
96
  def fatal_error(logger: logging.Logger, msg: str) -> NoReturn:
89
97
  logger.fatal(msg)
90
98
  raise Exception(msg)
91
99
 
92
-
93
100
  class InputConfig:
94
101
  def __init__(self, context: CertoraContext) -> None:
95
102
  """
@@ -1396,7 +1403,7 @@ class CertoraBuildGenerator:
1396
1403
  if not self.context.strict_solc_optimizer and self.context.solc_via_ir:
1397
1404
  # The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
1398
1405
  # full inliner step removed
1399
- solc0_8_26_to_0_8_29 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
1406
+ solc0_8_26_to_0_8_30 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
1400
1407
  "IulcscCTUtx[scCTUt]TOntnfDIuljmul[jul]VcTOculjmul")
1401
1408
  solc0_8_13_to_0_8_25 = "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]T" + \
1402
1409
  "peulxa[rul]xa[r]cLgvifCTUca[r]LSsTFOtfDnca[r]Iulc]jmul[jul]VcTOculjmul"
@@ -1440,8 +1447,8 @@ class CertoraBuildGenerator:
1440
1447
  yul_optimizer_steps = solc0_8_12
1441
1448
  elif minor == 8 and 13 <= patch <= 25:
1442
1449
  yul_optimizer_steps = solc0_8_13_to_0_8_25
1443
- elif minor == 8 and 26 <= patch <= 29:
1444
- yul_optimizer_steps = solc0_8_26_to_0_8_29
1450
+ elif minor == 8 and 26 <= patch <= 30:
1451
+ yul_optimizer_steps = solc0_8_26_to_0_8_30
1445
1452
  assert yul_optimizer_steps is not None, \
1446
1453
  'Yul Optimizer steps missing for requested Solidity version. Please contact Certora team.'
1447
1454
 
@@ -2539,7 +2546,6 @@ class CertoraBuildGenerator:
2539
2546
  Util.print_progress_message(f"Compiling {orig_file_name}...")
2540
2547
  sdc_pre_finders = self.collect_for_file(build_arg_contract_file, i, compiler_lang, Path(os.getcwd()),
2541
2548
  path_for_compiler_collector_file, original_sdc=None)
2542
-
2543
2549
  # Build sources tree
2544
2550
  build_logger.debug("Building source tree")
2545
2551
  sources_from_pre_finder_SDCs = set()
@@ -2672,8 +2678,154 @@ class CertoraBuildGenerator:
2672
2678
  self.handle_links()
2673
2679
  self.handle_struct_links()
2674
2680
  self.handle_contract_extensions()
2681
+ if self.context.storage_extension_annotation:
2682
+ self.handle_erc7201_annotations()
2675
2683
  self.handle_storage_extension_harnesses()
2676
2684
 
2685
+ def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage]) -> NewStorageInfo:
2686
+ """
2687
+ Given a file containing a contract with namespaced storage, extract the storage information
2688
+ corresponding to the namespaced types.
2689
+
2690
+ Args:
2691
+ original_file: Path to the Solidity file containing namespaced storage declarations
2692
+ ns_storage: Set of tuples (type_name, namespace) for each namespaced storage
2693
+
2694
+ Returns:
2695
+ NewStorageInfo: A tuple (fields, types) where:
2696
+ - fields: List of new fields added by the namespaced storage
2697
+ - types: Dictionary of types referenced by the fields
2698
+ """
2699
+ file_dir = Path(original_file).parent
2700
+
2701
+ # Generate a unique name for the harness contract based on the contract names in the file
2702
+ harness_name = storageExtension.generate_harness_name(original_file)
2703
+
2704
+ with tempfile.NamedTemporaryFile(mode="w+t", suffix=".sol", dir=file_dir, delete=True) as tmp_file:
2705
+ # Import the original file.
2706
+ # Note we import and don't inline the file's contents since that's how the
2707
+ # original file is accessed also in the actual code, and compiling it
2708
+ # directly can cause issues.
2709
+ tmp_file.write(f"import \"{original_file}\";\n\n")
2710
+
2711
+ # Write the harness contract with dummy fields for each namespaced storage
2712
+ var_to_slot = storageExtension.write_harness_contract(tmp_file, harness_name, ns_storage)
2713
+ tmp_file.flush()
2714
+
2715
+ if self.context.extract_storage_extension_annotation:
2716
+ # If the flag is set, save the storage extension contract
2717
+ build_dir = Util.get_build_dir()
2718
+ shutil.copyfile(
2719
+ Path(tmp_file.name),
2720
+ build_dir / f"{Path(original_file).stem}_storage_extension.sol"
2721
+ )
2722
+
2723
+ # Add harness to compiler map
2724
+ storageExtension.add_harness_to_compiler_map(
2725
+ original_file,
2726
+ tmp_file,
2727
+ self.context
2728
+ )
2729
+
2730
+ # normalize the path exactly the way collect_for_file expects it:
2731
+ abs_path = Util.abs_posix_path(tmp_file.name)
2732
+ self.context.file_to_contract[abs_path] = {harness_name}
2733
+
2734
+ try:
2735
+ # Compile & fetch the raw storage_layout
2736
+ compile_idx = storageExtension.get_next_file_index(self.file_to_sdc_name)
2737
+ sdcs = self.collect_for_file(tmp_file.name, compile_idx, CompilerLangSol(), Path.cwd(), Util.abs_posix_path(tmp_file.name), None)
2738
+ if not sdcs:
2739
+ raise RuntimeError(f"Failed to compile harness contract for {tmp_file}")
2740
+ layout = storageExtension.extract_harness_contract_layout(sdcs, harness_name)
2741
+
2742
+ # Remap each slot according to the ERC-7201 namespace
2743
+ remapped_fields = storageExtension.remapped_fields_from_layout(layout, var_to_slot)
2744
+
2745
+ return (remapped_fields, layout.get('types', {}))
2746
+
2747
+ except Exception as e:
2748
+ build_logger.error(f"Error extracting storage layout for {original_file}: {str(e)}")
2749
+ raise
2750
+ finally:
2751
+ # Delete the key from the context
2752
+ self.context.file_to_contract.pop(abs_path, None)
2753
+
2754
+ def handle_erc7201_annotations(self) -> None:
2755
+ """
2756
+ Look for contracts that use erc-7201 namespaced storage layout
2757
+ (see https://eips.ethereum.org/EIPS/eip-7201).
2758
+
2759
+ Find contracts A s.t. A contain a type declaration with such an annotation, e.g.
2760
+ /** @custom:storage-location erc-7201:some.name.space */
2761
+ struct T { ... }
2762
+
2763
+ Then, for any contract C that has A as a base contract, _extend_ C's storage layout
2764
+ information such that it contains the information for a `T` at the slot
2765
+ erc-7201(some.name.space) as defined in the EIP.
2766
+ """
2767
+ # Find all erc7201-like contracts, generate+compile a harness & extract layout information
2768
+ # maps (path,contract) -> new storage info added by (path,contract)
2769
+ slayouts: Dict[Tuple[str, str], NewStorageInfo] = {}
2770
+
2771
+ # Scan all of the contracts (including dependencies of targets) for namespaced storage
2772
+ # layout information
2773
+ for target_file in self.context.file_paths:
2774
+ if target_file not in self.asts:
2775
+ # No AST for this file, so we can't do anything
2776
+ continue
2777
+ for (imported_file, imported_file_ast) in self.asts[target_file].items():
2778
+ for def_node in imported_file_ast.values():
2779
+ if def_node.get("nodeType") != "ContractDefinition":
2780
+ continue
2781
+
2782
+ # Construct a key for the contract definition node
2783
+ contract_name = def_node.get("name")
2784
+ key = (imported_file, contract_name)
2785
+ if key in slayouts:
2786
+ # We already have this contract's storage layout information
2787
+ continue
2788
+
2789
+ # Collect any @custom:storage-location annotations
2790
+ ns_storage = storageExtension.get_namespace_storage_from_ast(def_node)
2791
+
2792
+ if not ns_storage:
2793
+ # No namespaced storage found in this contract
2794
+ continue
2795
+
2796
+ # Now that we have all the storage layout information, extract it once
2797
+ slayouts[key] = self.extract_slayout(imported_file, ns_storage)
2798
+
2799
+ if self.context.test == str(Util.TestValue.STORAGE_EXTENSION_LAYOUT):
2800
+ raise Util.TestResultsReady(slayouts)
2801
+
2802
+ if not slayouts:
2803
+ # No contracts with namespaced storage found
2804
+ return
2805
+
2806
+ # Finally, extend each target contract with the storage layout info from
2807
+ # all of its base contracts
2808
+ for target in self.get_primary_contracts_from_sdcs():
2809
+ if target.name not in self.context.contract_to_file:
2810
+ # This is a contract that was not compiled, so we don't have a file for it
2811
+ continue
2812
+ target_file = self.context.contract_to_file[target.name]
2813
+ base_contracts = self.retrieve_base_contracts_list(
2814
+ target_file,
2815
+ Util.abs_posix_path(target_file),
2816
+ target.name
2817
+ )
2818
+ extensions: Set[str] = set()
2819
+ harnesses: Dict[str, NewStorageInfo] = {}
2820
+ for base in base_contracts:
2821
+ layout = slayouts.get((base[0], base[1]))
2822
+ if layout is not None:
2823
+ extensions.add(base[1])
2824
+ harnesses[base[1]] = layout
2825
+ else:
2826
+ build_logger.warning(f"Could not find storage layout for {base[1]} in {base[0]}")
2827
+ storageExtension.apply_extensions(target, extensions, harnesses)
2828
+
2677
2829
  def handle_storage_extension_harnesses(self) -> None:
2678
2830
  def new_field_of_node(ext_instance: Any, node: Dict[str, Any]) -> Optional[Dict[str, Any]]:
2679
2831
  """
@@ -2760,52 +2912,6 @@ class CertoraBuildGenerator:
2760
2912
  new_fields.append(new_field)
2761
2913
 
2762
2914
  return (ext_instance, extension_sdc_name, new_fields)
2763
-
2764
- def apply_extensions(target_contract: Any, extensions: Set[str], to_add: Dict[str, Tuple[List[Dict[str, Any]], Dict[str, Any]]]) -> None:
2765
- """
2766
- Apply the fields from each extension to the target contract,
2767
- """
2768
- if target_contract.storage_layout.get("storage") is None:
2769
- target_contract.storage_layout["storage"] = []
2770
- if target_contract.storage_layout.get("types") is None:
2771
- target_contract.storage_layout["types"] = {}
2772
- target_slots = {storage["slot"] for storage in target_contract.storage_layout["storage"]}
2773
- target_vars = {storage["label"] for storage in target_contract.storage_layout["storage"]}
2774
- # Keep track of slots we've added, and error if we
2775
- # find two extensions extending the same slot
2776
- added_slots: Dict[str, str] = {}
2777
- added_vars: Dict[str, str] = {}
2778
- for ext in extensions:
2779
- (new_fields, new_types) = to_add[ext]
2780
-
2781
- for f in new_fields:
2782
- # See if any of the new fields is a slot or variable name we've already added
2783
- slot = f["slot"]
2784
- var = f["label"]
2785
- if slot in added_slots:
2786
- seen = added_slots[slot]
2787
- raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} was already added by {seen}")
2788
-
2789
- if var in added_vars:
2790
- seen = added_vars[var]
2791
- raise Util.CertoraUserInputError(f"Var '{var}' added to {target_contract.name} by {ext} was already added by {seen}")
2792
-
2793
- if slot in target_slots:
2794
- raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} is already mapped by {target_contract.name}")
2795
-
2796
- if var in target_vars:
2797
- raise Util.CertoraUserInputError(f"Var '{var}' added to {target_contract.name} by {ext} is already declared by {target_contract.name}")
2798
-
2799
- added_slots[slot] = ext
2800
- added_vars[var] = ext
2801
-
2802
- target_contract.storage_layout["storage"].extend(new_fields)
2803
-
2804
- for (new_id, new_ty) in new_types.items():
2805
- if new_id in target_contract.storage_layout["types"]:
2806
- continue
2807
- target_contract.storage_layout["types"][new_id] = new_ty
2808
-
2809
2915
  extension_contracts: Set[str] = set()
2810
2916
  storage_extensions: Dict[str, Set[str]] = defaultdict(set)
2811
2917
  storage_ext = self.context.storage_extension_harnesses
@@ -2834,7 +2940,7 @@ class CertoraBuildGenerator:
2834
2940
  sdc = self.SDCs[target_sdc]
2835
2941
  target_contract = sdc.find_contract(target)
2836
2942
  assert target_contract is not None, f"could not find contract for {target}"
2837
- apply_extensions(target_contract, extensions, extension_to_fields_and_types)
2943
+ storageExtension.apply_extensions(target_contract, extensions, extension_to_fields_and_types)
2838
2944
 
2839
2945
  def finders_compilation_round(self,
2840
2946
  build_arg_contract_file: str,
@@ -16,7 +16,7 @@
16
16
  import glob
17
17
  import shutil
18
18
  from pathlib import Path
19
- from typing import Set, List
19
+ from typing import Set
20
20
 
21
21
  from CertoraProver.certoraBuild import build_source_tree
22
22
  from CertoraProver.certoraContextClass import CertoraContext
@@ -25,42 +25,9 @@ import CertoraProver.certoraContextAttributes as Attrs
25
25
  from Shared import certoraUtils as Util
26
26
 
27
27
 
28
- def build_rust_app(context: CertoraContext) -> None:
29
- build_command: List[str] = []
30
- feature_flag = None
31
- if context.build_script:
32
- build_command = [context.build_script]
33
- feature_flag = '--cargo_features'
34
-
35
- elif not context.files:
36
- build_command = ["cargo", "certora-sbf"]
37
- feature_flag = '--features'
38
- if context.cargo_tools_version:
39
- build_command.append("--tools-version")
40
- build_command.append(context.cargo_tools_version)
41
-
42
- if build_command:
43
- if context.cargo_features is not None:
44
- build_command.extend([feature_flag] + context.cargo_features)
45
-
46
- build_command.extend(['--json', '-l'])
47
-
48
- if context.test == str(Util.TestValue.SOLANA_BUILD_CMD):
49
- raise Util.TestResultsReady(build_command)
50
-
51
- run_rust_build(context, build_command)
52
-
53
- else:
54
- if not context.files:
55
- raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
56
- if len(context.files) > 1:
57
- raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
58
-
59
- try:
60
- Util.get_certora_sources_dir().mkdir(parents=True, exist_ok=True)
61
- shutil.copy(Util.get_last_conf_file(), Util.get_certora_sources_dir() / Util.LAST_CONF_FILE)
62
- except Exception as e:
63
- raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
28
+ def set_rust_build_directory(context: CertoraContext) -> None:
29
+ if not context.files:
30
+ build_rust_app(context)
64
31
 
65
32
  copy_files_to_build_dir(context)
66
33
 
@@ -74,6 +41,25 @@ def build_rust_app(context: CertoraContext) -> None:
74
41
  except Exception as e:
75
42
  raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
76
43
 
44
+ def build_rust_app(context: CertoraContext) -> None:
45
+ assert not context.files, "build_rust_app: expecting files to be empty"
46
+ if context.build_script:
47
+ build_command = [context.build_script, '--json', '-l']
48
+ feature_flag = '--cargo_features'
49
+ else: # cargo
50
+ build_command = ["cargo", "certora-sbf", '--json']
51
+ feature_flag = '--features'
52
+ if context.cargo_tools_version:
53
+ build_command.extend(["--tools-version", context.cargo_tools_version])
54
+
55
+ if context.cargo_features is not None:
56
+ build_command.append(feature_flag)
57
+ build_command.extend(context.cargo_features)
58
+
59
+ if context.test == str(Util.TestValue.SOLANA_BUILD_CMD):
60
+ raise Util.TestResultsReady(build_command)
61
+
62
+ run_rust_build(context, build_command)
77
63
 
78
64
  def add_solana_files_from_prover_args(context: CertoraContext) -> None:
79
65
  if context.prover_args:
@@ -134,7 +120,7 @@ def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path])
134
120
  sources.add(file_path)
135
121
 
136
122
  sources.add(project_directory.absolute())
137
- if Path(context.build_script).exists():
123
+ if context.build_script:
138
124
  sources.add(Path(context.build_script).resolve())
139
125
  if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
140
126
  sources.add(Path(context.conf_file).absolute())
@@ -70,6 +70,16 @@ Response = requests.models.Response
70
70
 
71
71
  FEATURES_REPORT_FILE = Path("featuresReport.json")
72
72
 
73
+ class EcoEnum(Util.NoValEnum):
74
+ EVM = Util.auto()
75
+ SOROBAN = Util.auto()
76
+ SOLANA = Util.auto()
77
+
78
+ class ProductEnum(Util.NoValEnum):
79
+ PROVER = Util.auto()
80
+ RANGER = Util.auto()
81
+ SOPHY = Util.auto()
82
+
73
83
 
74
84
  class TimeError(Exception):
75
85
  """A custom exception used to report on time elapsed errors"""
@@ -631,6 +641,20 @@ class CloudVerification:
631
641
 
632
642
  auth_data["useLatestFe"] = self.context.fe_version == str(Util.FeValue.LATEST)
633
643
 
644
+ if Attrs.is_solana_app():
645
+ auth_data["ecosystem"] = EcoEnum.SOLANA.name
646
+ elif Attrs.is_soroban_app():
647
+ auth_data["ecosystem"] = EcoEnum.SOROBAN.name
648
+ else:
649
+ auth_data["ecosystem"] = EcoEnum.EVM.name
650
+
651
+ if Attrs.is_ranger_app():
652
+ auth_data["product"] = ProductEnum.RANGER.name
653
+ elif Attrs.is_sophy_app():
654
+ auth_data["product"] = ProductEnum.SOPHY.name
655
+ else:
656
+ auth_data["product"] = ProductEnum.PROVER.name
657
+
634
658
  if Attrs.is_evm_app() and self.context.cache is not None:
635
659
  auth_data["toolSceneCacheKey"] = self.context.cache
636
660
 
@@ -278,6 +278,8 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
278
278
  if Attrs.is_evm_app():
279
279
  validator.check_args_post_argparse()
280
280
  setup_cache(context) # Here context.cache, context.user_defined_cache are set
281
+ if Attrs.is_rust_app():
282
+ validator.check_rust_args_post_argparse()
281
283
 
282
284
  attrs_to_relative(context)
283
285
  # Setup defaults (defaults are not recorded in conf file)
@@ -998,6 +998,24 @@ class EvmAttributes(AttrUtil.Attributes):
998
998
  disables_build_cache=False
999
999
  )
1000
1000
 
1001
+ STORAGE_EXTENSION_ANNOTATION = AttrUtil.AttributeDefinition(
1002
+ arg_type=AttrUtil.AttrArgType.BOOLEAN,
1003
+ argparse_args={
1004
+ 'action': AttrUtil.STORE_TRUE
1005
+ },
1006
+ affects_build_cache_key=True,
1007
+ disables_build_cache=False
1008
+ )
1009
+
1010
+ EXTRACT_STORAGE_EXTENSION_ANNOTATION = AttrUtil.AttributeDefinition(
1011
+ arg_type=AttrUtil.AttrArgType.BOOLEAN,
1012
+ argparse_args={
1013
+ 'action': AttrUtil.STORE_TRUE
1014
+ },
1015
+ affects_build_cache_key=False,
1016
+ disables_build_cache=False
1017
+ )
1018
+
1001
1019
  OPTIMISTIC_HASHING = AttrUtil.AttributeDefinition(
1002
1020
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
1003
1021
  help_msg="Bound the length of data (with potentially unbounded length) to the value given in "
@@ -1854,3 +1872,6 @@ def is_evm_app() -> bool:
1854
1872
 
1855
1873
  def is_ranger_app() -> bool:
1856
1874
  return get_attribute_class() == RangerAttributes
1875
+
1876
+ def is_sophy_app() -> bool:
1877
+ return False # wait for the tool to be added
@@ -154,6 +154,14 @@ class CertoraContextValidator:
154
154
  elif value not in [True, False]:
155
155
  raise Util.CertoraUserInputError(f"value of {conf_key} {value} is not a boolean (true/false)")
156
156
 
157
+ def check_rust_args_post_argparse(self) -> None:
158
+ context = self.context
159
+ if context.files:
160
+ if context.build_script:
161
+ raise Util.CertoraUserInputError("'files' and 'build_script' cannot be both set for Rust projects")
162
+ if len(context.files) > 1:
163
+ raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
164
+
157
165
  def check_args_post_argparse(self) -> None:
158
166
  """
159
167
  Performs checks over the arguments after basic argparse parsing
@@ -0,0 +1,45 @@
1
+ # The Certora Prover
2
+ # Copyright (C) 2025 Certora Ltd.
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, version 3 of the License.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
15
+
16
+ from Crypto.Hash import keccak
17
+
18
+
19
+ def calculate_keccak_hash(x: bytes) -> int:
20
+ """
21
+ Calculates the Keccak-256 hash of the input bytes and returns it as an integer.
22
+
23
+ Args:
24
+ x (bytes): The input bytes to be hashed.
25
+ Returns:
26
+ int: The Keccak-256 hash value as an integer.
27
+ """
28
+ k = keccak.new(digest_bits=256)
29
+ k.update(x)
30
+ return int(k.hexdigest(), base=16)
31
+
32
+ def erc7201(x: bytes) -> int:
33
+ """
34
+ Hashes the input bytes using the Keccak-256 algorithm and returns
35
+ the result as an integer. The input is first hashed, then decremented
36
+ by 1, and the resulting hash is used to compute the final hash.
37
+ The final hash is masked to 256 bits and the last byte is cleared
38
+ (set to zero).
39
+
40
+ Args:
41
+ x (bytes): The input bytes to be hashed.
42
+ Returns:
43
+ int: The final hash value as an integer.
44
+ """
45
+ return calculate_keccak_hash((calculate_keccak_hash(x) - 1).to_bytes(32, byteorder='big')) & (~0xff)