Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

feat(solc): add support for Paris and Shanghai EVM versions #2385

Merged
merged 3 commits into from
Apr 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 144 additions & 65 deletions ethers-solc/src/artifacts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
};
use ethers_core::abi::Abi;
use md5::Digest;
use once_cell::sync::Lazy;
use semver::{Version, VersionReq};
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use std::{
Expand Down Expand Up @@ -151,7 +152,7 @@ impl CompilerInput {
/// supported by the provided compiler version.
#[must_use]
pub fn normalize_evm_version(mut self, version: &Version) -> Self {
if let Some(ref mut evm_version) = self.settings.evm_version {
if let Some(evm_version) = &mut self.settings.evm_version {
self.settings.evm_version = evm_version.normalize_version(version);
}
self
Expand Down Expand Up @@ -228,7 +229,7 @@ impl StandardJsonCompilerInput {
/// supported by the provided compiler version.
#[must_use]
pub fn normalize_evm_version(mut self, version: &Version) -> Self {
if let Some(ref mut evm_version) = self.settings.evm_version {
if let Some(evm_version) = &mut self.settings.evm_version {
self.settings.evm_version = evm_version.normalize_version(version);
}
self
Expand Down Expand Up @@ -306,28 +307,22 @@ impl Settings {
self
}

/// This will remove/adjust values in the settings that are not compatible with this
/// version
/// This will remove/adjust values in the settings that are not compatible with this version.
pub fn sanitize(&mut self, version: &Version) {
static PRE_V0_6_0: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse("<0.6.0").unwrap());
static PRE_V0_7_5: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse("<0.7.5").unwrap());
static PRE_V0_8_7: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse("<0.8.7").unwrap());
static PRE_V0_8_10: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse("<0.8.10").unwrap());
static PRE_V0_8_18: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse("<0.8.18").unwrap());
static PRE_V0_6_0: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse("<0.6.0").unwrap());
static PRE_V0_7_5: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse("<0.7.5").unwrap());
static PRE_V0_8_7: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse("<0.8.7").unwrap());
static PRE_V0_8_10: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse("<0.8.10").unwrap());
static PRE_V0_8_18: Lazy<VersionReq> = Lazy::new(|| VersionReq::parse("<0.8.18").unwrap());

if PRE_V0_6_0.matches(version) {
if let Some(ref mut meta) = self.metadata {
if let Some(meta) = &mut self.metadata {
// introduced in <https://docs.soliditylang.org/en/v0.6.0/using-the-compiler.html#compiler-api>
// missing in <https://docs.soliditylang.org/en/v0.5.17/using-the-compiler.html#compiler-api>
meta.bytecode_hash.take();
}
// introduced in <https://docs.soliditylang.org/en/v0.6.0/using-the-compiler.html#compiler-api>
let _ = self.debug.take();
self.debug.take();
}

if PRE_V0_7_5.matches(version) {
Expand All @@ -343,26 +338,26 @@ impl Settings {
}

if PRE_V0_8_10.matches(version) {
if let Some(ref mut debug) = self.debug {
if let Some(debug) = &mut self.debug {
// introduced in <https://docs.soliditylang.org/en/v0.8.10/using-the-compiler.html#compiler-api>
// <https://github.com/ethereum/solidity/releases/tag/v0.8.10>
debug.debug_info.clear();
}

if let Some(ref mut model_checker) = self.model_checker {
if let Some(model_checker) = &mut self.model_checker {
// introduced in <https://github.com/ethereum/solidity/releases/tag/v0.8.10>
model_checker.invariants = None;
}
}

if PRE_V0_8_18.matches(version) {
// introduced in 0.8.18 <https://github.com/ethereum/solidity/releases/tag/v0.8.18>
if let Some(ref mut meta) = self.metadata {
if let Some(meta) = &mut self.metadata {
meta.cbor_metadata = None;
}

if let Some(ref mut model_checker) = self.model_checker {
if let Some(ref mut solvers) = model_checker.solvers {
if let Some(model_checker) = &mut self.model_checker {
if let Some(solvers) = &mut model_checker.solvers {
// elf solver introduced in 0.8.18 <https://github.com/ethereum/solidity/releases/tag/v0.8.18>
solvers.retain(|solver| *solver != ModelCheckerSolver::Eld);
}
Expand Down Expand Up @@ -739,6 +734,9 @@ impl YulDetails {
}
}

/// EVM versions.
///
/// Kept in sync with: <https://github.com/ethereum/solidity/blob/develop/liblangutil/EVMVersion.h>
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum EvmVersion {
Homestead,
Expand All @@ -749,54 +747,109 @@ pub enum EvmVersion {
Petersburg,
Istanbul,
Berlin,
#[default]
London,
#[default] // TODO: Move to Shanghai once Solc 0.8.20 is released
Paris,
Shanghai,
}

impl EvmVersion {
/// Checks against the given solidity `semver::Version`
pub fn normalize_version(self, version: &Version) -> Option<EvmVersion> {
// the EVM version flag was only added at 0.4.21
// we work our way backwards
if version >= &BYZANTIUM_SOLC {
// If the Solc is at least at london, it supports all EVM versions
Some(if version >= &LONDON_SOLC {
/// Normalizes this EVM version by checking against the given Solc [`Version`].
pub fn normalize_version(self, version: &Version) -> Option<Self> {
// The EVM version flag was only added in 0.4.21; we work our way backwards
if *version >= BYZANTIUM_SOLC {
// If the Solc is at least at Shanghai, it supports all EVM versions.
// For all other cases, cap at the at-the-time highest possible fork.
let normalized = if self >= Self::Shanghai &&
// TODO: Remove once Solc 0.8.20 is released
SUPPORTS_SHANGHAI.matches(version)
{
self
// For all other cases, cap at the at-the-time highest possible
// fork
} else if version >= &BERLIN_SOLC && self >= EvmVersion::Berlin {
EvmVersion::Berlin
} else if version >= &ISTANBUL_SOLC && self >= EvmVersion::Istanbul {
EvmVersion::Istanbul
} else if version >= &PETERSBURG_SOLC && self >= EvmVersion::Petersburg {
EvmVersion::Petersburg
} else if version >= &CONSTANTINOPLE_SOLC && self >= EvmVersion::Constantinople {
EvmVersion::Constantinople
} else if self >= EvmVersion::Byzantium {
EvmVersion::Byzantium
} else if self >= Self::Paris && *version >= PARIS_SOLC {
Self::Paris
} else if self >= Self::London && *version >= LONDON_SOLC {
Self::London
} else if self >= Self::Berlin && *version >= BERLIN_SOLC {
Self::Berlin
} else if self >= Self::Istanbul && *version >= ISTANBUL_SOLC {
Self::Istanbul
} else if self >= Self::Petersburg && *version >= PETERSBURG_SOLC {
Self::Petersburg
} else if self >= Self::Constantinople && *version >= CONSTANTINOPLE_SOLC {
Self::Constantinople
} else if self >= Self::Byzantium {
Self::Byzantium
Comment on lines +768 to +781
Copy link
Collaborator Author

@DaniPopes DaniPopes Apr 29, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Swapped the conditions so the faster one is first.

} else {
self
})
};
Some(normalized)
} else {
None
}
}

/// Returns the EVM version as a string.
pub const fn as_str(&self) -> &'static str {
match self {
Self::Homestead => "homestead",
Self::TangerineWhistle => "tangerineWhistle",
Self::SpuriousDragon => "spuriousDragon",
Self::Byzantium => "byzantium",
Self::Constantinople => "constantinople",
Self::Petersburg => "petersburg",
Self::Istanbul => "istanbul",
Self::Berlin => "berlin",
Self::London => "london",
Self::Paris => "paris",
Self::Shanghai => "shanghai",
}
}

/// Has the `RETURNDATACOPY` and `RETURNDATASIZE` opcodes.
pub fn supports_returndata(&self) -> bool {
*self >= Self::Byzantium
}

pub fn has_static_call(&self) -> bool {
*self >= Self::Byzantium
}

pub fn has_bitwise_shifting(&self) -> bool {
*self >= Self::Constantinople
}

pub fn has_create2(&self) -> bool {
*self >= Self::Constantinople
}

pub fn has_ext_code_hash(&self) -> bool {
*self >= Self::Constantinople
}

pub fn has_chain_id(&self) -> bool {
*self >= Self::Istanbul
}

pub fn has_self_balance(&self) -> bool {
*self >= Self::Istanbul
}

pub fn has_base_fee(&self) -> bool {
*self >= Self::London
}

pub fn has_prevrandao(&self) -> bool {
*self >= Self::Paris
}

pub fn has_push0(&self) -> bool {
*self >= Self::Shanghai
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

impl fmt::Display for EvmVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = match self {
EvmVersion::Homestead => "homestead",
EvmVersion::TangerineWhistle => "tangerineWhistle",
EvmVersion::SpuriousDragon => "spuriousDragon",
EvmVersion::Constantinople => "constantinople",
EvmVersion::Petersburg => "petersburg",
EvmVersion::Istanbul => "istanbul",
EvmVersion::Berlin => "berlin",
EvmVersion::London => "london",
EvmVersion::Byzantium => "byzantium",
};
write!(f, "{string}")
f.write_str(self.as_str())
}
}

Expand All @@ -805,15 +858,17 @@ impl FromStr for EvmVersion {

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"homestead" => Ok(EvmVersion::Homestead),
"tangerineWhistle" => Ok(EvmVersion::TangerineWhistle),
"spuriousDragon" => Ok(EvmVersion::SpuriousDragon),
"constantinople" => Ok(EvmVersion::Constantinople),
"petersburg" => Ok(EvmVersion::Petersburg),
"istanbul" => Ok(EvmVersion::Istanbul),
"berlin" => Ok(EvmVersion::Berlin),
"london" => Ok(EvmVersion::London),
"byzantium" => Ok(EvmVersion::Byzantium),
"homestead" => Ok(Self::Homestead),
"tangerineWhistle" => Ok(Self::TangerineWhistle),
"spuriousDragon" => Ok(Self::SpuriousDragon),
"byzantium" => Ok(Self::Byzantium),
"constantinople" => Ok(Self::Constantinople),
"petersburg" => Ok(Self::Petersburg),
"istanbul" => Ok(Self::Istanbul),
"berlin" => Ok(Self::Berlin),
"london" => Ok(Self::London),
"paris" => Ok(Self::Paris),
"shanghai" => Ok(Self::Shanghai),
Comment on lines +861 to +871
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reordered to match variant order.

s => Err(format!("Unknown evm version: {s}")),
}
}
Expand Down Expand Up @@ -2312,7 +2367,7 @@ mod tests {
#[test]
fn test_evm_version_normalization() {
for (solc_version, evm_version, expected) in &[
// Ensure before 0.4.21 it always returns None
// Everything before 0.4.21 should always return None
("0.4.20", EvmVersion::Homestead, None),
// Byzantium clipping
("0.4.21", EvmVersion::Homestead, Some(EvmVersion::Homestead)),
Expand All @@ -2337,7 +2392,31 @@ mod tests {
// London
("0.8.7", EvmVersion::Homestead, Some(EvmVersion::Homestead)),
("0.8.7", EvmVersion::London, Some(EvmVersion::London)),
("0.8.7", EvmVersion::London, Some(EvmVersion::London)),
("0.8.7", EvmVersion::Paris, Some(EvmVersion::London)),
// Paris
("0.8.18", EvmVersion::Homestead, Some(EvmVersion::Homestead)),
("0.8.18", EvmVersion::Paris, Some(EvmVersion::Paris)),
("0.8.18", EvmVersion::Shanghai, Some(EvmVersion::Paris)),
// Shanghai
("0.8.20", EvmVersion::Homestead, Some(EvmVersion::Homestead)),
("0.8.20", EvmVersion::Paris, Some(EvmVersion::Paris)),
("0.8.20", EvmVersion::Shanghai, Some(EvmVersion::Shanghai)),
// TODO: Remove once Solc 0.8.20 is released
(
"0.8.20-nightly.2023.4.12+commit.f0c0df2d",
EvmVersion::Shanghai,
Some(EvmVersion::Paris),
),
(
"0.8.20-nightly.2023.4.13+commit.5d42bb5e",
EvmVersion::Shanghai,
Some(EvmVersion::Shanghai),
),
(
"0.8.20-nightly.2023.4.14+commit.e1a9446f",
EvmVersion::Shanghai,
Some(EvmVersion::Shanghai),
),
] {
assert_eq!(
&evm_version.normalize_version(&Version::from_str(solc_version).unwrap()),
Expand Down
26 changes: 21 additions & 5 deletions ethers-solc/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
error::{Result, SolcError},
utils, CompilerInput, CompilerOutput,
};
use once_cell::sync::Lazy;
use semver::{Version, VersionReq};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::{
Expand Down Expand Up @@ -44,20 +45,35 @@ pub const BERLIN_SOLC: Version = Version::new(0, 8, 5);
/// <https://blog.soliditylang.org/2021/08/11/solidity-0.8.7-release-announcement/>
pub const LONDON_SOLC: Version = Version::new(0, 8, 7);

/// Paris support
/// <https://blog.soliditylang.org/2023/02/01/solidity-0.8.18-release-announcement/>
pub const PARIS_SOLC: Version = Version::new(0, 8, 18);

/// Shanghai support
// TODO: Solc blogpost link
pub const SHANGHAI_SOLC: Version = Version::new(0, 8, 20);

/// This will be removed once 0.8.20 is released.
///
/// Shanghai support was added in [ethereum/solidity#14107](https://github.com/ethereum/solidity/pull/14107),
/// which was released in `nightly.2023.4.13`.
pub(crate) static SUPPORTS_SHANGHAI: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.8.20-nightly.2023.4.13").unwrap());

// `--base-path` was introduced in 0.6.9 <https://github.com/ethereum/solidity/releases/tag/v0.6.9>
pub static SUPPORTS_BASE_PATH: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());
pub static SUPPORTS_BASE_PATH: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());

// `--include-path` was introduced in 0.8.8 <https://github.com/ethereum/solidity/releases/tag/v0.8.8>
pub static SUPPORTS_INCLUDE_PATH: once_cell::sync::Lazy<VersionReq> =
once_cell::sync::Lazy::new(|| VersionReq::parse(">=0.8.8").unwrap());
pub static SUPPORTS_INCLUDE_PATH: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.8.8").unwrap());

#[cfg(any(test, feature = "tests"))]
use std::sync::Mutex;

#[cfg(any(test, feature = "tests"))]
#[allow(unused)]
static LOCK: once_cell::sync::Lazy<Mutex<()>> = once_cell::sync::Lazy::new(|| Mutex::new(()));
static LOCK: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));

/// take the lock in tests, we use this to enforce that
/// a test does not run while a compiler version is being installed
Expand Down