certora-cli-beta-mirror 7.28.0__py3-none-any.whl → 8.5.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 +10 -3
- certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +51 -16
- certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +3 -0
- certora_cli/CertoraProver/castingInstrumenter.py +192 -0
- certora_cli/CertoraProver/certoraApp.py +52 -0
- certora_cli/CertoraProver/certoraBuild.py +694 -207
- certora_cli/CertoraProver/certoraBuildCacheManager.py +21 -17
- certora_cli/CertoraProver/certoraBuildDataClasses.py +8 -2
- certora_cli/CertoraProver/certoraBuildRust.py +88 -54
- certora_cli/CertoraProver/certoraBuildSui.py +112 -0
- certora_cli/CertoraProver/certoraCloudIO.py +97 -96
- certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +230 -84
- certora_cli/CertoraProver/certoraCollectRunMetadata.py +52 -6
- certora_cli/CertoraProver/certoraCompilerParameters.py +11 -0
- certora_cli/CertoraProver/certoraConfigIO.py +43 -35
- certora_cli/CertoraProver/certoraContext.py +128 -54
- certora_cli/CertoraProver/certoraContextAttributes.py +415 -234
- certora_cli/CertoraProver/certoraContextValidator.py +152 -105
- certora_cli/CertoraProver/certoraContractFuncs.py +34 -1
- certora_cli/CertoraProver/certoraParseBuildScript.py +8 -10
- certora_cli/CertoraProver/certoraType.py +10 -1
- certora_cli/CertoraProver/certoraVerifyGenerator.py +22 -4
- certora_cli/CertoraProver/erc7201.py +45 -0
- certora_cli/CertoraProver/splitRules.py +23 -18
- certora_cli/CertoraProver/storageExtension.py +351 -0
- certora_cli/EquivalenceCheck/Eq_default.conf +0 -1
- certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -1
- certora_cli/EquivalenceCheck/equivCheck.py +2 -1
- certora_cli/Mutate/mutateApp.py +41 -22
- certora_cli/Mutate/mutateAttributes.py +11 -0
- certora_cli/Mutate/mutateValidate.py +42 -2
- certora_cli/Shared/certoraAttrUtil.py +21 -5
- certora_cli/Shared/certoraUtils.py +180 -60
- certora_cli/Shared/certoraValidateFuncs.py +68 -26
- certora_cli/Shared/proverCommon.py +308 -0
- certora_cli/certoraCVLFormatter.py +76 -0
- certora_cli/certoraConcord.py +39 -0
- certora_cli/certoraEVMProver.py +4 -3
- certora_cli/certoraRanger.py +39 -0
- certora_cli/certoraRun.py +83 -223
- certora_cli/certoraSolanaProver.py +40 -128
- certora_cli/certoraSorobanProver.py +59 -4
- certora_cli/certoraSuiProver.py +93 -0
- certora_cli_beta_mirror-8.5.0.dist-info/LICENSE +15 -0
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/METADATA +21 -5
- certora_cli_beta_mirror-8.5.0.dist-info/RECORD +81 -0
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/WHEEL +1 -1
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/entry_points.txt +3 -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.28.0.dist-info/LICENSE +0 -22
- certora_cli_beta_mirror-7.28.0.dist-info/RECORD +0 -70
- {certora_cli_beta_mirror-7.28.0.dist-info → certora_cli_beta_mirror-8.5.0.dist-info}/top_level.txt +0 -0
|
@@ -13,13 +13,12 @@
|
|
|
13
13
|
# You should have received a copy of the GNU General Public License
|
|
14
14
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
15
|
|
|
16
|
-
import json5
|
|
17
16
|
import logging
|
|
18
17
|
from pathlib import Path
|
|
19
|
-
from typing import Dict, Any
|
|
18
|
+
from typing import Dict, Any, cast
|
|
20
19
|
|
|
21
20
|
import CertoraProver.certoraContext as Ctx
|
|
22
|
-
import CertoraProver.
|
|
21
|
+
import CertoraProver.certoraApp as App
|
|
23
22
|
from CertoraProver.certoraContextClass import CertoraContext
|
|
24
23
|
from Shared import certoraUtils as Util
|
|
25
24
|
|
|
@@ -46,11 +45,12 @@ def current_conf_to_file(context: CertoraContext) -> Dict[str, Any]:
|
|
|
46
45
|
4. Parsing the .conf file is simpler, as we can ignore the null case
|
|
47
46
|
"""
|
|
48
47
|
def input_arg_with_value(k: Any, v: Any) -> Any:
|
|
49
|
-
all_conf_names =
|
|
48
|
+
all_conf_names = context.app.attr_class.all_conf_names()
|
|
50
49
|
return v is not None and v is not False and k in all_conf_names
|
|
51
50
|
|
|
52
51
|
context_to_save = {k: v for k, v in vars(context).items() if input_arg_with_value(k, v)}
|
|
53
|
-
|
|
52
|
+
context_to_save = cast(Dict[str, Any], Util.convert_str_ints(context_to_save))
|
|
53
|
+
all_conf_names = context.app.attr_class.all_conf_names()
|
|
54
54
|
context_to_save = dict(sorted(context_to_save.items(), key=lambda x: all_conf_names.index(x[0])))
|
|
55
55
|
context_to_save.pop('build_dir', None) # build dir should not be saved, each run should define its own build_dir
|
|
56
56
|
context_to_save.pop('mutation_test_id', None) # mutation_test_id should be recreated for every run
|
|
@@ -75,11 +75,11 @@ def read_from_conf_file(context: CertoraContext) -> None:
|
|
|
75
75
|
|
|
76
76
|
try:
|
|
77
77
|
with conf_file_path.open() as conf_file:
|
|
78
|
-
conf_file_attr =
|
|
78
|
+
context.conf_file_attr = Util.read_conf_file(conf_file)
|
|
79
79
|
try:
|
|
80
|
-
check_conf_content(
|
|
80
|
+
check_conf_content(context)
|
|
81
81
|
except Util.CertoraUserInputError as e:
|
|
82
|
-
raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {
|
|
82
|
+
raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {e}") from None
|
|
83
83
|
context.conf_file = str(conf_file_path)
|
|
84
84
|
except FileNotFoundError:
|
|
85
85
|
raise Util.CertoraUserInputError(f"read_from_conf_file: {conf_file_path}: not found") from None
|
|
@@ -96,24 +96,34 @@ def handle_override_base_config(context: CertoraContext) -> None:
|
|
|
96
96
|
"""
|
|
97
97
|
|
|
98
98
|
if context.override_base_config:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
override_base_config_attrs =
|
|
99
|
+
try:
|
|
100
|
+
with (Path(context.override_base_config).open() as conf_file):
|
|
101
|
+
override_base_config_attrs = Util.read_conf_file(conf_file)
|
|
102
|
+
context.attrs_set_in_main_conf = context.conf_file_attr.copy()
|
|
103
|
+
context.conf_file_attr = {**override_base_config_attrs, **context.conf_file_attr}
|
|
102
104
|
|
|
103
105
|
if 'override_base_config' in override_base_config_attrs:
|
|
104
106
|
raise Util.CertoraUserInputError("base config cannot include 'override_base_config'")
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
except Exception as e:
|
|
108
|
+
raise Util.CertoraUserInputError(f"Cannot load base config: {context.override_base_config}\n{e}")
|
|
109
|
+
|
|
110
|
+
for attr in override_base_config_attrs:
|
|
111
|
+
if hasattr(context, attr):
|
|
112
|
+
value = getattr(context, attr, False)
|
|
113
|
+
if not value:
|
|
114
|
+
# if boolean attribute does not appear in main conf but is True in the base conf, set it to True
|
|
115
|
+
if override_base_config_attrs[attr] is True and attr not in context.attrs_set_in_main_conf:
|
|
116
|
+
setattr(context, attr, True)
|
|
117
|
+
continue
|
|
118
|
+
# if boolean attribute does appear in main conf and is False, do not override it
|
|
119
|
+
elif attr in context.conf_file_attr and value is False:
|
|
120
|
+
continue # skip override if a boolean attribute was explicitly set to False in the conf file
|
|
121
|
+
setattr(context, attr, override_base_config_attrs.get(attr))
|
|
122
|
+
else:
|
|
123
|
+
raise Util.CertoraUserInputError(f"{attr} appears in the base conf file {context.override_base_config} but is not a known attribute.")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def check_conf_content(context: CertoraContext) -> None:
|
|
117
127
|
"""
|
|
118
128
|
validating content read from the conf file
|
|
119
129
|
Note: a command line definition trumps the definition in the file.
|
|
@@ -122,15 +132,15 @@ def check_conf_content(conf_file_attr: Dict[str, Any], context: CertoraContext)
|
|
|
122
132
|
@param context: A namespace containing options from the command line, if any
|
|
123
133
|
"""
|
|
124
134
|
|
|
125
|
-
for option in conf_file_attr:
|
|
135
|
+
for option in context.conf_file_attr:
|
|
126
136
|
if hasattr(context, option):
|
|
127
137
|
val = getattr(context, option)
|
|
128
138
|
if val is None or val is False:
|
|
129
|
-
setattr(context, option, conf_file_attr[option])
|
|
130
|
-
elif option !=
|
|
139
|
+
setattr(context, option, context.conf_file_attr[option])
|
|
140
|
+
elif option != 'files' and val != context.conf_file_attr[option]:
|
|
131
141
|
cli_val = ' '.join(val) if isinstance(val, list) else str(val)
|
|
132
|
-
conf_val = ' '.join(conf_file_attr[option]) \
|
|
133
|
-
if isinstance(conf_file_attr[option], list) else str(conf_file_attr[option])
|
|
142
|
+
conf_val = ' '.join(context.conf_file_attr[option]) \
|
|
143
|
+
if isinstance(context.conf_file_attr[option], list) else str(context.conf_file_attr[option])
|
|
134
144
|
run_logger.warning(f"Note: attribute {option} value in CLI ({cli_val}) overrides value stored in conf"
|
|
135
145
|
f" file ({conf_val})")
|
|
136
146
|
else:
|
|
@@ -138,12 +148,10 @@ def check_conf_content(conf_file_attr: Dict[str, Any], context: CertoraContext)
|
|
|
138
148
|
|
|
139
149
|
handle_override_base_config(context)
|
|
140
150
|
|
|
141
|
-
if
|
|
151
|
+
if Ctx.is_evm_app_class(context) and not context.files and not context.project_sanity and not context.foundry:
|
|
142
152
|
raise Util.CertoraUserInputError("Mandatory 'files' attribute is missing from the configuration")
|
|
153
|
+
context.files = context.conf_file_attr.get('files')
|
|
154
|
+
if context.app == App.SorobanApp and not context.files and not context.build_script:
|
|
155
|
+
raise Util.CertoraUserInputError("'files' or 'build script' must be set for Soroban runs")
|
|
143
156
|
|
|
144
|
-
|
|
145
|
-
has_build_script = getattr(context, 'build_script', False)
|
|
146
|
-
if not has_build_script and 'files' not in conf_file_attr:
|
|
147
|
-
raise Util.CertoraUserInputError("Mandatory 'build_script' or 'files' attribute is missing from the configuration")
|
|
148
|
-
|
|
149
|
-
context.files = conf_file_attr.get('files')
|
|
157
|
+
context.files = context.conf_file_attr.get('files')
|
|
@@ -20,10 +20,12 @@ import os
|
|
|
20
20
|
import re
|
|
21
21
|
import sys
|
|
22
22
|
import logging
|
|
23
|
+
import fnmatch
|
|
24
|
+
from wcmatch import glob
|
|
23
25
|
|
|
24
26
|
|
|
25
27
|
from pathlib import Path
|
|
26
|
-
from typing import Dict, List, Optional, Any, Union
|
|
28
|
+
from typing import Dict, List, Optional, Any, Union, Type
|
|
27
29
|
from rich.console import Console
|
|
28
30
|
|
|
29
31
|
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
|
@@ -37,6 +39,8 @@ import CertoraProver.certoraContextAttributes as Attrs
|
|
|
37
39
|
|
|
38
40
|
from Shared import certoraValidateFuncs as Vf
|
|
39
41
|
from Shared import certoraAttrUtil as AttrUtil
|
|
42
|
+
import CertoraProver.certoraApp as App
|
|
43
|
+
|
|
40
44
|
|
|
41
45
|
context_logger = logging.getLogger("context")
|
|
42
46
|
|
|
@@ -53,6 +57,11 @@ def escape_string(string: str) -> str:
|
|
|
53
57
|
else:
|
|
54
58
|
return f"\"{string}\""
|
|
55
59
|
|
|
60
|
+
def is_evm_app_class(context: CertoraContext) -> bool:
|
|
61
|
+
return issubclass(context.app, App.EvmAppClass)
|
|
62
|
+
|
|
63
|
+
def is_rust_app_class(context: CertoraContext) -> bool:
|
|
64
|
+
return issubclass(context.app, App.RustAppClass)
|
|
56
65
|
|
|
57
66
|
def get_attr_value(context: CertoraContext, attr: AttrUtil.AttributeDefinition) -> Any:
|
|
58
67
|
conf_key = attr.get_conf_key()
|
|
@@ -60,18 +69,18 @@ def get_attr_value(context: CertoraContext, attr: AttrUtil.AttributeDefinition)
|
|
|
60
69
|
|
|
61
70
|
|
|
62
71
|
def collect_args_with_jar_flags(context: CertoraContext) -> List[AttrUtil.AttributeDefinition]:
|
|
63
|
-
attribute_list =
|
|
72
|
+
attribute_list = context.app.attr_class.attribute_list()
|
|
64
73
|
return [attr for attr in attribute_list if get_attr_value(context, attr) and attr.jar_flag]
|
|
65
74
|
|
|
66
75
|
|
|
67
76
|
def collect_args_build_cache_affecting(context: CertoraContext) -> List[AttrUtil.AttributeDefinition]:
|
|
68
|
-
attribute_list =
|
|
77
|
+
attribute_list = context.app.attr_class.attribute_list()
|
|
69
78
|
return [attr for attr in attribute_list if get_attr_value(context, attr) and
|
|
70
79
|
attr.affects_build_cache_key]
|
|
71
80
|
|
|
72
81
|
|
|
73
82
|
def collect_args_build_cache_disabling(context: CertoraContext) -> List[AttrUtil.AttributeDefinition]:
|
|
74
|
-
attribute_list =
|
|
83
|
+
attribute_list = context.app.attr_class.attribute_list()
|
|
75
84
|
return [attr for attr in attribute_list if get_attr_value(context, attr) and
|
|
76
85
|
attr.disables_build_cache]
|
|
77
86
|
|
|
@@ -120,18 +129,25 @@ def get_local_run_cmd(context: CertoraContext) -> List[str]:
|
|
|
120
129
|
"""
|
|
121
130
|
run_args = []
|
|
122
131
|
|
|
123
|
-
if
|
|
124
|
-
|
|
125
|
-
|
|
132
|
+
if context.app == App.SuiApp:
|
|
133
|
+
# For local runs, we want path to be relative to cwd instead of zip root.
|
|
134
|
+
move_rel_path = os.path.relpath(Path(context.move_path), os.getcwd())
|
|
135
|
+
run_args.extend(['-movePath', move_rel_path])
|
|
136
|
+
elif is_rust_app_class(context):
|
|
137
|
+
# For local runs, we want path to be relative to cwd instead of zip root.
|
|
138
|
+
rust_rel_path = os.path.relpath(Path(context.files[0]), os.getcwd())
|
|
139
|
+
run_args.append(rust_rel_path)
|
|
140
|
+
elif context.is_tac:
|
|
126
141
|
# For Rust app we assume the files holds the executable for the prover, currently we support a single file
|
|
127
142
|
try:
|
|
128
143
|
run_args.append(context.files[0])
|
|
129
144
|
except Exception:
|
|
130
145
|
raise RuntimeError("get_local_run_cmd: cannot find context.files[0]")
|
|
131
146
|
|
|
132
|
-
if
|
|
147
|
+
if is_evm_app_class(context) and context.cache is not None:
|
|
133
148
|
run_args.extend(['-cache', context.cache])
|
|
134
|
-
|
|
149
|
+
if context.app == App.ConcordApp:
|
|
150
|
+
run_args.extend(['-equivalenceCheck', 'true'])
|
|
135
151
|
jar_args = collect_jar_args(context)
|
|
136
152
|
run_args.extend(jar_args)
|
|
137
153
|
|
|
@@ -148,27 +164,37 @@ def get_local_run_cmd(context: CertoraContext) -> List[str]:
|
|
|
148
164
|
java_cmd.extend(context.java_args.strip().split(' '))
|
|
149
165
|
|
|
150
166
|
cmd = java_cmd + ["-jar", jar_path] + run_args
|
|
151
|
-
if context.test == str(Util.TestValue.LOCAL_JAR):
|
|
152
|
-
raise Util.TestResultsReady(' '.join(cmd))
|
|
153
167
|
return cmd
|
|
154
168
|
|
|
155
169
|
|
|
156
170
|
class ProverParser(AttrUtil.ContextAttributeParser):
|
|
157
|
-
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
171
|
+
def __init__(self, app: Type[App.CertoraApp], *args: Any, **kwargs: Any) -> None:
|
|
158
172
|
super().__init__(*args, **kwargs)
|
|
173
|
+
self.app = app
|
|
159
174
|
|
|
160
175
|
def format_help(self) -> str:
|
|
161
176
|
console = Console()
|
|
162
|
-
|
|
177
|
+
if self.app == App.ConcordApp:
|
|
178
|
+
console.print("\n\nConcord - Certora’s equivalence checker for smart contracts")
|
|
179
|
+
elif self.app == App.RangerApp:
|
|
180
|
+
console.print("\n\nRanger - Certora’s bounded model checker for smart contracts")
|
|
181
|
+
else:
|
|
182
|
+
console.print("\n\nThe Certora Prover - A formal verification tool for smart contracts")
|
|
163
183
|
# Using sys.stdout.write() as print() would color some of the strings here
|
|
164
184
|
sys.stdout.write(f"\n\nUsage: {sys.argv[0]} <Files> <Flags>\n\n")
|
|
165
|
-
if
|
|
185
|
+
if self.app == App.ConcordApp:
|
|
186
|
+
sys.stdout.write("Concord supports only Solidity (.sol/.yul) and configuration (.conf) files.\n"
|
|
187
|
+
"Rust and Vyper contracts are not currently supported.\n\n")
|
|
188
|
+
if self.app == App.RangerApp:
|
|
189
|
+
sys.stdout.write("Ranger supports only Solidity (.sol) and configuration (.conf) files.\n"
|
|
190
|
+
"Rust and Vyper contracts are not currently supported.\n\n")
|
|
191
|
+
elif isinstance(self.app, App.EvmAppClass):
|
|
166
192
|
sys.stdout.write("Acceptable files for EVM projects are Solidity files (.sol suffix), "
|
|
167
193
|
"Vyper files (.vy suffix), or conf files (.conf suffix)\n\n")
|
|
168
|
-
elif
|
|
194
|
+
elif self.app == App.SolanaApp:
|
|
169
195
|
sys.stdout.write("Acceptable files for Solana projects are RUST executables ('.so' suffix) or conf "
|
|
170
196
|
"files ('.conf' suffix')\n\n")
|
|
171
|
-
elif
|
|
197
|
+
elif self.app == App.SorobanApp:
|
|
172
198
|
sys.stdout.write("Acceptable files for Soroban projects are WASM executables ('.wasm' suffix) or conf "
|
|
173
199
|
"files ('.conf' suffix')\n\n")
|
|
174
200
|
else:
|
|
@@ -187,24 +213,23 @@ class ProverParser(AttrUtil.ContextAttributeParser):
|
|
|
187
213
|
|
|
188
214
|
return ''
|
|
189
215
|
|
|
190
|
-
def __get_argparser() -> argparse.ArgumentParser:
|
|
216
|
+
def __get_argparser(app: Type[App.CertoraApp]) -> argparse.ArgumentParser:
|
|
191
217
|
def formatter(prog: Any) -> argparse.HelpFormatter:
|
|
192
218
|
return argparse.HelpFormatter(prog, max_help_position=100, width=200)
|
|
193
219
|
|
|
194
|
-
parser = ProverParser(prog="certora-cli arguments and options", allow_abbrev=False,
|
|
220
|
+
parser = ProverParser(app, prog="certora-cli arguments and options", allow_abbrev=False,
|
|
195
221
|
formatter_class=formatter,
|
|
196
222
|
epilog=" -*-*-* You can find detailed documentation of the supported options in "
|
|
197
223
|
f"{CLI_DOCUMENTATION_URL} -*-*-*")
|
|
198
224
|
|
|
199
|
-
for arg in
|
|
225
|
+
for arg in app.attr_class.attribute_list():
|
|
200
226
|
flag = arg.get_flag()
|
|
201
227
|
parser.add_argument(flag, help=arg.help_msg, **arg.argparse_args)
|
|
202
228
|
return parser
|
|
203
229
|
|
|
204
|
-
|
|
205
|
-
def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
|
|
230
|
+
def get_args(args_list: List[str], app: Type[App.CertoraApp]) -> CertoraContext:
|
|
206
231
|
"""
|
|
207
|
-
Compiles an
|
|
232
|
+
Compiles an CertoraContext object from the given list of command line arguments.
|
|
208
233
|
|
|
209
234
|
Why do we handle --version before argparse?
|
|
210
235
|
Because on some platforms, mainly CI tests, we cannot fetch the installed distribution package version of
|
|
@@ -212,15 +237,13 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
|
|
|
212
237
|
We do it pre-argparse, because we do not care bout the input validity of anything else if we have a --version flag
|
|
213
238
|
"""
|
|
214
239
|
|
|
215
|
-
if not Attrs.ATTRIBUTES_CLASS:
|
|
216
|
-
Attrs.set_attribute_class(Attrs.EvmProverAttributes) # for scripts that call get_args directly
|
|
217
240
|
if args_list is None:
|
|
218
241
|
args_list = sys.argv
|
|
219
242
|
|
|
220
243
|
handle_version_flag(args_list)
|
|
221
244
|
|
|
222
245
|
pre_arg_fetching_checks(args_list)
|
|
223
|
-
parser = __get_argparser()
|
|
246
|
+
parser = __get_argparser(app)
|
|
224
247
|
|
|
225
248
|
# if there is a --help flag, we want to ignore all parsing errors, even those before it:
|
|
226
249
|
if any(string in [arg.strip() for arg in args_list] for string in ['--help', '-h']):
|
|
@@ -235,6 +258,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
|
|
|
235
258
|
|
|
236
259
|
args = parser.parse_args(args_list)
|
|
237
260
|
context = CertoraContext(**vars(args))
|
|
261
|
+
context.app = app
|
|
238
262
|
context.args_list = args_list
|
|
239
263
|
|
|
240
264
|
__remove_parsing_whitespace(args_list)
|
|
@@ -243,17 +267,18 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
|
|
|
243
267
|
|
|
244
268
|
if context.is_conf:
|
|
245
269
|
read_from_conf_file(context)
|
|
246
|
-
|
|
270
|
+
context.process = None
|
|
247
271
|
context.local = Util.is_local(context)
|
|
248
272
|
context.is_tac = context.files and context.files[0].endswith('.tac')
|
|
249
273
|
context.is_vyper = context.files and context.files[0].endswith('.vy')
|
|
250
274
|
|
|
251
|
-
if
|
|
275
|
+
if is_evm_app_class(context):
|
|
252
276
|
Cv.check_mode_of_operation(context) # Here boolean run characteristics are set
|
|
253
277
|
|
|
254
278
|
validator = Cv.CertoraContextValidator(context)
|
|
279
|
+
|
|
255
280
|
validator.validate()
|
|
256
|
-
if
|
|
281
|
+
if is_evm_app_class(context) or is_rust_app_class(context):
|
|
257
282
|
current_build_directory = Util.get_build_dir()
|
|
258
283
|
if context.build_dir is not None and current_build_directory != context.build_dir:
|
|
259
284
|
Util.reset_certora_internal_dir(context.build_dir)
|
|
@@ -265,14 +290,19 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
|
|
|
265
290
|
if context.java_args is not None:
|
|
266
291
|
context.java_args = ' '.join(context.java_args).replace('"', '').replace("'", '')
|
|
267
292
|
|
|
268
|
-
if
|
|
293
|
+
if is_evm_app_class(context) or context.app == App.ConcordApp:
|
|
269
294
|
validator.check_args_post_argparse()
|
|
270
295
|
setup_cache(context) # Here context.cache, context.user_defined_cache are set
|
|
296
|
+
validator.handle_ranger_attrs()
|
|
297
|
+
validator.handle_concord_attrs()
|
|
298
|
+
if is_rust_app_class(context):
|
|
299
|
+
validator.check_rust_args_post_argparse()
|
|
271
300
|
|
|
272
301
|
attrs_to_relative(context)
|
|
273
302
|
# Setup defaults (defaults are not recorded in conf file)
|
|
274
303
|
context.expected_file = context.expected_file or "expected.json"
|
|
275
304
|
context.run_source = context.run_source or Vf.RunSources.COMMAND.name.upper()
|
|
305
|
+
context.java_version = Util.get_java_version()
|
|
276
306
|
|
|
277
307
|
context_logger.debug("parsed args successfully.")
|
|
278
308
|
context_logger.debug(f"args= {context}")
|
|
@@ -346,7 +376,7 @@ def format_input(context: CertoraContext) -> None:
|
|
|
346
376
|
:param context: Namespace containing all command line arguments, generated by get_args()
|
|
347
377
|
"""
|
|
348
378
|
flatten_arg_lists(context)
|
|
349
|
-
if
|
|
379
|
+
if is_evm_app_class(context):
|
|
350
380
|
__dedup_link(context)
|
|
351
381
|
|
|
352
382
|
|
|
@@ -391,7 +421,7 @@ def setup_cache(context: CertoraContext) -> None:
|
|
|
391
421
|
# we have a user defined cache key if the user provided a cache key
|
|
392
422
|
context.user_defined_cache = context.cache is not None
|
|
393
423
|
if not context.disable_auto_cache_key_gen and not os.environ.get("CERTORA_DISABLE_AUTO_CACHE") is not None:
|
|
394
|
-
if context.is_verify or context.
|
|
424
|
+
if context.is_verify or context.is_conf:
|
|
395
425
|
# in local mode we don't want to create a cache key if not such is given
|
|
396
426
|
if (context.cache is None) and (not context.local):
|
|
397
427
|
optimistic_loop = context.optimistic_loop
|
|
@@ -425,7 +455,7 @@ def write_output_conf_to_path(json_content: Dict[str, Any], path: Path) -> None:
|
|
|
425
455
|
json.dump(json_content, out_file, indent=4, sort_keys=True)
|
|
426
456
|
|
|
427
457
|
|
|
428
|
-
def handle_flags_in_args(args: List[str]) -> None:
|
|
458
|
+
def handle_flags_in_args(args: List[str], app: Type[App.CertoraApp]) -> None:
|
|
429
459
|
"""
|
|
430
460
|
For argparse flags are strings that start with a dash. Some arguments get flags as value.
|
|
431
461
|
The problem is that argparse will not treat the string as a value but rather as a new flag. There are different ways
|
|
@@ -441,7 +471,7 @@ def handle_flags_in_args(args: List[str]) -> None:
|
|
|
441
471
|
Will all be converted to " -d"
|
|
442
472
|
|
|
443
473
|
"""
|
|
444
|
-
all_flags = list(map(lambda member: member.get_flag(),
|
|
474
|
+
all_flags = list(map(lambda member: member.get_flag(), app.attr_class.attribute_list()))
|
|
445
475
|
|
|
446
476
|
def surrounded(string: str, char: str) -> bool:
|
|
447
477
|
if len(string) < 2:
|
|
@@ -492,14 +522,27 @@ def __rename_key(context: CertoraContext, old_key: str, new_key: str) -> None:
|
|
|
492
522
|
context.delete_key(old_key)
|
|
493
523
|
|
|
494
524
|
|
|
495
|
-
def
|
|
525
|
+
def should_run_local_speck_check(context: CertoraContext) -> bool:
|
|
526
|
+
output = context.compilation_steps_only or not (context.disable_local_typechecking or Util.is_ci_or_git_action())
|
|
527
|
+
if not output:
|
|
528
|
+
if context.disable_local_typechecking:
|
|
529
|
+
context_logger.warning(
|
|
530
|
+
"Local checks of CVL specification files disabled. It is recommended to enable the checks."
|
|
531
|
+
)
|
|
532
|
+
else:
|
|
533
|
+
context_logger.info("Local checks of CVL specification files skipped in CI and will run remotely.")
|
|
534
|
+
return output
|
|
535
|
+
|
|
536
|
+
|
|
537
|
+
def run_typechecker(typechecker_name: str, with_typechecking: bool, args: List[str], print_errors: bool) -> None:
|
|
496
538
|
"""
|
|
497
539
|
Runs a spec typechecker or syntax checker
|
|
498
|
-
|
|
499
|
-
@param
|
|
540
|
+
|
|
541
|
+
@param typechecker_name: the name of the jar that we should run for running typechecking
|
|
542
|
+
@param with_typechecking: True if we want full typechecking including build (Solidity outputs) file,
|
|
500
543
|
False if we run only the leaner syntax checking.
|
|
501
544
|
@param args: A list of strings of additional arguments to the typechecker jar.
|
|
502
|
-
|
|
545
|
+
@param print_errors: Toggles printing the typechecker's errors to stderr.
|
|
503
546
|
"""
|
|
504
547
|
# Find path to typechecker jar
|
|
505
548
|
path_to_typechecker = Util.find_jar(typechecker_name)
|
|
@@ -512,31 +555,35 @@ def run_typechecker(typechecker_name: str, with_typechecking: bool, args: List[s
|
|
|
512
555
|
if with_typechecking:
|
|
513
556
|
cmd_str_list.append('-typeCheck')
|
|
514
557
|
|
|
558
|
+
context_logger.debug(f"typechecking cmd: {' '.join(cmd_str_list)}")
|
|
559
|
+
|
|
515
560
|
exit_code = Util.run_jar_cmd(cmd_str_list, False,
|
|
516
|
-
custom_error_message="Failed to run Certora Prover locally
|
|
517
|
-
"below for problems in the specifications
|
|
518
|
-
"prover_args defined in the .conf file.",
|
|
519
|
-
logger_topic="type_check"
|
|
561
|
+
custom_error_message="Failed to run Certora Prover typechecker locally.\n"
|
|
562
|
+
"Please check the errors below for problems in the specifications"
|
|
563
|
+
" (.spec files) or the prover_args defined in the .conf file.",
|
|
564
|
+
logger_topic="type_check",
|
|
565
|
+
print_err=print_errors)
|
|
520
566
|
if exit_code != 0:
|
|
521
|
-
raise Util.CertoraUserInputError(
|
|
522
|
-
"CVL syntax or type check failed.\n Please fix the issue. "
|
|
523
|
-
"Using --disable_local_typechecking to skip this check is strongly discouraged, "
|
|
524
|
-
"as simple syntax errors will only be detected during the cloud run."
|
|
525
|
-
"with --disable_local_typechecking is not recommended is not recommended as "
|
|
526
|
-
"simple syntax failures will be visible only on the cloud run.")
|
|
567
|
+
raise Util.CertoraUserInputError("CVL syntax or type check failed, please fix the issue.")
|
|
527
568
|
|
|
528
569
|
|
|
529
|
-
def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> None:
|
|
570
|
+
def run_local_spec_check(with_typechecking: bool, context: CertoraContext, extra_args: List[str] = list(), print_errors: bool = True) -> None:
|
|
530
571
|
"""
|
|
531
|
-
Runs the local type checker
|
|
532
|
-
|
|
572
|
+
Runs the local type checker
|
|
573
|
+
|
|
574
|
+
@param with_typechecking: If `True` will perform a full typecheck pass which requires Solidity build information.
|
|
575
|
+
Otherwise performs only a syntax check.
|
|
576
|
+
|
|
577
|
+
@param context: The `CertoraContext`.
|
|
578
|
+
|
|
579
|
+
@param extra_args: a list of additional arguments that should be sent to the typechecker.
|
|
580
|
+
|
|
581
|
+
@param print_errors: Toggles printing the typechecker's errors to stderr. Default: True
|
|
533
582
|
"""
|
|
534
583
|
|
|
535
|
-
if context.disable_local_typechecking or Util.is_ci_or_git_action():
|
|
536
|
-
return
|
|
537
584
|
args = collect_jar_args(context)
|
|
538
|
-
if Util.is_java_installed():
|
|
539
|
-
run_typechecker("Typechecker.jar", with_typechecking, args)
|
|
585
|
+
if Util.is_java_installed(context.java_version):
|
|
586
|
+
run_typechecker("Typechecker.jar", with_typechecking, args + extra_args, print_errors)
|
|
540
587
|
else:
|
|
541
588
|
raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
|
|
542
589
|
"To skip local checks run with the --disable_local_typechecking flag")
|
|
@@ -603,3 +650,30 @@ def attrs_to_relative(context: CertoraContext) -> None:
|
|
|
603
650
|
packages_to_relative()
|
|
604
651
|
prover_resource_file_to_relative()
|
|
605
652
|
verify_to_relative()
|
|
653
|
+
|
|
654
|
+
def get_map_attribute_value(context: CertoraContext, path: Path, attr_name: str) -> Optional[Union[str, bool]]:
|
|
655
|
+
|
|
656
|
+
value = getattr(context, attr_name, None)
|
|
657
|
+
if value:
|
|
658
|
+
return value
|
|
659
|
+
|
|
660
|
+
map_value = getattr(context, f"{attr_name}_map", None)
|
|
661
|
+
if not map_value:
|
|
662
|
+
return None # No map value defined
|
|
663
|
+
for key, entry_value in map_value.items():
|
|
664
|
+
# Split key to handle contract:field syntax
|
|
665
|
+
key_parts = key.split(':')
|
|
666
|
+
pattern = key_parts[0]
|
|
667
|
+
|
|
668
|
+
if Path(pattern).suffix == "": # This is a contract pattern
|
|
669
|
+
# Find contracts that match the pattern
|
|
670
|
+
for contract_name, contract_file_path in context.contract_to_file.items():
|
|
671
|
+
if fnmatch.fnmatch(contract_name, pattern):
|
|
672
|
+
# Check if this contract's file matches our target path
|
|
673
|
+
if str(path).endswith(contract_file_path):
|
|
674
|
+
return entry_value
|
|
675
|
+
else: # This is a file pattern
|
|
676
|
+
# Match the file pattern against the path
|
|
677
|
+
if glob.globmatch(str(path), pattern, flags=glob.GLOBSTAR):
|
|
678
|
+
return entry_value
|
|
679
|
+
raise RuntimeError(f"cannot match {attr_name} to {path} from {attr_name}_map")
|