Conversation
|
Commit: 6216848 SP1 Execution Results
|
66d124e to
3125c4c
Compare
| accumulated_da: AccumulatedDaData, | ||
| } | ||
|
|
||
| impl BlockTemplateResult { | ||
| /// Create a new block template result. | ||
| pub(crate) fn new(template: FullBlockTemplate, failed_txs: Vec<FailedMempoolTx>) -> Self { | ||
| pub(crate) fn new( | ||
| template: FullBlockTemplate, | ||
| failed_txs: Vec<FailedMempoolTx>, | ||
| accumulated_da: AccumulatedDaData, |
There was a problem hiding this comment.
This doesn't make sense to include here, it's like it's threading it through block assembly the whole way.
There was a problem hiding this comment.
I think it does, but I will still look more into it.
| #[derive(Clone, Debug)] | ||
| pub(crate) struct AccumulatedDaData { | ||
| epoch: Epoch, | ||
| accumulator: EpochDaAccumulator, | ||
| logs: Vec<OLLog>, | ||
| } |
There was a problem hiding this comment.
This type hierarchy isn't very well thought-out. The AccumulatedDaData vs EpochDaAccumulator, it's not clear what's being represented here.
There was a problem hiding this comment.
I can see how the name can be confusing. I think it's because of what "da" represents. In EpochDaAccumulator it is about the state diff while in AccumulatedDaData it is about both state diff and logs.
I am interested to hear what exactly about the hierarchy can be made better.
| /// Inserts the entry for given block id and also removes the entry for parent if exists. This | ||
| /// method is used to optimize memory usage because in the next assembly we would require | ||
| /// accumulation upto the current block and not the parent block. | ||
| pub(crate) fn set_accumulated_da_and_remove_parent_entry( | ||
| &mut self, | ||
| blkid: OLBlockId, | ||
| parent: OLBlockId, | ||
| da: AccumulatedDaData, | ||
| ) { | ||
| self.set_accumulated_da(blkid, da); | ||
| self.block_da_map.remove(&parent); | ||
| } |
There was a problem hiding this comment.
What's the point of having the map then? If the intention is that in normal circumstances we should always be consuming the old state and creating a new state, then we should explicitly track "the old state" and directly replace it with the new state. The way this is written suggests that it could get stuck in a dirty intermediate state with dangling refs that only get cleaned up when the epoch finishes.
There was a problem hiding this comment.
The point of having a map is that we can't expect block assembly to request template generation requests from a single canonical chain. It might get requests to build block on other forks of the chain as well. What this does is keeps track of the accumulated DA of different tips. When a block is made on top of a tip, it removes that tip and replaces it with the new proposed tip.
There was a problem hiding this comment.
The way this is written suggests that it could get stuck in a dirty intermediate state with dangling refs that only get cleaned up when the epoch finishes.
We can just expose this particular method and not the other one which doesn't remove the parent.
| /// Takes the epoch accumulator, leaving a default in its place. | ||
| pub fn take_accumulator(&mut self) -> EpochDaAccumulator { | ||
| take(&mut self.epoch_acc) | ||
| } |
There was a problem hiding this comment.
This is a C++-ism. If the point of the type is to be about epoch DA, then it should be consumed at the end of the epoch and a new one created at the beginning of the next one.
| /// Executes a batch of blocks sequentially, verifying each produced header | ||
| /// matches the input block's header. | ||
| /// | ||
| /// Generic over `S: IStateAccessor` so callers can pass `OLState` directly | ||
| /// or wrap it (e.g. `DaAccumulatingState<OLState>`) to intercept mutations. | ||
| pub fn execute_block_batch<S: IStateAccessor>( |
There was a problem hiding this comment.
What is the purpose of this function?
This function looks like it's verifying that the blocks were constructed correctly by executing them as if we were constructing them the first time, re-constructing them, and then verifying the re-constructed header matches the header we were given. There is a verify_block function that does block verification the expected way already.
storopoli
left a comment
There was a problem hiding this comment.
The design this PR is using is combination 4 from the Notion page right? accumulate in block assembly, persist by block, and enforce DA limits.
| failed_txs.push((txid, stf_exec_error_to_mempool_reason(&e))); | ||
| } | ||
| } | ||
| // TODO: Check for DA limits here and somehow signal caller to seal epoch. |
There was a problem hiding this comment.
This is still the core missing behavior for design combination 4. We accumulate DA here, but block sealing is still driven only by should_seal_epoch(block_slot) and this function never returns a signal when the accumulated blob crosses the DA budget. As written, block assembly can still assemble an epoch whose checkpoint sidecar is too large, so the PR does not yet enforce the DA cap that STR-1877 and the design doc require.
| Some(da) => da.clone(), | ||
| None => { | ||
| // TODO: rebuild by re-executing epoch blocks | ||
| AccumulatedDaData::new_empty(0) |
There was a problem hiding this comment.
This fallback makes the persisted-accumulation story incorrect. EpochDaTracker is only an in-memory map and is initialized empty on service startup
alpen/crates/ol/block-assembly/src/state.rs
Lines 191 to 209 in 3125c4c
on a tracker miss we silently restart from AccumulatedDaData::new_empty(0), and the parent entry is removed as soon as a template is generated
alpen/crates/ol/block-assembly/src/service.rs
Lines 158 to 163 in 3125c4c
After a restart, or after regenerating from the same parent, DA accumulation can resume from an empty state instead of the actual parent epoch state.
| // We work directly on this state and only clone for backup before each tx. | ||
| // On success: backup is discarded. On failure: restore from backup. | ||
| let mut staging_state = WriteTrackingState::new(parent_state, accumulated_batch); | ||
| let mut staging_state = DaAccumulatingState::new_with_accumulator( |
There was a problem hiding this comment.
Because execute_block_initialization() above still runs under WriteTrackingState, block assembly and checkpoint replay are accumulating different DA surfaces. Checkpoint generation replays the whole epoch under DaAccumulatingState
alpen/crates/ol/checkpoint/src/state.rs
Lines 267 to 270 in 3125c4c
and EpochDaAccumulator explicitly records slot changes as DA-relevant
alpen/crates/ol/state-support-types/src/da_accumulating_layer.rs
Lines 227 to 234 in 3125c4c
So any DA-size decision made from the block-assembly accumulator will undercount relative to the checkpoint blob, because it misses at least block-start and epoch-start writes.
Description
Type of Change
Notes to Reviewers
Is this PR addressing any specification, design doc or external reference document?
If yes, please add relevant links:
Checklist
Related Issues