mirror of
https://github.com/pezkuwichain/revive-differential-tests.git
synced 2026-06-12 13:31:06 +00:00
Add resolution logic for other matterlabs variables
This commit is contained in:
Generated
+1
@@ -3984,6 +3984,7 @@ dependencies = [
|
|||||||
"alloy-primitives",
|
"alloy-primitives",
|
||||||
"alloy-sol-types",
|
"alloy-sol-types",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"revive-dt-node-interaction",
|
||||||
"semver 1.0.26",
|
"semver 1.0.26",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! The test driver handles the compilation and execution of the test cases.
|
//! The test driver handles the compilation and execution of the test cases.
|
||||||
|
|
||||||
use alloy::json_abi::JsonAbi;
|
use alloy::json_abi::JsonAbi;
|
||||||
use alloy::network::TransactionBuilder;
|
use alloy::network::{Ethereum, TransactionBuilder};
|
||||||
use alloy::rpc::types::TransactionReceipt;
|
use alloy::rpc::types::TransactionReceipt;
|
||||||
use alloy::rpc::types::trace::geth::GethTrace;
|
use alloy::rpc::types::trace::geth::GethTrace;
|
||||||
use alloy::{
|
use alloy::{
|
||||||
@@ -135,17 +135,21 @@ where
|
|||||||
std::any::type_name::<T>()
|
std::any::type_name::<T>()
|
||||||
);
|
);
|
||||||
|
|
||||||
let tx =
|
let tx = match input.legacy_transaction(
|
||||||
match input.legacy_transaction(nonce, &self.deployed_contracts, &self.deployed_abis) {
|
nonce,
|
||||||
Ok(tx) => {
|
&self.deployed_contracts,
|
||||||
tracing::debug!("Legacy transaction data: {tx:#?}");
|
&self.deployed_abis,
|
||||||
tx
|
node,
|
||||||
}
|
) {
|
||||||
Err(err) => {
|
Ok(tx) => {
|
||||||
tracing::error!("Failed to construct legacy transaction: {err:?}");
|
tracing::debug!("Legacy transaction data: {tx:#?}");
|
||||||
return Err(err);
|
tx
|
||||||
}
|
}
|
||||||
};
|
Err(err) => {
|
||||||
|
tracing::error!("Failed to construct legacy transaction: {err:?}");
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tracing::trace!("Executing transaction for input: {input:?}");
|
tracing::trace!("Executing transaction for input: {input:?}");
|
||||||
|
|
||||||
@@ -231,10 +235,12 @@ where
|
|||||||
// automatically fill in all of the missing fields from the provider that we
|
// automatically fill in all of the missing fields from the provider that we
|
||||||
// are using.
|
// are using.
|
||||||
let code = alloy::hex::decode(&code)?;
|
let code = alloy::hex::decode(&code)?;
|
||||||
let tx = TransactionRequest::default()
|
let tx = {
|
||||||
.nonce(nonce)
|
let tx = TransactionRequest::default()
|
||||||
.from(input.caller)
|
.nonce(nonce)
|
||||||
.with_deploy_code(code);
|
.from(input.caller);
|
||||||
|
TransactionBuilder::<Ethereum>::with_deploy_code(tx, code)
|
||||||
|
};
|
||||||
|
|
||||||
let receipt = match node.execute_transaction(tx) {
|
let receipt = match node.execute_transaction(tx) {
|
||||||
Ok(receipt) => receipt,
|
Ok(receipt) => receipt,
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ repository.workspace = true
|
|||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
revive-dt-node-interaction = { workspace = true }
|
||||||
|
|
||||||
alloy = { workspace = true }
|
alloy = { workspace = true }
|
||||||
alloy-primitives = { workspace = true }
|
alloy-primitives = { workspace = true }
|
||||||
alloy-sol-types = { workspace = true }
|
alloy-sol-types = { workspace = true }
|
||||||
|
|||||||
+233
-27
@@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use alloy::{
|
use alloy::{
|
||||||
|
eips::BlockNumberOrTag,
|
||||||
json_abi::JsonAbi,
|
json_abi::JsonAbi,
|
||||||
network::TransactionBuilder,
|
network::TransactionBuilder,
|
||||||
primitives::{Address, Bytes, U256},
|
primitives::{Address, Bytes, U256},
|
||||||
@@ -10,6 +11,8 @@ use semver::VersionReq;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
|
use revive_dt_node_interaction::EthereumNode;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
|
||||||
pub struct Input {
|
pub struct Input {
|
||||||
#[serde(default = "default_caller")]
|
#[serde(default = "default_caller")]
|
||||||
@@ -84,6 +87,7 @@ impl Input {
|
|||||||
&self,
|
&self,
|
||||||
deployed_abis: &HashMap<String, JsonAbi>,
|
deployed_abis: &HashMap<String, JsonAbi>,
|
||||||
deployed_contracts: &HashMap<String, Address>,
|
deployed_contracts: &HashMap<String, Address>,
|
||||||
|
chain_state_provider: &impl EthereumNode,
|
||||||
) -> anyhow::Result<Bytes> {
|
) -> anyhow::Result<Bytes> {
|
||||||
let Method::FunctionName(ref function_name) = self.method else {
|
let Method::FunctionName(ref function_name) = self.method else {
|
||||||
return Ok(Bytes::default()); // fallback or deployer — no input
|
return Ok(Bytes::default()); // fallback or deployer — no input
|
||||||
@@ -140,7 +144,7 @@ impl Input {
|
|||||||
calldata.extend(function.selector().0);
|
calldata.extend(function.selector().0);
|
||||||
|
|
||||||
for (arg_idx, arg) in calldata_args.iter().enumerate() {
|
for (arg_idx, arg) in calldata_args.iter().enumerate() {
|
||||||
match resolve_argument(arg, deployed_contracts) {
|
match resolve_argument(arg, deployed_contracts, chain_state_provider) {
|
||||||
Ok(resolved) => {
|
Ok(resolved) => {
|
||||||
calldata.extend(resolved.to_be_bytes::<32>());
|
calldata.extend(resolved.to_be_bytes::<32>());
|
||||||
}
|
}
|
||||||
@@ -160,8 +164,10 @@ impl Input {
|
|||||||
nonce: u64,
|
nonce: u64,
|
||||||
deployed_contracts: &HashMap<String, Address>,
|
deployed_contracts: &HashMap<String, Address>,
|
||||||
deployed_abis: &HashMap<String, JsonAbi>,
|
deployed_abis: &HashMap<String, JsonAbi>,
|
||||||
|
chain_state_provider: &impl EthereumNode,
|
||||||
) -> anyhow::Result<TransactionRequest> {
|
) -> anyhow::Result<TransactionRequest> {
|
||||||
let input_data = self.encoded_input(deployed_abis, deployed_contracts)?;
|
let input_data =
|
||||||
|
self.encoded_input(deployed_abis, deployed_contracts, chain_state_provider)?;
|
||||||
let transaction_request = TransactionRequest::default().nonce(nonce);
|
let transaction_request = TransactionRequest::default().nonce(nonce);
|
||||||
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)),
|
||||||
@@ -191,6 +197,7 @@ fn default_caller() -> Address {
|
|||||||
fn resolve_argument(
|
fn resolve_argument(
|
||||||
value: &str,
|
value: &str,
|
||||||
deployed_contracts: &HashMap<String, Address>,
|
deployed_contracts: &HashMap<String, Address>,
|
||||||
|
chain_state_provider: &impl EthereumNode,
|
||||||
) -> anyhow::Result<U256> {
|
) -> anyhow::Result<U256> {
|
||||||
if let Some(instance) = value.strip_suffix(".address") {
|
if let Some(instance) = value.strip_suffix(".address") {
|
||||||
Ok(U256::from_be_slice(
|
Ok(U256::from_be_slice(
|
||||||
@@ -212,30 +219,40 @@ fn resolve_argument(
|
|||||||
} else if let Some(value) = value.strip_prefix("0x") {
|
} else if let Some(value) = value.strip_prefix("0x") {
|
||||||
Ok(U256::from_str_radix(value, 16)
|
Ok(U256::from_str_radix(value, 16)
|
||||||
.map_err(|error| anyhow::anyhow!("Invalid hexadecimal literal: {}", error))?)
|
.map_err(|error| anyhow::anyhow!("Invalid hexadecimal literal: {}", error))?)
|
||||||
} else {
|
} else if value == "$CHAIN_ID" {
|
||||||
// TODO: This is a set of "variables" that we need to be able to resolve to be fully in
|
let chain_id = chain_state_provider.chain_id()?;
|
||||||
// compliance with the matter labs tester but we currently do not resolve them. We need to
|
Ok(U256::from(chain_id))
|
||||||
// add logic that does their resolution in the future, perhaps through some kind of system
|
} else if value == "$GAS_LIMIT" {
|
||||||
// context API that we pass down to the resolution function that allows it to make calls to
|
let gas_limit = chain_state_provider.block_gas_limit(BlockNumberOrTag::Latest)?;
|
||||||
// the node to perform these resolutions.
|
Ok(U256::from(gas_limit))
|
||||||
let is_unsupported = [
|
} else if value == "$COINBASE" {
|
||||||
"$CHAIN_ID",
|
let coinbase = chain_state_provider.block_coinbase(BlockNumberOrTag::Latest)?;
|
||||||
"$GAS_LIMIT",
|
Ok(U256::from_be_slice(coinbase.as_ref()))
|
||||||
"$COINBASE",
|
} else if value == "$DIFFICULTY" {
|
||||||
"$DIFFICULTY",
|
let block_difficulty = chain_state_provider.block_difficulty(BlockNumberOrTag::Latest)?;
|
||||||
"$BLOCK_HASH",
|
Ok(block_difficulty)
|
||||||
"$BLOCK_TIMESTAMP",
|
} else if value.starts_with("$BLOCK_HASH") {
|
||||||
]
|
let offset: u64 = value
|
||||||
.iter()
|
.split(':')
|
||||||
.any(|var| value.starts_with(var));
|
.next_back()
|
||||||
|
.and_then(|value| value.parse().ok())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
if is_unsupported {
|
let current_block_number = chain_state_provider.last_block_number()?;
|
||||||
tracing::error!(value, "Unsupported variable used");
|
let desired_block_number = current_block_number - offset;
|
||||||
anyhow::bail!("Encountered {value} which is currently unsupported by the framework");
|
|
||||||
} else {
|
let block_hash = chain_state_provider.block_hash(desired_block_number.into())?;
|
||||||
Ok(U256::from_str_radix(value, 10)
|
|
||||||
.map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))?)
|
Ok(U256::from_be_bytes(block_hash.0))
|
||||||
}
|
} else if value == "$BLOCK_NUMBER" {
|
||||||
|
let current_block_number = chain_state_provider.last_block_number()?;
|
||||||
|
Ok(U256::from(current_block_number))
|
||||||
|
} else if value == "$BLOCK_TIMESTAMP" {
|
||||||
|
let timestamp = chain_state_provider.block_timestamp(BlockNumberOrTag::Latest)?;
|
||||||
|
Ok(U256::from(timestamp))
|
||||||
|
} else {
|
||||||
|
Ok(U256::from_str_radix(value, 10)
|
||||||
|
.map_err(|error| anyhow::anyhow!("Invalid decimal literal: {}", error))?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,6 +265,69 @@ mod tests {
|
|||||||
use alloy_sol_types::SolValue;
|
use alloy_sol_types::SolValue;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
struct DummyEthereumNode;
|
||||||
|
|
||||||
|
impl EthereumNode for DummyEthereumNode {
|
||||||
|
fn execute_transaction(
|
||||||
|
&self,
|
||||||
|
_: TransactionRequest,
|
||||||
|
) -> anyhow::Result<alloy::rpc::types::TransactionReceipt> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trace_transaction(
|
||||||
|
&self,
|
||||||
|
_: alloy::rpc::types::TransactionReceipt,
|
||||||
|
) -> anyhow::Result<alloy::rpc::types::trace::geth::GethTrace> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn state_diff(
|
||||||
|
&self,
|
||||||
|
_: alloy::rpc::types::TransactionReceipt,
|
||||||
|
) -> anyhow::Result<alloy::rpc::types::trace::geth::DiffMode> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fetch_add_nonce(&self, _: Address) -> anyhow::Result<u64> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chain_id(&self) -> anyhow::Result<alloy_primitives::ChainId> {
|
||||||
|
Ok(0x123)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_gas_limit(&self, _: alloy::eips::BlockNumberOrTag) -> anyhow::Result<u128> {
|
||||||
|
Ok(0x1234)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_coinbase(&self, _: alloy::eips::BlockNumberOrTag) -> anyhow::Result<Address> {
|
||||||
|
Ok(Address::ZERO)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_difficulty(&self, _: alloy::eips::BlockNumberOrTag) -> anyhow::Result<U256> {
|
||||||
|
Ok(U256::from(0x12345u128))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_hash(
|
||||||
|
&self,
|
||||||
|
_: alloy::eips::BlockNumberOrTag,
|
||||||
|
) -> anyhow::Result<alloy_primitives::BlockHash> {
|
||||||
|
Ok([0xEE; 32].into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_timestamp(
|
||||||
|
&self,
|
||||||
|
_: alloy::eips::BlockNumberOrTag,
|
||||||
|
) -> anyhow::Result<alloy_primitives::BlockTimestamp> {
|
||||||
|
Ok(0x123456)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn last_block_number(&self) -> anyhow::Result<alloy_primitives::BlockNumber> {
|
||||||
|
Ok(0x1234567)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_encoded_input_uint256() {
|
fn test_encoded_input_uint256() {
|
||||||
let raw_metadata = r#"
|
let raw_metadata = r#"
|
||||||
@@ -283,7 +363,7 @@ mod tests {
|
|||||||
let deployed_contracts = HashMap::new();
|
let deployed_contracts = HashMap::new();
|
||||||
|
|
||||||
let encoded = input
|
let encoded = input
|
||||||
.encoded_input(&deployed_abis, &deployed_contracts)
|
.encoded_input(&deployed_abis, &deployed_contracts, &DummyEthereumNode)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(encoded.0.starts_with(&selector));
|
assert!(encoded.0.starts_with(&selector));
|
||||||
|
|
||||||
@@ -326,7 +406,9 @@ mod tests {
|
|||||||
abis.insert("Contract".to_string(), parsed_abi);
|
abis.insert("Contract".to_string(), parsed_abi);
|
||||||
let contracts = HashMap::new();
|
let contracts = HashMap::new();
|
||||||
|
|
||||||
let encoded = input.encoded_input(&abis, &contracts).unwrap();
|
let encoded = input
|
||||||
|
.encoded_input(&abis, &contracts, &DummyEthereumNode)
|
||||||
|
.unwrap();
|
||||||
assert!(encoded.0.starts_with(&selector));
|
assert!(encoded.0.starts_with(&selector));
|
||||||
|
|
||||||
type T = (alloy_primitives::Address,);
|
type T = (alloy_primitives::Address,);
|
||||||
@@ -336,4 +418,128 @@ mod tests {
|
|||||||
address!("0x1000000000000000000000000000000000000001")
|
address!("0x1000000000000000000000000000000000000001")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_chain_id_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$CHAIN_ID";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(resolved, U256::from(DummyEthereumNode.chain_id().unwrap()))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_gas_limit_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$GAS_LIMIT";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
U256::from(
|
||||||
|
DummyEthereumNode
|
||||||
|
.block_gas_limit(Default::default())
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_coinbase_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$COINBASE";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
U256::from_be_slice(
|
||||||
|
DummyEthereumNode
|
||||||
|
.block_coinbase(Default::default())
|
||||||
|
.unwrap()
|
||||||
|
.as_ref()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_block_difficulty_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$DIFFICULTY";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
DummyEthereumNode
|
||||||
|
.block_difficulty(Default::default())
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_block_hash_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$BLOCK_HASH";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
U256::from_be_bytes(DummyEthereumNode.block_hash(Default::default()).unwrap().0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_block_number_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$BLOCK_NUMBER";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
U256::from(DummyEthereumNode.last_block_number().unwrap())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn resolver_can_resolve_block_timestamp_variable() {
|
||||||
|
// Arrange
|
||||||
|
let input = "$BLOCK_TIMESTAMP";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let resolved = resolve_argument(input, &Default::default(), &DummyEthereumNode);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
let resolved = resolved.expect("Failed to resolve argument");
|
||||||
|
assert_eq!(
|
||||||
|
resolved,
|
||||||
|
U256::from(
|
||||||
|
DummyEthereumNode
|
||||||
|
.block_timestamp(Default::default())
|
||||||
|
.unwrap()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user