certora-cli-beta-mirror 7.28.0__py3-none-any.whl → 7.29.0__py3-none-any.whl
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/CertoraProver/Compiler/CompilerCollectorVy.py +48 -13
- certora_cli/CertoraProver/certoraBuild.py +61 -30
- certora_cli/CertoraProver/certoraBuildDataClasses.py +5 -2
- certora_cli/CertoraProver/certoraBuildRust.py +77 -41
- certora_cli/CertoraProver/certoraCloudIO.py +29 -64
- certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +205 -70
- certora_cli/CertoraProver/certoraCollectRunMetadata.py +3 -1
- certora_cli/CertoraProver/certoraConfigIO.py +14 -15
- certora_cli/CertoraProver/certoraContext.py +13 -5
- certora_cli/CertoraProver/certoraContextAttributes.py +95 -26
- certora_cli/CertoraProver/certoraContextValidator.py +39 -5
- certora_cli/CertoraProver/certoraParseBuildScript.py +7 -10
- certora_cli/CertoraProver/certoraVerifyGenerator.py +12 -0
- certora_cli/CertoraProver/splitRules.py +3 -1
- certora_cli/Mutate/mutateApp.py +3 -3
- certora_cli/Shared/certoraAttrUtil.py +10 -0
- certora_cli/Shared/certoraUtils.py +9 -1
- certora_cli/Shared/certoraValidateFuncs.py +7 -0
- certora_cli/certoraRanger.py +71 -0
- certora_cli/certoraRun.py +11 -13
- certora_cli/certoraSolanaProver.py +7 -0
- certora_cli/certoraSorobanProver.py +253 -4
- certora_cli_beta_mirror-7.29.0.dist-info/LICENSE +15 -0
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-7.29.0.dist-info}/METADATA +18 -4
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-7.29.0.dist-info}/RECORD +30 -29
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-7.29.0.dist-info}/WHEEL +1 -1
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-7.29.0.dist-info}/entry_points.txt +1 -0
- certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
- certora_jars/Typechecker.jar +0 -0
- certora_cli_beta_mirror-7.28.0.dist-info/LICENSE +0 -22
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-7.29.0.dist-info}/top_level.txt +0 -0
|
@@ -556,43 +556,78 @@ class CompilerLangVy(CompilerLang, metaclass=Singleton):
|
|
|
556
556
|
else:
|
|
557
557
|
raise Exception(f"Unexpected ast_node {ast_node}, cannot evaluate constant")
|
|
558
558
|
|
|
559
|
+
@staticmethod
|
|
560
|
+
def subscript_node(ast_node: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
561
|
+
ty = ast_node.get("ast_type")
|
|
562
|
+
# pre-vyper 0.4.0, look deeper within
|
|
563
|
+
if ty == "Index":
|
|
564
|
+
nested = ast_node.get("value")
|
|
565
|
+
if nested is None or not isinstance(nested, dict):
|
|
566
|
+
return None
|
|
567
|
+
return nested
|
|
568
|
+
else:
|
|
569
|
+
return ast_node
|
|
570
|
+
|
|
571
|
+
@staticmethod
|
|
572
|
+
def int_subscript(ast_node: Dict[str, Any], named_constants: Dict[str, int]) -> Optional[int]:
|
|
573
|
+
node = CompilerLangVy.subscript_node(ast_node)
|
|
574
|
+
if node is None:
|
|
575
|
+
return None
|
|
576
|
+
|
|
577
|
+
if "id" in node and node["id"] in named_constants:
|
|
578
|
+
return named_constants[node["id"]]
|
|
579
|
+
elif node.get("ast_type") == "Int":
|
|
580
|
+
return node.get("value")
|
|
581
|
+
else:
|
|
582
|
+
return None
|
|
583
|
+
|
|
584
|
+
@staticmethod
|
|
585
|
+
def get_tuple_elements(ast_node: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
586
|
+
node = CompilerLangVy.subscript_node(ast_node)
|
|
587
|
+
if node is None or node.get("ast_type") != "Tuple":
|
|
588
|
+
raise Exception("Couldn't get tuple elements from ast node")
|
|
589
|
+
return node["elements"]
|
|
590
|
+
|
|
559
591
|
@staticmethod
|
|
560
592
|
def extract_type_from_subscript_node(ast_subscript_node: Dict[str, Any],
|
|
561
593
|
named_constants: Dict[str, int]) -> VyperType:
|
|
562
594
|
value_id = ast_subscript_node['value'].get('id', None)
|
|
563
595
|
if value_id == 'String':
|
|
564
|
-
max_bytes = ast_subscript_node['slice']
|
|
596
|
+
max_bytes = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
|
|
597
|
+
if max_bytes is None:
|
|
598
|
+
raise Exception("Failed to find max length for string type declaration")
|
|
565
599
|
return CompilerLangVy.VyperTypeString(max_bytes)
|
|
566
600
|
elif value_id == 'Bytes':
|
|
567
|
-
max_bytes = ast_subscript_node['slice']
|
|
601
|
+
max_bytes = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
|
|
602
|
+
if max_bytes is None:
|
|
603
|
+
raise Exception("Failed to find max length for bytes type declaration")
|
|
568
604
|
return CompilerLangVy.VyperTypeBytes(max_bytes)
|
|
569
605
|
elif value_id == 'DynArray':
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
max_elements = CompilerLangVy.extract_constant(
|
|
573
|
-
named_constants)
|
|
606
|
+
tup_elems = CompilerLangVy.get_tuple_elements(ast_subscript_node["slice"])
|
|
607
|
+
elem_type = CompilerLangVy.extract_type_from_type_annotation_node(tup_elems[0], named_constants)
|
|
608
|
+
max_elements = CompilerLangVy.extract_constant(tup_elems[1], named_constants)
|
|
574
609
|
return CompilerLangVy.VyperTypeDynArray(elem_type, max_elements)
|
|
575
610
|
elif value_id == 'HashMap':
|
|
576
|
-
elements_node = ast_subscript_node['slice']
|
|
611
|
+
elements_node = CompilerLangVy.get_tuple_elements(ast_subscript_node['slice'])
|
|
577
612
|
key_type = CompilerLangVy.extract_type_from_type_annotation_node(elements_node[0], named_constants)
|
|
578
613
|
value_type = CompilerLangVy.extract_type_from_type_annotation_node(elements_node[1], named_constants)
|
|
579
614
|
return CompilerLangVy.VyperTypeHashMap(key_type, value_type)
|
|
580
615
|
else: # StaticArray key_type[size]
|
|
581
616
|
key_type = CompilerLangVy.extract_type_from_type_annotation_node(ast_subscript_node['value'],
|
|
582
617
|
named_constants)
|
|
583
|
-
|
|
584
|
-
if
|
|
585
|
-
return CompilerLangVy.VyperTypeStaticArray(key_type,
|
|
618
|
+
max_elem_value = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
|
|
619
|
+
if max_elem_value is not None:
|
|
620
|
+
return CompilerLangVy.VyperTypeStaticArray(key_type, max_elem_value)
|
|
586
621
|
else:
|
|
587
622
|
# this is very specific to curve code which has uint256[CONST/2] static array declaration.
|
|
623
|
+
max_elements_node = CompilerLangVy.subscript_node(ast_subscript_node["slice"])
|
|
624
|
+
if max_elements_node is None:
|
|
625
|
+
raise Exception("Couldn't find subscript node for array type")
|
|
588
626
|
if 'ast_type' in max_elements_node:
|
|
589
627
|
if max_elements_node['ast_type'] == 'BinOp' or max_elements_node['ast_type'] in ('Int', 'Name'):
|
|
590
628
|
# good chance this will succeed
|
|
591
629
|
static_array_len = CompilerLangVy.extract_constant(max_elements_node, named_constants)
|
|
592
630
|
return CompilerLangVy.VyperTypeStaticArray(key_type, static_array_len)
|
|
593
|
-
elif 'value' in max_elements_node:
|
|
594
|
-
return CompilerLangVy.VyperTypeStaticArray(key_type, max_elements_node['value'])
|
|
595
|
-
|
|
596
631
|
raise Exception(
|
|
597
632
|
f"Don't know how to deal with vyper static array declaration with length {max_elements_node}")
|
|
598
633
|
|
|
@@ -411,6 +411,8 @@ def generate_modifier_finder(f: Func, internal_id: int, sym: int,
|
|
|
411
411
|
formal_strings = []
|
|
412
412
|
arg_strings = []
|
|
413
413
|
for (logged_ty, logged_name) in zip(loggable_types, loggable_names):
|
|
414
|
+
if logged_name == "":
|
|
415
|
+
continue
|
|
414
416
|
arg_strings.append(logged_name)
|
|
415
417
|
formal_strings.append(f"{logged_ty} {logged_name}")
|
|
416
418
|
modifier_body = f"modifier {modifier_name}"
|
|
@@ -1198,10 +1200,15 @@ class CertoraBuildGenerator:
|
|
|
1198
1200
|
return x[TYPE] == FUNCTION or x[TYPE] == CertoraBuildGenerator.CONSTRUCTOR_STRING
|
|
1199
1201
|
|
|
1200
1202
|
@staticmethod
|
|
1201
|
-
def collect_srcmap(data: Dict[str, Any]) ->
|
|
1203
|
+
def collect_srcmap(data: Dict[str, Any]) -> Tuple[str, str]:
|
|
1202
1204
|
# no source map object in vyper
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
+
deployed = data["evm"]["deployedBytecode"].get("sourceMap", "")
|
|
1206
|
+
if isinstance(deployed, dict):
|
|
1207
|
+
deployed = deployed.get("pc_pos_map_compressed", "")
|
|
1208
|
+
regular = data["evm"]["bytecode"].get("sourceMap", "")
|
|
1209
|
+
if isinstance(regular, dict):
|
|
1210
|
+
regular = regular.get("pc_pos_map_compressed", "")
|
|
1211
|
+
return deployed, regular
|
|
1205
1212
|
|
|
1206
1213
|
@staticmethod
|
|
1207
1214
|
def collect_varmap(contract: str, data: Dict[str, Any]) -> Any:
|
|
@@ -1516,7 +1523,8 @@ class CertoraBuildGenerator:
|
|
|
1516
1523
|
sources_dict = {str(contract_file_posix_abs): {
|
|
1517
1524
|
"urls": [str(contract_file_posix_abs)]}} # type: Dict[str, Dict[str, Any]]
|
|
1518
1525
|
output_selection = ["transientStorageLayout", "storageLayout", "abi", "evm.bytecode",
|
|
1519
|
-
"evm.deployedBytecode", "evm.methodIdentifiers", "evm.assembly"
|
|
1526
|
+
"evm.deployedBytecode", "evm.methodIdentifiers", "evm.assembly",
|
|
1527
|
+
"evm.bytecode.functionDebugData"]
|
|
1520
1528
|
ast_selection = ["id", "ast"]
|
|
1521
1529
|
elif compiler_collector_lang == CompilerLangVy():
|
|
1522
1530
|
with open(contract_file_posix_abs) as f:
|
|
@@ -1739,6 +1747,10 @@ class CertoraBuildGenerator:
|
|
|
1739
1747
|
sdc_name = f"{Path(build_arg_contract_file).name}_{file_index}"
|
|
1740
1748
|
compilation_path = self.get_compilation_path(sdc_name)
|
|
1741
1749
|
self.file_to_sdc_name[Util.abs_norm_path(build_arg_contract_file)] = sdc_name
|
|
1750
|
+
|
|
1751
|
+
compiler_collector = self.compiler_coll_factory \
|
|
1752
|
+
.get_compiler_collector(Path(path_for_compiler_collector_file))
|
|
1753
|
+
|
|
1742
1754
|
# update remappings and collect_cmd:
|
|
1743
1755
|
if not is_vyper:
|
|
1744
1756
|
Util.safe_create_dir(compilation_path)
|
|
@@ -1786,8 +1798,10 @@ class CertoraBuildGenerator:
|
|
|
1786
1798
|
compiler_logger.debug(f"collect_cmd: {collect_cmd}\n")
|
|
1787
1799
|
else:
|
|
1788
1800
|
compiler_ver_to_run = get_relevant_compiler(Path(build_arg_contract_file), self.context)
|
|
1789
|
-
|
|
1790
|
-
|
|
1801
|
+
path_string = ""
|
|
1802
|
+
if compiler_collector.compiler_version[1] < 4:
|
|
1803
|
+
path_string = f' -p "{self.context.solc_allow_path}"'
|
|
1804
|
+
collect_cmd = f'{compiler_ver_to_run}{path_string} -o "{compilation_path}" ' \
|
|
1791
1805
|
f'--standard-json'
|
|
1792
1806
|
|
|
1793
1807
|
# Make sure compilation artifacts are always deleted
|
|
@@ -1800,9 +1814,6 @@ class CertoraBuildGenerator:
|
|
|
1800
1814
|
# (we do not try to save a big chain history of changes, just a previous and current)
|
|
1801
1815
|
self.backup_compiler_outputs(sdc_name, smart_contract_lang, "prev")
|
|
1802
1816
|
|
|
1803
|
-
compiler_collector = self.compiler_coll_factory \
|
|
1804
|
-
.get_compiler_collector(Path(path_for_compiler_collector_file))
|
|
1805
|
-
|
|
1806
1817
|
# Standard JSON
|
|
1807
1818
|
remappings = [] if isinstance(compiler_collector, CompilerCollectorYul) else self.context.remappings
|
|
1808
1819
|
input_for_solc = self.standard_json(Path(file_abs_path), build_arg_contract_file, remappings,
|
|
@@ -2246,6 +2257,14 @@ class CertoraBuildGenerator:
|
|
|
2246
2257
|
build_arg_contract_file)
|
|
2247
2258
|
immutables = self.collect_immutables(contract_data, build_arg_contract_file, compiler_lang)
|
|
2248
2259
|
|
|
2260
|
+
internal_function_entrypoints = set([])
|
|
2261
|
+
|
|
2262
|
+
if compiler_lang == CompilerLangSol() and "functionDebugData" in contract_data["evm"]["deployedBytecode"]:
|
|
2263
|
+
debug = contract_data["evm"]["deployedBytecode"]["functionDebugData"]
|
|
2264
|
+
for (_, v) in debug.items():
|
|
2265
|
+
if "entryPoint" in v and v["entryPoint"] is not None:
|
|
2266
|
+
internal_function_entrypoints.add(v["entryPoint"])
|
|
2267
|
+
|
|
2249
2268
|
if self.context.internal_funcs is not None:
|
|
2250
2269
|
all_internal_functions: Dict[str, Any] = \
|
|
2251
2270
|
Util.read_json_file(self.context.internal_funcs)
|
|
@@ -2296,7 +2315,8 @@ class CertoraBuildGenerator:
|
|
|
2296
2315
|
extension_contracts=list(),
|
|
2297
2316
|
local_assignments={},
|
|
2298
2317
|
branches={},
|
|
2299
|
-
requires={}
|
|
2318
|
+
requires={},
|
|
2319
|
+
internal_starts=list(internal_function_entrypoints)
|
|
2300
2320
|
)
|
|
2301
2321
|
|
|
2302
2322
|
@staticmethod
|
|
@@ -2527,7 +2547,7 @@ class CertoraBuildGenerator:
|
|
|
2527
2547
|
sources_from_pre_finder_SDCs |= sdc.all_contract_files
|
|
2528
2548
|
sources = self.collect_sources(context, certora_verify_generator, sources_from_pre_finder_SDCs)
|
|
2529
2549
|
try:
|
|
2530
|
-
|
|
2550
|
+
build_source_tree(sources, context)
|
|
2531
2551
|
except Exception as e:
|
|
2532
2552
|
build_logger.debug(f"build_source_tree failed. Sources: {sources}", exc_info=e)
|
|
2533
2553
|
raise
|
|
@@ -2698,8 +2718,8 @@ class CertoraBuildGenerator:
|
|
|
2698
2718
|
return None
|
|
2699
2719
|
cloned_field = storage_field_info.copy()
|
|
2700
2720
|
cloned_field["slot"] = str(link_slot)
|
|
2701
|
-
#
|
|
2702
|
-
cloned_field["label"] =
|
|
2721
|
+
# Don't bother trying to uniquify the name
|
|
2722
|
+
cloned_field["label"] = var_name
|
|
2703
2723
|
return cloned_field
|
|
2704
2724
|
|
|
2705
2725
|
def handle_one_extension(storage_ext: str) -> tuple[Any, str, List[Dict[str, Any]]] :
|
|
@@ -2750,23 +2770,34 @@ class CertoraBuildGenerator:
|
|
|
2750
2770
|
if target_contract.storage_layout.get("types") is None:
|
|
2751
2771
|
target_contract.storage_layout["types"] = {}
|
|
2752
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"]}
|
|
2753
2774
|
# Keep track of slots we've added, and error if we
|
|
2754
2775
|
# find two extensions extending the same slot
|
|
2755
2776
|
added_slots: Dict[str, str] = {}
|
|
2777
|
+
added_vars: Dict[str, str] = {}
|
|
2756
2778
|
for ext in extensions:
|
|
2757
2779
|
(new_fields, new_types) = to_add[ext]
|
|
2758
2780
|
|
|
2759
2781
|
for f in new_fields:
|
|
2760
|
-
# See if any of the new fields is a slot we've already added
|
|
2782
|
+
# See if any of the new fields is a slot or variable name we've already added
|
|
2761
2783
|
slot = f["slot"]
|
|
2784
|
+
var = f["label"]
|
|
2762
2785
|
if slot in added_slots:
|
|
2763
2786
|
seen = added_slots[slot]
|
|
2764
|
-
raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} already added by {seen}")
|
|
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}")
|
|
2765
2792
|
|
|
2766
2793
|
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}")
|
|
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}")
|
|
2768
2798
|
|
|
2769
2799
|
added_slots[slot] = ext
|
|
2800
|
+
added_vars[var] = ext
|
|
2770
2801
|
|
|
2771
2802
|
target_contract.storage_layout["storage"].extend(new_fields)
|
|
2772
2803
|
|
|
@@ -3003,7 +3034,7 @@ class CertoraBuildGenerator:
|
|
|
3003
3034
|
for k, v in autofinder_remappings.items():
|
|
3004
3035
|
self.function_finder_file_remappings[Util.abs_posix_path(k)] = Util.abs_posix_path(v)
|
|
3005
3036
|
new_sdcs = self.collect_for_file(new_file, i, get_compiler_lang(build_arg_contract_file),
|
|
3006
|
-
Util.get_certora_sources_dir() / self.cwd_rel_in_sources,
|
|
3037
|
+
Util.get_certora_sources_dir() / self.context.cwd_rel_in_sources,
|
|
3007
3038
|
path_for_compiler_collector_file,
|
|
3008
3039
|
sdc_pre_finder,
|
|
3009
3040
|
fail_on_compilation_error=False,
|
|
@@ -3031,7 +3062,7 @@ class CertoraBuildGenerator:
|
|
|
3031
3062
|
contract_path = Util.abs_posix_path_obj(contract_file)
|
|
3032
3063
|
rel_directory = Path(os.path.relpath(contract_file, '.')).parent
|
|
3033
3064
|
contract_filename = contract_path.name
|
|
3034
|
-
new_path = Util.get_certora_sources_dir() / self.cwd_rel_in_sources / rel_directory / contract_filename
|
|
3065
|
+
new_path = Util.get_certora_sources_dir() / self.context.cwd_rel_in_sources / rel_directory / contract_filename
|
|
3035
3066
|
new_path.parent.mkdir(parents=True, exist_ok=True)
|
|
3036
3067
|
return str(new_path)
|
|
3037
3068
|
|
|
@@ -3041,7 +3072,7 @@ class CertoraBuildGenerator:
|
|
|
3041
3072
|
This assumes those paths can be related to cwd.
|
|
3042
3073
|
"""
|
|
3043
3074
|
rel_to_cwd_path = Path(os.path.relpath(path, '.'))
|
|
3044
|
-
new_path = Util.get_certora_sources_dir() / self.cwd_rel_in_sources / rel_to_cwd_path
|
|
3075
|
+
new_path = Util.get_certora_sources_dir() / self.context.cwd_rel_in_sources / rel_to_cwd_path
|
|
3045
3076
|
return str(new_path.absolute())
|
|
3046
3077
|
|
|
3047
3078
|
def handle_links(self) -> None:
|
|
@@ -3449,20 +3480,19 @@ def sources_to_abs(sources: Set[Path]) -> Set[Path]:
|
|
|
3449
3480
|
return result
|
|
3450
3481
|
|
|
3451
3482
|
|
|
3452
|
-
def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bool = False) ->
|
|
3483
|
+
def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bool = False) -> None:
|
|
3453
3484
|
"""
|
|
3454
3485
|
Copies files to .certora_sources
|
|
3455
|
-
@returns the cwd relative in sources
|
|
3456
3486
|
"""
|
|
3457
3487
|
sources = sources_to_abs(sources)
|
|
3458
|
-
cwd_rel_in_sources, common_path = CertoraBuildGenerator.get_cwd_rel_in_sources(sources)
|
|
3488
|
+
context.cwd_rel_in_sources, context.common_path = CertoraBuildGenerator.get_cwd_rel_in_sources(sources)
|
|
3459
3489
|
|
|
3460
3490
|
for source_path in sources:
|
|
3461
3491
|
is_dir = source_path.is_dir()
|
|
3462
3492
|
# copy file to the path of the file from the common root under the sources directory
|
|
3463
3493
|
|
|
3464
3494
|
# make sure directory exists
|
|
3465
|
-
target_path = Util.get_certora_sources_dir() / source_path.relative_to(common_path)
|
|
3495
|
+
target_path = Util.get_certora_sources_dir() / source_path.relative_to(context.common_path)
|
|
3466
3496
|
target_directory = target_path if is_dir else target_path.parent
|
|
3467
3497
|
try:
|
|
3468
3498
|
target_directory.mkdir(parents=True, exist_ok=True)
|
|
@@ -3491,7 +3521,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
|
|
|
3491
3521
|
raise
|
|
3492
3522
|
|
|
3493
3523
|
# the empty file .cwd is written in the source tree to denote the current working directory
|
|
3494
|
-
cwd_file_path = Util.get_certora_sources_dir() / cwd_rel_in_sources / Util.CWD_FILE
|
|
3524
|
+
cwd_file_path = Util.get_certora_sources_dir() / context.cwd_rel_in_sources / Util.CWD_FILE
|
|
3495
3525
|
cwd_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
3496
3526
|
cwd_file_path.touch()
|
|
3497
3527
|
|
|
@@ -3500,7 +3530,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
|
|
|
3500
3530
|
if rust_proj_dir:
|
|
3501
3531
|
proj_dir_parent_relative = os.path.relpath(rust_proj_dir, os.getcwd())
|
|
3502
3532
|
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 /
|
|
3533
|
+
proj_dir_parent_file = (Util.get_certora_sources_dir() / context.cwd_rel_in_sources / proj_dir_parent_relative /
|
|
3504
3534
|
Util.PROJECT_DIR_FILE)
|
|
3505
3535
|
proj_dir_parent_file.parent.mkdir(parents=True, exist_ok=True)
|
|
3506
3536
|
proj_dir_parent_file.touch()
|
|
@@ -3516,9 +3546,6 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
|
|
|
3516
3546
|
build_logger.debug("Couldn't copy repro conf to certora sources.", exc_info=e)
|
|
3517
3547
|
raise
|
|
3518
3548
|
|
|
3519
|
-
return cwd_rel_in_sources
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
3549
|
def build_from_scratch(certora_build_generator: CertoraBuildGenerator,
|
|
3523
3550
|
certora_verify_generator: CertoraVerifyGenerator,
|
|
3524
3551
|
build_cache_enabled: bool) -> CachedFiles:
|
|
@@ -3661,8 +3688,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
|
|
|
3661
3688
|
|
|
3662
3689
|
# Start by syntax checking, if we're in the right mode
|
|
3663
3690
|
if Cv.mode_has_spec_file(context) and not context.build_only and not ignore_spec_syntax_check:
|
|
3664
|
-
|
|
3665
|
-
if attr:
|
|
3691
|
+
if context.disable_local_typechecking:
|
|
3666
3692
|
build_logger.warning(
|
|
3667
3693
|
"Local checks of CVL specification files disabled. It is recommended to enable the checks.")
|
|
3668
3694
|
else:
|
|
@@ -3675,6 +3701,11 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
|
|
|
3675
3701
|
certora_verify_generator,
|
|
3676
3702
|
certora_build_cache_manager)
|
|
3677
3703
|
|
|
3704
|
+
# avoid running the same test over and over again for each split run, context.split_rules is true only for
|
|
3705
|
+
# the first run and is set to [] for split runs
|
|
3706
|
+
if context.split_rules:
|
|
3707
|
+
Ctx.run_local_spec_check(True, context)
|
|
3708
|
+
|
|
3678
3709
|
# .certora_verify.json is always constructed even if build cache is enabled
|
|
3679
3710
|
# Sources construction should only happen when rebuilding
|
|
3680
3711
|
# Build sources tree
|
|
@@ -107,7 +107,8 @@ class ContractInSDC:
|
|
|
107
107
|
extension_contracts: List[ContractExtension],
|
|
108
108
|
local_assignments: Dict[str, UnspecializedSourceFinder],
|
|
109
109
|
branches: Dict[str, UnspecializedSourceFinder],
|
|
110
|
-
requires: Dict[str, UnspecializedSourceFinder]
|
|
110
|
+
requires: Dict[str, UnspecializedSourceFinder],
|
|
111
|
+
internal_starts: List[int]
|
|
111
112
|
):
|
|
112
113
|
self.name = name
|
|
113
114
|
self.original_file = source_file
|
|
@@ -139,6 +140,7 @@ class ContractInSDC:
|
|
|
139
140
|
self.original_file_name = Path(source_file).name
|
|
140
141
|
self.compiler_collector = compiler_collector
|
|
141
142
|
self.compiler_parameters = compiler_parameters
|
|
143
|
+
self.internal_starts = internal_starts
|
|
142
144
|
|
|
143
145
|
if not self.compiler_collector:
|
|
144
146
|
compiler_version = ""
|
|
@@ -193,7 +195,8 @@ class ContractInSDC:
|
|
|
193
195
|
"compilerParameters": None if not self.compiler_parameters else self.compiler_parameters.as_dict(),
|
|
194
196
|
"sourceBytes": None if self.source_bytes is None else self.source_bytes.as_dict(),
|
|
195
197
|
"extensionContracts": [e.as_dict() for e in self.extension_contracts],
|
|
196
|
-
"localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()}
|
|
198
|
+
"localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()},
|
|
199
|
+
"internalFunctionStarts": self.internal_starts
|
|
197
200
|
}
|
|
198
201
|
# "sourceHints": {"localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()},
|
|
199
202
|
# "branches": {k: v.as_dict() for k, v in self.branches.items()},
|
|
@@ -16,32 +16,40 @@
|
|
|
16
16
|
import glob
|
|
17
17
|
import shutil
|
|
18
18
|
from pathlib import Path
|
|
19
|
-
from typing import Set
|
|
19
|
+
from typing import Set, List
|
|
20
20
|
|
|
21
21
|
from CertoraProver.certoraBuild import build_source_tree
|
|
22
22
|
from CertoraProver.certoraContextClass import CertoraContext
|
|
23
|
-
from CertoraProver.certoraParseBuildScript import
|
|
23
|
+
from CertoraProver.certoraParseBuildScript import run_rust_build
|
|
24
24
|
import CertoraProver.certoraContextAttributes as Attrs
|
|
25
25
|
from Shared import certoraUtils as Util
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def build_rust_app(context: CertoraContext) -> None:
|
|
29
|
+
build_command: List[str] = []
|
|
30
|
+
feature_flag = None
|
|
29
31
|
if context.build_script:
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
raise Util.CertoraUserInputError("failed to get target executable")
|
|
32
|
+
build_command = [context.build_script]
|
|
33
|
+
feature_flag = '--cargo_features'
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
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)
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
if build_command:
|
|
43
|
+
if context.cargo_features is not None:
|
|
44
|
+
build_command.extend([feature_flag] + context.cargo_features)
|
|
40
45
|
|
|
41
|
-
|
|
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)
|
|
42
52
|
|
|
43
|
-
except Exception as e:
|
|
44
|
-
raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
|
|
45
53
|
else:
|
|
46
54
|
if not context.files:
|
|
47
55
|
raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
|
|
@@ -54,17 +62,49 @@ def build_rust_app(context: CertoraContext) -> None:
|
|
|
54
62
|
except Exception as e:
|
|
55
63
|
raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
|
|
56
64
|
|
|
57
|
-
|
|
65
|
+
copy_files_to_build_dir(context)
|
|
66
|
+
|
|
67
|
+
sources: Set[Path] = set()
|
|
68
|
+
collect_files_from_rust_sources(context, sources)
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
# Create generators
|
|
72
|
+
build_source_tree(sources, context)
|
|
58
73
|
|
|
74
|
+
except Exception as e:
|
|
75
|
+
raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
77
|
+
|
|
78
|
+
def add_solana_files_from_prover_args(context: CertoraContext) -> None:
|
|
79
|
+
if context.prover_args:
|
|
80
|
+
inlining_file = False
|
|
81
|
+
summaries_file = False
|
|
82
|
+
for prover_arg in context.prover_args:
|
|
83
|
+
for arg in prover_arg.split(' '):
|
|
84
|
+
if inlining_file:
|
|
85
|
+
context.solana_inlining = context.solana_inlining or [Path(arg)]
|
|
86
|
+
inlining_file = False
|
|
87
|
+
if summaries_file:
|
|
88
|
+
context.solana_summaries = context.solana_summaries or [Path(arg)]
|
|
89
|
+
summaries_file = False
|
|
90
|
+
|
|
91
|
+
if arg == '-solanaInlining':
|
|
92
|
+
inlining_file = True
|
|
93
|
+
elif arg == '-solanaSummaries':
|
|
94
|
+
summaries_file = True
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def add_solana_files_to_sources(context: CertoraContext, sources: Set[Path]) -> None:
|
|
98
|
+
for attr in [Attrs.SolanaProverAttributes.SOLANA_INLINING,
|
|
99
|
+
Attrs.SolanaProverAttributes.SOLANA_SUMMARIES,
|
|
100
|
+
Attrs.SolanaProverAttributes.BUILD_SCRIPT,
|
|
101
|
+
Attrs.SolanaProverAttributes.FILES]:
|
|
62
102
|
attr_name = attr.get_conf_key()
|
|
63
103
|
attr_value = getattr(context, attr_name, None)
|
|
64
104
|
if not attr_value:
|
|
65
105
|
continue
|
|
66
106
|
if isinstance(attr_value, str):
|
|
67
|
-
attr_value = [
|
|
107
|
+
attr_value = [attr_value]
|
|
68
108
|
if not isinstance(attr_value, list):
|
|
69
109
|
raise Util.CertoraUserInputError(f"{attr_value} is not a valid value for {attr_name} {attr_value}. Value "
|
|
70
110
|
f"must be a string or a llist ")
|
|
@@ -72,44 +112,40 @@ def add_solana_files(context: CertoraContext, sources: Set[Path]) -> None:
|
|
|
72
112
|
for file_path in file_paths:
|
|
73
113
|
if not file_path.exists():
|
|
74
114
|
raise Util.CertoraUserInputError(f"in {attr_name} file {file_path} does not exist")
|
|
75
|
-
sources.add(file_path.absolute())
|
|
115
|
+
sources.add(file_path.absolute().resolve())
|
|
76
116
|
|
|
77
117
|
|
|
78
118
|
def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path]) -> None:
|
|
79
119
|
patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
|
|
80
120
|
exclude_dirs = [".certora_internal"]
|
|
81
121
|
|
|
82
|
-
|
|
122
|
+
if hasattr(context, 'rust_project_directory'):
|
|
123
|
+
project_directory = Path(context.rust_project_directory)
|
|
83
124
|
|
|
84
|
-
|
|
85
|
-
|
|
125
|
+
if not project_directory.is_dir():
|
|
126
|
+
raise ValueError(f"The given directory '{project_directory}' is not valid.")
|
|
86
127
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
128
|
+
for source in context.rust_sources:
|
|
129
|
+
for file in glob.glob(f'{project_directory.joinpath(source)}', recursive=True):
|
|
130
|
+
file_path = Path(file)
|
|
131
|
+
if any(excluded in file_path.parts for excluded in exclude_dirs):
|
|
132
|
+
continue
|
|
133
|
+
if file_path.is_file() and any(file_path.match(pattern) for pattern in patterns):
|
|
134
|
+
sources.add(file_path)
|
|
94
135
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
136
|
+
sources.add(project_directory.absolute())
|
|
137
|
+
if Path(context.build_script).exists():
|
|
138
|
+
sources.add(Path(context.build_script).resolve())
|
|
98
139
|
if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
|
|
99
140
|
sources.add(Path(context.conf_file).absolute())
|
|
100
|
-
add_solana_files(context, sources)
|
|
101
141
|
|
|
142
|
+
add_solana_files_from_prover_args(context)
|
|
143
|
+
add_solana_files_to_sources(context, sources)
|
|
102
144
|
|
|
103
|
-
def copy_files_to_build_dir(context: CertoraContext) -> None:
|
|
104
|
-
rust_executable = Path(context.rust_project_directory) / context.rust_executables
|
|
105
|
-
shutil.copyfile(rust_executable, Util.get_build_dir() / rust_executable.name)
|
|
106
|
-
|
|
107
|
-
additional_files = (getattr(context, 'solana_inlining', None) or []) + \
|
|
108
|
-
(getattr(context, 'solana_summaries', None) or [])
|
|
109
145
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
146
|
+
def copy_files_to_build_dir(context: CertoraContext) -> None:
|
|
147
|
+
assert context.files, "copy_files_to_build_dir: expecting files to be non-empty"
|
|
148
|
+
shutil.copyfile(context.files[0], Util.get_build_dir() / Path(context.files[0]).name)
|
|
113
149
|
|
|
114
150
|
if rust_logs := getattr(context, 'rust_logs_stdout', None):
|
|
115
151
|
shutil.copy(Path(rust_logs), Util.get_build_dir() / Path(rust_logs).name)
|