Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Str 1099 update get checkpoint info rpc #687

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/prover-client/src/operators/checkpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@
idx: checkpoint_idx,
l1_range: (start_l1_commitment, end_l1_commitment),
l2_range: (start_l2_commitment, end_l2_commitment),
commitment: None,
confirmation_status: None,
l1_reference: None,
confirmation_status: strata_rpc_types::RpcCheckpointConfStatus::Pending,

Check warning on line 164 in bin/prover-client/src/operators/checkpoint.rs

View check run for this annotation

Codecov / codecov/patch

bin/prover-client/src/operators/checkpoint.rs#L163-L164

Added lines #L163 - L164 were not covered by tests
};
let proof_ctx = self.construct_proof_ctx(&checkpoint_idx)?;

Expand Down
8 changes: 4 additions & 4 deletions bin/strata-client/src/rpc_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -648,7 +648,9 @@
// that behavior here
if let Some(last_checkpoint) = cstate.get_last_checkpoint() {
if last_checkpoint.batch_info.includes_l2_block(block_slot) {
return Ok(L2BlockStatus::Finalized(last_checkpoint.height));
return Ok(L2BlockStatus::Finalized(
last_checkpoint.l1_reference.block_height,
));

Check warning on line 653 in bin/strata-client/src/rpc_server.rs

View check run for this annotation

Codecov / codecov/patch

bin/strata-client/src/rpc_server.rs#L651-L653

Added lines #L651 - L653 were not covered by tests
}
}

Expand Down Expand Up @@ -911,9 +913,7 @@
Err(Error::MissingCheckpointProof(checkpoint_idx))?;
}

if entry.confirmation_status == CheckpointConfStatus::Confirmed
|| entry.confirmation_status == CheckpointConfStatus::Finalized
{
if entry.confirmation_status != CheckpointConfStatus::Pending {

Check warning on line 916 in bin/strata-client/src/rpc_server.rs

View check run for this annotation

Codecov / codecov/patch

bin/strata-client/src/rpc_server.rs#L916

Added line #L916 was not covered by tests
Err(Error::CheckpointAlreadyPosted(checkpoint_idx))?;
}

Expand Down
44 changes: 1 addition & 43 deletions crates/btcio/src/reader/handler.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use bitcoin::{consensus::serialize, hashes::Hash, Block};
use strata_l1tx::messages::{BlockData, L1Event};
use strata_primitives::{
batch::{verify_signed_checkpoint_sig, Checkpoint, CommitmentInfo, L1CommittedCheckpoint},
buf::Buf32,
l1::{
generate_l1_tx, HeaderVerificationState, L1BlockCommitment, L1BlockManifest,
L1HeaderRecord, L1Tx, ProtocolOperation,
L1HeaderRecord, L1Tx,
},
params::RollupParams,
};
use strata_state::sync_event::{EventSubmitter, SyncEvent};
use tracing::*;
Expand Down Expand Up @@ -81,46 +79,6 @@ async fn handle_blockdata<R: ReaderRpc>(
Ok(sync_evs)
}

/// Extracts checkpoints from the block.
fn find_checkpoints(blockdata: &BlockData, params: &RollupParams) -> Vec<L1CommittedCheckpoint> {
blockdata
.relevant_txs()
.iter()
.flat_map(|txref| {
txref.contents().protocol_ops().iter().map(|x| match x {
ProtocolOperation::Checkpoint(envelope) => Some((
envelope,
&blockdata.block().txdata[txref.index() as usize],
txref.index(),
)),
_ => None,
})
})
.filter_map(|ckpt_data| {
let (signed_checkpoint, tx, position) = ckpt_data?;
if !verify_signed_checkpoint_sig(signed_checkpoint, params) {
error!(
?tx,
?signed_checkpoint,
"signature verification failed on checkpoint"
);
return None;
}

let checkpoint: Checkpoint = signed_checkpoint.clone().into();

let blockhash = (*blockdata.block().block_hash().as_byte_array()).into();
let txid = (*tx.compute_txid().as_byte_array()).into();
let wtxid = (*tx.compute_wtxid().as_byte_array()).into();
let block_height = blockdata.block_num();
let commitment_info =
CommitmentInfo::new(blockhash, txid, wtxid, block_height, position);

Some(L1CommittedCheckpoint::new(checkpoint, commitment_info))
})
.collect()
}

/// Given a block, generates a manifest of the parts we care about that we can
/// store in the database.
fn generate_block_manifest(
Expand Down
5 changes: 2 additions & 3 deletions crates/btcio/src/reader/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ mod test {
use strata_rocksdb::{test_utils::get_rocksdb_tmp_instance, L1Db};
use strata_state::{
chain_state::Chainstate,
client_state::{ClientState, L1Checkpoint},
client_state::{CheckpointL1Ref, ClientState, L1Checkpoint},
};
use strata_status::ChainSyncStatusUpdate;
use strata_test_utils::{l2::gen_params, ArbitraryGenerator};
Expand Down Expand Up @@ -603,8 +603,7 @@ mod test {
let mut ckpt: L1Checkpoint = ArbitraryGenerator::new().generate();

let ckpt_height = N_RECENT_BLOCKS as u64 - 5; // within recent blocks range, else panics
ckpt.height = ckpt_height;

ckpt.l1_reference = CheckpointL1Ref::new(ckpt_height, Buf32::zero(), Buf32::zero());
// This is a horrible hack to update the height.
ckpt.batch_info.l1_range.1 =
L1BlockCommitment::new(ckpt_height, *ckpt.batch_info.l1_range.1.blkid());
Expand Down
139 changes: 32 additions & 107 deletions crates/consensus-logic/src/csm/client_transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

use std::cmp::min;

use bitcoin::block::Header;
use bitcoin::{block::Header, Transaction};
use strata_db::traits::{ChainstateDatabase, Database, L1Database, L2BlockDatabase};
use strata_primitives::{
batch::{verify_signed_checkpoint_sig, BatchInfo, Checkpoint, L1CommittedCheckpoint},
batch::{verify_signed_checkpoint_sig, BatchInfo, Checkpoint},
l1::{get_btc_params, HeaderVerificationState, L1BlockCommitment, L1BlockId},
prelude::*,
};
Expand Down Expand Up @@ -142,8 +142,11 @@
.get_internal_state(height - 1)
.expect("clientstate: missing expected block state");

let new_istate = process_l1_block(prev_istate, height, block_mf, params.rollup())?;
let (new_istate, sync_actions) =
process_l1_block(prev_istate, height, block_mf, params.rollup())?;
state.accept_l1_block_state(block, new_istate);
// Push actions from processing l1 block if any
state.push_actions(sync_actions.into_iter());

// TODO make max states configurable
let max_states = 20;
Expand Down Expand Up @@ -195,14 +198,6 @@
state.push_action(SyncAction::FinalizeEpoch(*decl_epoch));
}

// If we have some number of L1 blocks finalized, also emit an `UpdateBuried` write.
let safe_depth = params.rollup().l1_reorg_safe_depth as u64;
let maturable_height = next_exp_height.saturating_sub(safe_depth);

if maturable_height > params.rollup().horizon_l1_height && state.state().is_chain_active() {
handle_mature_l1_height(state, maturable_height, context);
}

Ok(())
}

Expand All @@ -219,14 +214,15 @@
height: u64,
block_mf: &L1BlockManifest,
params: &RollupParams,
) -> Result<InternalState, Error> {
) -> Result<(InternalState, Vec<SyncAction>), Error> {
let blkid = block_mf.blkid();
let mut checkpoint = state.last_checkpoint().cloned();
let mut sync_actions = Vec::new();

// Iterate through all of the protocol operations in all of the txs.
// TODO split out each proto op handling into a separate function
for txs in block_mf.txs() {
for op in txs.protocol_ops() {
for tx in block_mf.txs() {
for op in tx.protocol_ops() {

Check warning on line 225 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L225

Added line #L225 was not covered by tests
match op {
ProtocolOperation::Checkpoint(signed_ckpt) => {
// Before we do anything, check its signature.
Expand All @@ -244,17 +240,24 @@
continue;
}

let ckpt_ref = get_l1_reference(tx, height)?;

Check warning on line 243 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L243

Added line #L243 was not covered by tests

// Construct the state bookkeeping entry for the checkpoint.
let l1ckpt = L1Checkpoint::new(
ckpt.batch_info().clone(),
ckpt.batch_transition().clone(),
ckpt.base_state_commitment().clone(),
!ckpt.proof().is_empty(),
height,
ckpt_ref.clone(),

Check warning on line 250 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L250

Added line #L250 was not covered by tests
);

// If it all looks good then overwrite the saved checkpoint.
checkpoint = Some(l1ckpt);

// Emit a sync action to update checkpoint entry in db
sync_actions.push(SyncAction::UpdateCheckpointInclusion {
epoch: ckpt.batch_info().epoch(),
l1_reference: ckpt_ref,
});

Check warning on line 260 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L255-L260

Added lines #L255 - L260 were not covered by tests
}

// The rest we don't care about here. Maybe we will in the
Expand All @@ -263,102 +266,24 @@
}
}
}
let istate = InternalState::new(*blkid, checkpoint);

Ok(InternalState::new(*blkid, checkpoint))
Ok((istate, sync_actions))
}

/// Handles the maturation of L1 height by finalizing checkpoints and emitting
/// sync actions.
///
/// This function checks if there are any verified checkpoints at or before the
/// given `maturable_height`. If such checkpoints exist, it attempts to
/// finalize them by checking if the corresponding L2 block is available in the
/// L2 database. If the L2 block is found, it marks the checkpoint as finalized
/// and emits a sync action to finalize the L2 block. If the L2 block is not
/// found, it logs a warning and skips the finalization.
///
/// # Arguments
///
/// * `state` - A reference to the current client state.
/// * `maturable_height` - The height at which L1 blocks are considered mature.
/// * `database` - A reference to the database interface.
///
/// # Returns
///
/// A tuple containing:
/// * A vector of [`ClientStateWrite`] representing the state changes to be written.
/// * A vector of [`SyncAction`] representing the actions to be synchronized.
fn handle_mature_l1_height(
state: &mut ClientStateMut,
maturable_height: u64,
context: &impl EventContext,
) -> Result<(), Error> {
// If there are no checkpoints then return early.
if !state
.state()
.has_verified_checkpoint_before(maturable_height)
{
return Ok(());
}

// If there *are* checkpoints at or before the maturable height, mark them
// as finalized
if let Some(checkpt) = state
.state()
.get_last_verified_checkpoint_before(maturable_height)
{
// FinalizeBlock Should only be applied when l2_block is actually
// available in l2_db
// If l2 blocks is not in db then finalization will happen when
// l2Block is fetched from the network and the corresponding
//checkpoint is already finalized.
let epoch = checkpt.batch_info.get_epoch_commitment();
let blkid = *epoch.last_blkid();

match context.get_l2_block_data(&blkid) {
Ok(_) => {
// Emit sync action for finalizing an epoch
trace!(%maturable_height, %blkid, "epoch terminal block found in DB, emitting FinalizedEpoch action");
state.push_action(SyncAction::FinalizeEpoch(epoch));
}

// TODO figure out how to make this not matter
Err(Error::MissingL2Block(_)) => {
warn!(
%maturable_height, ?epoch, "epoch terminal not in DB yet, skipping finalization"
);
}

Err(e) => {
error!(%blkid, err = %e, "error while checking for block present");
return Err(e);
}
}
} else {
warn!(
%maturable_height,
"expected to find blockid corresponding to buried l1 height in confirmed_blocks but could not find"
fn get_l1_reference(tx: &L1Tx, height: u64) -> Result<CheckpointL1Ref, Error> {
let btx: Transaction = tx.tx_data().try_into().map_err(|e| {
warn!(%height, "Invalid bitcoin transaction data in L1Tx");
let msg = format!(
"Invalid bitcoin transaction data in L1Tx at height {}",
height

Check warning on line 279 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L274-L279

Added lines #L274 - L279 were not covered by tests
);
}

Ok(())
}
Error::Other(msg)
})?;

Check warning on line 282 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L281-L282

Added lines #L281 - L282 were not covered by tests

/// Searches for a given [`L2BlockId`] within a slice of [`L1Checkpoint`] structs
/// and returns the height of the corresponding L1 block if found.
fn find_l1_height_for_l2_blockid(
checkpoints: &[L1Checkpoint],
target_l2_blockid: &L2BlockId,
) -> Option<u64> {
checkpoints
.binary_search_by(|checkpoint| {
checkpoint
.batch_info
.final_l2_blockid()
.cmp(target_l2_blockid)
})
.ok()
.map(|index| checkpoints[index].height)
let txid = btx.compute_txid().into();
let wtxid = btx.compute_wtxid().into();
Ok(CheckpointL1Ref::new(height, txid, wtxid))

Check warning on line 286 in crates/consensus-logic/src/csm/client_transition.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/client_transition.rs#L284-L286

Added lines #L284 - L286 were not covered by tests
}

#[cfg(test)]
Expand Down
53 changes: 44 additions & 9 deletions crates/consensus-logic/src/csm/worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,25 +318,60 @@
_status_channel: &StatusChannel,
) -> anyhow::Result<()> {
match action {
SyncAction::FinalizeEpoch(epoch) => {
SyncAction::FinalizeEpoch(epoch_comm) => {

Check warning on line 321 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L321

Added line #L321 was not covered by tests
// For the fork choice manager this gets picked up later. We don't have
// to do anything here *necessarily*.
info!(?epoch, "finalizing epoch");
info!(?epoch_comm, "finalizing epoch");

Check warning on line 324 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L324

Added line #L324 was not covered by tests

strata_common::check_bail_trigger("sync_event_finalize_epoch");

// TODO error checking here
engine.update_finalized_block(*epoch.last_blkid())?;
engine.update_finalized_block(*epoch_comm.last_blkid())?;

Check warning on line 329 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L329

Added line #L329 was not covered by tests

// Write that the checkpoint is finalized.
//
// TODO In the future we should just be able to determine this on the fly.
let ckman = state.storage.checkpoint();
update_checkpoint_status(
epoch.epoch(),
CheckpointConfStatus::Finalized,
ckman.as_ref(),
)?;
let epoch = epoch_comm.epoch();
let Some(mut ckpt_entry) = state.storage.checkpoint().get_checkpoint_blocking(epoch)?

Check warning on line 335 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L334-L335

Added lines #L334 - L335 were not covered by tests
else {
warn!(%epoch, "missing checkpoint we wanted to set the state of, ignoring");
return Ok(());

Check warning on line 338 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L337-L338

Added lines #L337 - L338 were not covered by tests
};

let CheckpointConfStatus::Confirmed(l1ref) = ckpt_entry.confirmation_status else {
warn!(

Check warning on line 342 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L341-L342

Added lines #L341 - L342 were not covered by tests
?epoch_comm,
?ckpt_entry.confirmation_status,
"Expected epoch checkpoint to be confirmed in db, but has different status"

Check warning on line 345 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L345

Added line #L345 was not covered by tests
);
return Ok(());

Check warning on line 347 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L347

Added line #L347 was not covered by tests
};

// Mark it as finalized.
ckpt_entry.confirmation_status = CheckpointConfStatus::Finalized(l1ref);
state
.storage
.checkpoint()
.put_checkpoint_blocking(epoch, ckpt_entry)?;

Check warning on line 355 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L351-L355

Added lines #L351 - L355 were not covered by tests
}

// Update checkpoint entry in database to mark it as included in L1.
SyncAction::UpdateCheckpointInclusion {
epoch,
l1_reference,

Check warning on line 361 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L360-L361

Added lines #L360 - L361 were not covered by tests
} => {
let Some(mut ckpt_entry) = state.storage.checkpoint().get_checkpoint_blocking(epoch)?

Check warning on line 363 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L363

Added line #L363 was not covered by tests
else {
warn!(%epoch, "missing checkpoint we wanted to set the state of, ignoring");
return Ok(());

Check warning on line 366 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L365-L366

Added lines #L365 - L366 were not covered by tests
};

ckpt_entry.confirmation_status = CheckpointConfStatus::Confirmed(l1_reference);

state
.storage
.checkpoint()
.put_checkpoint_blocking(epoch, ckpt_entry)?;

Check warning on line 374 in crates/consensus-logic/src/csm/worker.rs

View check run for this annotation

Codecov / codecov/patch

crates/consensus-logic/src/csm/worker.rs#L369-L374

Added lines #L369 - L374 were not covered by tests
}

SyncAction::L2Genesis(l1blkid) => {
Expand Down
Loading
Loading