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

feat: Refactors #4

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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,393 changes: 1,315 additions & 78 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 8 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,11 @@ members = ["crates/*"]

[workspace.package]
edition = "2021"
version = "0.0.1"
version = "0.1.0"
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"
23 changes: 18 additions & 5 deletions crates/fault/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
[package]
name = "durin-fault"
description = "Game solver for the OP Stack's FaultDisputeGame"
authors = ["clabby"]
resolver = "2"

edition.workspace = true
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 = { version = "0.4.2" }
alloy-sol-types = { version = "0.4.2" }
anyhow = "1.0.75"
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" }
serde = { version = "1.0.195", features = ["derive"] }
url = "2.5.0"
reqwest = "0.11.23"
futures = "0.3.30"

[dev-dependencies]
proptest = "1.2.0"
proptest = "1.4.0"
33 changes: 22 additions & 11 deletions crates/fault/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,41 @@

This crate contains an implementation of a solver for the [OP Stack][op-stack]'s `FaultDisputeGame`. This implementation is currently
generic over the `TraceProvider`, `ClaimSolver`, and local resolution algorithm. This allows for expanding the solver to support multiple
backends, such as [Asterisc][asterisc] or [Cannon][cannon], as well as multiple local resolution algorithms, such as @inphi's
new sub-game resolution mechanism.
backends, such as [Asterisc][asterisc] or [Cannon][cannon], as well as multiple local resolution algorithms.

## Solvers
* [`AlphaClaimSolver`](./src/solvers/alpha.rs) - The first iteration of the Fault dispute game solver used in the alpha release of the Fault proof system on Optimism.

- [`AlphaClaimSolver`](./src/solvers/alpha.rs) - [DEPRECATED] The first iteration of the Fault dispute game solver used in the alpha release of the Fault proof system on Optimism.
- [`ChadClaimSolver`](./src/solvers/alpha_chad.rs) - The second iteration of the Fault dispute game solver used in the alpha chad release of the Fault proof system on Optimism.

### Rules

`Rules` (see: [Rules](../../README.md)) in `durin-fault` are defined within the `solvers` module. These rules are used to describe the
expected behavior of all possible state transitions that the solver can suggest to the game's state.

## Trace Providers
* [`AlphabetTraceProvider`](./src/providers/alphabet.rs) - A mock trace provider for the `AlphabetVM` used for testing.

- [`SplitTraceProvider`](./src/providers/split.rs) - An abstraction over two implementations of the `TraceProvider` trait that splits which one is used depending on the `Position` passed.
- [`CannonTraceProvider`](./src/providers/cannon.rs) - A trace provider that can issue state witnesses and memory access proofs for instructions within a `cannon` trace.
- [`OutputTraceProvider`](./src/providers/output.rs) - A trace provider that can issue output roots for L2 block numbers.

### Mock Trace Providers

- [`AlphabetTraceProvider`](./src/providers/mocks/alphabet.rs) - A mock trace provider for the `AlphabetVM` used for testing.
- [`MockOutputTraceProvider`](./src/providers/mocks/mock_output.rs) - A mock trace provider for output roots used for testing.

## Resolution Functions
* *todo*
* [`(Planned) Sweep`] - "Sweep" resolution is the first implementation of a global resolution algorithm for the fault dispute game. In reverse
chronological order, the algorithm looks for the left-most uncountered instruction in the game DAG and compares its
agreement with the root claim to determine the outcome of the game.
* [`(Planned) @inphi's Sub-Game Resolution`] - @inphi's sub-game resolution algorithm is a new resolution algorithm that allows for
the resolution of a game to be split into multiple sub-games. This allows for the solver to reduce the amount of
moves necessary to resolve a game as well as enforce incentive compatibility in bond payouts.

- _todo_
- [`(Planned) Sweep`] - "Sweep" resolution is the first implementation of a global resolution algorithm for the fault dispute game. In reverse
chronological order, the algorithm looks for the left-most uncountered instruction in the game DAG and compares its
agreement with the root claim to determine the outcome of the game.
- [`(Planned) @inphi's Sub-Game Resolution`] - @inphi's sub-game resolution algorithm is a new resolution algorithm that allows for
the resolution of a game to be split into multiple sub-games. This allows for the solver to reduce the amount of
moves necessary to resolve a game as well as enforce incentive compatibility in bond payouts.

<!-- LINKS -->

[op-stack]: https://github.com/ethereum-optimism/optimism
[cannon]: https://github.com/ethereum-optimism/optimism/tree/develop/cannon
[asterisc]: https://github.com/protolambda/asterisc
4 changes: 1 addition & 3 deletions crates/fault/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! The fault module contains types and traits related to the FaultDisputeGame.

extern crate alloy_primitives;
extern crate alloy_sol_types;
extern crate durin_primitives;
#![allow(dead_code, unused_imports)]

#[cfg(test)]
extern crate proptest;
Expand Down
35 changes: 35 additions & 0 deletions crates/fault/src/providers/cannon.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! This module contains the implementation of the [crate::TraceProvider] trait for calling out to `cannon` to fetch
//! state witnesses and proof values.

use crate::{Position, TraceProvider};
use anyhow::Result;
use durin_primitives::Claim;
use std::sync::Arc;

/// The [CannonTraceProvider] is a [TraceProvider] that runs `cannon` to retrieve state witnesses and proof values.
pub struct CannonTraceProvider {
pub split_depth: u8,
}

#[async_trait::async_trait]
impl TraceProvider for CannonTraceProvider {
async fn absolute_prestate(&self, _: Position) -> Result<Arc<[u8]>> {
todo!()
Copy link
Contributor

Choose a reason for hiding this comment

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

Are these todos the main blockers?

}

async fn absolute_prestate_hash(&self, _: Position) -> Result<Claim> {
todo!()
}

async fn state_at(&self, _: Position) -> Result<Arc<[u8]>> {
todo!()
}

async fn state_hash(&self, _: Position) -> Result<Claim> {
todo!()
}

async fn proof_at(&self, _: Position) -> Result<Arc<[u8]>> {
todo!()
}
}
Original file line number Diff line number Diff line change
@@ -1,69 +1,65 @@
//! This module contains the implementation of the [crate::TraceProvider] trait for the
//! mock Alphabet VM.

#![allow(dead_code, unused_variables)]
//! This module contains the implementation of the [crate::TraceProvider] trait for the mock Alphabet VM.

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};

type AlphabetClaimConstruction = sol! { tuple(uint256, uint256) };

/// The [AlphabetTraceProvider] is a [TraceProvider] that provides the correct
/// trace for the mock Alphabet VM.
/// The [AlphabetTraceProvider] is a [TraceProvider] that provides the correct trace for the mock Alphabet VM.
pub struct AlphabetTraceProvider {
/// The absolute prestate of the alphabet VM is the setup state.
/// This will be the ascii representation of letter prior to the first
/// in the honest alphabet trace.
pub absolute_prestate: u8,
/// The absolute prestate of the alphabet VM is the setup state. This will be the ascii representation of letter
/// prior to the first in the honest alphabet trace.
pub absolute_prestate: [u8; 32],
/// The maximum depth of the dispute game position tree.
pub max_depth: u8,
}

impl AlphabetTraceProvider {
pub fn new(absolute_prestate: u8, max_depth: u8) -> Self {
pub fn new(absolute_prestate: u64, max_depth: u8) -> Self {
Self {
absolute_prestate,
absolute_prestate: U256::from(absolute_prestate).to_be_bytes(),
max_depth,
}
}
}

impl TraceProvider<[u8; 1]> for AlphabetTraceProvider {
fn absolute_prestate(&self) -> Arc<[u8; 1]> {
Arc::new([self.absolute_prestate])
#[async_trait::async_trait]
impl TraceProvider for AlphabetTraceProvider {
async fn absolute_prestate(&self, _: Position) -> Result<Arc<[u8]>> {
Ok(Arc::new(self.absolute_prestate))
}

fn absolute_prestate_hash(&self) -> Claim {
let prestate = U256::from(self.absolute_prestate);
async fn absolute_prestate_hash(&self, _: Position) -> Result<Claim> {
let prestate = U256::from_be_bytes(self.absolute_prestate);
let mut prestate_hash = keccak256(<sol!(uint256)>::abi_encode(&prestate));
prestate_hash[0] = VMStatus::Unfinished as u8;
prestate_hash
Ok(prestate_hash)
}

fn state_at(&self, position: Position) -> anyhow::Result<Arc<[u8; 1]>> {
let absolute_prestate = self.absolute_prestate as u64;
async fn state_at(&self, position: Position) -> Result<Arc<[u8]>> {
let absolute_prestate = u64::from_be_bytes(self.absolute_prestate[24..32].try_into()?);
let trace_index = position.trace_index(self.max_depth);

let state = (absolute_prestate + trace_index + 1)
.try_into()
.unwrap_or(self.absolute_prestate + 2u8.pow(self.max_depth as u32));
Ok(Arc::new([state]))
let state = absolute_prestate + trace_index + 1;
Ok(Arc::<[u8; 32]>::new(U256::from(state).to_be_bytes()))
}

fn state_hash(&self, position: Position) -> anyhow::Result<Claim> {
async fn state_hash(&self, position: Position) -> Result<Claim> {
let state: [u8; 32] = (*self.state_at(position).await?).try_into()?;
let state_sol = (
U256::from(position.trace_index(self.max_depth)),
U256::from(self.state_at(position)?[0]),
U256::from_be_bytes(state),
);
let mut state_hash = keccak256(AlphabetClaimConstruction::abi_encode(&state_sol));
state_hash[0] = VMStatus::Invalid as u8;
Ok(state_hash)
}

fn proof_at(&self, position: Position) -> anyhow::Result<Arc<[u8]>> {
async fn proof_at(&self, _: Position) -> Result<Arc<[u8]>> {
Ok(Arc::new([]))
}
}
Expand All @@ -74,32 +70,35 @@ mod test {
use crate::compute_gindex;
use alloy_primitives::hex;

#[test]
fn alphabet_encoding() {
#[tokio::test]
async fn alphabet_encoding() {
let provider = AlphabetTraceProvider {
absolute_prestate: b'a',
absolute_prestate: U256::from(b'a').to_be_bytes(),
max_depth: 4,
};

let prestate_sol = U256::from(provider.absolute_prestate()[0]);
let prestate_bytes: [u8; 32] = (*provider.absolute_prestate(0).await.unwrap())
.try_into()
.unwrap();
let prestate_sol = U256::from_be_bytes(prestate_bytes);
let prestate = <sol!(uint256)>::abi_encode(&prestate_sol);
assert_eq!(
hex!("0000000000000000000000000000000000000000000000000000000000000061"),
prestate.as_slice()
);

let mut prestate_hash = provider.absolute_prestate_hash();
let mut prestate_hash = provider.absolute_prestate_hash(0).await.unwrap();
prestate_hash[0] = VMStatus::Unfinished as u8;
assert_eq!(
hex!("03ecb75dd1820844c57b6762233d4e26853b3a7b8157bbd9f41f280a0f1cee9b"),
prestate_hash.as_slice()
);
}

#[test]
fn alphabet_trace_at() {
#[tokio::test]
async fn alphabet_trace_at() {
let provider = AlphabetTraceProvider {
absolute_prestate: b'a',
absolute_prestate: U256::from(b'a').to_be_bytes(),
max_depth: 4,
};

Expand All @@ -112,8 +111,11 @@ mod test {
keccak256(AlphabetClaimConstruction::abi_encode(&expected_encoded));
expected_hash[0] = VMStatus::Invalid as u8;

assert_eq!(provider.state_at(position).unwrap()[0], expected);
assert_eq!(provider.state_hash(position).unwrap(), expected_hash);
assert_eq!(
provider.state_at(position).await.unwrap(),
U256::from(expected).to_be_bytes::<32>().into()
);
assert_eq!(provider.state_hash(position).await.unwrap(), expected_hash);
}
}
}
51 changes: 51 additions & 0 deletions crates/fault/src/providers/mocks/mock_output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! This module contains the implementation of the [crate::TraceProvider] trait for serving mock output commitments.

use crate::{Gindex, Position, TraceProvider};
use alloy_primitives::U256;
use anyhow::Result;
use durin_primitives::Claim;
use std::sync::Arc;

/// The [MockOutputTraceProvider] is a [TraceProvider] that provides mock L2 output commitments for a [Position].
pub struct MockOutputTraceProvider {
pub starting_block_number: u64,
pub leaf_depth: u8,
}

impl MockOutputTraceProvider {
pub fn new(starting_block_number: u64, leaf_depth: u8) -> Self {
Self {
starting_block_number,
leaf_depth,
}
}
}

#[async_trait::async_trait]
impl TraceProvider for MockOutputTraceProvider {
async fn absolute_prestate(&self, _: Position) -> Result<Arc<[u8]>> {
Ok(Arc::<[u8; 32]>::new(
U256::from(self.starting_block_number).to_be_bytes(),
))
}

async fn absolute_prestate_hash(&self, position: Position) -> Result<Claim> {
// The raw state is equivalent to the state hash in the output trace provider. It must be 32 bytes in size.
Ok((*self.absolute_prestate(position).await?).try_into()?)
}

async fn state_at(&self, position: Position) -> Result<Arc<[u8]>> {
let state =
U256::from(position.trace_index(self.leaf_depth) + self.starting_block_number + 1);
Ok(Arc::<[u8; 32]>::new(state.to_be_bytes()))
}

async fn state_hash(&self, position: Position) -> Result<Claim> {
// The raw state is equivalent to the state hash in the output trace provider. It must be 32 bytes in size.
Ok((*self.state_at(position).await?).try_into()?)
}

async fn proof_at(&self, _: Position) -> Result<Arc<[u8]>> {
unimplemented!("Proofs are not supported for the OutputTraceProvider")
}
}
7 changes: 7 additions & 0 deletions crates/fault/src/providers/mocks/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//! Mock implementations of the [crate::TraceProvider] trait for testing.

mod alphabet;
pub use self::alphabet::AlphabetTraceProvider;

mod mock_output;
pub use self::mock_output::MockOutputTraceProvider;
13 changes: 11 additions & 2 deletions crates/fault/src/providers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
//! This modules contains trace providers for the variants of the [crate::FaultDisputeGame].

mod alphabet;
pub use self::alphabet::AlphabetTraceProvider;
mod split;
pub use self::split::SplitTraceProvider;

mod output;
pub use self::output::OutputTraceProvider;

mod cannon;
pub use self::cannon::CannonTraceProvider;

mod mocks;
pub use self::mocks::{AlphabetTraceProvider, MockOutputTraceProvider};
Loading
Loading