Skip to content

Commit

Permalink
✨ Track visited claims
Browse files Browse the repository at this point in the history
  • Loading branch information
clabby committed Sep 9, 2023
1 parent f3c3a30 commit ca6c28a
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 16 deletions.
120 changes: 105 additions & 15 deletions crates/fault/src/solver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
#![allow(dead_code, unused_variables)]

use crate::{
ClaimData, FaultDisputeGame, FaultDisputeState, FaultSolverResponse, Gindex, Position,
TraceProvider,
FaultDisputeGame, FaultDisputeState, FaultSolverResponse, Gindex, Position, TraceProvider,
};
use durin_primitives::{DisputeGame, DisputeSolver};
use std::marker::PhantomData;
Expand All @@ -29,16 +28,25 @@ where
{
fn available_moves(
&self,
game: &FaultDisputeState,
game: &mut FaultDisputeState,
) -> anyhow::Result<Vec<FaultSolverResponse>> {
// Fetch the local opinion on the root claim.
let attacking_root =
self.provider.state_hash(Self::ROOT_CLAIM_POSITION)? != game.root_claim();

game.state()
// Fetch the indices of all unvisited claims within the world DAG.
let unvisited_indices = game
.state()
.iter()
.filter(|c| !c.visited)
.map(|c| self.solve_claim(game, c, attacking_root))
.enumerate()
.filter(|(_, c)| !c.visited)
.map(|(i, _)| i)
.collect::<Vec<_>>();

// 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))
.collect()
}
}
Expand All @@ -60,12 +68,20 @@ where
#[inline]
fn solve_claim(
&self,
game: &FaultDisputeState,
claim: &ClaimData,
world: &mut FaultDisputeState,
claim_index: usize,
attacking_root: bool,
) -> anyhow::Result<FaultSolverResponse> {
let claim = world
.state_mut()
.get_mut(claim_index)
.ok_or(anyhow::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
// error is thrown.
claim.visited = true;

// In the case that the claim's opinion about the root claim is the same as the local
// opinion, we can skip the claim. It does not matter if this claim is valid or not
// because it supports the local opinion of the root claim. Countering it would put the
Expand All @@ -74,8 +90,19 @@ where
return Ok(FaultSolverResponse::Skip);
}

// If the claim's parent index is `u32::MAX`, it is the root claim. In this case, the only
// opportunity is to attack if we disagree with the root - there is no other valid move.
if claim.parent_index == u32::MAX && attacking_root {
return Ok(FaultSolverResponse::Attack);
}

// Fetch the local trace provider's opinion of the state hash at the claim's position
let self_state_hash = self.provider.state_hash(claim.position)?;
let self_state_hash = self.provider.state_hash(claim.position).map_err(|e| {
// Prior to returning the error, mark the claim as unvisited so that it can be
// re-visited by the solver in the future.
claim.visited = false;
e
})?;

let move_direction = if self_state_hash == claim.value {
// If the local opinion of the state hash at the claim's position is the same as the
Expand All @@ -92,7 +119,7 @@ where
//
// TODO(clabby): Return the data necessary for the inputs to the contract calls in the
// `FaultSolverResponse` variants.
if claim_depth == game.max_depth - 1 {
if claim_depth == world.max_depth - 1 {
Ok(FaultSolverResponse::Step(Box::new(move_direction)))
} else {
Ok(move_direction)
Expand All @@ -104,7 +131,7 @@ where
#[cfg(test)]
mod test {
use super::*;
use crate::providers::AlphabetTraceProvider;
use crate::{providers::AlphabetTraceProvider, ClaimData};
use alloy_primitives::hex;
use durin_primitives::{Claim, GameStatus};

Expand All @@ -129,7 +156,7 @@ mod test {
];

for (claim, expected_move) in moves {
let state = FaultDisputeState::new(
let mut state = FaultDisputeState::new(
vec![ClaimData {
parent_index: u32::MAX,
visited: false,
Expand All @@ -142,7 +169,7 @@ mod test {
4,
);

let moves = solver.available_moves(&state).unwrap();
let moves = solver.available_moves(&mut state).unwrap();
assert_eq!(&[expected_move], moves.as_slice());
}
}
Expand All @@ -159,7 +186,7 @@ mod test {
];

for (claim, expected_move) in moves {
let state = FaultDisputeState::new(
let mut state = FaultDisputeState::new(
vec![
ClaimData {
parent_index: u32::MAX,
Expand Down Expand Up @@ -188,8 +215,71 @@ mod test {
4,
);

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

#[test]
fn available_moves_static_many() {
let (solver, root_claim) = mocks();
let moves = [
(
solver.provider.state_hash(4).unwrap(),
FaultSolverResponse::Defend,
),
(root_claim, FaultSolverResponse::Attack),
];

let mut state = FaultDisputeState::new(
vec![
// Invalid root claim - ATTACK
ClaimData {
parent_index: u32::MAX,
visited: false,
value: root_claim,
position: 1,
clock: 0,
},
// Right level; Wrong claim - SKIP
ClaimData {
parent_index: 0,
visited: false,
value: root_claim,
position: 2,
clock: 0,
},
// Wrong level; Right claim - DEFEND
ClaimData {
parent_index: 1,
visited: false,
value: solver.provider.state_hash(4).unwrap(),
position: 4,
clock: 0,
},
// Right level; Wrong claim - SKIP
ClaimData {
parent_index: 3,
visited: false,
value: root_claim,
position: 8,
clock: 0,
},
],
root_claim,
GameStatus::InProgress,
4,
);

let moves = solver.available_moves(&mut state).unwrap();
assert_eq!(
&[
FaultSolverResponse::Attack,
FaultSolverResponse::Skip,
FaultSolverResponse::Defend,
FaultSolverResponse::Skip
],
moves.as_slice()
);
}
}
2 changes: 1 addition & 1 deletion crates/primitives/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,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: &DG) -> anyhow::Result<Vec<R>>;
fn available_moves(&self, game: &mut DG) -> anyhow::Result<Vec<R>>;
}

0 comments on commit ca6c28a

Please sign in to comment.