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.
- {certora-cli-7.25.3 → certora-cli-7.28.0}/LICENSE +1 -1
- {certora-cli-7.25.3/certora_cli.egg-info → certora-cli-7.28.0}/PKG-INFO +2 -2
- certora-cli-7.28.0/README.md +1 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +6 -3
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuild.py +173 -28
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildDataClasses.py +14 -2
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildRust.py +34 -16
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraCloudIO.py +6 -5
- certora-cli-7.28.0/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +242 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +10 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraConfigIO.py +52 -16
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContext.py +1 -1
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContextAttributes.py +147 -51
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContextValidator.py +67 -45
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraParseBuildScript.py +8 -2
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/equivCheck.py +5 -4
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateApp.py +21 -12
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateConstants.py +0 -1
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraAttrUtil.py +4 -2
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraUtils.py +23 -7
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraValidateFuncs.py +31 -14
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraRun.py +8 -1
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraSolanaProver.py +8 -1
- {certora-cli-7.25.3 → certora-cli-7.28.0/certora_cli.egg-info}/PKG-INFO +2 -2
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/SOURCES.txt +1 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/requires.txt +0 -1
- certora-cli-7.28.0/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_jars/Typechecker.jar +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/setup.py +3 -3
- certora-cli-7.25.3/README.md +0 -1
- certora-cli-7.25.3/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
- {certora-cli-7.25.3 → certora-cli-7.28.0}/MANIFEST.in +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_bins/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildCacheManager.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraJobList.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraType.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/splitRules.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateAttributes.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateUtil.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Mutate/mutateValidate.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/ExpectedComparator.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/Shared/certoraLogging.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraEVMProver.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraEqCheck.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraMutate.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/certoraSorobanProver.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/rustMutator.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/dependency_links.txt +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/entry_points.txt +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli.egg-info/top_level.txt +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/certora_jars/__init__.py +0 -0
- {certora-cli-7.25.3 → certora-cli-7.28.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: certora-cli
|
|
3
|
-
Version: 7.
|
|
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
|
|
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
|
-
|
|
43
|
-
|
|
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
|
|
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
|
-
|
|
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 <=
|
|
1437
|
-
yul_optimizer_steps =
|
|
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.
|
|
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
|
-
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
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
|
{certora-cli-7.25.3 → certora-cli-7.28.0}/certora_cli/CertoraProver/certoraBuildDataClasses.py
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
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
|
|
83
|
-
|
|
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(),
|
|
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))
|