-
Notifications
You must be signed in to change notification settings - Fork 890
Rough prototype for architectural changes needed to introduce execution proofs #7755
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
Draft
kevaundray
wants to merge
116
commits into
sigp:unstable
Choose a base branch
from
kevaundray:kw/exec-proofs
base: unstable
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
116 commits
Select commit
Hold shift + click to select a range
3c64dcc
initial stateless-validation flag
kevaundray 1a372d8
add initial execution payload proof store
kevaundray 5e56ab8
add initial code to config subnets (without having subnets)
kevaundray ab60fbf
Add code for proof generation + some refactor
kevaundray 2ec002d
gossip subnet
kevaundray 260b642
proof broadcasting
kevaundray 9f7fca3
remove unneeded methods
kevaundray 4b07bdf
cargo fmt
kevaundray 42c269e
make cli
kevaundray bc9fa8f
add code to re-evaluate blocks when a proof arrives
kevaundray 089cf75
avoid duplicate blocks and cleanup pending blocks after some period
kevaundray b372270
add TODO about orphaned proofs
kevaundray 38cb552
short lived write lock
kevaundray 23008f4
rough architecture doc
kevaundray e3071aa
log lines
kevaundray 98e350c
add proof subnets to whitelist
kevaundray c63f439
All nodes subscribe to all subnets
kevaundray 0364285
fmt
kevaundray 82557b7
add generate-execution-proofs flag
kevaundray 7bea51d
remove `spawn_proof_generation_task`
kevaundray f42ee91
execution payload instead of hash
kevaundray b7c1dfe
typo in logs
kevaundray 1a5be90
add execution state witness and random proof delays
kevaundray b5b922e
allow min_proofs parameter
kevaundray 7100010
add cli arg for stateless-min-proofs
kevaundray bdd7ad2
check node level maximum against protocol level maximum
kevaundray a700e3b
docs
kevaundray 2108ca3
refactor
kevaundray 0cc34e4
make cli
kevaundray 8708639
cargo fmt
kevaundray ee48e62
commit
kevaundray 54f9f32
update logs
kevaundray d2afdec
refactor loggin and add proof delay
kevaundray 5cf853b
refactor
kevaundray 9156429
refactor
kevaundray 855224d
be more concise
kevaundray 5343347
tests
kevaundray c497fca
decrease number of proof generators
kevaundray bc281d3
refactor subnet_id
kevaundray 9fc5733
refactor code
kevaundray 9e70570
remove timestamp
kevaundray 3d51683
add comment on version
kevaundray 98d01df
Make ProofId::new failable
kevaundray 0f9a272
remove return values -- tech debt
kevaundray 1f48d27
api cleanup
kevaundray c3ea66e
add separate storage for broadcast_queue
kevaundray 8950959
refactor
kevaundray 682741a
make state machine clearer
kevaundray e8ae9cb
cleanup when proofs exceed max_attempts
kevaundray 88ce599
guard broadcaster with stateless-validation flag
kevaundray bc72b4f
cargo fmt
kevaundray 3a51953
remove unused parameter
kevaundray 8c39d67
fix
kevaundray c45b7f3
refactor comments
kevaundray 3c8bdcc
refactor
kevaundray b1b5d64
remove outdated comment
kevaundray 0e7bbb1
remove unused methods
kevaundray b649fb2
typo
kevaundray 3fea602
small comment on take_unqueued_proofs
kevaundray bf99fa3
cusot -> new and remove timestamp field
kevaundray bf122f5
remove ExecutionPayloadProof
kevaundray ac6a5bc
fix rename
kevaundray 1d3d492
cleanup
kevaundray 66be583
fix rename to generate_proof
kevaundray 72ee1e2
move proof generation to separate module
kevaundray 189a01c
move proofs to execution_proof_generation
kevaundray cdd79ae
add comments
kevaundray 718122b
fix comments
kevaundray 219edb9
rename to cleanup_pending_blocks
kevaundray 97c4b29
comment
kevaundray 6cdc7a0
small clean up of cleanup_pending_block
kevaundray e496b45
add comment on 1:1 being common
kevaundray 80e0fc6
comments
kevaundray 9ed554c
change visibility of private methods
kevaundray 6d3a621
refactor
kevaundray ca49ad3
fmt
kevaundray 6a44879
refactor tests
kevaundray 82763b5
make ProvenBlockInfo private
kevaundray 4c64988
add an impl block for test methods
kevaundray 791bb96
cargo clippy
kevaundray 29acac5
update doc comment for ExecutionPayloadProofStore
kevaundray 4bd7692
decrease public API and temporarily use comments to organize code
kevaundray 2ad9161
refactor more of the logging
kevaundray 9e7a312
pull out execution proof changes from beacon_chain.rs
kevaundray 34bfeb3
remove `cleanup_pending_blocks_older_than` method
kevaundray 5440f97
remove unneeded methods
kevaundray 9551136
refactor comments
kevaundray 611715c
remove comment
kevaundray 40de678
comments
kevaundray 2346b5f
fix cli flag
kevaundray 7005acb
make proof generation async
kevaundray 0072d08
remove unused methods
kevaundray 2b5b870
remove debug log
kevaundray 1b6fde7
fix test
kevaundray 843803e
clean up logging
kevaundray 392269a
make cli
kevaundray 7f1eb7f
less indenting
kevaundray a131e06
refactor logs
kevaundray 88c48a2
add comment on code expectation
kevaundray 75922d7
add comment
kevaundray c41a475
revert
kevaundray 7de9914
use direct method to get execution payload
kevaundray 59df8ff
cache proof generation
kevaundray dfad96b
cargo fmt
kevaundray 3f0c9de
add comment
kevaundray 94190c6
fix
kevaundray ff93ee1
more notes
kevaundray 4e99f8d
top level import
kevaundray 4de9df0
remove debug test
kevaundray ab30139
rename execution_payload_proof -> execution_proof_store
kevaundray 19b6960
refactor rest of code
kevaundray 588ba6a
fix tests
kevaundray c13724a
Merge branch 'unstable' into kw/exec-proofs
kevaundray b187aa9
remove out of place tests
kevaundray f176dde
Update beacon_node/beacon_chain/src/execution_payload.rs
kevaundray 8fbe3f5
Update beacon_node/network/src/network_beacon_processor/gossip_method…
kevaundray File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
229 changes: 229 additions & 0 deletions
229
beacon_node/beacon_chain/src/beacon_chain_execution_proof.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
use crate::errors::BeaconChainError as Error; | ||
use crate::{BeaconChain, BeaconChainTypes}; | ||
use tracing::{debug, info}; | ||
use types::{EthSpec, ExecPayload, ExecutionBlockHash, Hash256, Slot}; | ||
|
||
// Execution Proof Management for BeaconChain | ||
// | ||
// This module contains all execution proof-related functionality for the | ||
// BeaconChain, if we follow the current code structure, this would belong in | ||
// beacon_chain.rs. It has been pulled into this separate file to make the diff | ||
// easier to manage. | ||
// | ||
impl<T: BeaconChainTypes> BeaconChain<T> { | ||
// ======================================================================== | ||
// Subnet Management | ||
// ======================================================================== | ||
|
||
/// Determine which execution proof subnets this node should subscribe to. | ||
/// | ||
/// Currently uses a simple sequential allocation: if max_execution_proof_subnets is N, | ||
/// this node will subscribe to subnets [0, 1, 2, ..., N-1]. | ||
/// | ||
/// Examples: | ||
/// - max_execution_proof_subnets = 8: subscribes to subnets [0, 1, 2, 3, 4, 5, 6, 7] | ||
/// - max_execution_proof_subnets = 4: subscribes to subnets [0, 1, 2, 3] | ||
/// - max_execution_proof_subnets = 1: subscribes to subnet [0] only | ||
/// | ||
/// In the future, this could be made more sophisticated to support: | ||
/// - Random assignment for better distribution | ||
pub fn execution_proof_subnets(&self) -> Vec<u64> { | ||
(0..self.config.max_execution_proof_subnets).collect() | ||
} | ||
|
||
/// Get the maximum number of execution proof subnets for this configuration | ||
pub fn max_execution_proof_subnets(&self) -> u64 { | ||
self.config.max_execution_proof_subnets | ||
} | ||
|
||
/// Check if this node should generate execution proofs for the given subnet | ||
/// | ||
/// Returns true if the subnet is within our configured range | ||
pub fn should_generate_execution_proof_for_subnet(&self, subnet_id: u64) -> bool { | ||
// We generate proofs for all subnets we're subscribed to | ||
subnet_id < self.max_execution_proof_subnets() && self.config.generate_execution_proofs | ||
} | ||
|
||
// ======================================================================== | ||
// Proof Validation and Chain Updates | ||
// ======================================================================== | ||
|
||
/// Re-evaluate optimistic blocks that can now be validated with received proofs | ||
/// This method is called when new execution proofs arrive via gossip | ||
/// In the dual-view architecture, this updates the proven chain but does NOT | ||
/// modify fork choice weights | ||
pub fn re_evaluate_optimistic_blocks_with_proofs( | ||
&self, | ||
execution_block_hash: ExecutionBlockHash, | ||
) -> Result<bool, Error> { | ||
// Only perform re-evaluation if stateless validation is enabled | ||
if !self.config.stateless_validation { | ||
return Ok(false); | ||
} | ||
|
||
// Get the proofs we have for this execution block hash | ||
let available_proofs = self | ||
.execution_payload_proof_store | ||
.get_proofs(&execution_block_hash); | ||
let proof_count = available_proofs.len(); | ||
|
||
// Check if we have enough valid proofs | ||
if proof_count < self.config.stateless_min_proofs_required { | ||
// Only log if we're close to having enough proofs | ||
if proof_count > 0 { | ||
debug!( | ||
execution_block_hash = %execution_block_hash, | ||
proof_count, | ||
required_proofs = self.config.stateless_min_proofs_required, | ||
"Insufficient proofs for execution block" | ||
); | ||
} | ||
return Ok(false); | ||
} | ||
|
||
debug!( | ||
execution_block_hash = %execution_block_hash, | ||
proof_count, | ||
required_proofs = self.config.stateless_min_proofs_required, | ||
"Minimum proofs reached, updating proven chain" | ||
); | ||
|
||
// Get current chain state | ||
let head = self.canonical_head.cached_head(); | ||
let head_block_root = head.head_block_root(); | ||
let head_slot = head.head_slot(); | ||
let current_slot = self.slot().unwrap_or(Slot::new(0)); | ||
let slots_per_epoch = T::EthSpec::slots_per_epoch(); | ||
|
||
// Update the proven canonical chain based on available proofs | ||
// This does NOT modify fork choice - validators continue with optimistic view | ||
let proven_status = self | ||
.execution_payload_proof_store | ||
.update_proven_chain( | ||
|block_root| { | ||
self.get_blinded_block(block_root).map(|result| { | ||
result.map(|block| { | ||
let slot = block.slot(); | ||
let parent_root = block.parent_root(); | ||
let exec_hash_opt = block | ||
.message() | ||
.execution_payload() | ||
.ok() | ||
.map(|payload| payload.block_hash()); | ||
(slot, parent_root, exec_hash_opt) | ||
}) | ||
}) | ||
}, | ||
head_block_root, | ||
current_slot, | ||
slots_per_epoch, | ||
self.config.stateless_min_proofs_required, | ||
) | ||
.map_err(Error::ExecutionProofError)?; | ||
|
||
// Log proven chain status if it changed | ||
if let Some((_proven_root, proven_slot)) = proven_status.proven_head { | ||
if proven_status.head_changed { | ||
let lag_slots = head_slot.saturating_sub(proven_slot); | ||
info!( | ||
proven_slot = %proven_slot, | ||
head_slot = %head_slot, | ||
lag_slots = %lag_slots, | ||
"Proven chain updated" | ||
); | ||
} | ||
} | ||
|
||
// Remove pending blocks that now have sufficient proofs | ||
let proven_blocks = self | ||
.execution_payload_proof_store | ||
.take_pending_blocks(&execution_block_hash); | ||
// Note: That if we were to modify fork choice, it would likely be here, where we know what set of | ||
// beacon blocks have valid execution payloads. | ||
Comment on lines
+141
to
+142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As the comment mentions, one could trigger head recomputation here based on the proven blocks |
||
|
||
if !proven_blocks.is_empty() { | ||
debug!( | ||
%execution_block_hash, | ||
proven_count = proven_blocks.len(), | ||
"Removed pending blocks that now have sufficient proofs" | ||
); | ||
} | ||
|
||
// Perform periodic cleanup of finalized pending blocks | ||
if proven_status.head_changed { | ||
// TODO: Revisit, if this is still needed | ||
let _cleaned_count = self.cleanup_finalized_pending_blocks(); | ||
} | ||
|
||
// Return false - we never trigger head recomputation in dual-view mode | ||
// Fork choice remains permanently optimistic | ||
Ok(false) | ||
} | ||
|
||
/// Register a beacon block as pending execution proof validation | ||
/// This is called when a block is imported optimistically in stateless validation mode | ||
pub fn register_optimistic_block_for_proof( | ||
&self, | ||
beacon_block_root: Hash256, | ||
execution_block_hash: ExecutionBlockHash, | ||
) { | ||
if self.config.stateless_validation { | ||
self.execution_payload_proof_store | ||
.register_pending_block(execution_block_hash, beacon_block_root); | ||
|
||
debug!( | ||
beacon_block_root = %beacon_block_root, | ||
execution_block_hash = %execution_block_hash, | ||
"Registered optimistic block awaiting proofs" | ||
); | ||
} | ||
} | ||
|
||
// ======================================================================== | ||
// Cleanup Operations | ||
// ======================================================================== | ||
|
||
/// Clean up pending blocks that have been finalized or are too old | ||
/// This should be called periodically to prevent memory leaks in the proof store | ||
/// | ||
/// This method in mainly here for the case that a block has been finalized | ||
/// that did not have sufficient amount of proofs. This can happen while | ||
/// proofs are not on the critical path and for reasons (prover killers), | ||
/// take more than 2 epochs to generate. | ||
pub fn cleanup_finalized_pending_blocks(&self) -> usize { | ||
if !self.config.stateless_validation { | ||
return 0; | ||
} | ||
|
||
let finalized_slot = self | ||
.canonical_head | ||
.cached_head() | ||
.finalized_checkpoint() | ||
.epoch | ||
.start_slot(T::EthSpec::slots_per_epoch()); | ||
|
||
// Remove pending blocks that are older than finalized slot | ||
let removed_count = | ||
self.execution_payload_proof_store | ||
.cleanup_pending_blocks(|block_root| { | ||
// Check if this block is older than finalized slot | ||
// We need to look up the block to get its slot | ||
if let Ok(Some(block)) = self.get_blinded_block(&block_root) { | ||
block.slot() <= finalized_slot | ||
} else { | ||
// If we can't find the block, it's likely been pruned, so remove it | ||
true | ||
} | ||
}); | ||
|
||
if removed_count > 0 { | ||
debug!( | ||
finalized_slot = %finalized_slot, | ||
removed_count, | ||
"Cleaned up finalized pending blocks from proof store" | ||
); | ||
} | ||
|
||
removed_count | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is where the beacon block is registered in the proof store and we start listening for proofs of the particular execution_block_hash