certora-cli-alpha-master 20250520.21.30.33372__py3-none-any.whl → 20250521.12.50.829403__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/certoraRun.py CHANGED
@@ -17,10 +17,8 @@
17
17
  import sys
18
18
  import time
19
19
  import logging
20
- from dataclasses import dataclass
21
20
  from typing import List, Optional, Type
22
21
  from pathlib import Path
23
- from rich.console import Console
24
22
 
25
23
  scripts_dir_path = Path(__file__).parent.resolve() # containing directory
26
24
  sys.path.insert(0, str(scripts_dir_path))
@@ -28,9 +26,6 @@ from Shared import certoraUtils as Util
28
26
 
29
27
  from CertoraProver.certoraCloudIO import CloudVerification, validate_version_and_branch
30
28
 
31
- from CertoraProver.certoraCollectRunMetadata import collect_run_metadata
32
- from CertoraProver.certoraCollectConfigurationLayout import collect_configuration_layout
33
- from Shared.certoraLogging import LoggingManager
34
29
  from CertoraProver.certoraBuild import build
35
30
  from CertoraProver.certoraBuildRust import set_rust_build_directory
36
31
  import CertoraProver.certoraContext as Ctx
@@ -38,29 +33,25 @@ import CertoraProver.certoraContext as Ctx
38
33
  from CertoraProver import certoraContextValidator as Cv
39
34
  import CertoraProver.certoraContextAttributes as Attrs
40
35
  from Shared import certoraAttrUtil as AttrUtil
36
+ from Shared.proverCommon import (
37
+ build_context,
38
+ collect_and_dump_metadata,
39
+ collect_and_dump_config_layout,
40
+ ensure_version_compatibility,
41
+ run_local,
42
+ run_remote,
43
+ CertoraRunResult,
44
+ handle_exit,
45
+ catch_exits
46
+ )
41
47
  import CertoraProver.splitRules as splitRules
42
48
 
43
49
  BUILD_SCRIPT_PATH = Path("CertoraProver/certoraBuild.py")
44
- VIOLATIONS_EXIT_CODE = 100
45
50
 
46
51
  # logger for issues regarding the general run flow.
47
52
  # Also serves as the default logger for errors originating from unexpected places.
48
53
  run_logger = logging.getLogger("run")
49
54
 
50
-
51
- @dataclass
52
- class CertoraRunResult:
53
- link: Optional[str] # Path to emv_dir if running locally, or the link to the job status page
54
- is_local_link: bool
55
- src_dir: Path
56
- rule_report_link: Optional[str]
57
-
58
-
59
- class CertoraFoundViolations(Exception):
60
- def __init__(self, message: str, results: Optional[CertoraRunResult] = None) -> None:
61
- super().__init__(message)
62
- self.results = results
63
-
64
55
  def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]] = None,
65
56
  prover_cmd: Optional[str] = None) -> Optional[CertoraRunResult]:
66
57
  """
@@ -70,54 +61,22 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
70
61
  2. Run the necessary steps (type checking/ build/ cloud verification/ local verification)
71
62
 
72
63
  """
73
-
74
- non_str_els = [x for x in args if not isinstance(x, str)]
75
- if non_str_els:
76
- print(f"args for run_certora that are not strings: {non_str_els}")
77
- exit(1)
78
-
79
- # If we are not in debug mode, we do not want to print the traceback in case of exceptions.
80
- if '--debug' not in args: # We check manually, because we want no traceback in argument parsing exceptions
81
- sys.tracebacklimit = 0
82
-
83
- # creating the default internal dir, files may be copied to user defined build directory after
84
- # parsing the input
85
-
86
- if not ('--help' in args or '--version' in args):
87
- Util.reset_certora_internal_dir()
88
- Util.safe_create_dir(Util.get_build_dir())
89
- logging_manager = LoggingManager()
90
-
91
64
  if not attrs_class:
92
65
  attrs_class = Attrs.detect_application_class(args)
93
- Attrs.set_attribute_class(attrs_class)
94
66
 
95
- Ctx.handle_flags_in_args(args)
96
- context = Ctx.get_args(args) # Parse arguments
67
+ context, logging_manager = build_context(args, attrs_class)
68
+
97
69
  if prover_cmd:
98
70
  context.prover_cmd = prover_cmd
99
- assert logging_manager, "logging manager was not set"
100
- logging_manager.set_log_level_and_format(is_quiet=Ctx.is_minimal_cli_output(context),
101
- debug=context.debug,
102
- debug_topics=context.debug_topics,
103
- show_debug_topics=context.show_debug_topics)
104
71
 
105
72
  timings = {}
106
73
  exit_code = 0 # The exit code of the script. 0 means success, any other number is an error.
107
74
  return_value = None
108
75
 
109
- metadata = (
110
- collect_run_metadata(wd=Path.cwd(), raw_args=sys.argv, context=context))
111
-
112
- if context.test == str(Util.TestValue.CHECK_METADATA):
113
- raise Util.TestResultsReady(metadata)
114
- metadata.dump()
115
-
116
- configuration_layout = collect_configuration_layout()
117
-
118
- if context.test == str(Util.TestValue.CHECK_CONFIG_LAYOUT):
119
- raise Util.TestResultsReady(configuration_layout)
120
- configuration_layout.dump()
76
+ # Collect and validate metadata
77
+ collect_and_dump_metadata(context)
78
+ # Collect and dump configuration layout
79
+ collect_and_dump_config_layout(context)
121
80
 
122
81
  if Attrs.is_evm_app() and context.split_rules:
123
82
  context.build_only = True
@@ -165,14 +124,8 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
165
124
  exit_code = 1
166
125
  else:
167
126
 
168
- if not context.local and not context.build_only and not context.compilation_steps_only:
169
- """
170
- Before running the local type checker, we see if the current package version is compatible with
171
- the latest. We check it before running the local type checker, because local type checking
172
- errors could be simply a result of syntax introduced in the newest version.
173
- The line below will raise an exception if the local version is incompatible.
174
- """
175
- validate_version_and_branch(context)
127
+ # Version validation
128
+ ensure_version_compatibility(context)
176
129
 
177
130
  # When a TAC file is provided, no build arguments will be processed
178
131
  if not context.is_tac:
@@ -180,81 +133,54 @@ def run_certora(args: List[str], attrs_class: Optional[Type[AttrUtil.Attributes]
180
133
  build_start = time.perf_counter()
181
134
 
182
135
  # If we are not in CI, we also check the spec for Syntax errors.
183
- build(context, ignore_spec_syntax_check=False)
136
+ build(context)
184
137
  build_end = time.perf_counter()
185
138
 
186
139
  timings["buildTime"] = round(build_end - build_start, 4)
187
140
  if context.test == str(Util.TestValue.AFTER_BUILD):
188
141
  raise Util.TestResultsReady(context)
189
142
 
190
- if not context.build_only and exit_code == 0:
191
- # either we skipped building (TAC MODE) or build succeeded
192
- if context.local:
193
- compare_with_expected_file = Path(context.expected_file).exists()
194
-
195
- check_cmd = Ctx.get_local_run_cmd(context)
196
- check_cmd_string = " ".join(check_cmd)
197
-
198
- # In local mode, this is reserved for Certora devs, so let the script print it
199
- print(f"Verifier run command:\n {check_cmd_string}", flush=True)
200
- run_result = \
201
- Util.run_jar_cmd(check_cmd, compare_with_expected_file,
202
- logger_topic="verification", print_output=True)
203
- emv_dir = latest_emv_dir()
204
- return_value = CertoraRunResult(str(emv_dir) if emv_dir else None, True,
205
- Util.get_certora_sources_dir(), None)
206
- if run_result != 0:
207
- exit_code = run_result
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.")
208
172
  else:
209
- Util.print_completion_message("Finished running verifier:")
210
- print(f"\t{check_cmd_string}")
211
- if compare_with_expected_file:
212
- print("Comparing tool output to the expected output:")
213
- output_path = (context.tool_output if context.tool_output else
214
- ('tmpOutput.json' if emv_dir is None
215
- else str(emv_dir / 'Reports/output.json')))
216
- result = Util.check_results_from_file(output_path, context.expected_file)
217
- if not result:
218
- exit_code = 1
219
- else: # Remote run
220
- # Syntax checking and typechecking
221
- if Cv.mode_has_spec_file(context):
222
- if context.disable_local_typechecking:
223
- run_logger.warning(
224
- "Local checks of CVL specification files disabled. It is recommended to enable "
225
- "the checks.")
226
- else:
227
- typechecking_start = time.perf_counter()
228
- Ctx.run_local_spec_check(True, context)
229
- typechecking_end = time.perf_counter()
230
- timings['typecheckingTime'] = round(typechecking_end - typechecking_start, 4)
231
-
232
- if exit_code == 0:
233
- if context.compilation_steps_only:
234
- # Give a non-None value for the overall result, but with links set to None
235
- return_value = CertoraRunResult(None, False, Util.get_certora_sources_dir(), None)
236
- else:
237
- # typechecking either succeeded or skipped
238
-
239
- context.key = Cv.validate_certora_key()
240
- cloud_verifier = CloudVerification(context, timings)
241
-
242
- # Wrap strings with space with ' so it can be copied and pasted to shell
243
- pretty_args = [f"'{arg}'" if ' ' in arg else arg for arg in args]
244
- cl_args = ' '.join(pretty_args)
245
-
246
- logging_manager.remove_debug_logger()
247
- if not cloud_verifier.cli_verify_and_report(cl_args, context.wait_for_results):
248
- exit_code = VIOLATIONS_EXIT_CODE
249
- if cloud_verifier.statusUrl:
250
- return_value = CertoraRunResult(cloud_verifier.statusUrl, False,
251
- Util.get_certora_sources_dir(), cloud_verifier.reportUrl)
252
-
253
- if exit_code == VIOLATIONS_EXIT_CODE:
254
- raise CertoraFoundViolations("violations were found", return_value)
255
- if exit_code != 0:
256
- raise Util.CertoraUserInputError(f"run_certora failed (code {exit_code})")
257
- return return_value
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)
181
+
182
+ # Handle exit codes and return
183
+ return handle_exit(exit_code, return_value)
258
184
 
259
185
 
260
186
  def latest_emv_dir() -> Optional[Path]:
@@ -277,43 +203,13 @@ def latest_emv_dir() -> Optional[Path]:
277
203
  return max
278
204
 
279
205
 
206
+ @catch_exits
280
207
  def entry_point() -> None:
281
208
  """
282
209
  This function is the entry point of the certora_cli customer-facing package, as well as this script.
283
210
  It is important this function gets no arguments!
284
211
  """
285
- try:
286
- run_certora(sys.argv[1:], prover_cmd=sys.argv[0])
287
- sys.exit(0)
288
- except KeyboardInterrupt:
289
- Console().print("[bold red]\nInterrupted by user")
290
- sys.exit(1)
291
- except Util.TestResultsReady:
292
- print("reached checkpoint")
293
- sys.exit(0)
294
- except CertoraFoundViolations as e:
295
- try:
296
- assert e.results
297
- print(f"report url: {e.results.rule_report_link}")
298
- except Exception:
299
- pass
300
- Console().print("[bold red]\nViolations were found\n")
301
- sys.exit(1)
302
- except Util.CertoraUserInputError as e:
303
- if e.orig:
304
- print(f"\n{str(e.orig).strip()}")
305
- if e.more_info:
306
- print(f"\n{e.more_info.strip()}")
307
- Console().print(f"[bold red]\n{e}\n")
308
- sys.exit(1)
309
-
310
- except Util.ExitException as e:
311
- Console().print(f"[bold red]{e}")
312
- sys.exit(e.exit_code)
313
-
314
- except Exception as e:
315
- Console().print(f"[bold red]{e}")
316
- sys.exit(1)
212
+ run_certora(sys.argv[1:], prover_cmd=sys.argv[0])
317
213
 
318
214
 
319
215
  if __name__ == '__main__':
@@ -16,28 +16,27 @@
16
16
 
17
17
 
18
18
  import sys
19
- import time
20
19
  import logging
21
- from typing import List, Optional
20
+ from typing import List, Optional, Dict
22
21
  from pathlib import Path
23
- from rich.console import Console
24
22
 
25
23
  scripts_dir_path = Path(__file__).parent.resolve() # containing directory
26
24
  sys.path.insert(0, str(scripts_dir_path))
27
25
 
28
- from Shared import certoraUtils as Util
29
- from Shared.certoraLogging import LoggingManager
30
26
 
31
- from CertoraProver.certoraCloudIO import CloudVerification, validate_version_and_branch
32
- from CertoraProver.certoraCollectRunMetadata import collect_run_metadata
33
- from CertoraProver.certoraCollectConfigurationLayout import collect_configuration_layout
34
- from CertoraProver import certoraContextValidator as Cv
35
-
36
- import CertoraProver.certoraContext as Ctx
37
27
  import CertoraProver.certoraContextAttributes as Attrs
38
- from CertoraProver.certoraBuildRust import set_rust_build_directory
39
-
40
- from certoraRun import CertoraRunResult, VIOLATIONS_EXIT_CODE, CertoraFoundViolations
28
+ from Shared.rustProverCommon import build_rust_project
29
+ from Shared.proverCommon import (
30
+ build_context,
31
+ collect_and_dump_metadata,
32
+ collect_and_dump_config_layout,
33
+ ensure_version_compatibility,
34
+ run_local,
35
+ run_remote,
36
+ CertoraRunResult,
37
+ handle_exit,
38
+ catch_exits,
39
+ )
41
40
 
42
41
  # logger for issues regarding the general run flow.
43
42
  # Also serves as the default logger for errors originating from unexpected places.
@@ -51,142 +50,48 @@ def run_solana_prover(args: List[str]) -> Optional[CertoraRunResult]:
51
50
  1. Parse program arguments
52
51
  2. Run the necessary steps (build/ cloud verification/ local verification)
53
52
  """
53
+ context, logging_manager = build_context(args, Attrs.SolanaProverAttributes)
54
54
 
55
- Attrs.set_attribute_class(Attrs.SolanaProverAttributes)
56
- non_str_els = [x for x in args if not isinstance(x, str)]
57
- if non_str_els:
58
- print(f"args for run_certora that are not strings: {non_str_els}")
59
- exit(1)
60
-
61
- # If we are not in debug mode, we do not want to print the traceback in case of exceptions.
62
- if '--debug' not in args: # We check manually, because we want no traceback in argument parsing exceptions
63
- sys.tracebacklimit = 0
64
-
65
- # creating the default internal dir, files may be copied to user defined build directory after
66
- # parsing the input
67
-
68
- if not ('--help' in args or '--version' in args):
69
- Util.reset_certora_internal_dir()
70
- Util.safe_create_dir(Util.get_build_dir())
71
- logging_manager = LoggingManager()
72
-
73
- Ctx.handle_flags_in_args(args)
74
- context = Ctx.get_args(args) # Parse arguments
75
- logging_manager.set_log_level_and_format(is_quiet=Ctx.is_minimal_cli_output(context),
76
- debug=context.debug,
77
- debug_topics=context.debug_topics,
78
- show_debug_topics=context.show_debug_topics)
79
-
80
- timings = {}
55
+ timings: Dict[str, float] = {}
81
56
  exit_code = 0 # The exit code of the script. 0 means success, any other number is an error.
82
57
  return_value = None
83
58
 
84
- metadata = (
85
- collect_run_metadata(wd=Path.cwd(), raw_args=sys.argv, context=context))
59
+ # Collect and validate metadata
60
+ collect_and_dump_metadata(context)
61
+ collect_and_dump_config_layout(context)
86
62
 
87
- if context.test == str(Util.TestValue.CHECK_METADATA):
88
- raise Util.TestResultsReady(metadata)
89
- metadata.dump()
63
+ # Version validation
64
+ ensure_version_compatibility(context)
90
65
 
91
- configuration_layout = collect_configuration_layout()
66
+ # Build Solana - If input file is .so or .o file, we skip building part
67
+ build_rust_project(context, timings)
92
68
 
93
- if context.test == str(Util.TestValue.CHECK_CONFIG_LAYOUT):
94
- raise Util.TestResultsReady(configuration_layout)
95
- configuration_layout.dump()
69
+ if context.build_only:
70
+ return return_value
96
71
 
97
- if not context.local and not context.build_only and not context.compilation_steps_only:
98
- """
99
- The line below will raise an exception if the local version is incompatible.
100
- """
101
- validate_version_and_branch(context)
72
+ if context.local:
73
+ additional_commands = []
74
+ if context.solana_summaries:
75
+ additional_commands += ["-solanaSummaries", ",".join(context.solana_summaries)]
76
+ if context.solana_inlining:
77
+ additional_commands += ["-solanaInlining", ",".join(context.solana_inlining)]
78
+ # Run local verification
79
+ exit_code = run_local(context, timings, additional_commands)
80
+ else:
81
+ logging_manager.remove_debug_logger()
82
+ exit_code, return_value = run_remote(context, args, timings)
102
83
 
103
- # Build Solana - If input file is .so or .o file, we skip building part
104
- run_logger.debug("Build Solana target")
105
- build_start = time.perf_counter()
106
-
107
- set_rust_build_directory(context)
108
- build_end = time.perf_counter()
109
- timings["buildTime"] = round(build_end - build_start, 4)
110
- if context.test == str(Util.TestValue.AFTER_BUILD):
111
- raise Util.TestResultsReady(context)
112
-
113
- if not context.build_only and exit_code == 0:
114
-
115
- if context.local:
116
- check_cmd = Ctx.get_local_run_cmd(context)
117
- if context.solana_summaries:
118
- check_cmd.append("-solanaSummaries")
119
- check_cmd.append(','.join(context.solana_summaries))
120
- if context.solana_inlining:
121
- check_cmd.append("-solanaInlining")
122
- check_cmd.append(','.join(context.solana_inlining))
123
-
124
- print(f"Verifier run command:\n {check_cmd}", flush=True)
125
-
126
- compare_with_tool_output = False
127
- run_result = Util.run_jar_cmd(check_cmd, compare_with_tool_output, logger_topic="verification",
128
- print_output=True)
129
-
130
- if run_result != 0:
131
- exit_code = 1
132
- else:
133
- Util.print_completion_message("Finished running verifier:")
134
- print(f"\t{check_cmd}")
135
- else:
136
- if context.compilation_steps_only:
137
- # Give a non-None value for the overall result, but with links set to None
138
- return_value = CertoraRunResult(None, False, Util.get_certora_sources_dir(), None)
139
- else:
140
- context.key = Cv.validate_certora_key()
141
- cloud_verifier = CloudVerification(context, timings)
142
-
143
- # Wrap strings with space with ' so it can be copied and pasted to shell
144
- pretty_args = [f"'{arg}'" if ' ' in arg else arg for arg in args]
145
- cl_args = ' '.join(pretty_args)
146
-
147
- logging_manager.remove_debug_logger()
148
- if not cloud_verifier.cli_verify_and_report(cl_args, context.wait_for_results):
149
- exit_code = VIOLATIONS_EXIT_CODE
150
- if cloud_verifier.statusUrl:
151
- return_value = CertoraRunResult(cloud_verifier.statusUrl, False,
152
- Util.get_certora_sources_dir(), cloud_verifier.reportUrl)
153
-
154
- if exit_code == VIOLATIONS_EXIT_CODE:
155
- raise CertoraFoundViolations("violations were found", return_value)
156
- if exit_code != 0:
157
- raise Util.CertoraUserInputError(f"run_certora failed (code {exit_code})")
158
- return return_value
84
+ # Handle exit codes and return
85
+ return handle_exit(exit_code, return_value)
159
86
 
160
87
 
88
+ @catch_exits
161
89
  def entry_point() -> None:
162
90
  """
163
91
  This function is the entry point of the certora_cli customer-facing package, as well as this script.
164
92
  It is important this function gets no arguments!
165
93
  """
166
- try:
167
- run_solana_prover(sys.argv[1:])
168
- sys.exit(0)
169
- except KeyboardInterrupt:
170
- Console().print("[bold red]\nInterrupted by user")
171
- sys.exit(1)
172
- except CertoraFoundViolations as e:
173
- try:
174
- if e.results and e.results.rule_report_link:
175
- print(f"report url: {e.results.rule_report_link}")
176
- except Exception:
177
- pass
178
- Console().print("[bold red]\nViolations were found\n")
179
- sys.exit(1)
180
- except Util.CertoraUserInputError as e:
181
- if e.orig:
182
- print(f"\n{str(e.orig).strip()}")
183
- if e.more_info:
184
- print(f"\n{e.more_info.strip()}")
185
- Console().print(f"[bold red]\n{e}\n")
186
- sys.exit(1)
187
- except Exception as e:
188
- Console().print(f"[bold red]{e}")
189
- sys.exit(1)
94
+ run_solana_prover(sys.argv[1:])
190
95
 
191
96
 
192
97
  if __name__ == '__main__':