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.
- {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
- certora_cli_alpha_master-20250519.9.10.855346/README.md +1 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuild.py +161 -55
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuildRust.py +24 -38
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCloudIO.py +24 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContext.py +2 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContextAttributes.py +21 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContextValidator.py +8 -0
- certora_cli_alpha_master-20250519.9.10.855346/certora_cli/CertoraProver/erc7201.py +45 -0
- certora_cli_alpha_master-20250519.9.10.855346/certora_cli/CertoraProver/storageExtension.py +386 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraUtils.py +1 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraRun.py +2 -2
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraSolanaProver.py +2 -2
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraSorobanProver.py +2 -2
- {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
- {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
- certora_cli_alpha_master-20250519.9.10.855346/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_jars/Typechecker.jar +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/setup.py +2 -2
- certora_cli_alpha_master-20250508.17.30.185947/README.md +0 -1
- certora_cli_alpha_master-20250508.17.30.185947/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/LICENSE +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/MANIFEST.in +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_bins/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuildCacheManager.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraBuildDataClasses.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraConfigIO.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraJobList.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraParseBuildScript.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraType.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/CertoraProver/splitRules.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
- {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
- {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
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateApp.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateAttributes.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateConstants.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateUtil.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Mutate/mutateValidate.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/ExpectedComparator.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraAttrUtil.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraLogging.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/Shared/certoraValidateFuncs.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/__init__.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraEVMProver.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraEqCheck.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraMutate.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/certoraRanger.py +0 -0
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_cli/rustMutator.py +0 -0
- {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
- {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
- {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
- {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
- {certora_cli_alpha_master-20250508.17.30.185947 → certora_cli_alpha_master-20250519.9.10.855346}/certora_jars/__init__.py +0 -0
- {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:
|
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
|
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
|
-
|
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 <=
|
1444
|
-
yul_optimizer_steps =
|
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
|
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
|
29
|
-
|
30
|
-
|
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
|
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)
|