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

feat(solc): set default EVM version to Shanghai #2414

Merged
merged 9 commits into from
May 18, 2023
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ env:
ETHERSCAN_API_KEY: "I5BXNZYP5GEDWFINGVEZKYIVU2695NPQZB"
GOERLI_PRIVATE_KEY: "fa4a1a79e869a96fcb42727f75e3232d6865a82ea675bb95de967a7fe6a773b2"
GETH_BUILD: "1.11.2-73b01f40"
SOLC_VERSION: "0.8.19"
SOLC_VERSION: "0.8.20"
CARGO_TERM_COLOR: always

jobs:
Expand Down
17 changes: 9 additions & 8 deletions ethers-core/src/types/opcode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ pub enum Opcode {
/// Opcode 0x43 - Get the block’s number
NUMBER,
/// Opcode 0x44 - Get the block’s difficulty
#[serde(alias = "PREVRANDAO", alias = "RANDOM")]
#[strum(to_string = "DIFFICULTY", serialize = "PREVRANDAO", serialize = "RANDOM")]
DIFFICULTY,
//RANDOM, // 0x44 // Same as DIFFICULTY
//PREVRANDAO, // 0x44 // Same as DIFFICULTY
/// Opcode 0x45 - Get the block’s gas limit
GASLIMIT,
/// Opcode 0x46 - Get the chain ID
Expand Down Expand Up @@ -181,10 +181,11 @@ pub enum Opcode {
/// Opcode 0x5B - Mark a valid destination for jumps
JUMPDEST,

// 0x5C - 0x5F are invalid
// 0x5C - 0x5E are invalid

// 0x60 range - pushes.
// PUSH0, // 0x5F (https://eips.ethereum.org/EIPS/eip-3855)
// 0x5F range - pushes.
/// Opcode 0x5F - Place the constant value 0 on stack
PUSH0 = 0x5f,
/// Opcode 0x60 - Place 1 byte item on stack
PUSH1 = 0x60,
/// Opcode 0x61 - Place 2 byte item on stack
Expand Down Expand Up @@ -384,7 +385,8 @@ mod tests {
use serde::de::{value::StrDeserializer, IntoDeserializer};
use std::collections::HashSet;

// Taken from: https://github.com/bluealloy/revm/blob/main/crates/interpreter/src/instructions/opcode.rs#L181
// Taken from REVM:
// https://github.com/bluealloy/revm/blob/f8ff6b330dce126ab9359ab8dde02ba1d09b9306/crates/interpreter/src/instructions/opcode.rs#L184
const OPCODE_JUMPMAP: [Option<&'static str>; 256] = [
/* 0x00 */ Some("STOP"),
/* 0x01 */ Some("ADD"),
Expand Down Expand Up @@ -481,8 +483,7 @@ mod tests {
/* 0x5c */ None,
/* 0x5d */ None,
/* 0x5e */ None,
// /* 0x5f */ Some("PUSH0"),
/* 0x5f */ None,
/* 0x5f */ Some("PUSH0"),
/* 0x60 */ Some("PUSH1"),
/* 0x61 */ Some("PUSH2"),
/* 0x62 */ Some("PUSH3"),
Expand Down
4 changes: 4 additions & 0 deletions ethers-solc/src/artifacts/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ ast_node!(
scope: usize,
#[serde(default)]
used_errors: Vec<usize>,
#[serde(default)]
used_events: Vec<usize>,
#[serde(default)]
internal_function_ids: BTreeMap<usize, usize>,
}
);

Expand Down
83 changes: 38 additions & 45 deletions ethers-solc/src/artifacts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use crate::{
};
use ethers_core::abi::Abi;
use md5::Digest;
use once_cell::sync::Lazy;
use semver::{Version, VersionReq};
use semver::Version;
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use std::{
collections::{BTreeMap, HashSet},
Expand Down Expand Up @@ -309,35 +308,33 @@ impl Settings {

/// 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: 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) {
const V0_6_0: Version = Version::new(0, 6, 0);
if *version < V0_6_0 {
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();
meta.bytecode_hash = None;
}
// introduced in <https://docs.soliditylang.org/en/v0.6.0/using-the-compiler.html#compiler-api>
self.debug.take();
self.debug = None;
}

if PRE_V0_7_5.matches(version) {
const V0_7_5: Version = Version::new(0, 7, 5);
if *version < V0_7_5 {
// introduced in 0.7.5 <https://github.com/ethereum/solidity/releases/tag/v0.7.5>
self.via_ir.take();
self.via_ir = None;
}

if PRE_V0_8_7.matches(version) {
const V0_8_7: Version = Version::new(0, 8, 7);
if *version < V0_8_7 {
// lower the disable version from 0.8.10 to 0.8.7, due to `divModNoSlacks`,
// `showUnproved` and `solvers` are implemented
// introduced in <https://github.com/ethereum/solidity/releases/tag/v0.8.7>
self.model_checker = None;
}

if PRE_V0_8_10.matches(version) {
const V0_8_10: Version = Version::new(0, 8, 10);
if *version < V0_8_10 {
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>
Expand All @@ -350,7 +347,8 @@ impl Settings {
}
}

if PRE_V0_8_18.matches(version) {
const V0_8_18: Version = Version::new(0, 8, 18);
if *version < V0_8_18 {
// introduced in 0.8.18 <https://github.com/ethereum/solidity/releases/tag/v0.8.18>
if let Some(meta) = &mut self.metadata {
meta.cbor_metadata = None;
Expand All @@ -363,6 +361,14 @@ impl Settings {
}
}
}

if *version < SHANGHAI_SOLC {
// introduced in 0.8.20 <https://github.com/ethereum/solidity/releases/tag/v0.8.20>
if let Some(model_checker) = &mut self.model_checker {
model_checker.show_proved_safe = None;
model_checker.show_unsupported = None;
}
}
}

/// Inserts a set of `ContractOutputSelection`
Expand Down Expand Up @@ -748,8 +754,8 @@ pub enum EvmVersion {
Istanbul,
Berlin,
London,
#[default] // TODO: Move to Shanghai once Solc 0.8.20 is released
Paris,
#[default]
Shanghai,
}

Expand All @@ -758,12 +764,9 @@ impl EvmVersion {
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.
// If the Solc version 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)
{
let normalized = if *version >= SHANGHAI_SOLC {
self
} else if self >= Self::Paris && *version >= PARIS_SOLC {
Self::Paris
Expand Down Expand Up @@ -1123,7 +1126,8 @@ pub struct MetadataSource {
}

/// Model checker settings for solc
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ModelCheckerSettings {
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub contracts: BTreeMap<String, Vec<String>>,
Expand All @@ -1139,12 +1143,16 @@ pub struct ModelCheckerSettings {
pub targets: Option<Vec<ModelCheckerTarget>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub invariants: Option<Vec<ModelCheckerInvariant>>,
#[serde(rename = "showUnproved", skip_serializing_if = "Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub show_unproved: Option<bool>,
#[serde(rename = "divModWithSlacks", skip_serializing_if = "Option::is_none")]
#[serde(skip_serializing_if = "Option::is_none")]
pub div_mod_with_slacks: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub solvers: Option<Vec<ModelCheckerSolver>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show_unsupported: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub show_proved_safe: Option<bool>,
}

/// Which model checker engine to run.
Expand Down Expand Up @@ -2402,26 +2410,12 @@ mod tests {
("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),
),
] {
let version = Version::from_str(solc_version).unwrap();
assert_eq!(
&evm_version.normalize_version(&Version::from_str(solc_version).unwrap()),
expected
&evm_version.normalize_version(&version),
expected,
"({version}, {evm_version:?})"
)
}
}
Expand Down Expand Up @@ -2464,8 +2458,7 @@ mod tests {
let i = input.clone().sanitized(&version);
assert_eq!(i.settings.metadata.unwrap().cbor_metadata, Some(true));

let version: Version = "0.8.0".parse().unwrap();
let i = input.sanitized(&version);
let i = input.sanitized(&Version::new(0, 8, 0));
assert!(i.settings.metadata.unwrap().cbor_metadata.is_none());
}

Expand Down
11 changes: 2 additions & 9 deletions ethers-solc/src/compile/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,9 @@ pub const LONDON_SOLC: Version = Version::new(0, 8, 7);
pub const PARIS_SOLC: Version = Version::new(0, 8, 18);

/// Shanghai support
// TODO: Solc blogpost link
/// <https://blog.soliditylang.org/2023/05/10/solidity-0.8.20-release-announcement/>
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: Lazy<VersionReq> =
Lazy::new(|| VersionReq::parse(">=0.6.9").unwrap());
Expand Down Expand Up @@ -850,7 +843,7 @@ mod tests {
// update this test whenever there's a new sol
// version. that's ok! good reminder to check the
// patch notes.
(">=0.5.0", "0.8.19"),
(">=0.5.0", "0.8.20"),
// range
(">=0.4.0 <0.5.0", "0.4.26"),
]
Expand Down
21 changes: 17 additions & 4 deletions ethers-solc/src/compile/output/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,26 +188,39 @@ impl<T: ArtifactOutput> ProjectCompileOutput<T> {
self.compiler_output
}

/// Whether this type has a compiler output
/// Returns whether this type has a compiler output.
pub fn has_compiled_contracts(&self) -> bool {
self.compiler_output.is_empty()
}

/// Whether this type does not contain compiled contracts
/// Returns whether this type does not contain compiled contracts.
pub fn is_unchanged(&self) -> bool {
self.compiler_output.is_unchanged()
}

/// Whether there were errors
/// Returns whether any errors were emitted by the compiler.
pub fn has_compiler_errors(&self) -> bool {
self.compiler_output.has_error(&self.ignored_error_codes, &self.compiler_severity_filter)
}

/// Whether there were warnings
/// Returns whether any warnings were emitted by the compiler.
pub fn has_compiler_warnings(&self) -> bool {
self.compiler_output.has_warning(&self.ignored_error_codes)
}

/// Panics if any errors were emitted by the compiler.
#[track_caller]
pub fn succeeded(self) -> Self {
self.assert_success();
self
}

/// Panics if any errors were emitted by the compiler.
#[track_caller]
pub fn assert_success(&self) {
assert!(!self.has_compiler_errors(), "\n{self}\n");
}

/// Returns the set of `Artifacts` that were cached and got reused during
/// [`crate::Project::compile()`]
pub fn cached_artifacts(&self) -> &Artifacts<T::Artifact> {
Expand Down
4 changes: 2 additions & 2 deletions ethers-solc/src/compile/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ mod tests {
let project = TempProject::<MinimalCombinedArtifacts>::new(paths).unwrap();

let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
compiled.assert_success();

let inner = project.project();
let compiler = ProjectCompiler::new(inner).unwrap();
Expand Down Expand Up @@ -761,7 +761,7 @@ mod tests {
)
.unwrap();
let compiled = tmp.compile().unwrap();
assert!(!compiled.has_compiler_errors());
compiled.assert_success();

tmp.artifacts_snapshot().unwrap().assert_artifacts_essentials_present();

Expand Down
19 changes: 4 additions & 15 deletions ethers-solc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,29 +966,24 @@ impl<T: ArtifactOutput> ArtifactOutput for Project<T> {
#[cfg(test)]
#[cfg(all(feature = "svm-solc", not(target_arch = "wasm32")))]
mod tests {
use super::*;
use crate::remappings::Remapping;

#[test]
fn test_build_all_versions() {
use super::*;

let paths = ProjectPathsConfig::builder()
.root("./test-data/test-contract-versions")
.sources("./test-data/test-contract-versions")
.build()
.unwrap();
let project = Project::builder().paths(paths).no_artifacts().ephemeral().build().unwrap();
let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
let contracts = compiled.output().contracts;
let contracts = project.compile().unwrap().succeeded().output().contracts;
// Contracts A to F
assert_eq!(contracts.contracts().count(), 5);
}

#[test]
fn test_build_many_libs() {
use super::*;

let root = utils::canonicalize("./test-data/test-contract-libs").unwrap();

let paths = ProjectPathsConfig::builder()
Expand All @@ -1010,16 +1005,12 @@ mod tests {
.no_artifacts()
.build()
.unwrap();
let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
let contracts = compiled.output().contracts;
let contracts = project.compile().unwrap().succeeded().output().contracts;
assert_eq!(contracts.contracts().count(), 3);
}

#[test]
fn test_build_remappings() {
use super::*;

let root = utils::canonicalize("./test-data/test-contract-remappings").unwrap();
let paths = ProjectPathsConfig::builder()
.root(&root)
Expand All @@ -1029,9 +1020,7 @@ mod tests {
.build()
.unwrap();
let project = Project::builder().no_artifacts().paths(paths).ephemeral().build().unwrap();
let compiled = project.compile().unwrap();
assert!(!compiled.has_compiler_errors());
let contracts = compiled.output().contracts;
let contracts = project.compile().unwrap().succeeded().output().contracts;
assert_eq!(contracts.contracts().count(), 2);
}
}
Loading