certora-cli-alpha-master 20241216.22.25.927248__py3-none-any.whl → 20241217.16.7.546354__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of certora-cli-alpha-master might be problematic. Click here for more details.
- certora_cli/EVMVerifier/certoraCloudIO.py +90 -46
- certora_cli/EVMVerifier/certoraConfigIO.py +7 -3
- certora_cli/EVMVerifier/certoraContext.py +16 -64
- certora_cli/EVMVerifier/certoraContextAttributes.py +43 -27
- certora_cli/EVMVerifier/certoraContextValidator.py +7 -12
- certora_cli/EVMVerifier/certoraParseBuildScript.py +36 -0
- certora_cli/Mutate/mutateApp.py +16 -7
- certora_cli/Shared/certoraUtils.py +6 -9
- certora_cli/Shared/certoraValidateFuncs.py +14 -7
- certora_cli/certoraRun.py +3 -5
- certora_cli/certoraSolanaProver.py +153 -0
- certora_cli/rustMutator.py +108 -38
- {certora_cli_alpha_master-20241216.22.25.927248.dist-info → certora_cli_alpha_master-20241217.16.7.546354.dist-info}/METADATA +2 -2
- {certora_cli_alpha_master-20241216.22.25.927248.dist-info → certora_cli_alpha_master-20241217.16.7.546354.dist-info}/RECORD +20 -18
- {certora_cli_alpha_master-20241216.22.25.927248.dist-info → certora_cli_alpha_master-20241217.16.7.546354.dist-info}/entry_points.txt +1 -0
- certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -1
- certora_jars/Typechecker.jar +0 -0
- {certora_cli_alpha_master-20241216.22.25.927248.dist-info → certora_cli_alpha_master-20241217.16.7.546354.dist-info}/LICENSE +0 -0
- {certora_cli_alpha_master-20241216.22.25.927248.dist-info → certora_cli_alpha_master-20241217.16.7.546354.dist-info}/WHEEL +0 -0
- {certora_cli_alpha_master-20241216.22.25.927248.dist-info → certora_cli_alpha_master-20241217.16.7.546354.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,9 @@
|
|
1
|
+
import glob
|
1
2
|
import itertools
|
2
3
|
import json
|
3
4
|
import os
|
4
5
|
import re
|
6
|
+
import shutil
|
5
7
|
import uuid
|
6
8
|
|
7
9
|
import requests
|
@@ -190,21 +192,41 @@ def parse_json(response: Response) -> Dict[str, Any]:
|
|
190
192
|
return json_response
|
191
193
|
|
192
194
|
|
193
|
-
def
|
195
|
+
def zip_rust_files(zip_file_path: Path, context: CertoraContext, *resource_paths: Path) -> bool:
|
196
|
+
patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
|
197
|
+
exclude_dirs = [".certora_internal"]
|
198
|
+
|
199
|
+
root_directory = Path(context.rust_project_directory)
|
200
|
+
if not root_directory.is_dir():
|
201
|
+
raise ValueError(f"The given directory {root_directory} is not valid.")
|
202
|
+
|
194
203
|
with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zip_obj:
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
204
|
+
for source in context.rust_sources:
|
205
|
+
for file in glob.glob(f'{root_directory.joinpath(source)}', recursive=True):
|
206
|
+
file_path = Path(file)
|
207
|
+
if any(excluded in file_path.parts for excluded in exclude_dirs):
|
208
|
+
continue
|
209
|
+
if file_path.is_file() and any(file_path.match(pattern) for pattern in patterns):
|
210
|
+
zip_obj.write(file_path, Util.CERTORA_SOURCES / file_path.relative_to(root_directory))
|
211
|
+
if context.build_script and Path(context.build_script).exists():
|
212
|
+
zip_obj.write(Path(context.build_script),
|
213
|
+
Util.CERTORA_SOURCES / Path(context.build_script).absolute().relative_to(root_directory))
|
214
|
+
if context.conf_file and Path(context.conf_file).exists():
|
215
|
+
zip_obj.write(Path(context.conf_file),
|
216
|
+
Util.CERTORA_SOURCES / Path(context.conf_file).absolute().relative_to(root_directory))
|
217
|
+
if Util.get_last_conf_file():
|
218
|
+
zip_obj.write(Util.get_last_conf_file(),
|
219
|
+
Util.CERTORA_SOURCES / Util.get_last_conf_file().relative_to(Util.get_build_dir()))
|
220
|
+
|
221
|
+
rust_executable = root_directory.joinpath(context.rust_executables)
|
222
|
+
zip_obj.write(rust_executable, rust_executable.relative_to(rust_executable.parent))
|
223
|
+
for path in resource_paths:
|
224
|
+
zip_obj.write(path, os.path.relpath(path, Util.get_build_dir()))
|
225
|
+
if path.suffix == '.txt':
|
226
|
+
zip_obj.write(path, Util.CERTORA_SOURCES / path.relative_to(Util.get_build_dir()))
|
205
227
|
|
206
228
|
if zip_file_path.stat().st_size > MAX_FILE_SIZE:
|
207
|
-
cloud_logger.error(f"{GENERAL_ERR_PREFIX} Max 25MB file size exceeded.")
|
229
|
+
cloud_logger.error(f"{GENERAL_ERR_PREFIX} Max 25MB file size exceeded. File is located at {zip_file_path}")
|
208
230
|
return False
|
209
231
|
return True
|
210
232
|
|
@@ -600,8 +622,12 @@ class CloudVerification:
|
|
600
622
|
# We need to strip "../" path component from all file paths because
|
601
623
|
# unzip will also do that.
|
602
624
|
solana_jar_settings = []
|
603
|
-
|
604
|
-
solana_jar_settings.append(
|
625
|
+
if hasattr(self.context, 'build_script') and self.context.build_script:
|
626
|
+
solana_jar_settings.append(Path(self.context.rust_executables).name)
|
627
|
+
|
628
|
+
else:
|
629
|
+
for file in self.context.files:
|
630
|
+
solana_jar_settings.append(file.split('../')[-1])
|
605
631
|
|
606
632
|
is_file = False
|
607
633
|
for arg in jar_settings:
|
@@ -616,18 +642,19 @@ class CloudVerification:
|
|
616
642
|
elif arg == '-solanaSummaries':
|
617
643
|
is_file = True
|
618
644
|
auth_data["jarSettings"] = solana_jar_settings
|
619
|
-
elif Attrs.
|
645
|
+
elif Attrs.is_soroban_app():
|
620
646
|
# We need to strip "../" path component from all file paths because
|
621
647
|
# unzip will also do that.
|
622
|
-
|
623
|
-
|
624
|
-
|
648
|
+
soroban_jar_settings = []
|
649
|
+
# not needed - should be in files
|
650
|
+
if hasattr(self.context, 'build_script') and self.context.build_script:
|
651
|
+
soroban_jar_settings.append(Path(self.context.rust_executables).name)
|
625
652
|
else:
|
626
653
|
for file in self.context.files:
|
627
|
-
|
654
|
+
soroban_jar_settings.append(file.split('../')[-1])
|
628
655
|
for arg in jar_settings:
|
629
|
-
|
630
|
-
auth_data["jarSettings"] =
|
656
|
+
soroban_jar_settings.append(arg)
|
657
|
+
auth_data["jarSettings"] = soroban_jar_settings
|
631
658
|
else:
|
632
659
|
auth_data["jarSettings"] = jar_settings
|
633
660
|
|
@@ -744,34 +771,51 @@ class CloudVerification:
|
|
744
771
|
paths.append(Path(self.context.bytecode_spec))
|
745
772
|
result = compress_files(self.ZipFilePath, *paths,
|
746
773
|
short_output=Ctx.is_minimal_cli_output(self.context))
|
747
|
-
elif Attrs.
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
774
|
+
elif Attrs.is_rust_app():
|
775
|
+
files_list = [Util.get_certora_metadata_file()]
|
776
|
+
|
777
|
+
if hasattr(self.context, 'build_script') and self.context.build_script:
|
778
|
+
if self.context.prover_resource_files:
|
779
|
+
for value in self.context.prover_resource_files:
|
780
|
+
_, file_path = value.split(':')
|
781
|
+
cur_path = (Path(self.context.conf_file).parent / Path(file_path)).resolve()
|
782
|
+
shutil.copy(cur_path, Util.get_build_dir())
|
783
|
+
files_list.append(Util.get_build_dir() / cur_path.name)
|
784
|
+
|
785
|
+
result = zip_rust_files(self.ZipFilePath, self.context, *files_list)
|
786
|
+
|
787
|
+
elif Attrs.is_solana_app():
|
788
|
+
# We zip the ELF files and the two configuration files
|
789
|
+
jar_args = Ctx.collect_jar_args(self.context)
|
758
790
|
|
759
|
-
if arg == '-solanaInlining':
|
760
|
-
is_file = True
|
761
|
-
elif arg == '-solanaSummaries':
|
762
|
-
is_file = True
|
763
|
-
result = compress_files(self.ZipFilePath, *paths,
|
764
|
-
short_output=Ctx.is_minimal_cli_output(self.context))
|
765
|
-
elif Attrs.is_wasm_app():
|
766
|
-
# We zip the wat file
|
767
|
-
paths = []
|
768
|
-
if hasattr(self.context, 'wasm_file') and self.context.wasm_file:
|
769
|
-
result = compress_files_wasm(self.ZipFilePath, self.context)
|
770
|
-
else:
|
771
791
|
for file in self.context.files:
|
772
|
-
|
773
|
-
|
792
|
+
files_list.append(Path(file))
|
793
|
+
is_file = False
|
794
|
+
for arg in jar_args:
|
795
|
+
if is_file:
|
796
|
+
files_list.append(Path(arg))
|
797
|
+
is_file = False
|
798
|
+
|
799
|
+
if arg == '-solanaInlining':
|
800
|
+
is_file = True
|
801
|
+
elif arg == '-solanaSummaries':
|
802
|
+
is_file = True
|
803
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
774
804
|
short_output=Ctx.is_minimal_cli_output(self.context))
|
805
|
+
|
806
|
+
elif Attrs.is_soroban_app():
|
807
|
+
# We zip the wat file
|
808
|
+
for file in self.context.files:
|
809
|
+
files_list.append(Path(file))
|
810
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
811
|
+
short_output=Ctx.is_minimal_cli_output(self.context))
|
812
|
+
else:
|
813
|
+
raise Util.CertoraUserInputError(
|
814
|
+
'When running rust application, context should either have attribute "rust_executables" '
|
815
|
+
'provided by build_script execution, '
|
816
|
+
'or either is_solana_app(), is_soroban_app() should be set to True'
|
817
|
+
)
|
818
|
+
|
775
819
|
else:
|
776
820
|
# Zip the log file first separately and again with the rest of the files, so it will not be decompressed
|
777
821
|
# on each run in order to save space
|
@@ -53,7 +53,6 @@ def read_from_conf_file(context: CertoraContext) -> None:
|
|
53
53
|
context namespace if the key was not set in the command line (command line shadows conf data).
|
54
54
|
@param context: A namespace containing options from the command line
|
55
55
|
"""
|
56
|
-
|
57
56
|
conf_file_path = Path(context.files[0])
|
58
57
|
assert conf_file_path.suffix == ".conf", f"conf file must be of type .conf, instead got {conf_file_path}"
|
59
58
|
|
@@ -86,6 +85,11 @@ def check_conf_content(conf: Dict[str, Any], context: CertoraContext) -> None:
|
|
86
85
|
f" file ({conf_val})")
|
87
86
|
else:
|
88
87
|
raise Util.CertoraUserInputError(f"{option} appears in the conf file but is not a known attribute. ")
|
88
|
+
|
89
89
|
if 'files' not in conf:
|
90
|
-
|
91
|
-
|
90
|
+
if Attrs.is_rust_app() and hasattr(context, 'build_script') and context.build_script is not None:
|
91
|
+
context.files = conf.get('files', None) # Override the current .conf file
|
92
|
+
else:
|
93
|
+
raise Util.CertoraUserInputError("Mandatory 'files' attribute is missing from the configuration")
|
94
|
+
else:
|
95
|
+
context.files = conf['files'] # Override the current .conf file
|
@@ -5,19 +5,13 @@ import logging
|
|
5
5
|
import os
|
6
6
|
import re
|
7
7
|
import sys
|
8
|
-
import shutil
|
9
|
-
import platform
|
10
8
|
|
11
|
-
# tomllib was added to python 3.11 this import is for supporting older versions
|
12
|
-
if sys.version_info >= (3, 11):
|
13
|
-
import tomllib
|
14
|
-
else:
|
15
|
-
import tomli as tomllib
|
16
9
|
|
17
10
|
from pathlib import Path
|
18
11
|
from typing import Dict, List, Optional, Any
|
19
12
|
from rich.console import Console
|
20
13
|
|
14
|
+
from EVMVerifier.certoraParseBuildScript import run_script_and_parse_json
|
21
15
|
|
22
16
|
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
23
17
|
sys.path.insert(0, str(scripts_dir_path))
|
@@ -112,15 +106,10 @@ def get_local_run_cmd(context: CertoraContext) -> str:
|
|
112
106
|
@return: A command for running the prover locally
|
113
107
|
"""
|
114
108
|
run_args = []
|
115
|
-
if context.is_tac or Attrs.
|
109
|
+
if context.is_tac or Attrs.is_rust_app():
|
110
|
+
# For Rust app we assume the files holds the executable for the prover, currently we support a single file
|
116
111
|
run_args.append(context.files[0])
|
117
112
|
|
118
|
-
if Attrs.is_wasm_app():
|
119
|
-
if hasattr(context, 'wasm_file') and context.wasm_file:
|
120
|
-
run_args.append(str(context.wasm_file))
|
121
|
-
else:
|
122
|
-
run_args.append(context.files[0])
|
123
|
-
|
124
113
|
if Attrs.is_evm_app() and context.cache is not None:
|
125
114
|
run_args.extend(['-cache', context.cache])
|
126
115
|
|
@@ -158,7 +147,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
|
|
158
147
|
console = Console()
|
159
148
|
console.print("\n\nThe Certora Prover - A formal verification tool for smart contracts")
|
160
149
|
# Using sys.stdout.write() as print() would color some of the strings here
|
161
|
-
sys.stdout.write("\n\nUsage:
|
150
|
+
sys.stdout.write(f"\n\nUsage: {sys.argv[0]} <Files> <Flags>\n\n")
|
162
151
|
sys.stdout.write("Files are Solidity, Vyper contract files, a shared object Solana contract file, "
|
163
152
|
"or a conf file. Solidity contracts are denoted as file:contract "
|
164
153
|
"e.g. f.sol:A. If the contract name is identical to the file name, "
|
@@ -171,7 +160,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
|
|
171
160
|
console.print("3. list (L): gets multiple strings as a value, flags may also appear multiple times")
|
172
161
|
console.print("4. map (M): collection of key, value pairs\n\n")
|
173
162
|
|
174
|
-
Attrs.
|
163
|
+
Attrs.get_attribute_class().print_attr_help()
|
175
164
|
console.print("\n\nYou can find detailed documentation of the supported flags here: "
|
176
165
|
f"{Util.print_rich_link(CLI_DOCUMENTATION_URL)}\n\n")
|
177
166
|
|
@@ -527,52 +516,15 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> No
|
|
527
516
|
raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
|
528
517
|
"To skip local checks run with the --disable_local_typechecking flag")
|
529
518
|
|
530
|
-
def set_cargo_name(context: CertoraContext) -> None:
|
531
|
-
mode = "r" if platform.system().lower() == 'windows' else "rb"
|
532
|
-
with open(context.cargo_file, mode) as f:
|
533
|
-
cargo_data = tomllib.load(f)
|
534
|
-
|
535
|
-
if 'package' in cargo_data and 'name' in cargo_data['package']:
|
536
|
-
context.cargo_name = cargo_data['package']['name'].replace('-', '_')
|
537
|
-
|
538
|
-
def build_wasm(context: CertoraContext) -> None:
|
539
|
-
if len(context.files) != 1:
|
540
|
-
raise Util.CertoraUserInputError(f"expected a single wasm file ({Util.CARGO_FILE} or .wat file)."
|
541
|
-
f" Got: {context.files}")
|
542
|
-
file_path = Path(context.files[0])
|
543
|
-
if file_path.name != Util.CARGO_FILE:
|
544
|
-
return
|
545
|
-
context.cargo_dir = file_path.parent
|
546
|
-
context.cargo_file = file_path
|
547
|
-
set_cargo_name(context)
|
548
|
-
run_wasm_compiler(context)
|
549
|
-
|
550
|
-
|
551
|
-
def run_wasm_compiler(context: CertoraContext) -> None:
|
552
|
-
env = os.environ.copy()
|
553
|
-
env['RUSTFLAGS'] = "-C strip=none --emit=llvm-ir -C debuginfo=2"
|
554
|
-
try:
|
555
|
-
context_logger.info("Executing build process...")
|
556
|
-
|
557
|
-
cmd = f"cargo build --target={context.wasm_target} --release --features certora"
|
558
|
-
Util.run_shell_command(cmd, env=env, cwd=context.cargo_dir)
|
559
519
|
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
cmd = f"wasm2wat {intermediate_wasm} -o {wat_file}"
|
572
|
-
Util.run_shell_command(cmd, cwd=context.cargo_dir, env=env)
|
573
|
-
|
574
|
-
intermediate_wasm.unlink()
|
575
|
-
shutil.copy(context.wasm_file, context.cargo_dir)
|
576
|
-
|
577
|
-
except Exception as e:
|
578
|
-
raise RuntimeError(f"Failed to build wasm file\n{e}")
|
520
|
+
def build_rust_app(context: CertoraContext) -> None:
|
521
|
+
if context.build_script:
|
522
|
+
run_script_and_parse_json(context)
|
523
|
+
if not context.rust_executables:
|
524
|
+
raise Util.CertoraUserInputError("failed to get target executable")
|
525
|
+
else:
|
526
|
+
if not context.files:
|
527
|
+
raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
|
528
|
+
if len(context.files) > 1:
|
529
|
+
raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
|
530
|
+
context.rust_executables = context.files[0]
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
|
2
3
|
import json5
|
3
4
|
import sys
|
4
5
|
from pathlib import Path
|
@@ -37,6 +38,17 @@ def validate_prover_args(value: str) -> str:
|
|
37
38
|
|
38
39
|
|
39
40
|
class CommonAttributes(AttrUtil.Attributes):
|
41
|
+
COMPILATION_STEPS_ONLY = AttrUtil.AttributeDefinition(
|
42
|
+
arg_type=AttrUtil.AttrArgType.BOOLEAN,
|
43
|
+
help_msg="Compile the spec and the code without sending a verification request to the cloud",
|
44
|
+
default_desc="Sends a request after source compilation and spec syntax checking",
|
45
|
+
argparse_args={
|
46
|
+
'action': AttrUtil.STORE_TRUE
|
47
|
+
},
|
48
|
+
affects_build_cache_key=False,
|
49
|
+
disables_build_cache=False
|
50
|
+
)
|
51
|
+
|
40
52
|
MSG = AttrUtil.AttributeDefinition(
|
41
53
|
attr_validation_func=Vf.validate_msg,
|
42
54
|
help_msg="Add a message description to your run",
|
@@ -149,6 +161,7 @@ class CommonAttributes(AttrUtil.Attributes):
|
|
149
161
|
disables_build_cache=False
|
150
162
|
)
|
151
163
|
|
164
|
+
|
152
165
|
class DeprecatedAttributes(AttrUtil.Attributes):
|
153
166
|
AUTO_NONDET_DIFFICULT_INTERNAL_FUNCS = AttrUtil.AttributeDefinition(
|
154
167
|
arg_type=AttrUtil.AttrArgType.BOOLEAN,
|
@@ -418,17 +431,6 @@ class EvmAttributes(AttrUtil.Attributes):
|
|
418
431
|
disables_build_cache=False
|
419
432
|
)
|
420
433
|
|
421
|
-
COMPILATION_STEPS_ONLY = AttrUtil.AttributeDefinition(
|
422
|
-
arg_type=AttrUtil.AttrArgType.BOOLEAN,
|
423
|
-
help_msg="Compile the spec and the code without sending a verification request to the cloud",
|
424
|
-
default_desc="Sends a request after source compilation and spec syntax checking",
|
425
|
-
argparse_args={
|
426
|
-
'action': AttrUtil.STORE_TRUE
|
427
|
-
},
|
428
|
-
affects_build_cache_key=False,
|
429
|
-
disables_build_cache=False
|
430
|
-
)
|
431
|
-
|
432
434
|
NO_MEMORY_SAFE_AUTOFINDERS = AttrUtil.AttributeDefinition(
|
433
435
|
arg_type=AttrUtil.AttrArgType.BOOLEAN,
|
434
436
|
# This is a hidden flag, the following two attributes are left intentionally as comments to help devs
|
@@ -1384,6 +1386,20 @@ class BackendAttributes(AttrUtil.Attributes):
|
|
1384
1386
|
disables_build_cache=False
|
1385
1387
|
)
|
1386
1388
|
|
1389
|
+
|
1390
|
+
class RustAttributes(AttrUtil.Attributes):
|
1391
|
+
|
1392
|
+
BUILD_SCRIPT = AttrUtil.AttributeDefinition(
|
1393
|
+
help_msg="Python script to build the project",
|
1394
|
+
default_desc="Using default building command",
|
1395
|
+
argparse_args={
|
1396
|
+
'action': AttrUtil.UniqueStore
|
1397
|
+
},
|
1398
|
+
affects_build_cache_key=False,
|
1399
|
+
disables_build_cache=False
|
1400
|
+
)
|
1401
|
+
|
1402
|
+
|
1387
1403
|
class EvmProverAttributes(CommonAttributes, DeprecatedAttributes, EvmAttributes, InternalUseAttributes,
|
1388
1404
|
BackendAttributes):
|
1389
1405
|
FILES = AttrUtil.AttributeDefinition(
|
@@ -1399,9 +1415,9 @@ class EvmProverAttributes(CommonAttributes, DeprecatedAttributes, EvmAttributes,
|
|
1399
1415
|
)
|
1400
1416
|
|
1401
1417
|
|
1402
|
-
class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes):
|
1418
|
+
class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes, RustAttributes):
|
1403
1419
|
FILES = AttrUtil.AttributeDefinition(
|
1404
|
-
attr_validation_func=Vf.
|
1420
|
+
attr_validation_func=Vf.validate_soroban_extension,
|
1405
1421
|
arg_type=AttrUtil.AttrArgType.LIST,
|
1406
1422
|
help_msg="binary .wat files for the Prover",
|
1407
1423
|
default_desc="",
|
@@ -1412,19 +1428,10 @@ class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAt
|
|
1412
1428
|
disables_build_cache=False
|
1413
1429
|
)
|
1414
1430
|
|
1415
|
-
WASM_TARGET = AttrUtil.AttributeDefinition(
|
1416
|
-
default_desc="",
|
1417
|
-
argparse_args={
|
1418
|
-
'action': AttrUtil.UniqueStore,
|
1419
|
-
'default': "wasm32-unknown-unknown"
|
1420
|
-
},
|
1421
|
-
affects_build_cache_key=False,
|
1422
|
-
disables_build_cache=False
|
1423
|
-
)
|
1424
1431
|
|
1425
|
-
class SolanaProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes):
|
1432
|
+
class SolanaProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes, RustAttributes):
|
1426
1433
|
FILES = AttrUtil.AttributeDefinition(
|
1427
|
-
attr_validation_func=Vf.
|
1434
|
+
attr_validation_func=Vf.validate_solana_extension,
|
1428
1435
|
arg_type=AttrUtil.AttrArgType.LIST,
|
1429
1436
|
help_msg="contract files for analysis SOLANA_FILE.so or a conf file",
|
1430
1437
|
|
@@ -1460,9 +1467,9 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
|
|
1460
1467
|
def application_by_suffix(file: str) -> Type[AttrUtil.Attributes]:
|
1461
1468
|
if file.endswith(Util.EVM_EXTENSIONS):
|
1462
1469
|
return EvmProverAttributes
|
1463
|
-
elif file.endswith(Util.
|
1470
|
+
elif file.endswith(Util.SOROBAN_EXEC_EXTENSION):
|
1464
1471
|
return SorobanProverAttributes
|
1465
|
-
elif file.endswith(Util.
|
1472
|
+
elif file.endswith(Util.SOLANA_EXEC_EXTENSION):
|
1466
1473
|
return SolanaProverAttributes
|
1467
1474
|
elif file.endswith('.conf'):
|
1468
1475
|
raise Util.CertoraUserInputError(f"Cannot use conf files inside a conf file: {file}")
|
@@ -1472,6 +1479,7 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
|
|
1472
1479
|
cli_files = []
|
1473
1480
|
cli_conf_files = []
|
1474
1481
|
files = []
|
1482
|
+
build_script = None
|
1475
1483
|
for arg in args:
|
1476
1484
|
if arg.startswith('-'):
|
1477
1485
|
break # Stop processing when a flag is detected
|
@@ -1485,6 +1493,10 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
|
|
1485
1493
|
with conf_file_path.open() as conf_file:
|
1486
1494
|
configuration = json5.load(conf_file, allow_duplicate_keys=False)
|
1487
1495
|
files = configuration.get('files', [])
|
1496
|
+
build_script = configuration.get('build_script')
|
1497
|
+
|
1498
|
+
if build_script:
|
1499
|
+
return SorobanProverAttributes
|
1488
1500
|
|
1489
1501
|
if len(cli_conf_files) == 0:
|
1490
1502
|
files = cli_files
|
@@ -1516,9 +1528,13 @@ def is_solana_app() -> bool:
|
|
1516
1528
|
return get_attribute_class() == SolanaProverAttributes
|
1517
1529
|
|
1518
1530
|
|
1519
|
-
def
|
1531
|
+
def is_soroban_app() -> bool:
|
1520
1532
|
return get_attribute_class() == SorobanProverAttributes
|
1521
1533
|
|
1522
1534
|
|
1535
|
+
def is_rust_app() -> bool:
|
1536
|
+
return is_soroban_app() or is_solana_app()
|
1537
|
+
|
1538
|
+
|
1523
1539
|
def is_evm_app() -> bool:
|
1524
1540
|
return get_attribute_class() == EvmProverAttributes
|
@@ -409,8 +409,7 @@ def check_contract_name_arg_inputs(context: CertoraContext) -> None:
|
|
409
409
|
def valid_input_file(filename: str) -> bool:
|
410
410
|
pattern_match_sol = bool(re.match(r'([\w.$]+)\.sol:([\w$]+)', Path(filename).name))
|
411
411
|
ends_with_extension = any(filename.endswith(ext) for ext in Util.VALID_FILE_EXTENSIONS)
|
412
|
-
|
413
|
-
return pattern_match_sol or ends_with_extension or is_cargo_file
|
412
|
+
return pattern_match_sol or ends_with_extension
|
414
413
|
|
415
414
|
|
416
415
|
def check_files_attribute(context: CertoraContext) -> None:
|
@@ -492,7 +491,7 @@ def check_mode_of_operation(context: CertoraContext) -> None:
|
|
492
491
|
raise Util.CertoraUserInputError(
|
493
492
|
f"Cannot use conf files inside a conf file: {','.join(conf_files_inside_conf_file)}")
|
494
493
|
|
495
|
-
special_file_suffixes = [".tac", ".json"] +
|
494
|
+
special_file_suffixes = [".tac", ".json"] + [Util.SOLANA_EXEC_EXTENSION, Util.SOROBAN_EXEC_EXTENSION]
|
496
495
|
for input_file in context.files:
|
497
496
|
special_file_type = next((suffix for suffix in special_file_suffixes if input_file.endswith(suffix)), None)
|
498
497
|
|
@@ -710,7 +709,6 @@ def validate_certora_key() -> str:
|
|
710
709
|
raise Util.CertoraUserInputError(f"environment variable {KEY_ENV_VAR} has an illegal length")
|
711
710
|
return key
|
712
711
|
|
713
|
-
|
714
712
|
def check_files_input(file_list: List[str]) -> None:
|
715
713
|
"""
|
716
714
|
Verifies that correct input was inserted as input to files.
|
@@ -719,8 +717,8 @@ def check_files_input(file_list: List[str]) -> None:
|
|
719
717
|
The allowed disjoint cases are:
|
720
718
|
1. Use a single .conf file
|
721
719
|
2. Use a single .tac file
|
722
|
-
3. Use a single .
|
723
|
-
4. Use a single .
|
720
|
+
3. Use a single .so file
|
721
|
+
4. Use a single .wasm file
|
724
722
|
5. Use any number of [contract.sol:nickname ...] (at least one is guaranteed by argparser)
|
725
723
|
@param file_list: A list of strings representing file paths
|
726
724
|
@raise CertoraUserInputError if more than one of the modes above was used
|
@@ -733,13 +731,10 @@ def check_files_input(file_list: List[str]) -> None:
|
|
733
731
|
if '.conf' in file:
|
734
732
|
raise Util.CertoraUserInputError(
|
735
733
|
f'The conf file {file} cannot be accompanied with other files')
|
736
|
-
if file.endswith(Util.
|
734
|
+
if file.endswith(Util.SOLANA_EXEC_EXTENSION):
|
737
735
|
raise Util.CertoraUserInputError(f'The Solana file {file} cannot be accompanied with other files')
|
738
|
-
if file.endswith(Util.
|
739
|
-
raise Util.CertoraUserInputError(
|
740
|
-
f'When using the tool in WASM mode, you can only provide a single Cargo.toml file or .wat file.'
|
741
|
-
f'{num_files} files were given.')
|
742
|
-
|
736
|
+
if file.endswith(Util.SOROBAN_EXEC_EXTENSION):
|
737
|
+
raise Util.CertoraUserInputError(f'The Soroban file {file} cannot be accompanied with other files')
|
743
738
|
|
744
739
|
def set_wait_for_results_default(context: CertoraContext) -> None:
|
745
740
|
if context.wait_for_results is None:
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import subprocess
|
2
|
+
import json
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from EVMVerifier.certoraContextClass import CertoraContext
|
6
|
+
from Shared import certoraUtils as Util
|
7
|
+
import os
|
8
|
+
|
9
|
+
|
10
|
+
def run_script_and_parse_json(context: CertoraContext) -> None:
|
11
|
+
if not context.build_script:
|
12
|
+
return
|
13
|
+
try:
|
14
|
+
env = os.environ.copy()
|
15
|
+
result = subprocess.run(["python3", Path(context.build_script).resolve(), '--json'], capture_output=True, text=True,
|
16
|
+
env=env, cwd=Path(context.build_script).resolve().parent)
|
17
|
+
|
18
|
+
# Check if the script executed successfully
|
19
|
+
if result.returncode != 0:
|
20
|
+
raise Util.CertoraUserInputError(f"Error running the script {context.build_script}\n{result.stderr}")
|
21
|
+
|
22
|
+
json_obj = json.loads(result.stdout)
|
23
|
+
|
24
|
+
if not json_obj or not json_obj.get("success"):
|
25
|
+
raise Util.CertoraUserInputError(f"{result.stderr}\nBuild from {context.build_script} failed")
|
26
|
+
|
27
|
+
context.rust_project_directory = json_obj.get("project_directory")
|
28
|
+
context.rust_sources = json_obj.get("sources")
|
29
|
+
context.rust_executables = json_obj.get("executables")
|
30
|
+
|
31
|
+
except FileNotFoundError as e:
|
32
|
+
raise Util.CertoraUserInputError(f"File not found: {e}")
|
33
|
+
except json.JSONDecodeError as e:
|
34
|
+
raise Util.CertoraUserInputError(f"Error decoding JSON: {e}")
|
35
|
+
except Exception as e:
|
36
|
+
raise Util.CertoraUserInputError(f"An unexpected error occurred: {e}")
|
certora_cli/Mutate/mutateApp.py
CHANGED
@@ -715,11 +715,7 @@ class MutateApp:
|
|
715
715
|
self.server = self.config_server()
|
716
716
|
|
717
717
|
def is_soroban_run(self) -> bool:
|
718
|
-
|
719
|
-
files = self.prover_context.files
|
720
|
-
except AttributeError:
|
721
|
-
return False
|
722
|
-
return Util.CARGO_FILE in files or any(Path(path).suffix == '.wasm' for path in files)
|
718
|
+
return (hasattr(self.prover_context, 'build_script') and self.prover_context is not None)
|
723
719
|
|
724
720
|
def config_server(self) -> str:
|
725
721
|
"""
|
@@ -873,6 +869,16 @@ class MutateApp:
|
|
873
869
|
self.backup_paths = []
|
874
870
|
|
875
871
|
def submit_soroban(self) -> None:
|
872
|
+
|
873
|
+
try:
|
874
|
+
original_run_result = self.run_certora_prover(self.conf, self.mutation_test_id, msg=MConstants.ORIGINAL)
|
875
|
+
except Exception as e:
|
876
|
+
raise Util.CertoraUserInputError(f"Orig run: {e}")
|
877
|
+
if not original_run_result:
|
878
|
+
raise Util.CertoraUserInputError("No Orig results")
|
879
|
+
result_link = original_run_result.rule_report_link
|
880
|
+
assert result_link, "submit_soroban: Null result_link"
|
881
|
+
|
876
882
|
mutation_logger.info("Generating mutants and submitting...")
|
877
883
|
generated_mutants: List[Mutant] = self.get_universal_mutator_mutants()
|
878
884
|
|
@@ -1341,7 +1347,8 @@ class MutateApp:
|
|
1341
1347
|
for mutant in self.universal_mutator:
|
1342
1348
|
file_to_mutate = Path(os.path.normpath(mutant[MConstants.FILE_TO_MUTATE]))
|
1343
1349
|
mutants_location = Path(mutant[MConstants.MUTANTS_LOCATION])
|
1344
|
-
|
1350
|
+
num_of_mutants = mutant[MConstants.NUM_MUTANTS]
|
1351
|
+
run_universal_mutator(file_to_mutate, self.prover_context.build_script, mutants_location, num_of_mutants)
|
1345
1352
|
self.add_dir_to_mutants(ret_mutants, mutants_location, file_to_mutate)
|
1346
1353
|
return ret_mutants
|
1347
1354
|
|
@@ -1784,7 +1791,9 @@ class MutateApp:
|
|
1784
1791
|
|
1785
1792
|
# all keys in prover_context must exist as attribute of certoraRun
|
1786
1793
|
def check_prover_context(self) -> None:
|
1787
|
-
|
1794
|
+
attributes = Attrs.SorobanProverAttributes.attribute_list() if self.is_soroban_run() \
|
1795
|
+
else Attrs.EvmProverAttributes.attribute_list()
|
1796
|
+
prover_attrs = [attr.get_conf_key() for attr in attributes]
|
1788
1797
|
for key in vars(self.prover_context).keys():
|
1789
1798
|
if key not in prover_attrs:
|
1790
1799
|
raise Util.CertoraUserInputError(f"{key} not a valid attribute in a conf file")
|
@@ -78,7 +78,7 @@ RECENT_JOBS_FILE = Path(".certora_recent_jobs.json")
|
|
78
78
|
LAST_CONF_FILE = Path("run.conf")
|
79
79
|
EMV_JAR = Path("emv.jar")
|
80
80
|
CERTORA_SOURCES = Path(".certora_sources")
|
81
|
-
|
81
|
+
SOLANA_DEFAULT_COMMAND = "cargo +solana build-sbf"
|
82
82
|
ALPHA_PACKAGE_NAME = 'certora-cli-alpha-master'
|
83
83
|
# contract names in Solidity consists of alphanums, underscores and dollar signs
|
84
84
|
# https://docs.soliditylang.org/en/latest/grammar.html#a4.SolidityLexer.Identifier
|
@@ -89,11 +89,10 @@ NEW_LINE = '\n' # for new lines in f strings
|
|
89
89
|
VY_EXT = '.vy'
|
90
90
|
SOL_EXT = '.sol'
|
91
91
|
YUL_EXT = '.yul'
|
92
|
-
CARGO_FILE = "Cargo.toml"
|
93
92
|
EVM_EXTENSIONS = ('.sol', VY_EXT, YUL_EXT, '.tac', '.json')
|
94
|
-
|
95
|
-
|
96
|
-
VALID_FILE_EXTENSIONS = ['.conf'] + list(EVM_EXTENSIONS) +
|
93
|
+
SOLANA_EXEC_EXTENSION = '.so'
|
94
|
+
SOROBAN_EXEC_EXTENSION = '.wasm'
|
95
|
+
VALID_FILE_EXTENSIONS = ['.conf'] + list(EVM_EXTENSIONS) + [SOLANA_EXEC_EXTENSION, SOROBAN_EXEC_EXTENSION]
|
97
96
|
# Type alias definition, not a variable
|
98
97
|
CompilerVersion = Tuple[int, int, int]
|
99
98
|
|
@@ -1241,7 +1240,7 @@ class TestValue(NoValEnum):
|
|
1241
1240
|
AFTER_COLLECT = auto()
|
1242
1241
|
AFTER_BUILD_MUTANTS_DIRECTORY = auto()
|
1243
1242
|
AFTER_GENERATE_COLLECT_REPORT = auto()
|
1244
|
-
|
1243
|
+
AFTER_BUILD_RUST = auto()
|
1245
1244
|
|
1246
1245
|
|
1247
1246
|
class FeValue(NoValEnum):
|
@@ -1368,9 +1367,7 @@ def get_ir_flag(solc: str) -> str:
|
|
1368
1367
|
return ''
|
1369
1368
|
|
1370
1369
|
|
1371
|
-
def run_shell_command(cmd: str, env: Dict[str, str], cwd:
|
1372
|
-
if not cwd:
|
1373
|
-
cwd = Path.cwd()
|
1370
|
+
def run_shell_command(cmd: Union[str, List[str]], env: Dict[str, str] = os.environ.copy(), cwd: Path = Path.cwd()) -> None:
|
1374
1371
|
try:
|
1375
1372
|
print(f"Executing: {cmd}")
|
1376
1373
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True, env=env, cwd=cwd)
|
@@ -244,6 +244,17 @@ def validate_optional_readable_file(filename: str) -> str:
|
|
244
244
|
def validate_spec_file(filename: str) -> str:
|
245
245
|
return validate_readable_file(filename, (".spec", ".cvl"))
|
246
246
|
|
247
|
+
def validate_soroban_extension(filename: str) -> str:
|
248
|
+
if not filename.lower().endswith(Util.SOROBAN_EXEC_EXTENSION):
|
249
|
+
raise Util.CertoraUserInputError(f"{filename} does not end with {Util.SOROBAN_EXEC_EXTENSION}")
|
250
|
+
return filename
|
251
|
+
|
252
|
+
|
253
|
+
def validate_solana_extension(filename: str) -> str:
|
254
|
+
if not filename.lower().endswith(Util.SOLANA_EXEC_EXTENSION):
|
255
|
+
raise Util.CertoraUserInputError(f"{filename} does not end with {Util.SOLANA_EXEC_EXTENSION}")
|
256
|
+
return filename
|
257
|
+
|
247
258
|
|
248
259
|
def validate_readable_file(filename: str, extensions: Union[str, tuple] = '') -> str:
|
249
260
|
file_path = Path(filename)
|
@@ -253,8 +264,6 @@ def validate_readable_file(filename: str, extensions: Union[str, tuple] = '') ->
|
|
253
264
|
raise Util.CertoraUserInputError(f"'{filename}' is a directory and not a file")
|
254
265
|
if not os.access(filename, os.R_OK):
|
255
266
|
raise Util.CertoraUserInputError(f"no read permissions for {filename}")
|
256
|
-
if Path(filename).name == Util.CARGO_FILE:
|
257
|
-
return filename
|
258
267
|
if extensions and not filename.lower().endswith(extensions):
|
259
268
|
raise Util.CertoraUserInputError(f"{filename} does not end with {extensions}")
|
260
269
|
|
@@ -375,14 +384,12 @@ def validate_exec_file(file_name: str) -> str:
|
|
375
384
|
return file_name
|
376
385
|
|
377
386
|
|
378
|
-
def
|
379
|
-
|
380
|
-
return validate_readable_file(Util.CARGO_FILE)
|
381
|
-
return validate_readable_file(file, Util.WASM_EXTENSIONS)
|
387
|
+
def validate_soroban_file(file: str) -> str:
|
388
|
+
return validate_readable_file(file, Util.SOROBAN_EXEC_EXTENSION)
|
382
389
|
|
383
390
|
|
384
391
|
def validate_solana_file(file: str) -> str:
|
385
|
-
return validate_readable_file(file, Util.
|
392
|
+
return validate_readable_file(file, Util.SOLANA_EXEC_EXTENSION)
|
386
393
|
|
387
394
|
|
388
395
|
def validate_contract_extension_attr(map: Any) -> Dict[str, List[Dict[str, Any]]]:
|
certora_cli/certoraRun.py
CHANGED
@@ -24,7 +24,6 @@ from EVMVerifier import certoraContextValidator as Cv
|
|
24
24
|
import EVMVerifier.certoraContextAttributes as Attrs
|
25
25
|
from Shared import certoraAttrUtil as AttrUtil
|
26
26
|
|
27
|
-
|
28
27
|
BUILD_SCRIPT_PATH = Path("EVMVerifier/certoraBuild.py")
|
29
28
|
VIOLATIONS_EXIT_CODE = 100
|
30
29
|
|
@@ -95,9 +94,9 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
|
|
95
94
|
raise Util.TestResultsReady(metadata)
|
96
95
|
metadata.dump()
|
97
96
|
|
98
|
-
if Attrs.
|
99
|
-
|
100
|
-
|
97
|
+
if Attrs.is_rust_app():
|
98
|
+
Ctx.build_rust_app(context)
|
99
|
+
|
101
100
|
if context.local:
|
102
101
|
check_cmd = Ctx.get_local_run_cmd(context)
|
103
102
|
print(f"Verifier run command:\n {check_cmd}", flush=True)
|
@@ -244,7 +243,6 @@ def entry_point() -> None:
|
|
244
243
|
It is important this function gets no arguments!
|
245
244
|
"""
|
246
245
|
try:
|
247
|
-
Attrs.set_attribute_class(Attrs.EvmProverAttributes)
|
248
246
|
run_certora(sys.argv[1:])
|
249
247
|
sys.exit(0)
|
250
248
|
except KeyboardInterrupt:
|
@@ -0,0 +1,153 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
import sys
|
4
|
+
import time
|
5
|
+
import logging
|
6
|
+
from typing import List, Optional, Type
|
7
|
+
from pathlib import Path
|
8
|
+
from rich.console import Console
|
9
|
+
|
10
|
+
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
11
|
+
sys.path.insert(0, str(scripts_dir_path))
|
12
|
+
|
13
|
+
from Shared import certoraUtils as Util
|
14
|
+
from Shared import certoraAttrUtil as AttrUtil
|
15
|
+
from Shared.certoraLogging import LoggingManager
|
16
|
+
|
17
|
+
from EVMVerifier.certoraCloudIO import CloudVerification, validate_version_and_branch
|
18
|
+
from EVMVerifier.certoraCollectRunMetadata import collect_run_metadata
|
19
|
+
from EVMVerifier import certoraContextValidator as Cv
|
20
|
+
|
21
|
+
import EVMVerifier.certoraContext as Ctx
|
22
|
+
import EVMVerifier.certoraContextAttributes as Attrs
|
23
|
+
|
24
|
+
from certoraRun import CertoraRunResult, VIOLATIONS_EXIT_CODE, CertoraFoundViolations
|
25
|
+
|
26
|
+
# logger for issues regarding the general run flow.
|
27
|
+
# Also serves as the default logger for errors originating from unexpected places.
|
28
|
+
run_logger = logging.getLogger("run")
|
29
|
+
|
30
|
+
|
31
|
+
def run_solana_prover(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]] = None) \
|
32
|
+
-> Optional[CertoraRunResult]:
|
33
|
+
"""
|
34
|
+
The main function that is responsible for the general flow of the script.
|
35
|
+
The general flow is:
|
36
|
+
1. Parse program arguments
|
37
|
+
2. Run the necessary steps (build/ cloud verification/ local verification)
|
38
|
+
"""
|
39
|
+
|
40
|
+
Attrs.set_attribute_class(Attrs.SolanaProverAttributes)
|
41
|
+
non_str_els = [x for x in args if not isinstance(x, str)]
|
42
|
+
if non_str_els:
|
43
|
+
print(f"args for run_certora that are not strings: {non_str_els}")
|
44
|
+
exit(1)
|
45
|
+
|
46
|
+
# If we are not in debug mode, we do not want to print the traceback in case of exceptions.
|
47
|
+
if '--debug' not in args: # We check manually, because we want no traceback in argument parsing exceptions
|
48
|
+
sys.tracebacklimit = 0
|
49
|
+
|
50
|
+
# creating the default internal dir, files may be copied to user defined build directory after
|
51
|
+
# parsing the input
|
52
|
+
|
53
|
+
if not ('--help' in args or '--version' in args):
|
54
|
+
Util.reset_certora_internal_dir()
|
55
|
+
Util.safe_create_dir(Util.get_build_dir())
|
56
|
+
logging_manager = LoggingManager()
|
57
|
+
|
58
|
+
Ctx.handle_flags_in_args(args)
|
59
|
+
context = Ctx.get_args(args) # Parse arguments
|
60
|
+
logging_manager.set_log_level_and_format(is_quiet=Ctx.is_minimal_cli_output(context),
|
61
|
+
debug=context.debug,
|
62
|
+
debug_topics=context.debug_topics,
|
63
|
+
show_debug_topics=context.show_debug_topics)
|
64
|
+
|
65
|
+
timings = {}
|
66
|
+
exit_code = 0 # The exit code of the script. 0 means success, any other number is an error.
|
67
|
+
return_value = None
|
68
|
+
|
69
|
+
metadata = (
|
70
|
+
collect_run_metadata(wd=Path.cwd(), raw_args=sys.argv, context=context))
|
71
|
+
|
72
|
+
if context.test == str(Util.TestValue.CHECK_METADATA):
|
73
|
+
raise Util.TestResultsReady(metadata)
|
74
|
+
metadata.dump()
|
75
|
+
|
76
|
+
# Build Solana - If input file is .so or .o file, we skip building part
|
77
|
+
run_logger.debug("Build Solana target")
|
78
|
+
build_start = time.perf_counter()
|
79
|
+
|
80
|
+
Ctx.build_rust_app(context)
|
81
|
+
build_end = time.perf_counter()
|
82
|
+
timings["buildTime"] = round(build_end - build_start, 4)
|
83
|
+
if context.test == str(Util.TestValue.AFTER_BUILD):
|
84
|
+
raise Util.TestResultsReady(None)
|
85
|
+
|
86
|
+
if context.local:
|
87
|
+
check_cmd = Ctx.get_local_run_cmd(context)
|
88
|
+
print(f"Verifier run command:\n {check_cmd}", flush=True)
|
89
|
+
|
90
|
+
compare_with_tool_output = False
|
91
|
+
run_result = Util.run_jar_cmd(check_cmd, compare_with_tool_output, logger_topic="verification",
|
92
|
+
print_output=True)
|
93
|
+
|
94
|
+
if run_result != 0:
|
95
|
+
exit_code = 1
|
96
|
+
else:
|
97
|
+
Util.print_completion_message("Finished running verifier:")
|
98
|
+
print(f"\t{check_cmd}")
|
99
|
+
else:
|
100
|
+
validate_version_and_branch(context)
|
101
|
+
context.key = Cv.validate_certora_key()
|
102
|
+
cloud_verifier = CloudVerification(context, timings)
|
103
|
+
# Wrap strings with space with ' so it can be copied and pasted to shell
|
104
|
+
pretty_args = [f"'{arg}'" if ' ' in arg else arg for arg in args]
|
105
|
+
cl_args = ' '.join(pretty_args)
|
106
|
+
logging_manager.remove_debug_logger()
|
107
|
+
result = cloud_verifier.cli_verify_and_report(cl_args, context.wait_for_results)
|
108
|
+
if cloud_verifier.statusUrl:
|
109
|
+
return_value = CertoraRunResult(cloud_verifier.statusUrl, False,
|
110
|
+
Util.get_certora_sources_dir(), cloud_verifier.reportUrl)
|
111
|
+
if not result:
|
112
|
+
exit_code = 1
|
113
|
+
|
114
|
+
if exit_code == VIOLATIONS_EXIT_CODE:
|
115
|
+
raise CertoraFoundViolations("violations were found", return_value)
|
116
|
+
if exit_code != 0:
|
117
|
+
raise Util.CertoraUserInputError(f"run_certora failed (code {exit_code})")
|
118
|
+
return return_value
|
119
|
+
|
120
|
+
|
121
|
+
def entry_point() -> None:
|
122
|
+
"""
|
123
|
+
This function is the entry point of the certora_cli customer-facing package, as well as this script.
|
124
|
+
It is important this function gets no arguments!
|
125
|
+
"""
|
126
|
+
try:
|
127
|
+
run_solana_prover(sys.argv[1:])
|
128
|
+
sys.exit(0)
|
129
|
+
except KeyboardInterrupt:
|
130
|
+
Console().print("[bold red]\nInterrupted by user")
|
131
|
+
sys.exit(1)
|
132
|
+
except CertoraFoundViolations as e:
|
133
|
+
try:
|
134
|
+
if e.results and e.results.rule_report_link:
|
135
|
+
print(f"report url: {e.results.rule_report_link}")
|
136
|
+
except Exception:
|
137
|
+
pass
|
138
|
+
Console().print("[bold red]\nViolations were found\n")
|
139
|
+
sys.exit(1)
|
140
|
+
except Util.CertoraUserInputError as e:
|
141
|
+
if e.orig:
|
142
|
+
print(f"\n{str(e.orig).strip()}")
|
143
|
+
if e.more_info:
|
144
|
+
print(f"\n{e.more_info.strip()}")
|
145
|
+
Console().print(f"[bold red]\n{e}\n")
|
146
|
+
sys.exit(1)
|
147
|
+
except Exception as e:
|
148
|
+
Console().print(f"[bold red]{e}")
|
149
|
+
sys.exit(1)
|
150
|
+
|
151
|
+
|
152
|
+
if __name__ == '__main__':
|
153
|
+
entry_point()
|
certora_cli/rustMutator.py
CHANGED
@@ -6,8 +6,8 @@ import argparse
|
|
6
6
|
from pathlib import Path
|
7
7
|
import logging
|
8
8
|
import re
|
9
|
-
import os
|
10
9
|
import random
|
10
|
+
from tqdm import tqdm
|
11
11
|
from typing import List, Tuple
|
12
12
|
|
13
13
|
from Shared import certoraUtils as Util
|
@@ -37,6 +37,13 @@ def parse_args() -> argparse.Namespace:
|
|
37
37
|
required=True,
|
38
38
|
help="Path to the Rust source file to mutate (e.g., src/lib.rs)"
|
39
39
|
)
|
40
|
+
parser.add_argument(
|
41
|
+
"--build_script",
|
42
|
+
"-b",
|
43
|
+
type=str,
|
44
|
+
required=True,
|
45
|
+
help = "Custom build command to execute for each mutant (e.g., 'cargo build --release')"
|
46
|
+
)
|
40
47
|
parser.add_argument(
|
41
48
|
"--mutants_location",
|
42
49
|
"-m",
|
@@ -82,16 +89,25 @@ def restore_source(backup_file: Path, file_to_mutant: Path) -> None:
|
|
82
89
|
rust_mutator_logger.warning(f"No backup file '{backup_file}' found. Skipping restoration.")
|
83
90
|
|
84
91
|
|
85
|
-
def clean_temp_files() -> None:
|
92
|
+
def clean_temp_files(file_to_mutant: Path) -> None:
|
86
93
|
"""
|
87
94
|
Remove temporary mutant output files matching the pattern '.um.mutant_output.*'.
|
95
|
+
|
96
|
+
Args:
|
97
|
+
file_to_mutant (Path): Path to the original source file.
|
88
98
|
"""
|
89
|
-
temp_files = Path().
|
99
|
+
temp_files = Path().rglob(".um.mutant_output.*")
|
90
100
|
|
91
101
|
rust_mutator_logger.info("Removing temporary mutant output files...")
|
92
102
|
for temp_file in temp_files:
|
93
103
|
temp_file.unlink()
|
94
104
|
rust_mutator_logger.info(f"Removed: {temp_file}")
|
105
|
+
|
106
|
+
backup_file = next(Path().rglob(f"{file_to_mutant}.um.backup.*"), None)
|
107
|
+
if backup_file:
|
108
|
+
backup_file.unlink()
|
109
|
+
rust_mutator_logger.info(f"Removed: {backup_file}")
|
110
|
+
|
95
111
|
rust_mutator_logger.info("Temporary files removal completed.")
|
96
112
|
|
97
113
|
|
@@ -224,18 +240,6 @@ def validate_mutant_count(file_to_mutant: Path, mutants_location: Path, num_muta
|
|
224
240
|
rust_mutator_logger.debug(f"Renamed {mutant} to {new_file_name}")
|
225
241
|
break
|
226
242
|
|
227
|
-
|
228
|
-
def validate_cargo() -> None:
|
229
|
-
"""
|
230
|
-
Validate that the cargo.toml file is in the current working directory.
|
231
|
-
|
232
|
-
Raises:
|
233
|
-
Util.CertoraUserInputError: If validation fails.
|
234
|
-
"""
|
235
|
-
if not Path(Util.CARGO_FILE).exists():
|
236
|
-
raise Util.CertoraUserInputError(f"'{Util.CARGO_FILE}' not found in the current working directory: {os.getcwd()}")
|
237
|
-
|
238
|
-
|
239
243
|
def validate_mutate_command() -> None:
|
240
244
|
"""
|
241
245
|
Validate that the universalmutator command is available in the PATH.
|
@@ -247,15 +251,17 @@ def validate_mutate_command() -> None:
|
|
247
251
|
raise Util.CertoraUserInputError("universalmutator command 'mutate' not found in PATH.")
|
248
252
|
|
249
253
|
|
250
|
-
def run_mutate(file_to_mutant: Path, mutants_location: Path, build_command: str
|
254
|
+
def run_mutate(file_to_mutant: Path, mutants_location: Path, build_command: str) -> None:
|
251
255
|
"""
|
252
|
-
Execute the universalmutator command to generate mutants.
|
256
|
+
Execute the universalmutator command to generate mutants, displaying a progress bar.
|
253
257
|
|
254
258
|
Args:
|
255
259
|
file_to_mutant (Path): Path to the Rust source file.
|
256
260
|
mutants_location (Path): Directory to store generated mutants.
|
257
261
|
build_command (str): Command to execute for each mutant to verify compilation.
|
258
|
-
|
262
|
+
|
263
|
+
Raises:
|
264
|
+
subprocess.CalledProcessError: If the mutate command fails.
|
259
265
|
"""
|
260
266
|
mutate_command = [
|
261
267
|
"mutate",
|
@@ -266,22 +272,87 @@ def run_mutate(file_to_mutant: Path, mutants_location: Path, build_command: str,
|
|
266
272
|
"--cmd",
|
267
273
|
build_command
|
268
274
|
]
|
269
|
-
|
270
275
|
rust_mutator_logger.info("Generating mutants...")
|
271
276
|
rust_mutator_logger.debug(f"Running universalmutator with command: {' '.join(mutate_command)}")
|
272
|
-
if not debug:
|
273
|
-
subprocess.run(mutate_command, check=True, stdout=subprocess.DEVNULL)
|
274
|
-
else:
|
275
|
-
subprocess.run(mutate_command, check=True)
|
276
|
-
rust_mutator_logger.info("Mutation generation completed successfully.")
|
277
277
|
|
278
|
+
# Initialize the subprocess with real-time output capture
|
279
|
+
process = subprocess.Popen(
|
280
|
+
mutate_command,
|
281
|
+
stdout=subprocess.PIPE,
|
282
|
+
stderr=subprocess.STDOUT,
|
283
|
+
universal_newlines=True,
|
284
|
+
bufsize=1
|
285
|
+
)
|
286
|
+
|
287
|
+
total_mutants = None
|
288
|
+
mutant_pattern = re.compile(r"^PROCESSING MUTANT: (\d+):")
|
289
|
+
total_pattern = re.compile(r"^(\d+) MUTANTS GENERATED BY RULES")
|
290
|
+
valid_mutant_pattern = re.compile(rf"VALID \[written to {mutants_location.stem}/{file_to_mutant.stem}\.mutant\.\d+\.rs\]")
|
291
|
+
|
292
|
+
progress_bar = None
|
293
|
+
|
294
|
+
try:
|
295
|
+
if not process.stdout:
|
296
|
+
rust_mutator_logger.error("Failed to capture output from the mutate command.")
|
297
|
+
raise subprocess.CalledProcessError(1, mutate_command)
|
298
|
+
|
299
|
+
for line in process.stdout:
|
300
|
+
line = line.strip()
|
301
|
+
rust_mutator_logger.debug(line)
|
302
|
+
|
303
|
+
# Parse total number of mutants from the initial output
|
304
|
+
if total_mutants is None:
|
305
|
+
total_match = total_pattern.match(line)
|
306
|
+
if total_match:
|
307
|
+
total_mutants = int(total_match.group(1))
|
308
|
+
progress_bar = tqdm(total=total_mutants, desc="Mutants Generated", unit="mutant")
|
309
|
+
rust_mutator_logger.info(f"Total mutants to generate: {total_mutants}")
|
310
|
+
continue
|
311
|
+
|
312
|
+
# Update progress bar for each processed mutant
|
313
|
+
mutant_match = mutant_pattern.match(line)
|
314
|
+
if mutant_match and progress_bar:
|
315
|
+
progress_bar.update(1)
|
316
|
+
continue
|
317
|
+
|
318
|
+
# Optionally, handle validation messages
|
319
|
+
if valid_mutant_pattern.search(line):
|
320
|
+
rust_mutator_logger.info(f"Valid mutant generated: {line}")
|
321
|
+
|
322
|
+
process.wait()
|
278
323
|
|
279
|
-
|
324
|
+
if progress_bar:
|
325
|
+
progress_bar.close()
|
326
|
+
|
327
|
+
if process.returncode != 0:
|
328
|
+
rust_mutator_logger.error(f"Mutation generation failed with return code {process.returncode}")
|
329
|
+
raise subprocess.CalledProcessError(process.returncode, mutate_command)
|
330
|
+
|
331
|
+
rust_mutator_logger.info("Mutation generation completed successfully.")
|
332
|
+
|
333
|
+
except Exception as e:
|
334
|
+
if progress_bar:
|
335
|
+
progress_bar.close()
|
336
|
+
rust_mutator_logger.error(f"An error occurred during mutation generation: {e}")
|
337
|
+
raise
|
338
|
+
finally:
|
339
|
+
if process and process.poll() is None:
|
340
|
+
process.terminate()
|
341
|
+
|
342
|
+
def run_universal_mutator(
|
343
|
+
file_to_mutant: Path,
|
344
|
+
build_script: str,
|
345
|
+
mutants_location: Path,
|
346
|
+
num_mutants: int = NUM_MUTANTS,
|
347
|
+
seed: int = SEED,
|
348
|
+
debug: bool = False
|
349
|
+
) -> None:
|
280
350
|
f"""
|
281
351
|
Generate mutants for the specified source file and ensure the original file remains unchanged.
|
282
352
|
|
283
353
|
Args:
|
284
354
|
file_to_mutant (Path): Path to the Rust source file.
|
355
|
+
build_script (str): Command to execute for each mutant to verify compilation.
|
285
356
|
mutants_location (Path): Directory to store generated mutants.
|
286
357
|
num_mutants (int): Upper bound on the number of mutants to generate (default: {NUM_MUTANTS}).
|
287
358
|
seed (int): Seed value for random selection to ensure repeatable testing (default: {SEED}).
|
@@ -291,14 +362,9 @@ def run_universal_mutator(file_to_mutant: Path, mutants_location: Path, num_muta
|
|
291
362
|
Util.CertoraUserInputError: If any validation fails.
|
292
363
|
Exception: For unexpected errors.
|
293
364
|
"""
|
294
|
-
|
295
|
-
# Define the build command, ensuring it dynamically references the correct source file
|
296
|
-
build_command = (
|
297
|
-
f'cp MUTANT {file_to_mutant} && '
|
298
|
-
'RUSTFLAGS="-C strip=none --emit=llvm-ir" '
|
299
|
-
'cargo build --target=wasm32-unknown-unknown --release --features certora'
|
300
|
-
)
|
301
365
|
backup_file = None
|
366
|
+
build_command = f"cp MUTATE & python3 {build_script}"
|
367
|
+
|
302
368
|
try:
|
303
369
|
# Set up the logger
|
304
370
|
setup_logger(debug)
|
@@ -306,9 +372,6 @@ def run_universal_mutator(file_to_mutant: Path, mutants_location: Path, num_muta
|
|
306
372
|
# Validate mutate command is available
|
307
373
|
validate_mutate_command()
|
308
374
|
|
309
|
-
# Validate cargo.toml is in CWD
|
310
|
-
validate_cargo()
|
311
|
-
|
312
375
|
# Validate source file
|
313
376
|
validate_source_file(file_to_mutant)
|
314
377
|
|
@@ -324,7 +387,7 @@ def run_universal_mutator(file_to_mutant: Path, mutants_location: Path, num_muta
|
|
324
387
|
validate_mutant_dir(mutants_location)
|
325
388
|
|
326
389
|
# Run the mutation command
|
327
|
-
run_mutate(file_to_mutant, mutants_location, build_command
|
390
|
+
run_mutate(file_to_mutant, mutants_location, build_command)
|
328
391
|
|
329
392
|
# Make sure the number of mutants generated is less than or equal to the specified limit
|
330
393
|
validate_mutant_count(file_to_mutant, mutants_location, num_mutants, seed)
|
@@ -334,10 +397,17 @@ def run_universal_mutator(file_to_mutant: Path, mutants_location: Path, num_muta
|
|
334
397
|
if backup_file:
|
335
398
|
Util.restore_backup(backup_file)
|
336
399
|
# Clean up temporary files
|
337
|
-
clean_temp_files()
|
400
|
+
clean_temp_files(file_to_mutant)
|
338
401
|
|
339
402
|
|
340
403
|
if __name__ == "__main__":
|
341
404
|
# Parse command-line arguments
|
342
405
|
args = parse_args()
|
343
|
-
run_universal_mutator(
|
406
|
+
run_universal_mutator(
|
407
|
+
args.file_to_mutant,
|
408
|
+
args.build_script,
|
409
|
+
args.mutants_location,
|
410
|
+
args.num_mutants,
|
411
|
+
args.seed,
|
412
|
+
args.debug
|
413
|
+
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: certora-cli-alpha-master
|
3
|
-
Version:
|
3
|
+
Version: 20241217.16.7.546354
|
4
4
|
Summary: Runner for the Certora Prover
|
5
5
|
Home-page: https://pypi.org/project/certora-cli-alpha-master
|
6
6
|
Author: Certora
|
@@ -23,4 +23,4 @@ Requires-Dist: StrEnum
|
|
23
23
|
Requires-Dist: tomli
|
24
24
|
Requires-Dist: universalmutator
|
25
25
|
|
26
|
-
Commit
|
26
|
+
Commit 77317ff. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
@@ -2,25 +2,27 @@ certora_bins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
certora_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
certora_cli/certoraEqCheck.py,sha256=5LtN9GNBSYR_kYghDBeNNL4yWshPw9PNpe0stLtuIxQ,379
|
4
4
|
certora_cli/certoraMutate.py,sha256=BUK_keaunrcCYgLiwHfIkDSjIHM_KoGwwnIiuojc-ug,2643
|
5
|
-
certora_cli/certoraRun.py,sha256=
|
6
|
-
certora_cli/
|
5
|
+
certora_cli/certoraRun.py,sha256=rnczNnSoYRuUKOkIN7_0cRxjiVDK0x4NJxOT4KNkYdg,11884
|
6
|
+
certora_cli/certoraSolanaProver.py,sha256=3Vb05zd5PqkU5CmCNzYvmYfbC-hsmU6Tt390msjQlOc,5898
|
7
|
+
certora_cli/rustMutator.py,sha256=wSE_4qDegDhItxHw7dtcFzDMgYCTsVxW4yFGxYRHlQc,14418
|
7
8
|
certora_cli/EVMVerifier/__init__.py,sha256=AJxj90KAGh1JqAsxKqDBTL2rFbqgtkhDfW_XmxSHft0,159
|
8
9
|
certora_cli/EVMVerifier/certoraBuild.py,sha256=omIs-4cQWR-IT3WTckXMZe9EZMyk2e-hSfRqG7iQJHo,203131
|
9
10
|
certora_cli/EVMVerifier/certoraBuildCacheManager.py,sha256=ZSVCsdTkFFtawuNJ8VK3wJOb01Uyu5e8viPQ8wTziSQ,12563
|
10
11
|
certora_cli/EVMVerifier/certoraBuildDataClasses.py,sha256=4IhvOA_fg-ij5RM9RRlvDDoS7imfp4Zru-AgHhxcHc0,13314
|
11
|
-
certora_cli/EVMVerifier/certoraCloudIO.py,sha256=
|
12
|
+
certora_cli/EVMVerifier/certoraCloudIO.py,sha256=ZZDjMsSkp0ZN5IA1JczhJa2m16s0nC3DBcHHDv5J2kc,54931
|
12
13
|
certora_cli/EVMVerifier/certoraCollectRunMetadata.py,sha256=selEf-p7UW8_70-AbPfa5659ki6WcGrBwfPkUQWKhFU,10322
|
13
14
|
certora_cli/EVMVerifier/certoraCompilerParameters.py,sha256=PDKW3K_lNeRNPHisW0e5hYtfu6unDRjJUop-yItGHO0,762
|
14
|
-
certora_cli/EVMVerifier/certoraConfigIO.py,sha256=
|
15
|
-
certora_cli/EVMVerifier/certoraContext.py,sha256=
|
16
|
-
certora_cli/EVMVerifier/certoraContextAttributes.py,sha256=
|
15
|
+
certora_cli/EVMVerifier/certoraConfigIO.py,sha256=oyNOyBjVWFfB1dmj0jNnEwvokM7UMoGZ91luSh-cFGk,4634
|
16
|
+
certora_cli/EVMVerifier/certoraContext.py,sha256=z86n6XbsU9dKfxiIqlPrCHz6EtrBAuYIRE0BS4rxSHQ,21974
|
17
|
+
certora_cli/EVMVerifier/certoraContextAttributes.py,sha256=__JqYpti0mDwBvgtunajEotCAKprUyoeDabe1ooSnro,55637
|
17
18
|
certora_cli/EVMVerifier/certoraContextClass.py,sha256=qdHYmrzI4zeQaAaMnonw4C-h1PFrPEhI84trrgvxFj0,210
|
18
|
-
certora_cli/EVMVerifier/certoraContextValidator.py,sha256=
|
19
|
+
certora_cli/EVMVerifier/certoraContextValidator.py,sha256=YE4_BYRhZhXWEtjodD_Pligh8opdaOjs27mVEoy74ZY,37210
|
19
20
|
certora_cli/EVMVerifier/certoraContractFuncs.py,sha256=W3lAvKotGKK7ZGJLg4yYh0FzTwwSQJbZMpizyGaYXZo,6229
|
20
21
|
certora_cli/EVMVerifier/certoraExtensionInfo.py,sha256=bHjEBKg3qQ1lS419KSU6v6HKbhrSOz2TmEE21YrOfYI,1336
|
21
22
|
certora_cli/EVMVerifier/certoraJobList.py,sha256=JlPOANEzNHP6cDrM5UKfO314pmWYokuhAPTkH3ezNb4,10768
|
22
23
|
certora_cli/EVMVerifier/certoraMiniSpecParser.py,sha256=Thtcr1BLmCz9xOiOkaP04mSnrDaqIUOUSCM81TrqD_s,8970
|
23
24
|
certora_cli/EVMVerifier/certoraNodeFilters.py,sha256=0e8iPdj4cWjwReaAhfJhpdnSUYc8gephmKQzOOP5X_g,2140
|
25
|
+
certora_cli/EVMVerifier/certoraParseBuildScript.py,sha256=vX_lhus5VgLQaZp5GrNtJP6R5cqChjv_Rfpk_JZz2uY,1456
|
24
26
|
certora_cli/EVMVerifier/certoraSourceFinders.py,sha256=fZLYGjNgntIySnG76DwsjRZ-UHiy0zEkM6xNP7cYpDE,18342
|
25
27
|
certora_cli/EVMVerifier/certoraType.py,sha256=mmMQ2yrffKmoH_dHhO7cMQfEENZwHw1jLbyUo98xzas,28598
|
26
28
|
certora_cli/EVMVerifier/certoraVerifyGenerator.py,sha256=OT5zrtQMXDoaVdGfQ4_gbAnITin3nlXgXTgw3msqlNQ,9452
|
@@ -40,7 +42,7 @@ certora_cli/EquivalenceCheck/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
40
42
|
certora_cli/EquivalenceCheck/equivCheck.py,sha256=eG6jCa1DmHqPweGpjby-6Bnym91maffH4olE22uB2-c,19828
|
41
43
|
certora_cli/EquivalenceCheck/sanity.spec,sha256=tWmE9z2Sq3_SWaqKDRQaNajRrw94maUrirvoUmX89LE,103
|
42
44
|
certora_cli/Mutate/__init__.py,sha256=AJxj90KAGh1JqAsxKqDBTL2rFbqgtkhDfW_XmxSHft0,159
|
43
|
-
certora_cli/Mutate/mutateApp.py,sha256=
|
45
|
+
certora_cli/Mutate/mutateApp.py,sha256=wDCfkgXgV3fdAGw8SxWt1hlU1W9kl90HOcQIdNpb_aM,85871
|
44
46
|
certora_cli/Mutate/mutateAttributes.py,sha256=l7tpkq3WZumVBuORaM2VLv93Px_X_XueL2IkfThTMNg,9402
|
45
47
|
certora_cli/Mutate/mutateConstants.py,sha256=SHFQ0lBCYwJYH3Uk2ncG-7-4ANQIC4JRczNvWflfNU4,3277
|
46
48
|
certora_cli/Mutate/mutateUtil.py,sha256=O3ub9gJe6LWQTDPbWyDqdfBhupwYl0ZrlfRX4kSn18c,1597
|
@@ -49,14 +51,14 @@ certora_cli/Shared/ExpectedComparator.py,sha256=rDDY3-FaE3OS_ZjZ-nOyvUTlUp8eQCHw
|
|
49
51
|
certora_cli/Shared/__init__.py,sha256=QGoFb_Uu87tWp4E4L6C_VtzdG-sfNrzdNtRK79h5_Lw,333
|
50
52
|
certora_cli/Shared/certoraAttrUtil.py,sha256=IbIIvTbtPzmPSSrK7VIWZNitkz1iwvW_e0ggX1e2joY,6751
|
51
53
|
certora_cli/Shared/certoraLogging.py,sha256=5Lx-XWKl7GnwnWi7KlwTLIfsEvUvCTZ8KeyfNyi_6RU,13323
|
52
|
-
certora_cli/Shared/certoraUtils.py,sha256=
|
53
|
-
certora_cli/Shared/certoraValidateFuncs.py,sha256=
|
54
|
-
certora_jars/CERTORA-CLI-VERSION-METADATA.json,sha256=
|
55
|
-
certora_jars/Typechecker.jar,sha256=
|
54
|
+
certora_cli/Shared/certoraUtils.py,sha256=8EGXrxfIxu34VTx46QeTx6tTYLdwpTLT9ulVQijmD9Y,53624
|
55
|
+
certora_cli/Shared/certoraValidateFuncs.py,sha256=OOjPAkcfrURZDD4oDjOMBFTvY6wwQSXboXzu-47AUbY,36871
|
56
|
+
certora_jars/CERTORA-CLI-VERSION-METADATA.json,sha256=CKa-Wwq9Rh28A51DZy0AdvS1UqUA3YxH7iMGm-ELUEY,168
|
57
|
+
certora_jars/Typechecker.jar,sha256=no7ucRdariDhJSTuF91i1KdG6vCuRJxWSeLFOkvuylc,16680403
|
56
58
|
certora_jars/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
-
certora_cli_alpha_master-
|
58
|
-
certora_cli_alpha_master-
|
59
|
-
certora_cli_alpha_master-
|
60
|
-
certora_cli_alpha_master-
|
61
|
-
certora_cli_alpha_master-
|
62
|
-
certora_cli_alpha_master-
|
59
|
+
certora_cli_alpha_master-20241217.16.7.546354.dist-info/LICENSE,sha256=VeEBJLgfzZqyAUfjLoKUztf7KJBBUjtZ1ap99eQubOo,1065
|
60
|
+
certora_cli_alpha_master-20241217.16.7.546354.dist-info/METADATA,sha256=IlmCZaJUG0ha0YrHEfQoy1AtGPGlzeLm131hzHWb9j0,837
|
61
|
+
certora_cli_alpha_master-20241217.16.7.546354.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
62
|
+
certora_cli_alpha_master-20241217.16.7.546354.dist-info/entry_points.txt,sha256=Y90wInBDlKPSU3YnuTn3JqAsSgPRTDHc8lf6BfKILjY,261
|
63
|
+
certora_cli_alpha_master-20241217.16.7.546354.dist-info/top_level.txt,sha256=8C77w3JLanY0-NW45vpJsjRssyCqVP-qmPiN9FjWiX4,38
|
64
|
+
certora_cli_alpha_master-20241217.16.7.546354.dist-info/RECORD,,
|
@@ -1 +1 @@
|
|
1
|
-
{"name": "certora-cli-alpha-master", "tag": "", "branch": "master", "commit": "
|
1
|
+
{"name": "certora-cli-alpha-master", "tag": "", "branch": "master", "commit": "77317ff", "timestamp": "20241217.16.7.546354", "version": "20241217.16.7.546354+77317ff"}
|
certora_jars/Typechecker.jar
CHANGED
Binary file
|
File without changes
|
File without changes
|