Skip to content

feat: simrevert type #98

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

Open
wants to merge 2 commits into
base: dylan/chore-deps-sdk-main
Choose a base branch
from
Open
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: 0 additions & 1 deletion src/tasks/block/sim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ impl Simulator {
block: BlockEnv,
) -> eyre::Result<BuiltBlock> {
let db = self.create_db().await.unwrap();

let block_build: BlockBuild<_, NoOpInspector> = BlockBuild::new(
db,
constants,
Expand Down
171 changes: 134 additions & 37 deletions src/tasks/submit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ use alloy::{
consensus::{SimpleCoder, constants::GWEI_TO_WEI},
eips::BlockNumberOrTag,
network::{TransactionBuilder, TransactionBuilder4844},
primitives::{FixedBytes, TxHash, U256},
primitives::{Bytes, FixedBytes, TxHash, U256},
providers::{Provider as _, SendableTx, WalletProvider},
rpc::types::eth::TransactionRequest,
rpc::{json_rpc::ErrorPayload, types::eth::TransactionRequest},
sol_types::{SolCall, SolError},
transports::TransportError,
};
Expand Down Expand Up @@ -48,6 +48,133 @@ macro_rules! spawn_provider_send {
};
}

/// Represents the kind of revert that can occur during simulation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SimRevertKind {
/// Incorrect host block error
IncorrectHostBlock,
/// Bad signature error
BadSignature,
Copy link
Member

Choose a reason for hiding this comment

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

I assume we're not preserving the data in tihs error since it's probably not worth it do so anyway

Copy link
Member Author

Choose a reason for hiding this comment

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

it's an error kind not an error itself. it's just a memoization of the type of the actual error data stored elsewhere

/// One rollup block per host block error
OneRollupBlockPerHostBlock,
/// Unknown error
Unknown,
}

impl From<Option<Bytes>> for SimRevertKind {
fn from(data: Option<Bytes>) -> Self {
let Some(data) = data else {
return Self::Unknown;
};

if data.starts_with(&IncorrectHostBlock::SELECTOR) {
Self::IncorrectHostBlock
} else if data.starts_with(&Zenith::BadSignature::SELECTOR) {
Self::BadSignature
} else if data.starts_with(&Zenith::OneRollupBlockPerHostBlock::SELECTOR) {
Self::OneRollupBlockPerHostBlock
} else {
Self::Unknown
}
}
}

#[derive(Debug, Clone)]
/// Represents an error that occurs during simulation of a transaction.
pub struct SimErrorResp {
/// The error payload containing the error code and message.
pub err: ErrorPayload,
/// The kind of revert that occurred (or unknown if not recognized).
kind: SimRevertKind,
}

impl core::fmt::Display for SimErrorResp {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"SimErrorResp {{ code: {}, message: {}, kind: {:?} }}",
self.code(),
self.message(),
self.kind
)
}
}

impl From<ErrorPayload> for SimErrorResp {
fn from(err: ErrorPayload) -> Self {
Self::new(err)
}
}

impl SimErrorResp {
/// Creates a new `SimRevertError` with the specified kind and error
/// payload.
pub fn new(err: ErrorPayload) -> Self {
let kind = err.as_revert_data().into();
Self { err, kind }
}

/// True if the error is an incorrect host block.
pub fn is_incorrect_host_block(&self) -> bool {
self.as_revert_data()
.map(|b| b.starts_with(&IncorrectHostBlock::SELECTOR))
.unwrap_or_default()
}

/// Attempts to decode the error payload as an [`IncorrectHostBlock`].
pub fn as_incorrect_host_block(&self) -> Option<IncorrectHostBlock> {
self.as_revert_data().and_then(|data| IncorrectHostBlock::abi_decode(&data).ok())
}

/// True if the error is a [`Zenith::BadSignature`].
pub fn is_bad_signature(&self) -> bool {
self.as_revert_data()
.map(|b| b.starts_with(&Zenith::BadSignature::SELECTOR))
.unwrap_or_default()
}

/// Attempts to decode the error payload as a [`Zenith::BadSignature`].
pub fn as_bad_signature(&self) -> Option<Zenith::BadSignature> {
self.as_revert_data().and_then(|data| Zenith::BadSignature::abi_decode(&data).ok())
}

/// True if the error is a [`Zenith::OneRollupBlockPerHostBlock`].
pub fn is_one_rollup_block_per_host_block(&self) -> bool {
self.as_revert_data()
.map(|b| b.starts_with(&Zenith::OneRollupBlockPerHostBlock::SELECTOR))
.unwrap_or_default()
}

/// Attempts to decode the error payload as a
/// [`Zenith::OneRollupBlockPerHostBlock`].
pub fn as_one_rollup_block_per_host_block(&self) -> Option<Zenith::OneRollupBlockPerHostBlock> {
self.as_revert_data()
.and_then(|data| Zenith::OneRollupBlockPerHostBlock::abi_decode(&data).ok())
}

/// True if the error is an unknown revert.
pub fn is_unknown(&self) -> bool {
!self.is_incorrect_host_block()
&& !self.is_bad_signature()
&& !self.is_one_rollup_block_per_host_block()
}

/// Returns the revert data if available.
pub fn as_revert_data(&self) -> Option<Bytes> {
self.err.as_revert_data()
}

/// Returns the JSON-RPC error code.
pub const fn code(&self) -> i64 {
self.err.code
}

/// Returns the error message.
pub fn message(&self) -> &str {
&self.err.message
}
}

/// Control flow for transaction submission.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ControlFlow {
Expand Down Expand Up @@ -145,44 +272,14 @@ impl SubmitTask {

/// Simulates the transaction with a call to the host provider to check for reverts.
async fn sim_with_call(&self, tx: &TransactionRequest) -> eyre::Result<()> {
if let Err(TransportError::ErrorResp(e)) =
self.provider().call(tx.clone()).block(BlockNumberOrTag::Pending.into()).await
{
// NB: These errors are all handled the same way but are logged for debugging purposes
if e.as_revert_data()
.map(|data| data.starts_with(&IncorrectHostBlock::SELECTOR))
.unwrap_or_default()
{
debug!(%e, "incorrect host block");
bail!(e)
}

if e.as_revert_data()
.map(|data| data.starts_with(&Zenith::BadSignature::SELECTOR))
.unwrap_or_default()
{
debug!(%e, "bad signature");
match self.provider().call(tx.clone()).block(BlockNumberOrTag::Pending.into()).await {
Err(TransportError::ErrorResp(e)) => {
let e = SimErrorResp::from(e);
bail!(e)
}

if e.as_revert_data()
.map(|data| data.starts_with(&Zenith::OneRollupBlockPerHostBlock::SELECTOR))
.unwrap_or_default()
{
debug!(%e, "one rollup block per host block");
bail!(e)
}

error!(
code = e.code,
message = %e.message,
data = ?e.data,
"unknown error in host transaction simulation call"
);
bail!(e)
Err(e) => bail!(e),
_ => Ok(()),
}

Ok(())
}

/// Creates a transaction request for the blob with the given header and signature values.
Expand Down