certora-cli 7.21.1__tar.gz → 7.22.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-7.21.1/certora_cli.egg-info → certora_cli-7.22.2}/PKG-INFO +3 -2
- certora_cli-7.22.2/README.md +1 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraBuild.py +92 -82
- certora_cli-7.22.2/certora_cli/EVMVerifier/certoraBuildRust.py +78 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraCloudIO.py +81 -54
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraConfigIO.py +10 -4
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraContext.py +11 -68
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraContextAttributes.py +88 -46
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraContextValidator.py +16 -19
- certora_cli-7.22.2/certora_cli/EVMVerifier/certoraParseBuildScript.py +58 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraType.py +1 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Mutate/mutateApp.py +22 -10
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Shared/certoraAttrUtil.py +2 -2
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Shared/certoraUtils.py +12 -11
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Shared/certoraValidateFuncs.py +25 -7
- certora_cli-7.22.2/certora_cli/certoraEVMProver.py +21 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/certoraRun.py +4 -5
- certora_cli-7.22.2/certora_cli/certoraSolanaProver.py +152 -0
- certora_cli-7.22.2/certora_cli/certoraSorobanProver.py +21 -0
- certora_cli-7.22.2/certora_cli/rustMutator.py +401 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2/certora_cli.egg-info}/PKG-INFO +3 -2
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli.egg-info/SOURCES.txt +5 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli.egg-info/entry_points.txt +3 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli.egg-info/requires.txt +1 -0
- certora_cli-7.22.2/certora_jars/CERTORA-CLI-VERSION-METADATA.json +1 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_jars/Typechecker.jar +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/setup.py +7 -4
- certora_cli-7.21.1/README.md +0 -1
- certora_cli-7.21.1/certora_cli/rustMutator.py +0 -286
- certora_cli-7.21.1/certora_jars/CERTORA-CLI-VERSION-METADATA.json +0 -1
- {certora_cli-7.21.1 → certora_cli-7.22.2}/LICENSE +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/MANIFEST.in +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_bins/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/CompilerCollector.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorFactory.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSol.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorSolBased.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorVy.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/CompilerCollectorYul.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/Compiler/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraBuildCacheManager.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraBuildDataClasses.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraCollectRunMetadata.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraCompilerParameters.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraContextClass.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraContractFuncs.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraExtensionInfo.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraJobList.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraMiniSpecParser.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraNodeFilters.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraSourceFinders.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EVMVerifier/certoraVerifyGenerator.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/Eq_default.conf +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/Eq_mc_no_out_template.spec +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/Eq_mc_template.spec +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/Eq_sanity.conf +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/Eq_template.spec +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/equivCheck.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/EquivalenceCheck/sanity.spec +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Mutate/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Mutate/mutateAttributes.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Mutate/mutateConstants.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Mutate/mutateUtil.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Mutate/mutateValidate.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Shared/ExpectedComparator.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Shared/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/Shared/certoraLogging.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/certoraEqCheck.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli/certoraMutate.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli.egg-info/dependency_links.txt +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_cli.egg-info/top_level.txt +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/certora_jars/__init__.py +0 -0
- {certora_cli-7.21.1 → certora_cli-7.22.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: certora-cli
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.22.2
|
|
4
4
|
Summary: Runner for the Certora Prover
|
|
5
5
|
Home-page: https://pypi.org/project/certora-cli
|
|
6
6
|
Author: Certora
|
|
@@ -22,5 +22,6 @@ Requires-Dist: tqdm
|
|
|
22
22
|
Requires-Dist: StrEnum
|
|
23
23
|
Requires-Dist: tomli
|
|
24
24
|
Requires-Dist: universalmutator
|
|
25
|
+
Requires-Dist: jinja2
|
|
25
26
|
|
|
26
|
-
Commit
|
|
27
|
+
Commit 31a4e5d. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Commit 31a4e5d. Build and Run scripts for executing the Certora Prover on Solidity smart contracts.
|
|
@@ -1577,7 +1577,17 @@ class CertoraBuildGenerator:
|
|
|
1577
1577
|
while queue:
|
|
1578
1578
|
pop = queue.pop(0)
|
|
1579
1579
|
if isinstance(pop, dict) and node_id_attrb in pop:
|
|
1580
|
-
|
|
1580
|
+
idAttr = pop[node_id_attrb]
|
|
1581
|
+
if isinstance(idAttr, int):
|
|
1582
|
+
container[int(idAttr)] = pop
|
|
1583
|
+
elif isinstance(idAttr, list) and len(idAttr) == 1 and isinstance(idAttr[0], int):
|
|
1584
|
+
# In the bug reported in https://certora.atlassian.net/browse/CERT-7643 the id field is a list
|
|
1585
|
+
# instead of a single integer - we except that the first element of the list is the actual id
|
|
1586
|
+
# and unpack it.
|
|
1587
|
+
container[int(idAttr[0])] = pop
|
|
1588
|
+
else:
|
|
1589
|
+
raise Exception(f"Unexpected type of attribute `{node_id_attrb}`, was {idAttr}, expected an integer or an int-typed list of length 1")
|
|
1590
|
+
|
|
1581
1591
|
for key, value in pop.items():
|
|
1582
1592
|
if (node_type_attrb in pop and
|
|
1583
1593
|
pop[node_type_attrb] == "InlineAssembly" and key == "externalReferences"):
|
|
@@ -2620,7 +2630,7 @@ class CertoraBuildGenerator:
|
|
|
2620
2630
|
sources_from_pre_finder_SDCs |= sdc.all_contract_files
|
|
2621
2631
|
sources = self.collect_sources(context, certora_verify_generator, sources_from_pre_finder_SDCs)
|
|
2622
2632
|
try:
|
|
2623
|
-
self.cwd_rel_in_sources =
|
|
2633
|
+
self.cwd_rel_in_sources = build_source_tree(sources, context)
|
|
2624
2634
|
except Exception as e:
|
|
2625
2635
|
build_logger.debug(f"build_source_tree failed. Sources: {sources}", exc_info=e)
|
|
2626
2636
|
raise
|
|
@@ -2787,85 +2797,6 @@ class CertoraBuildGenerator:
|
|
|
2787
2797
|
common_path = Path(os.path.commonpath(list(sources.union({cwd}))))
|
|
2788
2798
|
return cwd.relative_to(common_path), common_path
|
|
2789
2799
|
|
|
2790
|
-
@staticmethod
|
|
2791
|
-
def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bool = False) -> Path:
|
|
2792
|
-
"""
|
|
2793
|
-
Copies files to .certora_sources
|
|
2794
|
-
@returns the cwd relative in sources
|
|
2795
|
-
"""
|
|
2796
|
-
sources = sources_to_abs(sources)
|
|
2797
|
-
cwd_rel_in_sources, common_path = CertoraBuildGenerator.get_cwd_rel_in_sources(sources)
|
|
2798
|
-
|
|
2799
|
-
for source_path in sources:
|
|
2800
|
-
is_dir = source_path.is_dir()
|
|
2801
|
-
# copy file to the path of the file from the common root under the sources directory
|
|
2802
|
-
|
|
2803
|
-
# make sure directory exists
|
|
2804
|
-
target_path = Util.get_certora_sources_dir() / source_path.relative_to(common_path)
|
|
2805
|
-
target_directory = target_path if is_dir else target_path.parent
|
|
2806
|
-
try:
|
|
2807
|
-
target_directory.mkdir(parents=True, exist_ok=True)
|
|
2808
|
-
except OSError as e:
|
|
2809
|
-
build_logger.debug(f"Failed to create directory {target_directory}", exc_info=e)
|
|
2810
|
-
raise
|
|
2811
|
-
|
|
2812
|
-
# copy files. if we got a directory, nothing to do
|
|
2813
|
-
if is_dir:
|
|
2814
|
-
build_logger.debug(f"Skipping directory {source_path}")
|
|
2815
|
-
continue
|
|
2816
|
-
|
|
2817
|
-
try:
|
|
2818
|
-
if overwrite:
|
|
2819
|
-
# expecting target path to exist.
|
|
2820
|
-
if target_path.exists():
|
|
2821
|
-
build_logger.debug(f"Overwriting {target_path} by copying from {source_path}")
|
|
2822
|
-
else:
|
|
2823
|
-
build_logger.warning(f"Supposed to overwrite {target_path} by copying from {source_path}" +
|
|
2824
|
-
" but it does not exist... this may indicate bad things happen")
|
|
2825
|
-
if overwrite or not target_path.exists():
|
|
2826
|
-
build_logger.debug(f"Copying {source_path} to {target_path}")
|
|
2827
|
-
shutil.copyfile(source_path, target_path)
|
|
2828
|
-
except OSError as e:
|
|
2829
|
-
build_logger.debug(f"Couldn't copy {source_path} to {target_path}", exc_info=e)
|
|
2830
|
-
raise
|
|
2831
|
-
|
|
2832
|
-
# the empty file .cwd is written in the source tree to denote the current working directory
|
|
2833
|
-
if cwd_rel_in_sources != '.':
|
|
2834
|
-
file_path = Util.get_certora_sources_dir() / cwd_rel_in_sources / Util.CWD_FILE
|
|
2835
|
-
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
2836
|
-
file_path.touch()
|
|
2837
|
-
|
|
2838
|
-
"""
|
|
2839
|
-
Once the resource files are copied to the source tree, the paths in the value of the 'prover_resource_files'
|
|
2840
|
-
attribute are replaced with the relative path of the resource file from the source tree root.
|
|
2841
|
-
This way The server can easily find the resource files in the source tree. The path from the source tree is the
|
|
2842
|
-
relative path of cwd from source tree root (most cases '.') concatenated with the relative path of the resource
|
|
2843
|
-
file from cwd
|
|
2844
|
-
"""
|
|
2845
|
-
if context.prover_resource_files:
|
|
2846
|
-
new_value = []
|
|
2847
|
-
len_orig = len(context.prover_resource_files)
|
|
2848
|
-
for value in context.prover_resource_files:
|
|
2849
|
-
label, file_path = value.split(':')
|
|
2850
|
-
rel_path = Path(os.path.relpath(file_path, '.'))
|
|
2851
|
-
new_value.append(':'.join([label, os.path.normpath(rel_path)]))
|
|
2852
|
-
if len_orig != len(new_value):
|
|
2853
|
-
raise RuntimeError(f"fail to process prover_resource_files {len_orig} out of {len(new_value)}")
|
|
2854
|
-
context.prover_resource_files = new_value
|
|
2855
|
-
|
|
2856
|
-
# Copy the repro conf file to the sources as the overall way to reproduce this run easily.
|
|
2857
|
-
# The file is being copied in here and not added through collect_sources, because we want
|
|
2858
|
-
# to avoid building the whole tree up to the common path for this file. We simply want to copy
|
|
2859
|
-
# the repro file from .certora_internal to .certora_sources so it will be uploaded as a run
|
|
2860
|
-
# resource.
|
|
2861
|
-
try:
|
|
2862
|
-
shutil.copy(Util.get_last_conf_file(), Util.get_certora_sources_dir() / Util.LAST_CONF_FILE)
|
|
2863
|
-
except OSError as e:
|
|
2864
|
-
build_logger.debug("Couldn't copy repro conf to certora sources.", exc_info=e)
|
|
2865
|
-
raise
|
|
2866
|
-
|
|
2867
|
-
return cwd_rel_in_sources
|
|
2868
|
-
|
|
2869
2800
|
@staticmethod
|
|
2870
2801
|
def normalize_path(path: str) -> str:
|
|
2871
2802
|
"""
|
|
@@ -3462,6 +3393,85 @@ def sources_to_abs(sources: Set[Path]) -> Set[Path]:
|
|
|
3462
3393
|
return result
|
|
3463
3394
|
|
|
3464
3395
|
|
|
3396
|
+
def build_source_tree(sources: Set[Path], context: CertoraContext, overwrite: bool = False) -> Path:
|
|
3397
|
+
"""
|
|
3398
|
+
Copies files to .certora_sources
|
|
3399
|
+
@returns the cwd relative in sources
|
|
3400
|
+
"""
|
|
3401
|
+
sources = sources_to_abs(sources)
|
|
3402
|
+
cwd_rel_in_sources, common_path = CertoraBuildGenerator.get_cwd_rel_in_sources(sources)
|
|
3403
|
+
|
|
3404
|
+
for source_path in sources:
|
|
3405
|
+
is_dir = source_path.is_dir()
|
|
3406
|
+
# copy file to the path of the file from the common root under the sources directory
|
|
3407
|
+
|
|
3408
|
+
# make sure directory exists
|
|
3409
|
+
target_path = Util.get_certora_sources_dir() / source_path.relative_to(common_path)
|
|
3410
|
+
target_directory = target_path if is_dir else target_path.parent
|
|
3411
|
+
try:
|
|
3412
|
+
target_directory.mkdir(parents=True, exist_ok=True)
|
|
3413
|
+
except OSError as e:
|
|
3414
|
+
build_logger.debug(f"Failed to create directory {target_directory}", exc_info=e)
|
|
3415
|
+
raise
|
|
3416
|
+
|
|
3417
|
+
# copy files. if we got a directory, nothing to do
|
|
3418
|
+
if is_dir:
|
|
3419
|
+
build_logger.debug(f"Skipping directory {source_path}")
|
|
3420
|
+
continue
|
|
3421
|
+
|
|
3422
|
+
try:
|
|
3423
|
+
if overwrite:
|
|
3424
|
+
# expecting target path to exist.
|
|
3425
|
+
if target_path.exists():
|
|
3426
|
+
build_logger.debug(f"Overwriting {target_path} by copying from {source_path}")
|
|
3427
|
+
else:
|
|
3428
|
+
build_logger.warning(f"Supposed to overwrite {target_path} by copying from {source_path}" +
|
|
3429
|
+
" but it does not exist... this may indicate bad things happen")
|
|
3430
|
+
if overwrite or not target_path.exists():
|
|
3431
|
+
build_logger.debug(f"Copying {source_path} to {target_path}")
|
|
3432
|
+
shutil.copyfile(source_path, target_path)
|
|
3433
|
+
except OSError as e:
|
|
3434
|
+
build_logger.debug(f"Couldn't copy {source_path} to {target_path}", exc_info=e)
|
|
3435
|
+
raise
|
|
3436
|
+
|
|
3437
|
+
# the empty file .cwd is written in the source tree to denote the current working directory
|
|
3438
|
+
if cwd_rel_in_sources != '.':
|
|
3439
|
+
file_path = Util.get_certora_sources_dir() / cwd_rel_in_sources / Util.CWD_FILE
|
|
3440
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
3441
|
+
file_path.touch()
|
|
3442
|
+
|
|
3443
|
+
"""
|
|
3444
|
+
Once the resource files are copied to the source tree, the paths in the value of the 'prover_resource_files'
|
|
3445
|
+
attribute are replaced with the relative path of the resource file from the source tree root.
|
|
3446
|
+
This way The server can easily find the resource files in the source tree. The path from the source tree is the
|
|
3447
|
+
relative path of cwd from source tree root (most cases '.') concatenated with the relative path of the resource
|
|
3448
|
+
file from cwd
|
|
3449
|
+
"""
|
|
3450
|
+
if context.prover_resource_files:
|
|
3451
|
+
new_value = []
|
|
3452
|
+
len_orig = len(context.prover_resource_files)
|
|
3453
|
+
for value in context.prover_resource_files:
|
|
3454
|
+
label, file_path = value.split(':')
|
|
3455
|
+
rel_path = Path(os.path.relpath(file_path, '.'))
|
|
3456
|
+
new_value.append(':'.join([label, os.path.normpath(rel_path)]))
|
|
3457
|
+
if len_orig != len(new_value):
|
|
3458
|
+
raise RuntimeError(f"fail to process prover_resource_files {len_orig} out of {len(new_value)}")
|
|
3459
|
+
context.prover_resource_files = new_value
|
|
3460
|
+
|
|
3461
|
+
# Copy the repro conf file to the sources as the overall way to reproduce this run easily.
|
|
3462
|
+
# The file is being copied in here and not added through collect_sources, because we want
|
|
3463
|
+
# to avoid building the whole tree up to the common path for this file. We simply want to copy
|
|
3464
|
+
# the repro file from .certora_internal to .certora_sources so it will be uploaded as a run
|
|
3465
|
+
# resource.
|
|
3466
|
+
try:
|
|
3467
|
+
shutil.copy(Util.get_last_conf_file(), Util.get_certora_sources_dir() / Util.LAST_CONF_FILE)
|
|
3468
|
+
except OSError as e:
|
|
3469
|
+
build_logger.debug("Couldn't copy repro conf to certora sources.", exc_info=e)
|
|
3470
|
+
raise
|
|
3471
|
+
|
|
3472
|
+
return cwd_rel_in_sources
|
|
3473
|
+
|
|
3474
|
+
|
|
3465
3475
|
def build_from_scratch(certora_build_generator: CertoraBuildGenerator,
|
|
3466
3476
|
certora_verify_generator: CertoraVerifyGenerator,
|
|
3467
3477
|
build_cache_enabled: bool) -> CachedFiles:
|
|
@@ -3625,7 +3635,7 @@ def build(context: CertoraContext, ignore_spec_syntax_check: bool = False) -> No
|
|
|
3625
3635
|
cached_files.all_contract_files)
|
|
3626
3636
|
try:
|
|
3627
3637
|
# Copies files, not updating state
|
|
3628
|
-
|
|
3638
|
+
build_source_tree(sources, context)
|
|
3629
3639
|
except Exception as e:
|
|
3630
3640
|
build_logger.debug("build_source_tree failed", exc_info=e)
|
|
3631
3641
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import glob
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Set
|
|
5
|
+
|
|
6
|
+
from EVMVerifier.certoraBuild import build_source_tree
|
|
7
|
+
from EVMVerifier.certoraContextClass import CertoraContext
|
|
8
|
+
from EVMVerifier.certoraParseBuildScript import run_script_and_parse_json
|
|
9
|
+
from Shared import certoraUtils as Util
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def build_rust_app(context: CertoraContext) -> None:
|
|
13
|
+
if context.build_script:
|
|
14
|
+
run_script_and_parse_json(context)
|
|
15
|
+
if not context.rust_executables:
|
|
16
|
+
raise Util.CertoraUserInputError("failed to get target executable")
|
|
17
|
+
|
|
18
|
+
sources: Set[Path] = set()
|
|
19
|
+
root_directory = Path(context.rust_project_directory)
|
|
20
|
+
collect_files_from_rust_sources(context, sources, root_directory)
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
# Create generators
|
|
24
|
+
build_source_tree(sources, context)
|
|
25
|
+
|
|
26
|
+
copy_files_to_build_dir(context)
|
|
27
|
+
|
|
28
|
+
except Exception as e:
|
|
29
|
+
raise Util.CertoraUserInputError(f"Collecting build files failed with the exception: {e}")
|
|
30
|
+
else:
|
|
31
|
+
if not context.files:
|
|
32
|
+
raise Util.CertoraUserInputError("'files' or 'build_script' must be set for Rust projects")
|
|
33
|
+
if len(context.files) > 1:
|
|
34
|
+
raise Util.CertoraUserInputError("Rust projects must specify exactly one executable in 'files'.")
|
|
35
|
+
context.rust_executables = context.files[0]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def collect_files_from_rust_sources(context: CertoraContext, sources: Set[Path], root_directory: Path) -> None:
|
|
39
|
+
patterns = ["*.rs", "*.so", "*.wasm", "Cargo.toml", "Cargo.lock", "justfile"]
|
|
40
|
+
exclude_dirs = [".certora_internal"]
|
|
41
|
+
|
|
42
|
+
if not root_directory.is_dir():
|
|
43
|
+
raise ValueError(f"The given directory {root_directory} is not valid.")
|
|
44
|
+
|
|
45
|
+
for source in context.rust_sources:
|
|
46
|
+
for file in glob.glob(f'{root_directory.joinpath(source)}', recursive=True):
|
|
47
|
+
file_path = Path(file)
|
|
48
|
+
if any(excluded in file_path.parts for excluded in exclude_dirs):
|
|
49
|
+
continue
|
|
50
|
+
if file_path.is_file() and any(file_path.match(pattern) for pattern in patterns):
|
|
51
|
+
sources.add(file_path)
|
|
52
|
+
|
|
53
|
+
if Path(context.build_script).exists():
|
|
54
|
+
sources.add(Path(context.build_script).resolve())
|
|
55
|
+
if context.conf_file and Path(context.conf_file).exists():
|
|
56
|
+
sources.add(Path(context.conf_file).absolute())
|
|
57
|
+
|
|
58
|
+
additional_files = (getattr(context, 'solana_inlining', None) or []) + \
|
|
59
|
+
(getattr(context, 'solana_summaries', None) or [])
|
|
60
|
+
for file in additional_files:
|
|
61
|
+
sources.add(Path(file))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def copy_files_to_build_dir(context: CertoraContext) -> None:
|
|
65
|
+
rust_executable = Path(context.rust_project_directory) / context.rust_executables
|
|
66
|
+
shutil.copyfile(rust_executable, Util.get_build_dir() / rust_executable.name)
|
|
67
|
+
|
|
68
|
+
additional_files = (getattr(context, 'solana_inlining', None) or []) + \
|
|
69
|
+
(getattr(context, 'solana_summaries', None) or [])
|
|
70
|
+
|
|
71
|
+
for file in additional_files:
|
|
72
|
+
file_path = Path(file).resolve()
|
|
73
|
+
shutil.copy(file_path, Util.get_build_dir() / file_path.name)
|
|
74
|
+
|
|
75
|
+
if rust_logs := getattr(context, 'rust_logs_stdout', None):
|
|
76
|
+
shutil.copy(Path(rust_logs), Util.get_build_dir() / Path(rust_logs).name)
|
|
77
|
+
if rust_logs := getattr(context, 'rust_logs_stderr', None):
|
|
78
|
+
shutil.copy(Path(rust_logs), Util.get_build_dir() / Path(rust_logs).name)
|
|
@@ -190,25 +190,6 @@ def parse_json(response: Response) -> Dict[str, Any]:
|
|
|
190
190
|
return json_response
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
def compress_files_wasm(zip_file_path: Path, context: CertoraContext) -> bool:
|
|
194
|
-
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))
|
|
205
|
-
|
|
206
|
-
if zip_file_path.stat().st_size > MAX_FILE_SIZE:
|
|
207
|
-
cloud_logger.error(f"{GENERAL_ERR_PREFIX} Max 25MB file size exceeded.")
|
|
208
|
-
return False
|
|
209
|
-
return True
|
|
210
|
-
|
|
211
|
-
|
|
212
193
|
def compress_files(zip_file_path: Path, *resource_paths: Path, short_output: bool = False) -> bool:
|
|
213
194
|
with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zip_obj:
|
|
214
195
|
|
|
@@ -600,8 +581,12 @@ class CloudVerification:
|
|
|
600
581
|
# We need to strip "../" path component from all file paths because
|
|
601
582
|
# unzip will also do that.
|
|
602
583
|
solana_jar_settings = []
|
|
603
|
-
|
|
604
|
-
solana_jar_settings.append(
|
|
584
|
+
if hasattr(self.context, 'build_script') and self.context.build_script:
|
|
585
|
+
solana_jar_settings.append(Path(self.context.rust_executables).name)
|
|
586
|
+
|
|
587
|
+
else:
|
|
588
|
+
for file in self.context.files:
|
|
589
|
+
solana_jar_settings.append(file.split('../')[-1])
|
|
605
590
|
|
|
606
591
|
is_file = False
|
|
607
592
|
for arg in jar_settings:
|
|
@@ -616,18 +601,19 @@ class CloudVerification:
|
|
|
616
601
|
elif arg == '-solanaSummaries':
|
|
617
602
|
is_file = True
|
|
618
603
|
auth_data["jarSettings"] = solana_jar_settings
|
|
619
|
-
elif Attrs.
|
|
604
|
+
elif Attrs.is_soroban_app():
|
|
620
605
|
# We need to strip "../" path component from all file paths because
|
|
621
606
|
# unzip will also do that.
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
607
|
+
soroban_jar_settings = []
|
|
608
|
+
# not needed - should be in files
|
|
609
|
+
if hasattr(self.context, 'build_script') and self.context.build_script:
|
|
610
|
+
soroban_jar_settings.append(Path(self.context.rust_executables).name)
|
|
625
611
|
else:
|
|
626
612
|
for file in self.context.files:
|
|
627
|
-
|
|
613
|
+
soroban_jar_settings.append(file.split('../')[-1])
|
|
628
614
|
for arg in jar_settings:
|
|
629
|
-
|
|
630
|
-
auth_data["jarSettings"] =
|
|
615
|
+
soroban_jar_settings.append(arg)
|
|
616
|
+
auth_data["jarSettings"] = soroban_jar_settings
|
|
631
617
|
else:
|
|
632
618
|
auth_data["jarSettings"] = jar_settings
|
|
633
619
|
|
|
@@ -738,40 +724,81 @@ class CloudVerification:
|
|
|
738
724
|
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
739
725
|
elif Attrs.is_evm_app() and self.context.is_bytecode:
|
|
740
726
|
# We zip the bytecode jsons and the spec
|
|
741
|
-
paths = [
|
|
727
|
+
paths = [Util.get_certora_build_file(), Util.get_certora_verify_file(),
|
|
728
|
+
Util.get_certora_metadata_file()]
|
|
729
|
+
if Util.get_certora_sources_dir().exists():
|
|
730
|
+
paths.append(Util.get_certora_sources_dir())
|
|
731
|
+
|
|
742
732
|
for bytecode_json in self.context.bytecode_jsons:
|
|
743
733
|
paths.append(Path(bytecode_json))
|
|
744
734
|
paths.append(Path(self.context.bytecode_spec))
|
|
745
735
|
result = compress_files(self.ZipFilePath, *paths,
|
|
746
736
|
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
747
|
-
elif Attrs.
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
737
|
+
elif Attrs.is_rust_app():
|
|
738
|
+
files_list = [Util.get_certora_metadata_file()]
|
|
739
|
+
|
|
740
|
+
if hasattr(self.context, 'build_script') and self.context.build_script:
|
|
741
|
+
result = compress_files(self.logZipFilePath, Util.get_debug_log_file(),
|
|
742
|
+
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
743
|
+
|
|
744
|
+
if not result:
|
|
745
|
+
return False
|
|
746
|
+
files_list.append(self.logZipFilePath)
|
|
747
|
+
if Util.get_certora_sources_dir().exists():
|
|
748
|
+
files_list.append(Util.get_certora_sources_dir())
|
|
749
|
+
|
|
750
|
+
files_list.append(Util.get_build_dir() / Path(self.context.rust_executables).name)
|
|
751
|
+
|
|
752
|
+
# Create a .RustExecution file to classify zipInput as a rust source code
|
|
753
|
+
rust_execution_file = Util.get_build_dir() / ".RustExecution"
|
|
754
|
+
rust_execution_file.touch(exist_ok=True)
|
|
755
|
+
files_list.append(rust_execution_file)
|
|
756
|
+
|
|
757
|
+
additional_files = (getattr(self.context, 'solana_inlining', None) or []) + \
|
|
758
|
+
(getattr(self.context, 'solana_summaries', None) or [])
|
|
759
|
+
for file in additional_files:
|
|
760
|
+
files_list.append(Util.get_build_dir() / Path(file).name)
|
|
761
|
+
|
|
762
|
+
if attr_file := getattr(self.context, 'rust_logs_stdout', None):
|
|
763
|
+
files_list.append(Util.get_build_dir() / Path(attr_file).name)
|
|
764
|
+
if attr_file := getattr(self.context, 'rust_logs_stderr', None):
|
|
765
|
+
files_list.append(Util.get_build_dir() / Path(attr_file).name)
|
|
766
|
+
|
|
767
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
|
768
|
+
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
769
|
+
|
|
770
|
+
elif Attrs.is_solana_app():
|
|
771
|
+
# We zip the ELF files and the two configuration files
|
|
772
|
+
jar_args = Ctx.collect_jar_args(self.context)
|
|
758
773
|
|
|
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
774
|
for file in self.context.files:
|
|
772
|
-
|
|
773
|
-
|
|
775
|
+
files_list.append(Path(file))
|
|
776
|
+
is_file = False
|
|
777
|
+
for arg in jar_args:
|
|
778
|
+
if is_file:
|
|
779
|
+
files_list.append(Path(arg))
|
|
780
|
+
is_file = False
|
|
781
|
+
|
|
782
|
+
if arg == '-solanaInlining':
|
|
783
|
+
is_file = True
|
|
784
|
+
elif arg == '-solanaSummaries':
|
|
785
|
+
is_file = True
|
|
786
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
|
774
787
|
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
788
|
+
|
|
789
|
+
elif Attrs.is_soroban_app():
|
|
790
|
+
# We zip the wat file
|
|
791
|
+
for file in self.context.files:
|
|
792
|
+
files_list.append(Path(file))
|
|
793
|
+
result = compress_files(self.ZipFilePath, *files_list,
|
|
794
|
+
short_output=Ctx.is_minimal_cli_output(self.context))
|
|
795
|
+
else:
|
|
796
|
+
raise Util.CertoraUserInputError(
|
|
797
|
+
'When running rust application, context should either have attribute "rust_executables" '
|
|
798
|
+
'provided by build_script execution, '
|
|
799
|
+
'or either is_solana_app(), is_soroban_app() should be set to True'
|
|
800
|
+
)
|
|
801
|
+
|
|
775
802
|
else:
|
|
776
803
|
# Zip the log file first separately and again with the rest of the files, so it will not be decompressed
|
|
777
804
|
# 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
|
|
|
@@ -62,7 +61,7 @@ def read_from_conf_file(context: CertoraContext) -> None:
|
|
|
62
61
|
try:
|
|
63
62
|
check_conf_content(configuration, context)
|
|
64
63
|
except Util.CertoraUserInputError as e:
|
|
65
|
-
raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}", e) from None
|
|
64
|
+
raise Util.CertoraUserInputError(f"Error when reading {conf_file_path}: {str(e)}", e) from None
|
|
66
65
|
context.conf_file = str(conf_file_path)
|
|
67
66
|
|
|
68
67
|
|
|
@@ -86,6 +85,13 @@ 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. ")
|
|
89
|
-
|
|
88
|
+
|
|
89
|
+
if Attrs.is_evm_app() and 'files' not in conf:
|
|
90
90
|
raise Util.CertoraUserInputError("Mandatory 'files' attribute is missing from the configuration")
|
|
91
|
-
|
|
91
|
+
|
|
92
|
+
if Attrs.is_rust_app():
|
|
93
|
+
has_build_script = getattr(context, 'build_script', False)
|
|
94
|
+
if not has_build_script and 'files' not in conf:
|
|
95
|
+
raise Util.CertoraUserInputError("Mandatory 'build_script' or 'files' attribute is missing from the configuration")
|
|
96
|
+
|
|
97
|
+
context.files = conf.get('files')
|
|
@@ -1,24 +1,16 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import hashlib
|
|
3
3
|
import json
|
|
4
|
-
import logging
|
|
5
4
|
import os
|
|
6
5
|
import re
|
|
7
6
|
import sys
|
|
8
|
-
import
|
|
9
|
-
import platform
|
|
7
|
+
import logging
|
|
10
8
|
|
|
11
|
-
# tomllib was added to python 3.11 this import is for supporting older versions
|
|
12
|
-
if sys.version_info >= (3, 11):
|
|
13
|
-
import tomllib
|
|
14
|
-
else:
|
|
15
|
-
import tomli as tomllib
|
|
16
9
|
|
|
17
10
|
from pathlib import Path
|
|
18
11
|
from typing import Dict, List, Optional, Any
|
|
19
12
|
from rich.console import Console
|
|
20
13
|
|
|
21
|
-
|
|
22
14
|
scripts_dir_path = Path(__file__).parent.resolve() # containing directory
|
|
23
15
|
sys.path.insert(0, str(scripts_dir_path))
|
|
24
16
|
|
|
@@ -112,14 +104,15 @@ def get_local_run_cmd(context: CertoraContext) -> str:
|
|
|
112
104
|
@return: A command for running the prover locally
|
|
113
105
|
"""
|
|
114
106
|
run_args = []
|
|
115
|
-
if context.is_tac or Attrs.is_solana_app():
|
|
116
|
-
run_args.append(context.files[0])
|
|
117
107
|
|
|
118
|
-
if
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
if hasattr(context, 'rust_executables') and hasattr(context, 'rust_project_directory'):
|
|
109
|
+
run_args.append(os.path.join(context.rust_project_directory, context.rust_executables))
|
|
110
|
+
elif context.is_tac or Attrs.is_rust_app():
|
|
111
|
+
# For Rust app we assume the files holds the executable for the prover, currently we support a single file
|
|
112
|
+
try:
|
|
122
113
|
run_args.append(context.files[0])
|
|
114
|
+
except Exception:
|
|
115
|
+
raise RuntimeError("get_local_run_cmd: cannot find context.files[0]")
|
|
123
116
|
|
|
124
117
|
if Attrs.is_evm_app() and context.cache is not None:
|
|
125
118
|
run_args.extend(['-cache', context.cache])
|
|
@@ -158,7 +151,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
|
|
|
158
151
|
console = Console()
|
|
159
152
|
console.print("\n\nThe Certora Prover - A formal verification tool for smart contracts")
|
|
160
153
|
# Using sys.stdout.write() as print() would color some of the strings here
|
|
161
|
-
sys.stdout.write("\n\nUsage:
|
|
154
|
+
sys.stdout.write(f"\n\nUsage: {sys.argv[0]} <Files> <Flags>\n\n")
|
|
162
155
|
sys.stdout.write("Files are Solidity, Vyper contract files, a shared object Solana contract file, "
|
|
163
156
|
"or a conf file. Solidity contracts are denoted as file:contract "
|
|
164
157
|
"e.g. f.sol:A. If the contract name is identical to the file name, "
|
|
@@ -171,7 +164,7 @@ class ProverParser(AttrUtil.ContextAttributeParser):
|
|
|
171
164
|
console.print("3. list (L): gets multiple strings as a value, flags may also appear multiple times")
|
|
172
165
|
console.print("4. map (M): collection of key, value pairs\n\n")
|
|
173
166
|
|
|
174
|
-
Attrs.
|
|
167
|
+
Attrs.get_attribute_class().print_attr_help()
|
|
175
168
|
console.print("\n\nYou can find detailed documentation of the supported flags here: "
|
|
176
169
|
f"{Util.print_rich_link(CLI_DOCUMENTATION_URL)}\n\n")
|
|
177
170
|
|
|
@@ -242,7 +235,7 @@ def get_args(args_list: Optional[List[str]] = None) -> CertoraContext:
|
|
|
242
235
|
|
|
243
236
|
validator = Cv.CertoraContextValidator(context)
|
|
244
237
|
validator.validate()
|
|
245
|
-
if Attrs.is_evm_app():
|
|
238
|
+
if Attrs.is_evm_app() or Attrs.is_rust_app():
|
|
246
239
|
current_build_directory = Util.get_build_dir()
|
|
247
240
|
if context.build_dir is not None and current_build_directory != context.build_dir:
|
|
248
241
|
Util.reset_certora_internal_dir(context.build_dir)
|
|
@@ -526,53 +519,3 @@ def run_local_spec_check(with_typechecking: bool, context: CertoraContext) -> No
|
|
|
526
519
|
else:
|
|
527
520
|
raise Util.CertoraUserInputError("Cannot run local checks because of missing a suitable java installation. "
|
|
528
521
|
"To skip local checks run with the --disable_local_typechecking flag")
|
|
529
|
-
|
|
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
|
-
|
|
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}")
|