mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-26 14:28:01 +00:00
+87
-81
@@ -7,102 +7,108 @@ use crate::{mode::ParsedMode, steps::*};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, Eq, PartialEq, JsonSchema)]
|
||||
pub struct Case {
|
||||
/// An optional name of the test case.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
/// An optional name of the test case.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
|
||||
/// An optional comment on the case which has no impact on the execution in any way.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
/// An optional comment on the case which has no impact on the execution in any way.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub comment: Option<String>,
|
||||
|
||||
/// This represents a mode that has been parsed from test metadata.
|
||||
///
|
||||
/// Mode strings can take the following form (in pseudo-regex):
|
||||
///
|
||||
/// ```text
|
||||
/// [YEILV][+-]? (M[0123sz])? <semver>?
|
||||
/// ```
|
||||
///
|
||||
/// If this is provided then it takes higher priority than the modes specified in the metadata
|
||||
/// file.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub modes: Option<Vec<ParsedMode>>,
|
||||
/// This represents a mode that has been parsed from test metadata.
|
||||
///
|
||||
/// Mode strings can take the following form (in pseudo-regex):
|
||||
///
|
||||
/// ```text
|
||||
/// [YEILV][+-]? (M[0123sz])? <semver>?
|
||||
/// ```
|
||||
///
|
||||
/// If this is provided then it takes higher priority than the modes specified in the metadata
|
||||
/// file.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub modes: Option<Vec<ParsedMode>>,
|
||||
|
||||
/// The set of steps to run as part of this test case.
|
||||
#[serde(rename = "inputs")]
|
||||
pub steps: Vec<Step>,
|
||||
/// The set of steps to run as part of this test case.
|
||||
#[serde(rename = "inputs")]
|
||||
pub steps: Vec<Step>,
|
||||
|
||||
/// An optional name of the group of tests that this test belongs to.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub group: Option<String>,
|
||||
/// An optional name of the group of tests that this test belongs to.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub group: Option<String>,
|
||||
|
||||
/// An optional set of expectations and assertions to make about the transaction after it ran.
|
||||
///
|
||||
/// If this is not specified then the only assertion that will be ran is that the transaction
|
||||
/// was successful.
|
||||
///
|
||||
/// This expectation that's on the case itself will be attached to the final step of the case.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expected: Option<Expected>,
|
||||
/// An optional set of expectations and assertions to make about the transaction after it ran.
|
||||
///
|
||||
/// If this is not specified then the only assertion that will be ran is that the transaction
|
||||
/// was successful.
|
||||
///
|
||||
/// This expectation that's on the case itself will be attached to the final step of the case.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expected: Option<Expected>,
|
||||
|
||||
/// An optional boolean which defines if the case as a whole should be ignored. If null then
|
||||
/// the case will not be ignored.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ignore: Option<bool>,
|
||||
/// An optional boolean which defines if the case as a whole should be ignored. If null then
|
||||
/// the case will not be ignored.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub ignore: Option<bool>,
|
||||
}
|
||||
|
||||
impl Case {
|
||||
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
|
||||
let steps_len = self.steps.len();
|
||||
self.steps.clone().into_iter().enumerate().map(move |(idx, mut step)| {
|
||||
let Step::FunctionCall(ref mut input) = step else {
|
||||
return step;
|
||||
};
|
||||
pub fn steps_iterator(&self) -> impl Iterator<Item = Step> {
|
||||
let steps_len = self.steps.len();
|
||||
self.steps
|
||||
.clone()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(move |(idx, mut step)| {
|
||||
let Step::FunctionCall(ref mut input) = step else {
|
||||
return step;
|
||||
};
|
||||
|
||||
if idx + 1 == steps_len {
|
||||
if input.expected.is_none() {
|
||||
input.expected = self.expected.clone();
|
||||
}
|
||||
if idx + 1 == steps_len {
|
||||
if input.expected.is_none() {
|
||||
input.expected = self.expected.clone();
|
||||
}
|
||||
|
||||
// TODO: What does it mean for us to have an `expected` field on the case itself
|
||||
// but the final input also has an expected field that doesn't match the one on
|
||||
// the case? What are we supposed to do with that final expected field on the
|
||||
// case?
|
||||
// TODO: What does it mean for us to have an `expected` field on the case itself
|
||||
// but the final input also has an expected field that doesn't match the one on
|
||||
// the case? What are we supposed to do with that final expected field on the
|
||||
// case?
|
||||
|
||||
step
|
||||
} else {
|
||||
step
|
||||
}
|
||||
})
|
||||
}
|
||||
step
|
||||
} else {
|
||||
step
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn steps_iterator_for_benchmarks(
|
||||
&self,
|
||||
default_repeat_count: usize,
|
||||
) -> Box<dyn Iterator<Item = Step> + '_> {
|
||||
let contains_repeat = self.steps_iterator().any(|step| matches!(&step, Step::Repeat(..)));
|
||||
if contains_repeat {
|
||||
Box::new(self.steps_iterator()) as Box<_>
|
||||
} else {
|
||||
Box::new(std::iter::once(Step::Repeat(Box::new(RepeatStep {
|
||||
comment: None,
|
||||
repeat: default_repeat_count,
|
||||
steps: self.steps_iterator().collect(),
|
||||
})))) as Box<_>
|
||||
}
|
||||
}
|
||||
pub fn steps_iterator_for_benchmarks(
|
||||
&self,
|
||||
default_repeat_count: usize,
|
||||
) -> Box<dyn Iterator<Item = Step> + '_> {
|
||||
let contains_repeat = self
|
||||
.steps_iterator()
|
||||
.any(|step| matches!(&step, Step::Repeat(..)));
|
||||
if contains_repeat {
|
||||
Box::new(self.steps_iterator()) as Box<_>
|
||||
} else {
|
||||
Box::new(std::iter::once(Step::Repeat(Box::new(RepeatStep {
|
||||
comment: None,
|
||||
repeat: default_repeat_count,
|
||||
steps: self.steps_iterator().collect(),
|
||||
})))) as Box<_>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn solc_modes(&self) -> Vec<Mode> {
|
||||
match &self.modes {
|
||||
Some(modes) => ParsedMode::many_to_modes(modes.iter()).collect(),
|
||||
None => Mode::all().cloned().collect(),
|
||||
}
|
||||
}
|
||||
pub fn solc_modes(&self) -> Vec<Mode> {
|
||||
match &self.modes {
|
||||
Some(modes) => ParsedMode::many_to_modes(modes.iter()).collect(),
|
||||
None => Mode::all().cloned().collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
define_wrapper_type!(
|
||||
/// A wrapper type for the index of test cases found in metadata file.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct CaseIdx(usize) impl Display, FromStr;
|
||||
/// A wrapper type for the index of test cases found in metadata file.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct CaseIdx(usize) impl Display, FromStr;
|
||||
);
|
||||
|
||||
+106
-98
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
fs::File,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use revive_dt_common::iterators::FilesWithExtensionIterator;
|
||||
@@ -13,111 +13,119 @@ use anyhow::Context as _;
|
||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Corpus {
|
||||
SinglePath { name: String, path: PathBuf },
|
||||
MultiplePaths { name: String, paths: Vec<PathBuf> },
|
||||
SinglePath { name: String, path: PathBuf },
|
||||
MultiplePaths { name: String, paths: Vec<PathBuf> },
|
||||
}
|
||||
|
||||
impl Corpus {
|
||||
pub fn try_from_path(file_path: impl AsRef<Path>) -> anyhow::Result<Self> {
|
||||
let mut corpus = File::open(file_path.as_ref())
|
||||
.map_err(anyhow::Error::from)
|
||||
.and_then(|file| serde_json::from_reader::<_, Corpus>(file).map_err(Into::into))
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to open and deserialize corpus file at {}",
|
||||
file_path.as_ref().display()
|
||||
)
|
||||
})?;
|
||||
pub fn try_from_path(file_path: impl AsRef<Path>) -> anyhow::Result<Self> {
|
||||
let mut corpus = File::open(file_path.as_ref())
|
||||
.map_err(anyhow::Error::from)
|
||||
.and_then(|file| serde_json::from_reader::<_, Corpus>(file).map_err(Into::into))
|
||||
.with_context(|| {
|
||||
format!(
|
||||
"Failed to open and deserialize corpus file at {}",
|
||||
file_path.as_ref().display()
|
||||
)
|
||||
})?;
|
||||
|
||||
let corpus_directory = file_path
|
||||
.as_ref()
|
||||
.canonicalize()
|
||||
.context("Failed to canonicalize the path to the corpus file")?
|
||||
.parent()
|
||||
.context("Corpus file has no parent")?
|
||||
.to_path_buf();
|
||||
let corpus_directory = file_path
|
||||
.as_ref()
|
||||
.canonicalize()
|
||||
.context("Failed to canonicalize the path to the corpus file")?
|
||||
.parent()
|
||||
.context("Corpus file has no parent")?
|
||||
.to_path_buf();
|
||||
|
||||
for path in corpus.paths_iter_mut() {
|
||||
*path = corpus_directory.join(path.as_path())
|
||||
}
|
||||
for path in corpus.paths_iter_mut() {
|
||||
*path = corpus_directory.join(path.as_path())
|
||||
}
|
||||
|
||||
Ok(corpus)
|
||||
}
|
||||
Ok(corpus)
|
||||
}
|
||||
|
||||
pub fn enumerate_tests(&self) -> Vec<MetadataFile> {
|
||||
let mut tests = self
|
||||
.paths_iter()
|
||||
.flat_map(|root_path| {
|
||||
if !root_path.is_dir() {
|
||||
Box::new(std::iter::once(root_path.to_path_buf()))
|
||||
as Box<dyn Iterator<Item = _>>
|
||||
} else {
|
||||
Box::new(
|
||||
FilesWithExtensionIterator::new(root_path)
|
||||
.with_use_cached_fs(true)
|
||||
.with_allowed_extension("sol")
|
||||
.with_allowed_extension("json"),
|
||||
)
|
||||
}
|
||||
.map(move |metadata_file_path| (root_path, metadata_file_path))
|
||||
})
|
||||
.filter_map(|(root_path, metadata_file_path)| {
|
||||
Metadata::try_from_file(&metadata_file_path)
|
||||
.or_else(|| {
|
||||
debug!(
|
||||
discovered_from = %root_path.display(),
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
"Skipping file since it doesn't contain valid metadata"
|
||||
);
|
||||
None
|
||||
})
|
||||
.map(|metadata| MetadataFile {
|
||||
metadata_file_path,
|
||||
corpus_file_path: root_path.to_path_buf(),
|
||||
content: metadata,
|
||||
})
|
||||
.inspect(|metadata_file| {
|
||||
debug!(
|
||||
metadata_file_path = %metadata_file.relative_path().display(),
|
||||
"Loaded metadata file"
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tests.sort_by(|a, b| a.metadata_file_path.cmp(&b.metadata_file_path));
|
||||
tests.dedup_by(|a, b| a.metadata_file_path == b.metadata_file_path);
|
||||
info!(len = tests.len(), corpus_name = self.name(), "Found tests in Corpus");
|
||||
tests
|
||||
}
|
||||
pub fn enumerate_tests(&self) -> Vec<MetadataFile> {
|
||||
let mut tests = self
|
||||
.paths_iter()
|
||||
.flat_map(|root_path| {
|
||||
if !root_path.is_dir() {
|
||||
Box::new(std::iter::once(root_path.to_path_buf()))
|
||||
as Box<dyn Iterator<Item = _>>
|
||||
} else {
|
||||
Box::new(
|
||||
FilesWithExtensionIterator::new(root_path)
|
||||
.with_use_cached_fs(true)
|
||||
.with_allowed_extension("sol")
|
||||
.with_allowed_extension("json"),
|
||||
)
|
||||
}
|
||||
.map(move |metadata_file_path| (root_path, metadata_file_path))
|
||||
})
|
||||
.filter_map(|(root_path, metadata_file_path)| {
|
||||
Metadata::try_from_file(&metadata_file_path)
|
||||
.or_else(|| {
|
||||
debug!(
|
||||
discovered_from = %root_path.display(),
|
||||
metadata_file_path = %metadata_file_path.display(),
|
||||
"Skipping file since it doesn't contain valid metadata"
|
||||
);
|
||||
None
|
||||
})
|
||||
.map(|metadata| MetadataFile {
|
||||
metadata_file_path,
|
||||
corpus_file_path: root_path.to_path_buf(),
|
||||
content: metadata,
|
||||
})
|
||||
.inspect(|metadata_file| {
|
||||
debug!(
|
||||
metadata_file_path = %metadata_file.relative_path().display(),
|
||||
"Loaded metadata file"
|
||||
)
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tests.sort_by(|a, b| a.metadata_file_path.cmp(&b.metadata_file_path));
|
||||
tests.dedup_by(|a, b| a.metadata_file_path == b.metadata_file_path);
|
||||
info!(
|
||||
len = tests.len(),
|
||||
corpus_name = self.name(),
|
||||
"Found tests in Corpus"
|
||||
);
|
||||
tests
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Corpus::SinglePath { name, .. } | Corpus::MultiplePaths { name, .. } => name.as_str(),
|
||||
}
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
match self {
|
||||
Corpus::SinglePath { name, .. } | Corpus::MultiplePaths { name, .. } => name.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paths_iter(&self) -> impl Iterator<Item = &Path> {
|
||||
match self {
|
||||
Corpus::SinglePath { path, .. } =>
|
||||
Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>,
|
||||
Corpus::MultiplePaths { paths, .. } =>
|
||||
Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>,
|
||||
}
|
||||
}
|
||||
pub fn paths_iter(&self) -> impl Iterator<Item = &Path> {
|
||||
match self {
|
||||
Corpus::SinglePath { path, .. } => {
|
||||
Box::new(std::iter::once(path.as_path())) as Box<dyn Iterator<Item = _>>
|
||||
}
|
||||
Corpus::MultiplePaths { paths, .. } => {
|
||||
Box::new(paths.iter().map(|path| path.as_path())) as Box<dyn Iterator<Item = _>>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> {
|
||||
match self {
|
||||
Corpus::SinglePath { path, .. } =>
|
||||
Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>,
|
||||
Corpus::MultiplePaths { paths, .. } =>
|
||||
Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>,
|
||||
}
|
||||
}
|
||||
pub fn paths_iter_mut(&mut self) -> impl Iterator<Item = &mut PathBuf> {
|
||||
match self {
|
||||
Corpus::SinglePath { path, .. } => {
|
||||
Box::new(std::iter::once(path)) as Box<dyn Iterator<Item = _>>
|
||||
}
|
||||
Corpus::MultiplePaths { paths, .. } => {
|
||||
Box::new(paths.iter_mut()) as Box<dyn Iterator<Item = _>>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn path_count(&self) -> usize {
|
||||
match self {
|
||||
Corpus::SinglePath { .. } => 1,
|
||||
Corpus::MultiplePaths { paths, .. } => paths.len(),
|
||||
}
|
||||
}
|
||||
pub fn path_count(&self) -> usize {
|
||||
match self {
|
||||
Corpus::SinglePath { .. } => 1,
|
||||
Corpus::MultiplePaths { paths, .. } => paths.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+460
-414
File diff suppressed because it is too large
Load Diff
+188
-174
@@ -1,8 +1,8 @@
|
||||
use anyhow::Context as _;
|
||||
use regex::Regex;
|
||||
use revive_dt_common::{
|
||||
iterators::EitherIter,
|
||||
types::{Mode, ModeOptimizerSetting, ModePipeline},
|
||||
iterators::EitherIter,
|
||||
types::{Mode, ModeOptimizerSetting, ModePipeline},
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -20,17 +20,17 @@ use std::{collections::HashSet, fmt::Display, str::FromStr, sync::LazyLock};
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize, JsonSchema)]
|
||||
#[serde(try_from = "String", into = "String")]
|
||||
pub struct ParsedMode {
|
||||
pub pipeline: Option<ModePipeline>,
|
||||
pub optimize_flag: Option<bool>,
|
||||
pub optimize_setting: Option<ModeOptimizerSetting>,
|
||||
pub version: Option<semver::VersionReq>,
|
||||
pub pipeline: Option<ModePipeline>,
|
||||
pub optimize_flag: Option<bool>,
|
||||
pub optimize_setting: Option<ModeOptimizerSetting>,
|
||||
pub version: Option<semver::VersionReq>,
|
||||
}
|
||||
|
||||
impl FromStr for ParsedMode {
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"(?x)
|
||||
type Err = anyhow::Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static REGEX: LazyLock<Regex> = LazyLock::new(|| {
|
||||
Regex::new(r"(?x)
|
||||
^
|
||||
(?:(?P<pipeline>[YEILV])(?P<optimize_flag>[+-])?)? # Pipeline to use eg Y, E+, E-
|
||||
\s*
|
||||
@@ -39,204 +39,218 @@ impl FromStr for ParsedMode {
|
||||
(?P<version>[>=<]*\d+(?:\.\d+)*)? # Optional semver version eg >=0.8.0, 0.7, <0.8
|
||||
$
|
||||
").unwrap()
|
||||
});
|
||||
});
|
||||
|
||||
let Some(caps) = REGEX.captures(s) else {
|
||||
anyhow::bail!("Cannot parse mode '{s}' from string");
|
||||
};
|
||||
let Some(caps) = REGEX.captures(s) else {
|
||||
anyhow::bail!("Cannot parse mode '{s}' from string");
|
||||
};
|
||||
|
||||
let pipeline = match caps.name("pipeline") {
|
||||
Some(m) => Some(
|
||||
ModePipeline::from_str(m.as_str())
|
||||
.context("Failed to parse mode pipeline from string")?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
let pipeline = match caps.name("pipeline") {
|
||||
Some(m) => Some(
|
||||
ModePipeline::from_str(m.as_str())
|
||||
.context("Failed to parse mode pipeline from string")?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let optimize_flag = caps.name("optimize_flag").map(|m| m.as_str() == "+");
|
||||
let optimize_flag = caps.name("optimize_flag").map(|m| m.as_str() == "+");
|
||||
|
||||
let optimize_setting = match caps.name("optimize_setting") {
|
||||
Some(m) => Some(
|
||||
ModeOptimizerSetting::from_str(m.as_str())
|
||||
.context("Failed to parse optimizer setting from string")?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
let optimize_setting = match caps.name("optimize_setting") {
|
||||
Some(m) => Some(
|
||||
ModeOptimizerSetting::from_str(m.as_str())
|
||||
.context("Failed to parse optimizer setting from string")?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let version = match caps.name("version") {
|
||||
Some(m) => Some(
|
||||
semver::VersionReq::parse(m.as_str())
|
||||
.map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"Cannot parse the version requirement '{}': {e}",
|
||||
m.as_str()
|
||||
)
|
||||
})
|
||||
.context("Failed to parse semver requirement from mode string")?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
let version = match caps.name("version") {
|
||||
Some(m) => Some(
|
||||
semver::VersionReq::parse(m.as_str())
|
||||
.map_err(|e| {
|
||||
anyhow::anyhow!(
|
||||
"Cannot parse the version requirement '{}': {e}",
|
||||
m.as_str()
|
||||
)
|
||||
})
|
||||
.context("Failed to parse semver requirement from mode string")?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
|
||||
Ok(ParsedMode { pipeline, optimize_flag, optimize_setting, version })
|
||||
}
|
||||
Ok(ParsedMode {
|
||||
pipeline,
|
||||
optimize_flag,
|
||||
optimize_setting,
|
||||
version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ParsedMode {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut has_written = false;
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut has_written = false;
|
||||
|
||||
if let Some(pipeline) = self.pipeline {
|
||||
pipeline.fmt(f)?;
|
||||
if let Some(optimize_flag) = self.optimize_flag {
|
||||
f.write_str(if optimize_flag { "+" } else { "-" })?;
|
||||
}
|
||||
has_written = true;
|
||||
}
|
||||
if let Some(pipeline) = self.pipeline {
|
||||
pipeline.fmt(f)?;
|
||||
if let Some(optimize_flag) = self.optimize_flag {
|
||||
f.write_str(if optimize_flag { "+" } else { "-" })?;
|
||||
}
|
||||
has_written = true;
|
||||
}
|
||||
|
||||
if let Some(optimize_setting) = self.optimize_setting {
|
||||
if has_written {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
optimize_setting.fmt(f)?;
|
||||
has_written = true;
|
||||
}
|
||||
if let Some(optimize_setting) = self.optimize_setting {
|
||||
if has_written {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
optimize_setting.fmt(f)?;
|
||||
has_written = true;
|
||||
}
|
||||
|
||||
if let Some(version) = &self.version {
|
||||
if has_written {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
version.fmt(f)?;
|
||||
}
|
||||
if let Some(version) = &self.version {
|
||||
if has_written {
|
||||
f.write_str(" ")?;
|
||||
}
|
||||
version.fmt(f)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParsedMode> for String {
|
||||
fn from(parsed_mode: ParsedMode) -> Self {
|
||||
parsed_mode.to_string()
|
||||
}
|
||||
fn from(parsed_mode: ParsedMode) -> Self {
|
||||
parsed_mode.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for ParsedMode {
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
ParsedMode::from_str(&value)
|
||||
}
|
||||
type Error = anyhow::Error;
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
ParsedMode::from_str(&value)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParsedMode {
|
||||
/// This takes a [`ParsedMode`] and expands it into a list of [`Mode`]s that we should try.
|
||||
pub fn to_modes(&self) -> impl Iterator<Item = Mode> {
|
||||
let pipeline_iter = self.pipeline.as_ref().map_or_else(
|
||||
|| EitherIter::A(ModePipeline::test_cases()),
|
||||
|p| EitherIter::B(std::iter::once(*p)),
|
||||
);
|
||||
/// This takes a [`ParsedMode`] and expands it into a list of [`Mode`]s that we should try.
|
||||
pub fn to_modes(&self) -> impl Iterator<Item = Mode> {
|
||||
let pipeline_iter = self.pipeline.as_ref().map_or_else(
|
||||
|| EitherIter::A(ModePipeline::test_cases()),
|
||||
|p| EitherIter::B(std::iter::once(*p)),
|
||||
);
|
||||
|
||||
let optimize_flag_setting = self
|
||||
.optimize_flag
|
||||
.map(|flag| if flag { ModeOptimizerSetting::M3 } else { ModeOptimizerSetting::M0 });
|
||||
let optimize_flag_setting = self.optimize_flag.map(|flag| {
|
||||
if flag {
|
||||
ModeOptimizerSetting::M3
|
||||
} else {
|
||||
ModeOptimizerSetting::M0
|
||||
}
|
||||
});
|
||||
|
||||
let optimize_flag_iter = match optimize_flag_setting {
|
||||
Some(setting) => EitherIter::A(std::iter::once(setting)),
|
||||
None => EitherIter::B(ModeOptimizerSetting::test_cases()),
|
||||
};
|
||||
let optimize_flag_iter = match optimize_flag_setting {
|
||||
Some(setting) => EitherIter::A(std::iter::once(setting)),
|
||||
None => EitherIter::B(ModeOptimizerSetting::test_cases()),
|
||||
};
|
||||
|
||||
let optimize_settings_iter = self.optimize_setting.as_ref().map_or_else(
|
||||
|| EitherIter::A(optimize_flag_iter),
|
||||
|s| EitherIter::B(std::iter::once(*s)),
|
||||
);
|
||||
let optimize_settings_iter = self.optimize_setting.as_ref().map_or_else(
|
||||
|| EitherIter::A(optimize_flag_iter),
|
||||
|s| EitherIter::B(std::iter::once(*s)),
|
||||
);
|
||||
|
||||
pipeline_iter.flat_map(move |pipeline| {
|
||||
optimize_settings_iter.clone().map(move |optimize_setting| Mode {
|
||||
pipeline,
|
||||
optimize_setting,
|
||||
version: self.version.clone(),
|
||||
})
|
||||
})
|
||||
}
|
||||
pipeline_iter.flat_map(move |pipeline| {
|
||||
optimize_settings_iter
|
||||
.clone()
|
||||
.map(move |optimize_setting| Mode {
|
||||
pipeline,
|
||||
optimize_setting,
|
||||
version: self.version.clone(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Return a set of [`Mode`]s that correspond to the given [`ParsedMode`]s.
|
||||
/// This avoids any duplicate entries.
|
||||
pub fn many_to_modes<'a>(
|
||||
parsed: impl Iterator<Item = &'a ParsedMode>,
|
||||
) -> impl Iterator<Item = Mode> {
|
||||
let modes: HashSet<_> = parsed.flat_map(|p| p.to_modes()).collect();
|
||||
modes.into_iter()
|
||||
}
|
||||
/// Return a set of [`Mode`]s that correspond to the given [`ParsedMode`]s.
|
||||
/// This avoids any duplicate entries.
|
||||
pub fn many_to_modes<'a>(
|
||||
parsed: impl Iterator<Item = &'a ParsedMode>,
|
||||
) -> impl Iterator<Item = Mode> {
|
||||
let modes: HashSet<_> = parsed.flat_map(|p| p.to_modes()).collect();
|
||||
modes.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parsed_mode_from_str() {
|
||||
let strings = vec![
|
||||
("Mz", "Mz"),
|
||||
("Y", "Y"),
|
||||
("Y+", "Y+"),
|
||||
("Y-", "Y-"),
|
||||
("E", "E"),
|
||||
("E+", "E+"),
|
||||
("E-", "E-"),
|
||||
("Y M0", "Y M0"),
|
||||
("Y M1", "Y M1"),
|
||||
("Y M2", "Y M2"),
|
||||
("Y M3", "Y M3"),
|
||||
("Y Ms", "Y Ms"),
|
||||
("Y Mz", "Y Mz"),
|
||||
("E M0", "E M0"),
|
||||
("E M1", "E M1"),
|
||||
("E M2", "E M2"),
|
||||
("E M3", "E M3"),
|
||||
("E Ms", "E Ms"),
|
||||
("E Mz", "E Mz"),
|
||||
// When stringifying semver again, 0.8.0 becomes ^0.8.0 (same meaning)
|
||||
("Y 0.8.0", "Y ^0.8.0"),
|
||||
("E+ 0.8.0", "E+ ^0.8.0"),
|
||||
("Y M3 >=0.8.0", "Y M3 >=0.8.0"),
|
||||
("E Mz <0.7.0", "E Mz <0.7.0"),
|
||||
// We can parse +- _and_ M1/M2 but the latter takes priority.
|
||||
("Y+ M1 0.8.0", "Y+ M1 ^0.8.0"),
|
||||
("E- M2 0.7.0", "E- M2 ^0.7.0"),
|
||||
// We don't see this in the wild but it is parsed.
|
||||
("<=0.8", "<=0.8"),
|
||||
];
|
||||
#[test]
|
||||
fn test_parsed_mode_from_str() {
|
||||
let strings = vec![
|
||||
("Mz", "Mz"),
|
||||
("Y", "Y"),
|
||||
("Y+", "Y+"),
|
||||
("Y-", "Y-"),
|
||||
("E", "E"),
|
||||
("E+", "E+"),
|
||||
("E-", "E-"),
|
||||
("Y M0", "Y M0"),
|
||||
("Y M1", "Y M1"),
|
||||
("Y M2", "Y M2"),
|
||||
("Y M3", "Y M3"),
|
||||
("Y Ms", "Y Ms"),
|
||||
("Y Mz", "Y Mz"),
|
||||
("E M0", "E M0"),
|
||||
("E M1", "E M1"),
|
||||
("E M2", "E M2"),
|
||||
("E M3", "E M3"),
|
||||
("E Ms", "E Ms"),
|
||||
("E Mz", "E Mz"),
|
||||
// When stringifying semver again, 0.8.0 becomes ^0.8.0 (same meaning)
|
||||
("Y 0.8.0", "Y ^0.8.0"),
|
||||
("E+ 0.8.0", "E+ ^0.8.0"),
|
||||
("Y M3 >=0.8.0", "Y M3 >=0.8.0"),
|
||||
("E Mz <0.7.0", "E Mz <0.7.0"),
|
||||
// We can parse +- _and_ M1/M2 but the latter takes priority.
|
||||
("Y+ M1 0.8.0", "Y+ M1 ^0.8.0"),
|
||||
("E- M2 0.7.0", "E- M2 ^0.7.0"),
|
||||
// We don't see this in the wild but it is parsed.
|
||||
("<=0.8", "<=0.8"),
|
||||
];
|
||||
|
||||
for (actual, expected) in strings {
|
||||
let parsed = ParsedMode::from_str(actual)
|
||||
.unwrap_or_else(|_| panic!("Failed to parse mode string '{actual}'"));
|
||||
assert_eq!(
|
||||
expected,
|
||||
parsed.to_string(),
|
||||
"Mode string '{actual}' did not parse to '{expected}': got '{parsed}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
for (actual, expected) in strings {
|
||||
let parsed = ParsedMode::from_str(actual)
|
||||
.unwrap_or_else(|_| panic!("Failed to parse mode string '{actual}'"));
|
||||
assert_eq!(
|
||||
expected,
|
||||
parsed.to_string(),
|
||||
"Mode string '{actual}' did not parse to '{expected}': got '{parsed}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parsed_mode_to_test_modes() {
|
||||
let strings = vec![
|
||||
("Mz", vec!["Y Mz", "E Mz"]),
|
||||
("Y", vec!["Y M0", "Y M3"]),
|
||||
("E", vec!["E M0", "E M3"]),
|
||||
("Y+", vec!["Y M3"]),
|
||||
("Y-", vec!["Y M0"]),
|
||||
("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]),
|
||||
("<=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"]),
|
||||
];
|
||||
#[test]
|
||||
fn test_parsed_mode_to_test_modes() {
|
||||
let strings = vec![
|
||||
("Mz", vec!["Y Mz", "E Mz"]),
|
||||
("Y", vec!["Y M0", "Y M3"]),
|
||||
("E", vec!["E M0", "E M3"]),
|
||||
("Y+", vec!["Y M3"]),
|
||||
("Y-", vec!["Y M0"]),
|
||||
("Y <=0.8", vec!["Y M0 <=0.8", "Y M3 <=0.8"]),
|
||||
(
|
||||
"<=0.8",
|
||||
vec!["Y M0 <=0.8", "Y M3 <=0.8", "E M0 <=0.8", "E M3 <=0.8"],
|
||||
),
|
||||
];
|
||||
|
||||
for (actual, expected) in strings {
|
||||
let parsed = ParsedMode::from_str(actual)
|
||||
.unwrap_or_else(|_| panic!("Failed to parse mode string '{actual}'"));
|
||||
let expected_set: HashSet<_> = expected.into_iter().map(|s| s.to_owned()).collect();
|
||||
let actual_set: HashSet<_> = parsed.to_modes().map(|m| m.to_string()).collect();
|
||||
for (actual, expected) in strings {
|
||||
let parsed = ParsedMode::from_str(actual)
|
||||
.unwrap_or_else(|_| panic!("Failed to parse mode string '{actual}'"));
|
||||
let expected_set: HashSet<_> = expected.into_iter().map(|s| s.to_owned()).collect();
|
||||
let actual_set: HashSet<_> = parsed.to_modes().map(|m| m.to_string()).collect();
|
||||
|
||||
assert_eq!(
|
||||
expected_set, actual_set,
|
||||
"Mode string '{actual}' did not expand to '{expected_set:?}': got '{actual_set:?}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
assert_eq!(
|
||||
expected_set, actual_set,
|
||||
"Mode string '{actual}' did not expand to '{expected_set:?}': got '{actual_set:?}'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1061
-987
File diff suppressed because it is too large
Load Diff
+133
-132
@@ -1,9 +1,9 @@
|
||||
use std::{collections::HashMap, pin::Pin};
|
||||
|
||||
use alloy::{
|
||||
eips::BlockNumberOrTag,
|
||||
json_abi::JsonAbi,
|
||||
primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, U256},
|
||||
eips::BlockNumberOrTag,
|
||||
json_abi::JsonAbi,
|
||||
primitives::{Address, BlockHash, BlockNumber, BlockTimestamp, ChainId, TxHash, U256},
|
||||
};
|
||||
use anyhow::Result;
|
||||
|
||||
@@ -12,164 +12,165 @@ use crate::metadata::{ContractIdent, ContractInstance};
|
||||
/// A trait of the interface are required to implement to be used by the resolution logic that this
|
||||
/// crate implements to go from string calldata and into the bytes calldata.
|
||||
pub trait ResolverApi {
|
||||
/// Returns the ID of the chain that the node is on.
|
||||
fn chain_id(&self) -> Pin<Box<dyn Future<Output = Result<ChainId>> + '_>>;
|
||||
/// Returns the ID of the chain that the node is on.
|
||||
fn chain_id(&self) -> Pin<Box<dyn Future<Output = Result<ChainId>> + '_>>;
|
||||
|
||||
/// Returns the gas price for the specified transaction.
|
||||
fn transaction_gas_price(
|
||||
&self,
|
||||
tx_hash: TxHash,
|
||||
) -> Pin<Box<dyn Future<Output = Result<u128>> + '_>>;
|
||||
/// Returns the gas price for the specified transaction.
|
||||
fn transaction_gas_price(
|
||||
&self,
|
||||
tx_hash: TxHash,
|
||||
) -> Pin<Box<dyn Future<Output = Result<u128>> + '_>>;
|
||||
|
||||
// TODO: This is currently a u128 due to substrate needing more than 64 bits for its gas limit
|
||||
// when we implement the changes to the gas we need to adjust this to be a u64.
|
||||
/// Returns the gas limit of the specified block.
|
||||
fn block_gas_limit(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<u128>> + '_>>;
|
||||
// TODO: This is currently a u128 due to substrate needing more than 64 bits for its gas limit
|
||||
// when we implement the changes to the gas we need to adjust this to be a u64.
|
||||
/// Returns the gas limit of the specified block.
|
||||
fn block_gas_limit(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<u128>> + '_>>;
|
||||
|
||||
/// Returns the coinbase of the specified block.
|
||||
fn block_coinbase(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Address>> + '_>>;
|
||||
/// Returns the coinbase of the specified block.
|
||||
fn block_coinbase(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<Address>> + '_>>;
|
||||
|
||||
/// Returns the difficulty of the specified block.
|
||||
fn block_difficulty(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<U256>> + '_>>;
|
||||
/// Returns the difficulty of the specified block.
|
||||
fn block_difficulty(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<U256>> + '_>>;
|
||||
|
||||
/// Returns the base fee of the specified block.
|
||||
fn block_base_fee(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<u64>> + '_>>;
|
||||
/// Returns the base fee of the specified block.
|
||||
fn block_base_fee(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<u64>> + '_>>;
|
||||
|
||||
/// Returns the hash of the specified block.
|
||||
fn block_hash(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<BlockHash>> + '_>>;
|
||||
/// Returns the hash of the specified block.
|
||||
fn block_hash(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<BlockHash>> + '_>>;
|
||||
|
||||
/// Returns the timestamp of the specified block,
|
||||
fn block_timestamp(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<BlockTimestamp>> + '_>>;
|
||||
/// Returns the timestamp of the specified block,
|
||||
fn block_timestamp(
|
||||
&self,
|
||||
number: BlockNumberOrTag,
|
||||
) -> Pin<Box<dyn Future<Output = Result<BlockTimestamp>> + '_>>;
|
||||
|
||||
/// Returns the number of the last block.
|
||||
fn last_block_number(&self) -> Pin<Box<dyn Future<Output = Result<BlockNumber>> + '_>>;
|
||||
/// Returns the number of the last block.
|
||||
fn last_block_number(&self) -> Pin<Box<dyn Future<Output = Result<BlockNumber>> + '_>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
/// Contextual information required by the code that's performing the resolution.
|
||||
pub struct ResolutionContext<'a> {
|
||||
/// When provided the contracts provided here will be used for resolutions.
|
||||
deployed_contracts: Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
/// When provided the contracts provided here will be used for resolutions.
|
||||
deployed_contracts: Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
|
||||
/// When provided the variables in here will be used for performing resolutions.
|
||||
variables: Option<&'a HashMap<String, U256>>,
|
||||
/// When provided the variables in here will be used for performing resolutions.
|
||||
variables: Option<&'a HashMap<String, U256>>,
|
||||
|
||||
/// When provided this block number will be treated as the tip of the chain.
|
||||
block_number: Option<&'a BlockNumber>,
|
||||
/// When provided this block number will be treated as the tip of the chain.
|
||||
block_number: Option<&'a BlockNumber>,
|
||||
|
||||
/// When provided the resolver will use this transaction hash for all of its resolutions.
|
||||
transaction_hash: Option<&'a TxHash>,
|
||||
/// When provided the resolver will use this transaction hash for all of its resolutions.
|
||||
transaction_hash: Option<&'a TxHash>,
|
||||
}
|
||||
|
||||
impl<'a> ResolutionContext<'a> {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn new_from_parts(
|
||||
deployed_contracts: impl Into<
|
||||
Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
>,
|
||||
variables: impl Into<Option<&'a HashMap<String, U256>>>,
|
||||
block_number: impl Into<Option<&'a BlockNumber>>,
|
||||
transaction_hash: impl Into<Option<&'a TxHash>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
deployed_contracts: deployed_contracts.into(),
|
||||
variables: variables.into(),
|
||||
block_number: block_number.into(),
|
||||
transaction_hash: transaction_hash.into(),
|
||||
}
|
||||
}
|
||||
pub fn new_from_parts(
|
||||
deployed_contracts: impl Into<
|
||||
Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
>,
|
||||
variables: impl Into<Option<&'a HashMap<String, U256>>>,
|
||||
block_number: impl Into<Option<&'a BlockNumber>>,
|
||||
transaction_hash: impl Into<Option<&'a TxHash>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
deployed_contracts: deployed_contracts.into(),
|
||||
variables: variables.into(),
|
||||
block_number: block_number.into(),
|
||||
transaction_hash: transaction_hash.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_deployed_contracts(
|
||||
mut self,
|
||||
deployed_contracts: impl Into<
|
||||
Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
>,
|
||||
) -> Self {
|
||||
self.deployed_contracts = deployed_contracts.into();
|
||||
self
|
||||
}
|
||||
pub fn with_deployed_contracts(
|
||||
mut self,
|
||||
deployed_contracts: impl Into<
|
||||
Option<&'a HashMap<ContractInstance, (ContractIdent, Address, JsonAbi)>>,
|
||||
>,
|
||||
) -> Self {
|
||||
self.deployed_contracts = deployed_contracts.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_variables(
|
||||
mut self,
|
||||
variables: impl Into<Option<&'a HashMap<String, U256>>>,
|
||||
) -> Self {
|
||||
self.variables = variables.into();
|
||||
self
|
||||
}
|
||||
pub fn with_variables(
|
||||
mut self,
|
||||
variables: impl Into<Option<&'a HashMap<String, U256>>>,
|
||||
) -> Self {
|
||||
self.variables = variables.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_block_number(mut self, block_number: impl Into<Option<&'a BlockNumber>>) -> Self {
|
||||
self.block_number = block_number.into();
|
||||
self
|
||||
}
|
||||
pub fn with_block_number(mut self, block_number: impl Into<Option<&'a BlockNumber>>) -> Self {
|
||||
self.block_number = block_number.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_transaction_hash(
|
||||
mut self,
|
||||
transaction_hash: impl Into<Option<&'a TxHash>>,
|
||||
) -> Self {
|
||||
self.transaction_hash = transaction_hash.into();
|
||||
self
|
||||
}
|
||||
pub fn with_transaction_hash(
|
||||
mut self,
|
||||
transaction_hash: impl Into<Option<&'a TxHash>>,
|
||||
) -> Self {
|
||||
self.transaction_hash = transaction_hash.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn resolve_block_number(&self, number: BlockNumberOrTag) -> BlockNumberOrTag {
|
||||
match self.block_number {
|
||||
Some(block_number) => match number {
|
||||
BlockNumberOrTag::Latest => BlockNumberOrTag::Number(*block_number),
|
||||
n @ (BlockNumberOrTag::Finalized |
|
||||
BlockNumberOrTag::Safe |
|
||||
BlockNumberOrTag::Earliest |
|
||||
BlockNumberOrTag::Pending |
|
||||
BlockNumberOrTag::Number(_)) => n,
|
||||
},
|
||||
None => number,
|
||||
}
|
||||
}
|
||||
pub fn resolve_block_number(&self, number: BlockNumberOrTag) -> BlockNumberOrTag {
|
||||
match self.block_number {
|
||||
Some(block_number) => match number {
|
||||
BlockNumberOrTag::Latest => BlockNumberOrTag::Number(*block_number),
|
||||
n @ (BlockNumberOrTag::Finalized
|
||||
| BlockNumberOrTag::Safe
|
||||
| BlockNumberOrTag::Earliest
|
||||
| BlockNumberOrTag::Pending
|
||||
| BlockNumberOrTag::Number(_)) => n,
|
||||
},
|
||||
None => number,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deployed_contract(
|
||||
&self,
|
||||
instance: &ContractInstance,
|
||||
) -> Option<&(ContractIdent, Address, JsonAbi)> {
|
||||
self.deployed_contracts
|
||||
.and_then(|deployed_contracts| deployed_contracts.get(instance))
|
||||
}
|
||||
pub fn deployed_contract(
|
||||
&self,
|
||||
instance: &ContractInstance,
|
||||
) -> Option<&(ContractIdent, Address, JsonAbi)> {
|
||||
self.deployed_contracts
|
||||
.and_then(|deployed_contracts| deployed_contracts.get(instance))
|
||||
}
|
||||
|
||||
pub fn deployed_contract_address(&self, instance: &ContractInstance) -> Option<&Address> {
|
||||
self.deployed_contract(instance).map(|(_, a, _)| a)
|
||||
}
|
||||
pub fn deployed_contract_address(&self, instance: &ContractInstance) -> Option<&Address> {
|
||||
self.deployed_contract(instance).map(|(_, a, _)| a)
|
||||
}
|
||||
|
||||
pub fn deployed_contract_abi(&self, instance: &ContractInstance) -> Option<&JsonAbi> {
|
||||
self.deployed_contract(instance).map(|(_, _, a)| a)
|
||||
}
|
||||
pub fn deployed_contract_abi(&self, instance: &ContractInstance) -> Option<&JsonAbi> {
|
||||
self.deployed_contract(instance).map(|(_, _, a)| a)
|
||||
}
|
||||
|
||||
pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> {
|
||||
self.variables.and_then(|variables| variables.get(name.as_ref()))
|
||||
}
|
||||
pub fn variable(&self, name: impl AsRef<str>) -> Option<&U256> {
|
||||
self.variables
|
||||
.and_then(|variables| variables.get(name.as_ref()))
|
||||
}
|
||||
|
||||
pub fn tip_block_number(&self) -> Option<&'a BlockNumber> {
|
||||
self.block_number
|
||||
}
|
||||
pub fn tip_block_number(&self) -> Option<&'a BlockNumber> {
|
||||
self.block_number
|
||||
}
|
||||
|
||||
pub fn transaction_hash(&self) -> Option<&'a TxHash> {
|
||||
self.transaction_hash
|
||||
}
|
||||
pub fn transaction_hash(&self) -> Option<&'a TxHash> {
|
||||
self.transaction_hash
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user