certora-cli-beta-mirror 7.30.1__py3-none-any.whl → 8.0.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/CompilerCollectorFactory.py +11 -2
- certora_cli/CertoraProver/certoraBuild.py +68 -62
- certora_cli/CertoraProver/certoraBuildCacheManager.py +17 -16
- certora_cli/CertoraProver/certoraBuildRust.py +33 -21
- certora_cli/{Shared/rustProverCommon.py → CertoraProver/certoraBuildSui.py} +24 -18
- certora_cli/CertoraProver/certoraCloudIO.py +42 -33
- certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +62 -51
- certora_cli/CertoraProver/certoraCollectRunMetadata.py +20 -5
- certora_cli/CertoraProver/certoraConfigIO.py +17 -14
- certora_cli/CertoraProver/certoraContext.py +62 -10
- certora_cli/CertoraProver/certoraContextAttributes.py +132 -203
- certora_cli/CertoraProver/certoraContextValidator.py +108 -101
- certora_cli/CertoraProver/certoraParseBuildScript.py +4 -3
- certora_cli/CertoraProver/certoraVerifyGenerator.py +9 -4
- certora_cli/CertoraProver/splitRules.py +2 -0
- certora_cli/CertoraProver/storageExtension.py +0 -35
- certora_cli/Mutate/mutateApp.py +16 -10
- certora_cli/Mutate/mutateAttributes.py +11 -0
- certora_cli/Shared/certoraAttrUtil.py +11 -5
- certora_cli/Shared/certoraUtils.py +50 -47
- certora_cli/Shared/certoraValidateFuncs.py +29 -15
- certora_cli/Shared/proverCommon.py +6 -2
- certora_cli/certoraCVLFormatter.py +76 -0
- certora_cli/certoraConcord.py +39 -0
- certora_cli/certoraRun.py +53 -91
- certora_cli/certoraSolanaProver.py +1 -1
- certora_cli/certoraSorobanProver.py +1 -1
- {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/METADATA +4 -3
- {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/RECORD +36 -33
- {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/entry_points.txt +1 -0
- certora_jars/ASTExtraction.jar +0 -0
- certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
- certora_jars/Typechecker.jar +0 -0
- {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/LICENSE +0 -0
- {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/WHEEL +0 -0
- {certora_cli_beta_mirror-7.30.1.dist-info → certora_cli_beta_mirror-8.0.0.dist-info}/top_level.txt +0 -0
|
@@ -25,8 +25,9 @@ from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerColle
|
|
|
25
25
|
from CertoraProver.Compiler.CompilerCollectorSol import CompilerCollectorSol, CompilerLangSol
|
|
26
26
|
from CertoraProver.Compiler.CompilerCollectorYul import CompilerLangYul, CompilerCollectorYul
|
|
27
27
|
from CertoraProver.Compiler.CompilerCollectorVy import CompilerCollectorVy, CompilerLangVy
|
|
28
|
-
from Shared.certoraUtils import
|
|
28
|
+
from Shared.certoraUtils import remove_file, get_certora_config_dir
|
|
29
29
|
from CertoraProver.certoraContextClass import CertoraContext
|
|
30
|
+
import CertoraProver.certoraContext as Ctx
|
|
30
31
|
|
|
31
32
|
# logger for running the Solidity compiler and reporting any errors it emits
|
|
32
33
|
compiler_logger = logging.getLogger("compiler")
|
|
@@ -42,8 +43,16 @@ def get_relevant_compiler(contract_file_path: Path, context: CertoraContext) ->
|
|
|
42
43
|
|
|
43
44
|
if contract_file_path.is_absolute():
|
|
44
45
|
contract_file_path = Path(os.path.relpath(contract_file_path, Path.cwd()))
|
|
46
|
+
|
|
47
|
+
# If the contract file is in the sources directory, we want to use the relative path from the "cwd" in the source tree.
|
|
48
|
+
if Util.get_certora_sources_dir() in contract_file_path.parents:
|
|
49
|
+
cwd_in_source = Util.get_certora_sources_dir() / context.cwd_rel_in_sources
|
|
50
|
+
contract_file_path = Path(os.path.relpath(contract_file_path, cwd_in_source))
|
|
51
|
+
|
|
45
52
|
if context.compiler_map:
|
|
46
|
-
match =
|
|
53
|
+
match = Ctx.get_map_attribute_value(context, contract_file_path, 'compiler')
|
|
54
|
+
assert isinstance(match, str), (f"In the attribute compiler_map, {contract_file_path} expected to be matched "
|
|
55
|
+
f"to a string, {match} is of type {type(match)} not a string")
|
|
47
56
|
if match:
|
|
48
57
|
return match
|
|
49
58
|
else:
|
|
@@ -39,8 +39,6 @@ from CertoraProver.certoraSourceFinders import add_source_finders
|
|
|
39
39
|
from CertoraProver.certoraVerifyGenerator import CertoraVerifyGenerator
|
|
40
40
|
from CertoraProver.certoraContractFuncs import Func, InternalFunc, STATEMUT, SourceBytes
|
|
41
41
|
|
|
42
|
-
from Shared.certoraUtils import is_relative_to
|
|
43
|
-
|
|
44
42
|
scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
|
|
45
43
|
sys.path.insert(0, str(scripts_dir_path))
|
|
46
44
|
from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerCollector
|
|
@@ -450,8 +448,12 @@ def convert_pathname_to_posix(json_dict: Dict[str, Any], entry: str, smart_contr
|
|
|
450
448
|
if path_obj.is_file():
|
|
451
449
|
json_dict_posix_paths[path_obj.as_posix()] = json_dict[entry][file_path]
|
|
452
450
|
else:
|
|
453
|
-
|
|
454
|
-
|
|
451
|
+
json_dict_str = str(json_dict)
|
|
452
|
+
# protecting against long strings
|
|
453
|
+
if len(json_dict_str) > 200:
|
|
454
|
+
json_dict_str = json_dict_str[:200] + '...'
|
|
455
|
+
fatal_error(compiler_logger, f"The path of the source file {file_path} "
|
|
456
|
+
f"in the standard json file does not exist!\n{json_dict_str} ")
|
|
455
457
|
json_dict[entry] = json_dict_posix_paths
|
|
456
458
|
|
|
457
459
|
|
|
@@ -1361,46 +1363,48 @@ class CertoraBuildGenerator:
|
|
|
1361
1363
|
|
|
1362
1364
|
return bytecode
|
|
1363
1365
|
|
|
1364
|
-
def
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1366
|
+
def get_solc_via_ir_value(self, contract_file_path: Path) -> bool:
|
|
1367
|
+
match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_via_ir')
|
|
1368
|
+
assert isinstance(match, (bool, type(None))), f"Expected solc_via_ir to be bool or None, got {type(match)}"
|
|
1369
|
+
return bool(match)
|
|
1370
|
+
|
|
1371
|
+
def get_solc_evm_version_value(self, contract_file_path: Path) -> Optional[str]:
|
|
1372
|
+
match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_evm_version')
|
|
1373
|
+
assert isinstance(match, (str, type(None))), f"Expected solc_evm_version to be string or None, got {type(match)}"
|
|
1374
|
+
return match
|
|
1375
|
+
|
|
1376
|
+
def get_solc_optimize_value(self, contract_file_path: Path) -> Optional[str]:
|
|
1377
|
+
match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_optimize')
|
|
1378
|
+
assert isinstance(match, (str, type(None))), f"Expected solc_optimize to be string or None, got {type(match)}"
|
|
1379
|
+
return match
|
|
1380
|
+
|
|
1381
|
+
def _handle_via_ir(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
|
|
1382
|
+
if self.get_solc_via_ir_value(contract_file_path):
|
|
1372
1383
|
settings_dict["viaIR"] = True
|
|
1373
1384
|
|
|
1374
|
-
def _handle_evm_version(self, contract_file_path:
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
settings_dict["evmVersion"] = match
|
|
1379
|
-
elif self.context.solc_evm_version:
|
|
1380
|
-
settings_dict["evmVersion"] = self.context.solc_evm_version
|
|
1385
|
+
def _handle_evm_version(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
|
|
1386
|
+
match = self.get_solc_evm_version_value(contract_file_path)
|
|
1387
|
+
if match:
|
|
1388
|
+
settings_dict["evmVersion"] = match
|
|
1381
1389
|
|
|
1382
|
-
def _handle_optimize(self, contract_file_path:
|
|
1390
|
+
def _handle_optimize(self, contract_file_path: Path, settings_dict: Dict[str, Any],
|
|
1383
1391
|
compiler_collector: CompilerCollector) -> None:
|
|
1384
1392
|
"""
|
|
1385
1393
|
@param contract_file_path: the contract that we are working on
|
|
1386
1394
|
@param settings_dict: data structure for sending to the solc compiler
|
|
1387
1395
|
"""
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
if match and int(match) > 0:
|
|
1391
|
-
settings_dict["optimizer"] = {"enabled": True}
|
|
1392
|
-
settings_dict["optimizer"]['runs'] = int(match)
|
|
1393
|
-
elif self.context.solc_optimize:
|
|
1396
|
+
match = self.get_solc_optimize_value(contract_file_path)
|
|
1397
|
+
if match:
|
|
1394
1398
|
settings_dict["optimizer"] = {"enabled": True}
|
|
1395
|
-
if int(
|
|
1396
|
-
settings_dict["optimizer"]['runs'] = int(
|
|
1399
|
+
if int(match) > 0:
|
|
1400
|
+
settings_dict["optimizer"]['runs'] = int(match)
|
|
1397
1401
|
|
|
1398
1402
|
# if optimizer is true, we should also define settings_dict["optimizer"]["details"]
|
|
1399
1403
|
# for both optimize map and optimize
|
|
1400
1404
|
optimizer = settings_dict.get("optimizer")
|
|
1401
1405
|
if optimizer and isinstance(optimizer, dict) and optimizer.get('enabled'):
|
|
1402
1406
|
# if we are not disabling finder friendly optimizer specifically, enable it whenever viaIR is also enabled
|
|
1403
|
-
if not self.context.strict_solc_optimizer and self.
|
|
1407
|
+
if not self.context.strict_solc_optimizer and self.get_solc_via_ir_value(contract_file_path):
|
|
1404
1408
|
# The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
|
|
1405
1409
|
# full inliner step removed
|
|
1406
1410
|
solc0_8_26_to_0_8_30 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
|
|
@@ -1426,13 +1430,18 @@ class CertoraBuildGenerator:
|
|
|
1426
1430
|
compiler_version = compiler_collector.compiler_version
|
|
1427
1431
|
major, minor, patch = compiler_version
|
|
1428
1432
|
|
|
1429
|
-
err_msg =
|
|
1430
|
-
|
|
1433
|
+
err_msg = \
|
|
1434
|
+
f"Unsupported solc version {major}.{minor}.{patch} for `solc_via_ir`. " \
|
|
1435
|
+
f"Supported versions: 0.6.7 – 0.8.25.\n" \
|
|
1436
|
+
f"Use `solc_via_ir_map` to disable `solc_via_ir` for specific files with older compiler versions."
|
|
1437
|
+
|
|
1431
1438
|
yul_optimizer_steps = None
|
|
1432
1439
|
if major != 0:
|
|
1433
1440
|
raise Util.CertoraUserInputError(err_msg)
|
|
1434
1441
|
elif minor < 6 or (minor == 6 and patch < 7):
|
|
1435
1442
|
raise Util.CertoraUserInputError(err_msg)
|
|
1443
|
+
elif self.context.yul_optimizer_steps:
|
|
1444
|
+
yul_optimizer_steps = self.context.yul_optimizer_steps
|
|
1436
1445
|
elif (minor == 6 and patch >= 7) or (minor == 7 and 0 <= patch <= 1):
|
|
1437
1446
|
yul_optimizer_steps = solc0_6_7_to_0_7_1
|
|
1438
1447
|
elif minor == 7 and 2 <= patch <= 5:
|
|
@@ -1470,7 +1479,7 @@ class CertoraBuildGenerator:
|
|
|
1470
1479
|
for opt_pass in self.context.disable_solc_optimizers:
|
|
1471
1480
|
settings_dict["optimizer"]["details"][opt_pass] = False
|
|
1472
1481
|
|
|
1473
|
-
def _fill_codegen_related_options(self, contract_file_path:
|
|
1482
|
+
def _fill_codegen_related_options(self, contract_file_path: Path, settings_dict: Dict[str, Any],
|
|
1474
1483
|
compiler_collector: CompilerCollector) -> None:
|
|
1475
1484
|
"""
|
|
1476
1485
|
Fills options that control how we call solc and affect the bytecode in some way
|
|
@@ -1551,7 +1560,7 @@ class CertoraBuildGenerator:
|
|
|
1551
1560
|
}
|
|
1552
1561
|
}
|
|
1553
1562
|
|
|
1554
|
-
self._fill_codegen_related_options(contract_file_as_provided, settings_dict, compiler_collector)
|
|
1563
|
+
self._fill_codegen_related_options(Path(contract_file_as_provided), settings_dict, compiler_collector)
|
|
1555
1564
|
|
|
1556
1565
|
result_dict = {"language": compiler_collector_lang.name, "sources": sources_dict, "settings": settings_dict}
|
|
1557
1566
|
# debug_print("Standard json input")
|
|
@@ -2286,7 +2295,7 @@ class CertoraBuildGenerator:
|
|
|
2286
2295
|
|
|
2287
2296
|
if compiler_lang == CompilerLangSol():
|
|
2288
2297
|
settings_dict: Dict[str, Any] = {}
|
|
2289
|
-
self._fill_codegen_related_options(build_arg_contract_file, settings_dict,
|
|
2298
|
+
self._fill_codegen_related_options(Path(build_arg_contract_file), settings_dict,
|
|
2290
2299
|
compiler_collector_for_contract_file)
|
|
2291
2300
|
solc_optimizer_on, solc_optimizer_runs = self.solc_setting_optimizer_runs(settings_dict)
|
|
2292
2301
|
solc_via_ir = self.solc_setting_via_ir(settings_dict)
|
|
@@ -2416,7 +2425,7 @@ class CertoraBuildGenerator:
|
|
|
2416
2425
|
# if no function finder mode set, determine based on viaIR enabled or not:
|
|
2417
2426
|
if self.context.function_finder_mode is None:
|
|
2418
2427
|
# in via-ir, should not compress
|
|
2419
|
-
if self.
|
|
2428
|
+
if self.get_solc_via_ir_value(Path(contract_file)):
|
|
2420
2429
|
should_compress = False
|
|
2421
2430
|
else:
|
|
2422
2431
|
should_compress = True
|
|
@@ -2682,7 +2691,7 @@ class CertoraBuildGenerator:
|
|
|
2682
2691
|
self.handle_erc7201_annotations()
|
|
2683
2692
|
self.handle_storage_extension_harnesses()
|
|
2684
2693
|
|
|
2685
|
-
def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage]) -> NewStorageInfo:
|
|
2694
|
+
def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage], compiler_version: str) -> NewStorageInfo:
|
|
2686
2695
|
"""
|
|
2687
2696
|
Given a file containing a contract with namespaced storage, extract the storage information
|
|
2688
2697
|
corresponding to the namespaced types.
|
|
@@ -2707,7 +2716,9 @@ class CertoraBuildGenerator:
|
|
|
2707
2716
|
# original file is accessed also in the actual code, and compiling it
|
|
2708
2717
|
# directly can cause issues.
|
|
2709
2718
|
tmp_file.write(f"import \"{original_file}\";\n\n")
|
|
2710
|
-
|
|
2719
|
+
rel_path = os.path.relpath(tmp_file.name, Path.cwd())
|
|
2720
|
+
if self.context.compiler_map:
|
|
2721
|
+
self.context.compiler_map.update({rel_path: compiler_version}, last=False)
|
|
2711
2722
|
# Write the harness contract with dummy fields for each namespaced storage
|
|
2712
2723
|
var_to_slot = storageExtension.write_harness_contract(tmp_file, harness_name, ns_storage)
|
|
2713
2724
|
tmp_file.flush()
|
|
@@ -2720,13 +2731,6 @@ class CertoraBuildGenerator:
|
|
|
2720
2731
|
build_dir / f"{Path(original_file).stem}_storage_extension.sol"
|
|
2721
2732
|
)
|
|
2722
2733
|
|
|
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
2734
|
# normalize the path exactly the way collect_for_file expects it:
|
|
2731
2735
|
abs_path = Util.abs_posix_path(tmp_file.name)
|
|
2732
2736
|
self.context.file_to_contract[abs_path] = {harness_name}
|
|
@@ -2794,7 +2798,8 @@ class CertoraBuildGenerator:
|
|
|
2794
2798
|
continue
|
|
2795
2799
|
|
|
2796
2800
|
# Now that we have all the storage layout information, extract it once
|
|
2797
|
-
slayouts[key] = self.extract_slayout(imported_file, ns_storage
|
|
2801
|
+
slayouts[key] = self.extract_slayout(imported_file, ns_storage,
|
|
2802
|
+
get_relevant_compiler(Path(target_file), self.context))
|
|
2798
2803
|
|
|
2799
2804
|
if self.context.test == str(Util.TestValue.STORAGE_EXTENSION_LAYOUT):
|
|
2800
2805
|
raise Util.TestResultsReady(slayouts)
|
|
@@ -3575,7 +3580,11 @@ class CertoraBuildGenerator:
|
|
|
3575
3580
|
return sources
|
|
3576
3581
|
|
|
3577
3582
|
def __del__(self) -> None:
|
|
3578
|
-
|
|
3583
|
+
try:
|
|
3584
|
+
self.cleanup()
|
|
3585
|
+
except ImportError:
|
|
3586
|
+
# Avoiding Python interpreter shutdown exceptions which are safe to ignore
|
|
3587
|
+
pass
|
|
3579
3588
|
|
|
3580
3589
|
|
|
3581
3590
|
# make sure each source file exists and its path is in absolute format
|
|
@@ -3614,7 +3623,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
|
|
|
3614
3623
|
|
|
3615
3624
|
try:
|
|
3616
3625
|
if overwrite:
|
|
3617
|
-
# expecting target path to exist.
|
|
3626
|
+
# expecting the target path to exist.
|
|
3618
3627
|
if target_path.exists():
|
|
3619
3628
|
build_logger.debug(f"Overwriting {target_path} by copying from {source_path}")
|
|
3620
3629
|
else:
|
|
@@ -3632,7 +3641,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
|
|
|
3632
3641
|
cwd_file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
3633
3642
|
cwd_file_path.touch()
|
|
3634
3643
|
|
|
3635
|
-
# the empty file .project_directory is written in the source tree to denote the
|
|
3644
|
+
# the empty file .project_directory is written in the source tree to denote the project directory
|
|
3636
3645
|
rust_proj_dir = getattr(context, 'rust_project_directory', None)
|
|
3637
3646
|
if rust_proj_dir:
|
|
3638
3647
|
proj_dir_parent_relative = os.path.relpath(rust_proj_dir, os.getcwd())
|
|
@@ -3683,13 +3692,14 @@ def build_from_scratch(context: CertoraContext,
|
|
|
3683
3692
|
|
|
3684
3693
|
# add to cache also source files that were found in the SDCs (e.g., storage extensions)
|
|
3685
3694
|
paths_set = sdc.all_contract_files
|
|
3686
|
-
for
|
|
3687
|
-
|
|
3695
|
+
for f in context.files:
|
|
3696
|
+
path = f.split(':')[0] # `f` is either 'path/to/file.sol' or 'path/to/file.sol:ContractName'
|
|
3697
|
+
paths_set.add(Path(path).absolute())
|
|
3688
3698
|
|
|
3689
3699
|
# the contract files in SDCs are relative to .certora_sources. Which isn't good for us here.
|
|
3690
3700
|
# Need to be relative to original paths
|
|
3691
3701
|
for f in paths_set:
|
|
3692
|
-
if is_relative_to(
|
|
3702
|
+
if f.is_relative_to(absolute_sources_dir):
|
|
3693
3703
|
rel_f = f.relative_to(absolute_sources_dir)
|
|
3694
3704
|
else:
|
|
3695
3705
|
# may be an absolute path already outside .certora_sources, so we can keep it.
|
|
@@ -3724,8 +3734,7 @@ def build_from_scratch(context: CertoraContext,
|
|
|
3724
3734
|
|
|
3725
3735
|
def build_from_cache_or_scratch(context: CertoraContext,
|
|
3726
3736
|
certora_build_generator: CertoraBuildGenerator,
|
|
3727
|
-
certora_verify_generator: CertoraVerifyGenerator
|
|
3728
|
-
certora_build_cache_manager: CertoraBuildCacheManager) \
|
|
3737
|
+
certora_verify_generator: CertoraVerifyGenerator) \
|
|
3729
3738
|
-> Tuple[bool, bool, CachedFiles]:
|
|
3730
3739
|
"""
|
|
3731
3740
|
Builds either from cache (fast path) or from scratch (slow path)
|
|
@@ -3742,10 +3751,10 @@ def build_from_cache_or_scratch(context: CertoraContext,
|
|
|
3742
3751
|
False)
|
|
3743
3752
|
return cache_hit, False, cached_files
|
|
3744
3753
|
|
|
3745
|
-
build_cache_applicable =
|
|
3754
|
+
build_cache_applicable = CertoraBuildCacheManager.cache_is_applicable(context)
|
|
3746
3755
|
|
|
3747
3756
|
if not build_cache_applicable:
|
|
3748
|
-
build_cache_disabling_options =
|
|
3757
|
+
build_cache_disabling_options = CertoraBuildCacheManager.cache_disabling_options(context)
|
|
3749
3758
|
build_logger.warning("Requested to enable the build cache, but the build cache is not applicable "
|
|
3750
3759
|
f"to this run because of the given options: {build_cache_disabling_options}")
|
|
3751
3760
|
cached_files = build_from_scratch(context, certora_build_generator,
|
|
@@ -3753,7 +3762,7 @@ def build_from_cache_or_scratch(context: CertoraContext,
|
|
|
3753
3762
|
False)
|
|
3754
3763
|
return cache_hit, False, cached_files
|
|
3755
3764
|
|
|
3756
|
-
cached_files =
|
|
3765
|
+
cached_files = CertoraBuildCacheManager.build_from_cache(context)
|
|
3757
3766
|
# if no match, will rebuild from scratch
|
|
3758
3767
|
if cached_files is not None:
|
|
3759
3768
|
# write to .certora_build.json
|
|
@@ -3792,7 +3801,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
|
|
|
3792
3801
|
|
|
3793
3802
|
try:
|
|
3794
3803
|
input_config = InputConfig(context)
|
|
3795
|
-
|
|
3804
|
+
context.main_cache_key = CertoraBuildCacheManager.get_main_cache_key(context)
|
|
3796
3805
|
# Create generators
|
|
3797
3806
|
certora_build_generator = CertoraBuildGenerator(input_config, context)
|
|
3798
3807
|
|
|
@@ -3808,12 +3817,9 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
|
|
|
3808
3817
|
else:
|
|
3809
3818
|
Ctx.run_local_spec_check(False, context)
|
|
3810
3819
|
|
|
3811
|
-
certora_build_cache_manager = CertoraBuildCacheManager()
|
|
3812
|
-
|
|
3813
3820
|
cache_hit, build_cache_enabled, cached_files = build_from_cache_or_scratch(context,
|
|
3814
3821
|
certora_build_generator,
|
|
3815
|
-
certora_verify_generator
|
|
3816
|
-
certora_build_cache_manager)
|
|
3822
|
+
certora_verify_generator)
|
|
3817
3823
|
|
|
3818
3824
|
# avoid running the same test over and over again for each split run, context.split_rules is true only for
|
|
3819
3825
|
# the first run and is set to [] for split runs
|
|
@@ -3833,7 +3839,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
|
|
|
3833
3839
|
|
|
3834
3840
|
# save in build cache
|
|
3835
3841
|
if not cache_hit and build_cache_enabled and cached_files.may_store_in_build_cache:
|
|
3836
|
-
|
|
3842
|
+
CertoraBuildCacheManager.save_build_cache(context, cached_files)
|
|
3837
3843
|
|
|
3838
3844
|
certora_verify_generator.update_certora_verify_struct(True)
|
|
3839
3845
|
certora_verify_generator.dump() # second dump with properly rooted specs
|
|
@@ -66,10 +66,9 @@ class CertoraBuildCacheManager:
|
|
|
66
66
|
@returns None if no cache hit, otherwise - the matching `.certora_build.json` file, and a set of source files
|
|
67
67
|
"""
|
|
68
68
|
build_cache_dir = Util.get_certora_build_cache_dir()
|
|
69
|
-
|
|
70
|
-
main_cache_entry_dir = build_cache_dir / main_cache_key
|
|
69
|
+
main_cache_entry_dir = build_cache_dir / context.main_cache_key
|
|
71
70
|
if not main_cache_entry_dir.exists():
|
|
72
|
-
build_cache_logger.info(f"cache miss on build cache key {main_cache_key}")
|
|
71
|
+
build_cache_logger.info(f"cache miss on build cache key {context.main_cache_key}")
|
|
73
72
|
return None
|
|
74
73
|
|
|
75
74
|
# here's the tricky matching part:
|
|
@@ -92,7 +91,7 @@ class CertoraBuildCacheManager:
|
|
|
92
91
|
build_cache_logger.debug(f"Current sub build cache key computed for {file_list_file} is invalid")
|
|
93
92
|
continue
|
|
94
93
|
elif sub_cache_key_current_for_list == sub_cache_key_from_saved_entry:
|
|
95
|
-
build_cache_logger.info(f"We have a match on build cache key {main_cache_key} and "
|
|
94
|
+
build_cache_logger.info(f"We have a match on build cache key {context.main_cache_key} and "
|
|
96
95
|
f"{sub_cache_key_current_for_list}")
|
|
97
96
|
sub_cache_key = sub_cache_key_current_for_list
|
|
98
97
|
all_contract_files = set([Path(f) for f in file_list])
|
|
@@ -103,24 +102,24 @@ class CertoraBuildCacheManager:
|
|
|
103
102
|
|
|
104
103
|
if sub_cache_key is None:
|
|
105
104
|
build_cache_logger.info("All sub-cache-key file list files missed, cache miss on build cache key "
|
|
106
|
-
f"{main_cache_key}")
|
|
105
|
+
f"{context.main_cache_key}")
|
|
107
106
|
return None
|
|
108
107
|
|
|
109
108
|
# cache hit
|
|
110
109
|
certora_build_file = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.certora_build_suffix}"
|
|
111
110
|
if not certora_build_file.exists():
|
|
112
|
-
build_cache_logger.warning(f"Got a cache-hit on build cache key {main_cache_key} and {sub_cache_key} "
|
|
111
|
+
build_cache_logger.warning(f"Got a cache-hit on build cache key {context.main_cache_key} and {sub_cache_key} "
|
|
113
112
|
"but .certora_build.json file does not exist")
|
|
114
113
|
return None
|
|
115
114
|
|
|
116
115
|
if all_contract_files is None: # should not be feasible
|
|
117
|
-
build_cache_logger.warning(f"Got a cache-hit on build cache key {main_cache_key} and
|
|
118
|
-
"but file list file does not exist")
|
|
116
|
+
build_cache_logger.warning(f"Got a cache-hit on build cache key {context.main_cache_key} and "
|
|
117
|
+
f"{sub_cache_key} but file list file does not exist")
|
|
119
118
|
return None
|
|
120
119
|
|
|
121
120
|
build_output_props_file = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.build_output_props_suffix}"
|
|
122
121
|
if not build_output_props_file.exists():
|
|
123
|
-
build_cache_logger.warning(f"Got a cache-hit on build cache key {main_cache_key} and {sub_cache_key} "
|
|
122
|
+
build_cache_logger.warning(f"Got a cache-hit on build cache key {context.main_cache_key} and {sub_cache_key} "
|
|
124
123
|
f"but {CachedFiles.build_output_props_suffix} file does not exist")
|
|
125
124
|
return None
|
|
126
125
|
|
|
@@ -131,26 +130,26 @@ class CertoraBuildCacheManager:
|
|
|
131
130
|
@staticmethod
|
|
132
131
|
def save_build_cache(context: CertoraContext, cached_files: CachedFiles) -> None:
|
|
133
132
|
build_cache_dir = Util.get_certora_build_cache_dir()
|
|
134
|
-
|
|
135
|
-
main_cache_entry_dir = build_cache_dir / main_cache_key
|
|
133
|
+
main_cache_entry_dir = build_cache_dir / context.main_cache_key
|
|
136
134
|
sub_cache_key = CertoraBuildCacheManager.get_sub_cache_key(cached_files.all_contract_files)
|
|
137
135
|
if sub_cache_key is None:
|
|
138
|
-
build_cache_logger.warning(f"Cannot save cache for main build cache key {main_cache_key} "
|
|
136
|
+
build_cache_logger.warning(f"Cannot save cache for main build cache key {context.main_cache_key} "
|
|
139
137
|
"as sub-cache-key could not be computed")
|
|
140
138
|
return
|
|
141
139
|
|
|
142
140
|
if main_cache_entry_dir.exists():
|
|
143
|
-
build_cache_logger.info(f"main build cache key already exists {main_cache_key}, "
|
|
141
|
+
build_cache_logger.info(f"main build cache key already exists {context.main_cache_key}, "
|
|
144
142
|
f"saving sub build cache key {sub_cache_key}")
|
|
145
143
|
if cached_files.all_exist(main_cache_entry_dir, sub_cache_key):
|
|
146
144
|
build_cache_logger.debug("cache already saved under this build cache key, override")
|
|
147
145
|
else:
|
|
148
|
-
build_cache_logger.debug(f"cache was corrupted, need to re-save build cache key
|
|
149
|
-
f"and sub cache key {sub_cache_key}")
|
|
146
|
+
build_cache_logger.debug(f"cache was corrupted, need to re-save build cache key "
|
|
147
|
+
f"{context.main_cache_key} and sub cache key {sub_cache_key}")
|
|
150
148
|
CertoraBuildCacheManager.save_files(cached_files, main_cache_entry_dir,
|
|
151
149
|
sub_cache_key)
|
|
152
150
|
else:
|
|
153
|
-
build_cache_logger.info(f"saving main build cache key {main_cache_key} and sub cache key
|
|
151
|
+
build_cache_logger.info(f"saving main build cache key {context.main_cache_key} and sub cache key "
|
|
152
|
+
f"{sub_cache_key}")
|
|
154
153
|
safe_create_dir(main_cache_entry_dir)
|
|
155
154
|
CertoraBuildCacheManager.save_files(cached_files, main_cache_entry_dir,
|
|
156
155
|
sub_cache_key)
|
|
@@ -180,6 +179,8 @@ class CertoraBuildCacheManager:
|
|
|
180
179
|
trg = trg_path_with_additional_included_files / post_autofinder_dir.name
|
|
181
180
|
if post_autofinder_dir != trg:
|
|
182
181
|
if post_autofinder_dir.is_dir():
|
|
182
|
+
if trg.exists():
|
|
183
|
+
shutil.rmtree(trg)
|
|
183
184
|
Util.safe_copy_folder(post_autofinder_dir, trg, shutil.ignore_patterns())
|
|
184
185
|
else:
|
|
185
186
|
# highly unlikely .post_autofinder.[digit] will be a file and not a directory,
|
|
@@ -13,10 +13,20 @@
|
|
|
13
13
|
# You should have received a copy of the GNU General Public License
|
|
14
14
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
15
|
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
|
|
22
|
+
sys.path.insert(0, str(scripts_dir_path))
|
|
23
|
+
|
|
16
24
|
import glob
|
|
17
25
|
import shutil
|
|
26
|
+
import time
|
|
27
|
+
import logging
|
|
18
28
|
from pathlib import Path
|
|
19
|
-
from typing import Set
|
|
29
|
+
from typing import Set, Dict
|
|
20
30
|
|
|
21
31
|
from CertoraProver.certoraBuild import build_source_tree
|
|
22
32
|
from CertoraProver.certoraContextClass import CertoraContext
|
|
@@ -25,6 +35,25 @@ import CertoraProver.certoraContextAttributes as Attrs
|
|
|
25
35
|
from Shared import certoraUtils as Util
|
|
26
36
|
|
|
27
37
|
|
|
38
|
+
log = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def build_rust_project(context: CertoraContext, timings: Dict) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Compile the Rust artefact and record elapsed time in *timings*.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
context: The CertoraContext object containing the configuration.
|
|
47
|
+
timings: A dictionary to store timing information.
|
|
48
|
+
"""
|
|
49
|
+
log.debug("Build Rust target")
|
|
50
|
+
start = time.perf_counter()
|
|
51
|
+
set_rust_build_directory(context)
|
|
52
|
+
timings["buildTime"] = round(time.perf_counter() - start, 4)
|
|
53
|
+
if context.test == str(Util.TestValue.AFTER_BUILD):
|
|
54
|
+
raise Util.TestResultsReady(context)
|
|
55
|
+
|
|
56
|
+
|
|
28
57
|
def set_rust_build_directory(context: CertoraContext) -> None:
|
|
29
58
|
if not context.files:
|
|
30
59
|
build_rust_app(context)
|
|
@@ -41,6 +70,7 @@ def set_rust_build_directory(context: CertoraContext) -> None:
|
|
|
41
70
|
except Exception as e:
|
|
42
71
|
raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
|
|
43
72
|
|
|
73
|
+
|
|
44
74
|
def build_rust_app(context: CertoraContext) -> None:
|
|
45
75
|
assert not context.files, "build_rust_app: expecting files to be empty"
|
|
46
76
|
if context.build_script:
|
|
@@ -51,6 +81,7 @@ def build_rust_app(context: CertoraContext) -> None:
|
|
|
51
81
|
feature_flag = '--features'
|
|
52
82
|
if context.cargo_tools_version:
|
|
53
83
|
build_command.extend(["--tools-version", context.cargo_tools_version])
|
|
84
|
+
context.rust_project_directory = Util.find_nearest_cargo_toml()
|
|
54
85
|
|
|
55
86
|
if context.cargo_features is not None:
|
|
56
87
|
build_command.append(feature_flag)
|
|
@@ -61,24 +92,6 @@ def build_rust_app(context: CertoraContext) -> None:
|
|
|
61
92
|
|
|
62
93
|
run_rust_build(context, build_command)
|
|
63
94
|
|
|
64
|
-
def add_solana_files_from_prover_args(context: CertoraContext) -> None:
|
|
65
|
-
if context.prover_args:
|
|
66
|
-
inlining_file = False
|
|
67
|
-
summaries_file = False
|
|
68
|
-
for prover_arg in context.prover_args:
|
|
69
|
-
for arg in prover_arg.split(' '):
|
|
70
|
-
if inlining_file:
|
|
71
|
-
context.solana_inlining = context.solana_inlining or [Path(arg)]
|
|
72
|
-
inlining_file = False
|
|
73
|
-
if summaries_file:
|
|
74
|
-
context.solana_summaries = context.solana_summaries or [Path(arg)]
|
|
75
|
-
summaries_file = False
|
|
76
|
-
|
|
77
|
-
if arg == '-solanaInlining':
|
|
78
|
-
inlining_file = True
|
|
79
|
-
elif arg == '-solanaSummaries':
|
|
80
|
-
summaries_file = True
|
|
81
|
-
|
|
82
95
|
|
|
83
96
|
def add_solana_files_to_sources(context: CertoraContext, sources: Set[Path]) -> None:
|
|
84
97
|
for attr in [Attrs.SolanaProverAttributes.SOLANA_INLINING,
|
|
@@ -102,7 +115,7 @@ def add_solana_files_to_sources(context: CertoraContext, sources: Set[Path]) ->
|
|
|
102
115
|
|
|
103
116
|
|
|
104
117
|
def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path]) -> None:
|
|
105
|
-
patterns = ["*.rs", "*.so", "*.wasm",
|
|
118
|
+
patterns = ["*.rs", "*.so", "*.wasm", Util.CARGO_TOML_FILE, "Cargo.lock", "justfile"]
|
|
106
119
|
exclude_dirs = [".certora_internal"]
|
|
107
120
|
|
|
108
121
|
if hasattr(context, 'rust_project_directory'):
|
|
@@ -125,7 +138,6 @@ def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path])
|
|
|
125
138
|
if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
|
|
126
139
|
sources.add(Path(context.conf_file).absolute())
|
|
127
140
|
|
|
128
|
-
add_solana_files_from_prover_args(context)
|
|
129
141
|
add_solana_files_to_sources(context, sources)
|
|
130
142
|
|
|
131
143
|
|
|
@@ -13,13 +13,6 @@
|
|
|
13
13
|
# You should have received a copy of the GNU General Public License
|
|
14
14
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
15
|
|
|
16
|
-
"""
|
|
17
|
-
Shared helpers for Rust-based targets (Solana & Soroban).
|
|
18
|
-
|
|
19
|
-
Placing the build logic in one module removes
|
|
20
|
-
duplication from the dedicated entry scripts.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
16
|
from __future__ import annotations
|
|
24
17
|
|
|
25
18
|
import sys
|
|
@@ -28,25 +21,21 @@ from pathlib import Path
|
|
|
28
21
|
scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
|
|
29
22
|
sys.path.insert(0, str(scripts_dir_path))
|
|
30
23
|
|
|
24
|
+
import shutil
|
|
31
25
|
import time
|
|
32
26
|
import logging
|
|
33
|
-
from
|
|
34
|
-
|
|
35
|
-
from Shared import certoraUtils as Util
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Set, Dict
|
|
36
29
|
|
|
30
|
+
from CertoraProver.certoraBuild import build_source_tree
|
|
37
31
|
from CertoraProver.certoraContextClass import CertoraContext
|
|
38
|
-
|
|
39
|
-
from CertoraProver.certoraBuildRust import set_rust_build_directory
|
|
32
|
+
from Shared import certoraUtils as Util
|
|
40
33
|
|
|
41
34
|
|
|
42
35
|
log = logging.getLogger(__name__)
|
|
43
36
|
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
# Build
|
|
47
|
-
# --------------------------------------------------------------------------- #
|
|
48
|
-
|
|
49
|
-
def build_rust_project(context: CertoraContext, timings: Dict) -> None:
|
|
38
|
+
def build_sui_project(context: CertoraContext, timings: Dict) -> None:
|
|
50
39
|
"""
|
|
51
40
|
Compile the Rust artefact and record elapsed time in *timings*.
|
|
52
41
|
|
|
@@ -56,7 +45,24 @@ def build_rust_project(context: CertoraContext, timings: Dict) -> None:
|
|
|
56
45
|
"""
|
|
57
46
|
log.debug("Build Rust target")
|
|
58
47
|
start = time.perf_counter()
|
|
59
|
-
|
|
48
|
+
set_sui_build_directory(context)
|
|
60
49
|
timings["buildTime"] = round(time.perf_counter() - start, 4)
|
|
61
50
|
if context.test == str(Util.TestValue.AFTER_BUILD):
|
|
62
51
|
raise Util.TestResultsReady(context)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def set_sui_build_directory(context: CertoraContext) -> None:
|
|
55
|
+
assert context.move_path, "build_sui_project: expecting move_path to link to a build directory"
|
|
56
|
+
|
|
57
|
+
shutil.copytree(context.move_path, Util.get_build_dir() / Path(context.move_path).name)
|
|
58
|
+
|
|
59
|
+
sources: Set[Path] = set()
|
|
60
|
+
if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
|
|
61
|
+
sources.add(Path(context.conf_file).absolute())
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# Create generators
|
|
65
|
+
build_source_tree(sources, context)
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
|