certora-cli 8.8.1__tar.gz → 8.13.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 (91) hide show
  1. {certora_cli-8.8.1/certora_cli.egg-info → certora_cli-8.13.0}/PKG-INFO +2 -2
  2. certora_cli-8.13.0/README.md +1 -0
  3. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraBuild.py +83 -28
  4. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraBuildCacheManager.py +16 -1
  5. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraBuildRust.py +3 -0
  6. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraBuildSui.py +42 -12
  7. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraCloudIO.py +9 -0
  8. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraContext.py +39 -8
  9. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraContextAttributes.py +132 -7
  10. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraContextValidator.py +25 -1
  11. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraParseBuildScript.py +3 -0
  12. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/splitRules.py +31 -17
  13. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/certoraUtils.py +4 -2
  14. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/certoraValidateFuncs.py +18 -1
  15. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraSolanaProver.py +22 -1
  16. {certora_cli-8.8.1 → certora_cli-8.13.0/certora_cli.egg-info}/PKG-INFO +2 -2
  17. certora_cli-8.8.1/certora_jars/Typechecker.jar → certora_cli-8.13.0/certora_jars/ASTExtraction.jar +0 -0
  18. certora_cli-8.13.0/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  19. certora_cli-8.8.1/certora_jars/ASTExtraction.jar → certora_cli-8.13.0/certora_jars/Typechecker.jar +0 -0
  20. {certora_cli-8.8.1 → certora_cli-8.13.0}/setup.py +2 -2
  21. certora_cli-8.8.1/README.md +0 -1
  22. certora_cli-8.8.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  23. {certora_cli-8.8.1 → certora_cli-8.13.0}/LICENSE +0 -0
  24. {certora_cli-8.8.1 → certora_cli-8.13.0}/MANIFEST.in +0 -0
  25. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_bins/__init__.py +0 -0
  26. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
  27. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +0 -0
  28. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
  29. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
  30. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
  31. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
  32. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
  33. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/__init__.py +0 -0
  34. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/castingInstrumenter.py +0 -0
  35. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraApp.py +0 -0
  36. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraBuildDataClasses.py +0 -0
  37. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +0 -0
  38. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +0 -0
  39. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
  40. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraConfigIO.py +0 -0
  41. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
  42. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
  43. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
  44. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraJobList.py +0 -0
  45. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
  46. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
  47. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraOffsetConverter.py +0 -0
  48. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
  49. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraSourceFinders.py +0 -0
  50. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraType.py +0 -0
  51. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
  52. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/erc7201.py +0 -0
  53. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/storageExtension.py +0 -0
  54. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/CertoraProver/uncheckedOverflowInstrumenter.py +0 -0
  55. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  56. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  57. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  58. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  59. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  60. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  61. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
  62. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  63. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Mutate/__init__.py +0 -0
  64. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Mutate/mutateApp.py +0 -0
  65. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Mutate/mutateAttributes.py +0 -0
  66. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Mutate/mutateConstants.py +0 -0
  67. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Mutate/mutateUtil.py +0 -0
  68. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Mutate/mutateValidate.py +0 -0
  69. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/ExpectedComparator.py +0 -0
  70. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/__init__.py +0 -0
  71. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/certoraAttrUtil.py +0 -0
  72. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/certoraLogging.py +0 -0
  73. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/Shared/proverCommon.py +0 -0
  74. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/__init__.py +0 -0
  75. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraCVLFormatter.py +0 -0
  76. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraConcord.py +0 -0
  77. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraEVMProver.py +0 -0
  78. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraEqCheck.py +0 -0
  79. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraMutate.py +0 -0
  80. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraRanger.py +0 -0
  81. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraRun.py +0 -0
  82. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraSorobanProver.py +0 -0
  83. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/certoraSuiProver.py +0 -0
  84. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli/rustMutator.py +0 -0
  85. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli.egg-info/SOURCES.txt +0 -0
  86. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli.egg-info/dependency_links.txt +0 -0
  87. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli.egg-info/entry_points.txt +0 -0
  88. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli.egg-info/requires.txt +0 -0
  89. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_cli.egg-info/top_level.txt +0 -0
  90. {certora_cli-8.8.1 → certora_cli-8.13.0}/certora_jars/__init__.py +0 -0
  91. {certora_cli-8.8.1 → certora_cli-8.13.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: certora-cli
3
- Version: 8.8.1
3
+ Version: 8.13.0
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli
6
6
  Author: Certora
@@ -38,4 +38,4 @@ Dynamic: requires-dist
38
38
  Dynamic: requires-python
39
39
  Dynamic: summary
40
40
 
41
- Commit 64c2b53. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
41
+ Commit 3ce73ef. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit 3ce73ef. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -890,7 +890,7 @@ class CertoraBuildGenerator:
890
890
  # same name but used separately could exist right?
891
891
  # Add to the current contract all public/external functions, and internal/private ones if
892
892
  # this is the declaring contract.
893
- if func_visibility in ("public", "external") or (c_name == contract_name):
893
+ if func_visibility in ("public", "external") or (c_name == contract_name and c_file == contract_file):
894
894
  funcs.append(func)
895
895
  ast_logger.debug(f"Function {func.source_code_signature()} added")
896
896
 
@@ -1114,11 +1114,12 @@ class CertoraBuildGenerator:
1114
1114
  assert "baseName" in bc and "referencedDeclaration" in bc["baseName"]
1115
1115
  next_bc_ref = bc["baseName"]["referencedDeclaration"]
1116
1116
  next_bc = self.get_contract_file_of(build_arg_contract_file, next_bc_ref)
1117
- if next_bc not in base_contracts_lst:
1117
+ next_bc_name = self.asts[build_arg_contract_file][next_bc][next_bc_ref]["name"]
1118
+ if (next_bc, next_bc_name) not in [(e[0], e[1]) for e in base_contracts_lst]:
1118
1119
  base_contracts_lst.append(
1119
- (next_bc, self.asts[build_arg_contract_file][next_bc][next_bc_ref]["name"],
1120
+ (next_bc, next_bc_name,
1120
1121
  self.is_library_def_node(next_bc, next_bc_ref, build_arg_contract_file)))
1121
- base_contracts_queue.insert(0, (next_bc, bc["baseName"]["referencedDeclaration"]))
1122
+ base_contracts_queue.insert(0, (next_bc, next_bc_ref))
1122
1123
 
1123
1124
  if base_contracts_queue:
1124
1125
  retrieve_base_contracts_list_rec(base_contracts_queue, base_contracts_lst)
@@ -1447,7 +1448,9 @@ class CertoraBuildGenerator:
1447
1448
  if not self.context.strict_solc_optimizer and self.get_solc_via_ir_value(contract_file_path):
1448
1449
  # The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
1449
1450
  # full inliner step removed
1450
- solc0_8_26_to_0_8_30 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
1451
+ solc0_8_34_to_0_8_34 = ("dfDvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLvifMCTUca[r]LSsTFOtfDnca[r]"
1452
+ "IulcscCTUtvifMx[scCTUt]TOntnfDIulvifMjmul[jul]VcTOculjmul")
1453
+ solc0_8_26_to_0_8_33 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
1451
1454
  "IulcscCTUtx[scCTUt]TOntnfDIuljmul[jul]VcTOculjmul")
1452
1455
  solc0_8_13_to_0_8_25 = "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]T" + \
1453
1456
  "peulxa[rul]xa[r]cLgvifCTUca[r]LSsTFOtfDnca[r]Iulc]jmul[jul]VcTOculjmul"
@@ -1496,8 +1499,10 @@ class CertoraBuildGenerator:
1496
1499
  yul_optimizer_steps = solc0_8_12
1497
1500
  elif minor == 8 and 13 <= patch <= 25:
1498
1501
  yul_optimizer_steps = solc0_8_13_to_0_8_25
1499
- elif minor == 8 and 26 <= patch <= 30:
1500
- yul_optimizer_steps = solc0_8_26_to_0_8_30
1502
+ elif minor == 8 and 26 <= patch <= 33:
1503
+ yul_optimizer_steps = solc0_8_26_to_0_8_33
1504
+ elif minor == 8 and 34 <= patch <= 34:
1505
+ yul_optimizer_steps = solc0_8_34_to_0_8_34
1501
1506
  assert yul_optimizer_steps is not None, \
1502
1507
  'Yul Optimizer steps missing for requested Solidity version. Please contact Certora team.'
1503
1508
 
@@ -1591,10 +1596,12 @@ class CertoraBuildGenerator:
1591
1596
  "evm.bytecode.functionDebugData"]
1592
1597
  ast_selection = ["id", "ast"]
1593
1598
  elif compiler_collector_lang == CompilerLangVy():
1594
- main_contract_for_output_selection = "*"
1595
1599
  sources_dict = {}
1596
- with open(contract_file_posix_abs) as f:
1597
- if self.context.vyper_custom_std_json_in_map and contract_file_as_provided in self.context.vyper_custom_std_json_in_map:
1600
+ vyper_custom_std_json_in = None
1601
+
1602
+ if self.context.vyper_custom_std_json_in_map:
1603
+ main_contract_for_output_selection = contract_file_as_provided
1604
+ if contract_file_as_provided in self.context.vyper_custom_std_json_in_map:
1598
1605
  """
1599
1606
  If we're given a custom standard_json, we'll take from it the sources and
1600
1607
  the search paths.
@@ -1606,18 +1613,36 @@ class CertoraBuildGenerator:
1606
1613
  if we get more projects.
1607
1614
  """
1608
1615
  vyper_custom_std_json_in = self.context.vyper_custom_std_json_in_map[contract_file_as_provided]
1609
- with open(vyper_custom_std_json_in) as custom:
1610
- custom_json = json.load(custom)
1611
- sources_dict = custom_json.get("sources", None)
1612
- search_paths_arr = custom_json.get("settings", {}).get("search_paths", None)
1613
- additional_asts = [x for x, _ in sources_dict.items() if x.endswith(".vy")]
1614
- if not sources_dict:
1616
+ elif self.context.resolve_vyper_imports and compiler_collector.compiler_version >= (0, 4, 0):
1617
+ main_contract_for_output_selection = contract_file_as_provided
1618
+ """
1619
+ If resolve_vyper_imports is enabled, we ask the vyper compiler to generate a standard_json for us.
1620
+ """
1621
+ compiler_ver_to_run = get_relevant_compiler(Path(contract_file_as_provided), self.context)
1622
+ vyper_options = f"-f solc_json {contract_file_as_provided} --disable-sys-path"
1623
+ for path in ((self.context.vyper_system_path or []) + (self.context.vyper_import_path or [])):
1624
+ vyper_options += f" -p {path}"
1625
+ Util.run_compiler_cmd(f"{compiler_ver_to_run} {vyper_options}",
1626
+ f"{contract_file_posix_abs.name}.std_json_generated.json",
1627
+ wd=compile_wd)
1628
+ vyper_custom_std_json_in = compiler_collector_lang.compilation_output_path(
1629
+ f"{contract_file_posix_abs.name}.std_json_generated.stdout.json")
1630
+
1631
+ if vyper_custom_std_json_in:
1632
+ with open(vyper_custom_std_json_in) as custom:
1633
+ custom_json = json.load(custom)
1634
+ sources_dict = custom_json.get("sources", None)
1635
+ search_paths_arr = custom_json.get("settings", {}).get("search_paths", None)
1636
+ additional_asts = [x for x, _ in sources_dict.items() if x.endswith(".vy")]
1637
+
1638
+ if not sources_dict:
1639
+ with open(contract_file_posix_abs) as f:
1615
1640
  contents = f.read()
1616
1641
  sources_dict = {str(contract_file_posix_abs): {"content": contents}}
1617
- output_selection = ["abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers"]
1618
- if compiler_collector.compiler_version >= (0, 4, 4):
1619
- output_selection += ["metadata", "evm.deployedBytecode.symbolMap"]
1620
- ast_selection = ["ast"]
1642
+ output_selection = ["abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers"]
1643
+ if compiler_collector.compiler_version >= (0, 4, 4):
1644
+ output_selection += ["metadata", "evm.deployedBytecode.symbolMap"]
1645
+ ast_selection = ["ast"]
1621
1646
  else:
1622
1647
  # "non-compilable" language so no need to deal with it
1623
1648
  fatal_error(compiler_logger, "Expected only Solidity and Vyper as "
@@ -2483,8 +2508,8 @@ class CertoraBuildGenerator:
2483
2508
  return f"{contract}_{address}"
2484
2509
 
2485
2510
  @staticmethod
2486
- def get_primary_contract_from_sdc(contracts: List[ContractInSDC], primary: str) -> List[ContractInSDC]:
2487
- return [x for x in contracts if x.name == primary]
2511
+ def get_primary_contract_from_sdc(contracts: List[ContractInSDC], primary: str, primary_file: str) -> List[ContractInSDC]:
2512
+ return [x for x in contracts if x.name == primary and Util.abs_norm_path(x.original_file) == Util.abs_norm_path(primary_file)]
2488
2513
 
2489
2514
  @staticmethod
2490
2515
  def generate_library_import(file_absolute_path: str, library_name: str) -> str:
@@ -2945,7 +2970,7 @@ class CertoraBuildGenerator:
2945
2970
  sdc.original_srclist,
2946
2971
  sdc.report_srclist,
2947
2972
  f"{sdc.sdc_name}_{library_contract.name}",
2948
- self.get_primary_contract_from_sdc(sdc.contracts, library_contract.name),
2973
+ self.get_primary_contract_from_sdc(sdc.contracts, library_contract.name, library_contract.original_file),
2949
2974
  [],
2950
2975
  {},
2951
2976
  {},
@@ -2956,7 +2981,7 @@ class CertoraBuildGenerator:
2956
2981
  self.SDCs[self.get_sdc_key(sdc_lib.primary_contract, sdc_lib.primary_contract_address)] = sdc_lib
2957
2982
 
2958
2983
  # Filter out irrelevant contracts, now that we extracted the libraries, leave just the primary
2959
- sdc.contracts = self.get_primary_contract_from_sdc(sdc.contracts, sdc.primary_contract)
2984
+ sdc.contracts = self.get_primary_contract_from_sdc(sdc.contracts, sdc.primary_contract, sdc.sdc_origin_file)
2960
2985
  assert len(
2961
2986
  sdc.contracts) == 1, f"Found multiple primary contracts ({sdc.contracts}) in SDC {sdc.sdc_name}"
2962
2987
 
@@ -2977,8 +3002,13 @@ class CertoraBuildGenerator:
2977
3002
  corresponding to the namespaced types.
2978
3003
 
2979
3004
  Args:
2980
- original_file: Path to the Solidity file containing namespaced storage declarations
3005
+ original_file: Path to the Solidity file containing namespaced storage declarations.
3006
+ This may be a (transitivly) imported file rather than a user-specified target.
2981
3007
  ns_storage: Set of tuples (type_name, namespace) for each namespaced storage
3008
+ compiler_version: The compiler version to use, already resolved from target_file
3009
+ target_file: The user-specified file (from conf file) that imported original_file.
3010
+ Used to resolve compilation settings (solc_optimize_map, solc_via_ir_map, etc.)
3011
+ for the temporary harness file, since original_file may not have entries in those maps.
2982
3012
 
2983
3013
  Returns:
2984
3014
  NewStorageInfo: A tuple (fields, types) where:
@@ -2997,8 +3027,24 @@ class CertoraBuildGenerator:
2997
3027
  # directly can cause issues.
2998
3028
  tmp_file.write(f"import \"{original_file}\";\n\n")
2999
3029
  rel_path = os.path.relpath(tmp_file.name, Path.cwd())
3000
- if self.context.compiler_map:
3001
- self.context.compiler_map.update({rel_path: compiler_version}, last=False)
3030
+ tmp_abs_path = Util.abs_posix_path(tmp_file.name)
3031
+ # Add the temp file to all map attributes so that map lookups
3032
+ # (solc_optimize_map, solc_via_ir_map, etc.) succeed during compilation.
3033
+ # We resolve values from target_file (a user-specified file in context.file_paths)
3034
+ # rather than original_file (which may be a transitive dependency not in any map).
3035
+ # We store both relative and absolute path keys because different lookup sites
3036
+ # normalize paths differently: get_relevant_compiler uses relative paths,
3037
+ # while _handle_via_ir/_handle_optimize use absolute paths.
3038
+ for map_attr in self.context.app.attr_class.all_map_attrs():
3039
+ map_attr_value = getattr(self.context, map_attr, None)
3040
+ if not map_attr_value:
3041
+ continue
3042
+ attr_name = map_attr[:-len(Util.MAP_SUFFIX)]
3043
+ target_value = Ctx.get_map_attribute_value(
3044
+ self.context, Path(target_file), attr_name
3045
+ )
3046
+ if target_value is not None:
3047
+ map_attr_value.update({rel_path: target_value, tmp_abs_path: target_value}, last=False)
3002
3048
  # Write the harness contract with dummy fields for each namespaced storage
3003
3049
  var_to_slot = storageExtension.write_harness_contract(tmp_file, harness_name, ns_storage)
3004
3050
  tmp_file.flush()
@@ -4121,8 +4167,13 @@ def build_from_scratch(context: CertoraContext,
4121
4167
  indent=4,
4122
4168
  sort_keys=True)
4123
4169
 
4170
+ # check if asts file exists (only when --dump_asts is used)
4171
+ asts_file = Util.get_asts_file()
4172
+ asts_file_if_exists = asts_file if asts_file.exists() else None
4173
+
4124
4174
  return CachedFiles(certora_build_file, all_contract_files, build_output_props_file, may_store_in_build_cache,
4125
- path_with_additional_included_files=absolute_sources_dir)
4175
+ path_with_additional_included_files=absolute_sources_dir,
4176
+ asts_file=asts_file_if_exists)
4126
4177
 
4127
4178
 
4128
4179
  def build_from_cache_or_scratch(context: CertoraContext,
@@ -4204,6 +4255,10 @@ def build_from_cache_or_scratch(context: CertoraContext,
4204
4255
  else:
4205
4256
  shutil.copyfile(p, Util.get_certora_sources_dir() / p.name)
4206
4257
 
4258
+ # restore asts file from cache if present
4259
+ if cached_files.asts_file is not None and cached_files.asts_file.exists():
4260
+ shutil.copyfile(cached_files.asts_file, Util.get_asts_file())
4261
+
4207
4262
  return False, cached_files
4208
4263
 
4209
4264
 
@@ -41,6 +41,9 @@ class CachedFiles:
41
41
  # to keep .post_autofinders
42
42
  path_with_additional_included_files: Path
43
43
 
44
+ # optional asts file (only present when --dump_asts is used)
45
+ asts_file: Optional[Path] = None
46
+
44
47
  def all_exist(self, main_cache_entry_dir: Path, sub_cache_key: str) -> bool:
45
48
  exist = self.certora_build_file.exists() and \
46
49
  self.certora_build_file == main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.certora_build_suffix}"
@@ -55,6 +58,7 @@ class CachedFiles:
55
58
  file_list_suffix = "file_list.json"
56
59
  build_output_props_suffix = "build_output_props.json"
57
60
  additional_included_files = "additional_included_files"
61
+ asts_suffix = "asts.json"
58
62
 
59
63
 
60
64
  class CertoraBuildCacheManager:
@@ -124,8 +128,14 @@ class CertoraBuildCacheManager:
124
128
  return None
125
129
 
126
130
  additional_included_files = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.additional_included_files}"
131
+
132
+ # check if asts file exists in cache
133
+ asts_file_in_cache = main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.asts_suffix}"
134
+ asts_file = asts_file_in_cache if asts_file_in_cache.exists() else None
135
+
127
136
  return CachedFiles(certora_build_file, all_contract_files, build_output_props_file,
128
- may_store_in_build_cache=True, path_with_additional_included_files=additional_included_files)
137
+ may_store_in_build_cache=True, path_with_additional_included_files=additional_included_files,
138
+ asts_file=asts_file)
129
139
 
130
140
  @staticmethod
131
141
  def save_build_cache(context: CertoraContext, cached_files: CachedFiles) -> None:
@@ -170,6 +180,11 @@ class CertoraBuildCacheManager:
170
180
  # save additional props file
171
181
  shutil.copyfile(cached_files.build_output_props_file,
172
182
  main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.build_output_props_suffix}")
183
+ # save asts file if present
184
+ if cached_files.asts_file is not None and cached_files.asts_file.exists():
185
+ shutil.copyfile(cached_files.asts_file,
186
+ main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.asts_suffix}")
187
+
173
188
  # save .post_autofinders from additional included files
174
189
  trg_path_with_additional_included_files = \
175
190
  main_cache_entry_dir / f"{sub_cache_key}.{CachedFiles.additional_included_files}"
@@ -83,6 +83,9 @@ def build_rust_app(context: CertoraContext) -> None:
83
83
  build_command.extend(["--tools-version", context.cargo_tools_version])
84
84
  context.rust_project_directory = Util.find_nearest_cargo_toml()
85
85
 
86
+ if getattr(context, 'solana_sbf_arch', None) is not None:
87
+ build_command.extend(["--arch", context.solana_sbf_arch])
88
+
86
89
  if context.cargo_features is not None:
87
90
  build_command.append(feature_flag)
88
91
  build_command.append(' '.join(context.cargo_features))
@@ -55,13 +55,42 @@ def build_sui_project(context: CertoraContext, timings: Dict) -> None:
55
55
  def set_sui_build_directory(context: CertoraContext) -> None:
56
56
  sources: Set[Path] = set()
57
57
 
58
- # If no move_path was specified, try to build the package
59
- if not context.move_path:
60
- move_toml_file = Util.find_file_in_parents("Move.toml")
61
- if not move_toml_file:
62
- raise Util.CertoraUserInputError("Could not find Move.toml, and no move_path was specified.")
58
+ move_toml_file: Path | None = None
59
+
60
+ if context.move_path:
61
+ # If move_path is specified, we assume the user has already built the spec package separately. This is a legacy
62
+ # option that should not be used by new projects; they should use `spec_package_path` and/or `build_script`
63
+ # instead.
64
+ assert not context.build_script, "cannot have move_path and build_script together"
65
+ assert not context.spec_package_path, "cannot have move_path and spec_package_path together"
66
+ context.sui_package_summary_path = None
67
+ else:
68
+ if context.spec_package_path:
69
+ # Verify that there is a Move.toml at the specified spec_package_path
70
+ spec_package_dir = Path(context.spec_package_path)
71
+ move_toml_file = spec_package_dir / "Move.toml"
72
+ if not move_toml_file.exists():
73
+ raise Util.CertoraUserInputError(f"Could not find Move.toml at specified spec_package_path '{context.spec_package_path}'")
74
+ else:
75
+ # If spec_package_path is not specified, try to find the package that the current directory is in.
76
+ move_toml_file = Util.find_file_in_parents("Move.toml")
77
+ if not move_toml_file:
78
+ raise Util.CertoraUserInputError("Could not find Move.toml, and no spec_package_path was specified.")
79
+ spec_package_dir = move_toml_file.parent
80
+ context.spec_package_path = str(spec_package_dir)
81
+
63
82
  sources.add(move_toml_file.absolute())
64
- run_sui_build(context, move_toml_file.parent)
83
+ context.move_path = str(spec_package_dir / "build")
84
+ context.sui_package_summary_path = spec_package_dir / "package_summaries"
85
+
86
+ if context.build_script:
87
+ # Run the user-provided build script
88
+ script_path = Path(context.build_script).resolve()
89
+ sources.add(script_path)
90
+ run_sui_build([str(script_path), str(spec_package_dir)])
91
+ else:
92
+ # Build the package using `sui move summary`, which will also produce the package summaries.
93
+ run_sui_build(["sui", "move", "summary", "--test", "--path", str(move_toml_file.parent)])
65
94
 
66
95
  assert context.move_path, "expecting move_path to be set after build"
67
96
  move_dir = Path(context.move_path)
@@ -81,6 +110,12 @@ def set_sui_build_directory(context: CertoraContext) -> None:
81
110
  Util.get_build_dir() / move_dir.name,
82
111
  ignore=shutil.ignore_patterns('*.move'))
83
112
 
113
+ # Copy package summary directory if it exists. Projects built manually with "sui move build" may not have this.
114
+ if context.sui_package_summary_path and context.sui_package_summary_path.exists():
115
+ assert context.sui_package_summary_path.is_dir(), f"Package summary path '{context.sui_package_summary_path}' is not a directory"
116
+ shutil.copytree(context.sui_package_summary_path,
117
+ Util.get_build_dir() / context.sui_package_summary_path.name)
118
+
84
119
  try:
85
120
  # Create generators
86
121
  build_source_tree(sources, context)
@@ -88,11 +123,8 @@ def set_sui_build_directory(context: CertoraContext) -> None:
88
123
  except Exception as e:
89
124
  raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
90
125
 
91
- def run_sui_build(context: CertoraContext, package_dir: Path) -> None:
92
- assert not context.move_path, "run_sui_build: expecting move_path to be empty"
93
-
94
- build_cmd = ["sui", "move", "build", "--test", "--path", str(package_dir)]
95
126
 
127
+ def run_sui_build(build_cmd: list[str]) -> None:
96
128
  try:
97
129
  build_cmd_text = ' '.join(build_cmd)
98
130
  log.info(f"Building by calling `{build_cmd_text}`")
@@ -102,8 +134,6 @@ def run_sui_build(context: CertoraContext, package_dir: Path) -> None:
102
134
  if result.returncode != 0:
103
135
  raise Util.CertoraUserInputError(f"Error running `{build_cmd_text}`")
104
136
 
105
- context.move_path = str(package_dir / "build")
106
-
107
137
  except Util.TestResultsReady as e:
108
138
  raise e
109
139
  except Util.CertoraUserInputError as e:
@@ -611,10 +611,16 @@ class CloudVerification:
611
611
  rust_jar_settings.append('-solanaInlining')
612
612
  rust_jar_settings.append(paths_in_source_dir(self.context.solana_inlining))
613
613
 
614
+ if self.context.assert_on_panic is not None:
615
+ rust_jar_settings.extend(['-solanaAssertOnPanic', str(self.context.assert_on_panic).lower()])
616
+
614
617
  auth_data["jarSettings"] = rust_jar_settings + jar_settings
615
618
 
616
619
  elif Attrs.is_sui_app():
617
620
  sui_jar_settings = ['-movePath', Path(self.context.move_path).name]
621
+ if self.context.sui_package_summary_path and self.context.sui_package_summary_path.exists():
622
+ sui_jar_settings.append('-suiPackageSummaryPath')
623
+ sui_jar_settings.append(self.context.sui_package_summary_path.name)
618
624
  auth_data["jarSettings"] = sui_jar_settings + jar_settings
619
625
 
620
626
  elif Attrs.is_concord_app():
@@ -766,6 +772,9 @@ class CloudVerification:
766
772
  Util.get_configuration_layout_data_file(),
767
773
  Util.get_build_dir() / Path(self.context.move_path).name]
768
774
 
775
+ if self.context.sui_package_summary_path and self.context.sui_package_summary_path.exists():
776
+ files_list.append(Util.get_build_dir() / self.context.sui_package_summary_path.name)
777
+
769
778
  if Util.get_certora_sources_dir().exists():
770
779
  files_list.append(Util.get_certora_sources_dir())
771
780
 
@@ -63,6 +63,9 @@ def is_evm_app_class(context: CertoraContext) -> bool:
63
63
  def is_rust_app_class(context: CertoraContext) -> bool:
64
64
  return issubclass(context.app, App.RustAppClass)
65
65
 
66
+ def is_move_app_class(context: CertoraContext) -> bool:
67
+ return issubclass(context.app, App.MoveAppClass)
68
+
66
69
  def get_attr_value(context: CertoraContext, attr: AttrUtil.AttributeDefinition) -> Any:
67
70
  conf_key = attr.get_conf_key()
68
71
  return getattr(context, conf_key, None)
@@ -133,6 +136,9 @@ def get_local_run_cmd(context: CertoraContext) -> List[str]:
133
136
  # For local runs, we want path to be relative to cwd instead of zip root.
134
137
  move_rel_path = os.path.relpath(Path(context.move_path), os.getcwd())
135
138
  run_args.extend(['-movePath', move_rel_path])
139
+ if context.sui_package_summary_path and context.sui_package_summary_path.exists():
140
+ spec_package_summary_rel_path = os.path.relpath(context.sui_package_summary_path, os.getcwd())
141
+ run_args.extend(['-suiPackageSummaryPath', spec_package_summary_rel_path])
136
142
  elif is_rust_app_class(context):
137
143
  # For local runs, we want path to be relative to cwd instead of zip root.
138
144
  rust_rel_path = os.path.relpath(Path(context.files[0]), os.getcwd())
@@ -284,6 +290,9 @@ def get_args(args_list: List[str], app: Type[App.CertoraApp]) -> CertoraContext:
284
290
  Util.reset_certora_internal_dir(context.build_dir)
285
291
  os.rename(current_build_directory, context.build_dir)
286
292
 
293
+ if is_evm_app_class(context):
294
+ handle_vyper_system_path(context)
295
+
287
296
  # Store current options (including the ones read from .conf file)
288
297
  context.conf_options = current_conf_to_file(context)
289
298
 
@@ -297,6 +306,8 @@ def get_args(args_list: List[str], app: Type[App.CertoraApp]) -> CertoraContext:
297
306
  validator.handle_concord_attrs()
298
307
  if is_rust_app_class(context):
299
308
  validator.check_rust_args_post_argparse()
309
+ if is_move_app_class(context):
310
+ validator.check_move_args_post_argparse()
300
311
 
301
312
  attrs_to_relative(context)
302
313
  # Setup defaults (defaults are not recorded in conf file)
@@ -594,14 +605,14 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext, extra
594
605
  raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
595
606
  "To skip local checks run with the --disable_local_typechecking flag")
596
607
 
597
- def attrs_to_relative(context: CertoraContext) -> None:
598
- def to_relative(input_path: Union[str, Path]) -> Union[str, Path]:
599
- path = Path(input_path) if isinstance(input_path, str) else input_path
600
- if path.is_absolute():
601
- relative_path = os.path.relpath(path, cwd) # Path.relative_to only works for subdirectories
602
- return str(relative_path) if isinstance(input_path, str) else relative_path
603
- return input_path
608
+ def to_relative(input_path: Union[str, Path]) -> Union[str, Path]:
609
+ path = Path(input_path) if isinstance(input_path, str) else input_path
610
+ if path.is_absolute():
611
+ relative_path = os.path.relpath(path, Path.cwd()) # Path.relative_to only works for subdirectories
612
+ return str(relative_path) if isinstance(input_path, str) else relative_path
613
+ return input_path
604
614
 
615
+ def attrs_to_relative(context: CertoraContext) -> None:
605
616
  def str_attr_to_relative(attr_name: str) -> None:
606
617
  attr_value = getattr(context, attr_name, None)
607
618
  if attr_value:
@@ -647,7 +658,6 @@ def attrs_to_relative(context: CertoraContext) -> None:
647
658
  if Path(spec_file).is_absolute():
648
659
  context.verify = ':'.join([contract, str(to_relative(spec_file))])
649
660
 
650
- cwd = Path.cwd()
651
661
  str_attr_to_relative('packages_path')
652
662
  str_attr_to_relative('bytecode_spec')
653
663
  str_attr_to_relative('yul_abi')
@@ -683,3 +693,24 @@ def get_map_attribute_value(context: CertoraContext, path: Path, attr_name: str)
683
693
  if glob.globmatch(str(path), pattern, flags=glob.GLOBSTAR):
684
694
  return entry_value
685
695
  raise RuntimeError(f"cannot match {attr_name} to {path} from {attr_name}_map")
696
+
697
+ def handle_vyper_system_path(context: CertoraContext) -> None:
698
+ """
699
+ Just like the Vyper compiler, we add the sys.path to the search path for imports, which allows us to resolve imports
700
+ using a package manager.
701
+
702
+ Note that we do this here, rather than allowing the Vyper compiler to do it, because we want to persist these paths
703
+ in the generated run.conf file, to make it easy to reproduce in a different environment.
704
+ """
705
+ if context.resolve_vyper_imports:
706
+ if not context.vyper_system_path:
707
+ # Pass the directories from sys.path in reversed order (Vyper treats later paths as higher precedence)
708
+ context.vyper_system_path = [
709
+ to_relative(p) for p in reversed(sys.path) if Path(p).is_dir()
710
+ ]
711
+ else:
712
+ # Filter out any directories that do not exist, to make the generated run.conf file more portable to
713
+ # different environments.
714
+ context.vyper_system_path = [
715
+ p for p in context.vyper_system_path if Path(p).is_dir()
716
+ ]