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
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
15
|
|
|
16
16
|
import csv
|
|
17
|
-
import fnmatch
|
|
18
17
|
import json
|
|
19
18
|
import os
|
|
20
19
|
import subprocess
|
|
@@ -97,6 +96,7 @@ EMV_JAR = Path("emv.jar")
|
|
|
97
96
|
CERTORA_SOURCES = Path(".certora_sources")
|
|
98
97
|
SOLANA_INLINING = "solana_inlining"
|
|
99
98
|
SOLANA_SUMMARIES = "solana_summaries"
|
|
99
|
+
CARGO_TOML_FILE = "cargo.toml"
|
|
100
100
|
|
|
101
101
|
ALPHA_PACKAGE_NAME = 'certora-cli-alpha'
|
|
102
102
|
ALPHA_PACKAGE_MASTER_NAME = ALPHA_PACKAGE_NAME + '-master'
|
|
@@ -114,7 +114,8 @@ EVM_SOURCE_EXTENSIONS = (SOL_EXT, VY_EXT, YUL_EXT)
|
|
|
114
114
|
EVM_EXTENSIONS = EVM_SOURCE_EXTENSIONS + ('.tac', '.json')
|
|
115
115
|
SOLANA_EXEC_EXTENSION = '.so'
|
|
116
116
|
SOROBAN_EXEC_EXTENSION = '.wasm'
|
|
117
|
-
|
|
117
|
+
VALID_EVM_EXTENSIONS = list(EVM_EXTENSIONS) + ['.conf']
|
|
118
|
+
VALID_FILE_EXTENSIONS = VALID_EVM_EXTENSIONS + [SOLANA_EXEC_EXTENSION, SOROBAN_EXEC_EXTENSION]
|
|
118
119
|
# Type alias definition, not a variable
|
|
119
120
|
CompilerVersion = Tuple[int, int, int]
|
|
120
121
|
MAP_SUFFIX = '_map'
|
|
@@ -368,11 +369,8 @@ def remove_file(file_path: Union[str, Path]) -> None: # TODO - accept only Path
|
|
|
368
369
|
except OSError:
|
|
369
370
|
pass
|
|
370
371
|
else:
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
file_path.unlink()
|
|
374
|
-
except FileNotFoundError:
|
|
375
|
-
pass
|
|
372
|
+
file_path.unlink(missing_ok=True)
|
|
373
|
+
|
|
376
374
|
|
|
377
375
|
def abs_norm_path(file_path: Union[str, Path]) -> Path:
|
|
378
376
|
"""
|
|
@@ -959,18 +957,6 @@ def flatten_set_list(set_list: List[Set[Any]]) -> List[Any]:
|
|
|
959
957
|
return list(ret_set)
|
|
960
958
|
|
|
961
959
|
|
|
962
|
-
def is_relative_to(path1: Path, path2: Path) -> bool:
|
|
963
|
-
"""certora-cli currently requires python3.8 and it's the last version without support for is_relative_to.
|
|
964
|
-
Shamelessly copying.
|
|
965
|
-
"""
|
|
966
|
-
# return path1.is_relative_to(path2)
|
|
967
|
-
try:
|
|
968
|
-
path1.relative_to(path2)
|
|
969
|
-
return True
|
|
970
|
-
except ValueError:
|
|
971
|
-
return False
|
|
972
|
-
|
|
973
|
-
|
|
974
960
|
def find_jar(jar_name: str) -> Path:
|
|
975
961
|
# if we are a dev running certoraRun.py (local version), we want to get the local jar
|
|
976
962
|
# if we are a dev running an installed version of certoraRun, we want to get the installed jar
|
|
@@ -982,7 +968,7 @@ def find_jar(jar_name: str) -> Path:
|
|
|
982
968
|
|
|
983
969
|
if certora_home != "":
|
|
984
970
|
local_certora_path = Path(certora_home) / CERTORA_JARS / jar_name
|
|
985
|
-
if
|
|
971
|
+
if Path(__file__).is_relative_to(Path(certora_home)) and local_certora_path.is_file():
|
|
986
972
|
return local_certora_path
|
|
987
973
|
|
|
988
974
|
return get_package_resource(CERTORA_JARS / jar_name)
|
|
@@ -997,31 +983,46 @@ def get_package_resource(resource: Path) -> Path:
|
|
|
997
983
|
return Path(__file__).parents[2] / resource
|
|
998
984
|
|
|
999
985
|
|
|
1000
|
-
def
|
|
986
|
+
def get_java_version() -> str:
|
|
1001
987
|
"""
|
|
1002
|
-
|
|
1003
|
-
@return
|
|
988
|
+
Retrieve installed java version
|
|
989
|
+
@return installed java version on success or empty string
|
|
1004
990
|
"""
|
|
1005
991
|
# Check if java exists on the machine
|
|
1006
992
|
java = which("java")
|
|
1007
993
|
if java is None:
|
|
994
|
+
return ''
|
|
995
|
+
|
|
996
|
+
try:
|
|
997
|
+
java_version_str = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT).decode()
|
|
998
|
+
java_version = re.search(r'version \"([\d\.]+)\"', java_version_str).groups()[0] # type: ignore[union-attr]
|
|
999
|
+
|
|
1000
|
+
return java_version
|
|
1001
|
+
except (subprocess.CalledProcessError, AttributeError):
|
|
1002
|
+
typecheck_logger.debug("Couldn't find the installed Java version.")
|
|
1003
|
+
return ''
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def is_java_installed(java_version: str) -> bool:
|
|
1007
|
+
"""
|
|
1008
|
+
Check that java is installed and with a version that is suitable for running certora jars
|
|
1009
|
+
@return True on success
|
|
1010
|
+
"""
|
|
1011
|
+
if not java_version:
|
|
1008
1012
|
typecheck_logger.warning(
|
|
1009
1013
|
f"`java` is not installed. Installing Java version {MIN_JAVA_VERSION} or later will enable faster "
|
|
1010
1014
|
f"CVL specification syntax checking before uploading to the cloud.")
|
|
1011
1015
|
return False
|
|
1012
1016
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
major_java_version = re.search(r'version \"(\d+).*', java_version_str).groups()[0] # type: ignore[union-attr]
|
|
1017
|
+
else:
|
|
1018
|
+
major_java_version = java_version.split('.')[0]
|
|
1016
1019
|
if int(major_java_version) < MIN_JAVA_VERSION:
|
|
1017
1020
|
typecheck_logger.warning("Installed Java version is too old to check CVL specification files locally. "
|
|
1018
1021
|
f" Java version should be at least {MIN_JAVA_VERSION} to allow local java-based "
|
|
1019
1022
|
"type checking")
|
|
1020
1023
|
|
|
1021
1024
|
return False
|
|
1022
|
-
|
|
1023
|
-
typecheck_logger.warning("Couldn't find the installed Java version.")
|
|
1024
|
-
return False
|
|
1025
|
+
|
|
1025
1026
|
return True
|
|
1026
1027
|
|
|
1027
1028
|
|
|
@@ -1171,26 +1172,9 @@ class Singleton(type):
|
|
|
1171
1172
|
cls.__instancesinstances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
|
1172
1173
|
return cls.__instancesinstances[cls]
|
|
1173
1174
|
|
|
1174
|
-
|
|
1175
1175
|
class AbstractAndSingleton(Singleton, ABCMeta):
|
|
1176
1176
|
pass
|
|
1177
1177
|
|
|
1178
|
-
|
|
1179
|
-
def match_path_to_mapping_key(path: Path, m: Dict[str, str]) -> Optional[str]:
|
|
1180
|
-
"""
|
|
1181
|
-
Matches the path to the best match in the dictionary's keys.
|
|
1182
|
-
For example, given an absolute path `/Users/JohnDoe/Path/ToSolc/a.sol`, if the map contains
|
|
1183
|
-
`b/a.sol` and `ToSolc/a.sol`, it will match on `ToSolc/a.sol`.
|
|
1184
|
-
@param path: the path to match against
|
|
1185
|
-
@param m: the map whose keys we're searching
|
|
1186
|
-
@return: the value from the map that best matches the path, None if not found.
|
|
1187
|
-
"""
|
|
1188
|
-
for k, v in m.items():
|
|
1189
|
-
if fnmatch.fnmatch(str(path), k):
|
|
1190
|
-
return v
|
|
1191
|
-
return None
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
1178
|
def find_in(dir_path: Path, file_to_find: Path) -> Optional[Path]:
|
|
1195
1179
|
"""
|
|
1196
1180
|
Given a directory dir_path and a file we wish to find within that directory,
|
|
@@ -1262,7 +1246,7 @@ class TestValue(NoValEnum):
|
|
|
1262
1246
|
determines the chekpoint where the execution will halt. The exception TestResultsReady will be thrown. The value
|
|
1263
1247
|
will also determine what object will be attached to the exception for inspection by the caller
|
|
1264
1248
|
"""
|
|
1265
|
-
|
|
1249
|
+
BEFORE_LOCAL_PROVER_CALL = auto()
|
|
1266
1250
|
CHECK_ARGS = auto()
|
|
1267
1251
|
AFTER_BUILD = auto()
|
|
1268
1252
|
CHECK_SOLC_OPTIONS = auto()
|
|
@@ -1452,3 +1436,22 @@ def eq_by(f: Callable[[T, T], bool], a: Sequence[T], b: Sequence[T]) -> bool:
|
|
|
1452
1436
|
check if Sequences a and b are equal according to function f.
|
|
1453
1437
|
"""
|
|
1454
1438
|
return len(a) == len(b) and all(map(f, a, b))
|
|
1439
|
+
|
|
1440
|
+
def find_nearest_cargo_toml() -> Optional[Path]:
|
|
1441
|
+
current = Path.cwd()
|
|
1442
|
+
for parent in [current] + list(current.parents):
|
|
1443
|
+
candidate = parent / CARGO_TOML_FILE
|
|
1444
|
+
if candidate.is_file():
|
|
1445
|
+
return candidate
|
|
1446
|
+
return None
|
|
1447
|
+
|
|
1448
|
+
def file_in_source_tree(file_path: Path) -> bool:
|
|
1449
|
+
# if the file is under .certora_source, return True
|
|
1450
|
+
file_path = Path(file_path).absolute()
|
|
1451
|
+
parent_dir = get_certora_sources_dir().absolute()
|
|
1452
|
+
|
|
1453
|
+
try:
|
|
1454
|
+
file_path.relative_to(parent_dir)
|
|
1455
|
+
return True
|
|
1456
|
+
except ValueError:
|
|
1457
|
+
return False
|
|
@@ -18,6 +18,8 @@ import sys
|
|
|
18
18
|
import argparse
|
|
19
19
|
import json
|
|
20
20
|
import logging
|
|
21
|
+
from collections import OrderedDict
|
|
22
|
+
|
|
21
23
|
import json5
|
|
22
24
|
import re
|
|
23
25
|
import urllib3.util
|
|
@@ -89,7 +91,7 @@ class RunSources(Util.NoValEnum):
|
|
|
89
91
|
MUTATION = auto()
|
|
90
92
|
BENCHMARK = auto()
|
|
91
93
|
LIGHT_TEST = auto()
|
|
92
|
-
|
|
94
|
+
REPORT = auto()
|
|
93
95
|
|
|
94
96
|
|
|
95
97
|
class WaitForResultOptions(Util.NoValEnum):
|
|
@@ -97,6 +99,11 @@ class WaitForResultOptions(Util.NoValEnum):
|
|
|
97
99
|
ALL = auto()
|
|
98
100
|
|
|
99
101
|
|
|
102
|
+
class UrlVisibilityOptions(Util.NoValEnum):
|
|
103
|
+
PRIVATE = auto()
|
|
104
|
+
PUBLIC = auto()
|
|
105
|
+
|
|
106
|
+
|
|
100
107
|
def is_solc_file_valid(orig_filename: Optional[str]) -> str:
|
|
101
108
|
"""
|
|
102
109
|
Verifies that a given --solc argument is valid:
|
|
@@ -122,7 +129,7 @@ def is_solc_file_valid(orig_filename: Optional[str]) -> str:
|
|
|
122
129
|
if filename.endswith(f".{suffix}"):
|
|
123
130
|
raise Util.CertoraUserInputError(f"wrong Solidity executable given: {filename}")
|
|
124
131
|
|
|
125
|
-
# see https://docs.python.org/3.
|
|
132
|
+
# see https://docs.python.org/3.9/library/shutil.html#shutil.which. We use no mask to give a precise error
|
|
126
133
|
solc_location = shutil.which(filename, os.F_OK)
|
|
127
134
|
if solc_location is not None:
|
|
128
135
|
solc_path = Path(solc_location)
|
|
@@ -423,7 +430,7 @@ def validate_contract_extension_attr(map: Any) -> Dict[str, List[Dict[str, Any]]
|
|
|
423
430
|
return map
|
|
424
431
|
|
|
425
432
|
|
|
426
|
-
def
|
|
433
|
+
def validate_evm_input_file(file: str) -> str:
|
|
427
434
|
# [file[:contractName] ...] or CONF_FILE.conf or TAC_FILE.tac
|
|
428
435
|
|
|
429
436
|
if Util.SOL_EXT in file:
|
|
@@ -467,12 +474,12 @@ def validate_input_file(file: str) -> str:
|
|
|
467
474
|
except Exception as e:
|
|
468
475
|
raise Util.CertoraUserInputError(f"Cannot access file {file} : {e}")
|
|
469
476
|
return file
|
|
470
|
-
elif any(file.endswith(ext) for ext in Util.
|
|
477
|
+
elif any(file.endswith(ext) for ext in Util.VALID_EVM_EXTENSIONS):
|
|
471
478
|
validate_readable_file(file)
|
|
472
479
|
return file
|
|
473
480
|
|
|
474
481
|
raise Util.CertoraUserInputError(
|
|
475
|
-
f"input file {file} is not in one of the supported types ({Util.
|
|
482
|
+
f"input file {file} is not in one of the supported types ({Util.VALID_EVM_EXTENSIONS})")
|
|
476
483
|
|
|
477
484
|
|
|
478
485
|
def validate_json_file(file: str) -> str:
|
|
@@ -543,13 +550,6 @@ def validate_struct_link(link: str) -> str:
|
|
|
543
550
|
return link
|
|
544
551
|
|
|
545
552
|
|
|
546
|
-
def validate_assert_contracts(contract: str) -> str:
|
|
547
|
-
if not re.match(Util.SOLIDITY_ID_STRING_RE, contract):
|
|
548
|
-
raise Util.CertoraUserInputError(
|
|
549
|
-
f"Contract name {contract} can include only alphanumeric characters, dollar signs or underscores")
|
|
550
|
-
return contract
|
|
551
|
-
|
|
552
|
-
|
|
553
553
|
def validate_equivalence_contracts(equiv_string: str) -> str:
|
|
554
554
|
if not re.match(f'^{Util.SOLIDITY_ID_SUBSTRING_RE}={Util.SOLIDITY_ID_SUBSTRING_RE}$', equiv_string):
|
|
555
555
|
raise Util.CertoraUserInputError(
|
|
@@ -684,7 +684,7 @@ def validate_solc_via_ir_map(args: Dict[str, bool]) -> None:
|
|
|
684
684
|
if first:
|
|
685
685
|
validation_logger.warning("all via_ir values are set to True '--solc_via_ir' can be used instead")
|
|
686
686
|
else:
|
|
687
|
-
validation_logger.warning("all via_ir values are set to False, this flag/attribute
|
|
687
|
+
validation_logger.warning("all via_ir values are set to False, this flag/attribute can be omitted")
|
|
688
688
|
|
|
689
689
|
def validate_solc_evm_version_map(args: Dict[str, str]) -> None:
|
|
690
690
|
if not isinstance(args, dict):
|
|
@@ -758,6 +758,16 @@ def validate_git_hash(git_hash: str) -> str:
|
|
|
758
758
|
raise Util.CertoraUserInputError("Git hash must consist of between 1 and 40 characters")
|
|
759
759
|
return git_hash
|
|
760
760
|
|
|
761
|
+
def validate_check_method_flag(method: str) -> str:
|
|
762
|
+
if '.' in method:
|
|
763
|
+
raise Util.CertoraUserInputError(f"Malformed `check_mathod` argument '{method}': checked method cannot contain a dot. Use only the method name without the contract prefix."
|
|
764
|
+
"the contract part is not allowed in `--check_method`")
|
|
765
|
+
if ' ' in method:
|
|
766
|
+
raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--check_method`: remove all whitespace")
|
|
767
|
+
|
|
768
|
+
if not __validate_matching_parens(method):
|
|
769
|
+
raise Util.CertoraUserInputError(f"Malformed method '{method}' in `--check_method`: unmatched parenthesis")
|
|
770
|
+
return method
|
|
761
771
|
|
|
762
772
|
def validate_method_flag(method: str) -> str:
|
|
763
773
|
contract_and_method = method.split('.')
|
|
@@ -938,7 +948,7 @@ def validate_on_off(value: str) -> str:
|
|
|
938
948
|
return __validate_enum_value(value, OnOffValue)
|
|
939
949
|
|
|
940
950
|
|
|
941
|
-
def
|
|
951
|
+
def parse_ordered_dict(conf_key: str, input_string: str, value_type: Type = str) -> OrderedDict[str, Union[str, bool]]:
|
|
942
952
|
"""
|
|
943
953
|
convert CLI flag value string of the form <key>=<value>,<key>=<value>,.. to a Dict.
|
|
944
954
|
Keys with different values raise an exception
|
|
@@ -971,7 +981,7 @@ def parse_dict(conf_key: str, input_string: str, value_type: Type = str) -> Dict
|
|
|
971
981
|
raise argparse.ArgumentTypeError(f"{conf_key} argument {input_string} is of wrong format. Must be of format:"
|
|
972
982
|
f"<key>=<value>[,..]")
|
|
973
983
|
|
|
974
|
-
return_dict =
|
|
984
|
+
return_dict = OrderedDict() # type: OrderedDict[str, Union[str, bool]]
|
|
975
985
|
|
|
976
986
|
for match in input_string.split(','):
|
|
977
987
|
key, value = match.split('=')
|
|
@@ -993,6 +1003,10 @@ def validate_wait_for_results(value: str) -> str:
|
|
|
993
1003
|
return __validate_enum_value(value, WaitForResultOptions)
|
|
994
1004
|
|
|
995
1005
|
|
|
1006
|
+
def validate_url_visibility(value: str) -> str:
|
|
1007
|
+
return __validate_enum_value(value, UrlVisibilityOptions)
|
|
1008
|
+
|
|
1009
|
+
|
|
996
1010
|
def validate_json5_file(file: str) -> str:
|
|
997
1011
|
file_exists_and_readable(file)
|
|
998
1012
|
|
|
@@ -156,11 +156,13 @@ def ensure_version_compatibility(context: CertoraContext) -> None:
|
|
|
156
156
|
"""
|
|
157
157
|
validate_version_and_branch(context)
|
|
158
158
|
|
|
159
|
+
|
|
159
160
|
# --------------------------------------------------------------------------- #
|
|
160
161
|
# Verification helpers
|
|
161
162
|
# --------------------------------------------------------------------------- #
|
|
162
163
|
|
|
163
|
-
def run_local(context: CertoraContext, timings: Dict, additional_commands: Optional[List[str]] = None,
|
|
164
|
+
def run_local(context: CertoraContext, timings: Dict, additional_commands: Optional[List[str]] = None,
|
|
165
|
+
compare_with_expected_file: bool = False) -> int:
|
|
164
166
|
"""
|
|
165
167
|
Run the verifier locally and return its exit code (0 = success).
|
|
166
168
|
Args:
|
|
@@ -176,6 +178,8 @@ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optio
|
|
|
176
178
|
cmd.extend(additional_commands)
|
|
177
179
|
|
|
178
180
|
print(f'Verifier run command:\n {" ".join(cmd)}')
|
|
181
|
+
if context.test == str(Util.TestValue.BEFORE_LOCAL_PROVER_CALL):
|
|
182
|
+
raise Util.TestResultsReady(' '.join(cmd))
|
|
179
183
|
rc = Util.run_jar_cmd(
|
|
180
184
|
cmd,
|
|
181
185
|
override_exit_code=compare_with_expected_file,
|
|
@@ -185,7 +189,7 @@ def run_local(context: CertoraContext, timings: Dict, additional_commands: Optio
|
|
|
185
189
|
|
|
186
190
|
if rc == 0:
|
|
187
191
|
Util.print_completion_message("Finished running verifier:")
|
|
188
|
-
print(
|
|
192
|
+
print(f'\t{" ".join(cmd)}')
|
|
189
193
|
timings.setdefault("buildTime", 0.0) # ensure key exists
|
|
190
194
|
return 0
|
|
191
195
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# The Certora Prover
|
|
3
|
+
# Copyright (C) 2025 Certora Ltd.
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, version 3 of the License.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import argparse
|
|
21
|
+
import subprocess
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
|
25
|
+
sys.path.insert(0, str(scripts_dir_path))
|
|
26
|
+
|
|
27
|
+
from Shared import certoraUtils as Util
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
FORMATTER_JAR = "ASTExtraction.jar"
|
|
31
|
+
|
|
32
|
+
scripts_dir_path = Path(__file__).parent.resolve()
|
|
33
|
+
sys.path.insert(0, str(scripts_dir_path))
|
|
34
|
+
|
|
35
|
+
def spec_file_type(spec_file: str) -> str:
|
|
36
|
+
if not os.path.isfile(spec_file):
|
|
37
|
+
raise argparse.ArgumentTypeError(f"File {spec_file} does not exist")
|
|
38
|
+
return spec_file
|
|
39
|
+
|
|
40
|
+
def run_formatter_from_jar(spec_file: str, overwrite: bool) -> None:
|
|
41
|
+
path_to_typechecker = Util.find_jar(FORMATTER_JAR)
|
|
42
|
+
cmd = ['java', '-jar', str(path_to_typechecker), 'format', '--file', spec_file]
|
|
43
|
+
|
|
44
|
+
result = subprocess.run(cmd, text=True, capture_output=True)
|
|
45
|
+
|
|
46
|
+
if result.returncode != 0:
|
|
47
|
+
raise Util.CertoraUserInputError(f"Error running formatter on {spec_file}: {result.stderr.strip() if result.stderr else ''}")
|
|
48
|
+
|
|
49
|
+
if overwrite:
|
|
50
|
+
with open(spec_file, 'w', encoding='utf-8') as f:
|
|
51
|
+
f.write(result.stdout)
|
|
52
|
+
else:
|
|
53
|
+
print(result.stdout, end='', file=sys.stdout)
|
|
54
|
+
|
|
55
|
+
def parse_args() -> tuple[str, bool]:
|
|
56
|
+
parser = argparse.ArgumentParser()
|
|
57
|
+
parser.add_argument('spec_file', type=spec_file_type, help='Path to the .spec file')
|
|
58
|
+
parser.add_argument('-w', '--overwrite', action='store_true', help='If set, output is written to the spec file instead of stdout')
|
|
59
|
+
|
|
60
|
+
args = parser.parse_args()
|
|
61
|
+
return args.spec_file, args.overwrite
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def check_java_version() -> None:
|
|
65
|
+
if not (Util.is_java_installed(Util.get_java_version())):
|
|
66
|
+
raise Util.CertoraUserInputError(f"Java {Util.MIN_JAVA_VERSION} or higher is required to run the formatter. "
|
|
67
|
+
f"Please install Java and try again.")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def entry_point() -> None:
|
|
71
|
+
spec_file, overwrite = parse_args()
|
|
72
|
+
check_java_version()
|
|
73
|
+
run_formatter_from_jar(spec_file, overwrite)
|
|
74
|
+
|
|
75
|
+
if __name__ == '__main__':
|
|
76
|
+
entry_point()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# The Certora Prover
|
|
3
|
+
# Copyright (C) 2025 Certora Ltd.
|
|
4
|
+
#
|
|
5
|
+
# This program is free software: you can redistribute it and/or modify
|
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
|
7
|
+
# the Free Software Foundation, version 3 of the License.
|
|
8
|
+
#
|
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
# GNU General Public License for more details.
|
|
13
|
+
#
|
|
14
|
+
# You should have received a copy of the GNU General Public License
|
|
15
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
|
22
|
+
sys.path.insert(0, str(scripts_dir_path))
|
|
23
|
+
|
|
24
|
+
import CertoraProver.certoraContextAttributes as Attrs
|
|
25
|
+
from certoraRun import run_certora
|
|
26
|
+
from Shared.proverCommon import CertoraRunResult, catch_exits
|
|
27
|
+
|
|
28
|
+
from typing import List, Optional
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def run_concord(args: List[str]) -> Optional[CertoraRunResult]:
|
|
32
|
+
return run_certora(args, Attrs.ConcordAttributes, prover_cmd=sys.argv[0])
|
|
33
|
+
|
|
34
|
+
@catch_exits
|
|
35
|
+
def entry_point() -> None:
|
|
36
|
+
run_concord(sys.argv[1:])
|
|
37
|
+
|
|
38
|
+
if __name__ == '__main__':
|
|
39
|
+
entry_point()
|
certora_cli/certoraRun.py
CHANGED
|
@@ -24,10 +24,9 @@ scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
|
|
24
24
|
sys.path.insert(0, str(scripts_dir_path))
|
|
25
25
|
from Shared import certoraUtils as Util
|
|
26
26
|
|
|
27
|
-
from CertoraProver.certoraCloudIO import CloudVerification
|
|
27
|
+
from CertoraProver.certoraCloudIO import CloudVerification
|
|
28
28
|
|
|
29
29
|
from CertoraProver.certoraBuild import build
|
|
30
|
-
from CertoraProver.certoraBuildRust import set_rust_build_directory
|
|
31
30
|
import CertoraProver.certoraContext as Ctx
|
|
32
31
|
|
|
33
32
|
from CertoraProver import certoraContextValidator as Cv
|
|
@@ -52,7 +51,7 @@ BUILD_SCRIPT_PATH = Path("CertoraProver/certoraBuild.py")
|
|
|
52
51
|
# Also serves as the default logger for errors originating from unexpected places.
|
|
53
52
|
run_logger = logging.getLogger("run")
|
|
54
53
|
|
|
55
|
-
def run_certora(args: List[str], attrs_class:
|
|
54
|
+
def run_certora(args: List[str], attrs_class: Type[AttrUtil.Attributes] = Attrs.EvmProverAttributes,
|
|
56
55
|
prover_cmd: Optional[str] = None) -> Optional[CertoraRunResult]:
|
|
57
56
|
"""
|
|
58
57
|
The main function that is responsible for the general flow of the script.
|
|
@@ -61,9 +60,6 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
|
|
|
61
60
|
2. Run the necessary steps (type checking/ build/ cloud verification/ local verification)
|
|
62
61
|
|
|
63
62
|
"""
|
|
64
|
-
if not attrs_class:
|
|
65
|
-
attrs_class = Attrs.detect_application_class(args)
|
|
66
|
-
|
|
67
63
|
context, logging_manager = build_context(args, attrs_class)
|
|
68
64
|
|
|
69
65
|
if prover_cmd:
|
|
@@ -78,7 +74,7 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
|
|
|
78
74
|
# Collect and dump configuration layout
|
|
79
75
|
collect_and_dump_config_layout(context)
|
|
80
76
|
|
|
81
|
-
if
|
|
77
|
+
if context.split_rules:
|
|
82
78
|
context.build_only = True
|
|
83
79
|
build(context)
|
|
84
80
|
context.build_only = False
|
|
@@ -90,94 +86,60 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
|
|
|
90
86
|
else:
|
|
91
87
|
raise Util.ExitException("Split rules failed", exit_code)
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
# Version validation
|
|
90
|
+
ensure_version_compatibility(context)
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
# When a TAC file is provided, no build arguments will be processed
|
|
93
|
+
if not context.is_tac:
|
|
94
|
+
run_logger.debug(f"There is no TAC file. Going to script {BUILD_SCRIPT_PATH} to main_with_args()")
|
|
95
|
+
build_start = time.perf_counter()
|
|
100
96
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
97
|
+
# If we are not in CI, we also check the spec for Syntax errors.
|
|
98
|
+
build(context)
|
|
99
|
+
build_end = time.perf_counter()
|
|
100
|
+
|
|
101
|
+
timings["buildTime"] = round(build_end - build_start, 4)
|
|
102
|
+
if context.test == str(Util.TestValue.AFTER_BUILD):
|
|
103
|
+
raise Util.TestResultsReady(context)
|
|
104
|
+
|
|
105
|
+
if context.build_only:
|
|
106
|
+
return return_value
|
|
107
|
+
|
|
108
|
+
# either we skipped building (TAC MODE) or build succeeded
|
|
109
|
+
if context.local:
|
|
110
|
+
compare_with_expected_file = Path(context.expected_file).exists()
|
|
111
|
+
|
|
112
|
+
run_result = run_local(context, timings, compare_with_expected_file=compare_with_expected_file)
|
|
113
|
+
emv_dir = latest_emv_dir()
|
|
114
|
+
return_value = CertoraRunResult(str(emv_dir) if emv_dir else None, True,
|
|
115
|
+
Util.get_certora_sources_dir(), None)
|
|
116
|
+
if run_result != 0:
|
|
117
|
+
exit_code = run_result
|
|
118
|
+
elif compare_with_expected_file:
|
|
119
|
+
print("Comparing tool output to the expected output:")
|
|
120
|
+
output_path = context.tool_output or (
|
|
121
|
+
'tmpOutput.json' if emv_dir is None else
|
|
122
|
+
str(emv_dir / 'Reports/output.json')
|
|
123
|
+
)
|
|
124
|
+
result = Util.check_results_from_file(output_path, context.expected_file)
|
|
123
125
|
if not result:
|
|
124
126
|
exit_code = 1
|
|
125
|
-
else:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
raise Util.TestResultsReady(context)
|
|
142
|
-
|
|
143
|
-
if context.build_only:
|
|
144
|
-
return return_value
|
|
145
|
-
|
|
146
|
-
# either we skipped building (TAC MODE) or build succeeded
|
|
147
|
-
if context.local:
|
|
148
|
-
compare_with_expected_file = Path(context.expected_file).exists()
|
|
149
|
-
|
|
150
|
-
run_result = run_local(context, timings, compare_with_expected_file=compare_with_expected_file)
|
|
151
|
-
emv_dir = latest_emv_dir()
|
|
152
|
-
return_value = CertoraRunResult(str(emv_dir) if emv_dir else None, True,
|
|
153
|
-
Util.get_certora_sources_dir(), None)
|
|
154
|
-
if run_result != 0:
|
|
155
|
-
exit_code = run_result
|
|
156
|
-
elif compare_with_expected_file:
|
|
157
|
-
print("Comparing tool output to the expected output:")
|
|
158
|
-
output_path = context.tool_output or (
|
|
159
|
-
'tmpOutput.json' if emv_dir is None else
|
|
160
|
-
str(emv_dir / 'Reports/output.json')
|
|
161
|
-
)
|
|
162
|
-
result = Util.check_results_from_file(output_path, context.expected_file)
|
|
163
|
-
if not result:
|
|
164
|
-
exit_code = 1
|
|
165
|
-
else: # Remote run
|
|
166
|
-
# Syntax checking and typechecking
|
|
167
|
-
if Cv.mode_has_spec_file(context):
|
|
168
|
-
if context.disable_local_typechecking:
|
|
169
|
-
run_logger.warning(
|
|
170
|
-
"Local checks of CVL specification files disabled. It is recommended to enable "
|
|
171
|
-
"the checks.")
|
|
172
|
-
else:
|
|
173
|
-
typechecking_start = time.perf_counter()
|
|
174
|
-
Ctx.run_local_spec_check(True, context)
|
|
175
|
-
typechecking_end = time.perf_counter()
|
|
176
|
-
timings['typecheckingTime'] = round(typechecking_end - typechecking_start, 4)
|
|
177
|
-
|
|
178
|
-
# Remove debug logger and run remote verification
|
|
179
|
-
logging_manager.remove_debug_logger()
|
|
180
|
-
exit_code, return_value = run_remote(context, args, timings)
|
|
127
|
+
else: # Remote run
|
|
128
|
+
# Syntax checking and typechecking
|
|
129
|
+
if Cv.mode_has_spec_file(context):
|
|
130
|
+
if context.disable_local_typechecking:
|
|
131
|
+
run_logger.warning(
|
|
132
|
+
"Local checks of CVL specification files disabled. It is recommended to enable "
|
|
133
|
+
"the checks.")
|
|
134
|
+
else:
|
|
135
|
+
typechecking_start = time.perf_counter()
|
|
136
|
+
Ctx.run_local_spec_check(True, context)
|
|
137
|
+
typechecking_end = time.perf_counter()
|
|
138
|
+
timings['typecheckingTime'] = round(typechecking_end - typechecking_start, 4)
|
|
139
|
+
|
|
140
|
+
# Remove debug logger and run remote verification
|
|
141
|
+
logging_manager.remove_debug_logger()
|
|
142
|
+
exit_code, return_value = run_remote(context, args, timings)
|
|
181
143
|
|
|
182
144
|
# Handle exit codes and return
|
|
183
145
|
return handle_exit(exit_code, return_value)
|
|
@@ -25,7 +25,7 @@ sys.path.insert(0, str(scripts_dir_path))
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
import CertoraProver.certoraContextAttributes as Attrs
|
|
28
|
-
from
|
|
28
|
+
from CertoraProver.certoraBuildRust import build_rust_project
|
|
29
29
|
from Shared.proverCommon import (
|
|
30
30
|
build_context,
|
|
31
31
|
collect_and_dump_metadata,
|
|
@@ -26,7 +26,7 @@ from typing import List, Optional, Dict
|
|
|
26
26
|
|
|
27
27
|
import CertoraProver.certoraContextAttributes as Attrs
|
|
28
28
|
|
|
29
|
-
from
|
|
29
|
+
from CertoraProver.certoraBuildRust import build_rust_project
|
|
30
30
|
from Shared.proverCommon import (
|
|
31
31
|
build_context,
|
|
32
32
|
collect_and_dump_metadata,
|