certora-cli-alpha-master 20241216.19.20.76131__tar.gz → 20241219.20.36.174596__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. {certora_cli_alpha_master-20241216.19.20.76131/certora_cli_alpha_master.egg-info → certora_cli_alpha_master-20241219.20.36.174596}/PKG-INFO +2 -2
  2. certora_cli_alpha_master-20241219.20.36.174596/README.md +1 -0
  3. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraBuild.py +11 -1
  4. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraCloudIO.py +95 -47
  5. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraConfigIO.py +7 -3
  6. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraContext.py +23 -65
  7. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraContextAttributes.py +51 -36
  8. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraContextValidator.py +13 -15
  9. certora_cli_alpha_master-20241219.20.36.174596/certora_cli/EVMVerifier/certoraParseBuildScript.py +36 -0
  10. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraType.py +1 -0
  11. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Mutate/mutateApp.py +16 -7
  12. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Shared/certoraUtils.py +8 -10
  13. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Shared/certoraValidateFuncs.py +14 -7
  14. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/certoraRun.py +3 -5
  15. certora_cli_alpha_master-20241219.20.36.174596/certora_cli/certoraSolanaProver.py +153 -0
  16. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/rustMutator.py +135 -77
  17. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596/certora_cli_alpha_master.egg-info}/PKG-INFO +2 -2
  18. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli_alpha_master.egg-info/SOURCES.txt +2 -0
  19. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli_alpha_master.egg-info/entry_points.txt +1 -0
  20. certora_cli_alpha_master-20241219.20.36.174596/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  21. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_jars/Typechecker.jar +0 -0
  22. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/setup.py +4 -3
  23. certora_cli_alpha_master-20241216.19.20.76131/README.md +0 -1
  24. certora_cli_alpha_master-20241216.19.20.76131/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  25. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/LICENSE +0 -0
  26. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/MANIFEST.in +0 -0
  27. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_bins/__init__.py +0 -0
  28. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/CompilerCollector.py +0 -0
  29. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/CompilerCollectorFactory.py +0 -0
  30. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSol.py +0 -0
  31. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSolBased.py +0 -0
  32. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/CompilerCollectorVy.py +0 -0
  33. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/CompilerCollectorYul.py +0 -0
  34. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/Compiler/__init__.py +0 -0
  35. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/__init__.py +0 -0
  36. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraBuildCacheManager.py +0 -0
  37. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraBuildDataClasses.py +0 -0
  38. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraCollectRunMetadata.py +0 -0
  39. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraCompilerParameters.py +0 -0
  40. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraContextClass.py +0 -0
  41. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraContractFuncs.py +0 -0
  42. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraExtensionInfo.py +0 -0
  43. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraJobList.py +0 -0
  44. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraMiniSpecParser.py +0 -0
  45. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraNodeFilters.py +0 -0
  46. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraSourceFinders.py +0 -0
  47. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EVMVerifier/certoraVerifyGenerator.py +0 -0
  48. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  49. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  50. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  51. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  52. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  53. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  54. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
  55. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  56. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Mutate/__init__.py +0 -0
  57. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Mutate/mutateAttributes.py +0 -0
  58. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Mutate/mutateConstants.py +0 -0
  59. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Mutate/mutateUtil.py +0 -0
  60. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Mutate/mutateValidate.py +0 -0
  61. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Shared/ExpectedComparator.py +0 -0
  62. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Shared/__init__.py +0 -0
  63. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Shared/certoraAttrUtil.py +0 -0
  64. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/Shared/certoraLogging.py +0 -0
  65. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/__init__.py +0 -0
  66. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/certoraEqCheck.py +0 -0
  67. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli/certoraMutate.py +0 -0
  68. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli_alpha_master.egg-info/dependency_links.txt +0 -0
  69. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli_alpha_master.egg-info/requires.txt +0 -0
  70. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_cli_alpha_master.egg-info/top_level.txt +0 -0
  71. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/certora_jars/__init__.py +0 -0
  72. {certora_cli_alpha_master-20241216.19.20.76131 → certora_cli_alpha_master-20241219.20.36.174596}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: certora-cli-alpha-master
3
- Version: 20241216.19.20.76131
3
+ Version: 20241219.20.36.174596
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli-alpha-master
6
6
  Author: Certora
@@ -23,4 +23,4 @@ Requires-Dist: StrEnum
23
23
  Requires-Dist: tomli
24
24
  Requires-Dist: universalmutator
25
25
 
26
- Commit 8fa71f6. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
26
+ Commit a0a7788. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit a0a7788. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -1577,7 +1577,17 @@ class CertoraBuildGenerator:
1577
1577
  while queue:
1578
1578
  pop = queue.pop(0)
1579
1579
  if isinstance(pop, dict) and node_id_attrb in pop:
1580
- container[int(pop[node_id_attrb])] = pop
1580
+ idAttr = pop[node_id_attrb]
1581
+ if isinstance(idAttr, int):
1582
+ container[int(idAttr)] = pop
1583
+ elif isinstance(idAttr, list) and len(idAttr) == 1 and isinstance(idAttr[0], int):
1584
+ # In the bug reported in https://certora.atlassian.net/browse/CERT-7643 the id field is a list
1585
+ # instead of a single integer - we except that the first element of the list is the actual id
1586
+ # and unpack it.
1587
+ container[int(idAttr[0])] = pop
1588
+ else:
1589
+ raise Exception(f"Unexpected type of attribute `{node_id_attrb}`, was {idAttr}, expected an integer or an int-typed list of length 1")
1590
+
1581
1591
  for key, value in pop.items():
1582
1592
  if (node_type_attrb in pop and
1583
1593
  pop[node_type_attrb] == "InlineAssembly" and key == "externalReferences"):
@@ -1,7 +1,9 @@
1
+ import glob
1
2
  import itertools
2
3
  import json
3
4
  import os
4
5
  import re
6
+ import shutil
5
7
  import uuid
6
8
 
7
9
  import requests
@@ -190,21 +192,41 @@ def parse_json(response: Response) -> Dict[str, Any]:
190
192
  return json_response
191
193
 
192
194
 
193
- def compress_files_wasm(zip_file_path: Path, context: CertoraContext) -> bool:
195
+ def zip_rust_files(zip_file_path: Path, context: CertoraContext, *resource_paths: Path) -> bool:
196
+ patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
197
+ exclude_dirs = [".certora_internal"]
198
+
199
+ root_directory = Path(context.rust_project_directory)
200
+ if not root_directory.is_dir():
201
+ raise ValueError(f"The given directory {root_directory} is not valid.")
202
+
194
203
  with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zip_obj:
195
- cargo_directory = context.cargo_dir
196
- for path in cargo_directory.rglob('*.rs'):
197
- if cargo_directory / 'target' in path.parents or cargo_directory / '.certora_internal' in path.parents:
198
- continue
199
- zip_obj.write(path, Util.CERTORA_SOURCES / path.relative_to(cargo_directory))
200
- for path in [cargo_directory / 'Cargo.toml', cargo_directory / 'Cargo.lock']:
201
- if path.exists():
202
- zip_obj.write(path, path.relative_to(cargo_directory))
203
- if context.wasm_file.exists():
204
- zip_obj.write(context.wasm_file, context.wasm_file.relative_to(context.wasm_file.parent))
204
+ for source in context.rust_sources:
205
+ for file in glob.glob(f'{root_directory.joinpath(source)}', recursive=True):
206
+ file_path = Path(file)
207
+ if any(excluded in file_path.parts for excluded in exclude_dirs):
208
+ continue
209
+ if file_path.is_file() and any(file_path.match(pattern) for pattern in patterns):
210
+ zip_obj.write(file_path, Util.CERTORA_SOURCES / file_path.relative_to(root_directory))
211
+ if context.build_script and Path(context.build_script).exists():
212
+ zip_obj.write(Path(context.build_script),
213
+ Util.CERTORA_SOURCES / Path(context.build_script).absolute().relative_to(root_directory))
214
+ if context.conf_file and Path(context.conf_file).exists():
215
+ zip_obj.write(Path(context.conf_file),
216
+ Util.CERTORA_SOURCES / Path(context.conf_file).absolute().relative_to(root_directory))
217
+ if Util.get_last_conf_file():
218
+ zip_obj.write(Util.get_last_conf_file(),
219
+ Util.CERTORA_SOURCES / Util.get_last_conf_file().relative_to(Util.get_build_dir()))
220
+
221
+ rust_executable = root_directory.joinpath(context.rust_executables)
222
+ zip_obj.write(rust_executable, rust_executable.relative_to(rust_executable.parent))
223
+ for path in resource_paths:
224
+ zip_obj.write(path, os.path.relpath(path, Util.get_build_dir()))
225
+ if path.suffix == '.txt':
226
+ zip_obj.write(path, Util.CERTORA_SOURCES / path.relative_to(Util.get_build_dir()))
205
227
 
206
228
  if zip_file_path.stat().st_size > MAX_FILE_SIZE:
207
- cloud_logger.error(f"{GENERAL_ERR_PREFIX} Max 25MB file size exceeded.")
229
+ cloud_logger.error(f"{GENERAL_ERR_PREFIX} Max 25MB file size exceeded. File is located at {zip_file_path}")
208
230
  return False
209
231
  return True
210
232
 
@@ -600,8 +622,12 @@ class CloudVerification:
600
622
  # We need to strip "../" path component from all file paths because
601
623
  # unzip will also do that.
602
624
  solana_jar_settings = []
603
- for file in self.context.files:
604
- solana_jar_settings.append(file.split('../')[-1])
625
+ if hasattr(self.context, 'build_script') and self.context.build_script:
626
+ solana_jar_settings.append(Path(self.context.rust_executables).name)
627
+
628
+ else:
629
+ for file in self.context.files:
630
+ solana_jar_settings.append(file.split('../')[-1])
605
631
 
606
632
  is_file = False
607
633
  for arg in jar_settings:
@@ -616,18 +642,19 @@ class CloudVerification:
616
642
  elif arg == '-solanaSummaries':
617
643
  is_file = True
618
644
  auth_data["jarSettings"] = solana_jar_settings
619
- elif Attrs.is_wasm_app():
645
+ elif Attrs.is_soroban_app():
620
646
  # We need to strip "../" path component from all file paths because
621
647
  # unzip will also do that.
622
- wasm_jar_settings = []
623
- if hasattr(self.context, 'wasm_file') and self.context.wasm_file:
624
- wasm_jar_settings.append(self.context.wasm_file.name) # assuming root is cargo_directory
648
+ soroban_jar_settings = []
649
+ # not needed - should be in files
650
+ if hasattr(self.context, 'build_script') and self.context.build_script:
651
+ soroban_jar_settings.append(Path(self.context.rust_executables).name)
625
652
  else:
626
653
  for file in self.context.files:
627
- wasm_jar_settings.append(file.split('../')[-1])
654
+ soroban_jar_settings.append(file.split('../')[-1])
628
655
  for arg in jar_settings:
629
- wasm_jar_settings.append(arg)
630
- auth_data["jarSettings"] = wasm_jar_settings
656
+ soroban_jar_settings.append(arg)
657
+ auth_data["jarSettings"] = soroban_jar_settings
631
658
  else:
632
659
  auth_data["jarSettings"] = jar_settings
633
660
 
@@ -738,40 +765,61 @@ class CloudVerification:
738
765
  short_output=Ctx.is_minimal_cli_output(self.context))
739
766
  elif Attrs.is_evm_app() and self.context.is_bytecode:
740
767
  # We zip the bytecode jsons and the spec
741
- paths = []
768
+ paths = [Util.get_certora_build_file(), Util.get_certora_verify_file(),
769
+ Util.get_certora_metadata_file()]
770
+ if Util.get_certora_sources_dir().exists():
771
+ paths.append(Util.get_certora_sources_dir())
772
+
742
773
  for bytecode_json in self.context.bytecode_jsons:
743
774
  paths.append(Path(bytecode_json))
744
775
  paths.append(Path(self.context.bytecode_spec))
745
776
  result = compress_files(self.ZipFilePath, *paths,
746
777
  short_output=Ctx.is_minimal_cli_output(self.context))
747
- elif Attrs.is_solana_app():
748
- # We zip the ELF files and the two configuration files
749
- jar_args = Ctx.collect_jar_args(self.context)
750
- paths = []
751
- for file in self.context.files:
752
- paths.append(Path(file))
753
- is_file = False
754
- for arg in jar_args:
755
- if is_file:
756
- paths.append(Path(arg))
757
- is_file = False
778
+ elif Attrs.is_rust_app():
779
+ files_list = [Util.get_certora_metadata_file()]
780
+
781
+ if hasattr(self.context, 'build_script') and self.context.build_script:
782
+ if self.context.prover_resource_files:
783
+ for value in self.context.prover_resource_files:
784
+ _, file_path = value.split(':')
785
+ cur_path = (Path(self.context.conf_file).parent / Path(file_path)).resolve()
786
+ shutil.copy(cur_path, Util.get_build_dir())
787
+ files_list.append(Util.get_build_dir() / cur_path.name)
788
+
789
+ result = zip_rust_files(self.ZipFilePath, self.context, *files_list)
790
+
791
+ elif Attrs.is_solana_app():
792
+ # We zip the ELF files and the two configuration files
793
+ jar_args = Ctx.collect_jar_args(self.context)
758
794
 
759
- if arg == '-solanaInlining':
760
- is_file = True
761
- elif arg == '-solanaSummaries':
762
- is_file = True
763
- result = compress_files(self.ZipFilePath, *paths,
764
- short_output=Ctx.is_minimal_cli_output(self.context))
765
- elif Attrs.is_wasm_app():
766
- # We zip the wat file
767
- paths = []
768
- if hasattr(self.context, 'wasm_file') and self.context.wasm_file:
769
- result = compress_files_wasm(self.ZipFilePath, self.context)
770
- else:
771
795
  for file in self.context.files:
772
- paths.append(Path(file))
773
- result = compress_files(self.ZipFilePath, *paths,
796
+ files_list.append(Path(file))
797
+ is_file = False
798
+ for arg in jar_args:
799
+ if is_file:
800
+ files_list.append(Path(arg))
801
+ is_file = False
802
+
803
+ if arg == '-solanaInlining':
804
+ is_file = True
805
+ elif arg == '-solanaSummaries':
806
+ is_file = True
807
+ result = compress_files(self.ZipFilePath, *files_list,
774
808
  short_output=Ctx.is_minimal_cli_output(self.context))
809
+
810
+ elif Attrs.is_soroban_app():
811
+ # We zip the wat file
812
+ for file in self.context.files:
813
+ files_list.append(Path(file))
814
+ result = compress_files(self.ZipFilePath, *files_list,
815
+ short_output=Ctx.is_minimal_cli_output(self.context))
816
+ else:
817
+ raise Util.CertoraUserInputError(
818
+ 'When running rust application, context should either have attribute "rust_executables" '
819
+ 'provided by build_script execution, '
820
+ 'or either is_solana_app(), is_soroban_app() should be set to True'
821
+ )
822
+
775
823
  else:
776
824
  # Zip the log file first separately and again with the rest of the files, so it will not be decompressed
777
825
  # on each run in order to save space
@@ -53,7 +53,6 @@ def read_from_conf_file(context: CertoraContext) -> None:
53
53
  context namespace if the key was not set in the command line (command line shadows conf data).
54
54
  @param context: A namespace containing options from the command line
55
55
  """
56
-
57
56
  conf_file_path = Path(context.files[0])
58
57
  assert conf_file_path.suffix == ".conf", f"conf file must be of type .conf, instead got {conf_file_path}"
59
58
 
@@ -86,6 +85,11 @@ def check_conf_content(conf: Dict[str, Any], context: CertoraContext) -> None:
86
85
  f" file ({conf_val})")
87
86
  else:
88
87
  raise Util.CertoraUserInputError(f"{option} appears in the conf file but is not a known attribute. ")
88
+
89
89
  if 'files' not in conf:
90
- raise Util.CertoraUserInputError("Mandatory 'files' attribute is missing from the configuration")
91
- context.files = conf['files'] # Override the current .conf file
90
+ if Attrs.is_rust_app() and hasattr(context, 'build_script') and context.build_script is not None:
91
+ context.files = conf.get('files', None) # Override the current .conf file
92
+ else:
93
+ raise Util.CertoraUserInputError("Mandatory 'files' attribute is missing from the configuration")
94
+ else:
95
+ context.files = conf['files'] # Override the current .conf file
@@ -1,23 +1,17 @@
1
1
  import argparse
2
2
  import hashlib
3
3
  import json
4
- import logging
5
4
  import os
6
5
  import re
7
6
  import sys
8
- import shutil
9
- import platform
7
+ import logging
10
8
 
11
- # tomllib was added to python 3.11 this import is for supporting older versions
12
- if sys.version_info >= (3, 11):
13
- import tomllib
14
- else:
15
- import tomli as tomllib
16
9
 
17
10
  from pathlib import Path
18
11
  from typing import Dict, List, Optional, Any
19
12
  from rich.console import Console
20
13
 
14
+ from EVMVerifier.certoraParseBuildScript import run_script_and_parse_json
21
15
 
22
16
  scripts_dir_path = Path(__file__).parent.resolve() # containing directory
23
17
  sys.path.insert(0, str(scripts_dir_path))
@@ -112,14 +106,15 @@ def get_local_run_cmd(context: CertoraContext) -> str:
112
106
  @return: A command for running the prover locally
113
107
  """
114
108
  run_args = []
115
- if context.is_tac or Attrs.is_solana_app():
116
- run_args.append(context.files[0])
117
109
 
118
- if Attrs.is_wasm_app():
119
- if hasattr(context, 'wasm_file') and context.wasm_file:
120
- run_args.append(str(context.wasm_file))
121
- else:
110
+ if hasattr(context, 'rust_executables') and hasattr(context, 'rust_project_directory'):
111
+ run_args.append(os.path.join(context.rust_project_directory, context.rust_executables))
112
+ elif context.is_tac or Attrs.is_rust_app():
113
+ # For Rust app we assume the files holds the executable for the prover, currently we support a single file
114
+ try:
122
115
  run_args.append(context.files[0])
116
+ except Exception:
117
+ raise RuntimeError("get_local_run_cmd: cannot find context.files[0]")
123
118
 
124
119
  if Attrs.is_evm_app() and context.cache is not None:
125
120
  run_args.extend(['-cache', context.cache])
@@ -158,7 +153,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
158
153
  console = Console()
159
154
  console.print("\n\nThe Certora Prover - A formal verification tool for smart contracts")
160
155
  # Using sys.stdout.write() as print() would color some of the strings here
161
- sys.stdout.write("\n\nUsage: certoraRun <Files> <Flags>\n\n")
156
+ sys.stdout.write(f"\n\nUsage: {sys.argv[0]} <Files> <Flags>\n\n")
162
157
  sys.stdout.write("Files are Solidity, Vyper contract files, a shared object Solana contract file, "
163
158
  "or a conf file. Solidity contracts are denoted as file:contract "
164
159
  "e.g. f.sol:A. If the contract name is identical to the file name, "
@@ -171,7 +166,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
171
166
  console.print("3. list (L): gets multiple strings as a value, flags may also appear multiple times")
172
167
  console.print("4. map (M): collection of key, value pairs\n\n")
173
168
 
174
- Attrs.EvmProverAttributes.print_attr_help()
169
+ Attrs.get_attribute_class().print_attr_help()
175
170
  console.print("\n\nYou can find detailed documentation of the supported flags here: "
176
171
  f"{Util.print_rich_link(CLI_DOCUMENTATION_URL)}\n\n")
177
172
 
@@ -242,7 +237,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
242
237
 
243
238
  validator = Cv.CertoraContextValidator(context)
244
239
  validator.validate()
245
- if Attrs.is_evm_app():
240
+ if Attrs.is_evm_app() or Attrs.is_rust_app():
246
241
  current_build_directory = Util.get_build_dir()
247
242
  if context.build_dir is not None and current_build_directory != context.build_dir:
248
243
  Util.reset_certora_internal_dir(context.build_dir)
@@ -527,52 +522,15 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> No
527
522
  raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
528
523
  "To skip local checks run with the --disable_local_typechecking flag")
529
524
 
530
- def set_cargo_name(context: CertoraContext) -> None:
531
- mode = "r" if platform.system().lower() == 'windows' else "rb"
532
- with open(context.cargo_file, mode) as f:
533
- cargo_data = tomllib.load(f)
534
-
535
- if 'package' in cargo_data and 'name' in cargo_data['package']:
536
- context.cargo_name = cargo_data['package']['name'].replace('-', '_')
537
-
538
- def build_wasm(context: CertoraContext) -> None:
539
- if len(context.files) != 1:
540
- raise Util.CertoraUserInputError(f"expected a single wasm file ({Util.CARGO_FILE} or .wat file)."
541
- f" Got: {context.files}")
542
- file_path = Path(context.files[0])
543
- if file_path.name != Util.CARGO_FILE:
544
- return
545
- context.cargo_dir = file_path.parent
546
- context.cargo_file = file_path
547
- set_cargo_name(context)
548
- run_wasm_compiler(context)
549
-
550
-
551
- def run_wasm_compiler(context: CertoraContext) -> None:
552
- env = os.environ.copy()
553
- env['RUSTFLAGS'] = "-C strip=none --emit=llvm-ir -C debuginfo=2"
554
- try:
555
- context_logger.info("Executing build process...")
556
525
 
557
- cmd = f"cargo build --target={context.wasm_target} --release --features certora"
558
- Util.run_shell_command(cmd, env=env, cwd=context.cargo_dir)
559
-
560
- context.wasm_file = Path(f"{context.cargo_dir}/target/{context.wasm_target}/release/{context.cargo_name}.wasm")
561
- intermediate_wasm = Path("tmpwasm.wasm")
562
-
563
- wat_file = context.wasm_file.with_suffix(".wat")
564
-
565
- cmd = f"wasm2wat {context.wasm_file} --generate-names -o {wat_file}"
566
- Util.run_shell_command(cmd, cwd=context.cargo_dir, env=env)
567
-
568
- cmd = f"wat2wasm {wat_file} --debug-names -o {intermediate_wasm}"
569
- Util.run_shell_command(cmd, cwd=context.cargo_dir, env=env)
570
-
571
- cmd = f"wasm2wat {intermediate_wasm} -o {wat_file}"
572
- Util.run_shell_command(cmd, cwd=context.cargo_dir, env=env)
573
-
574
- intermediate_wasm.unlink()
575
- shutil.copy(context.wasm_file, context.cargo_dir)
576
-
577
- except Exception as e:
578
- raise RuntimeError(f"Failed to build wasm file\n{e}")
526
+ def build_rust_app(context: CertoraContext) -> None:
527
+ if context.build_script:
528
+ run_script_and_parse_json(context)
529
+ if not context.rust_executables:
530
+ raise Util.CertoraUserInputError("failed to get target executable")
531
+ else:
532
+ if not context.files:
533
+ raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
534
+ if len(context.files) > 1:
535
+ raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
536
+ context.rust_executables = context.files[0]
@@ -1,4 +1,5 @@
1
1
  import logging
2
+
2
3
  import json5
3
4
  import sys
4
5
  from pathlib import Path
@@ -37,6 +38,17 @@ def validate_prover_args(value: str) -> str:
37
38
 
38
39
 
39
40
  class CommonAttributes(AttrUtil.Attributes):
41
+ COMPILATION_STEPS_ONLY = AttrUtil.AttributeDefinition(
42
+ arg_type=AttrUtil.AttrArgType.BOOLEAN,
43
+ help_msg="Compile the spec and the code without sending a verification request to the cloud",
44
+ default_desc="Sends a request after source compilation and spec syntax checking",
45
+ argparse_args={
46
+ 'action': AttrUtil.STORE_TRUE
47
+ },
48
+ affects_build_cache_key=False,
49
+ disables_build_cache=False
50
+ )
51
+
40
52
  MSG = AttrUtil.AttributeDefinition(
41
53
  attr_validation_func=Vf.validate_msg,
42
54
  help_msg="Add a message description to your run",
@@ -149,6 +161,15 @@ class CommonAttributes(AttrUtil.Attributes):
149
161
  disables_build_cache=False
150
162
  )
151
163
 
164
+ BUILD_DIR = AttrUtil.AttributeDefinition(
165
+ attr_validation_func=Vf.validate_build_dir,
166
+ argparse_args={
167
+ 'action': AttrUtil.UniqueStore
168
+ },
169
+ affects_build_cache_key=False,
170
+ disables_build_cache=False
171
+ )
172
+
152
173
  class DeprecatedAttributes(AttrUtil.Attributes):
153
174
  AUTO_NONDET_DIFFICULT_INTERNAL_FUNCS = AttrUtil.AttributeDefinition(
154
175
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
@@ -418,17 +439,6 @@ class EvmAttributes(AttrUtil.Attributes):
418
439
  disables_build_cache=False
419
440
  )
420
441
 
421
- COMPILATION_STEPS_ONLY = AttrUtil.AttributeDefinition(
422
- arg_type=AttrUtil.AttrArgType.BOOLEAN,
423
- help_msg="Compile the spec and the code without sending a verification request to the cloud",
424
- default_desc="Sends a request after source compilation and spec syntax checking",
425
- argparse_args={
426
- 'action': AttrUtil.STORE_TRUE
427
- },
428
- affects_build_cache_key=False,
429
- disables_build_cache=False
430
- )
431
-
432
442
  NO_MEMORY_SAFE_AUTOFINDERS = AttrUtil.AttributeDefinition(
433
443
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
434
444
  # This is a hidden flag, the following two attributes are left intentionally as comments to help devs
@@ -637,15 +647,6 @@ class EvmAttributes(AttrUtil.Attributes):
637
647
  disables_build_cache=False
638
648
  )
639
649
 
640
- BUILD_DIR = AttrUtil.AttributeDefinition(
641
- attr_validation_func=Vf.validate_build_dir,
642
- argparse_args={
643
- 'action': AttrUtil.UniqueStore
644
- },
645
- affects_build_cache_key=False,
646
- disables_build_cache=False
647
- )
648
-
649
650
  DISABLE_LOCAL_TYPECHECKING = AttrUtil.AttributeDefinition(
650
651
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
651
652
  argparse_args={
@@ -1384,6 +1385,20 @@ class BackendAttributes(AttrUtil.Attributes):
1384
1385
  disables_build_cache=False
1385
1386
  )
1386
1387
 
1388
+
1389
+ class RustAttributes(AttrUtil.Attributes):
1390
+
1391
+ BUILD_SCRIPT = AttrUtil.AttributeDefinition(
1392
+ help_msg="Python script to build the project",
1393
+ default_desc="Using default building command",
1394
+ argparse_args={
1395
+ 'action': AttrUtil.UniqueStore
1396
+ },
1397
+ affects_build_cache_key=False,
1398
+ disables_build_cache=False
1399
+ )
1400
+
1401
+
1387
1402
  class EvmProverAttributes(CommonAttributes, DeprecatedAttributes, EvmAttributes, InternalUseAttributes,
1388
1403
  BackendAttributes):
1389
1404
  FILES = AttrUtil.AttributeDefinition(
@@ -1399,9 +1414,9 @@ class EvmProverAttributes(CommonAttributes, DeprecatedAttributes, EvmAttributes,
1399
1414
  )
1400
1415
 
1401
1416
 
1402
- class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes):
1417
+ class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes, RustAttributes):
1403
1418
  FILES = AttrUtil.AttributeDefinition(
1404
- attr_validation_func=Vf.validate_wasm_file,
1419
+ attr_validation_func=Vf.validate_soroban_extension,
1405
1420
  arg_type=AttrUtil.AttrArgType.LIST,
1406
1421
  help_msg="binary .wat files for the Prover",
1407
1422
  default_desc="",
@@ -1412,19 +1427,10 @@ class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAt
1412
1427
  disables_build_cache=False
1413
1428
  )
1414
1429
 
1415
- WASM_TARGET = AttrUtil.AttributeDefinition(
1416
- default_desc="",
1417
- argparse_args={
1418
- 'action': AttrUtil.UniqueStore,
1419
- 'default': "wasm32-unknown-unknown"
1420
- },
1421
- affects_build_cache_key=False,
1422
- disables_build_cache=False
1423
- )
1424
1430
 
1425
- class SolanaProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes):
1431
+ class SolanaProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes, RustAttributes):
1426
1432
  FILES = AttrUtil.AttributeDefinition(
1427
- attr_validation_func=Vf.validate_solana_file,
1433
+ attr_validation_func=Vf.validate_solana_extension,
1428
1434
  arg_type=AttrUtil.AttrArgType.LIST,
1429
1435
  help_msg="contract files for analysis SOLANA_FILE.so or a conf file",
1430
1436
 
@@ -1460,9 +1466,9 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1460
1466
  def application_by_suffix(file: str) -> Type[AttrUtil.Attributes]:
1461
1467
  if file.endswith(Util.EVM_EXTENSIONS):
1462
1468
  return EvmProverAttributes
1463
- elif file.endswith(Util.WASM_EXTENSIONS) or Path(file).name == Util.CARGO_FILE:
1469
+ elif file.endswith(Util.SOROBAN_EXEC_EXTENSION):
1464
1470
  return SorobanProverAttributes
1465
- elif file.endswith(Util.SOLANA_EXTENSIONS):
1471
+ elif file.endswith(Util.SOLANA_EXEC_EXTENSION):
1466
1472
  return SolanaProverAttributes
1467
1473
  elif file.endswith('.conf'):
1468
1474
  raise Util.CertoraUserInputError(f"Cannot use conf files inside a conf file: {file}")
@@ -1472,6 +1478,7 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1472
1478
  cli_files = []
1473
1479
  cli_conf_files = []
1474
1480
  files = []
1481
+ build_script = None
1475
1482
  for arg in args:
1476
1483
  if arg.startswith('-'):
1477
1484
  break # Stop processing when a flag is detected
@@ -1485,6 +1492,10 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1485
1492
  with conf_file_path.open() as conf_file:
1486
1493
  configuration = json5.load(conf_file, allow_duplicate_keys=False)
1487
1494
  files = configuration.get('files', [])
1495
+ build_script = configuration.get('build_script')
1496
+
1497
+ if build_script:
1498
+ return SorobanProverAttributes
1488
1499
 
1489
1500
  if len(cli_conf_files) == 0:
1490
1501
  files = cli_files
@@ -1516,9 +1527,13 @@ def is_solana_app() -> bool:
1516
1527
  return get_attribute_class() == SolanaProverAttributes
1517
1528
 
1518
1529
 
1519
- def is_wasm_app() -> bool:
1530
+ def is_soroban_app() -> bool:
1520
1531
  return get_attribute_class() == SorobanProverAttributes
1521
1532
 
1522
1533
 
1534
+ def is_rust_app() -> bool:
1535
+ return is_soroban_app() or is_solana_app()
1536
+
1537
+
1523
1538
  def is_evm_app() -> bool:
1524
1539
  return get_attribute_class() == EvmProverAttributes
@@ -173,14 +173,17 @@ class CertoraContextValidator:
173
173
  "'disable_source_finders' is set to true while 'dynamic_bound' is set to 1 or higher"
174
174
  )
175
175
  context.disable_source_finders = True
176
-
176
+ package_name = Util.get_package_and_version()[1]
177
177
  # if --fe_version was not set then if the package is alpha/beta we set it to latest else we set it to production
178
178
  if not context.fe_version:
179
- lastest_packages = [Util.ALPHA_PACKAGE_NAME, Util.BETA_PACKAGE_NAME, Util.BETA_MIRROR_PACKAGE_NAME]
180
- if Util.get_package_and_version()[1] in lastest_packages:
179
+ lastest_packages = [Util.ALPHA_PACKAGE_MASTER_NAME, Util.BETA_PACKAGE_NAME, Util.BETA_MIRROR_PACKAGE_NAME]
180
+ if package_name in lastest_packages:
181
181
  context.fe_version = str(Util.FeValue.LATEST)
182
182
  else:
183
183
  context.fe_version = str(Util.FeValue.PRODUCTION)
184
+ if Util.ALPHA_PACKAGE_NAME in package_name and not context.prover_version:
185
+ raise Util.CertoraUserInputError('When running an "alpha release", a specific branch must be supplied '
186
+ 'via --prover_version. For example: "--prover_version master"')
184
187
 
185
188
  check_conflicting_branch_and_hash(context)
186
189
  set_wait_for_results_default(context)
@@ -409,8 +412,7 @@ def check_contract_name_arg_inputs(context: CertoraContext) -> None:
409
412
  def valid_input_file(filename: str) -> bool:
410
413
  pattern_match_sol = bool(re.match(r'([\w.$]+)\.sol:([\w$]+)', Path(filename).name))
411
414
  ends_with_extension = any(filename.endswith(ext) for ext in Util.VALID_FILE_EXTENSIONS)
412
- is_cargo_file = Path(filename).name == Util.CARGO_FILE
413
- return pattern_match_sol or ends_with_extension or is_cargo_file
415
+ return pattern_match_sol or ends_with_extension
414
416
 
415
417
 
416
418
  def check_files_attribute(context: CertoraContext) -> None:
@@ -492,7 +494,7 @@ def check_mode_of_operation(context: CertoraContext) -> None:
492
494
  raise Util.CertoraUserInputError(
493
495
  f"Cannot use conf files inside a conf file: {','.join(conf_files_inside_conf_file)}")
494
496
 
495
- special_file_suffixes = [".tac", ".json"] + list(Util.SOLANA_EXTENSIONS) + list(Util.WASM_EXTENSIONS)
497
+ special_file_suffixes = [".tac", ".json"] + [Util.SOLANA_EXEC_EXTENSION, Util.SOROBAN_EXEC_EXTENSION]
496
498
  for input_file in context.files:
497
499
  special_file_type = next((suffix for suffix in special_file_suffixes if input_file.endswith(suffix)), None)
498
500
 
@@ -710,7 +712,6 @@ def validate_certora_key() -> str:
710
712
  raise Util.CertoraUserInputError(f"environment variable {KEY_ENV_VAR} has an illegal length")
711
713
  return key
712
714
 
713
-
714
715
  def check_files_input(file_list: List[str]) -> None:
715
716
  """
716
717
  Verifies that correct input was inserted as input to files.
@@ -719,8 +720,8 @@ def check_files_input(file_list: List[str]) -> None:
719
720
  The allowed disjoint cases are:
720
721
  1. Use a single .conf file
721
722
  2. Use a single .tac file
722
- 3. Use a single .o or .so file
723
- 4. Use a single .wat or .wasm file or a single Cargo.toml file
723
+ 3. Use a single .so file
724
+ 4. Use a single .wasm file
724
725
  5. Use any number of [contract.sol:nickname ...] (at least one is guaranteed by argparser)
725
726
  @param file_list: A list of strings representing file paths
726
727
  @raise CertoraUserInputError if more than one of the modes above was used
@@ -733,13 +734,10 @@ def check_files_input(file_list: List[str]) -> None:
733
734
  if '.conf' in file:
734
735
  raise Util.CertoraUserInputError(
735
736
  f'The conf file {file} cannot be accompanied with other files')
736
- if file.endswith(Util.SOLANA_EXTENSIONS):
737
+ if file.endswith(Util.SOLANA_EXEC_EXTENSION):
737
738
  raise Util.CertoraUserInputError(f'The Solana file {file} cannot be accompanied with other files')
738
- if file.endswith(Util.WASM_EXTENSIONS) or file == Util.CARGO_FILE:
739
- raise Util.CertoraUserInputError(
740
- f'When using the tool in WASM mode, you can only provide a single Cargo.toml file or .wat file.'
741
- f'{num_files} files were given.')
742
-
739
+ if file.endswith(Util.SOROBAN_EXEC_EXTENSION):
740
+ raise Util.CertoraUserInputError(f'The Soroban file {file} cannot be accompanied with other files')
743
741
 
744
742
  def set_wait_for_results_default(context: CertoraContext) -> None:
745
743
  if context.wait_for_results is None: