certora-cli-beta-mirror 7.31.0__tar.gz → 8.1.0__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.
Files changed (88) hide show
  1. {certora_cli_beta_mirror-7.31.0/certora_cli_beta_mirror.egg-info → certora_cli_beta_mirror-8.1.0}/PKG-INFO +4 -3
  2. certora_cli_beta_mirror-8.1.0/README.md +1 -0
  3. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +1 -3
  4. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +3 -0
  5. certora_cli_beta_mirror-8.1.0/certora_cli/CertoraProver/certoraApp.py +49 -0
  6. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraBuild.py +197 -44
  7. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraBuildCacheManager.py +2 -0
  8. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraBuildDataClasses.py +3 -1
  9. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraBuildRust.py +32 -21
  10. certora_cli_beta_mirror-7.31.0/certora_cli/Shared/rustProverCommon.py → certora_cli_beta_mirror-8.1.0/certora_cli/CertoraProver/certoraBuildSui.py +24 -18
  11. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraCloudIO.py +37 -8
  12. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +1 -1
  13. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +20 -5
  14. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraConfigIO.py +23 -22
  15. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraContext.py +77 -45
  16. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraContextAttributes.py +122 -195
  17. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraContextValidator.py +78 -49
  18. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraContractFuncs.py +6 -0
  19. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraType.py +10 -1
  20. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraVerifyGenerator.py +10 -4
  21. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/splitRules.py +20 -17
  22. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/storageExtension.py +0 -35
  23. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/equivCheck.py +2 -1
  24. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Mutate/mutateApp.py +28 -16
  25. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Mutate/mutateAttributes.py +11 -0
  26. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Mutate/mutateValidate.py +2 -2
  27. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/certoraAttrUtil.py +11 -5
  28. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/certoraUtils.py +99 -35
  29. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/certoraValidateFuncs.py +24 -12
  30. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/proverCommon.py +10 -8
  31. certora_cli_beta_mirror-8.1.0/certora_cli/certoraCVLFormatter.py +76 -0
  32. certora_cli_beta_mirror-8.1.0/certora_cli/certoraConcord.py +39 -0
  33. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/certoraEVMProver.py +2 -2
  34. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/certoraRanger.py +2 -2
  35. certora_cli_beta_mirror-8.1.0/certora_cli/certoraRun.py +178 -0
  36. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/certoraSolanaProver.py +3 -3
  37. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/certoraSorobanProver.py +3 -4
  38. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0/certora_cli_beta_mirror.egg-info}/PKG-INFO +4 -3
  39. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli_beta_mirror.egg-info/SOURCES.txt +5 -1
  40. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli_beta_mirror.egg-info/entry_points.txt +1 -0
  41. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli_beta_mirror.egg-info/requires.txt +1 -0
  42. certora_cli_beta_mirror-8.1.0/certora_jars/ASTExtraction.jar +0 -0
  43. certora_cli_beta_mirror-8.1.0/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  44. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_jars/Typechecker.jar +0 -0
  45. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/setup.py +6 -5
  46. certora_cli_beta_mirror-7.31.0/README.md +0 -1
  47. certora_cli_beta_mirror-7.31.0/certora_cli/certoraRun.py +0 -218
  48. certora_cli_beta_mirror-7.31.0/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  49. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/LICENSE +0 -0
  50. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/MANIFEST.in +0 -0
  51. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_bins/__init__.py +0 -0
  52. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
  53. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
  54. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
  55. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
  56. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
  57. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/__init__.py +0 -0
  58. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
  59. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
  60. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
  61. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraJobList.py +0 -0
  62. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
  63. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
  64. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraParseBuildScript.py +0 -0
  65. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
  66. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
  67. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/CertoraProver/erc7201.py +0 -0
  68. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  69. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  70. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  71. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  72. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  73. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  74. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  75. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Mutate/__init__.py +0 -0
  76. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Mutate/mutateConstants.py +0 -0
  77. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Mutate/mutateUtil.py +0 -0
  78. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/ExpectedComparator.py +0 -0
  79. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/__init__.py +0 -0
  80. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/Shared/certoraLogging.py +0 -0
  81. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/__init__.py +0 -0
  82. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/certoraEqCheck.py +0 -0
  83. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/certoraMutate.py +0 -0
  84. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli/rustMutator.py +0 -0
  85. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli_beta_mirror.egg-info/dependency_links.txt +0 -0
  86. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_cli_beta_mirror.egg-info/top_level.txt +0 -0
  87. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/certora_jars/__init__.py +0 -0
  88. {certora_cli_beta_mirror-7.31.0 → certora_cli_beta_mirror-8.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: certora-cli-beta-mirror
3
- Version: 7.31.0
3
+ Version: 8.1.0
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli-beta-mirror
6
6
  Author: Certora
@@ -11,7 +11,7 @@ Project-URL: Source, https://github.com/Certora/CertoraProver
11
11
  Classifier: Programming Language :: Python :: 3
12
12
  Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
13
13
  Classifier: Operating System :: OS Independent
14
- Requires-Python: >=3.8
14
+ Requires-Python: >=3.9
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
17
  Requires-Dist: click
@@ -26,6 +26,7 @@ Requires-Dist: StrEnum
26
26
  Requires-Dist: universalmutator
27
27
  Requires-Dist: jinja2
28
28
  Requires-Dist: wcmatch
29
+ Requires-Dist: typing_extensions
29
30
  Dynamic: author
30
31
  Dynamic: author-email
31
32
  Dynamic: classifier
@@ -38,4 +39,4 @@ Dynamic: requires-dist
38
39
  Dynamic: requires-python
39
40
  Dynamic: summary
40
41
 
41
- Commit 89159ec. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
42
+ Commit b6b9f58. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit b6b9f58. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -51,10 +51,8 @@ def get_relevant_compiler(contract_file_path: Path, context: CertoraContext) ->
51
51
 
52
52
  if context.compiler_map:
53
53
  match = Ctx.get_map_attribute_value(context, contract_file_path, 'compiler')
54
- assert isinstance(match, str), (f"In the attribute compiler_map, {contract_file_path} expected to be matched "
55
- f"to a string, {match} is of type {type(match)} not a string")
56
54
  if match:
57
- return match
55
+ return str(match)
58
56
  else:
59
57
  raise RuntimeError(f'cannot match compiler to {contract_file_path} from compiler_map')
60
58
 
@@ -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,49 @@
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 SolanaApp(RustAppClass):
37
+ attr_class = Attrs.SolanaProverAttributes
38
+
39
+ class SorobanApp(RustAppClass):
40
+ attr_class = Attrs.SorobanProverAttributes
41
+
42
+ class RangerApp(EvmAppClass):
43
+ attr_class = Attrs.RangerAttributes
44
+
45
+ class ConcordApp(EvmAppClass):
46
+ attr_class = Attrs.ConcordAttributes
47
+
48
+ class SuiApp(EvmAppClass):
49
+ attr_class = Attrs.SuiProverAttributes
@@ -39,8 +39,6 @@ from CertoraProver.certoraSourceFinders import add_source_finders
39
39
  from CertoraProver.certoraVerifyGenerator import CertoraVerifyGenerator
40
40
  from CertoraProver.certoraContractFuncs import Func, InternalFunc, STATEMUT, SourceBytes
41
41
 
42
- from Shared.certoraUtils import is_relative_to
43
-
44
42
  scripts_dir_path = Path(__file__).parent.parent.resolve() # containing directory
45
43
  sys.path.insert(0, str(scripts_dir_path))
46
44
  from CertoraProver.Compiler.CompilerCollector import CompilerLang, CompilerCollector
@@ -454,8 +452,8 @@ def convert_pathname_to_posix(json_dict: Dict[str, Any], entry: str, smart_contr
454
452
  # protecting against long strings
455
453
  if len(json_dict_str) > 200:
456
454
  json_dict_str = json_dict_str[:200] + '...'
457
- fatal_error(compiler_logger, f"The path of the source file {file_path}"
458
- f"in the standard json file {json_dict_str} does not exist")
455
+ fatal_error(compiler_logger, f"The path of the source file {file_path} "
456
+ f"in the standard json file does not exist!\n{json_dict_str} ")
459
457
  json_dict[entry] = json_dict_posix_paths
460
458
 
461
459
 
@@ -836,6 +834,7 @@ class CertoraBuildGenerator:
836
834
  body_location = body_node["src"]
837
835
  elif body_node is None and func_def["implemented"]:
838
836
  ast_logger.debug(f"No body for {func_def} but ast claims it is implemented")
837
+ location: Optional[str] = func_def.get("src", None)
839
838
 
840
839
  if original_contract is not None:
841
840
  if method := original_contract.find_method(func_name, solidity_type_args):
@@ -860,14 +859,17 @@ class CertoraBuildGenerator:
860
859
  func_def[STATEMUT] in ["nonpayable", "view", "pure"],
861
860
  c_is_lib,
862
861
  is_constructor,
862
+ func_def.get("kind") == CertoraBuildGenerator.FREEFUNCTION_STRING,
863
863
  func_def[STATEMUT],
864
864
  func_visibility,
865
865
  func_def["implemented"],
866
866
  func_def.get("overrides", None) is not None,
867
+ func_def.get("virtual", False),
867
868
  contract_name,
868
869
  source_bytes,
869
870
  ast_id=func_def.get("id", None),
870
871
  original_file=original_file,
872
+ location=location,
871
873
  body_location=body_location,
872
874
  )
873
875
 
@@ -919,15 +921,18 @@ class CertoraBuildGenerator:
919
921
  notpayable=is_not_payable,
920
922
  fromLib=c_is_lib,
921
923
  isConstructor=False,
924
+ is_free_func=False,
922
925
  stateMutability=state_mutability,
923
926
  implemented=True,
924
927
  overrides=public_state_var.get("overrides", None) is not None,
928
+ virtual=False,
925
929
  # according to Solidity docs, getter functions have external visibility
926
930
  visibility="external",
927
931
  contractName=contract_name,
928
932
  source_bytes=SourceBytes.from_ast_node(public_state_var),
929
933
  ast_id=None,
930
934
  original_file=c_file,
935
+ location=None,
931
936
  body_location=None,
932
937
  )
933
938
  )
@@ -961,6 +966,8 @@ class CertoraBuildGenerator:
961
966
  ret = solc_type == "address"
962
967
  elif isinstance(ct_type, CT.StructType):
963
968
  ret = solc_type == "tuple"
969
+ elif isinstance(ct_type, CT.EnumType):
970
+ ret = solc_type == "uint8"
964
971
  return ret
965
972
 
966
973
  fs = [f for f in fs if all(compareTypes(a.type, i)
@@ -981,7 +988,7 @@ class CertoraBuildGenerator:
981
988
  assert len(f.returns) == len(fabi["outputs"]), \
982
989
  f"function collected for {fabi['name']} has the wrong number of return values"
983
990
  assert all(compareTypes(a.type, i) for a, i in zip(f.returns, fabi["outputs"])), \
984
- f"function collected for {fabi['name']} has the wrong types of return values"
991
+ f"function collected for {fabi['name']} has the wrong types of return values. {[t.type.type_string for t in f.returns]} vs {fabi['outputs']}"
985
992
 
986
993
  verify_collected_all_abi_funcs(
987
994
  [f for f in data["abi"] if f["type"] == "function"],
@@ -1377,7 +1384,9 @@ class CertoraBuildGenerator:
1377
1384
 
1378
1385
  def get_solc_optimize_value(self, contract_file_path: Path) -> Optional[str]:
1379
1386
  match = Ctx.get_map_attribute_value(self.context, contract_file_path, 'solc_optimize')
1380
- assert isinstance(match, (str, type(None))), f"Expected solc_optimize to be string or None, got {type(match)}"
1387
+ assert isinstance(match, (str, int, type(None))), f"Expected solc_optimize to be string, integer , got {type(match)}"
1388
+ if isinstance(match, int):
1389
+ match = str(match)
1381
1390
  return match
1382
1391
 
1383
1392
  def _handle_via_ir(self, contract_file_path: Path, settings_dict: Dict[str, Any]) -> None:
@@ -1432,8 +1441,11 @@ class CertoraBuildGenerator:
1432
1441
  compiler_version = compiler_collector.compiler_version
1433
1442
  major, minor, patch = compiler_version
1434
1443
 
1435
- err_msg = "--finder_friendly_optimizer option supports solc versions 0.6.7 - 0.8.25 only, " \
1436
- f"got {major}.{minor}.{patch}"
1444
+ err_msg = \
1445
+ f"Unsupported solc version {major}.{minor}.{patch} for `solc_via_ir`. " \
1446
+ f"Supported versions: 0.6.7 – 0.8.25.\n" \
1447
+ f"Use `solc_via_ir_map` to disable `solc_via_ir` for specific files with older compiler versions."
1448
+
1437
1449
  yul_optimizer_steps = None
1438
1450
  if major != 0:
1439
1451
  raise Util.CertoraUserInputError(err_msg)
@@ -2216,14 +2228,17 @@ class CertoraBuildGenerator:
2216
2228
  notpayable=x.notpayable,
2217
2229
  fromLib=x.fromLib,
2218
2230
  isConstructor=x.isConstructor,
2231
+ is_free_func=False,
2219
2232
  stateMutability=x.stateMutability,
2220
2233
  visibility=x.visibility,
2221
2234
  implemented=x.implemented,
2222
2235
  overrides=x.overrides,
2236
+ virtual=False,
2223
2237
  contractName=x.contractName,
2224
2238
  ast_id=x.ast_id,
2225
2239
  source_bytes=x.source_bytes,
2226
2240
  original_file=x.original_file,
2241
+ location=None,
2227
2242
  body_location=x.body_location,
2228
2243
  ) for x in clfuncs]
2229
2244
  elif compiler_lang == CompilerLangYul():
@@ -2490,6 +2505,114 @@ class CertoraBuildGenerator:
2490
2505
  mut=InsertAfter())
2491
2506
  return function_finder_by_contract, function_finder_instrumentation
2492
2507
 
2508
+ def add_internal_func_harnesses(self, contract_file: str, sdc: SDC, specCalls: List[str]) -> Optional[Tuple[Dict[str, str], Dict[str, Dict[int, Instrumentation]]]]:
2509
+ # contract file -> byte offset -> to insert
2510
+ harness_function_instrumentation: Dict[str, Dict[int, Instrumentation]] = defaultdict(dict)
2511
+ # internal function name -> harness fuction name
2512
+ harness_function_names: Dict[str, str] = {}
2513
+
2514
+ if not isinstance(sdc.compiler_collector, CompilerCollectorSol):
2515
+ raise Exception(f"Encountered a compiler collector that is not solc for file {contract_file}"
2516
+ " when trying to add function autofinders")
2517
+ instrumentation_logger.debug(f"Using {sdc.compiler_collector} compiler to "
2518
+ f"add external function harnesses to contract {sdc.primary_contract}")
2519
+
2520
+ for c in sdc.contracts:
2521
+ for f in c.internal_funcs:
2522
+ if f"{sdc.primary_contract}.{f.name}" not in specCalls:
2523
+ continue
2524
+
2525
+ if f.fromLib:
2526
+ # Even external library functions can't be called directly from spec,
2527
+ # so skip harnessing internal ones.
2528
+ continue
2529
+
2530
+ if f.isConstructor:
2531
+ continue
2532
+
2533
+ if f.is_free_func:
2534
+ # Free functions (declared outside of any contract/library) don't have visibility modifiers
2535
+ continue
2536
+
2537
+ orig_file = f.original_file
2538
+ if not orig_file:
2539
+ instrumentation_logger.debug(f"missing file location for {f.name}")
2540
+ continue
2541
+
2542
+ loc = f.location
2543
+ if not loc:
2544
+ instrumentation_logger.debug(f"Found a function {f.name} in "
2545
+ f"{c.name} that doesn't have a location")
2546
+ continue
2547
+
2548
+ if f.implemented:
2549
+ expected_end_char = b"}"
2550
+ else:
2551
+ # This is a virtual function with no implementation. Declare also a virtual harness so that when
2552
+ # Implementing a harness for the override we can just add the `override` keyword to the harness
2553
+ # function as well without needing to have any extra logic at that point.
2554
+ expected_end_char = b";"
2555
+
2556
+ if len(f.fullArgs) != len(f.paramNames):
2557
+ instrumentation_logger.debug(f"Do not have argument names for {f.name} in"
2558
+ f" {c.name}, giving up internal function harnessing")
2559
+ continue
2560
+
2561
+ if any(ty.location == CT.TypeLocation.STORAGE for ty in f.fullArgs + f.returns):
2562
+ instrumentation_logger.debug(f"Function {f.name} has input arguments with 'storage' location - cannot harness it")
2563
+ continue
2564
+
2565
+ instrumentation_path = str(Util.abs_norm_path(orig_file))
2566
+ per_file_inst = harness_function_instrumentation[instrumentation_path]
2567
+
2568
+ start, size, _ = loc.split(":")
2569
+ body_end_byte = int(start) + int(size) - 1
2570
+
2571
+ def get_local_type_name(ty: CT.TypeInstance) -> str:
2572
+ # Handles imports that use 'as'. E.g. `import {A as B} from "A.sol";`
2573
+ ret = ty.get_source_str()
2574
+ assert orig_file
2575
+ for node in self.asts[sdc.sdc_origin_file][orig_file].values():
2576
+ if node["nodeType"] != "ImportDirective":
2577
+ continue
2578
+ for alias in node["symbolAliases"]:
2579
+ if alias["foreign"]["name"] == ty.get_source_str() and "local" in alias:
2580
+ ret = alias["local"]
2581
+ break
2582
+
2583
+ # Now add the location
2584
+ return ret + (f" {ty.location.value}" if ty.location != CT.TypeLocation.STACK else "")
2585
+
2586
+ harness_name = f"{f.name}_external_harness"
2587
+ harness_string = f" function {harness_name}({', '.join(f'{get_local_type_name(ty)} {n}' for ty, n in zip(f.fullArgs, f.paramNames))}) external"
2588
+
2589
+ if f.stateMutability in ["pure", "view"]:
2590
+ harness_string += f" {f.stateMutability}"
2591
+
2592
+ if f.virtual:
2593
+ harness_string += " virtual"
2594
+
2595
+ if f.overrides:
2596
+ harness_string += " override"
2597
+
2598
+ if f.returns:
2599
+ harness_string += f" returns ({', '.join(get_local_type_name(r) for r in f.returns)})"
2600
+
2601
+ if f.implemented:
2602
+ harness_call = f"{f.name}({', '.join(f.paramNames)})"
2603
+ if f.returns:
2604
+ harness_string += f" {{ return {harness_call}; }}"
2605
+ else:
2606
+ harness_string += f" {{ {harness_call}; }}"
2607
+ else:
2608
+ harness_string += ";"
2609
+
2610
+ per_file_inst[body_end_byte] = Instrumentation(expected=expected_end_char, to_ins=harness_string,
2611
+ mut=InsertAfter())
2612
+ harness_function_names[f.name] = harness_name
2613
+
2614
+ return harness_function_names, harness_function_instrumentation
2615
+
2493
2616
  def cleanup(self) -> None:
2494
2617
  for sdc_name, smart_contract_lang in self.__compiled_artifacts_to_clean:
2495
2618
  self.cleanup_compiler_outputs(sdc_name, smart_contract_lang)
@@ -2545,6 +2668,16 @@ class CertoraBuildGenerator:
2545
2668
 
2546
2669
  def build(self, certora_verify_generator: CertoraVerifyGenerator) -> None:
2547
2670
  context = self.context
2671
+
2672
+ specCalls: List[str] = []
2673
+ if context.verify and not context.disallow_internal_function_calls:
2674
+ with tempfile.NamedTemporaryFile("r", dir=Util.get_build_dir()) as tmp_file:
2675
+ try:
2676
+ Ctx.run_local_spec_check(False, context, ["-listCalls", tmp_file.name], print_errors=False)
2677
+ specCalls = tmp_file.read().split("\n")
2678
+ except Exception as e:
2679
+ instrumentation_logger.warning(f"Failed to get calls from spec\n{e}")
2680
+
2548
2681
  self.context.remappings = []
2549
2682
  for i, build_arg_contract_file in enumerate(sorted(self.input_config.sorted_files)):
2550
2683
  build_logger.debug(f"\nbuilding file {build_arg_contract_file}")
@@ -2596,7 +2729,7 @@ class CertoraBuildGenerator:
2596
2729
  # We start by trying to instrument _all_ finders, both autofinders and source finders
2597
2730
  added_finders, all_finders_success, src_finders_gen_success, post_backup_dir = self.finders_compilation_round(
2598
2731
  build_arg_contract_file, i, ignore_patterns, path_for_compiler_collector_file, pre_backup_dir,
2599
- sdc_pre_finders, not context.disable_source_finders)
2732
+ sdc_pre_finders, not context.disable_source_finders, specCalls)
2600
2733
 
2601
2734
  # we could have a case where source finders failed but regular finders succeeded.
2602
2735
  # e.g. if we processed the AST wrong and skipped source finders generation
@@ -2608,21 +2741,21 @@ class CertoraBuildGenerator:
2608
2741
  # let's try just the function autofinders
2609
2742
  added_finders, function_autofinder_success, _, post_backup_dir = self.finders_compilation_round(
2610
2743
  build_arg_contract_file, i, ignore_patterns, path_for_compiler_collector_file, pre_backup_dir,
2611
- sdc_pre_finders, False)
2744
+ sdc_pre_finders, False, specCalls)
2612
2745
 
2613
2746
  if not function_autofinder_success:
2614
2747
  self.auto_finders_failed = True
2615
2748
 
2616
2749
  if not self.auto_finders_failed or not self.source_finders_failed:
2617
2750
  # setup source_dir. note that post_backup_dir must include the finders in this case
2618
- for _, _, sdc in added_finders:
2751
+ for _, _, _, sdc in added_finders:
2619
2752
  sdc.source_dir = str(post_backup_dir.relative_to(Util.get_certora_sources_dir()))
2620
2753
  sdc.orig_source_dir = str(pre_backup_dir.relative_to(Util.get_certora_sources_dir()))
2621
2754
  else:
2622
2755
  # no point in running autofinders in vyper right now
2623
- added_finders = [({}, {}, sdc_pre_finder) for sdc_pre_finder in sdc_pre_finders]
2756
+ added_finders = [({}, {}, {}, sdc_pre_finder) for sdc_pre_finder in sdc_pre_finders]
2624
2757
 
2625
- for added_func_finders, added_source_finders, sdc in added_finders:
2758
+ for added_func_finders, added_source_finders, added_internal_function_harnesses, sdc in added_finders:
2626
2759
  for contract in sdc.contracts:
2627
2760
  all_functions: List[Func] = list()
2628
2761
  for k, v in added_func_finders.items():
@@ -2630,6 +2763,8 @@ class CertoraBuildGenerator:
2630
2763
  contract.function_finders[k] = v
2631
2764
  for source_key, source_value in added_source_finders.items():
2632
2765
  contract.local_assignments[source_key] = source_value
2766
+ if contract.name == sdc.primary_contract:
2767
+ contract.internal_function_harnesses = added_internal_function_harnesses
2633
2768
  all_functions.extend(contract.methods)
2634
2769
  all_functions.extend(contract.internal_funcs)
2635
2770
  functions_unique_by_internal_rep = list() # type: List[Func]
@@ -2690,7 +2825,7 @@ class CertoraBuildGenerator:
2690
2825
  self.handle_erc7201_annotations()
2691
2826
  self.handle_storage_extension_harnesses()
2692
2827
 
2693
- def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage]) -> NewStorageInfo:
2828
+ def extract_slayout(self, original_file: str, ns_storage: Set[NameSpacedStorage], compiler_version: str) -> NewStorageInfo:
2694
2829
  """
2695
2830
  Given a file containing a contract with namespaced storage, extract the storage information
2696
2831
  corresponding to the namespaced types.
@@ -2715,7 +2850,9 @@ class CertoraBuildGenerator:
2715
2850
  # original file is accessed also in the actual code, and compiling it
2716
2851
  # directly can cause issues.
2717
2852
  tmp_file.write(f"import \"{original_file}\";\n\n")
2718
-
2853
+ rel_path = os.path.relpath(tmp_file.name, Path.cwd())
2854
+ if self.context.compiler_map:
2855
+ self.context.compiler_map.update({rel_path: compiler_version}, last=False)
2719
2856
  # Write the harness contract with dummy fields for each namespaced storage
2720
2857
  var_to_slot = storageExtension.write_harness_contract(tmp_file, harness_name, ns_storage)
2721
2858
  tmp_file.flush()
@@ -2728,13 +2865,6 @@ class CertoraBuildGenerator:
2728
2865
  build_dir / f"{Path(original_file).stem}_storage_extension.sol"
2729
2866
  )
2730
2867
 
2731
- # Add harness to compiler map
2732
- storageExtension.add_harness_to_compiler_map(
2733
- original_file,
2734
- tmp_file,
2735
- self.context
2736
- )
2737
-
2738
2868
  # normalize the path exactly the way collect_for_file expects it:
2739
2869
  abs_path = Util.abs_posix_path(tmp_file.name)
2740
2870
  self.context.file_to_contract[abs_path] = {harness_name}
@@ -2802,7 +2932,8 @@ class CertoraBuildGenerator:
2802
2932
  continue
2803
2933
 
2804
2934
  # Now that we have all the storage layout information, extract it once
2805
- slayouts[key] = self.extract_slayout(imported_file, ns_storage)
2935
+ slayouts[key] = self.extract_slayout(imported_file, ns_storage,
2936
+ get_relevant_compiler(Path(target_file), self.context))
2806
2937
 
2807
2938
  if self.context.test == str(Util.TestValue.STORAGE_EXTENSION_LAYOUT):
2808
2939
  raise Util.TestResultsReady(slayouts)
@@ -2957,12 +3088,13 @@ class CertoraBuildGenerator:
2957
3088
  path_for_compiler_collector_file: str,
2958
3089
  pre_backup_dir: Path,
2959
3090
  sdc_pre_finders: List[SDC],
2960
- with_source_finders: bool) -> Tuple[
2961
- List[Tuple[Dict[str, InternalFunc], Dict[str, UnspecializedSourceFinder], SDC]], bool, bool, Path]:
3091
+ with_source_finders: bool,
3092
+ specCalls: List[str]) -> Tuple[
3093
+ List[Tuple[Dict[str, InternalFunc], Dict[str, UnspecializedSourceFinder], Dict[str, str], SDC]], bool, bool, Path]:
2962
3094
  added_finders_to_sdc, finders_compilation_success, source_finders_gen_success = \
2963
3095
  self.instrument_auto_finders(
2964
3096
  build_arg_contract_file, i, sdc_pre_finders,
2965
- path_for_compiler_collector_file, with_source_finders)
3097
+ path_for_compiler_collector_file, with_source_finders, specCalls)
2966
3098
  # successful or not, we backup current .certora_sources for either debuggability, or for availability
2967
3099
  # of sources.
2968
3100
  post_backup_dir = self.get_fresh_backupdir(Util.POST_AUTOFINDER_BACKUP_DIR)
@@ -3034,11 +3166,12 @@ class CertoraBuildGenerator:
3034
3166
  def instrument_auto_finders(self, build_arg_contract_file: str, i: int,
3035
3167
  sdc_pre_finders: List[SDC],
3036
3168
  path_for_compiler_collector_file: str,
3037
- instrument_source_finders: bool) -> Tuple[
3038
- List[Tuple[Dict[str, InternalFunc], Dict[str, UnspecializedSourceFinder], SDC]], bool, bool]:
3169
+ instrument_source_finders: bool,
3170
+ specCalls: List[str]) -> Tuple[
3171
+ List[Tuple[Dict[str, InternalFunc], Dict[str, UnspecializedSourceFinder], Dict[str, str], SDC]], bool, bool]:
3039
3172
 
3040
3173
  # initialization
3041
- ret = [] # type: List[Tuple[Dict[str, InternalFunc], Dict[str, UnspecializedSourceFinder], SDC]]
3174
+ ret: List[Tuple[Dict[str, InternalFunc], Dict[str, UnspecializedSourceFinder], Dict[str, str], SDC]] = []
3042
3175
  instrumentation_logger.debug(f"Instrumenting auto finders in {build_arg_contract_file}")
3043
3176
  # all of the [SDC]s inside [sdc_pre_finders] have the same list of [ContractInSDC]s
3044
3177
  # (generated in the [collect_from_file] function).
@@ -3047,10 +3180,19 @@ class CertoraBuildGenerator:
3047
3180
  if added_function_finders_tuple is None:
3048
3181
  instrumentation_logger.warning(
3049
3182
  f"Computing function finder instrumentation failed for {build_arg_contract_file}")
3050
- return [({}, {}, old_sdc) for old_sdc in sdc_pre_finders], False, False
3183
+ return [({}, {}, {}, old_sdc) for old_sdc in sdc_pre_finders], False, False
3051
3184
 
3052
3185
  (added_function_finders, function_instr) = added_function_finders_tuple
3053
3186
 
3187
+ instr = function_instr
3188
+
3189
+ added_internal_function_harnesses: Dict[str, str] = {}
3190
+ if not self.context.disallow_internal_function_calls:
3191
+ added_internal_func_harness_tuple = self.add_internal_func_harnesses(build_arg_contract_file, sdc_pre_finder, specCalls)
3192
+ if added_internal_func_harness_tuple:
3193
+ instr = CertoraBuildGenerator.merge_dicts_instrumentation(function_instr, added_internal_func_harness_tuple[1])
3194
+ added_internal_function_harnesses = added_internal_func_harness_tuple[0]
3195
+
3054
3196
  source_finders_gen_succeeded = False
3055
3197
  if instrument_source_finders:
3056
3198
  try:
@@ -3059,15 +3201,13 @@ class CertoraBuildGenerator:
3059
3201
  (added_source_finders, source_instr) = added_source_finders_tuple
3060
3202
  # Update instr with additional instrumentations. Recall it is a map file -> offset -> instr.
3061
3203
  # Function finders take precedence
3062
- instr = CertoraBuildGenerator.merge_dicts_instrumentation(function_instr, source_instr)
3204
+ instr = CertoraBuildGenerator.merge_dicts_instrumentation(instr, source_instr)
3063
3205
  source_finders_gen_succeeded = True
3064
3206
  except: # noqa: E722
3065
3207
  instrumentation_logger.warning(
3066
3208
  f"Computing source finder instrumentation failed for {build_arg_contract_file}")
3067
- instr = function_instr
3068
3209
  added_source_finders = {}
3069
3210
  else:
3070
- instr = function_instr
3071
3211
  added_source_finders = {}
3072
3212
 
3073
3213
  abs_build_arg_contract_file = Util.abs_posix_path(build_arg_contract_file)
@@ -3087,7 +3227,7 @@ class CertoraBuildGenerator:
3087
3227
  # instrumentation should be keyed only using absolute paths
3088
3228
  instrumentation_logger.warning(f"Already generated autofinder for {new_name}, "
3089
3229
  f"cannot instrument again for {contract_file}")
3090
- return [({}, {}, old_sdc) for old_sdc in sdc_pre_finders], False, False
3230
+ return [({}, {}, {}, old_sdc) for old_sdc in sdc_pre_finders], False, False
3091
3231
 
3092
3232
  autofinder_remappings[new_name] = contract_file
3093
3233
 
@@ -3116,7 +3256,7 @@ class CertoraBuildGenerator:
3116
3256
  instrumentation_logger.warning("Skipping source finder generation!")
3117
3257
  else:
3118
3258
  instrumentation_logger.warning("Skipping internal function finder generation!")
3119
- return [({}, {}, old_sdc) for old_sdc in sdc_pre_finders], False, False
3259
+ return [({}, {}, {}, old_sdc) for old_sdc in sdc_pre_finders], False, False
3120
3260
  to_skip = to_insert.mut.insert(to_insert.to_ins, to_insert.expected, output)
3121
3261
  if to_skip != 0:
3122
3262
  in_file.read(to_skip)
@@ -3128,7 +3268,7 @@ class CertoraBuildGenerator:
3128
3268
  build_arg_contract_file]
3129
3269
 
3130
3270
  # add generated file to map attributes
3131
- for map_attr in Attrs.get_attribute_class().all_map_attrs():
3271
+ for map_attr in self.context.app.attr_class.all_map_attrs():
3132
3272
  map_attr_value = getattr(self.context, map_attr)
3133
3273
  if map_attr_value and build_arg_contract_file in map_attr_value:
3134
3274
  map_attr_value[new_file] = map_attr_value[build_arg_contract_file]
@@ -3154,7 +3294,7 @@ class CertoraBuildGenerator:
3154
3294
  fail_on_compilation_error=False,
3155
3295
  reroute_main_path=True)
3156
3296
  for new_sdc in new_sdcs:
3157
- ret.append((added_function_finders, added_source_finders, new_sdc))
3297
+ ret.append((added_function_finders, added_source_finders, added_internal_function_harnesses, new_sdc))
3158
3298
 
3159
3299
  except Util.SolcCompilationException as e:
3160
3300
  print(f"Encountered an exception generating autofinder {new_file} ({e}), falling back to original "
@@ -3163,7 +3303,7 @@ class CertoraBuildGenerator:
3163
3303
  f"falling back to the original file {Path(build_arg_contract_file).name}", exc_info=e)
3164
3304
  # clean up mutation
3165
3305
  self.function_finder_file_remappings = {}
3166
- return [({}, {}, sdc_pre_finder) for sdc_pre_finder in sdc_pre_finders], False, False
3306
+ return [({}, {}, {}, sdc_pre_finder) for sdc_pre_finder in sdc_pre_finders], False, False
3167
3307
  return ret, True, source_finders_gen_succeeded
3168
3308
 
3169
3309
  def to_autofinder_file(self, contract_file: str) -> str:
@@ -3558,6 +3698,15 @@ class CertoraBuildGenerator:
3558
3698
  add_to_sources(Util.PACKAGE_FILE)
3559
3699
  if Util.REMAPPINGS_FILE.exists():
3560
3700
  add_to_sources(Util.REMAPPINGS_FILE)
3701
+ foundry_toml = Util.find_nearest_foundry_toml()
3702
+ if foundry_toml:
3703
+ # if we find foundry.toml we add it to source tree and, if exists, the remappings.txt file from the
3704
+ # root directory
3705
+ foundry_root = foundry_toml.parent
3706
+ add_to_sources(foundry_root / Util.FOUNDRY_TOML_FILE)
3707
+ remappings_file_in_root = foundry_root / Util.REMAPPINGS_FILE
3708
+ if remappings_file_in_root.exists():
3709
+ add_to_sources(remappings_file_in_root)
3561
3710
  if context.bytecode_jsons:
3562
3711
  for file in context.bytecode_jsons:
3563
3712
  add_to_sources(Path(file))
@@ -3583,7 +3732,11 @@ class CertoraBuildGenerator:
3583
3732
  return sources
3584
3733
 
3585
3734
  def __del__(self) -> None:
3586
- self.cleanup()
3735
+ try:
3736
+ self.cleanup()
3737
+ except ImportError:
3738
+ # Avoiding Python interpreter shutdown exceptions which are safe to ignore
3739
+ pass
3587
3740
 
3588
3741
 
3589
3742
  # make sure each source file exists and its path is in absolute format
@@ -3698,7 +3851,7 @@ def build_from_scratch(context: CertoraContext,
3698
3851
  # the contract files in SDCs are relative to .certora_sources. Which isn't good for us here.
3699
3852
  # Need to be relative to original paths
3700
3853
  for f in paths_set:
3701
- if is_relative_to(f, absolute_sources_dir):
3854
+ if f.is_relative_to(absolute_sources_dir):
3702
3855
  rel_f = f.relative_to(absolute_sources_dir)
3703
3856
  else:
3704
3857
  # may be an absolute path already outside .certora_sources, so we can keep it.
@@ -3810,11 +3963,11 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3810
3963
 
3811
3964
  # Start by syntax checking, if we're in the right mode
3812
3965
  if Cv.mode_has_spec_file(context) and not context.build_only and not ignore_spec_syntax_check:
3813
- if context.disable_local_typechecking:
3966
+ if Ctx.should_run_local_speck_check(context):
3967
+ Ctx.run_local_spec_check(False, context)
3968
+ else:
3814
3969
  build_logger.warning(
3815
3970
  "Local checks of CVL specification files disabled. It is recommended to enable the checks.")
3816
- else:
3817
- Ctx.run_local_spec_check(False, context)
3818
3971
 
3819
3972
  cache_hit, build_cache_enabled, cached_files = build_from_cache_or_scratch(context,
3820
3973
  certora_build_generator,
@@ -3822,7 +3975,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3822
3975
 
3823
3976
  # avoid running the same test over and over again for each split run, context.split_rules is true only for
3824
3977
  # the first run and is set to [] for split runs
3825
- if context.split_rules:
3978
+ if Ctx.should_run_local_speck_check(context) and context.split_rules:
3826
3979
  Ctx.run_local_spec_check(True, context)
3827
3980
 
3828
3981
  # .certora_verify.json is always constructed even if build cache is enabled
@@ -179,6 +179,8 @@ 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 trg.exists():
183
+ shutil.rmtree(trg)
182
184
  Util.safe_copy_folder(post_autofinder_dir, trg, shutil.ignore_patterns())
183
185
  else:
184
186
  # highly unlikely .post_autofinder.[digit] will be a file and not a directory,
@@ -163,6 +163,7 @@ class ContractInSDC:
163
163
  self.local_assignments = local_assignments
164
164
  self.branches = branches
165
165
  self.requires = requires
166
+ self.internal_function_harnesses: Dict[str, str] = {}
166
167
 
167
168
  def as_dict(self) -> Dict[str, Any]:
168
169
  """
@@ -196,7 +197,8 @@ class ContractInSDC:
196
197
  "sourceBytes": None if self.source_bytes is None else self.source_bytes.as_dict(),
197
198
  "extensionContracts": [e.as_dict() for e in self.extension_contracts],
198
199
  "localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()},
199
- "internalFunctionStarts": self.internal_starts
200
+ "internalFunctionStarts": self.internal_starts,
201
+ "internalFunctionHarnesses": self.internal_function_harnesses
200
202
  }
201
203
  # "sourceHints": {"localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()},
202
204
  # "branches": {k: v.as_dict() for k, v in self.branches.items()},