mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-04-23 00:17:57 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e7ebe4fa2f | |||
| 5c957e5ac1 |
@@ -26,6 +26,7 @@ use revive_dt_format::case::CaseIdx;
|
|||||||
use revive_dt_format::input::{Calldata, Expected, ExpectedOutput, Method};
|
use revive_dt_format::input::{Calldata, Expected, ExpectedOutput, Method};
|
||||||
use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdentifier};
|
use revive_dt_format::metadata::{ContractInstance, ContractPathAndIdentifier};
|
||||||
use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode};
|
use revive_dt_format::{input::Input, metadata::Metadata, mode::SolcMode};
|
||||||
|
use revive_dt_node::Node;
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
use revive_dt_report::reporter::{CompilationTask, Report, Span};
|
use revive_dt_report::reporter::{CompilationTask, Report, Span};
|
||||||
use revive_solc_json_interface::SolcStandardJsonOutput;
|
use revive_solc_json_interface::SolcStandardJsonOutput;
|
||||||
@@ -250,6 +251,12 @@ where
|
|||||||
|
|
||||||
let tx = {
|
let tx = {
|
||||||
let tx = TransactionRequest::default().from(input.caller);
|
let tx = TransactionRequest::default().from(input.caller);
|
||||||
|
let tx = match input.value {
|
||||||
|
Some(ref value) if deploy_with_constructor_arguments => {
|
||||||
|
tx.value(value.into_inner())
|
||||||
|
}
|
||||||
|
_ => tx,
|
||||||
|
};
|
||||||
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
|
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -435,16 +442,15 @@ where
|
|||||||
// Additionally, what happens if the compiler filter doesn't match? Do we consider that the
|
// Additionally, what happens if the compiler filter doesn't match? Do we consider that the
|
||||||
// transaction should succeed? Do we just ignore the expectation?
|
// transaction should succeed? Do we just ignore the expectation?
|
||||||
|
|
||||||
|
let error_span =
|
||||||
|
tracing::error_span!("Exception failed", ?tracing_result, ?execution_receipt,);
|
||||||
|
let _guard = error_span.enter();
|
||||||
|
|
||||||
// Handling the receipt state assertion.
|
// Handling the receipt state assertion.
|
||||||
let expected = !expectation.exception;
|
let expected = !expectation.exception;
|
||||||
let actual = execution_receipt.status();
|
let actual = execution_receipt.status();
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
tracing::error!(
|
tracing::error!(expected, actual, "Transaction status assertion failed",);
|
||||||
?execution_receipt,
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
"Transaction status assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"Transaction status assertion failed - Expected {expected} but got {actual}",
|
"Transaction status assertion failed - Expected {expected} but got {actual}",
|
||||||
);
|
);
|
||||||
@@ -457,7 +463,11 @@ where
|
|||||||
.map(Bytes::from)?;
|
.map(Bytes::from)?;
|
||||||
let actual = tracing_result.output.clone().unwrap_or_default();
|
let actual = tracing_result.output.clone().unwrap_or_default();
|
||||||
if !expected.starts_with(&actual) {
|
if !expected.starts_with(&actual) {
|
||||||
tracing::error!(?execution_receipt, %expected, %actual, "Calldata assertion failed");
|
tracing::error!(
|
||||||
|
%expected,
|
||||||
|
%actual,
|
||||||
|
"Calldata assertion failed"
|
||||||
|
);
|
||||||
anyhow::bail!("Calldata assertion failed - Expected {expected} but got {actual}",);
|
anyhow::bail!("Calldata assertion failed - Expected {expected} but got {actual}",);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -468,12 +478,7 @@ where
|
|||||||
let expected = expected_events.len();
|
let expected = expected_events.len();
|
||||||
let actual = execution_receipt.logs().len();
|
let actual = execution_receipt.logs().len();
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
tracing::error!(
|
tracing::error!(expected, actual, "Event count assertion failed",);
|
||||||
?execution_receipt,
|
|
||||||
expected,
|
|
||||||
actual,
|
|
||||||
"Event count assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"Event count assertion failed - Expected {expected} but got {actual}",
|
"Event count assertion failed - Expected {expected} but got {actual}",
|
||||||
);
|
);
|
||||||
@@ -489,7 +494,6 @@ where
|
|||||||
let actual = actual_event.address();
|
let actual = actual_event.address();
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
tracing::error!(
|
tracing::error!(
|
||||||
?execution_receipt,
|
|
||||||
%expected,
|
%expected,
|
||||||
%actual,
|
%actual,
|
||||||
"Event emitter assertion failed",
|
"Event emitter assertion failed",
|
||||||
@@ -511,12 +515,7 @@ where
|
|||||||
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)?;
|
.calldata(self.deployed_contracts.entry(case_idx).or_default(), node)?;
|
||||||
let actual = actual_topic.to_vec();
|
let actual = actual_topic.to_vec();
|
||||||
if actual != expected {
|
if actual != expected {
|
||||||
tracing::error!(
|
tracing::error!(?expected, ?actual, "Event topics assertion failed",);
|
||||||
?execution_receipt,
|
|
||||||
?expected,
|
|
||||||
?actual,
|
|
||||||
"Event topics assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"Event topics assertion failed - Expected {expected:?} but got {actual:?}",
|
"Event topics assertion failed - Expected {expected:?} but got {actual:?}",
|
||||||
);
|
);
|
||||||
@@ -530,12 +529,7 @@ where
|
|||||||
.map(Bytes::from)?;
|
.map(Bytes::from)?;
|
||||||
let actual = &actual_event.data().data;
|
let actual = &actual_event.data().data;
|
||||||
if !expected.starts_with(actual) {
|
if !expected.starts_with(actual) {
|
||||||
tracing::error!(
|
tracing::error!(?expected, ?actual, "Event value assertion failed",);
|
||||||
?execution_receipt,
|
|
||||||
?expected,
|
|
||||||
?actual,
|
|
||||||
"Event value assertion failed",
|
|
||||||
);
|
|
||||||
anyhow::bail!(
|
anyhow::bail!(
|
||||||
"Event value assertion failed - Expected {expected:?} but got {actual:?}",
|
"Event value assertion failed - Expected {expected:?} but got {actual:?}",
|
||||||
);
|
);
|
||||||
@@ -649,6 +643,22 @@ where
|
|||||||
let tracing_span = tracing::info_span!("Handling metadata file");
|
let tracing_span = tracing::info_span!("Handling metadata file");
|
||||||
let _guard = tracing_span.enter();
|
let _guard = tracing_span.enter();
|
||||||
|
|
||||||
|
// We only execute this input if it's valid for the leader and the follower. Otherwise, we
|
||||||
|
// skip it with a warning.
|
||||||
|
if !self
|
||||||
|
.leader_node
|
||||||
|
.matches_target(self.metadata.targets.as_deref())
|
||||||
|
|| !self
|
||||||
|
.follower_node
|
||||||
|
.matches_target(self.metadata.targets.as_deref())
|
||||||
|
{
|
||||||
|
tracing::warn!(
|
||||||
|
targets = ?self.metadata.targets,
|
||||||
|
"Either the leader or follower node do not support the targets of the file"
|
||||||
|
);
|
||||||
|
return execution_result;
|
||||||
|
}
|
||||||
|
|
||||||
for mode in self.metadata.solc_modes() {
|
for mode in self.metadata.solc_modes() {
|
||||||
let tracing_span = tracing::info_span!("With solc mode", solc_mode = ?mode);
|
let tracing_span = tracing::info_span!("With solc mode", solc_mode = ?mode);
|
||||||
let _guard = tracing_span.enter();
|
let _guard = tracing_span.enter();
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
|
use revive_dt_compiler::{SolidityCompiler, revive_resolc, solc};
|
||||||
use revive_dt_config::TestingPlatform;
|
use revive_dt_config::TestingPlatform;
|
||||||
use revive_dt_node::{geth, kitchensink::KitchensinkNode};
|
use revive_dt_node::{Node, geth, kitchensink::KitchensinkNode};
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
|
|
||||||
pub mod common;
|
pub mod common;
|
||||||
@@ -15,7 +15,7 @@ pub mod driver;
|
|||||||
///
|
///
|
||||||
/// For this we need a blockchain node implementation and a compiler.
|
/// For this we need a blockchain node implementation and a compiler.
|
||||||
pub trait Platform {
|
pub trait Platform {
|
||||||
type Blockchain: EthereumNode;
|
type Blockchain: EthereumNode + Node;
|
||||||
type Compiler: SolidityCompiler;
|
type Compiler: SolidityCompiler;
|
||||||
|
|
||||||
/// Returns the matching [TestingPlatform] of the [revive_dt_config::Arguments].
|
/// Returns the matching [TestingPlatform] of the [revive_dt_config::Arguments].
|
||||||
|
|||||||
@@ -26,7 +26,15 @@ impl Case {
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(idx, mut input)| {
|
.map(move |(idx, mut input)| {
|
||||||
if idx + 1 == inputs_len {
|
if idx + 1 == inputs_len {
|
||||||
|
if input.expected.is_none() {
|
||||||
input.expected = self.expected.clone();
|
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?
|
||||||
|
|
||||||
input
|
input
|
||||||
} else {
|
} else {
|
||||||
input
|
input
|
||||||
|
|||||||
@@ -7,13 +7,16 @@ use alloy::{
|
|||||||
primitives::{Address, Bytes, U256},
|
primitives::{Address, Bytes, U256},
|
||||||
rpc::types::TransactionRequest,
|
rpc::types::TransactionRequest,
|
||||||
};
|
};
|
||||||
use alloy_primitives::FixedBytes;
|
use alloy_primitives::{FixedBytes, utils::parse_units};
|
||||||
use semver::VersionReq;
|
use semver::VersionReq;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use revive_dt_node_interaction::EthereumNode;
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
|
|
||||||
use crate::metadata::{AddressReplacementMap, ContractInstance};
|
use crate::{
|
||||||
|
define_wrapper_type,
|
||||||
|
metadata::{AddressReplacementMap, ContractInstance},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
@@ -26,7 +29,7 @@ pub struct Input {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub calldata: Calldata,
|
pub calldata: Calldata,
|
||||||
pub expected: Option<Expected>,
|
pub expected: Option<Expected>,
|
||||||
pub value: Option<String>,
|
pub value: Option<EtherValue>,
|
||||||
pub storage: Option<HashMap<String, Calldata>>,
|
pub storage: Option<HashMap<String, Calldata>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +85,37 @@ pub enum Method {
|
|||||||
FunctionName(String),
|
FunctionName(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
define_wrapper_type!(
|
||||||
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
EtherValue(U256);
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Serialize for EtherValue {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
format!("{} wei", self.0).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for EtherValue {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let string = String::deserialize(deserializer)?;
|
||||||
|
let mut splitted = string.split(' ');
|
||||||
|
let (Some(value), Some(unit)) = (splitted.next(), splitted.next()) else {
|
||||||
|
return Err(serde::de::Error::custom("Failed to parse the value"));
|
||||||
|
};
|
||||||
|
let parsed = parse_units(value, unit.replace("eth", "ether"))
|
||||||
|
.map_err(|_| serde::de::Error::custom("Failed to parse units"))?
|
||||||
|
.into();
|
||||||
|
Ok(Self(parsed))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ExpectedOutput {
|
impl ExpectedOutput {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Default::default()
|
Default::default()
|
||||||
@@ -324,7 +358,11 @@ impl Input {
|
|||||||
chain_state_provider: &impl EthereumNode,
|
chain_state_provider: &impl EthereumNode,
|
||||||
) -> anyhow::Result<TransactionRequest> {
|
) -> anyhow::Result<TransactionRequest> {
|
||||||
let input_data = self.encoded_input(deployed_contracts, chain_state_provider)?;
|
let input_data = self.encoded_input(deployed_contracts, chain_state_provider)?;
|
||||||
let transaction_request = TransactionRequest::default();
|
let transaction_request = TransactionRequest::default().from(self.caller).value(
|
||||||
|
self.value
|
||||||
|
.map(|value| value.into_inner())
|
||||||
|
.unwrap_or_default(),
|
||||||
|
);
|
||||||
match self.method {
|
match self.method {
|
||||||
Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)),
|
Method::Deployer => Ok(transaction_request.with_deploy_code(input_data)),
|
||||||
_ => Ok(transaction_request
|
_ => Ok(transaction_request
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ impl Deref for MetadataFile {
|
|||||||
|
|
||||||
#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)]
|
#[derive(Debug, Default, Deserialize, Clone, Eq, PartialEq)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
|
pub targets: Option<Vec<String>>,
|
||||||
pub cases: Vec<Case>,
|
pub cases: Vec<Case>,
|
||||||
pub contracts: Option<BTreeMap<ContractInstance, ContractPathAndIdentifier>>,
|
pub contracts: Option<BTreeMap<ContractInstance, ContractPathAndIdentifier>>,
|
||||||
// TODO: Convert into wrapper types for clarity.
|
// TODO: Convert into wrapper types for clarity.
|
||||||
|
|||||||
+10
-1
@@ -85,7 +85,8 @@ impl Instance {
|
|||||||
<EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet)
|
<EthereumWallet as NetworkWallet<Ethereum>>::signer_addresses(&self.wallet)
|
||||||
{
|
{
|
||||||
genesis.alloc.entry(signer_address).or_insert(
|
genesis.alloc.entry(signer_address).or_insert(
|
||||||
GenesisAccount::default().with_balance(1000000000000000000u128.try_into().unwrap()),
|
GenesisAccount::default()
|
||||||
|
.with_balance(10000000000000000000000u128.try_into().unwrap()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let genesis_path = self.base_directory.join(Self::GENESIS_JSON_FILE);
|
let genesis_path = self.base_directory.join(Self::GENESIS_JSON_FILE);
|
||||||
@@ -510,6 +511,14 @@ impl Node for Instance {
|
|||||||
.stdout;
|
.stdout;
|
||||||
Ok(String::from_utf8_lossy(&output).into())
|
Ok(String::from_utf8_lossy(&output).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(geth_node_id = self.id))]
|
||||||
|
fn matches_target(&self, targets: Option<&[String]>) -> bool {
|
||||||
|
match targets {
|
||||||
|
None => true,
|
||||||
|
Some(targets) => targets.iter().any(|str| str.as_str() == "evm"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Instance {
|
impl Drop for Instance {
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ impl KitchensinkNode {
|
|||||||
{
|
{
|
||||||
genesis.alloc.entry(signer_address).or_insert(
|
genesis.alloc.entry(signer_address).or_insert(
|
||||||
GenesisAccount::default()
|
GenesisAccount::default()
|
||||||
.with_balance(1000000000000000000u128.try_into().unwrap()),
|
.with_balance(10000000000000000000000u128.try_into().unwrap()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
self.extract_balance_from_genesis_file(&genesis)?
|
self.extract_balance_from_genesis_file(&genesis)?
|
||||||
@@ -584,6 +584,14 @@ impl Node for KitchensinkNode {
|
|||||||
.stdout;
|
.stdout;
|
||||||
Ok(String::from_utf8_lossy(&output).into())
|
Ok(String::from_utf8_lossy(&output).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all, fields(kitchensink_node_id = self.id))]
|
||||||
|
fn matches_target(&self, targets: Option<&[String]>) -> bool {
|
||||||
|
match targets {
|
||||||
|
None => true,
|
||||||
|
Some(targets) => targets.iter().any(|str| str.as_str() == "pvm"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for KitchensinkNode {
|
impl Drop for KitchensinkNode {
|
||||||
|
|||||||
@@ -35,4 +35,8 @@ pub trait Node: EthereumNode {
|
|||||||
|
|
||||||
/// Returns the node version.
|
/// Returns the node version.
|
||||||
fn version(&self) -> anyhow::Result<String>;
|
fn version(&self) -> anyhow::Result<String>;
|
||||||
|
|
||||||
|
/// Given a list of targets from the metadata file, this function determines if the metadata
|
||||||
|
/// file can be ran on this node or not.
|
||||||
|
fn matches_target(&self, targets: Option<&[String]>) -> bool;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@
|
|||||||
"timestamp": "0x00",
|
"timestamp": "0x00",
|
||||||
"alloc": {
|
"alloc": {
|
||||||
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1": {
|
"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1": {
|
||||||
"balance": "1000000000000000000"
|
"balance": "10000000000000000000000"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user