Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Matchain to deployment options #285

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .github/workflows/manual-sol-artifacts.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ on:
- polygon
- sepolia
- songbird
- matchain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this the only intentional change? what is the other stuff for?


jobs:
deploy:
Expand Down
19 changes: 19 additions & 0 deletions crates/eval/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use alloy::primitives::ruint::FromUintError;
#[cfg(not(target_family = "wasm"))]
use foundry_evm::backend::DatabaseError;
#[cfg(not(target_family = "wasm"))]
use foundry_evm::executors::RawCallResult;
use rain_error_decoding::{AbiDecodeFailedErrors, AbiDecodedErrorType};
use thiserror::Error;
Expand All @@ -23,6 +25,23 @@ pub enum ForkCallError {
U64FromUint256(#[from] FromUintError<u64>),
#[error(transparent)]
Eyre(#[from] eyre::Report),
#[error("Replay transaction error: {:#?}", .0)]
ReplayTransactionError(ReplayTransactionError),
}

#[derive(Debug, Error)]
pub enum ReplayTransactionError {
#[error("Transaction not found for hash {0} and fork url {1}")]
TransactionNotFound(String, String),
#[error("No active fork found")]
NoActiveFork,
#[cfg(not(target_family = "wasm"))]
#[error("Database error for hash {0} and fork url {1}: {2}")]
DatabaseError(String, String, DatabaseError),
#[error("No block number found in transaction for hash {0} and fork url {1}")]
NoBlockNumberFound(String, String),
#[error("No from address found in transaction for hash {0} and fork url {1}")]
NoFromAddressFound(String, String),
}

#[cfg(not(target_family = "wasm"))]
Expand Down
141 changes: 139 additions & 2 deletions crates/eval/src/fork.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::error::ForkCallError;
use crate::error::{ForkCallError, ReplayTransactionError};
use alloy::primitives::{Address, BlockNumber, U256};
use alloy::sol_types::SolCall;
use foundry_evm::{
Expand All @@ -8,6 +8,7 @@ use foundry_evm::{
opts::EvmOpts,
};
use rain_error_decoding::AbiDecodedErrorType;
use revm::primitives::B256;
use revm::{
interpreter::InstructionResult,
primitives::{Address as Addr, Bytes, Env, HashSet, SpecId},
Expand Down Expand Up @@ -388,12 +389,120 @@ impl Forker {
)
.map_err(|v| ForkCallError::ExecutorError(v.to_string()))
}

/// Replays a transaction from the forked EVM.
/// # Arguments
/// * `tx_hash` - The transaction hash.
/// # Returns
/// A result containing the raw call result.
pub async fn replay_transaction(
&mut self,
tx_hash: B256,
) -> Result<RawCallResult, ForkCallError> {
let fork_url = self
.executor
.backend()
.active_fork_url()
.unwrap_or("No fork url found".to_string());

// get the transaction
let shared_backend = &self
.executor
.backend()
.active_fork_db()
.ok_or(ForkCallError::ReplayTransactionError(
ReplayTransactionError::NoActiveFork,
))?
.db;
let full_tx = shared_backend.get_transaction(tx_hash).map_err(|e| {
ForkCallError::ReplayTransactionError(ReplayTransactionError::DatabaseError(
tx_hash.to_string(),
fork_url.clone(),
e,
))
})?;

// get the block number from the transaction
let block_number = full_tx
.block_number
.ok_or(ForkCallError::ReplayTransactionError(
ReplayTransactionError::NoBlockNumberFound(tx_hash.to_string(), fork_url.clone()),
))?;

// get the block
let block = shared_backend.get_full_block(block_number).map_err(|e| {
ForkCallError::ReplayTransactionError(ReplayTransactionError::DatabaseError(
block_number.to_string(),
fork_url.clone(),
e,
))
})?;

// matching env to the env from the block the transaction is in
self.executor.env_mut().block.number = U256::from(block_number);
self.executor.env_mut().block.timestamp = U256::from(block.header.timestamp);
self.executor.env_mut().block.coinbase = block.header.miner;
self.executor.env_mut().block.difficulty = block.header.difficulty;
self.executor.env_mut().block.prevrandao = Some(block.header.mix_hash.unwrap_or_default());
self.executor.env_mut().block.basefee =
U256::from(block.header.base_fee_per_gas.unwrap_or_default());
self.executor.env_mut().block.gas_limit = U256::from(block.header.gas_limit);

let _ = &self
.add_or_select(
NewForkedEvm {
fork_url: fork_url.clone(),
fork_block_number: Some(block_number - 1),
},
None,
)
.await;

let active_fork_local_id = self
.executor
.backend()
.active_fork_id()
.ok_or(ForkCallError::ExecutorError("no active fork!".to_owned()))?;

let mut journaled_state = JournaledState::new(SpecId::LATEST, HashSet::new());

let env = self.executor.env().clone();

// replay all transactions that came before
let tx = self.executor.backend_mut().replay_until(
active_fork_local_id,
env,
tx_hash,
&mut journaled_state,
)?;

let res = match tx {
Some(tx) => match tx.to {
Some(to) => self.call(tx.from.as_slice(), to.as_slice(), &tx.input)?,
None => {
return Err(ForkCallError::ReplayTransactionError(
ReplayTransactionError::NoFromAddressFound(tx_hash.to_string(), fork_url),
))
}
},
None => {
return Err(ForkCallError::ReplayTransactionError(
ReplayTransactionError::TransactionNotFound(tx_hash.to_string(), fork_url),
))
}
};

Ok(res)
}
}

#[cfg(test)]

mod tests {
use crate::namespace::CreateNamespace;
use crate::{
namespace::CreateNamespace,
trace::{RainEvalResult, RainSourceTrace},
};
use rain_interpreter_env::{
CI_DEPLOY_SEPOLIA_RPC_URL, CI_FORK_SEPOLIA_BLOCK_NUMBER, CI_FORK_SEPOLIA_DEPLOYER_ADDRESS,
};
Expand Down Expand Up @@ -663,4 +772,32 @@ mod tests {
U256::from(POLYGON_FORK_NUMBER + 1)
);
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_fork_replay() {
let mut forker = Forker::new_with_fork(
NewForkedEvm {
fork_url: CI_DEPLOY_SEPOLIA_RPC_URL.to_string(),
fork_block_number: None,
},
None,
None,
)
.await
.unwrap();

let tx_hash = "0xcbfff7d9369afcc7a851dff42ca2769f32d77c3b9066023b887583ee9cd0809d"
.parse::<B256>()
.unwrap();

let replay_result = forker.replay_transaction(tx_hash).await.unwrap();

assert!(
replay_result.env.tx.caller
== "0x8924274F5304277FFDdd29fad5181D98D5F65eF6"
.parse::<Address>()
.unwrap()
);
assert!(replay_result.exit_reason.is_ok());
}
}
31 changes: 22 additions & 9 deletions crates/eval/src/trace.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::fork::ForkTypedReturn;
use alloy::primitives::{Address, U256};
use foundry_evm::executors::RawCallResult;
use rain_interpreter_bindings::IInterpreterV3::{eval3Call, eval3Return};

use thiserror::Error;

pub const RAIN_TRACER_ADDRESS: &str = "0xF06Cd48c98d7321649dB7D8b2C396A81A2046555";
Expand Down Expand Up @@ -56,13 +56,11 @@ pub struct RainEvalResult {
pub traces: Vec<RainSourceTrace>,
}

impl From<ForkTypedReturn<eval3Call>> for RainEvalResult {
fn from(typed_return: ForkTypedReturn<eval3Call>) -> Self {
let eval3Return { stack, writes } = typed_return.typed_return;

impl From<RawCallResult> for RainEvalResult {
fn from(raw_call_result: RawCallResult) -> Self {
let tracer_address = RAIN_TRACER_ADDRESS.parse::<Address>().unwrap();
let mut traces: Vec<RainSourceTrace> = typed_return
.raw

let mut traces: Vec<RainSourceTrace> = raw_call_result
.traces
.unwrap()
.to_owned()
Expand All @@ -79,10 +77,25 @@ impl From<ForkTypedReturn<eval3Call>> for RainEvalResult {
traces.reverse();

RainEvalResult {
reverted: typed_return.raw.reverted,
reverted: raw_call_result.reverted,
stack: vec![],
writes: vec![],
traces,
}
}
}

impl From<ForkTypedReturn<eval3Call>> for RainEvalResult {
fn from(typed_return: ForkTypedReturn<eval3Call>) -> Self {
let eval3Return { stack, writes } = typed_return.typed_return;

let res: RainEvalResult = typed_return.raw.into();

RainEvalResult {
reverted: res.reverted,
stack,
writes,
traces,
traces: res.traces,
}
}
}
Expand Down
Loading