certora-cli 8.13.1__tar.gz → 8.16.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.
- {certora_cli-8.13.1/certora_cli.egg-info → certora_cli-8.16.2}/PKG-INFO +2 -2
- certora_cli-8.16.2/README.md +1 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuild.py +78 -36
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraCloudIO.py +25 -10
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraSourceFinders.py +33 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Mutate/mutateApp.py +5 -4
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/certoraValidateFuncs.py +2 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/proverCommon.py +10 -3
- {certora_cli-8.13.1 → certora_cli-8.16.2/certora_cli.egg-info}/PKG-INFO +2 -2
- certora_cli-8.13.1/certora_jars/Typechecker.jar → certora_cli-8.16.2/certora_jars/ASTExtraction.jar +0 -0
- certora_cli-8.16.2/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
- certora_cli-8.13.1/certora_jars/ASTExtraction.jar → certora_cli-8.16.2/certora_jars/Typechecker.jar +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/setup.py +2 -2
- certora_cli-8.13.1/README.md +0 -1
- certora_cli-8.13.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
- {certora_cli-8.13.1 → certora_cli-8.16.2}/LICENSE +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/MANIFEST.in +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_bins/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollector.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorFactory.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorSolBased.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/castingInstrumenter.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraApp.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuildCacheManager.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuildDataClasses.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuildRust.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuildSui.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraCollectConfigurationLayout.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraCollectRunMetadata.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraCompilerParameters.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraConfigIO.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContext.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContextAttributes.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContextClass.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContextValidator.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContractFuncs.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraExtensionInfo.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraJobList.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraMiniSpecParser.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraNodeFilters.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraOffsetConverter.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraParseBuildScript.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraProjectScanner.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraType.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraVerifyGenerator.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/erc7201.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/splitRules.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/storageExtension.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/uncheckedOverflowInstrumenter.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Mutate/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Mutate/mutateAttributes.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Mutate/mutateConstants.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Mutate/mutateUtil.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Mutate/mutateValidate.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/ExpectedComparator.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/certoraAttrUtil.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/certoraLogging.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/Shared/certoraUtils.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraCVLFormatter.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraConcord.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraEVMProver.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraEqCheck.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraMutate.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraRanger.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraRun.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraSolanaProver.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraSorobanProver.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/certoraSuiProver.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/rustMutator.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli.egg-info/SOURCES.txt +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli.egg-info/dependency_links.txt +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli.egg-info/entry_points.txt +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli.egg-info/requires.txt +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli.egg-info/top_level.txt +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/certora_jars/__init__.py +0 -0
- {certora_cli-8.13.1 → certora_cli-8.16.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: certora-cli
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.16.2
|
|
4
4
|
Summary: Runner for the Certora Prover
|
|
5
5
|
Home-page: https://pypi.org/project/certora-cli
|
|
6
6
|
Author: Certora
|
|
@@ -38,4 +38,4 @@ Dynamic: requires-dist
|
|
|
38
38
|
Dynamic: requires-python
|
|
39
39
|
Dynamic: summary
|
|
40
40
|
|
|
41
|
-
Commit
|
|
41
|
+
Commit 94b86f7. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Commit 94b86f7. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
|
@@ -1448,7 +1448,7 @@ class CertoraBuildGenerator:
|
|
|
1448
1448
|
if not self.context.strict_solc_optimizer and self.get_solc_via_ir_value(contract_file_path):
|
|
1449
1449
|
# The default optimizer steps (taken from libsolidity/interface/OptimiserSettings.h) but with the
|
|
1450
1450
|
# full inliner step removed
|
|
1451
|
-
|
|
1451
|
+
solc0_8_34_to_0_8_35 = ("dfDvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLvifMCTUca[r]LSsTFOtfDnca[r]"
|
|
1452
1452
|
"IulcscCTUtvifMx[scCTUt]TOntnfDIulvifMjmul[jul]VcTOculjmul")
|
|
1453
1453
|
solc0_8_26_to_0_8_33 = ("dhfoDgvulfnTUtnIfxa[r]EscLMVcul[j]Trpeulxa[r]cLCTUca[r]LSsTFOtfDnca[r]" +
|
|
1454
1454
|
"IulcscCTUtx[scCTUt]TOntnfDIuljmul[jul]VcTOculjmul")
|
|
@@ -1501,8 +1501,8 @@ class CertoraBuildGenerator:
|
|
|
1501
1501
|
yul_optimizer_steps = solc0_8_13_to_0_8_25
|
|
1502
1502
|
elif minor == 8 and 26 <= patch <= 33:
|
|
1503
1503
|
yul_optimizer_steps = solc0_8_26_to_0_8_33
|
|
1504
|
-
elif minor == 8 and 34 <= patch <=
|
|
1505
|
-
yul_optimizer_steps =
|
|
1504
|
+
elif minor == 8 and 34 <= patch <= 35:
|
|
1505
|
+
yul_optimizer_steps = solc0_8_34_to_0_8_35
|
|
1506
1506
|
assert yul_optimizer_steps is not None, \
|
|
1507
1507
|
'Yul Optimizer steps missing for requested Solidity version. Please contact Certora team.'
|
|
1508
1508
|
|
|
@@ -1839,6 +1839,42 @@ class CertoraBuildGenerator:
|
|
|
1839
1839
|
else:
|
|
1840
1840
|
raise Util.CertoraUserInputError(err_msg)
|
|
1841
1841
|
|
|
1842
|
+
def _resolve_data_contracts_key(self,
|
|
1843
|
+
contract_file: str,
|
|
1844
|
+
compile_wd: Path,
|
|
1845
|
+
data: Dict[str, Any],
|
|
1846
|
+
resolved_to_orig: Optional[Dict[str, str]] = None,
|
|
1847
|
+
) -> str:
|
|
1848
|
+
"""Resolve which key in data[CONTRACTS] corresponds to ``contract_file``.
|
|
1849
|
+
|
|
1850
|
+
solc's standard-json output keys files in whatever form solc saw them — relative to
|
|
1851
|
+
compile_wd, absolute, the raw input string, or (for symlinks) a previously seen original.
|
|
1852
|
+
We try each in order and return the matching key. Raises if no form matches.
|
|
1853
|
+
|
|
1854
|
+
``resolved_to_orig`` is optional: the worklist populates it from base contracts and link
|
|
1855
|
+
references; the pre-worklist seed for the primary file passes ``None``.
|
|
1856
|
+
"""
|
|
1857
|
+
contract_file_abs = str(Util.abs_norm_path(contract_file))
|
|
1858
|
+
# using os.path.relpath because Path.relative_to cannot go up the directory tree (no ..)
|
|
1859
|
+
contract_file_rel = os.path.relpath(Path(contract_file_abs), compile_wd)
|
|
1860
|
+
|
|
1861
|
+
build_logger.debug(f"available keys: {data[CONTRACTS].keys()}")
|
|
1862
|
+
if contract_file_rel in data[CONTRACTS]:
|
|
1863
|
+
return contract_file_rel
|
|
1864
|
+
if contract_file_abs in data[CONTRACTS]:
|
|
1865
|
+
return contract_file_abs
|
|
1866
|
+
if contract_file in data[CONTRACTS]:
|
|
1867
|
+
# when does this happen? Saw this in TrustToken on a package source file
|
|
1868
|
+
return contract_file
|
|
1869
|
+
orig = resolved_to_orig.get(contract_file) if resolved_to_orig is not None else None
|
|
1870
|
+
if orig is not None and orig in data[CONTRACTS]:
|
|
1871
|
+
return orig
|
|
1872
|
+
# our file may be a symlink!
|
|
1873
|
+
raise Exception(
|
|
1874
|
+
f"Worklist contains {contract_file} (relative {contract_file_rel}, "
|
|
1875
|
+
f"absolute {contract_file_abs}), resolved from {orig} "
|
|
1876
|
+
f"that does not exist in contract set {orig in data[CONTRACTS] if orig else False}")
|
|
1877
|
+
|
|
1842
1878
|
def collect_for_file(self,
|
|
1843
1879
|
build_arg_contract_file: str,
|
|
1844
1880
|
file_index: int,
|
|
@@ -1986,6 +2022,12 @@ class CertoraBuildGenerator:
|
|
|
1986
2022
|
contracts_with_libraries = {}
|
|
1987
2023
|
file_compiler_path = compiler_collector.normalize_file_compiler_path_name(file_abs_path)
|
|
1988
2024
|
|
|
2025
|
+
# Resolve the data[CONTRACTS] key for the primary file once, before the worklist runs.
|
|
2026
|
+
# This is the (file, *) half of the primary contract-handle set used to scope the bytecode
|
|
2027
|
+
# check inside get_bytecode — see the primary_contract_handles construction just below.
|
|
2028
|
+
primary_file_key = self._resolve_data_contracts_key(str(Path(file_compiler_path)), compile_wd, data)
|
|
2029
|
+
primary_contract_handles: Set[Tuple[str, str]] = {(primary_file_key, n) for n in contracts_in_file}
|
|
2030
|
+
|
|
1989
2031
|
# But apparently this heavily depends on the Solidity AST format anyway
|
|
1990
2032
|
|
|
1991
2033
|
# Need to add all library dependencies that are in a different file:
|
|
@@ -1997,31 +2039,8 @@ class CertoraBuildGenerator:
|
|
|
1997
2039
|
contract_file_obj = contracts_to_add_dependencies_queue.pop()
|
|
1998
2040
|
contract_file = str(contract_file_obj)
|
|
1999
2041
|
build_logger.debug(f"Processing dependencies from file {contract_file}")
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
# using os.path.relpath because Path.relative_to cannot go up the directory tree (no ..)
|
|
2004
|
-
contract_file_rel = os.path.relpath(Path(contract_file_abs), compile_wd)
|
|
2005
|
-
|
|
2006
|
-
build_logger.debug(f"available keys: {data['contracts'].keys()}")
|
|
2007
|
-
if contract_file_rel in data[CONTRACTS]:
|
|
2008
|
-
contract_file = contract_file_rel
|
|
2009
|
-
unsorted_contract_list = data[CONTRACTS][contract_file]
|
|
2010
|
-
elif contract_file_abs in data[CONTRACTS]:
|
|
2011
|
-
contract_file = contract_file_abs
|
|
2012
|
-
unsorted_contract_list = data[CONTRACTS][contract_file_abs]
|
|
2013
|
-
elif contract_file in data[CONTRACTS]:
|
|
2014
|
-
# when does this happen? Saw this in TrustToken on a package source file
|
|
2015
|
-
unsorted_contract_list = data[CONTRACTS][contract_file]
|
|
2016
|
-
elif resolved_to_orig.get(contract_file) in data[CONTRACTS]:
|
|
2017
|
-
unsorted_contract_list = data[CONTRACTS][resolved_to_orig[contract_file]]
|
|
2018
|
-
contract_file = resolved_to_orig[contract_file]
|
|
2019
|
-
else:
|
|
2020
|
-
# our file may be a symlink!
|
|
2021
|
-
raise Exception(
|
|
2022
|
-
f"Worklist contains {contract_file} (relative {contract_file_rel}, "
|
|
2023
|
-
f"absolute {contract_file_abs}), resolved from {resolved_to_orig.get(contract_file)} "
|
|
2024
|
-
f"that does not exist in contract set {resolved_to_orig.get(contract_file) in data['contracts']}")
|
|
2042
|
+
contract_file = self._resolve_data_contracts_key(contract_file, compile_wd, data, resolved_to_orig)
|
|
2043
|
+
unsorted_contract_list = data[CONTRACTS][contract_file]
|
|
2025
2044
|
|
|
2026
2045
|
contract_list = sorted([c for c in unsorted_contract_list])
|
|
2027
2046
|
# every contract file may contain numerous primary contracts, but the dependent contracts
|
|
@@ -2125,7 +2144,7 @@ class CertoraBuildGenerator:
|
|
|
2125
2144
|
contracts_with_chosen_addresses,
|
|
2126
2145
|
data,
|
|
2127
2146
|
report_source_file,
|
|
2128
|
-
|
|
2147
|
+
primary_contract_handles,
|
|
2129
2148
|
build_arg_contract_file,
|
|
2130
2149
|
compiler_collector,
|
|
2131
2150
|
compile_wd,
|
|
@@ -2208,7 +2227,7 @@ class CertoraBuildGenerator:
|
|
|
2208
2227
|
def get_bytecode(self,
|
|
2209
2228
|
bytecode_object: Dict[str, Any],
|
|
2210
2229
|
contract_name: str,
|
|
2211
|
-
|
|
2230
|
+
is_primary: bool,
|
|
2212
2231
|
contracts_with_chosen_addresses: List[Tuple[int, Any]],
|
|
2213
2232
|
is_deployed_bytecode: bool
|
|
2214
2233
|
) -> str:
|
|
@@ -2218,7 +2237,11 @@ class CertoraBuildGenerator:
|
|
|
2218
2237
|
|
|
2219
2238
|
@param bytecode_object - the output from the Solidity compiler
|
|
2220
2239
|
@param contract_name - the contract that we are working on
|
|
2221
|
-
@param
|
|
2240
|
+
@param is_primary - whether (source_file, contract_name) is a primary handle in the current
|
|
2241
|
+
compilation; only primaries are required to have non-empty bytecode. The (file, name)
|
|
2242
|
+
scoping avoids false positives when a transitively-imported abstract contract shares a
|
|
2243
|
+
name with a primary (e.g. local concrete `Proxy` vs OpenZeppelin's abstract `Proxy`).
|
|
2244
|
+
Caller computes membership against the primary_contract_handles set.
|
|
2222
2245
|
@param contracts_with_chosen_addresses - a list of tuples of addresses and the
|
|
2223
2246
|
associated contract identifier
|
|
2224
2247
|
@param is_deployed_bytecode - true if we deal with deployed (runtime) bytecode,
|
|
@@ -2241,7 +2264,7 @@ class CertoraBuildGenerator:
|
|
|
2241
2264
|
f"If you need the contract, import the missing library directly and add a dummy usage:\n"
|
|
2242
2265
|
orig_msg = str(e)
|
|
2243
2266
|
raise Util.CertoraUserInputError(prefix_msg + orig_msg)
|
|
2244
|
-
if
|
|
2267
|
+
if is_primary and len(bytecode) == 0:
|
|
2245
2268
|
msg = f"Contract {contract_name} has no bytecode. " \
|
|
2246
2269
|
f"It may be caused because the contract is abstract, " \
|
|
2247
2270
|
f"or is missing constructor code. Please check the output of the Solidity compiler."
|
|
@@ -2350,7 +2373,7 @@ class CertoraBuildGenerator:
|
|
|
2350
2373
|
contracts_with_chosen_addresses: List[Tuple[int, Any]],
|
|
2351
2374
|
data: Dict[str, Any],
|
|
2352
2375
|
report_source_file: str,
|
|
2353
|
-
|
|
2376
|
+
primary_contract_handles: Set[Tuple[str, str]],
|
|
2354
2377
|
build_arg_contract_file: str,
|
|
2355
2378
|
compiler_collector_for_contract_file: CompilerCollector,
|
|
2356
2379
|
compile_wd: Path,
|
|
@@ -2422,12 +2445,14 @@ class CertoraBuildGenerator:
|
|
|
2422
2445
|
(srcmap, constructor_srcmap) = self.collect_srcmap(contract_data)
|
|
2423
2446
|
|
|
2424
2447
|
varmap = ""
|
|
2448
|
+
contract_is_primary = (source_code_file, contract_name) in primary_contract_handles
|
|
2425
2449
|
deployed_bytecode = self.get_bytecode(contract_data["evm"]["deployedBytecode"], contract_name,
|
|
2426
|
-
|
|
2450
|
+
contract_is_primary,
|
|
2427
2451
|
contracts_with_chosen_addresses, True)
|
|
2428
2452
|
deployed_bytecode = compiler_lang.normalize_deployed_bytecode(
|
|
2429
2453
|
deployed_bytecode)
|
|
2430
|
-
constructor_bytecode = self.get_bytecode(contract_data["evm"]["bytecode"], contract_name,
|
|
2454
|
+
constructor_bytecode = self.get_bytecode(contract_data["evm"]["bytecode"], contract_name,
|
|
2455
|
+
contract_is_primary,
|
|
2431
2456
|
contracts_with_chosen_addresses, False)
|
|
2432
2457
|
constructor_bytecode = compiler_lang.normalize_deployed_bytecode(
|
|
2433
2458
|
constructor_bytecode)
|
|
@@ -2509,7 +2534,24 @@ class CertoraBuildGenerator:
|
|
|
2509
2534
|
|
|
2510
2535
|
@staticmethod
|
|
2511
2536
|
def get_primary_contract_from_sdc(contracts: List[ContractInSDC], primary: str, primary_file: str) -> List[ContractInSDC]:
|
|
2512
|
-
|
|
2537
|
+
name_matched = [x for x in contracts if x.name == primary]
|
|
2538
|
+
if not name_matched:
|
|
2539
|
+
return []
|
|
2540
|
+
primary_parts = Path(Util.abs_norm_path(primary_file)).parts
|
|
2541
|
+
best = None
|
|
2542
|
+
best_suffix_len = -1
|
|
2543
|
+
for x in name_matched:
|
|
2544
|
+
x_parts = Path(Util.abs_norm_path(x.original_file)).parts
|
|
2545
|
+
suffix_len = 0
|
|
2546
|
+
for a, b in zip(reversed(primary_parts), reversed(x_parts)):
|
|
2547
|
+
if a == b:
|
|
2548
|
+
suffix_len += 1
|
|
2549
|
+
else:
|
|
2550
|
+
break
|
|
2551
|
+
if suffix_len > best_suffix_len:
|
|
2552
|
+
best_suffix_len = suffix_len
|
|
2553
|
+
best = x
|
|
2554
|
+
return [best] if best is not None else []
|
|
2513
2555
|
|
|
2514
2556
|
@staticmethod
|
|
2515
2557
|
def generate_library_import(file_absolute_path: str, library_name: str) -> str:
|
|
@@ -18,6 +18,8 @@ import json
|
|
|
18
18
|
import os
|
|
19
19
|
import re
|
|
20
20
|
import uuid
|
|
21
|
+
from collections import UserDict
|
|
22
|
+
from collections.abc import MutableMapping
|
|
21
23
|
|
|
22
24
|
import requests
|
|
23
25
|
import sys
|
|
@@ -823,24 +825,22 @@ class CloudVerification:
|
|
|
823
825
|
|
|
824
826
|
return True
|
|
825
827
|
|
|
826
|
-
def
|
|
828
|
+
def cli_verify(self, cl_args: str) -> Optional[JobList]:
|
|
827
829
|
"""
|
|
828
830
|
Sends a verification request to HTTP Handler, uploads a zip file, and outputs the results.
|
|
829
|
-
:param wait_for_results: If "all", we will wait for the results of the verification. If "none",
|
|
830
|
-
the run will be terminated after sending the job to the cloud.
|
|
831
831
|
:param cl_args: A string that can be copied to and run by the shell to recreate this run.
|
|
832
|
-
@returns
|
|
833
|
-
Otherwise, returns
|
|
832
|
+
@returns job_list which defines the jobs that have been submitted successfully.
|
|
833
|
+
Otherwise, returns None.
|
|
834
834
|
"""
|
|
835
835
|
|
|
836
836
|
post_result = self.__send_verification_request(cl_args)
|
|
837
837
|
if not post_result:
|
|
838
|
-
return
|
|
838
|
+
return None
|
|
839
839
|
|
|
840
840
|
file_upload_success = self.__compress_and_upload_zip_files()
|
|
841
841
|
|
|
842
842
|
if not file_upload_success:
|
|
843
|
-
return
|
|
843
|
+
return None
|
|
844
844
|
|
|
845
845
|
# set results urls. They are all functions of the form: self.set_output_url(self.userId, self.anonymousKey)
|
|
846
846
|
for func_name in ["set_output_url", "set_report_url", "set_zip_output_url", "set_json_output_url",
|
|
@@ -860,6 +860,18 @@ class CloudVerification:
|
|
|
860
860
|
self.vscode_extension_info_writer.add_field("verification_report_url", self.url_print_format())
|
|
861
861
|
self.vscode_extension_info_writer.write_file()
|
|
862
862
|
|
|
863
|
+
return job_list
|
|
864
|
+
|
|
865
|
+
def cli_report(self, job_list: JobList, wait_for_results: str) -> bool:
|
|
866
|
+
"""
|
|
867
|
+
Outputs and report the results from verification.
|
|
868
|
+
:param wait_for_results: If "all", we will wait for the results of the verification. If "none",
|
|
869
|
+
the run will be terminated after sending the job to the cloud.
|
|
870
|
+
:param job_list: A JobList which contains data relevant to the job which was sent for verification.
|
|
871
|
+
@returns If compareToExpected is True, returns True when the expected output equals the actual results.
|
|
872
|
+
Otherwise, returns False if there was at least one violated rule.
|
|
873
|
+
"""
|
|
874
|
+
|
|
863
875
|
if not wait_for_results or wait_for_results == str(Vf.WaitForResultOptions.NONE):
|
|
864
876
|
job_list.save_data()
|
|
865
877
|
job_list.save_recent_jobs_to_path()
|
|
@@ -927,10 +939,10 @@ class CloudVerification:
|
|
|
927
939
|
response = None
|
|
928
940
|
print("\nConnecting to server...")
|
|
929
941
|
cloud_logger.debug(f"requesting verification from {verify_url}")
|
|
930
|
-
headers = {
|
|
942
|
+
headers: MutableMapping[str, str | bytes] = UserDict({
|
|
931
943
|
'Authorization': f'Bearer {self.context.key}',
|
|
932
944
|
"Content-Type": "application/json"
|
|
933
|
-
}
|
|
945
|
+
})
|
|
934
946
|
|
|
935
947
|
# retry on request timeout or 502 (must take no more than 3 minutes)
|
|
936
948
|
# print error message on the 3rd exception and return
|
|
@@ -953,6 +965,9 @@ class CloudVerification:
|
|
|
953
965
|
elif status == 401:
|
|
954
966
|
print("Invalid key or key is missing. Please, make sure you entered a valid key.")
|
|
955
967
|
return None
|
|
968
|
+
elif status == 426:
|
|
969
|
+
output_error_response(response)
|
|
970
|
+
return None
|
|
956
971
|
elif status >= 500:
|
|
957
972
|
cloud_logger.debug(f'{status} received. Retry...')
|
|
958
973
|
if i < self.verification_request_retries:
|
|
@@ -961,7 +976,7 @@ class CloudVerification:
|
|
|
961
976
|
else:
|
|
962
977
|
print("Oops, an error occurred when sending your request. Please try again later")
|
|
963
978
|
return None
|
|
964
|
-
else: # status != 200, 403, 5XX
|
|
979
|
+
else: # status != 200, 403, 401, 426, 5XX
|
|
965
980
|
output_error_response(response)
|
|
966
981
|
return None
|
|
967
982
|
except requests.exceptions.Timeout:
|
|
@@ -220,6 +220,33 @@ def handle_unsupported_decl_value(source_snippet: str,
|
|
|
220
220
|
False)
|
|
221
221
|
|
|
222
222
|
|
|
223
|
+
def is_idempotent_index_expr(expr: Dict[str, Any]) -> bool:
|
|
224
|
+
"""Check if an index expression is idempotent (safe to duplicate without side effects).
|
|
225
|
+
Only allows Identifier, Literal, and arithmetic BinaryOperations (recursively)."""
|
|
226
|
+
node_type = expr.get("nodeType")
|
|
227
|
+
if node_type in ("Identifier", "Literal"):
|
|
228
|
+
return True
|
|
229
|
+
if node_type == "BinaryOperation" and expr.get("operator") in ("+", "-", "*", "/", "%"):
|
|
230
|
+
left = expr.get("leftExpression", {})
|
|
231
|
+
right = expr.get("rightExpression", {})
|
|
232
|
+
return is_idempotent_index_expr(left) and is_idempotent_index_expr(right)
|
|
233
|
+
return False
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def has_non_idempotent_index(left_hand_side: Dict[str, Any]) -> bool:
|
|
237
|
+
"""Walk the LHS chain and check that all IndexAccess indexExpressions are idempotent."""
|
|
238
|
+
node = left_hand_side
|
|
239
|
+
while node["nodeType"] in ("MemberAccess", "IndexAccess"):
|
|
240
|
+
if node["nodeType"] == "IndexAccess":
|
|
241
|
+
index_expr = node.get("indexExpression", {})
|
|
242
|
+
if not is_idempotent_index_expr(index_expr):
|
|
243
|
+
return True
|
|
244
|
+
node = node["baseExpression"]
|
|
245
|
+
else: # MemberAccess
|
|
246
|
+
node = node["expression"]
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
|
|
223
250
|
def handle_multi_def(source_snippet: str, assignment: Dict[str, Any]) -> LocalAssignmentFinderData:
|
|
224
251
|
return handle_unsupported_def_value(source_snippet, assignment)
|
|
225
252
|
|
|
@@ -260,6 +287,12 @@ def handle_single_def(source_snippet: str, finder_running_id: int, assignment: D
|
|
|
260
287
|
f"The assignment {source_snippet} assigns to a storage variable, skipping")
|
|
261
288
|
return handle_to_skip()
|
|
262
289
|
|
|
290
|
+
# check that all index expressions in the LHS chain are idempotent (no side effects like i++)
|
|
291
|
+
if has_non_idempotent_index(left_hand_side):
|
|
292
|
+
instrumentation_logger.debug(
|
|
293
|
+
f"The assignment {source_snippet} has a non-idempotent index expression, treating as unsupported")
|
|
294
|
+
return handle_unsupported_def_value(source_snippet, assignment)
|
|
295
|
+
|
|
263
296
|
type_string = left_hand_side["typeDescriptions"]["typeString"]
|
|
264
297
|
|
|
265
298
|
if is_unsupported_type(type_string):
|
|
@@ -22,6 +22,7 @@ import re
|
|
|
22
22
|
import shutil
|
|
23
23
|
import subprocess
|
|
24
24
|
import sys
|
|
25
|
+
from collections import UserDict
|
|
25
26
|
from dataclasses import dataclass
|
|
26
27
|
from pathlib import Path
|
|
27
28
|
from typing import Optional, Any, List, Dict, Tuple, Set, Union
|
|
@@ -503,7 +504,7 @@ class WebUtils:
|
|
|
503
504
|
self.mutation_test_final_result_url = f"https://{mutation_test_domain}"
|
|
504
505
|
mutation_logger.debug(f"Using server {args.server} with mutation_test_id_url {self.mutation_test_id_url}")
|
|
505
506
|
|
|
506
|
-
def put_response_with_timeout(self, url: str, data: Any, headers:
|
|
507
|
+
def put_response_with_timeout(self, url: str, data: Any, headers: UserDict) -> Optional[requests.Response]:
|
|
507
508
|
"""
|
|
508
509
|
Executes a put request and returns the response, uses a timeout mechanism
|
|
509
510
|
|
|
@@ -526,7 +527,7 @@ class WebUtils:
|
|
|
526
527
|
return None
|
|
527
528
|
|
|
528
529
|
def put_json_request_with_timeout(self, url: str, body: Dict[str, Any],
|
|
529
|
-
headers:
|
|
530
|
+
headers: UserDict) -> Optional[requests.Response]:
|
|
530
531
|
"""
|
|
531
532
|
Executes a put request and returns the response, uses a timeout mechanism
|
|
532
533
|
|
|
@@ -1725,7 +1726,7 @@ class MutateApp:
|
|
|
1725
1726
|
@staticmethod
|
|
1726
1727
|
def upload_file_to_cloud_storage(web_utils: WebUtils, presigned_url: str, data: Any) -> None:
|
|
1727
1728
|
mutation_logger.debug("Uploading file")
|
|
1728
|
-
headers = {"Content-Type": "application/json"}
|
|
1729
|
+
headers = UserDict({"Content-Type": "application/json"})
|
|
1729
1730
|
put_resp = web_utils.put_response_with_timeout(presigned_url, json.dumps(data), headers)
|
|
1730
1731
|
if not put_resp:
|
|
1731
1732
|
raise ConnectionError(f"Failed to submit to presigned URL. url - {presigned_url}")
|
|
@@ -1973,7 +1974,7 @@ def get_top_level_rules(progress_json: Dict[str, Any]) -> Optional[List[Dict[str
|
|
|
1973
1974
|
|
|
1974
1975
|
|
|
1975
1976
|
certora_key = os.getenv(KEY_ENV_VAR, '')
|
|
1976
|
-
auth_headers = {"Authorization": f"Bearer {certora_key}", "Content-Type": "application/json"}
|
|
1977
|
+
auth_headers = UserDict({"Authorization": f"Bearer {certora_key}", "Content-Type": "application/json"})
|
|
1977
1978
|
default_cookies = {str(MConstants.CERTORA_KEY): certora_key}
|
|
1978
1979
|
|
|
1979
1980
|
|
|
@@ -50,6 +50,7 @@ log = logging.getLogger(__name__)
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
VIOLATIONS_EXIT_CODE = 100
|
|
53
|
+
CLOUD_ERROR_EXIT_CODE = 426
|
|
53
54
|
|
|
54
55
|
@dataclass
|
|
55
56
|
class CertoraRunResult:
|
|
@@ -220,9 +221,13 @@ def run_remote(
|
|
|
220
221
|
|
|
221
222
|
pretty_args = [f"'{a}'" if " " in a else a for a in args]
|
|
222
223
|
|
|
223
|
-
|
|
224
|
+
job_list = cloud.cli_verify(" ".join(pretty_args))
|
|
225
|
+
if job_list:
|
|
226
|
+
ok = cloud.cli_report(job_list, context.wait_for_results)
|
|
227
|
+
exit_code = 0 if ok else VIOLATIONS_EXIT_CODE
|
|
228
|
+
else:
|
|
229
|
+
exit_code = CLOUD_ERROR_EXIT_CODE
|
|
224
230
|
|
|
225
|
-
exit_code = 0 if ok else VIOLATIONS_EXIT_CODE
|
|
226
231
|
result: Optional[CertoraRunResult] = None
|
|
227
232
|
if cloud.statusUrl:
|
|
228
233
|
result = CertoraRunResult(
|
|
@@ -248,8 +253,10 @@ def handle_exit(exit_code: int, return_value: Optional[CertoraRunResult]) -> Opt
|
|
|
248
253
|
"""
|
|
249
254
|
if exit_code == VIOLATIONS_EXIT_CODE:
|
|
250
255
|
raise CertoraFoundViolations("violations were found", return_value)
|
|
256
|
+
if exit_code == CLOUD_ERROR_EXIT_CODE:
|
|
257
|
+
raise Util.CertoraUserInputError("Certora Prover run failed due to a job submission error")
|
|
251
258
|
if exit_code != 0:
|
|
252
|
-
raise Util.CertoraUserInputError(f"
|
|
259
|
+
raise Util.CertoraUserInputError(f"Certora Prover run failed (code {exit_code})")
|
|
253
260
|
return return_value
|
|
254
261
|
|
|
255
262
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: certora-cli
|
|
3
|
-
Version: 8.
|
|
3
|
+
Version: 8.16.2
|
|
4
4
|
Summary: Runner for the Certora Prover
|
|
5
5
|
Home-page: https://pypi.org/project/certora-cli
|
|
6
6
|
Author: Certora
|
|
@@ -38,4 +38,4 @@ Dynamic: requires-dist
|
|
|
38
38
|
Dynamic: requires-python
|
|
39
39
|
Dynamic: summary
|
|
40
40
|
|
|
41
|
-
Commit
|
|
41
|
+
Commit 94b86f7. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
certora_cli-8.13.1/certora_jars/Typechecker.jar → certora_cli-8.16.2/certora_jars/ASTExtraction.jar
RENAMED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name": "certora-cli", "tag": "8.16.2", "branch": "", "commit": "94b86f7", "timestamp": "20260625.11.18.041053", "version": "8.16.2"}
|
certora_cli-8.13.1/certora_jars/ASTExtraction.jar → certora_cli-8.16.2/certora_jars/Typechecker.jar
RENAMED
|
Binary file
|
|
@@ -3,11 +3,11 @@ import setuptools
|
|
|
3
3
|
|
|
4
4
|
setuptools.setup(
|
|
5
5
|
name="certora-cli",
|
|
6
|
-
version="8.
|
|
6
|
+
version="8.16.2",
|
|
7
7
|
author="Certora",
|
|
8
8
|
author_email="support@certora.com",
|
|
9
9
|
description="Runner for the Certora Prover",
|
|
10
|
-
long_description="Commit
|
|
10
|
+
long_description="Commit 94b86f7. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.",
|
|
11
11
|
long_description_content_type="text/markdown",
|
|
12
12
|
url="https://pypi.org/project/certora-cli",
|
|
13
13
|
packages=setuptools.find_packages(),
|
certora_cli-8.13.1/README.md
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Commit deb4423. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"name": "certora-cli", "tag": "8.13.1", "branch": "", "commit": "deb4423", "timestamp": "20260519.10.54.736103", "version": "8.13.1"}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollector.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorSol.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorVy.py
RENAMED
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/Compiler/CompilerCollectorYul.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuildCacheManager.py
RENAMED
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraBuildDataClasses.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraCollectRunMetadata.py
RENAMED
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraCompilerParameters.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContextAttributes.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraContextValidator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraMiniSpecParser.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraOffsetConverter.py
RENAMED
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraParseBuildScript.py
RENAMED
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraProjectScanner.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/certoraVerifyGenerator.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/CertoraProver/uncheckedOverflowInstrumenter.py
RENAMED
|
File without changes
|
|
File without changes
|
{certora_cli-8.13.1 → certora_cli-8.16.2}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|