certora-cli-alpha-master 20241217.5.52.971947__tar.gz → 20241217.16.49.417062__tar.gz

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of certora-cli-alpha-master might be problematic. Click here for more details.

Files changed (72) hide show
  1. {certora_cli_alpha_master-20241217.5.52.971947/certora_cli_alpha_master.egg-info → certora_cli_alpha_master-20241217.16.49.417062}/PKG-INFO +2 -2
  2. certora_cli_alpha_master-20241217.16.49.417062/README.md +1 -0
  3. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraCloudIO.py +90 -46
  4. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraConfigIO.py +7 -3
  5. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraContext.py +16 -64
  6. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraContextAttributes.py +43 -27
  7. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraContextValidator.py +7 -12
  8. certora_cli_alpha_master-20241217.16.49.417062/certora_cli/EVMVerifier/certoraParseBuildScript.py +36 -0
  9. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Mutate/mutateApp.py +16 -7
  10. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Shared/certoraUtils.py +6 -9
  11. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Shared/certoraValidateFuncs.py +14 -7
  12. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/certoraRun.py +3 -5
  13. certora_cli_alpha_master-20241217.16.49.417062/certora_cli/certoraSolanaProver.py +153 -0
  14. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/rustMutator.py +108 -38
  15. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062/certora_cli_alpha_master.egg-info}/PKG-INFO +2 -2
  16. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli_alpha_master.egg-info/SOURCES.txt +2 -0
  17. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli_alpha_master.egg-info/entry_points.txt +1 -0
  18. certora_cli_alpha_master-20241217.16.49.417062/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  19. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_jars/Typechecker.jar +0 -0
  20. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/setup.py +4 -3
  21. certora_cli_alpha_master-20241217.5.52.971947/README.md +0 -1
  22. certora_cli_alpha_master-20241217.5.52.971947/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  23. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/LICENSE +0 -0
  24. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/MANIFEST.in +0 -0
  25. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_bins/__init__.py +0 -0
  26. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/CompilerCollector.py +0 -0
  27. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/CompilerCollectorFactory.py +0 -0
  28. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSol.py +0 -0
  29. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSolBased.py +0 -0
  30. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/CompilerCollectorVy.py +0 -0
  31. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/CompilerCollectorYul.py +0 -0
  32. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/Compiler/__init__.py +0 -0
  33. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/__init__.py +0 -0
  34. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraBuild.py +0 -0
  35. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraBuildCacheManager.py +0 -0
  36. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraBuildDataClasses.py +0 -0
  37. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraCollectRunMetadata.py +0 -0
  38. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraCompilerParameters.py +0 -0
  39. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraContextClass.py +0 -0
  40. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraContractFuncs.py +0 -0
  41. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraExtensionInfo.py +0 -0
  42. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraJobList.py +0 -0
  43. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraMiniSpecParser.py +0 -0
  44. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraNodeFilters.py +0 -0
  45. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraSourceFinders.py +0 -0
  46. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraType.py +0 -0
  47. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EVMVerifier/certoraVerifyGenerator.py +0 -0
  48. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  49. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  50. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  51. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  52. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  53. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  54. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
  55. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  56. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Mutate/__init__.py +0 -0
  57. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Mutate/mutateAttributes.py +0 -0
  58. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Mutate/mutateConstants.py +0 -0
  59. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Mutate/mutateUtil.py +0 -0
  60. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Mutate/mutateValidate.py +0 -0
  61. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Shared/ExpectedComparator.py +0 -0
  62. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Shared/__init__.py +0 -0
  63. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Shared/certoraAttrUtil.py +0 -0
  64. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/Shared/certoraLogging.py +0 -0
  65. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/__init__.py +0 -0
  66. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/certoraEqCheck.py +0 -0
  67. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli/certoraMutate.py +0 -0
  68. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli_alpha_master.egg-info/dependency_links.txt +0 -0
  69. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli_alpha_master.egg-info/requires.txt +0 -0
  70. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_cli_alpha_master.egg-info/top_level.txt +0 -0
  71. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/certora_jars/__init__.py +0 -0
  72. {certora_cli_alpha_master-20241217.5.52.971947 → certora_cli_alpha_master-20241217.16.49.417062}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: certora-cli-alpha-master
3
- Version: 20241217.5.52.971947
3
+ Version: 20241217.16.49.417062
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 d152edd. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
26
+ Commit d9800cf. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit d9800cf. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -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
 
@@ -744,34 +771,51 @@ class CloudVerification:
744
771
  paths.append(Path(self.context.bytecode_spec))
745
772
  result = compress_files(self.ZipFilePath, *paths,
746
773
  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
774
+ elif Attrs.is_rust_app():
775
+ files_list = [Util.get_certora_metadata_file()]
776
+
777
+ if hasattr(self.context, 'build_script') and self.context.build_script:
778
+ if self.context.prover_resource_files:
779
+ for value in self.context.prover_resource_files:
780
+ _, file_path = value.split(':')
781
+ cur_path = (Path(self.context.conf_file).parent / Path(file_path)).resolve()
782
+ shutil.copy(cur_path, Util.get_build_dir())
783
+ files_list.append(Util.get_build_dir() / cur_path.name)
784
+
785
+ result = zip_rust_files(self.ZipFilePath, self.context, *files_list)
786
+
787
+ elif Attrs.is_solana_app():
788
+ # We zip the ELF files and the two configuration files
789
+ jar_args = Ctx.collect_jar_args(self.context)
758
790
 
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
791
  for file in self.context.files:
772
- paths.append(Path(file))
773
- result = compress_files(self.ZipFilePath, *paths,
792
+ files_list.append(Path(file))
793
+ is_file = False
794
+ for arg in jar_args:
795
+ if is_file:
796
+ files_list.append(Path(arg))
797
+ is_file = False
798
+
799
+ if arg == '-solanaInlining':
800
+ is_file = True
801
+ elif arg == '-solanaSummaries':
802
+ is_file = True
803
+ result = compress_files(self.ZipFilePath, *files_list,
774
804
  short_output=Ctx.is_minimal_cli_output(self.context))
805
+
806
+ elif Attrs.is_soroban_app():
807
+ # We zip the wat file
808
+ for file in self.context.files:
809
+ files_list.append(Path(file))
810
+ result = compress_files(self.ZipFilePath, *files_list,
811
+ short_output=Ctx.is_minimal_cli_output(self.context))
812
+ else:
813
+ raise Util.CertoraUserInputError(
814
+ 'When running rust application, context should either have attribute "rust_executables" '
815
+ 'provided by build_script execution, '
816
+ 'or either is_solana_app(), is_soroban_app() should be set to True'
817
+ )
818
+
775
819
  else:
776
820
  # Zip the log file first separately and again with the rest of the files, so it will not be decompressed
777
821
  # 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
@@ -5,19 +5,13 @@ import logging
5
5
  import os
6
6
  import re
7
7
  import sys
8
- import shutil
9
- import platform
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,15 +106,10 @@ 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():
109
+ if context.is_tac or Attrs.is_rust_app():
110
+ # For Rust app we assume the files holds the executable for the prover, currently we support a single file
116
111
  run_args.append(context.files[0])
117
112
 
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:
122
- run_args.append(context.files[0])
123
-
124
113
  if Attrs.is_evm_app() and context.cache is not None:
125
114
  run_args.extend(['-cache', context.cache])
126
115
 
@@ -158,7 +147,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
158
147
  console = Console()
159
148
  console.print("\n\nThe Certora Prover - A formal verification tool for smart contracts")
160
149
  # Using sys.stdout.write() as print() would color some of the strings here
161
- sys.stdout.write("\n\nUsage: certoraRun <Files> <Flags>\n\n")
150
+ sys.stdout.write(f"\n\nUsage: {sys.argv[0]} <Files> <Flags>\n\n")
162
151
  sys.stdout.write("Files are Solidity, Vyper contract files, a shared object Solana contract file, "
163
152
  "or a conf file. Solidity contracts are denoted as file:contract "
164
153
  "e.g. f.sol:A. If the contract name is identical to the file name, "
@@ -171,7 +160,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
171
160
  console.print("3. list (L): gets multiple strings as a value, flags may also appear multiple times")
172
161
  console.print("4. map (M): collection of key, value pairs\n\n")
173
162
 
174
- Attrs.EvmProverAttributes.print_attr_help()
163
+ Attrs.get_attribute_class().print_attr_help()
175
164
  console.print("\n\nYou can find detailed documentation of the supported flags here: "
176
165
  f"{Util.print_rich_link(CLI_DOCUMENTATION_URL)}\n\n")
177
166
 
@@ -527,52 +516,15 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> No
527
516
  raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
528
517
  "To skip local checks run with the --disable_local_typechecking flag")
529
518
 
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
-
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
519
 
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}")
520
+ def build_rust_app(context: CertoraContext) -> None:
521
+ if context.build_script:
522
+ run_script_and_parse_json(context)
523
+ if not context.rust_executables:
524
+ raise Util.CertoraUserInputError("failed to get target executable")
525
+ else:
526
+ if not context.files:
527
+ raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
528
+ if len(context.files) > 1:
529
+ raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
530
+ 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,7 @@ class CommonAttributes(AttrUtil.Attributes):
149
161
  disables_build_cache=False
150
162
  )
151
163
 
164
+
152
165
  class DeprecatedAttributes(AttrUtil.Attributes):
153
166
  AUTO_NONDET_DIFFICULT_INTERNAL_FUNCS = AttrUtil.AttributeDefinition(
154
167
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
@@ -418,17 +431,6 @@ class EvmAttributes(AttrUtil.Attributes):
418
431
  disables_build_cache=False
419
432
  )
420
433
 
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
434
  NO_MEMORY_SAFE_AUTOFINDERS = AttrUtil.AttributeDefinition(
433
435
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
434
436
  # This is a hidden flag, the following two attributes are left intentionally as comments to help devs
@@ -1384,6 +1386,20 @@ class BackendAttributes(AttrUtil.Attributes):
1384
1386
  disables_build_cache=False
1385
1387
  )
1386
1388
 
1389
+
1390
+ class RustAttributes(AttrUtil.Attributes):
1391
+
1392
+ BUILD_SCRIPT = AttrUtil.AttributeDefinition(
1393
+ help_msg="Python script to build the project",
1394
+ default_desc="Using default building command",
1395
+ argparse_args={
1396
+ 'action': AttrUtil.UniqueStore
1397
+ },
1398
+ affects_build_cache_key=False,
1399
+ disables_build_cache=False
1400
+ )
1401
+
1402
+
1387
1403
  class EvmProverAttributes(CommonAttributes, DeprecatedAttributes, EvmAttributes, InternalUseAttributes,
1388
1404
  BackendAttributes):
1389
1405
  FILES = AttrUtil.AttributeDefinition(
@@ -1399,9 +1415,9 @@ class EvmProverAttributes(CommonAttributes, DeprecatedAttributes, EvmAttributes,
1399
1415
  )
1400
1416
 
1401
1417
 
1402
- class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes):
1418
+ class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes, RustAttributes):
1403
1419
  FILES = AttrUtil.AttributeDefinition(
1404
- attr_validation_func=Vf.validate_wasm_file,
1420
+ attr_validation_func=Vf.validate_soroban_extension,
1405
1421
  arg_type=AttrUtil.AttrArgType.LIST,
1406
1422
  help_msg="binary .wat files for the Prover",
1407
1423
  default_desc="",
@@ -1412,19 +1428,10 @@ class SorobanProverAttributes(CommonAttributes, InternalUseAttributes, BackendAt
1412
1428
  disables_build_cache=False
1413
1429
  )
1414
1430
 
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
1431
 
1425
- class SolanaProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes):
1432
+ class SolanaProverAttributes(CommonAttributes, InternalUseAttributes, BackendAttributes, RustAttributes):
1426
1433
  FILES = AttrUtil.AttributeDefinition(
1427
- attr_validation_func=Vf.validate_solana_file,
1434
+ attr_validation_func=Vf.validate_solana_extension,
1428
1435
  arg_type=AttrUtil.AttrArgType.LIST,
1429
1436
  help_msg="contract files for analysis SOLANA_FILE.so or a conf file",
1430
1437
 
@@ -1460,9 +1467,9 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1460
1467
  def application_by_suffix(file: str) -> Type[AttrUtil.Attributes]:
1461
1468
  if file.endswith(Util.EVM_EXTENSIONS):
1462
1469
  return EvmProverAttributes
1463
- elif file.endswith(Util.WASM_EXTENSIONS) or Path(file).name == Util.CARGO_FILE:
1470
+ elif file.endswith(Util.SOROBAN_EXEC_EXTENSION):
1464
1471
  return SorobanProverAttributes
1465
- elif file.endswith(Util.SOLANA_EXTENSIONS):
1472
+ elif file.endswith(Util.SOLANA_EXEC_EXTENSION):
1466
1473
  return SolanaProverAttributes
1467
1474
  elif file.endswith('.conf'):
1468
1475
  raise Util.CertoraUserInputError(f"Cannot use conf files inside a conf file: {file}")
@@ -1472,6 +1479,7 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1472
1479
  cli_files = []
1473
1480
  cli_conf_files = []
1474
1481
  files = []
1482
+ build_script = None
1475
1483
  for arg in args:
1476
1484
  if arg.startswith('-'):
1477
1485
  break # Stop processing when a flag is detected
@@ -1485,6 +1493,10 @@ def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1485
1493
  with conf_file_path.open() as conf_file:
1486
1494
  configuration = json5.load(conf_file, allow_duplicate_keys=False)
1487
1495
  files = configuration.get('files', [])
1496
+ build_script = configuration.get('build_script')
1497
+
1498
+ if build_script:
1499
+ return SorobanProverAttributes
1488
1500
 
1489
1501
  if len(cli_conf_files) == 0:
1490
1502
  files = cli_files
@@ -1516,9 +1528,13 @@ def is_solana_app() -> bool:
1516
1528
  return get_attribute_class() == SolanaProverAttributes
1517
1529
 
1518
1530
 
1519
- def is_wasm_app() -> bool:
1531
+ def is_soroban_app() -> bool:
1520
1532
  return get_attribute_class() == SorobanProverAttributes
1521
1533
 
1522
1534
 
1535
+ def is_rust_app() -> bool:
1536
+ return is_soroban_app() or is_solana_app()
1537
+
1538
+
1523
1539
  def is_evm_app() -> bool:
1524
1540
  return get_attribute_class() == EvmProverAttributes
@@ -409,8 +409,7 @@ def check_contract_name_arg_inputs(context: CertoraContext) -> None:
409
409
  def valid_input_file(filename: str) -> bool:
410
410
  pattern_match_sol = bool(re.match(r'([\w.$]+)\.sol:([\w$]+)', Path(filename).name))
411
411
  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
412
+ return pattern_match_sol or ends_with_extension
414
413
 
415
414
 
416
415
  def check_files_attribute(context: CertoraContext) -> None:
@@ -492,7 +491,7 @@ def check_mode_of_operation(context: CertoraContext) -> None:
492
491
  raise Util.CertoraUserInputError(
493
492
  f"Cannot use conf files inside a conf file: {','.join(conf_files_inside_conf_file)}")
494
493
 
495
- special_file_suffixes = [".tac", ".json"] + list(Util.SOLANA_EXTENSIONS) + list(Util.WASM_EXTENSIONS)
494
+ special_file_suffixes = [".tac", ".json"] + [Util.SOLANA_EXEC_EXTENSION, Util.SOROBAN_EXEC_EXTENSION]
496
495
  for input_file in context.files:
497
496
  special_file_type = next((suffix for suffix in special_file_suffixes if input_file.endswith(suffix)), None)
498
497
 
@@ -710,7 +709,6 @@ def validate_certora_key() -> str:
710
709
  raise Util.CertoraUserInputError(f"environment variable {KEY_ENV_VAR} has an illegal length")
711
710
  return key
712
711
 
713
-
714
712
  def check_files_input(file_list: List[str]) -> None:
715
713
  """
716
714
  Verifies that correct input was inserted as input to files.
@@ -719,8 +717,8 @@ def check_files_input(file_list: List[str]) -> None:
719
717
  The allowed disjoint cases are:
720
718
  1. Use a single .conf file
721
719
  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
720
+ 3. Use a single .so file
721
+ 4. Use a single .wasm file
724
722
  5. Use any number of [contract.sol:nickname ...] (at least one is guaranteed by argparser)
725
723
  @param file_list: A list of strings representing file paths
726
724
  @raise CertoraUserInputError if more than one of the modes above was used
@@ -733,13 +731,10 @@ def check_files_input(file_list: List[str]) -> None:
733
731
  if '.conf' in file:
734
732
  raise Util.CertoraUserInputError(
735
733
  f'The conf file {file} cannot be accompanied with other files')
736
- if file.endswith(Util.SOLANA_EXTENSIONS):
734
+ if file.endswith(Util.SOLANA_EXEC_EXTENSION):
737
735
  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
-
736
+ if file.endswith(Util.SOROBAN_EXEC_EXTENSION):
737
+ raise Util.CertoraUserInputError(f'The Soroban file {file} cannot be accompanied with other files')
743
738
 
744
739
  def set_wait_for_results_default(context: CertoraContext) -> None:
745
740
  if context.wait_for_results is None:
@@ -0,0 +1,36 @@
1
+ import subprocess
2
+ import json
3
+ from pathlib import Path
4
+
5
+ from EVMVerifier.certoraContextClass import CertoraContext
6
+ from Shared import certoraUtils as Util
7
+ import os
8
+
9
+
10
+ def run_script_and_parse_json(context: CertoraContext) -> None:
11
+ if not context.build_script:
12
+ return
13
+ try:
14
+ env = os.environ.copy()
15
+ result = subprocess.run(["python3", Path(context.build_script).resolve(), '--json'], capture_output=True, text=True,
16
+ env=env, cwd=Path(context.build_script).resolve().parent)
17
+
18
+ # Check if the script executed successfully
19
+ if result.returncode != 0:
20
+ raise Util.CertoraUserInputError(f"Error running the script {context.build_script}\n{result.stderr}")
21
+
22
+ json_obj = json.loads(result.stdout)
23
+
24
+ if not json_obj or not json_obj.get("success"):
25
+ raise Util.CertoraUserInputError(f"{result.stderr}\nBuild from {context.build_script} failed")
26
+
27
+ context.rust_project_directory = json_obj.get("project_directory")
28
+ context.rust_sources = json_obj.get("sources")
29
+ context.rust_executables = json_obj.get("executables")
30
+
31
+ except FileNotFoundError as e:
32
+ raise Util.CertoraUserInputError(f"File not found: {e}")
33
+ except json.JSONDecodeError as e:
34
+ raise Util.CertoraUserInputError(f"Error decoding JSON: {e}")
35
+ except Exception as e:
36
+ raise Util.CertoraUserInputError(f"An unexpected error occurred: {e}")
@@ -715,11 +715,7 @@ class MutateApp:
715
715
  self.server = self.config_server()
716
716
 
717
717
  def is_soroban_run(self) -> bool:
718
- try:
719
- files = self.prover_context.files
720
- except AttributeError:
721
- return False
722
- return Util.CARGO_FILE in files or any(Path(path).suffix == '.wasm' for path in files)
718
+ return (hasattr(self.prover_context, 'build_script') and self.prover_context is not None)
723
719
 
724
720
  def config_server(self) -> str:
725
721
  """
@@ -873,6 +869,16 @@ class MutateApp:
873
869
  self.backup_paths = []
874
870
 
875
871
  def submit_soroban(self) -> None:
872
+
873
+ try:
874
+ original_run_result = self.run_certora_prover(self.conf, self.mutation_test_id, msg=MConstants.ORIGINAL)
875
+ except Exception as e:
876
+ raise Util.CertoraUserInputError(f"Orig run: {e}")
877
+ if not original_run_result:
878
+ raise Util.CertoraUserInputError("No Orig results")
879
+ result_link = original_run_result.rule_report_link
880
+ assert result_link, "submit_soroban: Null result_link"
881
+
876
882
  mutation_logger.info("Generating mutants and submitting...")
877
883
  generated_mutants: List[Mutant] = self.get_universal_mutator_mutants()
878
884
 
@@ -1341,7 +1347,8 @@ class MutateApp:
1341
1347
  for mutant in self.universal_mutator:
1342
1348
  file_to_mutate = Path(os.path.normpath(mutant[MConstants.FILE_TO_MUTATE]))
1343
1349
  mutants_location = Path(mutant[MConstants.MUTANTS_LOCATION])
1344
- run_universal_mutator(file_to_mutate, mutants_location)
1350
+ num_of_mutants = mutant[MConstants.NUM_MUTANTS]
1351
+ run_universal_mutator(file_to_mutate, self.prover_context.build_script, mutants_location, num_of_mutants)
1345
1352
  self.add_dir_to_mutants(ret_mutants, mutants_location, file_to_mutate)
1346
1353
  return ret_mutants
1347
1354
 
@@ -1784,7 +1791,9 @@ class MutateApp:
1784
1791
 
1785
1792
  # all keys in prover_context must exist as attribute of certoraRun
1786
1793
  def check_prover_context(self) -> None:
1787
- prover_attrs = [attr.get_conf_key() for attr in Attrs.EvmProverAttributes.attribute_list()]
1794
+ attributes = Attrs.SorobanProverAttributes.attribute_list() if self.is_soroban_run() \
1795
+ else Attrs.EvmProverAttributes.attribute_list()
1796
+ prover_attrs = [attr.get_conf_key() for attr in attributes]
1788
1797
  for key in vars(self.prover_context).keys():
1789
1798
  if key not in prover_attrs:
1790
1799
  raise Util.CertoraUserInputError(f"{key} not a valid attribute in a conf file")