certora-cli-beta-mirror 7.27.1__tar.gz → 7.29.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 (83) hide show
  1. certora_cli_beta_mirror-7.29.0/LICENSE +15 -0
  2. {certora_cli_beta_mirror-7.27.1/certora_cli_beta_mirror.egg-info → certora_cli_beta_mirror-7.29.0}/PKG-INFO +8 -3
  3. certora_cli_beta_mirror-7.29.0/README.md +1 -0
  4. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +48 -13
  5. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraBuild.py +65 -32
  6. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraBuildDataClasses.py +19 -4
  7. certora_cli_beta_mirror-7.29.0/certora_cli/CertoraProver/certoraBuildRust.py +153 -0
  8. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraCloudIO.py +29 -64
  9. certora_cli_beta_mirror-7.29.0/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +377 -0
  10. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +3 -1
  11. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraConfigIO.py +55 -20
  12. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraContext.py +14 -6
  13. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraContextAttributes.py +106 -50
  14. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraContextValidator.py +40 -7
  15. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraParseBuildScript.py +7 -10
  16. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraVerifyGenerator.py +12 -0
  17. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/splitRules.py +3 -1
  18. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Mutate/mutateApp.py +3 -3
  19. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Shared/certoraAttrUtil.py +10 -0
  20. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Shared/certoraUtils.py +18 -2
  21. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Shared/certoraValidateFuncs.py +17 -11
  22. certora_cli_beta_mirror-7.29.0/certora_cli/certoraRanger.py +71 -0
  23. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/certoraRun.py +12 -14
  24. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/certoraSolanaProver.py +8 -1
  25. certora_cli_beta_mirror-7.29.0/certora_cli/certoraSorobanProver.py +285 -0
  26. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0/certora_cli_beta_mirror.egg-info}/PKG-INFO +8 -3
  27. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli_beta_mirror.egg-info/SOURCES.txt +1 -0
  28. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli_beta_mirror.egg-info/entry_points.txt +1 -0
  29. certora_cli_beta_mirror-7.29.0/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  30. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_jars/Typechecker.jar +0 -0
  31. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/setup.py +10 -4
  32. certora_cli_beta_mirror-7.27.1/LICENSE +0 -22
  33. certora_cli_beta_mirror-7.27.1/README.md +0 -1
  34. certora_cli_beta_mirror-7.27.1/certora_cli/CertoraProver/certoraBuildRust.py +0 -117
  35. certora_cli_beta_mirror-7.27.1/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +0 -242
  36. certora_cli_beta_mirror-7.27.1/certora_cli/certoraSorobanProver.py +0 -36
  37. certora_cli_beta_mirror-7.27.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  38. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/MANIFEST.in +0 -0
  39. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_bins/__init__.py +0 -0
  40. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
  41. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +0 -0
  42. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
  43. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
  44. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
  45. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
  46. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/__init__.py +0 -0
  47. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraBuildCacheManager.py +0 -0
  48. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
  49. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
  50. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
  51. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
  52. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraJobList.py +0 -0
  53. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
  54. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
  55. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
  56. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
  57. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/CertoraProver/certoraType.py +0 -0
  58. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  59. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  60. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  61. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  62. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  63. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  64. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
  65. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  66. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Mutate/__init__.py +0 -0
  67. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Mutate/mutateAttributes.py +0 -0
  68. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Mutate/mutateConstants.py +0 -0
  69. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Mutate/mutateUtil.py +0 -0
  70. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Mutate/mutateValidate.py +0 -0
  71. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Shared/ExpectedComparator.py +0 -0
  72. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Shared/__init__.py +0 -0
  73. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/Shared/certoraLogging.py +0 -0
  74. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/__init__.py +0 -0
  75. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/certoraEVMProver.py +0 -0
  76. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/certoraEqCheck.py +0 -0
  77. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/certoraMutate.py +0 -0
  78. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli/rustMutator.py +0 -0
  79. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli_beta_mirror.egg-info/dependency_links.txt +0 -0
  80. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli_beta_mirror.egg-info/requires.txt +0 -0
  81. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_cli_beta_mirror.egg-info/top_level.txt +0 -0
  82. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/certora_jars/__init__.py +0 -0
  83. {certora_cli_beta_mirror-7.27.1 → certora_cli_beta_mirror-7.29.0}/setup.cfg +0 -0
@@ -0,0 +1,15 @@
1
+
2
+ The Certora Prover
3
+ Copyright (C) 2025 Certora Ltd.
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, version 3 of the License.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY, without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR a PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
@@ -1,12 +1,15 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: certora-cli-beta-mirror
3
- Version: 7.27.1
3
+ Version: 7.29.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
7
7
  Author-email: support@certora.com
8
+ License: GPL-3.0-only
9
+ Project-URL: Documentation, https://docs.certora.com/en/latest/
10
+ Project-URL: Source, https://github.com/Certora/CertoraProver
8
11
  Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
10
13
  Classifier: Operating System :: OS Independent
11
14
  Requires-Python: >=3.8
12
15
  Description-Content-Type: text/markdown
@@ -28,8 +31,10 @@ Dynamic: classifier
28
31
  Dynamic: description
29
32
  Dynamic: description-content-type
30
33
  Dynamic: home-page
34
+ Dynamic: license
35
+ Dynamic: project-url
31
36
  Dynamic: requires-dist
32
37
  Dynamic: requires-python
33
38
  Dynamic: summary
34
39
 
35
- Commit 363f2a8. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
40
+ Commit 3f35845. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit 3f35845. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -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']['value']['value']
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']['value']['value']
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
- elem_type = CompilerLangVy.extract_type_from_type_annotation_node(
571
- ast_subscript_node['slice']['value']['elements'][0], named_constants)
572
- max_elements = CompilerLangVy.extract_constant(ast_subscript_node['slice']['value']['elements'][1],
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']['value']['elements']
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
- max_elements_node = ast_subscript_node['slice']['value']
584
- if 'id' in max_elements_node and max_elements_node['id'] in named_constants:
585
- return CompilerLangVy.VyperTypeStaticArray(key_type, named_constants[max_elements_node['id']])
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
 
@@ -411,6 +411,8 @@ def generate_modifier_finder(f: Func, internal_id: int, sym: int,
411
411
  formal_strings = []
412
412
  arg_strings = []
413
413
  for (logged_ty, logged_name) in zip(loggable_types, loggable_names):
414
+ if logged_name == "":
415
+ continue
414
416
  arg_strings.append(logged_name)
415
417
  formal_strings.append(f"{logged_ty} {logged_name}")
416
418
  modifier_body = f"modifier {modifier_name}"
@@ -825,7 +827,7 @@ class CertoraBuildGenerator:
825
827
  ast_logger.debug(f"No body for {func_def} but ast claims it is implemented")
826
828
 
827
829
  if original_contract is not None:
828
- if method := original_contract.find_method(func_name):
830
+ if method := original_contract.find_method(func_name, solidity_type_args):
829
831
  source_bytes = method.source_bytes
830
832
  original_file = method.original_file
831
833
  else:
@@ -1198,10 +1200,15 @@ class CertoraBuildGenerator:
1198
1200
  return x[TYPE] == FUNCTION or x[TYPE] == CertoraBuildGenerator.CONSTRUCTOR_STRING
1199
1201
 
1200
1202
  @staticmethod
1201
- def collect_srcmap(data: Dict[str, Any]) -> Any:
1203
+ def collect_srcmap(data: Dict[str, Any]) -> Tuple[str, str]:
1202
1204
  # no source map object in vyper
1203
- return (data["evm"]["deployedBytecode"].get("sourceMap", ""),
1204
- data["evm"]["bytecode"].get("sourceMap", ""))
1205
+ deployed = data["evm"]["deployedBytecode"].get("sourceMap", "")
1206
+ if isinstance(deployed, dict):
1207
+ deployed = deployed.get("pc_pos_map_compressed", "")
1208
+ regular = data["evm"]["bytecode"].get("sourceMap", "")
1209
+ if isinstance(regular, dict):
1210
+ regular = regular.get("pc_pos_map_compressed", "")
1211
+ return deployed, regular
1205
1212
 
1206
1213
  @staticmethod
1207
1214
  def collect_varmap(contract: str, data: Dict[str, Any]) -> Any:
@@ -1516,7 +1523,8 @@ class CertoraBuildGenerator:
1516
1523
  sources_dict = {str(contract_file_posix_abs): {
1517
1524
  "urls": [str(contract_file_posix_abs)]}} # type: Dict[str, Dict[str, Any]]
1518
1525
  output_selection = ["transientStorageLayout", "storageLayout", "abi", "evm.bytecode",
1519
- "evm.deployedBytecode", "evm.methodIdentifiers", "evm.assembly"]
1526
+ "evm.deployedBytecode", "evm.methodIdentifiers", "evm.assembly",
1527
+ "evm.bytecode.functionDebugData"]
1520
1528
  ast_selection = ["id", "ast"]
1521
1529
  elif compiler_collector_lang == CompilerLangVy():
1522
1530
  with open(contract_file_posix_abs) as f:
@@ -1739,6 +1747,10 @@ class CertoraBuildGenerator:
1739
1747
  sdc_name = f"{Path(build_arg_contract_file).name}_{file_index}"
1740
1748
  compilation_path = self.get_compilation_path(sdc_name)
1741
1749
  self.file_to_sdc_name[Util.abs_norm_path(build_arg_contract_file)] = sdc_name
1750
+
1751
+ compiler_collector = self.compiler_coll_factory \
1752
+ .get_compiler_collector(Path(path_for_compiler_collector_file))
1753
+
1742
1754
  # update remappings and collect_cmd:
1743
1755
  if not is_vyper:
1744
1756
  Util.safe_create_dir(compilation_path)
@@ -1786,8 +1798,10 @@ class CertoraBuildGenerator:
1786
1798
  compiler_logger.debug(f"collect_cmd: {collect_cmd}\n")
1787
1799
  else:
1788
1800
  compiler_ver_to_run = get_relevant_compiler(Path(build_arg_contract_file), self.context)
1789
-
1790
- collect_cmd = f'{compiler_ver_to_run} -p "{self.context.solc_allow_path}" -o "{compilation_path}" ' \
1801
+ path_string = ""
1802
+ if compiler_collector.compiler_version[1] < 4:
1803
+ path_string = f' -p "{self.context.solc_allow_path}"'
1804
+ collect_cmd = f'{compiler_ver_to_run}{path_string} -o "{compilation_path}" ' \
1791
1805
  f'--standard-json'
1792
1806
 
1793
1807
  # Make sure compilation artifacts are always deleted
@@ -1800,9 +1814,6 @@ class CertoraBuildGenerator:
1800
1814
  # (we do not try to save a big chain history of changes, just a previous and current)
1801
1815
  self.backup_compiler_outputs(sdc_name, smart_contract_lang, "prev")
1802
1816
 
1803
- compiler_collector = self.compiler_coll_factory \
1804
- .get_compiler_collector(Path(path_for_compiler_collector_file))
1805
-
1806
1817
  # Standard JSON
1807
1818
  remappings = [] if isinstance(compiler_collector, CompilerCollectorYul) else self.context.remappings
1808
1819
  input_for_solc = self.standard_json(Path(file_abs_path), build_arg_contract_file, remappings,
@@ -2246,6 +2257,14 @@ class CertoraBuildGenerator:
2246
2257
  build_arg_contract_file)
2247
2258
  immutables = self.collect_immutables(contract_data, build_arg_contract_file, compiler_lang)
2248
2259
 
2260
+ internal_function_entrypoints = set([])
2261
+
2262
+ if compiler_lang == CompilerLangSol() and "functionDebugData" in contract_data["evm"]["deployedBytecode"]:
2263
+ debug = contract_data["evm"]["deployedBytecode"]["functionDebugData"]
2264
+ for (_, v) in debug.items():
2265
+ if "entryPoint" in v and v["entryPoint"] is not None:
2266
+ internal_function_entrypoints.add(v["entryPoint"])
2267
+
2249
2268
  if self.context.internal_funcs is not None:
2250
2269
  all_internal_functions: Dict[str, Any] = \
2251
2270
  Util.read_json_file(self.context.internal_funcs)
@@ -2296,7 +2315,8 @@ class CertoraBuildGenerator:
2296
2315
  extension_contracts=list(),
2297
2316
  local_assignments={},
2298
2317
  branches={},
2299
- requires={}
2318
+ requires={},
2319
+ internal_starts=list(internal_function_entrypoints)
2300
2320
  )
2301
2321
 
2302
2322
  @staticmethod
@@ -2527,7 +2547,7 @@ class CertoraBuildGenerator:
2527
2547
  sources_from_pre_finder_SDCs |= sdc.all_contract_files
2528
2548
  sources = self.collect_sources(context, certora_verify_generator, sources_from_pre_finder_SDCs)
2529
2549
  try:
2530
- self.cwd_rel_in_sources = build_source_tree(sources, context)
2550
+ build_source_tree(sources, context)
2531
2551
  except Exception as e:
2532
2552
  build_logger.debug(f"build_source_tree failed. Sources: {sources}", exc_info=e)
2533
2553
  raise
@@ -2698,8 +2718,8 @@ class CertoraBuildGenerator:
2698
2718
  return None
2699
2719
  cloned_field = storage_field_info.copy()
2700
2720
  cloned_field["slot"] = str(link_slot)
2701
- # Try to uniquify the name
2702
- cloned_field["label"] = f"certoralink_{ext_instance.name}_{var_name}"
2721
+ # Don't bother trying to uniquify the name
2722
+ cloned_field["label"] = var_name
2703
2723
  return cloned_field
2704
2724
 
2705
2725
  def handle_one_extension(storage_ext: str) -> tuple[Any, str, List[Dict[str, Any]]] :
@@ -2750,23 +2770,34 @@ class CertoraBuildGenerator:
2750
2770
  if target_contract.storage_layout.get("types") is None:
2751
2771
  target_contract.storage_layout["types"] = {}
2752
2772
  target_slots = {storage["slot"] for storage in target_contract.storage_layout["storage"]}
2773
+ target_vars = {storage["label"] for storage in target_contract.storage_layout["storage"]}
2753
2774
  # Keep track of slots we've added, and error if we
2754
2775
  # find two extensions extending the same slot
2755
2776
  added_slots: Dict[str, str] = {}
2777
+ added_vars: Dict[str, str] = {}
2756
2778
  for ext in extensions:
2757
2779
  (new_fields, new_types) = to_add[ext]
2758
2780
 
2759
2781
  for f in new_fields:
2760
- # See if any of the new fields is a slot we've already added
2782
+ # See if any of the new fields is a slot or variable name we've already added
2761
2783
  slot = f["slot"]
2784
+ var = f["label"]
2762
2785
  if slot in added_slots:
2763
2786
  seen = added_slots[slot]
2764
- raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} already added by {seen}")
2787
+ raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} was already added by {seen}")
2788
+
2789
+ if var in added_vars:
2790
+ seen = added_vars[var]
2791
+ raise Util.CertoraUserInputError(f"Var '{var}' added to {target_contract.name} by {ext} was already added by {seen}")
2765
2792
 
2766
2793
  if slot in target_slots:
2767
- raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} already mapped by {target_contract.name}")
2794
+ raise Util.CertoraUserInputError(f"Slot {slot} added to {target_contract.name} by {ext} is already mapped by {target_contract.name}")
2795
+
2796
+ if var in target_vars:
2797
+ raise Util.CertoraUserInputError(f"Var '{var}' added to {target_contract.name} by {ext} is already declared by {target_contract.name}")
2768
2798
 
2769
2799
  added_slots[slot] = ext
2800
+ added_vars[var] = ext
2770
2801
 
2771
2802
  target_contract.storage_layout["storage"].extend(new_fields)
2772
2803
 
@@ -3003,7 +3034,7 @@ class CertoraBuildGenerator:
3003
3034
  for k, v in autofinder_remappings.items():
3004
3035
  self.function_finder_file_remappings[Util.abs_posix_path(k)] = Util.abs_posix_path(v)
3005
3036
  new_sdcs = self.collect_for_file(new_file, i, get_compiler_lang(build_arg_contract_file),
3006
- Util.get_certora_sources_dir() / self.cwd_rel_in_sources,
3037
+ Util.get_certora_sources_dir() / self.context.cwd_rel_in_sources,
3007
3038
  path_for_compiler_collector_file,
3008
3039
  sdc_pre_finder,
3009
3040
  fail_on_compilation_error=False,
@@ -3031,7 +3062,7 @@ class CertoraBuildGenerator:
3031
3062
  contract_path = Util.abs_posix_path_obj(contract_file)
3032
3063
  rel_directory = Path(os.path.relpath(contract_file, '.')).parent
3033
3064
  contract_filename = contract_path.name
3034
- new_path = Util.get_certora_sources_dir() / self.cwd_rel_in_sources / rel_directory / contract_filename
3065
+ new_path = Util.get_certora_sources_dir() / self.context.cwd_rel_in_sources / rel_directory / contract_filename
3035
3066
  new_path.parent.mkdir(parents=True, exist_ok=True)
3036
3067
  return str(new_path)
3037
3068
 
@@ -3041,7 +3072,7 @@ class CertoraBuildGenerator:
3041
3072
  This assumes those paths can be related to cwd.
3042
3073
  """
3043
3074
  rel_to_cwd_path = Path(os.path.relpath(path, '.'))
3044
- new_path = Util.get_certora_sources_dir() / self.cwd_rel_in_sources / rel_to_cwd_path
3075
+ new_path = Util.get_certora_sources_dir() / self.context.cwd_rel_in_sources / rel_to_cwd_path
3045
3076
  return str(new_path.absolute())
3046
3077
 
3047
3078
  def handle_links(self) -> None:
@@ -3137,7 +3168,7 @@ class CertoraBuildGenerator:
3137
3168
  f"for contract extension")
3138
3169
  extension_contract = contracts_by_name[extension]
3139
3170
  for f in ext["exclude"]:
3140
- if extension_contract.find_method(f) is None:
3171
+ if not extension_contract.has_method_with_name(f):
3141
3172
  raise Util.CertoraUserInputError(f"Can't find a method named {f} in contract "
3142
3173
  f"{extension_contract.name}")
3143
3174
  extended_contract.add_extension(ContractExtension(extension_contract, ext["exclude"]))
@@ -3419,6 +3450,8 @@ class CertoraBuildGenerator:
3419
3450
  sources.add(Path(context.bytecode_spec))
3420
3451
  if context.yul_abi:
3421
3452
  sources.add(Path(context.yul_abi))
3453
+ if context.override_base_config:
3454
+ sources.add(Path(context.override_base_config))
3422
3455
 
3423
3456
  if hasattr(context, Attrs.EvmProverAttributes.PROVER_RESOURCE_FILES.get_conf_key()) \
3424
3457
  and context.prover_resource_files:
@@ -3447,20 +3480,19 @@ def sources_to_abs(sources: Set[Path]) -> Set[Path]:
3447
3480
  return result
3448
3481
 
3449
3482
 
3450
- def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bool = False) -> Path:
3483
+ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bool = False) -> None:
3451
3484
  """
3452
3485
  Copies files to .certora_sources
3453
- @returns the cwd relative in sources
3454
3486
  """
3455
3487
  sources = sources_to_abs(sources)
3456
- cwd_rel_in_sources, common_path = CertoraBuildGenerator.get_cwd_rel_in_sources(sources)
3488
+ context.cwd_rel_in_sources, context.common_path = CertoraBuildGenerator.get_cwd_rel_in_sources(sources)
3457
3489
 
3458
3490
  for source_path in sources:
3459
3491
  is_dir = source_path.is_dir()
3460
3492
  # copy file to the path of the file from the common root under the sources directory
3461
3493
 
3462
3494
  # make sure directory exists
3463
- target_path = Util.get_certora_sources_dir() / source_path.relative_to(common_path)
3495
+ target_path = Util.get_certora_sources_dir() / source_path.relative_to(context.common_path)
3464
3496
  target_directory = target_path if is_dir else target_path.parent
3465
3497
  try:
3466
3498
  target_directory.mkdir(parents=True, exist_ok=True)
@@ -3489,7 +3521,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
3489
3521
  raise
3490
3522
 
3491
3523
  # the empty file .cwd is written in the source tree to denote the current working directory
3492
- cwd_file_path = Util.get_certora_sources_dir() / cwd_rel_in_sources / Util.CWD_FILE
3524
+ cwd_file_path = Util.get_certora_sources_dir() / context.cwd_rel_in_sources / Util.CWD_FILE
3493
3525
  cwd_file_path.parent.mkdir(parents=True, exist_ok=True)
3494
3526
  cwd_file_path.touch()
3495
3527
 
@@ -3498,7 +3530,7 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
3498
3530
  if rust_proj_dir:
3499
3531
  proj_dir_parent_relative = os.path.relpath(rust_proj_dir, os.getcwd())
3500
3532
  assert Path(rust_proj_dir).is_dir(), f"build_source_tree: not a directory {rust_proj_dir}"
3501
- proj_dir_parent_file = (Util.get_certora_sources_dir() / cwd_rel_in_sources / proj_dir_parent_relative /
3533
+ proj_dir_parent_file = (Util.get_certora_sources_dir() / context.cwd_rel_in_sources / proj_dir_parent_relative /
3502
3534
  Util.PROJECT_DIR_FILE)
3503
3535
  proj_dir_parent_file.parent.mkdir(parents=True, exist_ok=True)
3504
3536
  proj_dir_parent_file.touch()
@@ -3514,9 +3546,6 @@ def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bo
3514
3546
  build_logger.debug("Couldn't copy repro conf to certora sources.", exc_info=e)
3515
3547
  raise
3516
3548
 
3517
- return cwd_rel_in_sources
3518
-
3519
-
3520
3549
  def build_from_scratch(certora_build_generator: CertoraBuildGenerator,
3521
3550
  certora_verify_generator: CertoraVerifyGenerator,
3522
3551
  build_cache_enabled: bool) -> CachedFiles:
@@ -3659,8 +3688,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3659
3688
 
3660
3689
  # Start by syntax checking, if we're in the right mode
3661
3690
  if Cv.mode_has_spec_file(context) and not context.build_only and not ignore_spec_syntax_check:
3662
- attr = context.disable_local_typechecking
3663
- if attr:
3691
+ if context.disable_local_typechecking:
3664
3692
  build_logger.warning(
3665
3693
  "Local checks of CVL specification files disabled. It is recommended to enable the checks.")
3666
3694
  else:
@@ -3673,6 +3701,11 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
3673
3701
  certora_verify_generator,
3674
3702
  certora_build_cache_manager)
3675
3703
 
3704
+ # avoid running the same test over and over again for each split run, context.split_rules is true only for
3705
+ # the first run and is set to [] for split runs
3706
+ if context.split_rules:
3707
+ Ctx.run_local_spec_check(True, context)
3708
+
3676
3709
  # .certora_verify.json is always constructed even if build cache is enabled
3677
3710
  # Sources construction should only happen when rebuilding
3678
3711
  # Build sources tree
@@ -107,7 +107,8 @@ class ContractInSDC:
107
107
  extension_contracts: List[ContractExtension],
108
108
  local_assignments: Dict[str, UnspecializedSourceFinder],
109
109
  branches: Dict[str, UnspecializedSourceFinder],
110
- requires: Dict[str, UnspecializedSourceFinder]
110
+ requires: Dict[str, UnspecializedSourceFinder],
111
+ internal_starts: List[int]
111
112
  ):
112
113
  self.name = name
113
114
  self.original_file = source_file
@@ -139,6 +140,7 @@ class ContractInSDC:
139
140
  self.original_file_name = Path(source_file).name
140
141
  self.compiler_collector = compiler_collector
141
142
  self.compiler_parameters = compiler_parameters
143
+ self.internal_starts = internal_starts
142
144
 
143
145
  if not self.compiler_collector:
144
146
  compiler_version = ""
@@ -193,7 +195,8 @@ class ContractInSDC:
193
195
  "compilerParameters": None if not self.compiler_parameters else self.compiler_parameters.as_dict(),
194
196
  "sourceBytes": None if self.source_bytes is None else self.source_bytes.as_dict(),
195
197
  "extensionContracts": [e.as_dict() for e in self.extension_contracts],
196
- "localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()}
198
+ "localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()},
199
+ "internalFunctionStarts": self.internal_starts
197
200
  }
198
201
  # "sourceHints": {"localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()},
199
202
  # "branches": {k: v.as_dict() for k, v in self.branches.items()},
@@ -218,12 +221,24 @@ class ContractInSDC:
218
221
  "localAssignments": {k: v.as_dict() for k, v in self.local_assignments.items()}
219
222
  }
220
223
 
221
- def find_method(self, name: str) -> Optional[Func]:
224
+ def find_method(self, name: str, fullArgs: List[CT.TypeInstance]) -> Optional[Func]:
225
+ """
226
+ attempts to find and return a matching method. from Solidity documentation,
227
+ the combination of the method name and (ordered) parameter types
228
+ is sufficient to disambiguate method overloading.
229
+ """
222
230
  for method in self.methods:
223
231
  if method.name == name:
224
- return method
232
+ if Util.eq_by(CT.TypeInstance.matches, method.fullArgs, fullArgs):
233
+ return method
225
234
  return None
226
235
 
236
+ def has_method_with_name(self, name: str) -> bool:
237
+ for method in self.methods:
238
+ if method.name == name:
239
+ return True
240
+ return False
241
+
227
242
  def __repr__(self) -> str:
228
243
  return repr(self.as_printable_dict())
229
244
 
@@ -0,0 +1,153 @@
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 glob
17
+ import shutil
18
+ from pathlib import Path
19
+ from typing import Set, List
20
+
21
+ from CertoraProver.certoraBuild import build_source_tree
22
+ from CertoraProver.certoraContextClass import CertoraContext
23
+ from CertoraProver.certoraParseBuildScript import run_rust_build
24
+ import CertoraProver.certoraContextAttributes as Attrs
25
+ from Shared import certoraUtils as Util
26
+
27
+
28
+ def build_rust_app(context: CertoraContext) -> None:
29
+ build_command: List[str] = []
30
+ feature_flag = None
31
+ if context.build_script:
32
+ build_command = [context.build_script]
33
+ feature_flag = '--cargo_features'
34
+
35
+ elif not context.files:
36
+ build_command = ["cargo", "certora-sbf"]
37
+ feature_flag = '--features'
38
+ if context.cargo_tools_version:
39
+ build_command.append("--tools-version")
40
+ build_command.append(context.cargo_tools_version)
41
+
42
+ if build_command:
43
+ if context.cargo_features is not None:
44
+ build_command.extend([feature_flag] + context.cargo_features)
45
+
46
+ build_command.extend(['--json', '-l'])
47
+
48
+ if context.test == str(Util.TestValue.SOLANA_BUILD_CMD):
49
+ raise Util.TestResultsReady(build_command)
50
+
51
+ run_rust_build(context, build_command)
52
+
53
+ else:
54
+ if not context.files:
55
+ raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
56
+ if len(context.files) > 1:
57
+ raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
58
+
59
+ try:
60
+ Util.get_certora_sources_dir().mkdir(parents=True, exist_ok=True)
61
+ shutil.copy(Util.get_last_conf_file(), Util.get_certora_sources_dir() / Util.LAST_CONF_FILE)
62
+ except Exception as e:
63
+ raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
64
+
65
+ copy_files_to_build_dir(context)
66
+
67
+ sources: Set[Path] = set()
68
+ collect_files_from_rust_sources(context, sources)
69
+
70
+ try:
71
+ # Create generators
72
+ build_source_tree(sources, context)
73
+
74
+ except Exception as e:
75
+ raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
76
+
77
+
78
+ def add_solana_files_from_prover_args(context: CertoraContext) -> None:
79
+ if context.prover_args:
80
+ inlining_file = False
81
+ summaries_file = False
82
+ for prover_arg in context.prover_args:
83
+ for arg in prover_arg.split(' '):
84
+ if inlining_file:
85
+ context.solana_inlining = context.solana_inlining or [Path(arg)]
86
+ inlining_file = False
87
+ if summaries_file:
88
+ context.solana_summaries = context.solana_summaries or [Path(arg)]
89
+ summaries_file = False
90
+
91
+ if arg == '-solanaInlining':
92
+ inlining_file = True
93
+ elif arg == '-solanaSummaries':
94
+ summaries_file = True
95
+
96
+
97
+ def add_solana_files_to_sources(context: CertoraContext, sources: Set[Path]) -> None:
98
+ for attr in [Attrs.SolanaProverAttributes.SOLANA_INLINING,
99
+ Attrs.SolanaProverAttributes.SOLANA_SUMMARIES,
100
+ Attrs.SolanaProverAttributes.BUILD_SCRIPT,
101
+ Attrs.SolanaProverAttributes.FILES]:
102
+ attr_name = attr.get_conf_key()
103
+ attr_value = getattr(context, attr_name, None)
104
+ if not attr_value:
105
+ continue
106
+ if isinstance(attr_value, str):
107
+ attr_value = [attr_value]
108
+ if not isinstance(attr_value, list):
109
+ raise Util.CertoraUserInputError(f"{attr_value} is not a valid value for {attr_name} {attr_value}. Value "
110
+ f"must be a string or a llist ")
111
+ file_paths = [Path(s) for s in attr_value]
112
+ for file_path in file_paths:
113
+ if not file_path.exists():
114
+ raise Util.CertoraUserInputError(f"in {attr_name} file {file_path} does not exist")
115
+ sources.add(file_path.absolute().resolve())
116
+
117
+
118
+ def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path]) -> None:
119
+ patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
120
+ exclude_dirs = [".certora_internal"]
121
+
122
+ if hasattr(context, 'rust_project_directory'):
123
+ project_directory = Path(context.rust_project_directory)
124
+
125
+ if not project_directory.is_dir():
126
+ raise ValueError(f"The given directory '{project_directory}' is not valid.")
127
+
128
+ for source in context.rust_sources:
129
+ for file in glob.glob(f'{project_directory.joinpath(source)}', recursive=True):
130
+ file_path = Path(file)
131
+ if any(excluded in file_path.parts for excluded in exclude_dirs):
132
+ continue
133
+ if file_path.is_file() and any(file_path.match(pattern) for pattern in patterns):
134
+ sources.add(file_path)
135
+
136
+ sources.add(project_directory.absolute())
137
+ if Path(context.build_script).exists():
138
+ sources.add(Path(context.build_script).resolve())
139
+ if getattr(context, 'conf_file', None) and Path(context.conf_file).exists():
140
+ sources.add(Path(context.conf_file).absolute())
141
+
142
+ add_solana_files_from_prover_args(context)
143
+ add_solana_files_to_sources(context, sources)
144
+
145
+
146
+ def copy_files_to_build_dir(context: CertoraContext) -> None:
147
+ assert context.files, "copy_files_to_build_dir: expecting files to be non-empty"
148
+ shutil.copyfile(context.files[0], Util.get_build_dir() / Path(context.files[0]).name)
149
+
150
+ if rust_logs := getattr(context, 'rust_logs_stdout', None):
151
+ shutil.copy(Path(rust_logs), Util.get_build_dir() / Path(rust_logs).name)
152
+ if rust_logs := getattr(context, 'rust_logs_stderr', None):
153
+ shutil.copy(Path(rust_logs), Util.get_build_dir() / Path(rust_logs).name)