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
|
@@ -25,8 +25,9 @@ from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerColle
|
|
|
25
25
|
from CertoraProver.Compiler.CompilerCollectorSol import CompilerCollectorSol, CompilerLangSol
|
|
26
26
|
from CertoraProver.Compiler.CompilerCollectorYul import CompilerLangYul, CompilerCollectorYul
|
|
27
27
|
from CertoraProver.Compiler.CompilerCollectorVy import CompilerCollectorVy, CompilerLangVy
|
|
28
|
-
from Shared.certoraUtils import
|
|
28
|
+
from Shared.certoraUtils import remove_file, get_certora_config_dir
|
|
29
29
|
from CertoraProver.certoraContextClass import CertoraContext
|
|
30
|
+
import CertoraProver.certoraContext as Ctx
|
|
30
31
|
|
|
31
32
|
# logger for running the Solidity compiler and reporting any errors it emits
|
|
32
33
|
compiler_logger = logging.getLogger("compiler")
|
|
@@ -42,10 +43,16 @@ def get_relevant_compiler(contract_file_path: Path, context: CertoraContext) ->
|
|
|
42
43
|
|
|
43
44
|
if contract_file_path.is_absolute():
|
|
44
45
|
contract_file_path = Path(os.path.relpath(contract_file_path, Path.cwd()))
|
|
46
|
+
|
|
47
|
+
# If the contract file is in the sources directory, we want to use the relative path from the "cwd" in the source tree.
|
|
48
|
+
if Util.get_certora_sources_dir() in contract_file_path.parents:
|
|
49
|
+
cwd_in_source = Util.get_certora_sources_dir() / context.cwd_rel_in_sources
|
|
50
|
+
contract_file_path = Path(os.path.relpath(contract_file_path, cwd_in_source))
|
|
51
|
+
|
|
45
52
|
if context.compiler_map:
|
|
46
|
-
match =
|
|
53
|
+
match = Ctx.get_map_attribute_value(context, contract_file_path, 'compiler')
|
|
47
54
|
if match:
|
|
48
|
-
return match
|
|
55
|
+
return str(match)
|
|
49
56
|
else:
|
|
50
57
|
raise RuntimeError(f'cannot match compiler to {contract_file_path} from compiler_map')
|
|
51
58
|
|
|
@@ -556,43 +556,78 @@ class CompilerLangVy(CompilerLang, metaclass=Singleton):
|
|
|
556
556
|
else:
|
|
557
557
|
raise Exception(f"Unexpected ast_node {ast_node}, cannot evaluate constant")
|
|
558
558
|
|
|
559
|
+
@staticmethod
|
|
560
|
+
def subscript_node(ast_node: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
561
|
+
ty = ast_node.get("ast_type")
|
|
562
|
+
# pre-vyper 0.4.0, look deeper within
|
|
563
|
+
if ty == "Index":
|
|
564
|
+
nested = ast_node.get("value")
|
|
565
|
+
if nested is None or not isinstance(nested, dict):
|
|
566
|
+
return None
|
|
567
|
+
return nested
|
|
568
|
+
else:
|
|
569
|
+
return ast_node
|
|
570
|
+
|
|
571
|
+
@staticmethod
|
|
572
|
+
def int_subscript(ast_node: Dict[str, Any], named_constants: Dict[str, int]) -> Optional[int]:
|
|
573
|
+
node = CompilerLangVy.subscript_node(ast_node)
|
|
574
|
+
if node is None:
|
|
575
|
+
return None
|
|
576
|
+
|
|
577
|
+
if "id" in node and node["id"] in named_constants:
|
|
578
|
+
return named_constants[node["id"]]
|
|
579
|
+
elif node.get("ast_type") == "Int":
|
|
580
|
+
return node.get("value")
|
|
581
|
+
else:
|
|
582
|
+
return None
|
|
583
|
+
|
|
584
|
+
@staticmethod
|
|
585
|
+
def get_tuple_elements(ast_node: Dict[str, Any]) -> List[Dict[str, Any]]:
|
|
586
|
+
node = CompilerLangVy.subscript_node(ast_node)
|
|
587
|
+
if node is None or node.get("ast_type") != "Tuple":
|
|
588
|
+
raise Exception("Couldn't get tuple elements from ast node")
|
|
589
|
+
return node["elements"]
|
|
590
|
+
|
|
559
591
|
@staticmethod
|
|
560
592
|
def extract_type_from_subscript_node(ast_subscript_node: Dict[str, Any],
|
|
561
593
|
named_constants: Dict[str, int]) -> VyperType:
|
|
562
594
|
value_id = ast_subscript_node['value'].get('id', None)
|
|
563
595
|
if value_id == 'String':
|
|
564
|
-
max_bytes = ast_subscript_node['slice']
|
|
596
|
+
max_bytes = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
|
|
597
|
+
if max_bytes is None:
|
|
598
|
+
raise Exception("Failed to find max length for string type declaration")
|
|
565
599
|
return CompilerLangVy.VyperTypeString(max_bytes)
|
|
566
600
|
elif value_id == 'Bytes':
|
|
567
|
-
max_bytes = ast_subscript_node['slice']
|
|
601
|
+
max_bytes = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
|
|
602
|
+
if max_bytes is None:
|
|
603
|
+
raise Exception("Failed to find max length for bytes type declaration")
|
|
568
604
|
return CompilerLangVy.VyperTypeBytes(max_bytes)
|
|
569
605
|
elif value_id == 'DynArray':
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
max_elements = CompilerLangVy.extract_constant(
|
|
573
|
-
named_constants)
|
|
606
|
+
tup_elems = CompilerLangVy.get_tuple_elements(ast_subscript_node["slice"])
|
|
607
|
+
elem_type = CompilerLangVy.extract_type_from_type_annotation_node(tup_elems[0], named_constants)
|
|
608
|
+
max_elements = CompilerLangVy.extract_constant(tup_elems[1], named_constants)
|
|
574
609
|
return CompilerLangVy.VyperTypeDynArray(elem_type, max_elements)
|
|
575
610
|
elif value_id == 'HashMap':
|
|
576
|
-
elements_node = ast_subscript_node['slice']
|
|
611
|
+
elements_node = CompilerLangVy.get_tuple_elements(ast_subscript_node['slice'])
|
|
577
612
|
key_type = CompilerLangVy.extract_type_from_type_annotation_node(elements_node[0], named_constants)
|
|
578
613
|
value_type = CompilerLangVy.extract_type_from_type_annotation_node(elements_node[1], named_constants)
|
|
579
614
|
return CompilerLangVy.VyperTypeHashMap(key_type, value_type)
|
|
580
615
|
else: # StaticArray key_type[size]
|
|
581
616
|
key_type = CompilerLangVy.extract_type_from_type_annotation_node(ast_subscript_node['value'],
|
|
582
617
|
named_constants)
|
|
583
|
-
|
|
584
|
-
if
|
|
585
|
-
return CompilerLangVy.VyperTypeStaticArray(key_type,
|
|
618
|
+
max_elem_value = CompilerLangVy.int_subscript(ast_subscript_node['slice'], named_constants)
|
|
619
|
+
if max_elem_value is not None:
|
|
620
|
+
return CompilerLangVy.VyperTypeStaticArray(key_type, max_elem_value)
|
|
586
621
|
else:
|
|
587
622
|
# this is very specific to curve code which has uint256[CONST/2] static array declaration.
|
|
623
|
+
max_elements_node = CompilerLangVy.subscript_node(ast_subscript_node["slice"])
|
|
624
|
+
if max_elements_node is None:
|
|
625
|
+
raise Exception("Couldn't find subscript node for array type")
|
|
588
626
|
if 'ast_type' in max_elements_node:
|
|
589
627
|
if max_elements_node['ast_type'] == 'BinOp' or max_elements_node['ast_type'] in ('Int', 'Name'):
|
|
590
628
|
# good chance this will succeed
|
|
591
629
|
static_array_len = CompilerLangVy.extract_constant(max_elements_node, named_constants)
|
|
592
630
|
return CompilerLangVy.VyperTypeStaticArray(key_type, static_array_len)
|
|
593
|
-
elif 'value' in max_elements_node:
|
|
594
|
-
return CompilerLangVy.VyperTypeStaticArray(key_type, max_elements_node['value'])
|
|
595
|
-
|
|
596
631
|
raise Exception(
|
|
597
632
|
f"Don't know how to deal with vyper static array declaration with length {max_elements_node}")
|
|
598
633
|
|
|
@@ -624,7 +659,7 @@ class CompilerLangVy(CompilerLang, metaclass=Singleton):
|
|
|
624
659
|
|
|
625
660
|
@staticmethod
|
|
626
661
|
def extract_type_from_enum_def(ast_enumdef_node: Dict[str, Any]) -> VyperType:
|
|
627
|
-
fields = [n['target']['id'] for n in ast_enumdef_node['body']]
|
|
662
|
+
fields = [n['target']['id'] if "target" in n else n["value"]["id"] for n in ast_enumdef_node['body']]
|
|
628
663
|
return CompilerLangVy.VyperTypeEnum(ast_enumdef_node['name'], fields)
|
|
629
664
|
|
|
630
665
|
@staticmethod
|
|
@@ -1238,7 +1273,7 @@ class Collector:
|
|
|
1238
1273
|
self._collect_const(v)
|
|
1239
1274
|
|
|
1240
1275
|
# Extract and resolve types
|
|
1241
|
-
type_asts = {'EnumDef', 'StructDef', 'InterfaceDef', 'Import', 'Import', 'ImportFrom'}
|
|
1276
|
+
type_asts = {'EnumDef', 'StructDef', 'InterfaceDef', 'Import', 'Import', 'ImportFrom', 'FlagDef'}
|
|
1242
1277
|
types = [e for e in module_node['body'] if e['ast_type'] in type_asts]
|
|
1243
1278
|
for t in types:
|
|
1244
1279
|
self._collect_type_decl(t)
|
|
@@ -1265,7 +1300,7 @@ class Collector:
|
|
|
1265
1300
|
|
|
1266
1301
|
def _collect_type_decl(self, type_decl_node: Dict[str, Any]) -> None:
|
|
1267
1302
|
"""Update self.types to add type definition from `type_decl_node`. Expects self.consts to be initialized"""
|
|
1268
|
-
if type_decl_node['ast_type']
|
|
1303
|
+
if type_decl_node['ast_type'] in ('EnumDef', 'FlagDef'):
|
|
1269
1304
|
vy_type = CompilerLangVy.extract_type_from_enum_def(type_decl_node)
|
|
1270
1305
|
elif type_decl_node['ast_type'] == 'StructDef':
|
|
1271
1306
|
vy_type = CompilerLangVy.extract_type_from_struct_def(type_decl_node, self.consts)
|
|
@@ -104,14 +104,17 @@ class CompilerLangYul(CompilerLangSol, metaclass=Singleton):
|
|
|
104
104
|
notpayable=notpayable,
|
|
105
105
|
fromLib=False,
|
|
106
106
|
isConstructor=False,
|
|
107
|
+
is_free_func=False,
|
|
107
108
|
stateMutability=state_mutability,
|
|
108
109
|
visibility=visibility,
|
|
109
110
|
implemented=True,
|
|
110
111
|
overrides=False,
|
|
112
|
+
virtual=False,
|
|
111
113
|
contractName=contract_name,
|
|
112
114
|
source_bytes=None,
|
|
113
115
|
ast_id=None,
|
|
114
116
|
original_file=None,
|
|
117
|
+
location=None,
|
|
115
118
|
body_location=None,
|
|
116
119
|
)
|
|
117
120
|
funcs.append(func)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# The Certora Prover
|
|
2
|
+
# Copyright (C) 2025 Certora Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, version 3 of the License.
|
|
7
|
+
#
|
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11
|
+
# GNU General Public License for more details.
|
|
12
|
+
#
|
|
13
|
+
# You should have received a copy of the GNU General Public License
|
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import Dict, Any, Optional, Generator
|
|
19
|
+
|
|
20
|
+
from CertoraProver.Compiler.CompilerCollectorSol import CompilerCollectorSol
|
|
21
|
+
from CertoraProver.certoraBuildDataClasses import SDC, Instrumentation, Replace
|
|
22
|
+
from Shared import certoraUtils as Util
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class IntType:
|
|
27
|
+
is_signed: bool
|
|
28
|
+
bitwidth: int
|
|
29
|
+
|
|
30
|
+
def encode(self) -> Optional[int]:
|
|
31
|
+
"""
|
|
32
|
+
Encodes into a 9 bit number: lowest bit is on if [IntType] is true, and upper 8 bits are the width.
|
|
33
|
+
returns none if width is out of bounds.
|
|
34
|
+
"""
|
|
35
|
+
if self.bitwidth <= 0 or self.bitwidth > 256:
|
|
36
|
+
return None
|
|
37
|
+
return self.bitwidth * 2 + int(self.is_signed)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def parse_int_type(type_string: str) -> Optional[IntType]:
|
|
41
|
+
"""
|
|
42
|
+
Parse an integer type string of the form 'int<bitwidth>' or 'uint<bitwidth>'.
|
|
43
|
+
Returns IntType or None if the string doesn't match the pattern.
|
|
44
|
+
"""
|
|
45
|
+
if type_string == "int":
|
|
46
|
+
return IntType(True, 256)
|
|
47
|
+
elif type_string == "uint":
|
|
48
|
+
return IntType(False, 256)
|
|
49
|
+
elif type_string.startswith("int"):
|
|
50
|
+
is_signed = True
|
|
51
|
+
bitwidth_str = type_string[3:]
|
|
52
|
+
elif type_string.startswith("uint"):
|
|
53
|
+
is_signed = False
|
|
54
|
+
bitwidth_str = type_string[4:]
|
|
55
|
+
else:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
bitwidth = int(bitwidth_str)
|
|
60
|
+
return IntType(is_signed, bitwidth)
|
|
61
|
+
except ValueError:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def is_int_type(type_string: str) -> bool:
|
|
66
|
+
return parse_int_type(type_string) is not None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def encode_type(type_string: str) -> int:
|
|
70
|
+
int_type = parse_int_type(type_string)
|
|
71
|
+
if int_type is None:
|
|
72
|
+
raise Exception(f"Invalid type string: {type_string}")
|
|
73
|
+
encoded = int_type.encode()
|
|
74
|
+
if encoded is None:
|
|
75
|
+
raise Exception(f"Invalid type string: {type_string}")
|
|
76
|
+
return encoded
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class CastInfo:
|
|
81
|
+
arg_type_str: str
|
|
82
|
+
res_type_str: str
|
|
83
|
+
expr_id: int
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def find_casts(ast: Dict[int, Any]) -> list[CastInfo]:
|
|
87
|
+
"""
|
|
88
|
+
Search all nodes that are in functions for kind "typeConversion" with one argument.
|
|
89
|
+
Returns a list of CastInfo instances.
|
|
90
|
+
"""
|
|
91
|
+
conversions = []
|
|
92
|
+
function_nodes = [node for node in ast.values() if node.get('nodeType') == 'FunctionDefinition']
|
|
93
|
+
|
|
94
|
+
for func in function_nodes:
|
|
95
|
+
for node in iter_all_nodes(func):
|
|
96
|
+
if isinstance(node, dict) and node.get("kind") == "typeConversion":
|
|
97
|
+
arguments = node.get("arguments", [])
|
|
98
|
+
if len(arguments) == 1 and isinstance(arguments[0], dict):
|
|
99
|
+
expression = node["expression"]
|
|
100
|
+
expr_node_id = expression["id"]
|
|
101
|
+
arg_type_str = expression["argumentTypes"][0]["typeString"]
|
|
102
|
+
if not is_int_type(arg_type_str):
|
|
103
|
+
continue
|
|
104
|
+
if "typeName" not in expression:
|
|
105
|
+
continue
|
|
106
|
+
expr_type_node = expression["typeName"]
|
|
107
|
+
if isinstance(expr_type_node, str):
|
|
108
|
+
expr_type_str = expr_type_node
|
|
109
|
+
else:
|
|
110
|
+
expr_type_str = expr_type_node["name"]
|
|
111
|
+
if not is_int_type(expr_type_str):
|
|
112
|
+
continue
|
|
113
|
+
conversions.append(CastInfo(arg_type_str, expr_type_str, expr_node_id))
|
|
114
|
+
|
|
115
|
+
return conversions
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def casting_func_name(counter: int) -> str:
|
|
119
|
+
return f"cast_{counter}"
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def generate_casting_function(assembly_prefix: str, cast_info: CastInfo, counter: int) -> str:
|
|
123
|
+
"""
|
|
124
|
+
returns the text of a solidity function that does casting according to CastInfo. It also has an encoded mload
|
|
125
|
+
call, to be decoded later on the kotlin side if we run the `safeCasting` builtin rule.
|
|
126
|
+
"""
|
|
127
|
+
conversion_string = assembly_prefix + \
|
|
128
|
+
"{ mstore(0xffffff6e4604afefe123321beef1b03fffffffffffffffffffff" + \
|
|
129
|
+
f'{"%0.4x" % counter}{"%0.4x" % encode_type(cast_info.arg_type_str)}{"%0.4x" % encode_type(cast_info.res_type_str)}, x)' + "}"
|
|
130
|
+
function_head = f"function {casting_func_name(counter)}({cast_info.arg_type_str} x) internal pure returns ({cast_info.res_type_str})"
|
|
131
|
+
return function_head + "{\n" + conversion_string + f"return {cast_info.res_type_str}(x);\n" "}\n"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def generate_casting_instrumentation(asts: Dict[str, Dict[str, Dict[int, Any]]], contract_file: str, sdc: SDC) \
|
|
135
|
+
-> tuple[Dict[str, Dict[int, Instrumentation]], Dict[str, tuple[str, list[str]]]]:
|
|
136
|
+
"""
|
|
137
|
+
Generate instrumentation for integer type casts in Solidity code.
|
|
138
|
+
|
|
139
|
+
Returns a tuple of:
|
|
140
|
+
- casting_instrumentation: Maps file paths to offset->Instrumentation mappings that replace type names with
|
|
141
|
+
library calls to casting functions we generate.
|
|
142
|
+
- casting_types: Maps file paths to library_name and the text for our new casting functions. This library is added
|
|
143
|
+
to the end of the file.
|
|
144
|
+
|
|
145
|
+
It's not hundred precent sure this works well in combination with `compiler_map`, because the counter for library
|
|
146
|
+
names may reset?
|
|
147
|
+
"""
|
|
148
|
+
casting_instrumentation: Dict[str, Dict[int, Instrumentation]] = dict()
|
|
149
|
+
|
|
150
|
+
if not isinstance(sdc.compiler_collector, CompilerCollectorSol):
|
|
151
|
+
raise Exception(f"Encountered a compiler collector that is not solc for file {contract_file}"
|
|
152
|
+
" when trying to add casting instrumentation")
|
|
153
|
+
assembly_prefix = sdc.compiler_collector.gen_memory_safe_assembly_prefix()
|
|
154
|
+
|
|
155
|
+
casting_funcs : Dict[str, tuple[str, list[str]]] = dict()
|
|
156
|
+
counter = 0
|
|
157
|
+
original_files = sorted({Util.convert_path_for_solc_import(c.original_file) for c in sdc.contracts})
|
|
158
|
+
for file_count, solfile in enumerate(original_files, start=1):
|
|
159
|
+
main_ast = asts[contract_file]
|
|
160
|
+
curr_file_ast = main_ast.get(solfile, dict())
|
|
161
|
+
|
|
162
|
+
per_file_inst = casting_instrumentation.setdefault(solfile, dict())
|
|
163
|
+
libname, per_file_casts = casting_funcs.setdefault(solfile, (f"CertoraCastingLib{file_count}", []))
|
|
164
|
+
|
|
165
|
+
casts = find_casts(curr_file_ast)
|
|
166
|
+
for cast_info in casts:
|
|
167
|
+
start_offset, src_len, file = curr_file_ast[cast_info.expr_id]["src"].split(":")
|
|
168
|
+
counter += 1
|
|
169
|
+
per_file_inst[int(start_offset)] = Instrumentation(expected=bytes(cast_info.res_type_str[0], 'utf-8'),
|
|
170
|
+
to_ins=f"{libname}.{casting_func_name(counter)}",
|
|
171
|
+
mut=Replace(len(cast_info.res_type_str)))
|
|
172
|
+
new_func = generate_casting_function(assembly_prefix, cast_info, counter)
|
|
173
|
+
per_file_casts.append(new_func)
|
|
174
|
+
|
|
175
|
+
return casting_instrumentation, casting_funcs
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def iter_all_nodes(node: Any) -> Generator[Any, Optional[Any], None]:
|
|
179
|
+
"""
|
|
180
|
+
Yield a node and all its subnodes in depth-first order.
|
|
181
|
+
Works with dict nodes that may contain nested dicts and lists.
|
|
182
|
+
"""
|
|
183
|
+
yield node
|
|
184
|
+
|
|
185
|
+
if isinstance(node, dict):
|
|
186
|
+
for value in node.values():
|
|
187
|
+
if isinstance(value, (dict, list)):
|
|
188
|
+
yield from iter_all_nodes(value)
|
|
189
|
+
elif isinstance(node, list):
|
|
190
|
+
for item in node:
|
|
191
|
+
if isinstance(item, (dict, list)):
|
|
192
|
+
yield from iter_all_nodes(item)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# The Certora Prover
|
|
2
|
+
# Copyright (C) 2025 Certora Ltd.
|
|
3
|
+
#
|
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
|
6
|
+
# the Free Software Foundation, version 3 of the License.
|
|
7
|
+
#
|
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
11
|
+
# GNU General Public License for more details.
|
|
12
|
+
#
|
|
13
|
+
# You should have received a copy of the GNU General Public License
|
|
14
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
15
|
+
|
|
16
|
+
import CertoraProver.certoraContextAttributes as Attrs
|
|
17
|
+
from Shared import certoraAttrUtil as AttrUtil
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
from abc import ABC
|
|
21
|
+
from typing import Type
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class CertoraApp(ABC):
|
|
25
|
+
attr_class: Type[AttrUtil.Attributes] = Attrs.EvmProverAttributes
|
|
26
|
+
|
|
27
|
+
class EvmAppClass(CertoraApp):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
class EvmApp(EvmAppClass):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
class RustAppClass(CertoraApp):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
class MoveAppClass(CertoraApp):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
class SolanaApp(RustAppClass):
|
|
40
|
+
attr_class = Attrs.SolanaProverAttributes
|
|
41
|
+
|
|
42
|
+
class SorobanApp(RustAppClass):
|
|
43
|
+
attr_class = Attrs.SorobanProverAttributes
|
|
44
|
+
|
|
45
|
+
class RangerApp(EvmAppClass):
|
|
46
|
+
attr_class = Attrs.RangerAttributes
|
|
47
|
+
|
|
48
|
+
class ConcordApp(EvmAppClass):
|
|
49
|
+
attr_class = Attrs.ConcordAttributes
|
|
50
|
+
|
|
51
|
+
class SuiApp(MoveAppClass):
|
|
52
|
+
attr_class = Attrs.SuiProverAttributes
|