Merge branch 'develop' into versions-menu-overflow

This commit is contained in:
Paul Wackerow 2023-07-21 14:49:24 -07:00 committed by GitHub
commit 489d1a4428
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1341 additions and 141 deletions

View File

@ -691,7 +691,7 @@ defaults:
name: t_native_test_ext_prb_math
project: prb-math
binary_type: native
image: cimg/node:18.16
image: cimg/rust:1.70
- job_native_test_ext_elementfi: &job_native_test_ext_elementfi
<<: *requires_b_ubu_static
@ -1724,11 +1724,13 @@ workflows:
- t_native_test_ext_yield_liquidator
- t_native_test_ext_perpetual_pools
- t_native_test_ext_uniswap
- t_native_test_ext_prb_math
- t_native_test_ext_elementfi
- t_native_test_ext_brink
# NOTE: We are disabling gp2 tests due to constant failures.
#- t_native_test_ext_gp2
# TODO: Dropping prb-math from the benchmarks since it is not implemented yet
# in the new Foundry external testing infrastructure.
# - t_native_test_ext_prb_math
# NOTE: The external tests below were commented because they
# depend on a specific version of hardhat which does not support shanghai EVM.
#- t_native_test_ext_trident

View File

@ -7,6 +7,7 @@ Compiler Features:
Bugfixes:
* NatSpec: Fix internal error when requesting userdoc or devdoc for a contract that emits an event defined in a foreign contract or interface.
### 0.8.21 (2023-07-19)

View File

@ -3,7 +3,7 @@
### Requirements
- [ ] GitHub account with access to [solidity](https://github.com/ethereum/solidity), [solc-js](https://github.com/ethereum/solc-js),
[solc-bin](https://github.com/ethereum/solc-bin), [homebrew-ethereum](https://github.com/ethereum/homebrew-ethereum),
[solidity-blog](https://github.com/ethereum/solidity-blog) and [solidity-portal](https://github.com/ethereum/solidity-portal) repositories.
[solidity-website](https://github.com/ethereum/solidity-website).
- [ ] DockerHub account with push rights to the [``solc`` image](https://hub.docker.com/r/ethereum/solc).
- [ ] Launchpad (Ubuntu One) account with a membership in the ["Ethereum" team](https://launchpad.net/~ethereum) and
a gnupg key for your email in the ``ethereum.org`` domain (has to be version 1, gpg2 won't work).
@ -37,8 +37,8 @@ At least a day before the release:
- [ ] Prepare drafts of Twitter, Reddit and Solidity Forum announcements.
### Blog Post
- [ ] Create a post on [solidity-blog](https://github.com/ethereum/solidity-blog) in the ``Releases`` category and explain some of the new features or concepts.
- [ ] Create a post on [solidity-blog](https://github.com/ethereum/solidity-blog) in the ``Security Alerts`` category in case of important bug(s).
- [ ] Create a post on [solidity-website](https://github.com/ethereum/solidity-website/tree/main/src/posts) in the ``Releases`` category and explain some of the new features or concepts.
- [ ] Create a post on [solidity-website](https://github.com/ethereum/solidity-website/tree/main/src/posts) in the ``Security Alerts`` category in case of important bug(s).
### Changelog
- [ ] Sort the changelog entries alphabetically and correct any errors you notice. Commit it.
@ -104,9 +104,9 @@ At least a day before the release:
- [ ] Make sure the documentation for the new release has been published successfully.
Go to the [documentation status page at ReadTheDocs](https://readthedocs.org/projects/solidity/) and verify that the new version is listed, works and is marked as default.
- [ ] Remove "still in progress" warning from the [release notes](https://github.com/ethereum/solidity/releases).
- [ ] Merge the [blog posts](https://github.com/ethereum/solidity-blog/pulls) related to the release.
- [ ] Merge the [blog posts](https://github.com/ethereum/solidity-website/pulls) related to the release.
- [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry.
- [ ] Update the release information section [in the source of soliditylang.org](https://github.com/ethereum/solidity-portal/blob/master/index.html).
- [ ] Update the release information section [in the source of soliditylang.org](https://github.com/ethereum/solidity-website/blob/main/src/pages/index.tsx).
- [ ] Announce on [Twitter](https://twitter.com/solidity_lang), including links to the release and the blog post.
- [ ] Announce on [Fosstodon](https://fosstodon.org/@solidity/), including links to the release and the blog post.
- [ ] Share the announcement on Reddit in [``/r/ethdev``](https://reddit.com/r/ethdev/), cross-posted to [``/r/ethereum``](https://reddit.com/r/ethereum/).

View File

@ -80,14 +80,6 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
for (auto const& event: uniqueInterfaceEvents(_contractDef))
{
ContractDefinition const* eventOrigin = event->annotation().contract;
solAssert(eventOrigin);
solAssert(
*eventOrigin == _contractDef ||
(!eventOrigin->isLibrary() && _contractDef.derivesFrom(*eventOrigin)) ||
(eventOrigin->isLibrary() && !_contractDef.derivesFrom(*eventOrigin))
);
string value = extractDoc(event->annotation().docTags, "notice");
if (!value.empty())
doc["events"][event->functionType(true)->externalSignature()]["notice"] = value;
@ -178,16 +170,7 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
for (auto const& event: uniqueInterfaceEvents(_contractDef))
if (auto devDoc = devDocumentation(event->annotation().docTags); !devDoc.empty())
{
ContractDefinition const* eventOrigin = event->annotation().contract;
solAssert(eventOrigin);
solAssert(
*eventOrigin == _contractDef ||
(!eventOrigin->isLibrary() && _contractDef.derivesFrom(*eventOrigin)) ||
(eventOrigin->isLibrary() && !_contractDef.derivesFrom(*eventOrigin))
);
doc["events"][event->functionType(true)->externalSignature()] = devDoc;
}
for (auto const& error: _contractDef.interfaceErrors())
if (auto devDoc = devDocumentation(error->annotation().docTags); !devDoc.empty())
doc["errors"][error->functionType(true)->externalSignature()].append(devDoc);

View File

@ -0,0 +1,172 @@
#!/usr/bin/env python3
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2023 solidity contributors.
# ------------------------------------------------------------------------------
import os
import subprocess
from abc import ABCMeta
from abc import abstractmethod
from dataclasses import dataclass
from dataclasses import field
from pathlib import Path
from shutil import rmtree
from tempfile import mkdtemp
from textwrap import dedent
from typing import List
from typing import Set
from test_helpers import download_project
from test_helpers import get_solc_short_version
from test_helpers import parse_command_line
from test_helpers import parse_custom_presets
from test_helpers import parse_solc_version
from test_helpers import replace_version_pragmas
from test_helpers import settings_from_preset
from test_helpers import SettingsPreset
CURRENT_EVM_VERSION: str = "shanghai"
@dataclass
class TestConfig:
name: str
repo_url: str
ref_type: str
ref: str
compile_only_presets: List[SettingsPreset] = field(default_factory=list)
settings_presets: List[SettingsPreset] = field(default_factory=lambda: list(SettingsPreset))
evm_version: str = field(default=CURRENT_EVM_VERSION)
def selected_presets(self) -> Set[SettingsPreset]:
return set(self.compile_only_presets + self.settings_presets)
class BaseRunner(metaclass=ABCMeta):
config: TestConfig
solc_binary_type: str
solc_binary_path: Path
presets: Set[SettingsPreset]
def __init__(self, argv, config: TestConfig):
args = parse_command_line(f"{config.name} external tests", argv)
self.config = config
self.solc_binary_type = args.solc_binary_type
self.solc_binary_path = args.solc_binary_path
self.presets = parse_custom_presets(args.selected_presets) if args.selected_presets else config.selected_presets()
self.env = os.environ.copy()
self.tmp_dir = mkdtemp(prefix=f"ext-test-{config.name}-")
self.test_dir = Path(self.tmp_dir) / "ext"
def setup_solc(self) -> str:
if self.solc_binary_type == "solcjs":
# TODO: add support to solc-js
raise NotImplementedError()
print("Setting up solc...")
solc_version_output = subprocess.check_output(
[self.solc_binary_path, "--version"],
shell=False,
encoding="utf-8"
).split(":")[1]
return parse_solc_version(solc_version_output)
@staticmethod
def enter_test_dir(fn):
"""Run a function inside the test directory"""
previous_dir = os.getcwd()
def f(self, *args, **kwargs):
try:
assert self.test_dir is not None
os.chdir(self.test_dir)
return fn(self, *args, **kwargs)
finally:
# Restore the previous directory after execute fn
os.chdir(previous_dir)
return f
def setup_environment(self):
"""Configure the project build environment"""
print("Configuring Runner building environment...")
replace_version_pragmas(self.test_dir)
@enter_test_dir
def clean(self):
"""Clean temporary directories"""
rmtree(self.tmp_dir)
@enter_test_dir
@abstractmethod
def configure(self):
raise NotImplementedError()
@enter_test_dir
@abstractmethod
def compile(self, preset: SettingsPreset):
raise NotImplementedError()
@enter_test_dir
@abstractmethod
def run_test(self):
raise NotImplementedError()
def run_test(runner: BaseRunner):
print(f"Testing {runner.config.name}...\n===========================")
print(f"Selected settings presets: {' '.join(p.value for p in runner.presets)}")
# Configure solc compiler
solc_version = runner.setup_solc()
print(f"Using compiler version {solc_version}")
# Download project
download_project(runner.test_dir, runner.config.repo_url, runner.config.ref_type, runner.config.ref)
# Configure run environment
runner.setup_environment()
# Configure TestRunner instance
print(dedent(f"""\
Configuring runner's profiles with:
-------------------------------------
Binary type: {runner.solc_binary_type}
Compiler path: {runner.solc_binary_path}
-------------------------------------
"""))
runner.configure()
for preset in runner.presets:
print("Running compile function...")
settings = settings_from_preset(preset, runner.config.evm_version)
print(dedent(f"""\
-------------------------------------
Settings preset: {preset.value}
Settings: {settings}
EVM version: {runner.config.evm_version}
Compiler version: {get_solc_short_version(solc_version)}
Compiler version (full): {solc_version}
-------------------------------------
"""))
runner.compile(preset)
# TODO: COMPILE_ONLY should be a command-line option
if os.environ.get("COMPILE_ONLY") == "1" or preset in runner.config.compile_only_presets:
print("Skipping test function...")
else:
print("Running test function...")
runner.run_test()
# TODO: store_benchmark_report
runner.clean()
print("Done.")

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python3
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2023 solidity contributors.
# ------------------------------------------------------------------------------
import os
import re
import subprocess
from shutil import which
from textwrap import dedent
from typing import Optional
from runners.base import BaseRunner
from test_helpers import SettingsPreset
from test_helpers import settings_from_preset
def run_forge_command(command: str, env: Optional[dict] = None):
subprocess.run(
command.split(),
env=env if env is not None else os.environ.copy(),
check=True
)
class FoundryRunner(BaseRunner):
"""Configure and run Foundry-based projects"""
FOUNDRY_CONFIG_FILE = "foundry.toml"
def setup_environment(self):
super().setup_environment()
if which("forge") is None:
raise RuntimeError("Forge not found.")
@staticmethod
def profile_name(preset: SettingsPreset):
"""Returns foundry profile name"""
# Replace - or + by underscore to avoid invalid toml syntax
return re.sub(r"(\-|\+)+", "_", preset.value)
@staticmethod
def profile_section(profile_fields: dict) -> str:
return dedent("""\
[profile.{name}]
gas_reports = ["*"]
auto_detect_solc = false
solc = "{solc}"
evm_version = "{evm_version}"
optimizer = {optimizer}
via_ir = {via_ir}
[profile.{name}.optimizer_details]
yul = {yul}
""").format(**profile_fields)
@BaseRunner.enter_test_dir
def configure(self):
"""Configure forge tests profiles"""
profiles = []
for preset in self.presets:
settings = settings_from_preset(preset, self.config.evm_version)
profiles.append(self.profile_section({
"name": self.profile_name(preset),
"solc": self.solc_binary_path,
"evm_version": self.config.evm_version,
"optimizer": str(settings["optimizer"]["enabled"]).lower(),
"via_ir": str(settings["viaIR"]).lower(),
"yul": str(settings["optimizer"]["details"]["yul"]).lower(),
}))
with open(
file=self.test_dir / self.FOUNDRY_CONFIG_FILE,
mode="a",
encoding="utf-8",
) as f:
for profile in profiles:
f.write(profile)
run_forge_command("forge install", self.env)
@BaseRunner.enter_test_dir
def compile(self, preset: SettingsPreset):
"""Compile project"""
# Set the Foundry profile environment variable
self.env.update({"FOUNDRY_PROFILE": self.profile_name(preset)})
run_forge_command("forge build", self.env)
@BaseRunner.enter_test_dir
def run_test(self):
"""Run project tests"""
run_forge_command("forge test --gas-report", self.env)

View File

@ -0,0 +1,151 @@
#!/usr/bin/env python3
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2023 solidity contributors.
# ------------------------------------------------------------------------------
import os
import re
import subprocess
import sys
from argparse import ArgumentParser
from enum import Enum
from pathlib import Path
from typing import List
from typing import Set
# Our scripts/ is not a proper Python package so we need to modify PYTHONPATH to import from it
# pragma pylint: disable=import-error,wrong-import-position
PROJECT_ROOT = Path(__file__).parents[2]
sys.path.insert(0, f"{PROJECT_ROOT}/scripts/common")
from git_helpers import git_commit_hash
SOLC_FULL_VERSION_REGEX = re.compile(r"^[a-zA-Z: ]*(.*)$")
SOLC_SHORT_VERSION_REGEX = re.compile(r"^([0-9.]+).*\+|\-$")
class SettingsPreset(Enum):
LEGACY_NO_OPTIMIZE = 'legacy-no-optimize'
IR_NO_OPTIMIZE = 'ir-no-optimize'
LEGACY_OPTIMIZE_EVM_ONLY = 'legacy-optimize-evm-only'
IR_OPTIMIZE_EVM_ONLY = 'ir-optimize-evm-only'
LEGACY_OPTIMIZE_EVM_YUL = 'legacy-optimize-evm+yul'
IR_OPTIMIZE_EVM_YUL = 'ir-optimize-evm+yul'
def compiler_settings(evm_version: str, via_ir: bool = False, optimizer: bool = False, yul: bool = False) -> dict:
return {
"optimizer": {"enabled": optimizer, "details": {"yul": yul}},
"evmVersion": evm_version,
"viaIR": via_ir,
}
def settings_from_preset(preset: SettingsPreset, evm_version: str) -> dict:
return {
SettingsPreset.LEGACY_NO_OPTIMIZE: compiler_settings(evm_version),
SettingsPreset.IR_NO_OPTIMIZE: compiler_settings(evm_version, via_ir=True),
SettingsPreset.LEGACY_OPTIMIZE_EVM_ONLY: compiler_settings(evm_version, optimizer=True),
SettingsPreset.IR_OPTIMIZE_EVM_ONLY: compiler_settings(evm_version, via_ir=True, optimizer=True),
SettingsPreset.LEGACY_OPTIMIZE_EVM_YUL: compiler_settings(evm_version, optimizer=True, yul=True),
SettingsPreset.IR_OPTIMIZE_EVM_YUL: compiler_settings(evm_version, via_ir=True, optimizer=True, yul=True),
}[preset]
def parse_custom_presets(presets: List[str]) -> Set[SettingsPreset]:
return {SettingsPreset(p) for p in presets}
def parse_command_line(description: str, args: List[str]):
arg_parser = ArgumentParser(description)
arg_parser.add_argument(
"solc_binary_type",
metavar="solc-binary-type",
type=str,
default="native",
choices=["native", "solcjs"],
help="""Solidity compiler binary type""",
)
arg_parser.add_argument(
"solc_binary_path",
metavar="solc-binary-path",
type=Path,
help="""Path to solc binary""",
)
arg_parser.add_argument(
"selected_presets",
metavar="selected-presets",
help="""List of compiler settings presets""",
nargs='*',
)
return arg_parser.parse_args(args)
def download_project(test_dir: Path, repo_url: str, ref_type: str = "branch", ref: str = "master"):
assert ref_type in ("commit", "branch", "tag")
print(f"Cloning {ref_type} {ref} of {repo_url}...")
if ref_type == "commit":
os.mkdir(test_dir)
os.chdir(test_dir)
subprocess.run(["git", "init"], check=True)
subprocess.run(["git", "remote", "add", "origin", repo_url], check=True)
subprocess.run(["git", "fetch", "--depth", "1", "origin", ref], check=True)
subprocess.run(["git", "reset", "--hard", "FETCH_HEAD"], check=True)
else:
os.chdir(test_dir.parent)
subprocess.run(["git", "clone", "--no-progress", "--depth", "1", repo_url, "-b", ref, test_dir.resolve()], check=True)
if not test_dir.exists():
raise RuntimeError("Failed to clone the project.")
os.chdir(test_dir)
if (test_dir / ".gitmodules").exists():
subprocess.run(["git", "submodule", "update", "--init"], check=True)
print(f"Current commit hash: {git_commit_hash()}")
def parse_solc_version(solc_version_string: str) -> str:
solc_version_match = re.search(SOLC_FULL_VERSION_REGEX, solc_version_string)
if solc_version_match is None:
raise RuntimeError(f"Solc version could not be found in: {solc_version_string}.")
return solc_version_match.group(1)
def get_solc_short_version(solc_full_version: str) -> str:
solc_short_version_match = re.search(SOLC_SHORT_VERSION_REGEX, solc_full_version)
if solc_short_version_match is None:
raise RuntimeError(f"Error extracting short version string from: {solc_full_version}.")
return solc_short_version_match.group(1)
def store_benchmark_report(self):
raise NotImplementedError()
def replace_version_pragmas(test_dir: Path):
"""
Replace fixed-version pragmas (part of Consensys best practice).
Include all directories to also cover node dependencies.
"""
print("Replacing fixed-version pragmas...")
for source in test_dir.glob("**/*.sol"):
content = source.read_text(encoding="utf-8")
content = re.sub(r"pragma solidity [^;]+;", r"pragma solidity >=0.0;", content)
with open(source, "w", encoding="utf-8") as f:
f.write(content)

View File

@ -63,3 +63,7 @@ expected-line-ending-format=LF
# Maximum number of characters on a single line.
max-line-length=130
[MISCELLANEOUS]
notes=XXX,TMP

53
test/externalTests/prb-math.py Executable file
View File

@ -0,0 +1,53 @@
#!/usr/bin/env python3
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2023 solidity contributors.
# ------------------------------------------------------------------------------
import sys
from pathlib import Path
# Our scripts/ is not a proper Python package so we need to modify PYTHONPATH to import from it
# pragma pylint: disable=import-error,wrong-import-position
PROJECT_ROOT = Path(__file__).parents[2]
sys.path.insert(0, f"{PROJECT_ROOT}/scripts/externalTests")
from runners.base import run_test
from runners.base import TestConfig
from runners.foundry import FoundryRunner
from test_helpers import SettingsPreset
test_config = TestConfig(
name="PRBMath",
repo_url="https://github.com/PaulRBerg/prb-math.git",
ref_type="branch",
ref="main",
compile_only_presets=[
# pylint: disable=line-too-long
# SettingsPreset.IR_NO_OPTIMIZE, # Error: Yul exception:Variable expr_15699_address is 2 slot(s) too deep inside the stack. Stack too deep.
# SettingsPreset.IR_OPTIMIZE_EVM_ONLY, # Error: Yul exception:Variable expr_15699_address is 2 slot(s) too deep inside the stack. Stack too deep.
],
settings_presets=[
SettingsPreset.LEGACY_NO_OPTIMIZE,
SettingsPreset.LEGACY_OPTIMIZE_EVM_ONLY,
SettingsPreset.LEGACY_OPTIMIZE_EVM_YUL,
SettingsPreset.IR_OPTIMIZE_EVM_YUL,
],
)
sys.exit(run_test(FoundryRunner(argv=sys.argv[1:], config=test_config)))

View File

@ -1,116 +0,0 @@
#!/usr/bin/env bash
# ------------------------------------------------------------------------------
# This file is part of solidity.
#
# solidity is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# solidity is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with solidity. If not, see <http://www.gnu.org/licenses/>
#
# (c) 2022 solidity contributors.
#------------------------------------------------------------------------------
set -e
source scripts/common.sh
source scripts/externalTests/common.sh
REPO_ROOT=$(realpath "$(dirname "$0")/../..")
verify_input "$@"
BINARY_TYPE="$1"
BINARY_PATH="$(realpath "$2")"
SELECTED_PRESETS="$3"
function compile_fn { yarn compile; }
# NOTE: `yarn test` runs `mocha` which seems to disable the gas reporter.
function test_fn { npx --no hardhat --no-compile test; }
function prb_math_test
{
local repo="https://github.com/paulrberg/prb-math"
local ref_type=branch
# We currently pin the prb-math version to the latest version that support hardhat
# Please see here for details: https://github.com/ethereum/solidity/issues/13767
local ref=v2.5.0
local config_file="hardhat.config.ts"
local config_var="config"
local compile_only_presets=(
ir-no-optimize # Tests fail with "Error: Transaction reverted: trying to deploy a contract whose code is too large"
)
local settings_presets=(
"${compile_only_presets[@]}"
ir-optimize-evm-only
ir-optimize-evm+yul
legacy-optimize-evm-only
legacy-optimize-evm+yul
legacy-no-optimize
)
[[ $SELECTED_PRESETS != "" ]] || SELECTED_PRESETS=$(circleci_select_steps_multiarg "${settings_presets[@]}")
print_presets_or_exit "$SELECTED_PRESETS"
setup_solc "$DIR" "$BINARY_TYPE" "$BINARY_PATH"
download_project "$repo" "$ref_type" "$ref" "$DIR"
cp .env.example .env
# The project has yarn 3.1.0 binary stored in the repo and yarnrc forces the yarn 1.x binary
# installed system-wide to use it. Unfortunately Yarn 3 fails in weird ways when we remove
# yarn.lock. Remove the config to restore Yarn 1.x.
rm .yarnrc.yml
# Disable tests that won't pass on the ir presets due to Hardhat heuristics. Note that this also disables
# them for other presets but that's fine - we want same code run for benchmarks to be comparable.
# TODO: Remove this when Hardhat adjusts heuristics for IR (https://github.com/nomiclabs/hardhat/issues/3365).
pushd test/contracts/prbMathUd60x18/pure/
sed -i 's|context(\("when the sum overflows"\)|context.skip(\1|g' add.test.ts
sed -i 's|context(\("when the sum does not overflow"\)|context.skip(\1|g' add.test.ts
sed -i 's|context(\("when both operands are zero"\)|context.skip(\1|g' avg.test.ts
sed -i 's|context(\("when one operand is zero and the other is not zero"\)|context.skip(\1|g' avg.test.ts
sed -i 's|context(\("when the denominator is zero"\)|context.skip(\1|g' div.test.ts
sed -i 's|context(\("when x is zero"\)|context.skip(\1|g' inv.test.ts
popd
pushd test/contracts/prbMathSd59x18/pure/
sed -i 's|context(\("when the sum overflows"\)|context.skip(\1|g' add.test.ts
sed -i 's|context(\("when the sum underflows"\)|context.skip(\1|g' add.test.ts
sed -i 's|context(\("when the denominator is zero"\)|context.skip(\1|g' div.test.ts
sed -i 's|context(\("when x is zero"\)|context.skip(\1|g' inv.test.ts
sed -i 's|context(\("when the difference underflows"\)|context.skip(\1|g' sub.test.ts
sed -i 's|context(\("when the difference overflows"\)|context.skip(\1|g' sub.test.ts
popd
neutralize_package_lock
neutralize_package_json_hooks
force_hardhat_compiler_binary "$config_file" "$BINARY_TYPE" "$BINARY_PATH"
force_hardhat_compiler_settings "$config_file" "$(first_word "$SELECTED_PRESETS")" "$config_var"
yarn install --no-lock-file
yarn add hardhat-gas-reporter
# Workaround for error caused by the last release of hardhat-waffle@2.0.6 that bumps ethereum-waffle
# to version 4.0.10 and breaks prb-math build with the following error:
#
# Cannot find module 'ethereum-waffle/dist/cjs/src/deployContract'
#
# See: https://github.com/NomicFoundation/hardhat-waffle/commit/83ee9cb36ee59d0bedacbbd00043f030af104ad0
yarn add '@nomiclabs/hardhat-waffle@2.0.5'
replace_version_pragmas
for preset in $SELECTED_PRESETS; do
hardhat_run_test "$config_file" "$preset" "${compile_only_presets[*]}" compile_fn test_fn "$config_var"
store_benchmark_report hardhat prb-math "$repo" "$preset"
done
}
external_test PRBMath prb_math_test

View File

@ -36,7 +36,7 @@ def detect_external_tests() -> dict:
return {
file_path.stem: file_path
for file_path in Path(EXTERNAL_TESTS_DIR).iterdir()
if file_path.is_file() and file_path.suffix == ".sh"
if file_path.is_file() and file_path.suffix in (".sh", ".py")
}

View File

@ -0,0 +1,112 @@
interface I {
event Event(uint256 value);
}
contract C {
event Event(address indexed sender);
}
contract D {
event Event(address indexed sender);
function test(address sender) public {
emit I.Event(1);
emit C.Event(msg.sender);
emit Event(msg.sender);
}
}
// ----
// :C
// [
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "address",
// "name": "sender",
// "type": "address"
// }
// ],
// "name": "Event",
// "type": "event"
// }
// ]
//
//
// :D
// [
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": false,
// "internalType": "uint256",
// "name": "value",
// "type": "uint256"
// }
// ],
// "name": "Event",
// "type": "event"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "address",
// "name": "sender",
// "type": "address"
// }
// ],
// "name": "Event",
// "type": "event"
// },
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": true,
// "internalType": "address",
// "name": "sender",
// "type": "address"
// }
// ],
// "name": "Event",
// "type": "event"
// },
// {
// "inputs":
// [
// {
// "internalType": "address",
// "name": "sender",
// "type": "address"
// }
// ],
// "name": "test",
// "outputs": [],
// "stateMutability": "nonpayable",
// "type": "function"
// }
// ]
//
//
// :I
// [
// {
// "anonymous": false,
// "inputs":
// [
// {
// "indexed": false,
// "internalType": "uint256",
// "name": "value",
// "type": "uint256"
// }
// ],
// "name": "Event",
// "type": "event"
// }
// ]

View File

@ -0,0 +1,289 @@
{
"absolutePath": "a",
"exportedSymbols":
{
"C":
[
5
],
"D":
[
19
]
},
"id": 20,
"nodeType": "SourceUnit",
"nodes":
[
{
"abstract": false,
"baseContracts": [],
"canonicalName": "C",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 5,
"linearizedBaseContracts":
[
5
],
"name": "C",
"nameLocation": "9:1:1",
"nodeType": "ContractDefinition",
"nodes":
[
{
"anonymous": false,
"eventSelector": "bd1155618a34fd53d1a2de9f705f42f3582842cba0b985b25c59888d86e0c929",
"id": 4,
"name": "E",
"nameLocation": "23:1:1",
"nodeType": "EventDefinition",
"parameters":
{
"id": 3,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 2,
"indexed": true,
"mutability": "mutable",
"name": "sender",
"nameLocation": "41:6:1",
"nodeType": "VariableDeclaration",
"scope": 4,
"src": "25:22:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_address",
"typeString": "address"
},
"typeName":
{
"id": 1,
"name": "address",
"nodeType": "ElementaryTypeName",
"src": "25:7:1",
"stateMutability": "nonpayable",
"typeDescriptions":
{
"typeIdentifier": "t_address",
"typeString": "address"
}
},
"visibility": "internal"
}
],
"src": "24:24:1"
},
"src": "17:32:1"
}
],
"scope": 20,
"src": "0:51:1",
"usedErrors": [],
"usedEvents":
[
4
]
},
{
"abstract": false,
"baseContracts": [],
"canonicalName": "D",
"contractDependencies": [],
"contractKind": "contract",
"fullyImplemented": true,
"id": 19,
"linearizedBaseContracts":
[
19
],
"name": "D",
"nameLocation": "61:1:1",
"nodeType": "ContractDefinition",
"nodes":
[
{
"body":
{
"id": 17,
"nodeType": "Block",
"src": "106:37:1",
"statements":
[
{
"eventCall":
{
"arguments":
[
{
"expression":
{
"id": 13,
"name": "msg",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": -15,
"src": "125:3:1",
"typeDescriptions":
{
"typeIdentifier": "t_magic_message",
"typeString": "msg"
}
},
"id": 14,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"memberLocation": "129:6:1",
"memberName": "sender",
"nodeType": "MemberAccess",
"src": "125:10:1",
"typeDescriptions":
{
"typeIdentifier": "t_address",
"typeString": "address"
}
}
],
"expression":
{
"argumentTypes":
[
{
"typeIdentifier": "t_address",
"typeString": "address"
}
],
"expression":
{
"id": 10,
"name": "C",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"referencedDeclaration": 5,
"src": "121:1:1",
"typeDescriptions":
{
"typeIdentifier": "t_type$_t_contract$_C_$5_$",
"typeString": "type(contract C)"
}
},
"id": 12,
"isConstant": false,
"isLValue": false,
"isPure": false,
"lValueRequested": false,
"memberLocation": "123:1:1",
"memberName": "E",
"nodeType": "MemberAccess",
"referencedDeclaration": 4,
"src": "121:3:1",
"typeDescriptions":
{
"typeIdentifier": "t_function_event_nonpayable$_t_address_$returns$__$",
"typeString": "function (address)"
}
},
"id": 15,
"isConstant": false,
"isLValue": false,
"isPure": false,
"kind": "functionCall",
"lValueRequested": false,
"nameLocations": [],
"names": [],
"nodeType": "FunctionCall",
"src": "121:15:1",
"tryCall": false,
"typeDescriptions":
{
"typeIdentifier": "t_tuple$__$",
"typeString": "tuple()"
}
},
"id": 16,
"nodeType": "EmitStatement",
"src": "116:20:1"
}
]
},
"functionSelector": "bb29998e",
"id": 18,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "test",
"nameLocation": "78:4:1",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 8,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 7,
"mutability": "mutable",
"name": "sender",
"nameLocation": "91:6:1",
"nodeType": "VariableDeclaration",
"scope": 18,
"src": "83:14:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions":
{
"typeIdentifier": "t_address",
"typeString": "address"
},
"typeName":
{
"id": 6,
"name": "address",
"nodeType": "ElementaryTypeName",
"src": "83:7:1",
"stateMutability": "nonpayable",
"typeDescriptions":
{
"typeIdentifier": "t_address",
"typeString": "address"
}
},
"visibility": "internal"
}
],
"src": "82:16:1"
},
"returnParameters":
{
"id": 9,
"nodeType": "ParameterList",
"parameters": [],
"src": "106:0:1"
},
"scope": 19,
"src": "69:74:1",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
}
],
"scope": 20,
"src": "52:93:1",
"usedErrors": [],
"usedEvents":
[
4
]
}
],
"src": "0:146:1"
}

View File

@ -0,0 +1,10 @@
contract C {
event E(address indexed sender);
}
contract D {
function test(address sender) public {
emit C.E(msg.sender);
}
}
// ----

View File

@ -0,0 +1,194 @@
{
"absolutePath": "a",
"id": 20,
"nodeType": "SourceUnit",
"nodes":
[
{
"abstract": false,
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"id": 5,
"name": "C",
"nameLocation": "9:1:1",
"nodeType": "ContractDefinition",
"nodes":
[
{
"anonymous": false,
"id": 4,
"name": "E",
"nameLocation": "23:1:1",
"nodeType": "EventDefinition",
"parameters":
{
"id": 3,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 2,
"indexed": true,
"mutability": "mutable",
"name": "sender",
"nameLocation": "41:6:1",
"nodeType": "VariableDeclaration",
"src": "25:22:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {},
"typeName":
{
"id": 1,
"name": "address",
"nodeType": "ElementaryTypeName",
"src": "25:7:1",
"stateMutability": "nonpayable",
"typeDescriptions": {}
},
"visibility": "internal"
}
],
"src": "24:24:1"
},
"src": "17:32:1"
}
],
"src": "0:51:1",
"usedErrors": [],
"usedEvents": []
},
{
"abstract": false,
"baseContracts": [],
"contractDependencies": [],
"contractKind": "contract",
"id": 19,
"name": "D",
"nameLocation": "61:1:1",
"nodeType": "ContractDefinition",
"nodes":
[
{
"body":
{
"id": 17,
"nodeType": "Block",
"src": "106:37:1",
"statements":
[
{
"eventCall":
{
"arguments":
[
{
"expression":
{
"id": 13,
"name": "msg",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"src": "125:3:1",
"typeDescriptions": {}
},
"id": 14,
"memberLocation": "129:6:1",
"memberName": "sender",
"nodeType": "MemberAccess",
"src": "125:10:1",
"typeDescriptions": {}
}
],
"expression":
{
"expression":
{
"id": 10,
"name": "C",
"nodeType": "Identifier",
"overloadedDeclarations": [],
"src": "121:1:1",
"typeDescriptions": {}
},
"id": 12,
"memberLocation": "123:1:1",
"memberName": "E",
"nodeType": "MemberAccess",
"src": "121:3:1",
"typeDescriptions": {}
},
"id": 15,
"nameLocations": [],
"names": [],
"nodeType": "FunctionCall",
"src": "121:15:1",
"tryCall": false,
"typeDescriptions": {}
},
"id": 16,
"nodeType": "EmitStatement",
"src": "116:20:1"
}
]
},
"id": 18,
"implemented": true,
"kind": "function",
"modifiers": [],
"name": "test",
"nameLocation": "78:4:1",
"nodeType": "FunctionDefinition",
"parameters":
{
"id": 8,
"nodeType": "ParameterList",
"parameters":
[
{
"constant": false,
"id": 7,
"mutability": "mutable",
"name": "sender",
"nameLocation": "91:6:1",
"nodeType": "VariableDeclaration",
"src": "83:14:1",
"stateVariable": false,
"storageLocation": "default",
"typeDescriptions": {},
"typeName":
{
"id": 6,
"name": "address",
"nodeType": "ElementaryTypeName",
"src": "83:7:1",
"stateMutability": "nonpayable",
"typeDescriptions": {}
},
"visibility": "internal"
}
],
"src": "82:16:1"
},
"returnParameters":
{
"id": 9,
"nodeType": "ParameterList",
"parameters": [],
"src": "106:0:1"
},
"src": "69:74:1",
"stateMutability": "nonpayable",
"virtual": false,
"visibility": "public"
}
],
"src": "52:93:1",
"usedErrors": [],
"usedEvents": []
}
],
"src": "0:146:1"
}

View File

@ -485,6 +485,179 @@ BOOST_AUTO_TEST_CASE(event)
checkNatspec(sourceCode, "ERC20", userDoc, true);
}
BOOST_AUTO_TEST_CASE(emit_event_from_foreign_contract)
{
char const* sourceCode = R"(
contract X {
/// @notice Userdoc for event E.
/// @dev Devdoc for event E.
event E();
}
contract C {
function g() public {
emit X.E();
}
}
)";
char const* devDoc = R"ABCDEF(
{
"events":
{
"E()":
{
"details": "Devdoc for event E."
}
},
"kind": "dev",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "C", devDoc, false);
char const* userDoc = R"ABCDEF(
{
"events":
{
"E()":
{
"notice": "Userdoc for event E."
}
},
"kind": "user",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "C", userDoc, true);
}
BOOST_AUTO_TEST_CASE(emit_event_from_foreign_contract_with_same_signature)
{
char const* sourceCode = R"(
contract C {
/// @notice C.E event
/// @dev C.E event
event E(uint256 value);
}
contract D {
/// @notice D.E event
/// @dev D.E event
event E(uint256 value);
function test() public {
emit C.E(1);
emit E(2);
}
}
)";
char const* devDocC = R"ABCDEF(
{
"events":
{
"E(uint256)":
{
"details": "C.E event"
}
},
"kind": "dev",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "C", devDocC, false);
char const* devDocD = R"ABCDEF(
{
"events":
{
"E(uint256)":
{
"details": "D.E event"
}
},
"kind": "dev",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "D", devDocD, false);
char const* userDocC = R"ABCDEF(
{
"events":
{
"E(uint256)":
{
"notice": "C.E event"
}
},
"kind": "user",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "C", userDocC, true);
char const* userDocD = R"ABCDEF(
{
"events":
{
"E(uint256)":
{
"notice": "D.E event"
}
},
"kind": "user",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "D", userDocD, true);
}
// Tests that emitting an event from contract C in contract D does not inherit natspec from C.E
BOOST_AUTO_TEST_CASE(emit_event_from_foreign_contract_no_inheritance)
{
char const* sourceCode = R"(
contract C {
/// @notice C.E event
/// @dev C.E event
event E();
}
contract D {
event E();
function test() public {
emit C.E();
}
}
)";
char const* devDoc = R"ABCDEF(
{
"kind": "dev",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "D", devDoc, false);
char const* userDoc = R"ABCDEF(
{
"kind": "user",
"methods": {},
"version": 1
}
)ABCDEF";
checkNatspec(sourceCode, "D", userDoc, true);
}
BOOST_AUTO_TEST_CASE(emit_same_signature_event_library_contract)
{
char const* sourceCode = R"(

View File

@ -0,0 +1,13 @@
contract C {
event E();
}
contract D {
function test() public {
emit C.E();
}
}
// ----
// test() ->
// ~ emit E()

View File

@ -0,0 +1,17 @@
contract C {
event E(uint256 value);
}
contract D {
event E(uint256 value);
function test() public {
emit C.E(1);
emit E(2);
}
}
// ----
// test() ->
// ~ emit E(uint256): 0x01
// ~ emit E(uint256): 0x02

View File

@ -0,0 +1,19 @@
interface I {
event E();
}
library L {
function f() internal {
emit I.E();
}
}
contract C {
function g() public {
L.f();
}
}
// ----
// g() ->
// ~ emit E()

View File

@ -0,0 +1,13 @@
interface I {
event Event(address indexed _from, uint256 _value);
}
contract C {
function emitEvent(uint256 _value) public {
emit I.Event(msg.sender, _value);
}
}
// ----
// emitEvent(uint256): 100 ->
// ~ emit Event(address,uint256): #0x1212121212121212121212121212120000000012, 0x64