Skip to content

Commit

Permalink
Prevent recursive state reconstruction
Browse files Browse the repository at this point in the history
  • Loading branch information
jbearer committed Oct 17, 2024
1 parent 58c0b32 commit 0277a67
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 17 deletions.
79 changes: 64 additions & 15 deletions sequencer/src/api/sql.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use anyhow::{bail, ensure, Context};
use async_std::sync::Arc;
use async_trait::async_trait;
use committable::{Commitment, Committable};
use espresso_types::{
v0_3::ChainConfig, BlockMerkleTree, FeeAccount, FeeMerkleTree, Leaf, NodeState, ValidatedState,
get_l1_deposits, v0_3::ChainConfig, BlockMerkleTree, FeeAccount, FeeMerkleTree, Leaf,
NodeState, ValidatedState,
};
use hotshot::traits::ValidatedState as _;
use hotshot_query_service::{
Expand All @@ -26,14 +26,14 @@ use jf_merkle_tree::{
prelude::MerkleNode, ForgetableMerkleTreeScheme, ForgetableUniversalMerkleTreeScheme,
LookupResult, MerkleTreeScheme,
};
use std::collections::VecDeque;
use std::collections::{HashSet, VecDeque};

use super::{
data_source::{Provider, SequencerDataSource},
BlocksFrontier,
};
use crate::{
catchup::{CatchupStorage, SqlStateCatchup},
catchup::{CatchupStorage, NullStateCatchup},
persistence::{
sql::{sql_param, Options},
ChainConfigPersistence,
Expand Down Expand Up @@ -333,9 +333,16 @@ async fn reconstruct_state<'a>(
// Get the initial state.
let mut parent = from_leaf;
let mut state = ValidatedState::from_header(parent.block_header());

// Pre-load the state with the accounts we care about to ensure they will be present in the
// final state.
state.fee_merkle_tree = load_accounts(tx, from_height, accounts)
let mut accounts = accounts.iter().copied().collect::<HashSet<_>>();
// Add in all the accounts we will need to replay any of the headers, to ensure that we don't
// need to do catchup recursively.
accounts.insert(instance.chain_config.fee_recipient);
accounts.extend(fee_account_dependencies(instance, parent, &leaves).await);
let accounts = accounts.into_iter().collect::<Vec<_>>();
state.fee_merkle_tree = load_accounts(tx, from_height, &accounts)
.await
.context("unable to reconstruct state because accounts are not available at origin")?
.0;
Expand All @@ -344,26 +351,68 @@ async fn reconstruct_state<'a>(
"loaded fee state does not match parent header"
);

// For catchup that is required during the following state replay, use the local database via
// this transaction.
let peers = SqlStateCatchup::new(Arc::new(tx), Default::default());
// We need the blocks frontier as well, to apply the STF.
let frontier = load_frontier(tx, from_height)
.await
.context("unable to reconstruct state because frontier is not available at origin")?;
match frontier
.proof
.first()
.context("empty proof for frontier at origin")?
{
MerkleNode::Leaf { pos, elem, .. } => state
.block_merkle_tree
.remember(*pos, *elem, frontier)
.context("failed to remember frontier")?,
_ => bail!("invalid frontier proof"),
}

// Apply subsequent headers to compute the later state.
for proposal in &leaves {
state = compute_state_update(&state, instance, &peers, parent, proposal)
.await
.context(format!(
"unable to reconstruct state because state update {} failed",
proposal.height(),
))?
.0;
state = compute_state_update(
&state,
instance,
&NullStateCatchup::default(),
parent,
proposal,
)
.await
.context(format!(
"unable to reconstruct state because state update {} failed",
proposal.height(),
))?
.0;
parent = proposal;
}

tracing::info!(from_height, ?to_view, "successfully reconstructed state");
Ok((state, to_leaf))
}

/// Get the set of all fee accounts on which the STF depends, applied to the given list of headers.
async fn fee_account_dependencies(
instance: &NodeState,
mut parent: &Leaf,
leaves: impl IntoIterator<Item = &Leaf>,
) -> HashSet<FeeAccount> {
let mut accounts = HashSet::default();
for proposal in leaves {
accounts.extend(
get_l1_deposits(
instance,
proposal.block_header(),
parent,
instance.chain_config.fee_contract,
)
.await
.into_iter()
.map(|fee| fee.account()),
);
parent = proposal;
}
accounts
}

async fn get_leaf_from_proposal<'a>(
tx: &Transaction<'a>,
where_clause: &str,
Expand Down
41 changes: 41 additions & 0 deletions sequencer/src/catchup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,3 +387,44 @@ where
&self.backoff
}
}

/// Disable catchup entirely.
#[derive(Clone, Copy, Debug, Default)]
pub struct NullStateCatchup {
backoff: BackoffParams,
}

#[async_trait]
impl StateCatchup for NullStateCatchup {
async fn try_fetch_accounts(
&self,
_instance: &NodeState,
_height: u64,
_view: ViewNumber,
_fee_merkle_tree_root: FeeMerkleCommitment,
_account: &[FeeAccount],
) -> anyhow::Result<FeeMerkleTree> {
bail!("state catchup is disabled");
}

async fn try_remember_blocks_merkle_tree(
&self,
_instance: &NodeState,
_height: u64,
_view: ViewNumber,
_mt: &mut BlockMerkleTree,
) -> anyhow::Result<()> {
bail!("state catchup is disabled");
}

async fn try_fetch_chain_config(
&self,
_commitment: Commitment<ChainConfig>,
) -> anyhow::Result<ChainConfig> {
bail!("state catchup is disabled");
}

fn backoff(&self) -> &BackoffParams {
&self.backoff
}
}
5 changes: 4 additions & 1 deletion types/src/v0/impls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,7 @@ pub use auction::SolverAuctionResultsProvider;
pub use fee_info::{retain_accounts, FeeError};
pub use instance_state::{mock, NodeState};
pub use state::ProposalValidationError;
pub use state::{validate_proposal, BuilderValidationError, StateValidationError, ValidatedState};
pub use state::{
get_l1_deposits, validate_proposal, BuilderValidationError, StateValidationError,
ValidatedState,
};
2 changes: 1 addition & 1 deletion types/src/v0/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub mod traits;
mod utils;
pub use header::Header;
pub use impls::{
mock, retain_accounts, validate_proposal, BuilderValidationError, FeeError,
get_l1_deposits, mock, retain_accounts, validate_proposal, BuilderValidationError, FeeError,
ProposalValidationError, StateValidationError,
};
pub use utils::*;
Expand Down

0 comments on commit 0277a67

Please sign in to comment.