Skip to content

Commit

Permalink
✨ Fully generic solver
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Sep 9, 2023
1 parent f48fe87 commit fa3d332
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 79 deletions.
87 changes: 30 additions & 57 deletions crates/fault/src/solver.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,52 @@
//! This module contains the various implementations of the [crate::FaultDisputeSolver] trait.

#![allow(dead_code, unused_variables)]

use crate::{
ClaimData, FaultClaimSolver, FaultDisputeGame, FaultDisputeState, FaultSolverResponse,
Position, TraceProvider,
FaultClaimSolver, FaultDisputeGame, FaultDisputeState, FaultSolverResponse, Position,
TraceProvider,
};
use durin_primitives::{Claim, DisputeGame, DisputeSolver};
use durin_primitives::{DisputeGame, DisputeSolver};
use std::{marker::PhantomData, sync::Arc};

/// A [FaultDisputeSolver] is a [DisputeSolver] that is played over a fault proof VM backend. The
/// solver is responsible for honestly responding to any given [ClaimData] in a given
/// [FaultDisputeState]. It uses a [TraceProvider] to fetch the absolute prestate of the VM as
/// well as the state at any given [Position] within the tree.
pub struct FaultDisputeSolver<T, P>
pub struct FaultDisputeSolver<T, P, S>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
S: FaultClaimSolver<T, P>,
{
pub inner: S,
_phantom_t: PhantomData<T>,
_phantom_p: PhantomData<P>,
}

impl<T, P, S> FaultDisputeSolver<T, P, S>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
S: FaultClaimSolver<T, P>,
{
pub provider: P,
_phantom: PhantomData<T>,
pub fn provider(&self) -> &P {
self.inner.provider()
}
}

impl<T, P> DisputeSolver<FaultDisputeState, FaultSolverResponse<T>> for FaultDisputeSolver<T, P>
impl<T, P, S> DisputeSolver<FaultDisputeState, FaultSolverResponse<T>>
for FaultDisputeSolver<T, P, S>
where
FaultDisputeSolver<T, P>: FaultClaimSolver<T>,
T: AsRef<[u8]>,
P: TraceProvider<T>,
S: FaultClaimSolver<T, P>,
{
fn available_moves(
&self,
game: &mut FaultDisputeState,
) -> anyhow::Result<Arc<[FaultSolverResponse<T>]>> {
// Fetch the local opinion on the root claim.
let attacking_root =
self.provider.state_hash(Self::ROOT_CLAIM_POSITION)? != game.root_claim();
self.provider().state_hash(Self::ROOT_CLAIM_POSITION)? != game.root_claim();

// Fetch the indices of all unvisited claims within the world DAG.
let unvisited_indices = game
Expand All @@ -47,63 +59,24 @@ where
// Solve each unvisited claim, set the visited flag, and return the responses.
unvisited_indices
.iter()
.map(|claim_index| self.solve_claim(game, *claim_index, attacking_root))
.map(|claim_index| self.inner.solve_claim(game, *claim_index, attacking_root))
.collect()
}
}

impl<T, P> FaultDisputeSolver<T, P>
impl<T, P, S> FaultDisputeSolver<T, P, S>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
S: FaultClaimSolver<T, P>,
{
const ROOT_CLAIM_POSITION: Position = 1;

pub fn new(provider: P) -> Self {
pub fn new(claim_solver: S) -> Self {
Self {
provider,
_phantom: PhantomData,
inner: claim_solver,
_phantom_t: PhantomData,
_phantom_p: PhantomData,
}
}

/// Fetches the state hash at a given position from a [TraceProvider].
/// If the fetch fails, the claim is marked as unvisited and the error is returned.
#[inline]
pub(crate) fn fetch_state_hash(
provider: &P,
position: Position,
observed_claim: &mut ClaimData,
) -> anyhow::Result<Claim> {
let state_hash = provider.state_hash(position).map_err(|e| {
observed_claim.visited = false;
e
})?;
Ok(state_hash)
}

#[inline]
pub(crate) fn fetch_state_at(
provider: &P,
position: Position,
observed_claim: &mut ClaimData,
) -> anyhow::Result<Arc<T>> {
let state_at = provider.state_at(position).map_err(|e| {
observed_claim.visited = false;
e
})?;
Ok(state_at)
}

#[inline]
pub(crate) fn fetch_proof_at(
provider: &P,
position: Position,
observed_claim: &mut ClaimData,
) -> anyhow::Result<Arc<[u8]>> {
let proof_at = provider.proof_at(position).map_err(|e| {
observed_claim.visited = false;
e
})?;
Ok(proof_at)
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
//! Implementation of the [FaultClaimSolver] trait on the [FaultDisputeSolver].

#![allow(dead_code, unused_variables)]

use crate::{
FaultClaimSolver, FaultDisputeGame, FaultDisputeSolver, FaultDisputeState, FaultSolverResponse,
Gindex, TraceProvider,
ClaimData, FaultClaimSolver, FaultDisputeGame, FaultDisputeState, FaultSolverResponse, Gindex,
Position, TraceProvider,
};
use std::sync::Arc;
use durin_primitives::Claim;
use std::{marker::PhantomData, sync::Arc};

/// The alpha claim solver is the first iteration of the Fault dispute game solver used
/// in the alpha release of the Fault proof system on Optimism.
struct AlphaClaimSolver<T, P>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
{
provider: P,
_phantom: PhantomData<T>,
}

impl<T, P> FaultClaimSolver<T> for FaultDisputeSolver<T, P>
impl<T, P> FaultClaimSolver<T, P> for AlphaClaimSolver<T, P>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
Expand Down Expand Up @@ -115,19 +129,85 @@ where
))
}
}

fn provider(&self) -> &P {
&self.provider
}
}

impl<T, P> AlphaClaimSolver<T, P>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
{
fn new(provider: P) -> Self {
Self {
provider,
_phantom: PhantomData,
}
}

/// Fetches the state hash at a given position from a [TraceProvider].
/// If the fetch fails, the claim is marked as unvisited and the error is returned.
#[inline]
pub(crate) fn fetch_state_hash(
provider: &P,
position: Position,
observed_claim: &mut ClaimData,
) -> anyhow::Result<Claim> {
let state_hash = provider.state_hash(position).map_err(|e| {
observed_claim.visited = false;
e
})?;
Ok(state_hash)
}

#[inline]
pub(crate) fn fetch_state_at(
provider: &P,
position: Position,
observed_claim: &mut ClaimData,
) -> anyhow::Result<Arc<T>> {
let state_at = provider.state_at(position).map_err(|e| {
observed_claim.visited = false;
e
})?;
Ok(state_at)
}

#[inline]
pub(crate) fn fetch_proof_at(
provider: &P,
position: Position,
observed_claim: &mut ClaimData,
) -> anyhow::Result<Arc<[u8]>> {
let proof_at = provider.proof_at(position).map_err(|e| {
observed_claim.visited = false;
e
})?;
Ok(proof_at)
}
}

// TODO: prop tests for solving claims.
#[cfg(test)]
mod test {
use super::*;
use crate::{providers::AlphabetTraceProvider, ClaimData};
use crate::{providers::AlphabetTraceProvider, ClaimData, FaultDisputeSolver};
use alloy_primitives::hex;
use durin_primitives::{Claim, DisputeSolver, GameStatus};

fn mocks() -> (FaultDisputeSolver<[u8; 1], AlphabetTraceProvider>, Claim) {
fn mocks() -> (
FaultDisputeSolver<
[u8; 1],
AlphabetTraceProvider,
AlphaClaimSolver<[u8; 1], AlphabetTraceProvider>,
>,
Claim,
) {
let provider = AlphabetTraceProvider::new(b'a', 4);
let solver = FaultDisputeSolver::new(provider);
let claim_solver = AlphaClaimSolver::new(provider);
let solver = FaultDisputeSolver::new(claim_solver);
let root_claim = Claim::from_slice(&hex!(
"c0ffee00c0de0000000000000000000000000000000000000000000000000000"
));
Expand All @@ -139,12 +219,12 @@ mod test {
let (solver, root_claim) = mocks();
let moves = [
(
solver.provider.state_hash(1).unwrap(),
solver.provider().state_hash(1).unwrap(),
FaultSolverResponse::Skip(0),
),
(
root_claim,
FaultSolverResponse::Move(true, 0, solver.provider.state_hash(2).unwrap()),
FaultSolverResponse::Move(true, 0, solver.provider().state_hash(2).unwrap()),
),
];

Expand Down Expand Up @@ -172,12 +252,12 @@ mod test {
let (solver, root_claim) = mocks();
let moves = [
(
solver.provider.state_hash(4).unwrap(),
FaultSolverResponse::Move(false, 2, solver.provider.state_hash(10).unwrap()),
solver.provider().state_hash(4).unwrap(),
FaultSolverResponse::Move(false, 2, solver.provider().state_hash(10).unwrap()),
),
(
root_claim,
FaultSolverResponse::Move(true, 2, solver.provider.state_hash(8).unwrap()),
FaultSolverResponse::Move(true, 2, solver.provider().state_hash(8).unwrap()),
),
];

Expand All @@ -194,7 +274,7 @@ mod test {
ClaimData {
parent_index: 0,
visited: true,
value: solver.provider.state_hash(2).unwrap(),
value: solver.provider().state_hash(2).unwrap(),
position: 2,
clock: 0,
},
Expand Down Expand Up @@ -241,7 +321,7 @@ mod test {
ClaimData {
parent_index: 1,
visited: false,
value: solver.provider.state_hash(4).unwrap(),
value: solver.provider().state_hash(4).unwrap(),
position: 4,
clock: 0,
},
Expand All @@ -262,9 +342,9 @@ mod test {
let moves = solver.available_moves(&mut state).unwrap();
assert_eq!(
&[
FaultSolverResponse::Move(true, 0, solver.provider.state_hash(2).unwrap()),
FaultSolverResponse::Move(true, 0, solver.provider().state_hash(2).unwrap()),
FaultSolverResponse::Skip(1),
FaultSolverResponse::Move(false, 2, solver.provider.state_hash(10).unwrap()),
FaultSolverResponse::Move(false, 2, solver.provider().state_hash(10).unwrap()),
FaultSolverResponse::Skip(3)
],
moves.as_ref()
Expand Down Expand Up @@ -300,7 +380,7 @@ mod test {
ClaimData {
parent_index: 0,
visited: true,
value: solver.provider.state_hash(2).unwrap(),
value: solver.provider().state_hash(2).unwrap(),
position: 2,
clock: 0,
},
Expand All @@ -316,7 +396,7 @@ mod test {
ClaimData {
parent_index: 2,
visited: true,
value: solver.provider.state_hash(8).unwrap(),
value: solver.provider().state_hash(8).unwrap(),
position: 8,
clock: 0,
},
Expand All @@ -327,7 +407,7 @@ mod test {
value: if wrong_leaf {
root_claim
} else {
solver.provider.state_hash(16).unwrap()
solver.provider().state_hash(16).unwrap()
},
position: 16,
clock: 0,
Expand Down
4 changes: 2 additions & 2 deletions crates/fault/src/solvers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! This module contains implementations of the [crate::FaultClaimSolver] trait for various
//! solving methods and resolvers.

mod alphabet;
pub use self::alphabet::*;
mod alpha;
pub use self::alpha::*;
6 changes: 5 additions & 1 deletion crates/fault/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub trait FaultDisputeGame: DisputeGame {

/// A [FaultClaimSolver] is a solver that finds the correct response to a given [durin_primitives::Claim]
/// within a [FaultDisputeGame].
pub trait FaultClaimSolver<T: AsRef<[u8]>> {
pub trait FaultClaimSolver<T: AsRef<[u8]>, P: TraceProvider<T>> {
/// Finds the best move against a [crate::ClaimData] in a given [FaultDisputeState].
///
/// ### Takes
Expand All @@ -33,6 +33,10 @@ pub trait FaultClaimSolver<T: AsRef<[u8]>> {
claim_index: usize,
attacking_root: bool,
) -> anyhow::Result<FaultSolverResponse<T>>;

/// Returns a shared reference to the [TraceProvider] that the solver uses to fetch
/// the state of the VM and commitments to it.
fn provider(&self) -> &P;
}

/// A [TraceProvider] is a type that can provide the raw state (in bytes) at a given
Expand Down

0 comments on commit fa3d332

Please sign in to comment.