Skip to content

Commit

Permalink
refac to make the main loop cleaner
Browse files Browse the repository at this point in the history
  • Loading branch information
fakedev9999 committed Feb 14, 2025
1 parent 1d3eee9 commit be7e63f
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 85 deletions.
51 changes: 33 additions & 18 deletions book/fault_proofs/proposer.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,10 @@ The proposer will run indefinitely, creating new games and optionally resolving
## Features

### Game Creation
- Creates new dispute games at configurable block intervals
- Computes L2 output roots for game proposals
- Ensures proper game sequencing with parent-child relationships
- Handles bond requirements for game creation
- Creates new dispute games at configurable block intervals.
- Computes L2 output roots for game proposals.
- Ensures proper game sequencing with parent-child relationships.
- Handles bond requirements for game creation.

### Game Resolution
When enabled (`ENABLE_GAME_RESOLUTION=true`), the proposer:
Expand Down Expand Up @@ -117,23 +117,38 @@ Errors are logged with appropriate context to aid in debugging.
## Architecture

The proposer is built around the `OPSuccinctProposer` struct which manages:
- Configuration state
- Wallet management for transactions
- Game creation and resolution logic
- Chain monitoring and interval management
- Configuration state.
- Wallet management for transactions.
- Game creation and resolution logic.
- Chain monitoring and interval management.

Key components:
- `ProposerConfig`: Handles environment-based configuration
- `create_game`: Manages game creation with proper bonding
- `resolve_unchallenged_games`: Handles game resolution logic
- `should_attempt_resolution`: Determines if games can be resolved
- `run`: Main loop managing the proposer's operation
- `ProposerConfig`: Handles environment-based configuration.
- `handle_game_creation`: Main function for proposing new games that:
- Monitors the L2 chain's safe head.
- Determines appropriate block numbers for proposals.
- Creates new games with proper parent-child relationships.
- `handle_game_resolution`: Main function for resolving games that:
- Checks if resolution is enabled.
- Manages resolution of unchallenged games.
- Respects parent-child relationships.
- `run`: Main loop that:
- Runs at configurable intervals.
- Handles both game creation and resolution.
- Provides error isolation between creation and resolution tasks.

### Helper Functions
- `create_game`: Creates individual games with proper bonding.
- `try_resolve_unchallenged_game`: Attempts to resolve a single game.
- `should_attempt_resolution`: Determines if games can be resolved based on parent status.
- `resolve_unchallenged_games`: Manages batch resolution of games.

## Development

When developing or modifying the proposer:
1. Ensure all environment variables are properly set
2. Test with a local L1/L2 setup first
3. Monitor logs for proper operation
4. Test game creation and resolution separately
5. Verify proper handling of edge cases (network issues, invalid responses, etc.)
1. Ensure all environment variables are properly set.
2. Test with a local L1/L2 setup first.
3. Monitor logs for proper operation.
4. Test game creation and resolution separately.
5. Verify proper handling of edge cases (network issues, invalid responses, etc.).
6. Note that game creation and resolution are handled independently, with separate error handling.
141 changes: 74 additions & 67 deletions fault_proof/bin/proposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,76 @@ where
Ok(())
}

/// Handles the creation of a new game if conditions are met
async fn handle_game_creation(&self) -> Result<()> {
let _span = tracing::info_span!("[[Proposing]]").entered();

// Get the safe L2 head block number
let safe_l2_head_block_number = self
.l2_provider
.get_l2_block_by_number(BlockNumberOrTag::Safe)
.await?
.header
.number;
tracing::debug!("Safe L2 head block number: {:?}", safe_l2_head_block_number);

// Get the latest valid proposal
let latest_valid_proposal = self
.factory
.get_latest_valid_proposal(self.l1_provider.clone(), self.l2_provider.clone())
.await?;

// Determine next block number and parent game index
//
// Two cases based on the result of `get_latest_valid_proposal`:
// 1. With existing valid proposal:
// - Block number = latest valid proposal's block + proposal interval
// - Parent = latest valid game's index
//
// 2. Without valid proposal (first game or all existing games being faulty):
// - Block number = genesis block + proposal interval
// - Parent = u32::MAX (special value indicating no parent)
let (next_l2_block_number_for_proposal, parent_game_index) = match latest_valid_proposal {
Some((latest_block, latest_game_idx)) => (
latest_block + U256::from(self.config.proposal_interval_in_blocks),
latest_game_idx.to::<u32>(),
),
None => {
let genesis_l2_block_number = self
.factory
.get_genesis_l2_block_number(self.config.game_type, self.l1_provider.clone())
.await?;

(
genesis_l2_block_number
.checked_add(U256::from(self.config.proposal_interval_in_blocks))
.unwrap(),
u32::MAX,
)
}
};

// There's always a new game to propose, as the chain is always moving forward from the genesis block set for the game type
// Only create a new game if the safe L2 head block number is greater than the next L2 block number for proposal
if U256::from(safe_l2_head_block_number) > next_l2_block_number_for_proposal {
self.create_game(next_l2_block_number_for_proposal, parent_game_index)
.await?;
}

Ok(())
}

/// Handles the resolution of all eligible unchallenged games
async fn handle_game_resolution(&self) -> Result<()> {
// Only resolve games if the config is enabled
if !self.config.enable_game_resolution {
return Ok(());
}

let _span = tracing::info_span!("[[Resolving]]").entered();
self.resolve_unchallenged_games().await
}

/// Runs the proposer indefinitely.
async fn run(&self) -> Result<()> {
tracing::info!("OP Succinct Proposer running...");
Expand All @@ -232,75 +302,12 @@ where
loop {
interval.tick().await;

// Propose a new game
{
let _span = tracing::info_span!("[[Proposing]]").entered();

let safe_l2_head_block_number = self
.l2_provider
.get_l2_block_by_number(BlockNumberOrTag::Safe)
.await?
.header
.number;
tracing::debug!("Safe L2 head block number: {:?}", safe_l2_head_block_number);

// Get the latest valid proposal
let latest_valid_proposal = self
.factory
.get_latest_valid_proposal(self.l1_provider.clone(), self.l2_provider.clone())
.await?;

// Determine the next L2 block number and parent game index for the new proposal
//
// Two cases based on the result of `get_latest_valid_proposal`:
// 1. With existing valid proposal:
// - Block number = latest valid proposal's block + proposal interval
// - Parent = latest valid game's index
//
// 2. Without valid proposal (first game or all existing games being faulty):
// - Block number = genesis block + proposal interval
// - Parent = u32::MAX (special value indicating no parent)
let (next_l2_block_number_for_proposal, parent_game_index) =
match latest_valid_proposal {
Some((latest_block, latest_game_idx)) => (
latest_block + U256::from(self.config.proposal_interval_in_blocks),
latest_game_idx.to::<u32>(),
),
None => {
let genesis_l2_block_number = self
.factory
.get_genesis_l2_block_number(
self.config.game_type,
self.l1_provider.clone(),
)
.await?;

(
genesis_l2_block_number
.checked_add(U256::from(
self.config.proposal_interval_in_blocks,
))
.unwrap(),
u32::MAX,
)
}
};

// There's always a new game to propose, as the chain is always moving forward from the genesis block set for the game type
// Only create a new game if the safe L2 head block number is greater than the next L2 block number for proposal
if U256::from(safe_l2_head_block_number) > next_l2_block_number_for_proposal {
self.create_game(next_l2_block_number_for_proposal, parent_game_index)
.await?;
}
if let Err(e) = self.handle_game_creation().await {
tracing::warn!("Failed to handle game creation: {:?}", e);
}

// Only attempt game resolution if enabled
if self.config.enable_game_resolution {
let _span = tracing::info_span!("[[Resolving]]").entered();

if let Err(e) = self.resolve_unchallenged_games().await {
tracing::warn!("Failed to resolve unchallenged games: {:?}", e);
}
if let Err(e) = self.handle_game_resolution().await {
tracing::warn!("Failed to handle game resolution: {:?}", e);
}
}
}
Expand Down

0 comments on commit be7e63f

Please sign in to comment.