diff --git a/Cargo.lock b/Cargo.lock index a039568..9e0fa1a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -579,6 +579,7 @@ dependencies = [ "futures", "proptest", "reqwest", + "serde", "tokio", "url", ] @@ -1723,18 +1724,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.188" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 0bbe930..c274f79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,9 @@ members = ["crates/*"] edition = "2021" version = "0.0.1" authors = ["clabby"] + +[workspace.dependencies] +anyhow = "1.0.79" +tokio = { version = "1.35.1", features = ["macros"] } +async-trait = "0.1.77" +alloy-primitives = "0.6.0" diff --git a/crates/fault/Cargo.toml b/crates/fault/Cargo.toml index c2f9202..2364128 100644 --- a/crates/fault/Cargo.toml +++ b/crates/fault/Cargo.toml @@ -8,23 +8,23 @@ version.workspace = true authors.workspace = true [dependencies] +# Workspace +anyhow.workspace = true +tokio.workspace = true +async-trait.workspace = true +alloy-primitives.workspace = true + # Internal durin-primitives = { path = "../primitives" } # External - -alloy-primitives = "0.6.0" alloy-sol-types = "0.6.0" alloy-rpc-client = { git = "https://github.com/alloy-rs/alloy" } alloy-transport = { git = "https://github.com/alloy-rs/alloy" } alloy-transport-http = { git = "https://github.com/alloy-rs/alloy" } -anyhow = "1.0.79" - -# Async -tokio = { version = "1.35.1", features = ["macros"] } +serde = { version = "1.0.195", features = ["derive"] } url = "2.5.0" reqwest = "0.11.23" -async-trait = "0.1.77" futures = "0.3.30" [dev-dependencies] diff --git a/crates/fault/src/providers/alphabet.rs b/crates/fault/src/providers/alphabet.rs index 5af662e..7f25d43 100644 --- a/crates/fault/src/providers/alphabet.rs +++ b/crates/fault/src/providers/alphabet.rs @@ -3,6 +3,7 @@ use crate::{Gindex, Position, TraceProvider, VMStatus}; use alloy_primitives::{keccak256, U256}; use alloy_sol_types::{sol, SolType}; +use anyhow::Result; use durin_primitives::Claim; use std::{convert::TryInto, sync::Arc}; @@ -29,18 +30,18 @@ impl AlphabetTraceProvider { #[async_trait::async_trait] impl TraceProvider<[u8; 1]> for AlphabetTraceProvider { - async fn absolute_prestate(&self) -> anyhow::Result> { + async fn absolute_prestate(&self) -> Result> { Ok(Arc::new([self.absolute_prestate])) } - async fn absolute_prestate_hash(&self) -> anyhow::Result { + async fn absolute_prestate_hash(&self) -> Result { let prestate = U256::from(self.absolute_prestate); let mut prestate_hash = keccak256(::abi_encode(&prestate)); prestate_hash[0] = VMStatus::Unfinished as u8; Ok(prestate_hash) } - async fn state_at(&self, position: Position) -> anyhow::Result> { + async fn state_at(&self, position: Position) -> Result> { let absolute_prestate = self.absolute_prestate as u64; let trace_index = position.trace_index(self.max_depth); @@ -50,7 +51,7 @@ impl TraceProvider<[u8; 1]> for AlphabetTraceProvider { Ok(Arc::new([state])) } - async fn state_hash(&self, position: Position) -> anyhow::Result { + async fn state_hash(&self, position: Position) -> Result { let state_sol = ( U256::from(position.trace_index(self.max_depth)), U256::from(self.state_at(position).await?[0]), @@ -60,7 +61,7 @@ impl TraceProvider<[u8; 1]> for AlphabetTraceProvider { Ok(state_hash) } - async fn proof_at(&self, _: Position) -> anyhow::Result> { + async fn proof_at(&self, _: Position) -> Result> { Ok(Arc::new([])) } } diff --git a/crates/fault/src/providers/mod.rs b/crates/fault/src/providers/mod.rs index e505302..4751ea9 100644 --- a/crates/fault/src/providers/mod.rs +++ b/crates/fault/src/providers/mod.rs @@ -5,3 +5,6 @@ pub use self::alphabet::AlphabetTraceProvider; mod output; pub use self::output::OutputTraceProvider; + +mod split; +pub use self::split::SplitTraceProvider; diff --git a/crates/fault/src/providers/output.rs b/crates/fault/src/providers/output.rs index 052c18b..f3657d3 100644 --- a/crates/fault/src/providers/output.rs +++ b/crates/fault/src/providers/output.rs @@ -1,6 +1,8 @@ -//! This module contains the implementation of the [crate::TraceProvider] trait for the mock Alphabet VM. +//! This module contains the implementation of the [crate::TraceProvider] trait for fetching output roots from the +//! rollup node. -use crate::{Position, TraceProvider}; +use crate::{Gindex, Position, TraceProvider}; +use alloy_primitives::{keccak256, B256}; use alloy_rpc_client::RpcClient; use alloy_transport::TransportResult; use alloy_transport_http::Http; @@ -14,14 +16,14 @@ use std::sync::Arc; pub struct OutputTraceProvider { pub rpc_client: RpcClient>, pub starting_block_number: u64, - pub leaf_depth: u64, + pub leaf_depth: u8, } impl OutputTraceProvider { pub fn try_new( l2_archive_url: String, starting_block_number: u64, - leaf_depth: u64, + leaf_depth: u8, ) -> Result { let rpc_client = RpcClient::builder().reqwest_http(Url::parse(&l2_archive_url)?); Ok(Self { @@ -32,26 +34,42 @@ impl OutputTraceProvider { } } +#[derive(serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct OutputAtBlockResponse { + pub output_root: B256, +} + #[async_trait::async_trait] impl TraceProvider<[u8; 32]> for OutputTraceProvider { - async fn absolute_prestate(&self) -> anyhow::Result> { - todo!() - // let transport_result: TransportResult<> = self.rpc_client.prepare("optimism_outputAtBlock", (self.starting_block_number)).await.map_err(|e| anyhow::anyhow!(e))? + async fn absolute_prestate(&self) -> Result> { + let result: TransportResult = self + .rpc_client + .prepare("optimism_outputAtBlock", (self.starting_block_number)) + .await; + Ok(Arc::new(*result?.output_root)) } - async fn absolute_prestate_hash(&self) -> anyhow::Result { - todo!() + async fn absolute_prestate_hash(&self) -> Result { + Ok(keccak256(self.absolute_prestate().await?.as_slice())) } - async fn state_at(&self, position: Position) -> anyhow::Result> { - todo!() + async fn state_at(&self, position: Position) -> Result> { + let result: TransportResult = self + .rpc_client + .prepare( + "optimism_outputAtBlock", + (self.starting_block_number + position.trace_index(self.leaf_depth)), + ) + .await; + Ok(Arc::new(*result?.output_root)) } - async fn state_hash(&self, position: Position) -> anyhow::Result { - todo!() + async fn state_hash(&self, position: Position) -> Result { + Ok(keccak256(self.state_at(position).await?.as_slice())) } - async fn proof_at(&self, _: Position) -> anyhow::Result> { - todo!() + async fn proof_at(&self, _: Position) -> Result> { + unimplemented!("Proofs are not supported for the OutputTraceProvider") } } diff --git a/crates/fault/src/providers/split.rs b/crates/fault/src/providers/split.rs new file mode 100644 index 0000000..1ae1912 --- /dev/null +++ b/crates/fault/src/providers/split.rs @@ -0,0 +1,54 @@ +//! This module contains the implementation of the [crate::TraceProvider] trait for composing two trace providers together +//! based off of the input depth. This implementation can be used to compose several layers of bisection. + +use crate::{Gindex, Position, TraceProvider}; +use alloy_primitives::keccak256; +use anyhow::Result; +use durin_primitives::Claim; +use std::{marker::PhantomData, sync::Arc}; + +/// The [SplitTraceProvider] is a [TraceProvider] that composes two trace providers together based off of the input depth. +pub struct SplitTraceProvider +where + T: AsRef<[u8]> + Send + Sync, + TOP: TraceProvider, + BOTTOM: TraceProvider, +{ + pub top: TOP, + pub bottom: BOTTOM, + pub split_depth: u8, + pub _phantom: PhantomData, +} + +#[async_trait::async_trait] +impl TraceProvider for SplitTraceProvider +where + T: AsRef<[u8]> + Send + Sync, + TOP: TraceProvider + Sync, + BOTTOM: TraceProvider + Sync, +{ + async fn absolute_prestate(&self) -> Result> { + todo!() + } + + async fn absolute_prestate_hash(&self) -> Result { + todo!() + } + + async fn state_at(&self, position: Position) -> Result> { + if position.depth() <= self.split_depth { + self.top.state_at(position).await + } else { + // TODO: Pass relative position based on split depth? + self.bottom.state_at(position).await + } + } + + async fn state_hash(&self, position: Position) -> Result { + Ok(keccak256(self.state_at(position).await?.as_ref())) + } + + async fn proof_at(&self, _: Position) -> Result> { + todo!() + } +} diff --git a/crates/fault/src/solver.rs b/crates/fault/src/solver.rs index 6315eba..6af3556 100644 --- a/crates/fault/src/solver.rs +++ b/crates/fault/src/solver.rs @@ -4,6 +4,7 @@ use crate::{ FaultClaimSolver, FaultDisputeGame, FaultDisputeState, FaultSolverResponse, Position, TraceProvider, }; +use anyhow::Result; use durin_primitives::{DisputeGame, DisputeSolver}; use std::{marker::PhantomData, sync::Arc}; use tokio::sync::Mutex; @@ -44,7 +45,7 @@ where async fn available_moves( &self, game: Arc>, - ) -> anyhow::Result]>> { + ) -> Result]>> { let game_lock = game.lock().await; // Fetch the local opinion on the root claim. diff --git a/crates/fault/src/solvers/alpha.rs b/crates/fault/src/solvers/alpha.rs index d7d43c6..35b43f0 100644 --- a/crates/fault/src/solvers/alpha.rs +++ b/crates/fault/src/solvers/alpha.rs @@ -6,6 +6,7 @@ use crate::{ ClaimData, FaultClaimSolver, FaultDisputeGame, FaultDisputeState, FaultSolverResponse, Gindex, Position, TraceProvider, }; +use anyhow::{anyhow, Result}; use durin_primitives::Claim; use std::{marker::PhantomData, sync::Arc}; use tokio::sync::Mutex; @@ -41,7 +42,7 @@ where world: Arc>, claim_index: usize, attacking_root: bool, - ) -> anyhow::Result> { + ) -> Result> { let mut world_lock = world.lock().await; // Fetch the maximum depth of the game's position tree. @@ -51,7 +52,7 @@ where let claim = world_lock .state_mut() .get_mut(claim_index) - .ok_or(anyhow::anyhow!("Failed to fetch claim from passed state"))?; + .ok_or(anyhow!("Failed to fetch claim from passed state"))?; let claim_depth = claim.position.depth(); // Mark the claim as visited. This mutates the passed state and must be reverted if an @@ -160,7 +161,7 @@ where provider: &P, position: Position, observed_claim: &mut ClaimData, - ) -> anyhow::Result { + ) -> Result { let state_hash = provider.state_hash(position).await.map_err(|e| { observed_claim.visited = false; e @@ -173,7 +174,7 @@ where provider: &P, position: Position, observed_claim: &mut ClaimData, - ) -> anyhow::Result> { + ) -> Result> { let state_at = provider.state_at(position).await.map_err(|e| { observed_claim.visited = false; e @@ -186,7 +187,7 @@ where provider: &P, position: Position, observed_claim: &mut ClaimData, - ) -> anyhow::Result> { + ) -> Result> { let proof_at = provider.proof_at(position).await.map_err(|e| { observed_claim.visited = false; e diff --git a/crates/fault/src/traits.rs b/crates/fault/src/traits.rs index 27b1bff..c26d05d 100644 --- a/crates/fault/src/traits.rs +++ b/crates/fault/src/traits.rs @@ -1,6 +1,7 @@ //! This module holds traits related to the [FaultDisputeGame] use crate::{state::ClaimData, FaultDisputeState, FaultSolverResponse, Position}; +use anyhow::Result; use durin_primitives::{Claim, DisputeGame}; use std::sync::Arc; use tokio::sync::Mutex; @@ -34,7 +35,7 @@ pub trait FaultClaimSolver, P: TraceProvider> { world: Arc>, claim_index: usize, attacking_root: bool, - ) -> anyhow::Result>; + ) -> Result>; /// Returns a shared reference to the [TraceProvider] that the solver uses to fetch the state of the VM and /// commitments to it. @@ -46,19 +47,19 @@ pub trait FaultClaimSolver, P: TraceProvider> { #[async_trait::async_trait] pub trait TraceProvider> { /// Returns the raw absolute prestate (in bytes). - async fn absolute_prestate(&self) -> anyhow::Result>; + async fn absolute_prestate(&self) -> Result>; /// Returns the absolute prestate hash. - async fn absolute_prestate_hash(&self) -> anyhow::Result; + async fn absolute_prestate_hash(&self) -> Result; /// Returns the raw state (in bytes) at the given position. - async fn state_at(&self, position: Position) -> anyhow::Result>; + async fn state_at(&self, position: Position) -> Result>; /// Returns the state hash at the given position. - async fn state_hash(&self, position: Position) -> anyhow::Result; + async fn state_hash(&self, position: Position) -> Result; /// Returns the raw proof for the commitment at the given position. - async fn proof_at(&self, position: Position) -> anyhow::Result>; + async fn proof_at(&self, position: Position) -> Result>; } /// The [Gindex] trait defines the interface of a generalized index within a binary tree. diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 2fb0804..7472a58 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -4,11 +4,12 @@ description = "Primitive types for Durin." resolver = "2" edition.workspace = true -authors.workspace = true version.workspace = true +authors.workspace = true [dependencies] -alloy-primitives = "0.6.0" -anyhow = "1.0.79" -async-trait = "0.1.77" -tokio = { version = "1.35.1", features = ["macros"] } +# Workspace +tokio.workspace = true +anyhow.workspace = true +async-trait.workspace = true +alloy-primitives.workspace = true diff --git a/crates/primitives/src/dispute_game.rs b/crates/primitives/src/dispute_game.rs index a9c7277..d29cfdb 100644 --- a/crates/primitives/src/dispute_game.rs +++ b/crates/primitives/src/dispute_game.rs @@ -1,6 +1,7 @@ //! Types related to the [crate::DisputeGame] trait. use alloy_primitives::B256; +use anyhow::{bail, Error}; use std::convert::TryFrom; /// The [Claim] type is an alias to [B256], used to deliniate a claim hash from a regular hash. @@ -19,13 +20,13 @@ pub enum GameType { } impl TryFrom for GameType { - type Error = anyhow::Error; + type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(GameType::FaultCannon), 255 => Ok(GameType::Alphabet), - _ => anyhow::bail!("Invalid game type"), + _ => bail!("Invalid game type"), } } } @@ -44,14 +45,14 @@ pub enum GameStatus { } impl TryFrom for GameStatus { - type Error = anyhow::Error; + type Error = Error; fn try_from(value: u8) -> Result { match value { 0 => Ok(GameStatus::InProgress), 1 => Ok(GameStatus::ChallengerWins), 2 => Ok(GameStatus::DefenderWins), - _ => anyhow::bail!("Invalid game status"), + _ => bail!("Invalid game status"), } } } diff --git a/crates/primitives/src/rule.rs b/crates/primitives/src/rule.rs index ea4ad9b..e3fc087 100644 --- a/crates/primitives/src/rule.rs +++ b/crates/primitives/src/rule.rs @@ -5,7 +5,10 @@ //! the state back if successful or an error if not. They are used to validate state //! transitions in tests where the various solvers in durin suggest a state transition. -pub type Rule = Box anyhow::Result>; +#[allow(unused)] +use anyhow::{anyhow, Result}; + +pub type Rule = Box Result>; #[macro_export] macro_rules! chain_rules { @@ -35,21 +38,21 @@ mod test { if state < 10 { Ok(state) } else { - Err(anyhow::anyhow!("state must be less than 10")) + Err(anyhow!("state must be less than 10")) } }); let rule_double_10: Rule = Box::new(|state: u32| { if state * 2 == 10 { Ok(state) } else { - Err(anyhow::anyhow!("state must be half of 10")) + Err(anyhow!("state must be half of 10")) } }); let rule_bitwise: Rule = Box::new(|state: u32| { if state & 0xF == 0b0101 { Ok(state) } else { - Err(anyhow::anyhow!("state must be 5")) + Err(anyhow!("state must be 5")) } }); @@ -65,21 +68,21 @@ mod test { if state < 10 { Ok(state) } else { - Err(anyhow::anyhow!("state must be less than 10")) + Err(anyhow!("state must be less than 10")) } }); let rule_double_11: Rule = Box::new(|state: u32| { if state * 2 == 11 { Ok(state) } else { - Err(anyhow::anyhow!("state must be half of 11")) + Err(anyhow!("state must be half of 11")) } }); let rule_bitwise: Rule = Box::new(|state: u32| { if state & 0xF == 0b0101 { Ok(state) } else { - Err(anyhow::anyhow!("state must be 5")) + Err(anyhow!("state must be 5")) } }); diff --git a/crates/primitives/src/traits.rs b/crates/primitives/src/traits.rs index e5023fd..98f4870 100644 --- a/crates/primitives/src/traits.rs +++ b/crates/primitives/src/traits.rs @@ -1,6 +1,7 @@ //! The traits module contains traits used throughout the library. use crate::{dispute_game::Claim, GameStatus}; +use anyhow::Result; use std::sync::Arc; use tokio::sync::Mutex; @@ -41,5 +42,5 @@ pub trait DisputeSolver { /// Returns any available responses computed by the solver provided a [DisputeGame]. /// The consumer of the response is responsible for dispatching the action associated /// with the responses. - async fn available_moves(&self, game: Arc>) -> anyhow::Result>; + async fn available_moves(&self, game: Arc>) -> Result>; }