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
|
@@ -43,7 +43,6 @@ from tqdm import tqdm
|
|
|
43
43
|
|
|
44
44
|
import logging
|
|
45
45
|
|
|
46
|
-
|
|
47
46
|
cloud_logger = logging.getLogger("cloud")
|
|
48
47
|
|
|
49
48
|
MAX_FILE_SIZE = 25 * 1024 * 1024
|
|
@@ -74,11 +73,12 @@ class EcoEnum(Util.NoValEnum):
|
|
|
74
73
|
EVM = Util.auto()
|
|
75
74
|
SOROBAN = Util.auto()
|
|
76
75
|
SOLANA = Util.auto()
|
|
76
|
+
SUI = Util.auto()
|
|
77
77
|
|
|
78
78
|
class ProductEnum(Util.NoValEnum):
|
|
79
79
|
PROVER = Util.auto()
|
|
80
80
|
RANGER = Util.auto()
|
|
81
|
-
|
|
81
|
+
CONCORD = Util.auto()
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class TimeError(Exception):
|
|
@@ -587,7 +587,7 @@ class CloudVerification:
|
|
|
587
587
|
"""
|
|
588
588
|
|
|
589
589
|
auth_data = {
|
|
590
|
-
"
|
|
590
|
+
"runName": self.runName,
|
|
591
591
|
"run_source": self.context.run_source,
|
|
592
592
|
"group_id": self.context.group_id,
|
|
593
593
|
"branch": self.context.prover_version if self.context.prover_version else '',
|
|
@@ -624,6 +624,13 @@ class CloudVerification:
|
|
|
624
624
|
rust_jar_settings.append(paths_in_source_dir(self.context.solana_inlining))
|
|
625
625
|
|
|
626
626
|
auth_data["jarSettings"] = rust_jar_settings + jar_settings
|
|
627
|
+
|
|
628
|
+
elif Attrs.is_sui_app():
|
|
629
|
+
sui_jar_settings = ['-movePath', Path(self.context.move_path).name]
|
|
630
|
+
auth_data["jarSettings"] = sui_jar_settings + jar_settings
|
|
631
|
+
|
|
632
|
+
elif Attrs.is_concord_app():
|
|
633
|
+
auth_data["jarSettings"] = jar_settings + ["-equivalenceCheck", "true"]
|
|
627
634
|
else:
|
|
628
635
|
auth_data["jarSettings"] = jar_settings
|
|
629
636
|
|
|
@@ -645,13 +652,15 @@ class CloudVerification:
|
|
|
645
652
|
auth_data["ecosystem"] = EcoEnum.SOLANA.name
|
|
646
653
|
elif Attrs.is_soroban_app():
|
|
647
654
|
auth_data["ecosystem"] = EcoEnum.SOROBAN.name
|
|
655
|
+
elif Attrs.is_sui_app():
|
|
656
|
+
auth_data["ecosystem"] = EcoEnum.SUI.name
|
|
648
657
|
else:
|
|
649
658
|
auth_data["ecosystem"] = EcoEnum.EVM.name
|
|
650
659
|
|
|
651
660
|
if Attrs.is_ranger_app():
|
|
652
661
|
auth_data["product"] = ProductEnum.RANGER.name
|
|
653
|
-
elif Attrs.
|
|
654
|
-
auth_data["product"] = ProductEnum.
|
|
662
|
+
elif Attrs.is_concord_app():
|
|
663
|
+
auth_data["product"] = ProductEnum.CONCORD.name
|
|
655
664
|
else:
|
|
656
665
|
auth_data["product"] = ProductEnum.PROVER.name
|
|
657
666
|
|
|
@@ -686,12 +695,12 @@ class CloudVerification:
|
|
|
686
695
|
|
|
687
696
|
def print_output_links(self) -> None:
|
|
688
697
|
print(f"Manage your jobs at {self.get_domain()}")
|
|
689
|
-
print(f"Follow your job and see verification results at {self.
|
|
698
|
+
print(f"Follow your job and see verification results at {self.url_print_format()}", flush=True)
|
|
690
699
|
|
|
691
700
|
def print_verification_summary(self) -> None:
|
|
692
701
|
report_exists = self.check_file_exists(params={"filename": "index.html", "certoraKey": self.context.key})
|
|
693
702
|
if report_exists:
|
|
694
|
-
print(f"Job is completed! View the results at {self.
|
|
703
|
+
print(f"Job is completed! View the results at {self.url_print_format()}")
|
|
695
704
|
print("Finished verification request", flush=True)
|
|
696
705
|
|
|
697
706
|
def __send_verification_request(self, cl_args: str) -> bool:
|
|
@@ -760,42 +769,30 @@ class CloudVerification:
|
|
|
760
769
|
Util.get_configuration_layout_data_file(),
|
|
761
770
|
Util.get_build_dir() / Path(self.context.files[0]).name]
|
|
762
771
|
|
|
772
|
+
if Util.get_debug_log_file().exists():
|
|
773
|
+
files_list.append(Util.get_debug_log_file())
|
|
774
|
+
|
|
763
775
|
if Util.get_certora_sources_dir().exists():
|
|
764
776
|
files_list.append(Util.get_certora_sources_dir())
|
|
765
777
|
|
|
766
778
|
if hasattr(self.context, 'build_script') and self.context.build_script:
|
|
767
|
-
result = compress_files(self.logZipFilePath, Util.get_debug_log_file(),
|
|
768
|
-
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
769
|
-
|
|
770
|
-
if not result:
|
|
771
|
-
return False
|
|
772
|
-
files_list.append(self.logZipFilePath)
|
|
773
|
-
|
|
774
|
-
# Create a .RustExecution file to classify zipInput as a rust source code
|
|
775
|
-
rust_execution_file = Util.get_build_dir() / ".RustExecution"
|
|
776
|
-
rust_execution_file.touch(exist_ok=True)
|
|
777
|
-
files_list.append(rust_execution_file)
|
|
778
|
-
|
|
779
779
|
if attr_file := getattr(self.context, 'rust_logs_stdout', None):
|
|
780
780
|
files_list.append(Util.get_build_dir() / Path(attr_file).name)
|
|
781
781
|
if attr_file := getattr(self.context, 'rust_logs_stderr', None):
|
|
782
782
|
files_list.append(Util.get_build_dir() / Path(attr_file).name)
|
|
783
783
|
|
|
784
|
-
|
|
785
|
-
|
|
784
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
|
785
|
+
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
786
|
+
elif Attrs.is_sui_app():
|
|
787
|
+
files_list = [Util.get_certora_metadata_file(),
|
|
788
|
+
Util.get_configuration_layout_data_file(),
|
|
789
|
+
Util.get_build_dir() / Path(self.context.move_path).name]
|
|
786
790
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
files_list.append(Path(file))
|
|
791
|
+
if Util.get_certora_sources_dir().exists():
|
|
792
|
+
files_list.append(Util.get_certora_sources_dir())
|
|
790
793
|
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
else:
|
|
794
|
-
raise Util.CertoraUserInputError(
|
|
795
|
-
'When running rust application, context should either have attribute "rust_executables" '
|
|
796
|
-
'provided by build_script execution, '
|
|
797
|
-
'or either is_solana_app(), is_soroban_app() should be set to True'
|
|
798
|
-
)
|
|
794
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
|
795
|
+
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
799
796
|
|
|
800
797
|
else:
|
|
801
798
|
# Zip the log file first separately and again with the rest of the files, so it will not be decompressed
|
|
@@ -873,7 +870,7 @@ class CloudVerification:
|
|
|
873
870
|
self.runName, self.reportUrl, self.msg, self.get_domain(), str(self.userId), self.anonymousKey)
|
|
874
871
|
|
|
875
872
|
# Generate a json file for the VS Code extension with the relevant url
|
|
876
|
-
self.vscode_extension_info_writer.add_field("verification_report_url", self.
|
|
873
|
+
self.vscode_extension_info_writer.add_field("verification_report_url", self.url_print_format())
|
|
877
874
|
self.vscode_extension_info_writer.write_file()
|
|
878
875
|
|
|
879
876
|
if not wait_for_results or wait_for_results == str(Vf.WaitForResultOptions.NONE):
|
|
@@ -953,6 +950,8 @@ class CloudVerification:
|
|
|
953
950
|
for i in range(self.verification_request_retries):
|
|
954
951
|
try:
|
|
955
952
|
response = requests.post(verify_url, json=auth_data, headers=headers, timeout=60)
|
|
953
|
+
cloud_logger.debug(f"\n\n=================\n\nresponse - Status Code: {response.status_code}\n\n"
|
|
954
|
+
f"Headers:\n {response.headers}\n\nText:\n {response.text}\n\n=================\n\n")
|
|
956
955
|
if response is None:
|
|
957
956
|
continue
|
|
958
957
|
status = response.status_code
|
|
@@ -1160,6 +1159,12 @@ class CloudVerification:
|
|
|
1160
1159
|
Util.remove_file(self.ZipFilePath)
|
|
1161
1160
|
Util.remove_file(self.logZipFilePath)
|
|
1162
1161
|
|
|
1162
|
+
def url_print_format(self) -> str:
|
|
1163
|
+
if self.reportUrl and self.context.url_visibility == str(Vf.UrlVisibilityOptions.PRIVATE):
|
|
1164
|
+
return self.privatize_url()
|
|
1165
|
+
else:
|
|
1166
|
+
return self.reportUrl
|
|
1167
|
+
|
|
1163
1168
|
@lru_cache(maxsize=1, typed=False)
|
|
1164
1169
|
def get_domain(self) -> str:
|
|
1165
1170
|
"""
|
|
@@ -1175,3 +1180,7 @@ class CloudVerification:
|
|
|
1175
1180
|
domain = Util.SupportedServers.PRODUCTION.value
|
|
1176
1181
|
|
|
1177
1182
|
return domain
|
|
1183
|
+
|
|
1184
|
+
@lru_cache(maxsize=1)
|
|
1185
|
+
def privatize_url(self) -> str:
|
|
1186
|
+
return self.reportUrl.split('?anonymousKey=')[0]
|
|
@@ -25,12 +25,13 @@ sys.path.insert(0, str(scripts_dir_path))
|
|
|
25
25
|
import CertoraProver.certoraContextAttributes as Attrs
|
|
26
26
|
from CertoraProver.certoraCollectRunMetadata import RunMetaData, MetadataEncoder
|
|
27
27
|
import Shared.certoraUtils as Utils
|
|
28
|
+
from typing import List
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
class MainSection(Enum):
|
|
31
32
|
GENERAL = "GENERAL"
|
|
33
|
+
OPTIONS = "OPTIONS"
|
|
32
34
|
SOLIDITY_COMPILER = "SOLIDITY_COMPILER"
|
|
33
|
-
GIT = "GIT"
|
|
34
35
|
NEW_SECTION = "NEW_SECTION"
|
|
35
36
|
|
|
36
37
|
|
|
@@ -47,13 +48,11 @@ class InnerContent:
|
|
|
47
48
|
content: Any
|
|
48
49
|
doc_link: str = ''
|
|
49
50
|
tooltip: str = ''
|
|
50
|
-
unsound:
|
|
51
|
+
unsound: bool = False
|
|
51
52
|
|
|
52
53
|
def __post_init__(self) -> None:
|
|
53
54
|
if isinstance(self.content, bool):
|
|
54
55
|
self.content = 'true' if self.content else 'false'
|
|
55
|
-
if isinstance(self.unsound, bool):
|
|
56
|
-
self.unsound = 'true' if self.unsound else 'false'
|
|
57
56
|
|
|
58
57
|
|
|
59
58
|
@dataclasses.dataclass
|
|
@@ -65,6 +64,7 @@ class CardContent:
|
|
|
65
64
|
|
|
66
65
|
DOC_LINK_PREFIX = 'https://docs.certora.com/en/latest/docs/'
|
|
67
66
|
GIT_ATTRIBUTES = ['origin', 'revision', 'branch', 'dirty']
|
|
67
|
+
ARG_LIST_ATTRIBUTES = ['prover_args', 'java_args']
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
class AttributeJobConfigData:
|
|
@@ -73,13 +73,13 @@ class AttributeJobConfigData:
|
|
|
73
73
|
This should be added to the AttributeDefinition and configured for every new attribute
|
|
74
74
|
presented in the Rule report.
|
|
75
75
|
|
|
76
|
-
Note: Attributes
|
|
76
|
+
Note: Attributes that do not contain specific information will be presented in the OPTIONS main section!
|
|
77
77
|
|
|
78
78
|
arguments:
|
|
79
79
|
- main_section : MainSection -- the main section inside the config tab
|
|
80
|
-
default: MainSection.
|
|
81
|
-
- subsection : str -- the subsection within the main_section
|
|
82
|
-
default:
|
|
80
|
+
default: MainSection.OPTIONS
|
|
81
|
+
- subsection : str -- the subsection within the main_section
|
|
82
|
+
default: None - they will be presented inside the OPTIONS card
|
|
83
83
|
- doc_link : Optional[str] -- a link to the Documentation page of this attribute (if exists)
|
|
84
84
|
default: 'https://docs.certora.com/en/latest/docs/' + Solana/EVM path + #<attribute_name>
|
|
85
85
|
- tooltip : Optional[str] -- a description of this attribute to present in the config tab
|
|
@@ -88,7 +88,7 @@ class AttributeJobConfigData:
|
|
|
88
88
|
default: False
|
|
89
89
|
"""
|
|
90
90
|
|
|
91
|
-
def __init__(self, main_section: MainSection = MainSection.
|
|
91
|
+
def __init__(self, main_section: MainSection = MainSection.OPTIONS, subsection: str = '',
|
|
92
92
|
doc_link: Optional[str] = '', tooltip: Optional[str] = '', unsound: bool = False):
|
|
93
93
|
self.main_section = main_section
|
|
94
94
|
self.subsection = subsection
|
|
@@ -201,6 +201,40 @@ def create_or_get_card_content(output: list[CardContent], name: str) -> CardCont
|
|
|
201
201
|
return main_section
|
|
202
202
|
|
|
203
203
|
|
|
204
|
+
def split_and_sort_arg_list_value(args_list: List[str]) -> List[str]:
|
|
205
|
+
"""
|
|
206
|
+
Splits a unified CLI argument list of strings into a sorted list of flag+value groups.
|
|
207
|
+
This is useful mainly for --prover_args and --java_args.
|
|
208
|
+
|
|
209
|
+
For example:
|
|
210
|
+
"-depth 15 -adaptiveSolverConfig false" → ["-adaptiveSolverConfig false", "-depth 15"]
|
|
211
|
+
|
|
212
|
+
Assumes each flag starts with '-' and its value follows immediately, if exists.
|
|
213
|
+
Lines are sorted alphabetically.
|
|
214
|
+
"""
|
|
215
|
+
unified_args = ''.join(args_list)
|
|
216
|
+
|
|
217
|
+
if not unified_args.strip():
|
|
218
|
+
return []
|
|
219
|
+
|
|
220
|
+
lines: List[str] = []
|
|
221
|
+
tokens = unified_args.split()
|
|
222
|
+
curr_line = ""
|
|
223
|
+
|
|
224
|
+
for token in tokens:
|
|
225
|
+
if token.startswith('-'):
|
|
226
|
+
if curr_line:
|
|
227
|
+
lines.append(curr_line)
|
|
228
|
+
curr_line = token
|
|
229
|
+
else:
|
|
230
|
+
curr_line += f" {token}"
|
|
231
|
+
|
|
232
|
+
if curr_line:
|
|
233
|
+
lines.append(curr_line)
|
|
234
|
+
|
|
235
|
+
return sorted(lines)
|
|
236
|
+
|
|
237
|
+
|
|
204
238
|
def create_inner_content(name: str, content_type: ContentType, value: Any, doc_link: str,
|
|
205
239
|
config_data: AttributeJobConfigData) -> InnerContent:
|
|
206
240
|
return InnerContent(
|
|
@@ -209,7 +243,7 @@ def create_inner_content(name: str, content_type: ContentType, value: Any, doc_l
|
|
|
209
243
|
content=value,
|
|
210
244
|
doc_link=doc_link,
|
|
211
245
|
tooltip=config_data.tooltip or '',
|
|
212
|
-
unsound=
|
|
246
|
+
unsound=config_data.unsound
|
|
213
247
|
)
|
|
214
248
|
|
|
215
249
|
|
|
@@ -260,35 +294,23 @@ def collect_attribute_configs(metadata: dict) -> list[CardContent]:
|
|
|
260
294
|
)
|
|
261
295
|
continue
|
|
262
296
|
|
|
263
|
-
# Find or create the subsection (if it
|
|
297
|
+
# Find or create the subsection (if it doesn't exist)
|
|
264
298
|
if isinstance(attr_value, list):
|
|
265
|
-
current_section: Any = main_section
|
|
266
299
|
content_type = ContentType.SIMPLE
|
|
300
|
+
if attr_name in ARG_LIST_ATTRIBUTES:
|
|
301
|
+
attr_value = split_and_sort_arg_list_value(attr_value)
|
|
267
302
|
|
|
268
303
|
elif isinstance(attr_value, dict):
|
|
269
|
-
current_section = main_section
|
|
270
304
|
content_type = ContentType.COMPLEX
|
|
271
305
|
attr_value = [
|
|
272
306
|
create_inner_content(key, ContentType.FLAG, value, doc_link, config_data)
|
|
273
307
|
for key, value in attr_value.items()
|
|
274
308
|
]
|
|
275
309
|
else:
|
|
276
|
-
# this attribute is a value attribute without a subsection, it will be placed in flags.
|
|
277
|
-
subsection_key = config_data.subsection.lower() if config_data.subsection else 'flags'
|
|
278
|
-
current_section = next((section for section in main_section.content
|
|
279
|
-
if section.inner_title == subsection_key), None)
|
|
280
|
-
if current_section is None:
|
|
281
|
-
current_section = InnerContent(
|
|
282
|
-
inner_title=subsection_key,
|
|
283
|
-
content_type=ContentType.COMPLEX.value,
|
|
284
|
-
content=[]
|
|
285
|
-
)
|
|
286
|
-
main_section.content.append(current_section)
|
|
287
|
-
|
|
288
310
|
content_type = ContentType.FLAG
|
|
289
311
|
|
|
290
312
|
# Update the current section with attribute details
|
|
291
|
-
|
|
313
|
+
main_section.content.append(
|
|
292
314
|
create_inner_content(attr_name, content_type, attr_value, doc_link, config_data)
|
|
293
315
|
)
|
|
294
316
|
|
|
@@ -300,37 +322,23 @@ def collect_run_config_from_metadata(attributes_configs: list[CardContent], meta
|
|
|
300
322
|
Adding CLI and Git configuration from metadata
|
|
301
323
|
"""
|
|
302
324
|
|
|
303
|
-
# Define a mapping of metadata attributes to their keys in general_section
|
|
304
|
-
metadata_mappings = {
|
|
305
|
-
'CLI Version': metadata.get('CLI_version'),
|
|
306
|
-
'Verify': metadata.get('conf', {}).get('verify'),
|
|
307
|
-
}
|
|
308
|
-
|
|
309
325
|
general_section = create_or_get_card_content(attributes_configs, MainSection.GENERAL.value.lower())
|
|
310
326
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
content=value,
|
|
318
|
-
))
|
|
327
|
+
if cli_version := metadata.get('CLI_version'):
|
|
328
|
+
general_section.content.append(InnerContent(
|
|
329
|
+
inner_title='CLI Version',
|
|
330
|
+
content_type=ContentType.FLAG.value,
|
|
331
|
+
content=cli_version,
|
|
332
|
+
))
|
|
319
333
|
|
|
320
|
-
# Adding GIT configuration from metadata only if attributes are found
|
|
321
|
-
git_content = []
|
|
322
334
|
for attr in GIT_ATTRIBUTES:
|
|
323
335
|
if attr_value := metadata.get(attr):
|
|
324
|
-
|
|
336
|
+
general_section.content.append(InnerContent(
|
|
325
337
|
inner_title=attr,
|
|
326
338
|
content_type=ContentType.FLAG.value,
|
|
327
339
|
content=attr_value,
|
|
328
340
|
))
|
|
329
341
|
|
|
330
|
-
if git_content:
|
|
331
|
-
git_section = create_or_get_card_content(attributes_configs, MainSection.GIT.value.lower())
|
|
332
|
-
git_section.content = git_content
|
|
333
|
-
|
|
334
342
|
return attributes_configs
|
|
335
343
|
|
|
336
344
|
|
|
@@ -338,14 +346,17 @@ def sort_configuration_layout(data: list[CardContent]) -> list[CardContent]:
|
|
|
338
346
|
"""
|
|
339
347
|
Sorts a configuration layout:
|
|
340
348
|
- Top-level sorted by 'card_title'
|
|
341
|
-
- Nested content sorted by 'inner_title', with '
|
|
349
|
+
- Nested content sorted by 'inner_title', with 'verify' first
|
|
342
350
|
"""
|
|
343
351
|
priority = {
|
|
344
|
-
|
|
352
|
+
# Priorities for top-level cards
|
|
345
353
|
"general": 0,
|
|
354
|
+
"files": 1,
|
|
355
|
+
"options": 2,
|
|
356
|
+
# Top level items inside their respective cards
|
|
357
|
+
"verify": 0,
|
|
346
358
|
"solc": 0,
|
|
347
|
-
"CLI Version":
|
|
348
|
-
"flags": 2
|
|
359
|
+
"CLI Version": 0
|
|
349
360
|
}
|
|
350
361
|
|
|
351
362
|
def inner_sort_key(item: Any) -> Any:
|
|
@@ -31,6 +31,7 @@ import CertoraProver.certoraContextAttributes as Attrs
|
|
|
31
31
|
|
|
32
32
|
metadata_logger = logging.getLogger("metadata")
|
|
33
33
|
|
|
34
|
+
|
|
34
35
|
def collect_args_with_jar_flags() -> List[str]:
|
|
35
36
|
return_array = []
|
|
36
37
|
for attr in Attrs.get_attribute_class().attribute_list():
|
|
@@ -70,10 +71,18 @@ class RunMetaData:
|
|
|
70
71
|
dirty -- true iff the git repository has changes (git diff is not empty)
|
|
71
72
|
main_spec -- the relative path to the main spec file that should be displayed by default at the web report
|
|
72
73
|
conf_path -- the relative path form the cwd_relative to the configuration file
|
|
74
|
+
group_id -- optional identifier for grouping this run
|
|
75
|
+
java_version -- version of Java used during the run, if available
|
|
76
|
+
python_version -- version of Python running the process
|
|
77
|
+
certora_ci_client -- name of the CI client if available, derived from environment
|
|
78
|
+
timestamp -- UTC timestamp when the run metadata was generated
|
|
79
|
+
CLI_package_name -- name of the CLI package
|
|
80
|
+
CLI_version -- version of the CLI
|
|
81
|
+
jar_flag_info -- CLI attributes which are jarFlags
|
|
73
82
|
"""
|
|
74
83
|
def __init__(self, raw_args: List[str], conf: Dict[str, Any], origin: str, revision: str,
|
|
75
84
|
branch: str, cwd_relative: Path, dirty: bool, main_spec: Optional[str],
|
|
76
|
-
conf_path: Optional[Path], group_id: Optional[str]):
|
|
85
|
+
conf_path: Optional[Path], group_id: Optional[str], java_version: str):
|
|
77
86
|
self.raw_args = raw_args
|
|
78
87
|
self.conf = conf
|
|
79
88
|
self.origin = origin
|
|
@@ -85,6 +94,7 @@ class RunMetaData:
|
|
|
85
94
|
self.conf_path = conf_path
|
|
86
95
|
self.group_id = group_id
|
|
87
96
|
self.python_version = ".".join(str(x) for x in sys.version_info[:3])
|
|
97
|
+
self.java_version = java_version
|
|
88
98
|
self.certora_ci_client = Utils.get_certora_ci_name()
|
|
89
99
|
self.timestamp = str(datetime.now(timezone.utc).timestamp())
|
|
90
100
|
_, self.CLI_package_name, self.CLI_version = Utils.get_package_and_version()
|
|
@@ -105,6 +115,7 @@ class RunMetaData:
|
|
|
105
115
|
f" conf_path: {self.conf_path}\n"
|
|
106
116
|
f" group_id: {self.group_id}\n"
|
|
107
117
|
f" python_version: {self.python_version}\n"
|
|
118
|
+
f" java_version: {self.java_version}\n"
|
|
108
119
|
f" CertoraCI client: {self.certora_ci_client}\n"
|
|
109
120
|
f" jar_flag_info: {self.jar_flag_info}\n"
|
|
110
121
|
)
|
|
@@ -182,7 +193,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
|
|
|
182
193
|
dirty=True,
|
|
183
194
|
main_spec=None,
|
|
184
195
|
conf_path=None,
|
|
185
|
-
group_id=None
|
|
196
|
+
group_id=None,
|
|
197
|
+
java_version=context.java_version)
|
|
186
198
|
|
|
187
199
|
# collect information about current git snapshot
|
|
188
200
|
cwd_abs = wd.absolute()
|
|
@@ -210,7 +222,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
|
|
|
210
222
|
dirty=True,
|
|
211
223
|
main_spec=get_main_spec(context),
|
|
212
224
|
conf_path=conf_path,
|
|
213
|
-
group_id=context.group_id
|
|
225
|
+
group_id=context.group_id,
|
|
226
|
+
java_version=context.java_version)
|
|
214
227
|
|
|
215
228
|
try:
|
|
216
229
|
sha_out = subprocess.run(['git', 'rev-parse', 'HEAD'], cwd=wd,
|
|
@@ -245,7 +258,8 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
|
|
|
245
258
|
dirty=dirty,
|
|
246
259
|
main_spec=get_main_spec(context),
|
|
247
260
|
conf_path=conf_path,
|
|
248
|
-
group_id=context.group_id
|
|
261
|
+
group_id=context.group_id,
|
|
262
|
+
java_version=context.java_version)
|
|
249
263
|
|
|
250
264
|
metadata_logger.debug(f' collected data:\n{str(data)}')
|
|
251
265
|
|
|
@@ -263,4 +277,5 @@ def collect_run_metadata(wd: Path, raw_args: List[str], context: CertoraContext)
|
|
|
263
277
|
dirty=True,
|
|
264
278
|
main_spec=get_main_spec(context),
|
|
265
279
|
conf_path=conf_path,
|
|
266
|
-
group_id=context.group_id
|
|
280
|
+
group_id=context.group_id,
|
|
281
|
+
java_version=context.java_version)
|
|
@@ -17,6 +17,7 @@ import json5
|
|
|
17
17
|
import logging
|
|
18
18
|
from pathlib import Path
|
|
19
19
|
from typing import Dict, Any
|
|
20
|
+
from collections import OrderedDict
|
|
20
21
|
|
|
21
22
|
import CertoraProver.certoraContext as Ctx
|
|
22
23
|
import CertoraProver.certoraContextAttributes as Attrs
|
|
@@ -75,11 +76,11 @@ def read_from_conf_file(context: CertoraContext) -> None:
|
|
|
75
76
|
|
|
76
77
|
try:
|
|
77
78
|
with conf_file_path.open() as conf_file:
|
|
78
|
-
context.conf_file_attr = json5.load(conf_file, allow_duplicate_keys=False)
|
|
79
|
+
context.conf_file_attr = json5.load(conf_file, allow_duplicate_keys=False, object_pairs_hook=OrderedDict)
|
|
79
80
|
try:
|
|
80
81
|
check_conf_content(context)
|
|
81
82
|
except Util.CertoraUserInputError as e:
|
|
82
|
-
raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {
|
|
83
|
+
raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {e}") from None
|
|
83
84
|
context.conf_file = str(conf_file_path)
|
|
84
85
|
except FileNotFoundError:
|
|
85
86
|
raise Util.CertoraUserInputError(f"read_from_conf_file: {conf_file_path}: not found") from None
|
|
@@ -96,22 +97,24 @@ def handle_override_base_config(context: CertoraContext) -> None:
|
|
|
96
97
|
"""
|
|
97
98
|
|
|
98
99
|
if context.override_base_config:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
override_base_config_attrs = json5.load(conf_file, allow_duplicate_keys=False
|
|
100
|
+
try:
|
|
101
|
+
with Path(context.override_base_config).open() as conf_file:
|
|
102
|
+
override_base_config_attrs = json5.load(conf_file, allow_duplicate_keys=False,
|
|
103
|
+
object_pairs_hook=OrderedDict)
|
|
102
104
|
context.conf_file_attr = {**override_base_config_attrs, **context.conf_file_attr}
|
|
103
105
|
|
|
104
106
|
if 'override_base_config' in override_base_config_attrs:
|
|
105
107
|
raise Util.CertoraUserInputError("base config cannot include 'override_base_config'")
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
except Exception as e:
|
|
109
|
+
raise Util.CertoraUserInputError(f"Cannot load base config: {context.override_base_config}\n{e}")
|
|
110
|
+
|
|
111
|
+
for attr in override_base_config_attrs:
|
|
112
|
+
if hasattr(context, attr):
|
|
113
|
+
value = getattr(context, attr, False)
|
|
114
|
+
if not value:
|
|
115
|
+
setattr(context, attr, override_base_config_attrs.get(attr))
|
|
116
|
+
else:
|
|
117
|
+
raise Util.CertoraUserInputError(f"{attr} appears in the base conf file {context.override_base_config} but is not a known attribute.")
|
|
115
118
|
|
|
116
119
|
|
|
117
120
|
def check_conf_content(context: CertoraContext) -> None:
|