certora-cli 8.3.1__tar.gz → 8.5.1__tar.gz
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-8.3.1/certora_cli.egg-info → certora_cli-8.5.1}/PKG-INFO +2 -2
- certora_cli-8.5.1/README.md +1 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +3 -3
- certora_cli-8.5.1/certora_cli/CertoraProver/castingInstrumenter.py +192 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuild.py +129 -23
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuildCacheManager.py +6 -3
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuildDataClasses.py +1 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraCloudIO.py +4 -1
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +29 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraCompilerParameters.py +11 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraContext.py +2 -2
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraContextAttributes.py +63 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraContextValidator.py +0 -2
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraContractFuncs.py +28 -1
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/certoraUtils.py +23 -3
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/certoraValidateFuncs.py +14 -8
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/proverCommon.py +4 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraRun.py +9 -1
- certora_cli-8.5.1/certora_cli/certoraSuiProver.py +93 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1/certora_cli.egg-info}/PKG-INFO +2 -2
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli.egg-info/SOURCES.txt +2 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli.egg-info/entry_points.txt +1 -0
- certora_cli-8.3.1/certora_jars/Typechecker.jar → certora_cli-8.5.1/certora_jars/ASTExtraction.jar +0 -0
- certora_cli-8.5.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
- certora_cli-8.3.1/certora_jars/ASTExtraction.jar → certora_cli-8.5.1/certora_jars/Typechecker.jar +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/setup.py +3 -2
- certora_cli-8.3.1/README.md +0 -1
- certora_cli-8.3.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
- {certora_cli-8.3.1 → certora_cli-8.5.1}/LICENSE +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/MANIFEST.in +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_bins/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraApp.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuildRust.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuildSui.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraConfigIO.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraJobList.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraParseBuildScript.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraType.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/erc7201.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/splitRules.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/storageExtension.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Mutate/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Mutate/mutateApp.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Mutate/mutateAttributes.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Mutate/mutateConstants.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Mutate/mutateUtil.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Mutate/mutateValidate.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/ExpectedComparator.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/certoraAttrUtil.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/Shared/certoraLogging.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraCVLFormatter.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraConcord.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraEVMProver.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraEqCheck.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraMutate.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraRanger.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraSolanaProver.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/certoraSorobanProver.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/rustMutator.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli.egg-info/dependency_links.txt +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli.egg-info/requires.txt +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli.egg-info/top_level.txt +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/certora_jars/__init__.py +0 -0
- {certora_cli-8.3.1 → certora_cli-8.5.1}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: certora-cli
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.5.1
|
|
4
4
|
Summary: Runner for the Certora Prover
|
|
5
5
|
Home-page: https://pypi.org/project/certora-cli
|
|
6
6
|
Author: Certora
|
|
@@ -39,4 +39,4 @@ Dynamic: requires-dist
|
|
|
39
39
|
Dynamic: requires-python
|
|
40
40
|
Dynamic: summary
|
|
41
41
|
|
|
42
|
-
Commit
|
|
42
|
+
Commit 1738e5b. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Commit 1738e5b. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
{certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py
RENAMED
|
@@ -659,7 +659,7 @@ class CompilerLangVy(CompilerLang, metaclass=Singleton):
|
|
|
659
659
|
|
|
660
660
|
@staticmethod
|
|
661
661
|
def extract_type_from_enum_def(ast_enumdef_node: Dict[str, Any]) -> VyperType:
|
|
662
|
-
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']]
|
|
663
663
|
return CompilerLangVy.VyperTypeEnum(ast_enumdef_node['name'], fields)
|
|
664
664
|
|
|
665
665
|
@staticmethod
|
|
@@ -1273,7 +1273,7 @@ class Collector:
|
|
|
1273
1273
|
self._collect_const(v)
|
|
1274
1274
|
|
|
1275
1275
|
# Extract and resolve types
|
|
1276
|
-
type_asts = {'EnumDef', 'StructDef', 'InterfaceDef', 'Import', 'Import', 'ImportFrom'}
|
|
1276
|
+
type_asts = {'EnumDef', 'StructDef', 'InterfaceDef', 'Import', 'Import', 'ImportFrom', 'FlagDef'}
|
|
1277
1277
|
types = [e for e in module_node['body'] if e['ast_type'] in type_asts]
|
|
1278
1278
|
for t in types:
|
|
1279
1279
|
self._collect_type_decl(t)
|
|
@@ -1300,7 +1300,7 @@ class Collector:
|
|
|
1300
1300
|
|
|
1301
1301
|
def _collect_type_decl(self, type_decl_node: Dict[str, Any]) -> None:
|
|
1302
1302
|
"""Update self.types to add type definition from `type_decl_node`. Expects self.consts to be initialized"""
|
|
1303
|
-
if type_decl_node['ast_type']
|
|
1303
|
+
if type_decl_node['ast_type'] in ('EnumDef', 'FlagDef'):
|
|
1304
1304
|
vy_type = CompilerLangVy.extract_type_from_enum_def(type_decl_node)
|
|
1305
1305
|
elif type_decl_node['ast_type'] == 'StructDef':
|
|
1306
1306
|
vy_type = CompilerLangVy.extract_type_from_struct_def(type_decl_node, self.consts)
|
|
@@ -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)
|
|
@@ -20,24 +20,24 @@ import os
|
|
|
20
20
|
import re
|
|
21
21
|
import shutil
|
|
22
22
|
import sys
|
|
23
|
+
import tempfile
|
|
23
24
|
import typing
|
|
24
25
|
from collections import OrderedDict, defaultdict
|
|
25
26
|
from enum import Enum
|
|
26
27
|
from functools import lru_cache
|
|
27
28
|
from pathlib import Path
|
|
28
|
-
from Crypto.Hash import keccak
|
|
29
|
-
import tempfile
|
|
30
|
-
|
|
31
29
|
from typing import Any, Dict, List, Tuple, Optional, Set, Iterator, NoReturn
|
|
32
30
|
|
|
31
|
+
from Crypto.Hash import keccak
|
|
33
32
|
|
|
33
|
+
from CertoraProver.castingInstrumenter import generate_casting_instrumentation
|
|
34
34
|
from CertoraProver.certoraBuildCacheManager import CertoraBuildCacheManager, CachedFiles
|
|
35
35
|
from CertoraProver.certoraBuildDataClasses import CONTRACTS, ImmutableReference, ContractExtension, ContractInSDC, SDC, \
|
|
36
36
|
Instrumentation, InsertBefore, InsertAfter, UnspecializedSourceFinder, instrumentation_logger
|
|
37
37
|
from CertoraProver.certoraCompilerParameters import SolcParameters
|
|
38
|
+
from CertoraProver.certoraContractFuncs import Func, InternalFunc, STATEMUT, SourceBytes, VyperMetadata
|
|
38
39
|
from CertoraProver.certoraSourceFinders import add_source_finders
|
|
39
40
|
from CertoraProver.certoraVerifyGenerator import CertoraVerifyGenerator
|
|
40
|
-
from CertoraProver.certoraContractFuncs import Func, InternalFunc, STATEMUT, SourceBytes
|
|
41
41
|
|
|
42
42
|
scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
|
|
43
43
|
sys.path.insert(0, str(scripts_dir_path))
|
|
@@ -1381,11 +1381,17 @@ class CertoraBuildGenerator:
|
|
|
1381
1381
|
|
|
1382
1382
|
return bytecode
|
|
1383
1383
|
|
|
1384
|
-
def
|
|
1385
|
-
match = Ctx.get_map_attribute_value(self.context, contract_file_path,
|
|
1386
|
-
assert isinstance(match, (bool, type(None))), f"Expected
|
|
1384
|
+
def get_map_bool_attribute_value(self, contract_file_path: Path, attr_name: str) -> bool:
|
|
1385
|
+
match = Ctx.get_map_attribute_value(self.context, contract_file_path, attr_name)
|
|
1386
|
+
assert isinstance(match, (bool, type(None))), f"Expected {attr_name} to be bool or None, got {type(match)}"
|
|
1387
1387
|
return bool(match)
|
|
1388
1388
|
|
|
1389
|
+
def get_solc_via_ir_value(self, contract_file_path: Path) -> bool:
|
|
1390
|
+
return self.get_map_bool_attribute_value(contract_file_path, 'solc_via_ir')
|
|
1391
|
+
|
|
1392
|
+
def get_vyper_venom_value(self, contract_file_path: Path) -> bool:
|
|
1393
|
+
return self.get_map_bool_attribute_value(contract_file_path, 'vyper_venom')
|
|
1394
|
+
|
|
1389
1395
|
def get_solc_evm_version_value(self, contract_file_path: Path) -> Optional[str]:
|
|
1390
1396
|
match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_evm_version')
|
|
1391
1397
|
assert isinstance(match, (str, type(None))), f"Expected solc_evm_version to be string or None, got {type(match)}"
|
|
@@ -1398,6 +1404,10 @@ class CertoraBuildGenerator:
|
|
|
1398
1404
|
match = str(match)
|
|
1399
1405
|
return match
|
|
1400
1406
|
|
|
1407
|
+
def _handle_venom(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
|
|
1408
|
+
if self.get_vyper_venom_value(contract_file_path):
|
|
1409
|
+
settings_dict["experimentalCodegen"] = True
|
|
1410
|
+
|
|
1401
1411
|
def _handle_via_ir(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
|
|
1402
1412
|
if self.get_solc_via_ir_value(contract_file_path):
|
|
1403
1413
|
settings_dict["viaIR"] = True
|
|
@@ -1510,6 +1520,9 @@ class CertoraBuildGenerator:
|
|
|
1510
1520
|
self._handle_via_ir(contract_file_path, settings_dict)
|
|
1511
1521
|
self._handle_evm_version(contract_file_path, settings_dict)
|
|
1512
1522
|
self._handle_optimize(contract_file_path, settings_dict, compiler_collector)
|
|
1523
|
+
compiler_lang = compiler_collector.smart_contract_lang
|
|
1524
|
+
if compiler_lang == CompilerLangVy():
|
|
1525
|
+
self._handle_venom(contract_file_path, settings_dict)
|
|
1513
1526
|
|
|
1514
1527
|
@staticmethod
|
|
1515
1528
|
def solc_setting_optimizer_runs(settings_dict: Dict[str, Any]) -> Tuple[bool, Optional[int]]:
|
|
@@ -1543,7 +1556,8 @@ class CertoraBuildGenerator:
|
|
|
1543
1556
|
contract_file_posix_abs: Path,
|
|
1544
1557
|
contract_file_as_provided: str,
|
|
1545
1558
|
remappings: List[str],
|
|
1546
|
-
compiler_collector: CompilerCollector
|
|
1559
|
+
compiler_collector: CompilerCollector,
|
|
1560
|
+
compile_wd: Path) -> Dict[str, Any]:
|
|
1547
1561
|
"""
|
|
1548
1562
|
when calling solc with the standard_json api, instead of passing it flags, we pass it json to request what we
|
|
1549
1563
|
want -- currently we only use this to retrieve storage layout as this is the only way to do that,
|
|
@@ -1554,9 +1568,10 @@ class CertoraBuildGenerator:
|
|
|
1554
1568
|
@param compiler_collector: Solidity or Vyper compiler collector
|
|
1555
1569
|
@return:
|
|
1556
1570
|
"""
|
|
1571
|
+
solc_json_contract_key = os.path.relpath(contract_file_as_provided, compile_wd) if self.context.use_relpaths_for_solc_json else contract_file_posix_abs
|
|
1557
1572
|
compiler_collector_lang = compiler_collector.smart_contract_lang
|
|
1558
1573
|
if compiler_collector_lang == CompilerLangSol() or compiler_collector_lang == CompilerLangYul():
|
|
1559
|
-
sources_dict = {str(
|
|
1574
|
+
sources_dict = {str(solc_json_contract_key): {
|
|
1560
1575
|
"urls": [str(contract_file_posix_abs)]}} # type: Dict[str, Dict[str, Any]]
|
|
1561
1576
|
output_selection = ["transientStorageLayout", "storageLayout", "abi", "evm.bytecode",
|
|
1562
1577
|
"evm.deployedBytecode", "evm.methodIdentifiers", "evm.assembly",
|
|
@@ -1567,6 +1582,8 @@ class CertoraBuildGenerator:
|
|
|
1567
1582
|
contents = f.read()
|
|
1568
1583
|
sources_dict = {str(contract_file_posix_abs): {"content": contents}}
|
|
1569
1584
|
output_selection = ["abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers"]
|
|
1585
|
+
if compiler_collector.compiler_version >= (0, 4, 4):
|
|
1586
|
+
output_selection += ["metadata", "evm.deployedBytecode.symbolMap"]
|
|
1570
1587
|
ast_selection = ["ast"]
|
|
1571
1588
|
|
|
1572
1589
|
settings_dict: Dict[str, Any] = \
|
|
@@ -1853,7 +1870,7 @@ class CertoraBuildGenerator:
|
|
|
1853
1870
|
# Standard JSON
|
|
1854
1871
|
remappings = [] if isinstance(compiler_collector, CompilerCollectorYul) else self.context.remappings
|
|
1855
1872
|
input_for_solc = self.standard_json(Path(file_abs_path), build_arg_contract_file, remappings,
|
|
1856
|
-
compiler_collector)
|
|
1873
|
+
compiler_collector, compile_wd)
|
|
1857
1874
|
standard_json_input = json.dumps(input_for_solc).encode("utf-8")
|
|
1858
1875
|
compiler_logger.debug(f"about to run in {compile_wd} the command: {collect_cmd}")
|
|
1859
1876
|
compiler_logger.debug(f"solc input = {json.dumps(input_for_solc, indent=4)}")
|
|
@@ -1909,7 +1926,7 @@ class CertoraBuildGenerator:
|
|
|
1909
1926
|
contract_file_abs = str(Util.abs_norm_path(contract_file))
|
|
1910
1927
|
|
|
1911
1928
|
# using os.path.relpath because Path.relative_to cannot go up the directory tree (no ..)
|
|
1912
|
-
contract_file_rel = os.path.relpath(Path(contract_file_abs),
|
|
1929
|
+
contract_file_rel = os.path.relpath(Path(contract_file_abs), compile_wd)
|
|
1913
1930
|
|
|
1914
1931
|
build_logger.debug(f"available keys: {data['contracts'].keys()}")
|
|
1915
1932
|
if contract_file_rel in data[CONTRACTS]:
|
|
@@ -2008,7 +2025,15 @@ class CertoraBuildGenerator:
|
|
|
2008
2025
|
srclist = {"0": file_abs_path}
|
|
2009
2026
|
report_srclist = {"0": file_abs_path}
|
|
2010
2027
|
|
|
2011
|
-
|
|
2028
|
+
# Annoyingly, this is currently used just for... presentation?!
|
|
2029
|
+
# We should clean up report_source_file from all places...
|
|
2030
|
+
|
|
2031
|
+
build_logger.debug(f"Finding report source file, abs path {file_abs_path} relative to {compile_wd}")
|
|
2032
|
+
if self.context.use_relpaths_for_solc_json:
|
|
2033
|
+
orig_report_source_file = Util.abs_posix_path(os.path.relpath(file_abs_path, compile_wd))
|
|
2034
|
+
else:
|
|
2035
|
+
orig_report_source_file = file_abs_path
|
|
2036
|
+
report_source_file = report_srclist[[idx for idx in srclist if Util.abs_posix_path(srclist[idx]) == orig_report_source_file][0]]
|
|
2012
2037
|
|
|
2013
2038
|
# all "contracts in SDC" are the same for every primary contract of the compiled file.
|
|
2014
2039
|
# we can therefore compute those just once...
|
|
@@ -2039,8 +2064,9 @@ class CertoraBuildGenerator:
|
|
|
2039
2064
|
|
|
2040
2065
|
build_logger.debug(f"finding primary contract address of {file_compiler_path}:{primary_contract} in "
|
|
2041
2066
|
f"{contracts_with_chosen_addresses}")
|
|
2067
|
+
path_to_find = os.path.relpath(file_compiler_path, compile_wd) if self.context.use_relpaths_for_solc_json else file_compiler_path
|
|
2042
2068
|
primary_contract_address = \
|
|
2043
|
-
self.find_contract_address_str(
|
|
2069
|
+
self.find_contract_address_str(path_to_find,
|
|
2044
2070
|
primary_contract,
|
|
2045
2071
|
contracts_with_chosen_addresses)
|
|
2046
2072
|
build_logger.debug(f"Contracts in SDC {sdc_name}: {[contract.name for contract in contracts_in_sdc]}")
|
|
@@ -2086,7 +2112,7 @@ class CertoraBuildGenerator:
|
|
|
2086
2112
|
if new_path is None:
|
|
2087
2113
|
file_abs_path = Util.abs_posix_path(build_arg_contract_file)
|
|
2088
2114
|
# for vyper, because there are no autofinders, at least find the main file
|
|
2089
|
-
if (orig_file == file_abs_path and
|
|
2115
|
+
if (Util.abs_posix_path(orig_file) == file_abs_path and
|
|
2090
2116
|
((Util.get_certora_sources_dir() / build_arg_contract_file).exists() or
|
|
2091
2117
|
Path(build_arg_contract_file).exists())):
|
|
2092
2118
|
new_srclist_map[idx] = build_arg_contract_file
|
|
@@ -2101,6 +2127,7 @@ class CertoraBuildGenerator:
|
|
|
2101
2127
|
report_srclist = srclist
|
|
2102
2128
|
else:
|
|
2103
2129
|
report_srclist = srclist
|
|
2130
|
+
build_logger.debug(f"Report source list={report_srclist}")
|
|
2104
2131
|
return report_srclist
|
|
2105
2132
|
|
|
2106
2133
|
def get_bytecode(self,
|
|
@@ -2108,7 +2135,7 @@ class CertoraBuildGenerator:
|
|
|
2108
2135
|
contract_name: str,
|
|
2109
2136
|
primary_contracts: List[str],
|
|
2110
2137
|
contracts_with_chosen_addresses: List[Tuple[int, Any]],
|
|
2111
|
-
|
|
2138
|
+
is_deployed_bytecode: bool
|
|
2112
2139
|
) -> str:
|
|
2113
2140
|
"""
|
|
2114
2141
|
Computes the linked bytecode object from the Solidity compiler output.
|
|
@@ -2119,20 +2146,31 @@ class CertoraBuildGenerator:
|
|
|
2119
2146
|
@param primary_contracts - the names of the primary contracts we check to have a bytecode
|
|
2120
2147
|
@param contracts_with_chosen_addresses - a list of tuples of addresses and the
|
|
2121
2148
|
associated contract identifier
|
|
2122
|
-
@param
|
|
2149
|
+
@param is_deployed_bytecode - true if we deal with deployed (runtime) bytecode,
|
|
2150
|
+
thus the function should fail if bytecode object is missing,
|
|
2123
2151
|
false otherwise
|
|
2124
2152
|
@returns linked bytecode object
|
|
2125
2153
|
"""
|
|
2126
2154
|
# TODO: Only contract_name should be necessary. This requires a lot more test cases to make sure we're not
|
|
2127
2155
|
# missing any weird solidity outputs.
|
|
2128
2156
|
bytecode_ = bytecode_object["object"]
|
|
2129
|
-
|
|
2130
|
-
|
|
2157
|
+
try:
|
|
2158
|
+
bytecode = self.collect_and_link_bytecode(contract_name, contracts_with_chosen_addresses,
|
|
2159
|
+
bytecode_, bytecode_object.get("linkReferences", {}))
|
|
2160
|
+
except Util.CertoraUserInputError as e:
|
|
2161
|
+
if is_deployed_bytecode:
|
|
2162
|
+
raise e
|
|
2163
|
+
else:
|
|
2164
|
+
prefix_msg = f"Failed to find a dependency library while building the "\
|
|
2165
|
+
f"constructor bytecode of {contract_name}. "\
|
|
2166
|
+
f"If you need the contract, import the missing library directly and add a dummy usage:\n"
|
|
2167
|
+
orig_msg = str(e)
|
|
2168
|
+
raise Util.CertoraUserInputError(prefix_msg + orig_msg)
|
|
2131
2169
|
if contract_name in primary_contracts and len(bytecode) == 0:
|
|
2132
2170
|
msg = f"Contract {contract_name} has no bytecode. " \
|
|
2133
2171
|
f"It may be caused because the contract is abstract, " \
|
|
2134
2172
|
f"or is missing constructor code. Please check the output of the Solidity compiler."
|
|
2135
|
-
if
|
|
2173
|
+
if is_deployed_bytecode:
|
|
2136
2174
|
raise Util.CertoraUserInputError(msg)
|
|
2137
2175
|
else:
|
|
2138
2176
|
build_logger.warning(msg)
|
|
@@ -2202,6 +2240,40 @@ class CertoraBuildGenerator:
|
|
|
2202
2240
|
storage_slot['descriptor'] = type_descriptor.as_dict()
|
|
2203
2241
|
return storage_data
|
|
2204
2242
|
|
|
2243
|
+
@staticmethod
|
|
2244
|
+
def add_vyper_internal_function_data(internal_funcs: Set[Func], contract_data: Dict[str, Any]) -> None:
|
|
2245
|
+
metadata = contract_data["metadata"]
|
|
2246
|
+
symbol_map = contract_data["evm"]["deployedBytecode"]["symbolMap"]
|
|
2247
|
+
|
|
2248
|
+
for internal_func in internal_funcs:
|
|
2249
|
+
# find metadata entry
|
|
2250
|
+
func_name = internal_func.name
|
|
2251
|
+
func_info = metadata['function_info']
|
|
2252
|
+
metadata_func_info = None
|
|
2253
|
+
for value in func_info.values():
|
|
2254
|
+
if value.get("name") == func_name:
|
|
2255
|
+
metadata_func_info = value
|
|
2256
|
+
break
|
|
2257
|
+
assert metadata_func_info is not None, f"Could not find metadata for internal function {func_name}"
|
|
2258
|
+
vyper_metadata = VyperMetadata()
|
|
2259
|
+
if metadata_func_info.get('frame_info'):
|
|
2260
|
+
vyper_metadata.frame_size = metadata_func_info['frame_info']['frame_size']
|
|
2261
|
+
vyper_metadata.frame_start = metadata_func_info['frame_info']['frame_start']
|
|
2262
|
+
if metadata_func_info.get('venom_via_stack'):
|
|
2263
|
+
vyper_metadata.venom_via_stack = metadata_func_info['venom_via_stack']
|
|
2264
|
+
if metadata_func_info.get('venom_return_via_stack'):
|
|
2265
|
+
vyper_metadata.venom_return_via_stack = metadata_func_info['venom_return_via_stack']
|
|
2266
|
+
pattern_in_symbol_map = re.compile(fr"{func_name}\(.*\)_runtime$")
|
|
2267
|
+
matches = [k for k in symbol_map if pattern_in_symbol_map.search(k)]
|
|
2268
|
+
if len(matches) == 0:
|
|
2269
|
+
build_logger.warning(f"Could not find symbol map entry for {func_name} probably was inlined")
|
|
2270
|
+
continue
|
|
2271
|
+
elif len(matches) > 1:
|
|
2272
|
+
raise RuntimeError(f"Found multiple matches for {func_name} in symbol map: {matches}")
|
|
2273
|
+
else:
|
|
2274
|
+
vyper_metadata.runtime_start_pc = symbol_map[matches[0]]
|
|
2275
|
+
internal_func.vyper_metadata = vyper_metadata
|
|
2276
|
+
|
|
2205
2277
|
def get_contract_in_sdc(self,
|
|
2206
2278
|
source_code_file: str,
|
|
2207
2279
|
contract_name: str,
|
|
@@ -2326,6 +2398,9 @@ class CertoraBuildGenerator:
|
|
|
2326
2398
|
else:
|
|
2327
2399
|
compiler_parameters = None
|
|
2328
2400
|
|
|
2401
|
+
if compiler_lang == CompilerLangVy() and compiler_collector_for_contract_file.compiler_version >= (0, 4, 4):
|
|
2402
|
+
self.add_vyper_internal_function_data(internal_funcs, contract_data)
|
|
2403
|
+
|
|
2329
2404
|
return ContractInSDC(contract_name,
|
|
2330
2405
|
# somehow make sure this is an absolute path which obeys the autofinder remappings
|
|
2331
2406
|
# (i.e. make sure this file doesn't start with autoFinder_)
|
|
@@ -2842,6 +2917,8 @@ class CertoraBuildGenerator:
|
|
|
2842
2917
|
|
|
2843
2918
|
self.SDCs[self.get_sdc_key(sdc.primary_contract, sdc.primary_contract_address)] = sdc
|
|
2844
2919
|
|
|
2920
|
+
if self.context.dump_asts:
|
|
2921
|
+
self.dump_asts()
|
|
2845
2922
|
self.handle_links()
|
|
2846
2923
|
self.handle_struct_links()
|
|
2847
2924
|
self.handle_contract_extensions()
|
|
@@ -3002,7 +3079,7 @@ class CertoraBuildGenerator:
|
|
|
3002
3079
|
target_file = self.context.contract_to_file[target.name]
|
|
3003
3080
|
base_contracts = self.retrieve_base_contracts_list(
|
|
3004
3081
|
target_file,
|
|
3005
|
-
Util.abs_posix_path(target_file),
|
|
3082
|
+
target_file if self.context.use_relpaths_for_solc_json else Util.abs_posix_path(target_file),
|
|
3006
3083
|
target.name
|
|
3007
3084
|
)
|
|
3008
3085
|
extensions: Set[str] = set()
|
|
@@ -3078,8 +3155,9 @@ class CertoraBuildGenerator:
|
|
|
3078
3155
|
|
|
3079
3156
|
# let's try to find the AST now
|
|
3080
3157
|
build_file_ast = None
|
|
3158
|
+
orig_path = os.path.relpath(ext_instance.original_file, Path.cwd()) if self.context.use_relpaths_for_solc_json else ext_instance.original_file
|
|
3081
3159
|
for (k, v) in self.asts.items():
|
|
3082
|
-
if
|
|
3160
|
+
if orig_path in v:
|
|
3083
3161
|
build_file_ast = k
|
|
3084
3162
|
break
|
|
3085
3163
|
if build_file_ast is None:
|
|
@@ -3087,10 +3165,10 @@ class CertoraBuildGenerator:
|
|
|
3087
3165
|
|
|
3088
3166
|
contract_def_node = self.get_contract_def_node_ref(
|
|
3089
3167
|
build_file_ast,
|
|
3090
|
-
|
|
3168
|
+
orig_path,
|
|
3091
3169
|
storage_ext
|
|
3092
3170
|
)
|
|
3093
|
-
def_node = self.asts[build_file_ast][
|
|
3171
|
+
def_node = self.asts[build_file_ast][orig_path][contract_def_node]
|
|
3094
3172
|
nodes = def_node.get("nodes")
|
|
3095
3173
|
if not isinstance(nodes, list):
|
|
3096
3174
|
raise RuntimeError(f"Couldn't find nodes for body of {storage_ext}")
|
|
@@ -3261,6 +3339,16 @@ class CertoraBuildGenerator:
|
|
|
3261
3339
|
else:
|
|
3262
3340
|
added_source_finders = {}
|
|
3263
3341
|
|
|
3342
|
+
if self.context.safe_casting_builtin:
|
|
3343
|
+
try:
|
|
3344
|
+
casting_instrumentations, casting_types = generate_casting_instrumentation(self.asts, build_arg_contract_file, sdc_pre_finder)
|
|
3345
|
+
except Exception as e:
|
|
3346
|
+
instrumentation_logger.warning(
|
|
3347
|
+
f"Computing casting instrumentation failed for {build_arg_contract_file}: {e}", exc_info=True)
|
|
3348
|
+
casting_instrumentations, casting_types = {}, {}
|
|
3349
|
+
|
|
3350
|
+
instr = CertoraBuildGenerator.merge_dicts_instrumentation(instr, casting_instrumentations)
|
|
3351
|
+
|
|
3264
3352
|
abs_build_arg_contract_file = Util.abs_posix_path(build_arg_contract_file)
|
|
3265
3353
|
if abs_build_arg_contract_file not in instr:
|
|
3266
3354
|
instrumentation_logger.debug(
|
|
@@ -3314,6 +3402,14 @@ class CertoraBuildGenerator:
|
|
|
3314
3402
|
read_so_far += amt + 1 + to_skip
|
|
3315
3403
|
output.write(in_file.read(-1))
|
|
3316
3404
|
|
|
3405
|
+
if self.context.safe_casting_builtin:
|
|
3406
|
+
library_name, funcs = casting_types.get(contract_file, ("", list()))
|
|
3407
|
+
if len(funcs) > 0:
|
|
3408
|
+
output.write(bytes(f"\nlibrary {library_name}" + "{\n", "utf8"))
|
|
3409
|
+
for f in funcs:
|
|
3410
|
+
output.write(bytes(f, "utf8"))
|
|
3411
|
+
output.write(bytes("}\n", "utf8"))
|
|
3412
|
+
|
|
3317
3413
|
new_file = self.to_autofinder_file(build_arg_contract_file)
|
|
3318
3414
|
self.context.file_to_contract[new_file] = self.context.file_to_contract[
|
|
3319
3415
|
build_arg_contract_file]
|
|
@@ -3789,6 +3885,16 @@ class CertoraBuildGenerator:
|
|
|
3789
3885
|
# Avoiding Python interpreter shutdown exceptions which are safe to ignore
|
|
3790
3886
|
pass
|
|
3791
3887
|
|
|
3888
|
+
def dump_asts(self) -> None:
|
|
3889
|
+
asts_dump_file = Util.get_asts_file()
|
|
3890
|
+
filtered_asts = {k: v for k, v in self.asts.items() if not str(k).startswith(f"{Util.get_build_dir()}")}
|
|
3891
|
+
with asts_dump_file.open("w+") as output_file:
|
|
3892
|
+
try:
|
|
3893
|
+
json.dump(filtered_asts, output_file, indent=4, sort_keys=True)
|
|
3894
|
+
except Exception as e:
|
|
3895
|
+
ast_logger.debug(f"Couldn't dump ASTs to {asts_dump_file}", exc_info=e)
|
|
3896
|
+
raise
|
|
3897
|
+
|
|
3792
3898
|
|
|
3793
3899
|
# make sure each source file exists and its path is in absolute format
|
|
3794
3900
|
def sources_to_abs(sources: Set[Path]) -> Set[Path]:
|
{certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuildCacheManager.py
RENAMED
|
@@ -179,9 +179,12 @@ class CertoraBuildCacheManager:
|
|
|
179
179
|
trg = trg_path_with_additional_included_files / post_autofinder_dir.name
|
|
180
180
|
if post_autofinder_dir != trg:
|
|
181
181
|
if post_autofinder_dir.is_dir():
|
|
182
|
-
if
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
# if we match on the cache, and we run a few certoraRun-s in parallel,
|
|
183
|
+
# it could be this folder already exists. But the exact contents do not matter and are likely to
|
|
184
|
+
# agree with the exception of spec files. So let's merge the folders so that the
|
|
185
|
+
# sources are in any case runnable
|
|
186
|
+
if not trg.exists():
|
|
187
|
+
Util.safe_merge_folder(post_autofinder_dir, trg, shutil.ignore_patterns())
|
|
185
188
|
else:
|
|
186
189
|
# highly unlikely .post_autofinder.[digit] will be a file and not a directory,
|
|
187
190
|
# but would rather not crash and future-proof instead
|
{certora_cli-8.3.1 → certora_cli-8.5.1}/certora_cli/CertoraProver/certoraBuildDataClasses.py
RENAMED
|
@@ -189,6 +189,7 @@ class ContractInSDC:
|
|
|
189
189
|
# why are we using this and not the normal list of functions?
|
|
190
190
|
"internalFunctions": {key: method.as_dict() for key, method in self.function_finders.items()},
|
|
191
191
|
# this doesn't even have all functions
|
|
192
|
+
"internalFuncs": [f.as_dict() for f in self.internal_funcs],
|
|
192
193
|
"allMethods": [f.as_dict() for f in self.all_funcs],
|
|
193
194
|
"solidityTypes": [x.as_dict() for x in self.types],
|
|
194
195
|
"compilerName": "" if not self.compiler_collector else self.compiler_collector.compiler_name,
|
|
@@ -689,8 +689,11 @@ class CloudVerification:
|
|
|
689
689
|
auth_data["mutationTestId"] = self.context.mutation_test_id
|
|
690
690
|
return auth_data
|
|
691
691
|
|
|
692
|
+
def get_group_id_url(self) -> str:
|
|
693
|
+
return f"{self.get_domain()}/?groupIds={self.context.group_id}"
|
|
694
|
+
|
|
692
695
|
def print_group_id_url(self) -> None:
|
|
693
|
-
group_id_url =
|
|
696
|
+
group_id_url = self.get_group_id_url()
|
|
694
697
|
print(f"See all jobs generated from splitting the rules at {group_id_url}", flush=True)
|
|
695
698
|
|
|
696
699
|
def print_output_links(self) -> None:
|