Skip to content

Commit

Permalink
✨ Return step information in FaultSolverResponse
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Sep 9, 2023
1 parent 4d7bc46 commit 19dbbc3
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 22 deletions.
14 changes: 9 additions & 5 deletions crates/fault/src/providers/alphabet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{Gindex, Position, TraceProvider, VMStatus};
use alloy_primitives::{keccak256, U256};
use alloy_sol_types::{sol, SolType};
use durin_primitives::Claim;
use std::convert::TryInto;
use std::{convert::TryInto, sync::Arc};

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

Expand All @@ -32,8 +32,8 @@ impl AlphabetTraceProvider {
}

impl TraceProvider<[u8; 1]> for AlphabetTraceProvider {
fn absolute_prestate(&self) -> [u8; 1] {
[self.absolute_prestate]
fn absolute_prestate(&self) -> Arc<[u8; 1]> {
Arc::new([self.absolute_prestate])
}

fn absolute_prestate_hash(&self) -> Claim {
Expand All @@ -43,14 +43,14 @@ impl TraceProvider<[u8; 1]> for AlphabetTraceProvider {
prestate_hash
}

fn state_at(&self, position: Position) -> anyhow::Result<[u8; 1]> {
fn state_at(&self, position: Position) -> anyhow::Result<Arc<[u8; 1]>> {
let absolute_prestate = self.absolute_prestate as u64;
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([state])
Ok(Arc::new([state]))
}

fn state_hash(&self, position: Position) -> anyhow::Result<Claim> {
Expand All @@ -62,6 +62,10 @@ impl TraceProvider<[u8; 1]> for AlphabetTraceProvider {
state_hash[0] = VMStatus::Invalid as u8;
Ok(state_hash)
}

fn proof_at(&self, position: Position) -> anyhow::Result<Arc<[u8]>> {
Ok(Arc::new([]))
}
}

#[cfg(test)]
Expand Down
148 changes: 136 additions & 12 deletions crates/fault/src/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
TraceProvider,
};
use durin_primitives::{Claim, DisputeGame, DisputeSolver};
use std::marker::PhantomData;
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
Expand All @@ -22,15 +22,15 @@ where
_phantom: PhantomData<T>,
}

impl<T, P> DisputeSolver<FaultDisputeState, FaultSolverResponse> for FaultDisputeSolver<T, P>
impl<T, P> DisputeSolver<FaultDisputeState, FaultSolverResponse<T>> for FaultDisputeSolver<T, P>
where
T: AsRef<[u8]>,
P: TraceProvider<T>,
{
fn available_moves(
&self,
game: &mut FaultDisputeState,
) -> anyhow::Result<Vec<FaultSolverResponse>> {
) -> 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();
Expand Down Expand Up @@ -79,7 +79,7 @@ where
world: &mut FaultDisputeState,
claim_index: usize,
attacking_root: bool,
) -> anyhow::Result<FaultSolverResponse> {
) -> anyhow::Result<FaultSolverResponse<T>> {
// Fetch the maximum depth of the game's position tree.
let max_depth = world.max_depth;

Expand Down Expand Up @@ -122,11 +122,37 @@ where

// If the next move will be at the max depth of the game, then the proper move is to
// perform a VM step against the claim. Otherwise, move in the appropriate direction.
//
// TODO(clabby): Return the data necessary for the inputs to the contract calls in the
// `FaultSolverResponse` variants.
if claim_depth == max_depth - 1 {
Ok(FaultSolverResponse::Step(is_attack))
if claim_depth == max_depth {
// There is a special case when we are attacking the first leaf claim at the max
// level where we have to provide the absolute prestate. Otherwise, we can derive
// the prestate position based off of `is_attack` and the incorrect claim's
// position.
let (pre_state, proof) = if claim.position.index_at_depth() == 0 && is_attack {
let pre_state = self.provider.absolute_prestate();
// TODO(clabby): There may be a proof for the absolute prestate in Cannon.
let proof: Arc<[u8]> = Arc::new([]);

(pre_state, proof)
} else {
// If the move is an attack, the pre-state is left of the attacked claim's
// position. If the move is a defense, the pre-state for the step is at the
// claim's position.
//
// SAFETY: We can subtract 1 here due to the above check - we will never
// underflow the level.
let pre_state_pos = claim.position - is_attack as u128;

let pre_state = Self::fetch_state_at(&self.provider, pre_state_pos, claim)?;
let proof = Self::fetch_proof_at(&self.provider, pre_state_pos, claim)?;
(pre_state, proof)
};

Ok(FaultSolverResponse::Step(
is_attack,
claim_index,
pre_state,
proof,
))
} else {
// Fetch the local trace provider's opinion of the state hash at the move's position.
let claim_hash =
Expand Down Expand Up @@ -158,6 +184,32 @@ where
})?;
Ok(state_hash)
}

#[inline]
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]
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.
Expand Down Expand Up @@ -206,7 +258,7 @@ mod test {
);

let moves = solver.available_moves(&mut state).unwrap();
assert_eq!(&[expected_move], moves.as_slice());
assert_eq!(&[expected_move], moves.as_ref());
}
}

Expand Down Expand Up @@ -255,7 +307,7 @@ mod test {
);

let moves = solver.available_moves(&mut state).unwrap();
assert_eq!(&[expected_move], moves.as_slice());
assert_eq!(&[expected_move], moves.as_ref());
}
}

Expand Down Expand Up @@ -310,7 +362,79 @@ mod test {
FaultSolverResponse::Move(false, 2, solver.provider.state_hash(10).unwrap()),
FaultSolverResponse::Skip(3)
],
moves.as_slice()
moves.as_ref()
);
}

#[test]
fn available_moves_static_step() {
let (solver, root_claim) = mocks();
let cases = [
(
FaultSolverResponse::Step(true, 4, Arc::new([b'a']), Arc::new([])),
true,
),
(
FaultSolverResponse::Step(false, 4, Arc::new([b'b']), Arc::new([])),
false,
),
];

for (expected_response, wrong_leaf) in cases {
let mut state = FaultDisputeState::new(
vec![
// Invalid root claim - ATTACK
ClaimData {
parent_index: u32::MAX,
visited: true,
value: root_claim,
position: 1,
clock: 0,
},
// Honest Attack
ClaimData {
parent_index: 0,
visited: true,
value: solver.provider.state_hash(2).unwrap(),
position: 2,
clock: 0,
},
// Wrong level; Wrong claim - ATTACK
ClaimData {
parent_index: 1,
visited: true,
value: root_claim,
position: 4,
clock: 0,
},
// Honest Attack
ClaimData {
parent_index: 2,
visited: true,
value: solver.provider.state_hash(8).unwrap(),
position: 8,
clock: 0,
},
// Wrong level; Wrong claim - ATTACK STEP
ClaimData {
parent_index: 3,
visited: false,
value: if wrong_leaf {
root_claim
} else {
solver.provider.state_hash(16).unwrap()
},
position: 16,
clock: 0,
},
],
root_claim,
GameStatus::InProgress,
4,
);

let moves = solver.available_moves(&mut state).unwrap();
assert_eq!(&[expected_response], moves.as_ref());
}
}
}
8 changes: 6 additions & 2 deletions crates/fault/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

use crate::{state::ClaimData, Position};
use durin_primitives::{Claim, DisputeGame};
use std::sync::Arc;

/// A [FaultDisputeGame] is a [DisputeGame] that is played over a FaultVM backend. This
/// trait extends the [DisputeGame] trait with functionality that is specific to the
Expand All @@ -18,16 +19,19 @@ pub trait FaultDisputeGame: DisputeGame {
/// [Position] within a [FaultDisputeGame].
pub trait TraceProvider<P: AsRef<[u8]>> {
/// Returns the raw absolute prestate (in bytes).
fn absolute_prestate(&self) -> P;
fn absolute_prestate(&self) -> Arc<P>;

/// Returns the absolute prestate hash.
fn absolute_prestate_hash(&self) -> Claim;

/// Returns the raw state (in bytes) at the given position.
fn state_at(&self, position: Position) -> anyhow::Result<P>;
fn state_at(&self, position: Position) -> anyhow::Result<Arc<P>>;

/// Returns the state hash at the given position.
fn state_hash(&self, position: Position) -> anyhow::Result<Claim>;

/// Returns the raw proof for the commitment at the given position.
fn proof_at(&self, position: Position) -> anyhow::Result<Arc<[u8]>>;
}

/// The [Gindex] trait defines the interface of a generalized index within a binary tree.
Expand Down
6 changes: 4 additions & 2 deletions crates/fault/src/types.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! The position module holds the types specific to the [crate::FaultDisputeGame] solver.

use std::sync::Arc;

use crate::ChessClock;
use crate::Gindex;
use durin_primitives::Claim;
Expand All @@ -10,14 +12,14 @@ pub type Clock = u128;
/// The [FaultSolverResponse] enum describes the response that a solver should
/// return when asked to make a move.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FaultSolverResponse {
pub enum FaultSolverResponse<T: AsRef<[u8]>> {
/// A response indicating that the proper move is to attack or defend the given claim.
Move(bool, usize, Claim),
/// A response indicating that the proper move is to skip the given claim.
Skip(usize),
/// A response indicating that the proper move is to perform a VM step against
/// the given claim.
Step(bool),
Step(bool, usize, Arc<T>, Arc<[u8]>),
}

/// The [VMStatus] enum describes the status of a VM at a given position.
Expand Down
3 changes: 2 additions & 1 deletion crates/primitives/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! The traits module contains traits used throughout the library.

use crate::{dispute_game::Claim, GameStatus};
use std::sync::Arc;

/// The [DisputeGame] trait is the highest level trait in the library, describing
/// the state of a simple primitive dispute. It has several key properties:
Expand Down Expand Up @@ -38,5 +39,5 @@ pub trait DisputeSolver<DG: DisputeGame, R> {
/// 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.
fn available_moves(&self, game: &mut DG) -> anyhow::Result<Vec<R>>;
fn available_moves(&self, game: &mut DG) -> anyhow::Result<Arc<[R]>>;
}

0 comments on commit 19dbbc3

Please sign in to comment.