mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-22 20:47:58 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb8b4a100d | |||
| 429f2e92a2 | |||
| 65f41f2038 | |||
| 3ed8a1ca1c |
@@ -10,6 +10,10 @@ use crate::{CompilerInput, CompilerOutput, SolidityCompiler};
|
|||||||
use revive_dt_config::Arguments;
|
use revive_dt_config::Arguments;
|
||||||
use revive_solc_json_interface::SolcStandardJsonOutput;
|
use revive_solc_json_interface::SolcStandardJsonOutput;
|
||||||
|
|
||||||
|
// TODO: I believe that we need to also pass the solc compiler to resolc so that resolc uses the
|
||||||
|
// specified solc compiler. I believe that currently we completely ignore the specified solc binary
|
||||||
|
// when invoking resolc which doesn't seem right if we're using solc as a compiler frontend.
|
||||||
|
|
||||||
/// A wrapper around the `resolc` binary, emitting PVM-compatible bytecode.
|
/// A wrapper around the `resolc` binary, emitting PVM-compatible bytecode.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Resolc {
|
pub struct Resolc {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use alloy::json_abi::JsonAbi;
|
use alloy::json_abi::JsonAbi;
|
||||||
use alloy::network::{Ethereum, TransactionBuilder};
|
use alloy::network::{Ethereum, TransactionBuilder};
|
||||||
@@ -100,12 +101,30 @@ where
|
|||||||
anyhow::bail!("unsupported solc version: {:?}", &mode.solc_version);
|
anyhow::bail!("unsupported solc version: {:?}", &mode.solc_version);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Note: if the metadata is contained within a solidity file then this is the only file that
|
||||||
|
// we wish to compile since this is a self-contained test. Otherwise, if it's a JSON file
|
||||||
|
// then we need to compile all of the contracts that are in the directory since imports are
|
||||||
|
// allowed in there.
|
||||||
|
let Some(ref metadata_file_path) = metadata.file_path else {
|
||||||
|
anyhow::bail!("The metadata file path is not defined");
|
||||||
|
};
|
||||||
|
let mut files_to_compile = if metadata_file_path
|
||||||
|
.extension()
|
||||||
|
.is_some_and(|extension| extension.eq_ignore_ascii_case("sol"))
|
||||||
|
{
|
||||||
|
Box::new(std::iter::once(metadata_file_path.clone())) as Box<dyn Iterator<Item = _>>
|
||||||
|
} else {
|
||||||
|
Box::new(
|
||||||
|
FilesWithExtensionIterator::new(metadata.directory()?)
|
||||||
|
.with_allowed_extension("sol"),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
let compiler = Compiler::<T::Compiler>::new()
|
let compiler = Compiler::<T::Compiler>::new()
|
||||||
.allow_path(metadata.directory()?)
|
.allow_path(metadata.directory()?)
|
||||||
.solc_optimizer(mode.solc_optimize());
|
.solc_optimizer(mode.solc_optimize());
|
||||||
let mut compiler = FilesWithExtensionIterator::new(metadata.directory()?)
|
let mut compiler =
|
||||||
.with_allowed_extension("sol")
|
files_to_compile.try_fold(compiler, |compiler, path| compiler.with_source(&path))?;
|
||||||
.try_fold(compiler, |compiler, path| compiler.with_source(&path))?;
|
|
||||||
for (library_instance, (library_address, _)) in self.deployed_libraries.iter() {
|
for (library_instance, (library_address, _)) in self.deployed_libraries.iter() {
|
||||||
let library_ident = &metadata
|
let library_ident = &metadata
|
||||||
.contracts
|
.contracts
|
||||||
@@ -200,12 +219,13 @@ where
|
|||||||
case_idx: CaseIdx,
|
case_idx: CaseIdx,
|
||||||
input: &Input,
|
input: &Input,
|
||||||
node: &T::Blockchain,
|
node: &T::Blockchain,
|
||||||
|
mode: &SolcMode,
|
||||||
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
) -> anyhow::Result<(TransactionReceipt, GethTrace, DiffMode)> {
|
||||||
let deployment_receipts =
|
let deployment_receipts =
|
||||||
self.handle_contract_deployment(metadata, case_idx, input, node)?;
|
self.handle_contract_deployment(metadata, case_idx, input, node)?;
|
||||||
let execution_receipt =
|
let execution_receipt =
|
||||||
self.handle_input_execution(case_idx, input, deployment_receipts, node)?;
|
self.handle_input_execution(case_idx, input, deployment_receipts, node)?;
|
||||||
self.handle_input_expectations(case_idx, input, &execution_receipt, node)?;
|
self.handle_input_expectations(case_idx, input, &execution_receipt, node, mode)?;
|
||||||
self.handle_input_diff(case_idx, execution_receipt, node)
|
self.handle_input_diff(case_idx, execution_receipt, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,6 +332,7 @@ where
|
|||||||
input: &Input,
|
input: &Input,
|
||||||
execution_receipt: &TransactionReceipt,
|
execution_receipt: &TransactionReceipt,
|
||||||
node: &T::Blockchain,
|
node: &T::Blockchain,
|
||||||
|
mode: &SolcMode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let span = tracing::info_span!("Handling input expectations");
|
let span = tracing::info_span!("Handling input expectations");
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
@@ -367,6 +388,7 @@ where
|
|||||||
node,
|
node,
|
||||||
expectation,
|
expectation,
|
||||||
&tracing_result,
|
&tracing_result,
|
||||||
|
mode,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,11 +402,16 @@ where
|
|||||||
node: &T::Blockchain,
|
node: &T::Blockchain,
|
||||||
expectation: &ExpectedOutput,
|
expectation: &ExpectedOutput,
|
||||||
tracing_result: &CallFrame,
|
tracing_result: &CallFrame,
|
||||||
|
mode: &SolcMode,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
// TODO: We want to respect the compiler version filter on the expected output but would
|
if let Some(ref version_requirement) = expectation.compiler_version {
|
||||||
// require some changes to the interfaces of the compiler and such. So, we add it later.
|
let Some(compiler_version) = mode.last_patch_version(&self.config.solc) else {
|
||||||
// Additionally, what happens if the compiler filter doesn't match? Do we consider that the
|
anyhow::bail!("unsupported solc version: {:?}", &mode.solc_version);
|
||||||
// transaction should succeed? Do we just ignore the expectation?
|
};
|
||||||
|
if !version_requirement.matches(&compiler_version) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let deployed_contracts = self.deployed_contracts(case_idx);
|
let deployed_contracts = self.deployed_contracts(case_idx);
|
||||||
let chain_state_provider = node;
|
let chain_state_provider = node;
|
||||||
@@ -431,8 +458,18 @@ where
|
|||||||
expected_events.iter().zip(execution_receipt.logs())
|
expected_events.iter().zip(execution_receipt.logs())
|
||||||
{
|
{
|
||||||
// Handling the emitter assertion.
|
// Handling the emitter assertion.
|
||||||
if let Some(expected_address) = expected_event.address {
|
if let Some(ref expected_address) = expected_event.address {
|
||||||
let expected = expected_address;
|
let expected = if let Some(contract_instance) = expected_address
|
||||||
|
.strip_suffix(".address")
|
||||||
|
.map(ContractInstance::new)
|
||||||
|
{
|
||||||
|
deployed_contracts
|
||||||
|
.get(&contract_instance)
|
||||||
|
.map(|(address, _)| *address)
|
||||||
|
} else {
|
||||||
|
Address::from_str(expected_address).ok()
|
||||||
|
}
|
||||||
|
.context("Failed to get the address of the event")?;
|
||||||
let actual = actual_event.address();
|
let actual = actual_event.address();
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
@@ -865,8 +902,13 @@ where
|
|||||||
tracing::info_span!("Executing input", contract_name = ?input.instance)
|
tracing::info_span!("Executing input", contract_name = ?input.instance)
|
||||||
.in_scope(|| {
|
.in_scope(|| {
|
||||||
let (leader_receipt, _, leader_diff) = match leader_state
|
let (leader_receipt, _, leader_diff) = match leader_state
|
||||||
.handle_input(self.metadata, case_idx, &input, self.leader_node)
|
.handle_input(
|
||||||
{
|
self.metadata,
|
||||||
|
case_idx,
|
||||||
|
&input,
|
||||||
|
self.leader_node,
|
||||||
|
&mode,
|
||||||
|
) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
@@ -895,6 +937,7 @@ where
|
|||||||
case_idx,
|
case_idx,
|
||||||
&input,
|
&input,
|
||||||
self.follower_node,
|
self.follower_node,
|
||||||
|
&mode,
|
||||||
) {
|
) {
|
||||||
Ok(result) => result,
|
Ok(result) => result,
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
|
|||||||
@@ -17,7 +17,25 @@ impl Corpus {
|
|||||||
/// Try to read and parse the corpus definition file at given `path`.
|
/// Try to read and parse the corpus definition file at given `path`.
|
||||||
pub fn try_from_path(path: &Path) -> anyhow::Result<Self> {
|
pub fn try_from_path(path: &Path) -> anyhow::Result<Self> {
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
Ok(serde_json::from_reader(file)?)
|
let mut corpus: Corpus = serde_json::from_reader(file)?;
|
||||||
|
|
||||||
|
// Ensure that the path mentioned in the corpus is relative to the corpus file.
|
||||||
|
// Canonicalizing also helps make the path in any errors unambiguous.
|
||||||
|
corpus.path = path
|
||||||
|
.parent()
|
||||||
|
.ok_or_else(|| {
|
||||||
|
anyhow::anyhow!("Corpus path '{}' does not point to a file", path.display())
|
||||||
|
})?
|
||||||
|
.canonicalize()
|
||||||
|
.map_err(|error| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"Failed to canonicalize path to corpus '{}': {error}",
|
||||||
|
path.display()
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.join(corpus.path);
|
||||||
|
|
||||||
|
Ok(corpus)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scan the corpus base directory and return all tests found.
|
/// Scan the corpus base directory and return all tests found.
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub struct ExpectedOutput {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
pub address: Option<Address>,
|
pub address: Option<String>,
|
||||||
pub topics: Vec<String>,
|
pub topics: Vec<String>,
|
||||||
pub values: Calldata,
|
pub values: Calldata,
|
||||||
}
|
}
|
||||||
@@ -1010,4 +1010,53 @@ mod tests {
|
|||||||
// Assert
|
// Assert
|
||||||
assert!(resolved.is_err())
|
assert!(resolved.is_err())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_json_can_be_deserialized1() {
|
||||||
|
// Arrange
|
||||||
|
let str = r#"
|
||||||
|
{
|
||||||
|
"return_data": [
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"topics": [],
|
||||||
|
"values": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let expected = serde_json::from_str::<Expected>(str);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expected.expect("Failed to deserialize");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn expected_json_can_be_deserialized2() {
|
||||||
|
// Arrange
|
||||||
|
let str = r#"
|
||||||
|
{
|
||||||
|
"return_data": [
|
||||||
|
"1"
|
||||||
|
],
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"address": "Main.address",
|
||||||
|
"topics": [],
|
||||||
|
"values": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let expected = serde_json::from_str::<Expected>(str);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expected.expect("Failed to deserialize");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ impl Metadata {
|
|||||||
metadata.file_path = Some(path.to_path_buf());
|
metadata.file_path = Some(path.to_path_buf());
|
||||||
metadata.contracts = Some(
|
metadata.contracts = Some(
|
||||||
[(
|
[(
|
||||||
ContractInstance::new("test"),
|
ContractInstance::new("Test"),
|
||||||
ContractPathAndIdent {
|
ContractPathAndIdent {
|
||||||
contract_source_path: path.to_path_buf(),
|
contract_source_path: path.to_path_buf(),
|
||||||
contract_ident: ContractIdent::new("Test"),
|
contract_ident: ContractIdent::new("Test"),
|
||||||
|
|||||||
Reference in New Issue
Block a user