Skip to content

Commit

Permalink
fix(block_production): dynamic block closing (#456)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohiiit authored Jan 8, 2025
1 parent c03c8b1 commit 448bef5
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Next release

- fix(block_production): dynamic block closing now adds special address with prev block hash
- fix(rpc): call, simulate, estimate rpcs executed on top of the block, not at the start of it
- fix(compilation): crate-level compilation
- chore: Move crates under a madara subdir
Expand Down
85 changes: 57 additions & 28 deletions crates/madara/client/block_production/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,20 +359,66 @@ impl<Mempool: MempoolProvider> BlockProductionTask<Mempool> {
Ok(())
}

/// Updates the state diff to store a block hash at the special address 0x1, which serves as
/// Starknet's block hash registry.
///
/// # Purpose
/// Address 0x1 in Starknet is a special contract address that maintains a mapping of block numbers
/// to their corresponding block hashes. This storage is used by the `get_block_hash` system call
/// and is essential for block hash verification within the Starknet protocol.
///
/// # Storage Structure at Address 0x1
/// - Keys: Block numbers
/// - Values: Corresponding block hashes
/// - Default: 0 for all other block numbers
///
/// # Implementation Details
/// For each block N ≥ 10, this function stores the hash of block (N-10) at address 0x1
/// with the block number as the key.
///
/// For more details, see the [official Starknet documentation on special addresses]
/// (https://docs.starknet.io/architecture-and-concepts/network-architecture/starknet-state/#address_0x1)
///
/// It is also required by SNOS for PIEs creation of the block.
fn update_block_hash_registry(&self, state_diff: &mut StateDiff, block_n: u64) -> Result<(), Error> {
if block_n >= 10 {
let prev_block_number = block_n - 10;
let prev_block_hash = self
.backend
.get_block_hash(&BlockId::Number(prev_block_number))
.map_err(|err| {
Error::Unexpected(
format!("Error fetching block hash for block {prev_block_number}: {err:#}").into(),
)
})?
.ok_or_else(|| {
Error::Unexpected(format!("No block hash found for block number {prev_block_number}").into())
})?;

state_diff.storage_diffs.push(ContractStorageDiffItem {
address: Felt::ONE, // Address 0x1
storage_entries: vec![StorageEntry { key: Felt::from(prev_block_number), value: prev_block_hash }],
});
}
Ok(())
}

#[tracing::instrument(skip(self), fields(module = "BlockProductionTask"))]
pub async fn on_pending_time_tick(&mut self) -> Result<bool, Error> {
let current_pending_tick = self.current_pending_tick;
if current_pending_tick == 0 {
return Ok(false);
}

// Use full bouncer capacity
let bouncer_cap = self.backend.chain_config().bouncer_config.block_max_capacity;

let start_time = Instant::now();

let ContinueBlockResult { state_diff, visited_segments, bouncer_weights, stats, block_now_full } =
self.continue_block(bouncer_cap)?;
let ContinueBlockResult {
state_diff: mut new_state_diff,
visited_segments,
bouncer_weights,
stats,
block_now_full,
} = self.continue_block(self.backend.chain_config().bouncer_config.block_max_capacity)?;

if stats.n_added_to_block > 0 {
tracing::info!(
Expand All @@ -385,16 +431,19 @@ impl<Mempool: MempoolProvider> BlockProductionTask<Mempool> {

// Check if block is full
if block_now_full {
let block_n = self.block_n();
self.update_block_hash_registry(&mut new_state_diff, block_n)?;

tracing::info!("Resource limits reached, closing block early");
self.close_and_prepare_next_block(state_diff, visited_segments, start_time).await?;
self.close_and_prepare_next_block(new_state_diff, visited_segments, start_time).await?;
return Ok(true);
}

// Store pending block
// todo, prefer using the block import pipeline?
self.backend.store_block(
self.block.clone().into(),
state_diff,
new_state_diff,
self.declared_classes.clone(),
Some(visited_segments),
Some(bouncer_weights),
Expand All @@ -421,27 +470,7 @@ impl<Mempool: MempoolProvider> BlockProductionTask<Mempool> {
block_now_full: _block_now_full,
} = self.continue_block(self.backend.chain_config().bouncer_config.block_max_capacity)?;

// SNOS requirement: For blocks >= 10, the hash of the block 10 blocks prior
// at address 0x1 with the block number as the key
if block_n >= 10 {
let prev_block_number = block_n - 10;
let prev_block_hash = self
.backend
.get_block_hash(&BlockId::Number(prev_block_number))
.map_err(|err| {
Error::Unexpected(
format!("Error fetching block hash for block {prev_block_number}: {err:#}").into(),
)
})?
.ok_or_else(|| {
Error::Unexpected(format!("No block hash found for block number {prev_block_number}").into())
})?;

new_state_diff.storage_diffs.push(ContractStorageDiffItem {
address: Felt::ONE,
storage_entries: vec![StorageEntry { key: Felt::from(prev_block_number), value: prev_block_hash }],
});
}
self.update_block_hash_registry(&mut new_state_diff, block_n)?;

self.close_and_prepare_next_block(new_state_diff, visited_segments, start_time).await
}
Expand Down

0 comments on commit 448bef5

Please sign in to comment.