certora-cli 7.14.3__tar.gz → 7.17.2__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 (67) hide show
  1. {certora_cli-7.14.3/certora_cli.egg-info → certora_cli-7.17.2}/PKG-INFO +2 -2
  2. certora_cli-7.17.2/README.md +1 -0
  3. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraBuild.py +176 -56
  4. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraConfigIO.py +1 -0
  5. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraContext.py +6 -36
  6. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraContextAttributes.py +46 -14
  7. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraContextValidator.py +61 -194
  8. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Mutate/mutateApp.py +220 -335
  9. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Mutate/mutateAttributes.py +0 -1
  10. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Mutate/mutateConstants.py +1 -1
  11. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Mutate/mutateValidate.py +0 -2
  12. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Shared/certoraUtils.py +1 -1
  13. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Shared/certoraValidateFuncs.py +39 -4
  14. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/certoraMutate.py +8 -5
  15. {certora_cli-7.14.3 → certora_cli-7.17.2/certora_cli.egg-info}/PKG-INFO +2 -2
  16. certora_cli-7.17.2/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
  17. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_jars/Typechecker.jar +0 -0
  18. {certora_cli-7.14.3 → certora_cli-7.17.2}/setup.py +2 -2
  19. certora_cli-7.14.3/README.md +0 -1
  20. certora_cli-7.14.3/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
  21. {certora_cli-7.14.3 → certora_cli-7.17.2}/LICENSE +0 -0
  22. {certora_cli-7.14.3 → certora_cli-7.17.2}/MANIFEST.in +0 -0
  23. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_bins/__init__.py +0 -0
  24. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/CompilerCollector.py +0 -0
  25. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorFactory.py +0 -0
  26. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSol.py +0 -0
  27. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSolBased.py +0 -0
  28. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorVy.py +0 -0
  29. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorYul.py +0 -0
  30. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/Compiler/__init__.py +0 -0
  31. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/__init__.py +0 -0
  32. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraBuildCacheManager.py +0 -0
  33. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraCloudIO.py +0 -0
  34. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraCollectRunMetadata.py +0 -0
  35. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraCompilerParameters.py +0 -0
  36. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraContextClass.py +0 -0
  37. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraContractFuncs.py +0 -0
  38. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraExtensionInfo.py +0 -0
  39. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraJobList.py +0 -0
  40. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraMiniSpecParser.py +0 -0
  41. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraNodeFilters.py +0 -0
  42. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraType.py +0 -0
  43. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EVMVerifier/certoraVerifyGenerator.py +0 -0
  44. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
  45. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
  46. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
  47. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
  48. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
  49. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/__init__.py +0 -0
  50. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
  51. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
  52. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Mutate/__init__.py +0 -0
  53. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Mutate/mutateUtil.py +0 -0
  54. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Shared/ExpectedComparator.py +0 -0
  55. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Shared/__init__.py +0 -0
  56. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Shared/certoraAttrUtil.py +0 -0
  57. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/Shared/certoraLogging.py +0 -0
  58. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/__init__.py +0 -0
  59. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/certoraEqCheck.py +0 -0
  60. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli/certoraRun.py +0 -0
  61. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli.egg-info/SOURCES.txt +0 -0
  62. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli.egg-info/dependency_links.txt +0 -0
  63. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli.egg-info/entry_points.txt +0 -0
  64. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli.egg-info/requires.txt +0 -0
  65. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_cli.egg-info/top_level.txt +0 -0
  66. {certora_cli-7.14.3 → certora_cli-7.17.2}/certora_jars/__init__.py +0 -0
  67. {certora_cli-7.14.3 → certora_cli-7.17.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: certora-cli
3
- Version: 7.14.3
3
+ Version: 7.17.2
4
4
  Summary: Runner for the Certora Prover
5
5
  Home-page: https://pypi.org/project/certora-cli
6
6
  Author: Certora
@@ -21,4 +21,4 @@ Requires-Dist: tabulate
21
21
  Requires-Dist: tqdm
22
22
  Requires-Dist: StrEnum
23
23
 
24
- Commit 59b7da5. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
24
+ Commit 501a551. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -0,0 +1 @@
1
+ Commit 501a551. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
@@ -7,6 +7,7 @@ import shutil
7
7
  import sys
8
8
  import typing
9
9
  from collections import OrderedDict
10
+ from dataclasses import dataclass
10
11
  from enum import Enum
11
12
  from functools import lru_cache
12
13
  from pathlib import Path
@@ -50,6 +51,8 @@ OUTPUTS = "outputs"
50
51
  FUNCTION = "function"
51
52
  CONTRACTS = "contracts"
52
53
  NAME = "name"
54
+ MANUAL_MUTANTS = "manual_mutants"
55
+ MUTANTS_LOCATION = "mutants_location"
53
56
 
54
57
  FunctionSig = Tuple[str, List[str], List[str], str]
55
58
 
@@ -238,6 +241,18 @@ class PresetImmutableReference(ImmutableReference):
238
241
  return repr(self.as_dict())
239
242
 
240
243
 
244
+ @dataclass
245
+ class ContractExtension:
246
+ extension: "ContractInSDC"
247
+ exclusion: List[str]
248
+
249
+ def as_dict(self) -> Dict[str, Any]:
250
+ return {
251
+ "extension": self.extension.as_dict(),
252
+ "exclusion": self.exclusion
253
+ }
254
+
255
+
241
256
  # Python3.5 to which we maintain backward-compatibility due to CI's docker image, does not support @dataclass
242
257
  class ContractInSDC:
243
258
  def __init__(self, name: str, source_file: str, lang: str,
@@ -246,13 +261,15 @@ class ContractInSDC:
246
261
  methods: Set[Func], bytecode: str,
247
262
  constructor_bytecode: str,
248
263
  srcmap: str, varmap: Any, constructor_srcmap: str,
249
- storageLayout: Any, immutables: List[ImmutableReference],
264
+ storage_layout: Any, transient_storage_layout: Any,
265
+ immutables: List[ImmutableReference],
250
266
  function_finders: Dict[str, InternalFunc], internal_funcs: Set[Func], public_funcs: Set[Func],
251
267
  all_funcs: List[Func],
252
268
  types: List[CT.Type],
253
269
  compiler_collector: Optional[CompilerCollector],
254
270
  source_bytes: Optional[SourceBytes],
255
271
  compiler_parameters: Optional[CompilerParameters],
272
+ extension_contracts: List[ContractExtension]
256
273
  ):
257
274
  self.name = name
258
275
  self.original_file = source_file
@@ -266,7 +283,8 @@ class ContractInSDC:
266
283
  self.srcmap = srcmap
267
284
  self.varmap = varmap
268
285
  self.constructorSrcmap = constructor_srcmap
269
- self.storageLayout = storageLayout
286
+ self.storage_layout = storage_layout
287
+ self.transient_storage_layout = transient_storage_layout
270
288
  self.immutables = immutables
271
289
  # function finder: a mapping from external function ids to
272
290
  # the ids of the internal functions they are "finding" for us
@@ -301,6 +319,7 @@ class ContractInSDC:
301
319
  else:
302
320
  compiler_version = "unknown"
303
321
  self.compiler_version = compiler_version
322
+ self.extension_contracts = extension_contracts
304
323
 
305
324
  def as_dict(self) -> Dict[str, Any]:
306
325
  """
@@ -319,7 +338,8 @@ class ContractInSDC:
319
338
  "srcmap": self.srcmap,
320
339
  "varmap": self.varmap,
321
340
  "constructorSrcmap": self.constructorSrcmap,
322
- "storageLayout": self.storageLayout,
341
+ "storageLayout": self.storage_layout,
342
+ "transientStorageLayout": self.transient_storage_layout,
323
343
  "immutables": list(map(lambda x: x.as_dict(), self.immutables)),
324
344
  # the function finder field
325
345
  # why are we using this and not the normal list of functions?
@@ -331,6 +351,7 @@ class ContractInSDC:
331
351
  "compilerVersion": self.compiler_version,
332
352
  "compilerParameters": None if not self.compiler_parameters else self.compiler_parameters.as_dict(),
333
353
  "sourceBytes": None if self.source_bytes is None else self.source_bytes.as_dict(),
354
+ "extensionContracts": [e.as_dict() for e in self.extension_contracts]
334
355
  }
335
356
 
336
357
  def as_printable_dict(self) -> Dict[str, Any]:
@@ -345,7 +366,8 @@ class ContractInSDC:
345
366
  "file": self.report_source_file,
346
367
  "address": self.address,
347
368
  "methods": list(map(lambda x: x.as_dict(), self.methods)),
348
- "storageLayout": self.storageLayout,
369
+ "storageLayout": self.storage_layout,
370
+ "transientStorageLayout": self.transient_storage_layout,
349
371
  "immutables": list(map(lambda x: x.as_dict(), self.immutables)),
350
372
  "internalFunctions": {k: method.as_dict() for k, method in self.function_finders.items()}
351
373
  }
@@ -359,6 +381,9 @@ class ContractInSDC:
359
381
  def __repr__(self) -> str:
360
382
  return repr(self.as_printable_dict())
361
383
 
384
+ def add_extension(self, extension: ContractExtension) -> None:
385
+ self.extension_contracts.append(extension)
386
+
362
387
 
363
388
  class SDC:
364
389
  """
@@ -413,6 +438,7 @@ class SDC:
413
438
  return contract
414
439
  return None
415
440
 
441
+
416
442
  # this function is Solidity specific.
417
443
  # todo: create certoraBuildUtilsSol.py file, where such solidity specific functions will be.
418
444
  def generate_finder_body(f: Func, internal_id: int, sym: int, compiler_collector: CompilerCollectorSol,
@@ -562,7 +588,7 @@ def reprint_ast_type(node: Dict[str, Any]) -> Optional[str]:
562
588
  if "length" in node:
563
589
  lnode = node["length"]
564
590
  if type(lnode) is not dict or "nodeType" not in lnode or lnode["nodeType"] != "Literal" or \
565
- "value" not in lnode:
591
+ "value" not in lnode:
566
592
  return None
567
593
  len_str = lnode["value"]
568
594
  else:
@@ -573,7 +599,7 @@ def reprint_ast_type(node: Dict[str, Any]) -> Optional[str]:
573
599
  return None
574
600
  p_node = node["pathNode"]
575
601
  if type(p_node) is not dict or "nodeType" not in p_node or \
576
- p_node["nodeType"] != "IdentifierPath" or "name" not in p_node:
602
+ p_node["nodeType"] != "IdentifierPath" or "name" not in p_node:
577
603
  return None
578
604
  return p_node["name"]
579
605
  else:
@@ -595,7 +621,7 @@ def get_modifier_param_type_name(ind: int, def_node: Dict[str, Any], f: Func) ->
595
621
  return fallback
596
622
  param_def_node = params[ind]
597
623
  if type(param_def_node) is not dict or param_def_node["nodeType"] != "VariableDeclaration" or \
598
- "typeName" not in param_def_node:
624
+ "typeName" not in param_def_node:
599
625
  return fallback
600
626
  param_type_node = param_def_node["typeName"]
601
627
  if type(param_type_node) is not dict:
@@ -1429,6 +1455,10 @@ class CertoraBuildGenerator:
1429
1455
  @staticmethod
1430
1456
  def collect_storage_layout(data: Dict[str, Any]) -> Any:
1431
1457
  return data.get("storageLayout", None)
1458
+
1459
+ @staticmethod
1460
+ def collect_transient_storage_layout(data: Dict[str, Any]) -> Any:
1461
+ return data.get('transientStorageLayout', None)
1432
1462
 
1433
1463
  # Cache info - on PackingTest there are 514 hits and 34 misses
1434
1464
  @lru_cache(maxsize=128)
@@ -1579,10 +1609,10 @@ class CertoraBuildGenerator:
1579
1609
  settings_dict["optimizer"]['runs'] = int(self.context.solc_optimize)
1580
1610
  # if we are not disabling finder friendly optimizer specifically, enable it whenever viaIR is also enabled
1581
1611
  if not self.context.strict_solc_optimizer and (
1582
- self.context.solc_via_ir or self.context.solc_experimental_via_ir):
1612
+ self.context.solc_via_ir or self.context.solc_experimental_via_ir):
1583
1613
  # The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
1584
1614
  # full inliner step removed
1585
- solc0_8_26 = "dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" + \
1615
+ solc0_8_26_to_0_8_27 = "dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" + \
1586
1616
  "IulcscCTUtx[scCTUt]TOntnfDIuljmul[jul]VcTOculjmul"
1587
1617
  solc0_8_13_to_0_8_25 = "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]T" + \
1588
1618
  "peulxa[rul]xa[r]cLgvifCTUca[r]LSsTFOtfDnca[r]Iulc]jmul[jul]VcTOculjmul"
@@ -1626,9 +1656,9 @@ class CertoraBuildGenerator:
1626
1656
  yul_optimizer_steps = solc0_8_12
1627
1657
  elif minor == 8 and 13 <= patch <= 25:
1628
1658
  yul_optimizer_steps = solc0_8_13_to_0_8_25
1629
- elif minor == 8 and patch == 26:
1630
- yul_optimizer_steps = solc0_8_26
1631
- assert yul_optimizer_steps is not None
1659
+ elif minor == 8 and 26 <= patch <= 27:
1660
+ yul_optimizer_steps = solc0_8_26_to_0_8_27
1661
+ assert yul_optimizer_steps is not None, 'Yul Optimizer steps missing for requested Solidity version. Please contact Certora team.'
1632
1662
 
1633
1663
  yul_optimizer_steps = standard_replacement(yul_optimizer_steps)
1634
1664
 
@@ -1708,7 +1738,7 @@ class CertoraBuildGenerator:
1708
1738
  if compiler_collector_lang == CompilerLangSol() or compiler_collector_lang == CompilerLangYul():
1709
1739
  sources_dict = {str(contract_file_posix_abs): {
1710
1740
  "urls": [str(contract_file_posix_abs)]}} # type: Dict[str, Dict[str, Any]]
1711
- output_selection = ["storageLayout", "abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers",
1741
+ output_selection = ["transientStorageLayout", "storageLayout", "abi", "evm.bytecode", "evm.deployedBytecode", "evm.methodIdentifiers",
1712
1742
  "evm.assembly"]
1713
1743
  ast_selection = ["id", "ast"]
1714
1744
  elif compiler_collector_lang == CompilerLangVy():
@@ -2277,6 +2307,69 @@ class CertoraBuildGenerator:
2277
2307
  build_logger.warning(msg)
2278
2308
 
2279
2309
  return bytecode
2310
+
2311
+ def annotate_storage(self, storage_data: Dict, build_arg_contract_file: str) -> Dict:
2312
+ '''
2313
+ Summary
2314
+ -------
2315
+ Annotates the storage data dictionary in-place with more information needed by the prover.
2316
+
2317
+ Parameters
2318
+ ----------
2319
+ storage_data : Dict
2320
+ The dictionary holding the storage data.
2321
+
2322
+ Returns
2323
+ -------
2324
+ Dict
2325
+ A reference to the modified storage_data
2326
+ '''
2327
+ def annotate_type(desc: CT.Type, type_name: str, store_types: Dict[str, Any],
2328
+ slot: Any = None, offset: Any = None) -> None:
2329
+ number_of_bytes = store_types[type_name]['numberOfBytes']
2330
+ lower_bound = store_types[type_name].get('lowerBound')
2331
+ upper_bound = store_types[type_name].get('upperBound')
2332
+ annotations = [CT.StorageAnnotation(number_of_bytes, slot, offset, lower_bound,
2333
+ upper_bound)]
2334
+ desc.annotate(annotations)
2335
+
2336
+ if isinstance(desc, CT.StructType):
2337
+ for desc_member in desc.members:
2338
+ for struct_member in store_types[type_name]['members']:
2339
+ if desc_member.name == struct_member['label']:
2340
+ annotate_type(desc_member.type, struct_member['type'], store_types,
2341
+ struct_member['slot'], struct_member['offset'])
2342
+ if isinstance(desc, CT.MappingType):
2343
+ annotate_type(desc.domain, store_types[type_name]['key'], store_types)
2344
+ annotate_type(desc.codomain, store_types[type_name]['value'], store_types)
2345
+ if isinstance(desc, CT.ArrayType):
2346
+ annotate_type(desc.elementType, store_types[type_name]['base'], store_types)
2347
+
2348
+ if storage_data is None or not isinstance(storage_data, dict) or 'storage' not in storage_data:
2349
+ return storage_data
2350
+
2351
+ declarations = storage_data['storage']
2352
+ if not isinstance(declarations, list):
2353
+ return storage_data
2354
+
2355
+ for storage_slot in declarations:
2356
+ if not isinstance(storage_slot, dict) or not isinstance(storage_slot.get('astId', None), int):
2357
+ continue
2358
+
2359
+ ast_id = storage_slot['astId']
2360
+ node = self.get_node_from_asts(self.asts, build_arg_contract_file, ast_id)
2361
+ if not isinstance(node, dict) or 'typeName' not in node:
2362
+ continue
2363
+
2364
+ descriptor = CT.Type.from_type_name_node(
2365
+ lambda ref: self.get_original_def_node(build_arg_contract_file, ref),
2366
+ lambda ref: self.get_contract_file_of_non_autofinder(build_arg_contract_file, ref),
2367
+ node['typeName']
2368
+ )
2369
+ type_descriptor = descriptor
2370
+ annotate_type(descriptor, storage_slot['type'], storage_data['types'])
2371
+ storage_slot['descriptor'] = type_descriptor.as_dict()
2372
+ return storage_data
2280
2373
 
2281
2374
  def get_contract_in_sdc(self,
2282
2375
  source_code_file: str,
@@ -2319,7 +2412,7 @@ class CertoraBuildGenerator:
2319
2412
  overrides=x.overrides,
2320
2413
  contractName=x.contractName,
2321
2414
  ast_id=x.ast_id,
2322
- source_bytes= x.source_bytes,
2415
+ source_bytes=x.source_bytes,
2323
2416
  original_file=x.original_file,
2324
2417
  body_location=x.body_location,
2325
2418
  ) for x in clfuncs]
@@ -2364,46 +2457,8 @@ class CertoraBuildGenerator:
2364
2457
  constructor_bytecode)
2365
2458
  address, is_static_address = \
2366
2459
  self.find_contract_address(source_code_file, contract_name, contracts_with_chosen_addresses)
2367
- storage_layout = \
2368
- self.collect_storage_layout(contract_data)
2369
- if storage_layout is not None and type(storage_layout) is dict and "storage" in storage_layout:
2370
- declarations = storage_layout["storage"]
2371
- if type(declarations) is list:
2372
- for storage_slot in declarations:
2373
- if type(storage_slot) is dict and type(storage_slot.get("astId", None)) is int:
2374
- ast_id = storage_slot["astId"] # type: int
2375
- node = self.get_node_from_asts(self.asts, build_arg_contract_file, ast_id)
2376
- if type(node) is dict and "typeName" in node:
2377
- descriptor = CT.Type.from_type_name_node(
2378
- lambda ref: self.get_original_def_node(build_arg_contract_file, ref),
2379
- lambda ref: self.get_contract_file_of_non_autofinder(build_arg_contract_file, ref),
2380
- node["typeName"]
2381
- )
2382
- type_descriptor = descriptor
2383
-
2384
- def annotate_type(desc: CT.Type, type_name: str, store_types: Dict[str, Any],
2385
- slot: Any = None, offset: Any = None) -> None:
2386
- number_of_bytes = store_types[type_name]["numberOfBytes"]
2387
- lower_bound = store_types[type_name].get("lowerBound")
2388
- upper_bound = store_types[type_name].get("upperBound")
2389
- annotations = [CT.StorageAnnotation(number_of_bytes, slot, offset, lower_bound,
2390
- upper_bound)]
2391
- desc.annotate(annotations)
2392
-
2393
- if isinstance(desc, CT.StructType):
2394
- for desc_member in desc.members:
2395
- for struct_member in store_types[type_name]["members"]:
2396
- if desc_member.name == struct_member["label"]:
2397
- annotate_type(desc_member.type, struct_member["type"], store_types,
2398
- struct_member["slot"], struct_member["offset"])
2399
- if isinstance(desc, CT.MappingType):
2400
- annotate_type(desc.domain, store_types[type_name]["key"], store_types)
2401
- annotate_type(desc.codomain, store_types[type_name]["value"], store_types)
2402
- if isinstance(desc, CT.ArrayType):
2403
- annotate_type(desc.elementType, store_types[type_name]["base"], store_types)
2404
-
2405
- annotate_type(descriptor, storage_slot["type"], storage_layout["types"])
2406
- storage_slot["descriptor"] = type_descriptor.as_dict()
2460
+ storage_layout = self.annotate_storage(self.collect_storage_layout(contract_data), build_arg_contract_file)
2461
+ transient_storage_layout = self.annotate_storage(self.collect_transient_storage_layout(contract_data), build_arg_contract_file)
2407
2462
  immutables = self.collect_immutables(contract_data, build_arg_contract_file, compiler_lang)
2408
2463
 
2409
2464
  if self.context.internal_funcs is not None:
@@ -2443,6 +2498,7 @@ class CertoraBuildGenerator:
2443
2498
  varmap,
2444
2499
  constructor_srcmap,
2445
2500
  storage_layout,
2501
+ transient_storage_layout,
2446
2502
  immutables,
2447
2503
  function_finders,
2448
2504
  internal_funcs=internal_funcs,
@@ -2452,6 +2508,7 @@ class CertoraBuildGenerator:
2452
2508
  compiler_collector=compiler_collector_for_contract_file,
2453
2509
  source_bytes=source_bytes,
2454
2510
  compiler_parameters=compiler_parameters,
2511
+ extension_contracts=list()
2455
2512
  )
2456
2513
 
2457
2514
  @staticmethod
@@ -2936,6 +2993,7 @@ class CertoraBuildGenerator:
2936
2993
 
2937
2994
  self.handle_links()
2938
2995
  self.handle_struct_links()
2996
+ self.handle_contract_extensions()
2939
2997
 
2940
2998
  @staticmethod
2941
2999
  def get_cwd_rel_in_sources(sources: Set[Path]) -> Tuple[Path, Path]:
@@ -3250,6 +3308,25 @@ class CertoraBuildGenerator:
3250
3308
  f"in slot {resolved_src_slot}")
3251
3309
  self.SDCs[source_to_update].state[resolved_src_slot] = dst_address
3252
3310
 
3311
+ def handle_contract_extensions(self):
3312
+ if not self.context.contract_extensions:
3313
+ return
3314
+
3315
+ contracts_by_name = {c.name: c for c in self.get_primary_contracts_from_sdcs()}
3316
+ for extended, extending in self.context.contract_extensions.items():
3317
+ if extended not in contracts_by_name:
3318
+ raise Util.CertoraUserInputError(f"Can't find extended contract {extended} for contract extension")
3319
+ extended_contract = contracts_by_name[extended]
3320
+ for ext in extending:
3321
+ extension = ext["extension"]
3322
+ if extension not in contracts_by_name.keys():
3323
+ raise Util.CertoraUserInputError(f"Can't find extending contract {extension} for contract extension")
3324
+ extension_contract = contracts_by_name[extension]
3325
+ for f in ext["exclude"]:
3326
+ if extension_contract.find_method(f) is None:
3327
+ raise Util.CertoraUserInputError(f"Can't find a method named {f} in contract {extension_contract.name}")
3328
+ extended_contract.add_extension(ContractExtension(extension_contract, ext["exclude"]))
3329
+
3253
3330
  def check_if_immutable_is_defined_in_ast(self, reference_to_replace_with_link: str, src_contract: str) -> None:
3254
3331
  """
3255
3332
  @param reference_to_replace_with_link: The immutable we could not find in immutable_references
@@ -3331,6 +3408,11 @@ class CertoraBuildGenerator:
3331
3408
  def get_matching_sdc_names_from_SDCs(self, contract: str) -> List[str]:
3332
3409
  return list(self.__get_matching_sdc_names_for_SDCs_iterator(contract))
3333
3410
 
3411
+ def get_primary_contracts_from_sdcs(self) -> List[ContractInSDC]:
3412
+ ret = [v.find_contract(v.primary_contract) for v in self.SDCs.values()]
3413
+ assert all(c is not None for c in ret), "failed to find some primary contracts"
3414
+ return ret
3415
+
3334
3416
  class SlotResolution(Enum):
3335
3417
  SLOT_NO_STORAGE_LAYOUT = enum.auto()
3336
3418
  SLOT_INVALID_STORAGE_LAYOUT = enum.auto()
@@ -3349,8 +3431,8 @@ class CertoraBuildGenerator:
3349
3431
  string - returns the slot number associated with slot_name as hex without preceding 0x (or 0X)
3350
3432
  string - relevant slots found, in case more than 1 slot found.
3351
3433
  """
3352
- storage_layouts = [c.storageLayout for c in sdc.contracts if
3353
- c.name == primary_contract and c.storageLayout is not None]
3434
+ storage_layouts = [c.storage_layout for c in sdc.contracts if
3435
+ c.name == primary_contract and c.storage_layout is not None]
3354
3436
  if len(storage_layouts) != 1:
3355
3437
  build_logger.debug(f"Expected exactly one storage layout matching {primary_contract}, "
3356
3438
  f"got {len(storage_layouts)}")
@@ -3454,6 +3536,42 @@ class CertoraBuildGenerator:
3454
3536
 
3455
3537
  return slot_number
3456
3538
 
3539
+ @staticmethod
3540
+ def get_manual_mutants_file(context: CertoraContext) -> Set[Path]:
3541
+ """
3542
+ Returns a set of mutant Solidity files based on the manual mutations specified in the context.
3543
+ These files are added to .certora_sources.
3544
+
3545
+ certoraRun generally skips the mutations object therefore no exceptions are raised if mutant files
3546
+ cannot be read due to bad syntax or any other error
3547
+
3548
+ :param context:
3549
+ :return: list of mutants file
3550
+ """
3551
+
3552
+ def get_sol_files(path: Path) -> Set[Path]:
3553
+ sol_files: Set[Path] = set()
3554
+
3555
+ if path.is_file() and path.suffix == SOL:
3556
+ sol_files.add(path)
3557
+ elif path.is_dir():
3558
+ for file_path in path.rglob(fr'*{SOL}'):
3559
+ sol_files.add(file_path)
3560
+ return sol_files
3561
+
3562
+ result: Set[Path] = set()
3563
+ try:
3564
+ manual_mutations = context.mutations[MANUAL_MUTANTS]
3565
+ for mutation in manual_mutations:
3566
+ try:
3567
+ mutation_path = mutation[MUTANTS_LOCATION]
3568
+ except Exception:
3569
+ continue
3570
+ result |= get_sol_files(Path(mutation_path))
3571
+ except Exception:
3572
+ pass
3573
+ return result
3574
+
3457
3575
  # The sources that are collected for the .certora_sources directory are all the files that are provided as input
3458
3576
  # (i.e. they are not generated during the certora build process) that are needed for precise rerunning certoraRun.
3459
3577
  #
@@ -3499,6 +3617,7 @@ class CertoraBuildGenerator:
3499
3617
  if hasattr(context, CONF_FILE_ATTR):
3500
3618
  add_to_sources(Path(getattr(context, CONF_FILE_ATTR)))
3501
3619
 
3620
+ sources |= CertoraBuildGenerator.get_manual_mutants_file(context)
3502
3621
  return sources
3503
3622
 
3504
3623
  def __del__(self) -> None:
@@ -3633,6 +3752,7 @@ def build_from_cache_or_scratch(context: CertoraContext,
3633
3752
 
3634
3753
  return cache_hit, True, cached_files
3635
3754
 
3755
+
3636
3756
  def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> None:
3637
3757
  """
3638
3758
  This is the main function of certoraBuild
@@ -38,6 +38,7 @@ def current_conf_to_file(context: CertoraContext) -> Dict[str, Any]:
38
38
  all_conf_names = Attrs.get_attribute_class().all_conf_names()
39
39
  context_to_save = dict(sorted(context_to_save.items(), key=lambda x: all_conf_names.index(x[0])))
40
40
  context_to_save.pop('build_dir', None) # build dir should not be saved, each run should define its own build_dir
41
+ context_to_save.pop('mutation_test_id', None) # mutation_test_id should be recreated for every run
41
42
 
42
43
  out_file_path = Util.get_last_conf_file()
43
44
  run_logger.debug(f"Saving last configuration file to {out_file_path}")
@@ -92,39 +92,9 @@ def collect_jar_args(context: CertoraContext) -> List[str]:
92
92
  for value in prover_args_values:
93
93
  return_list.extend(value.split())
94
94
 
95
- # CERT-926: Because of the way that the type checker jar and the evm jar command line argument parser
96
- # is implemented, you cannot have one commandline argument for both jars. As a result we need an argument
97
- # for the type checker jar and for the evm jar that control whether the CVL type checking code
98
- # will permit Solidity function calls in CVL quantifier bodies. We prioritize the type checker jar
99
- # argument "typechecker_args" in the conf files and if it contains the flag "-allowSolidityQuantifierCalls true"
100
- # we append the prover argument version to the list of prover jar arguments.
101
- type_checker_values = getattr(context, Attrs.EvmProverAttributes.TYPECHECKER_ARGS.get_conf_key(), None)
102
- if type_checker_values:
103
- for value in type_checker_values:
104
- values = value.split()
105
- if values[0] == '-allowSolidityQuantifierCalls' and values[1] == 'true':
106
- return_list.extend(['-allowSolidityCallsInQuantifiers', 'true'])
107
95
  return return_list
108
96
 
109
97
 
110
- def get_typechecker_arguments(context: CertoraContext) -> List[str]:
111
- """
112
- Assembles a string of all the command-line options the typechecker cares about
113
- @param context: The context
114
- @return: A string with all the options in the format the typechecker understands.
115
- """
116
- args = []
117
- if context.typechecker_args:
118
- args += context.typechecker_args
119
-
120
- relevant_attrs = filter(lambda arg: get_attr_value(context, arg),
121
- [Attrs.EvmProverAttributes.RULE, Attrs.EvmProverAttributes.EXCLUDE_RULE,
122
- Attrs.EvmProverAttributes.FOUNDRY_TESTS_MODE])
123
- args += create_jar_flag_list(context, list(relevant_attrs))
124
-
125
- return args
126
-
127
-
128
98
  def get_local_run_cmd(context: CertoraContext) -> str:
129
99
  """
130
100
  Assembles a jar command for local run
@@ -147,9 +117,6 @@ def get_local_run_cmd(context: CertoraContext) -> str:
147
117
  certora_root_dir = Util.get_certora_root_directory().as_posix()
148
118
  jar_path = f"{certora_root_dir}/{Util.EMV_JAR}"
149
119
 
150
- if context.allow_solidity_calls_in_quantifiers:
151
- run_args.extend(['-allowSolidityCallsInQuantifiers true'])
152
-
153
120
  """
154
121
  This flag prevents the focus from being stolen from the terminal when running the java process.
155
122
  Stealing the focus makes it seem like the program is not responsive to Ctrl+C.
@@ -519,7 +486,10 @@ def run_typechecker(typechecker_name: str, with_typechecking: bool, args: List[s
519
486
  f"{'-typeCheck ' if with_typechecking else ''}" \
520
487
  f"{' '.join(args)}"
521
488
 
522
- exit_code = Util.run_jar_cmd(typecheck_cmd, False, custom_error_message="Failed to compile spec files:",
489
+ exit_code = Util.run_jar_cmd(typecheck_cmd, False,
490
+ custom_error_message="Failed to run Certora Prover locally. Please check the errors "
491
+ "below for problems in the specifications (.spec files) or the "
492
+ "prover_args defined in the .conf file.",
523
493
  logger_topic="type_check")
524
494
  if exit_code != 0:
525
495
  raise Util.CertoraUserInputError("CVL specification syntax and type check failed")
@@ -533,9 +503,9 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> No
533
503
 
534
504
  if context.disable_local_typechecking or Util.is_ci_or_git_action():
535
505
  return
536
- args = get_typechecker_arguments(context)
506
+ args = collect_jar_args(context)
537
507
  if Util.is_java_installed():
538
508
  run_typechecker("Typechecker.jar", with_typechecking, args)
539
509
  else:
540
- raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installtion. "
510
+ raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
541
511
  "To skip local checks run with the --disable_local_typechecking flag")
@@ -269,6 +269,19 @@ class EvmAttributes(AttrUtil.Attributes):
269
269
  disables_build_cache=False
270
270
  )
271
271
 
272
+ SOLC_EVM_VERSION_MAP = AttrUtil.AttributeDefinition(
273
+ attr_validation_func=Vf.validate_solc_evm_version_map,
274
+ arg_type=AttrUtil.AttrArgType.MAP,
275
+ help_msg='Map contracts to the appropriate EVM version',
276
+ default_desc="Uses the same Solidity EVM version for all contracts",
277
+ argparse_args={
278
+ 'action': AttrUtil.UniqueStore,
279
+ 'type': lambda value: Vf.parse_dict('solc_map', value)
280
+ },
281
+ affects_build_cache_key=True,
282
+ disables_build_cache=False
283
+ )
284
+
272
285
  SOLC_MAP = AttrUtil.AttributeDefinition(
273
286
  attr_validation_func=Vf.validate_solc_map,
274
287
  arg_type=AttrUtil.AttrArgType.MAP,
@@ -496,6 +509,37 @@ class EvmAttributes(AttrUtil.Attributes):
496
509
  disables_build_cache=False
497
510
  )
498
511
 
512
+ CONTRACT_EXTENSIONS = AttrUtil.AttributeDefinition(
513
+ attr_validation_func=Vf.validate_contract_extension_attr,
514
+ help_msg="Dictionary of extension contracts. Format:\n"
515
+ '{\n'
516
+ ' "ExtendedContractA: [\n'
517
+ ' {\n'
518
+ ' "extension": "ExtenderA",\n'
519
+ ' "exclude": [\n'
520
+ ' "method1",\n'
521
+ ' ...,\n'
522
+ ' "methodN"\n'
523
+ ' ]\n'
524
+ ' },\n'
525
+ ' {\n'
526
+ ' ...\n'
527
+ ' }\n'
528
+ ' ],\n'
529
+ ' "ExtendedContractB: [\n'
530
+ ' ...\n'
531
+ ' ],\n'
532
+ ' ...\n'
533
+ '}',
534
+ default_desc="",
535
+ affects_build_cache_key=True,
536
+ disables_build_cache=False,
537
+ arg_type=AttrUtil.AttrArgType.MAP,
538
+ argparse_args={
539
+ 'action': AttrUtil.UniqueStore
540
+ }
541
+ )
542
+
499
543
  PROTOTYPE = AttrUtil.AttributeDefinition(
500
544
  attr_validation_func=Vf.validate_prototype_attr,
501
545
  arg_type=AttrUtil.AttrArgType.LIST,
@@ -607,17 +651,6 @@ class EvmAttributes(AttrUtil.Attributes):
607
651
  disables_build_cache=False
608
652
  )
609
653
 
610
- TYPECHECKER_ARGS = AttrUtil.AttributeDefinition(
611
- arg_type=AttrUtil.AttrArgType.LIST,
612
- help_msg="Send flags directly to the CVL typechecker",
613
- default_desc="",
614
- argparse_args={
615
- 'nargs': AttrUtil.ONE_OR_MORE_OCCURRENCES,
616
- 'action': AttrUtil.APPEND
617
- },
618
- affects_build_cache_key=False,
619
- disables_build_cache=False
620
- )
621
654
  INTERNAL_FUNCS = AttrUtil.AttributeDefinition(
622
655
  attr_validation_func=Vf.validate_json_file,
623
656
  argparse_args={
@@ -767,8 +800,6 @@ class InternalUseAttributes(AttrUtil.Attributes):
767
800
  )
768
801
 
769
802
 
770
-
771
-
772
803
  class BackendAttributes(AttrUtil.Attributes):
773
804
 
774
805
  UNUSED_SUMMARY_HARD_FAIL = AttrUtil.AttributeDefinition(
@@ -1334,6 +1365,7 @@ class BackendAttributes(AttrUtil.Attributes):
1334
1365
 
1335
1366
  ALLOW_SOLIDITY_CALLS_IN_QUANTIFIERS = AttrUtil.AttributeDefinition(
1336
1367
  arg_type=AttrUtil.AttrArgType.BOOLEAN,
1368
+ jar_flag='-allowSolidityQuantifierCalls',
1337
1369
  argparse_args={
1338
1370
  'action': AttrUtil.STORE_TRUE
1339
1371
  },
@@ -1403,7 +1435,7 @@ def set_attribute_class(cls: Type[AttrUtil.Attributes]) -> None:
1403
1435
 
1404
1436
  def detect_application_class(args: List[str]) -> Type[AttrUtil.Attributes]:
1405
1437
  evm_suffixes = ('.sol', '.vy', '.yul', '.tac', '.json')
1406
- wasm_suffixes = '.wat'
1438
+ wasm_suffixes = ('.wat', '.wasm')
1407
1439
  solana_suffixes = ('.so', '.o')
1408
1440
 
1409
1441
  attributes_logger.debug("calling detect_application_class")