From d75d340f2769f647fc6e5083a02887175c110c4e Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Tue, 3 Dec 2024 17:58:51 -0700 Subject: [PATCH 01/12] Update builder api for electra --- beacon_node/execution_layer/src/lib.rs | 3 +-- .../src/test_utils/mock_builder.rs | 24 ++++++++++--------- .../execution_layer/src/test_utils/mod.rs | 4 +++- consensus/types/src/builder_bid.rs | 5 +++- 4 files changed, 21 insertions(+), 15 deletions(-) diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index ae0dca9833f..7976a13e5c5 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -121,8 +121,7 @@ impl TryFrom> for ProvenancedPayload( let mut message = match payload_response_type { crate::GetPayloadResponseType::Full(payload_response) => { #[allow(clippy::type_complexity)] - let (payload, _block_value, maybe_blobs_bundle, _maybe_requests): ( + let (payload, value, maybe_blobs_bundle, maybe_requests): ( ExecutionPayload, Uint256, Option>, @@ -600,8 +600,9 @@ pub fn serve( blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), + execution_requests: maybe_requests.unwrap_or_default(), }), ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { header: payload @@ -611,7 +612,7 @@ pub fn serve( blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), }), ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { @@ -619,7 +620,7 @@ pub fn serve( .as_capella() .map_err(|_| reject("incorrect payload variant"))? .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), }), ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { @@ -627,7 +628,7 @@ pub fn serve( .as_bellatrix() .map_err(|_| reject("incorrect payload variant"))? .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), }), ForkName::Base | ForkName::Altair => { @@ -637,7 +638,7 @@ pub fn serve( } crate::GetPayloadResponseType::Blinded(payload_response) => { #[allow(clippy::type_complexity)] - let (payload, _block_value, maybe_blobs_bundle, _maybe_requests): ( + let (payload, value, maybe_blobs_bundle, maybe_requests): ( ExecutionPayload, Uint256, Option>, @@ -652,8 +653,9 @@ pub fn serve( blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), + execution_requests: maybe_requests.unwrap_or_default(), }), ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { header: payload @@ -663,7 +665,7 @@ pub fn serve( blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), }), ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { @@ -671,7 +673,7 @@ pub fn serve( .as_capella() .map_err(|_| reject("incorrect payload variant"))? .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), }), ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { @@ -679,7 +681,7 @@ pub fn serve( .as_bellatrix() .map_err(|_| reject("incorrect payload variant"))? .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + value, pubkey: builder.builder_sk.public_key().compress(), }), ForkName::Base | ForkName::Altair => { diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index faf6d4ef0b6..ba66a743da4 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -31,7 +31,9 @@ pub use execution_block_generator::{ mock_el_extra_data, static_valid_tx, Block, ExecutionBlockGenerator, }; pub use hook::Hook; -pub use mock_builder::{mock_builder_extra_data, MockBuilder, Operation}; +pub use mock_builder::{ + mock_builder_extra_data, serve as serve_mock_builder, MockBuilder, Operation, +}; pub use mock_execution_layer::MockExecutionLayer; pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400; diff --git a/consensus/types/src/builder_bid.rs b/consensus/types/src/builder_bid.rs index 9885f78474f..5d405d25401 100644 --- a/consensus/types/src/builder_bid.rs +++ b/consensus/types/src/builder_bid.rs @@ -2,7 +2,8 @@ use crate::beacon_block_body::KzgCommitments; use crate::{ ChainSpec, EthSpec, ExecutionPayloadHeaderBellatrix, ExecutionPayloadHeaderCapella, ExecutionPayloadHeaderDeneb, ExecutionPayloadHeaderElectra, ExecutionPayloadHeaderRef, - ExecutionPayloadHeaderRefMut, ForkName, ForkVersionDeserialize, SignedRoot, Uint256, + ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionDeserialize, SignedRoot, + Uint256, }; use bls::PublicKeyBytes; use bls::Signature; @@ -33,6 +34,8 @@ pub struct BuilderBid { pub header: ExecutionPayloadHeaderElectra, #[superstruct(only(Deneb, Electra))] pub blob_kzg_commitments: KzgCommitments, + #[superstruct(only(Electra))] + pub execution_requests: ExecutionRequests, #[serde(with = "serde_utils::quoted_u256")] pub value: Uint256, pub pubkey: PublicKeyBytes, From 9bc4c3ccca5886b337f01ad2c97d54cf251f9db8 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Wed, 4 Dec 2024 17:28:00 -0800 Subject: [PATCH 02/12] Refactor mock builder to separate functionality --- .../src/test_utils/mock_builder.rs | 658 ++++++++++-------- .../execution_layer/src/test_utils/mod.rs | 4 +- 2 files changed, 351 insertions(+), 311 deletions(-) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index b2cad18899b..56ab404d4b9 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -317,8 +317,348 @@ impl MockBuilder { } bid.stamp_payload(); } + + /// Return the public key of the builder + pub fn public_key(&self) -> PublicKeyBytes { + self.builder_sk.public_key().compress() + } + + pub async fn register_validators( + &self, + registrations: Vec, + ) -> Result<(), String> { + for registration in registrations { + if !registration.verify_signature(&self.spec) { + return Err("invalid signature".to_string()); + } + self.val_registration_cache + .write() + .insert(registration.message.pubkey, registration); + } + Ok(()) + } + + pub async fn submit_blinded_block( + &self, + block: SignedBlindedBeaconBlock, + ) -> Result, String> { + let root = match &block { + SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => { + return Err("invalid fork".to_string()); + } + SignedBlindedBeaconBlock::Bellatrix(block) => { + block.message.body.execution_payload.tree_hash_root() + } + SignedBlindedBeaconBlock::Capella(block) => { + block.message.body.execution_payload.tree_hash_root() + } + SignedBlindedBeaconBlock::Deneb(block) => { + block.message.body.execution_payload.tree_hash_root() + } + SignedBlindedBeaconBlock::Electra(block) => { + block.message.body.execution_payload.tree_hash_root() + } + }; + let payload = self + .el + .get_payload_by_root(&root) + .ok_or_else(|| "missing payload for tx root".to_string())?; + let (payload, _) = payload.deconstruct(); + // TODO(pawan): the reconstruction in the beacon client should propagate the + // blobs as well. If that doesn't happen then its probably a bug here + self.beacon_client + .post_beacon_blinded_blocks(&block) + .await + .map_err(|e| format!("Failed to post blinded block {:?}", e))?; + Ok(payload) + } + + pub async fn get_header( + &self, + slot: Slot, + parent_hash: ExecutionBlockHash, + pubkey: PublicKeyBytes, + ) -> Result, String> { + let fork = self.fork_name_at_slot(slot); + let signed_cached_data = self + .val_registration_cache + .read() + .get(&pubkey) + .ok_or_else(|| "missing registration".to_string())? + .clone(); + let cached_data = signed_cached_data.message; + + let head = self + .beacon_client + .get_beacon_blocks::(BlockId::Head) + .await + .map_err(|_| "couldn't get head".to_string())? + .ok_or_else(|| "missing head block".to_string())?; + + let block = head.data.message(); + let head_block_root = block.tree_hash_root(); + let head_execution_payload = block + .body() + .execution_payload() + .map_err(|_| "pre-merge block".to_string())?; + let head_execution_hash = head_execution_payload.block_hash(); + let head_gas_limit = head_execution_payload.gas_limit(); + if head_execution_hash != parent_hash { + return Err("head mismatch".to_string()); + } + + let finalized_execution_hash = self + .beacon_client + .get_beacon_blocks::(BlockId::Finalized) + .await + .map_err(|_| "couldn't get finalized block".to_string())? + .ok_or_else(|| "missing finalized block".to_string())? + .data + .message() + .body() + .execution_payload() + .map_err(|_| "pre-merge block".to_string())? + .block_hash(); + + let justified_execution_hash = self + .beacon_client + .get_beacon_blocks::(BlockId::Justified) + .await + .map_err(|_| "couldn't get justified block".to_string())? + .ok_or_else(|| "missing justified block".to_string())? + .data + .message() + .body() + .execution_payload() + .map_err(|_| "pre-merge block".to_string())? + .block_hash(); + + let val_index = self + .beacon_client + .get_beacon_states_validator_id(StateId::Head, &ValidatorId::PublicKey(pubkey)) + .await + .map_err(|_| "couldn't get validator".to_string())? + .ok_or_else(|| "missing validator".to_string())? + .data + .index; + let fee_recipient = cached_data.fee_recipient; + let slots_since_genesis = slot.as_u64() - self.spec.genesis_slot.as_u64(); + + // TODO(pawan): cache this call + let genesis_data = self + .beacon_client + .get_beacon_genesis() + .await + .map_err(|_| "couldn't get beacon genesis".to_string())? + .data; + let genesis_time = genesis_data.genesis_time; + let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time; + + let head_state: BeaconState = self + .beacon_client + .get_debug_beacon_states(StateId::Head) + .await + .map_err(|_| "couldn't get state".to_string())? + .ok_or_else(|| "missing state".to_string())? + .data; + let prev_randao = head_state + .get_randao_mix(head_state.current_epoch()) + .map_err(|_| "couldn't get prev randao".to_string())?; + + let expected_withdrawals = if fork.capella_enabled() { + Some( + self.beacon_client + .get_expected_withdrawals(&StateId::Head) + .await + .unwrap() + .data, + ) + } else { + None + }; + + let payload_attributes = match fork { + // the withdrawals root is filled in by operations, but we supply the valid withdrawals + // first to avoid polluting the execution block generator with invalid payload attributes + // NOTE: this was part of an effort to add payload attribute uniqueness checks, + // which was abandoned because it broke too many tests in subtle ways. + ForkName::Bellatrix | ForkName::Capella => PayloadAttributes::new( + timestamp, + *prev_randao, + fee_recipient, + expected_withdrawals, + None, + ), + ForkName::Deneb | ForkName::Electra => PayloadAttributes::new( + timestamp, + *prev_randao, + fee_recipient, + expected_withdrawals, + Some(head_block_root), + ), + ForkName::Base | ForkName::Altair => { + return Err("invalid fork".to_string()); + } + }; + + self.el + .insert_proposer(slot, head_block_root, val_index, payload_attributes.clone()) + .await; + + let forkchoice_update_params = ForkchoiceUpdateParameters { + head_root: Hash256::zero(), + head_hash: None, + justified_hash: Some(justified_execution_hash), + finalized_hash: Some(finalized_execution_hash), + }; + + let proposer_gas_limit = self + .val_registration_cache + .read() + .get(&pubkey) + .map(|v| v.message.gas_limit); + + let payload_parameters = PayloadParameters { + parent_hash: head_execution_hash, + parent_gas_limit: head_gas_limit, + proposer_gas_limit, + payload_attributes: &payload_attributes, + forkchoice_update_params: &forkchoice_update_params, + current_fork: fork, + }; + + let payload_response_type = self + .el + .get_full_payload_caching(payload_parameters) + .await + .map_err(|_| "couldn't get payload".to_string())?; + + let mut message = match payload_response_type { + crate::GetPayloadResponseType::Full(payload_response) => { + #[allow(clippy::type_complexity)] + let (payload, value, maybe_blobs_bundle, maybe_requests): ( + ExecutionPayload, + Uint256, + Option>, + Option>, + ) = payload_response.into(); + + match fork { + ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { + header: payload + .as_electra() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments) + .unwrap_or_default(), + value, + pubkey: self.builder_sk.public_key().compress(), + execution_requests: maybe_requests.unwrap_or_default(), + }), + ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { + header: payload + .as_deneb() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments) + .unwrap_or_default(), + value, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { + header: payload + .as_capella() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + value, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { + header: payload + .as_bellatrix() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + value, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), + } + } + crate::GetPayloadResponseType::Blinded(payload_response) => { + #[allow(clippy::type_complexity)] + let (payload, value, maybe_blobs_bundle, maybe_requests): ( + ExecutionPayload, + Uint256, + Option>, + Option>, + ) = payload_response.into(); + match fork { + ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { + header: payload + .as_electra() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments) + .unwrap_or_default(), + value, + pubkey: self.builder_sk.public_key().compress(), + execution_requests: maybe_requests.unwrap_or_default(), + }), + ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { + header: payload + .as_deneb() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments) + .unwrap_or_default(), + value, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { + header: payload + .as_capella() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + value, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { + header: payload + .as_bellatrix() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + value, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), + } + } + }; + + self.apply_operations(&mut message); + + let mut signature = message.sign_builder_message(&self.builder_sk, &self.spec); + + if *self.invalidate_signatures.read() { + signature = Signature::empty(); + }; + let signed_bid = SignedBuilderBid { message, signature }; + Ok(signed_bid) + } + + fn fork_name_at_slot(&self, slot: Slot) -> ForkName { + self.spec.fork_name_at_slot::(slot) + } } +/// Serve the builder api using warp. Uses the functions defined in `MockBuilder` to serve +/// the requests. +/// +/// We should eventually move this to axum when we move everything else. pub fn serve( listen_addr: Ipv4Addr, listen_port: u16, @@ -338,18 +678,10 @@ pub fn serve( .and(ctx_filter.clone()) .and_then( |registrations: Vec, builder: MockBuilder| async move { - for registration in registrations { - if !registration.verify_signature(&builder.spec) { - return Err(reject("invalid signature")); - } - builder - .val_registration_cache - .write() - .insert(registration.message.pubkey, registration); - } - Ok(warp::reply()) + builder.register_validators(registrations).await.map_err(|e|warp::reject::custom(Custom(e)))?; + Ok::<_, Rejection>(warp::reply()) }, - ); + ).boxed(); let blinded_block = prefix @@ -362,27 +694,10 @@ pub fn serve( |block: SignedBlindedBeaconBlock, fork_name: ForkName, builder: MockBuilder| async move { - let root = match block { - SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => { - return Err(reject("invalid fork")); - } - SignedBlindedBeaconBlock::Bellatrix(block) => { - block.message.body.execution_payload.tree_hash_root() - } - SignedBlindedBeaconBlock::Capella(block) => { - block.message.body.execution_payload.tree_hash_root() - } - SignedBlindedBeaconBlock::Deneb(block) => { - block.message.body.execution_payload.tree_hash_root() - } - SignedBlindedBeaconBlock::Electra(block) => { - block.message.body.execution_payload.tree_hash_root() - } - }; let payload = builder - .el - .get_payload_by_root(&root) - .ok_or_else(|| reject("missing payload for tx root"))?; + .submit_blinded_block(block) + .await + .map_err(|e| warp::reject::custom(Custom(e)))?; let resp: ForkVersionedResponse<_> = ForkVersionedResponse { version: Some(fork_name), metadata: Default::default(), @@ -425,285 +740,12 @@ pub fn serve( parent_hash: ExecutionBlockHash, pubkey: PublicKeyBytes, builder: MockBuilder| async move { - let fork = builder.spec.fork_name_at_slot::(slot); - let signed_cached_data = builder - .val_registration_cache - .read() - .get(&pubkey) - .ok_or_else(|| reject("missing registration"))? - .clone(); - let cached_data = signed_cached_data.message; - - let head = builder - .beacon_client - .get_beacon_blocks::(BlockId::Head) - .await - .map_err(|_| reject("couldn't get head"))? - .ok_or_else(|| reject("missing head block"))?; - - let block = head.data.message(); - let head_block_root = block.tree_hash_root(); - let head_execution_payload = block - .body() - .execution_payload() - .map_err(|_| reject("pre-merge block"))?; - let head_execution_hash = head_execution_payload.block_hash(); - let head_gas_limit = head_execution_payload.gas_limit(); - if head_execution_hash != parent_hash { - return Err(reject("head mismatch")); - } - - let finalized_execution_hash = builder - .beacon_client - .get_beacon_blocks::(BlockId::Finalized) - .await - .map_err(|_| reject("couldn't get finalized block"))? - .ok_or_else(|| reject("missing finalized block"))? - .data - .message() - .body() - .execution_payload() - .map_err(|_| reject("pre-merge block"))? - .block_hash(); - - let justified_execution_hash = builder - .beacon_client - .get_beacon_blocks::(BlockId::Justified) - .await - .map_err(|_| reject("couldn't get justified block"))? - .ok_or_else(|| reject("missing justified block"))? - .data - .message() - .body() - .execution_payload() - .map_err(|_| reject("pre-merge block"))? - .block_hash(); - - let val_index = builder - .beacon_client - .get_beacon_states_validator_id(StateId::Head, &ValidatorId::PublicKey(pubkey)) - .await - .map_err(|_| reject("couldn't get validator"))? - .ok_or_else(|| reject("missing validator"))? - .data - .index; - let fee_recipient = cached_data.fee_recipient; - let slots_since_genesis = slot.as_u64() - builder.spec.genesis_slot.as_u64(); - - let genesis_data = builder - .beacon_client - .get_beacon_genesis() - .await - .map_err(|_| reject("couldn't get beacon genesis"))? - .data; - let genesis_time = genesis_data.genesis_time; - let timestamp = - (slots_since_genesis * builder.spec.seconds_per_slot) + genesis_time; - - let head_state: BeaconState = builder - .beacon_client - .get_debug_beacon_states(StateId::Head) + let fork_name = builder.fork_name_at_slot(slot); + let signed_bid = builder + .get_header(slot, parent_hash, pubkey) .await - .map_err(|_| reject("couldn't get state"))? - .ok_or_else(|| reject("missing state"))? - .data; - let prev_randao = head_state - .get_randao_mix(head_state.current_epoch()) - .map_err(|_| reject("couldn't get prev randao"))?; - - let expected_withdrawals = if fork.capella_enabled() { - Some( - builder - .beacon_client - .get_expected_withdrawals(&StateId::Head) - .await - .unwrap() - .data, - ) - } else { - None - }; - - let payload_attributes = match fork { - // the withdrawals root is filled in by operations, but we supply the valid withdrawals - // first to avoid polluting the execution block generator with invalid payload attributes - // NOTE: this was part of an effort to add payload attribute uniqueness checks, - // which was abandoned because it broke too many tests in subtle ways. - ForkName::Bellatrix | ForkName::Capella => PayloadAttributes::new( - timestamp, - *prev_randao, - fee_recipient, - expected_withdrawals, - None, - ), - ForkName::Deneb | ForkName::Electra => PayloadAttributes::new( - timestamp, - *prev_randao, - fee_recipient, - expected_withdrawals, - Some(head_block_root), - ), - ForkName::Base | ForkName::Altair => { - return Err(reject("invalid fork")); - } - }; - - builder - .el - .insert_proposer(slot, head_block_root, val_index, payload_attributes.clone()) - .await; - - let forkchoice_update_params = ForkchoiceUpdateParameters { - head_root: Hash256::zero(), - head_hash: None, - justified_hash: Some(justified_execution_hash), - finalized_hash: Some(finalized_execution_hash), - }; - - let proposer_gas_limit = builder - .val_registration_cache - .read() - .get(&pubkey) - .map(|v| v.message.gas_limit); - - let payload_parameters = PayloadParameters { - parent_hash: head_execution_hash, - parent_gas_limit: head_gas_limit, - proposer_gas_limit, - payload_attributes: &payload_attributes, - forkchoice_update_params: &forkchoice_update_params, - current_fork: fork, - }; - - let payload_response_type = builder - .el - .get_full_payload_caching(payload_parameters) - .await - .map_err(|_| reject("couldn't get payload"))?; - - let mut message = match payload_response_type { - crate::GetPayloadResponseType::Full(payload_response) => { - #[allow(clippy::type_complexity)] - let (payload, value, maybe_blobs_bundle, maybe_requests): ( - ExecutionPayload, - Uint256, - Option>, - Option>, - ) = payload_response.into(); - - match fork { - ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { - header: payload - .as_electra() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value, - pubkey: builder.builder_sk.public_key().compress(), - execution_requests: maybe_requests.unwrap_or_default(), - }), - ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { - header: payload - .as_deneb() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value, - pubkey: builder.builder_sk.public_key().compress(), - }), - ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { - header: payload - .as_capella() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - value, - pubkey: builder.builder_sk.public_key().compress(), - }), - ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { - header: payload - .as_bellatrix() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - value, - pubkey: builder.builder_sk.public_key().compress(), - }), - ForkName::Base | ForkName::Altair => { - return Err(reject("invalid fork")) - } - } - } - crate::GetPayloadResponseType::Blinded(payload_response) => { - #[allow(clippy::type_complexity)] - let (payload, value, maybe_blobs_bundle, maybe_requests): ( - ExecutionPayload, - Uint256, - Option>, - Option>, - ) = payload_response.into(); - match fork { - ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { - header: payload - .as_electra() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value, - pubkey: builder.builder_sk.public_key().compress(), - execution_requests: maybe_requests.unwrap_or_default(), - }), - ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { - header: payload - .as_deneb() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value, - pubkey: builder.builder_sk.public_key().compress(), - }), - ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { - header: payload - .as_capella() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - value, - pubkey: builder.builder_sk.public_key().compress(), - }), - ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { - header: payload - .as_bellatrix() - .map_err(|_| reject("incorrect payload variant"))? - .into(), - value, - pubkey: builder.builder_sk.public_key().compress(), - }), - ForkName::Base | ForkName::Altair => { - return Err(reject("invalid fork")) - } - } - } - }; - - builder.apply_operations(&mut message); - - let mut signature = - message.sign_builder_message(&builder.builder_sk, &builder.spec); - - if *builder.invalidate_signatures.read() { - signature = Signature::empty(); - } + .map_err(|e| warp::reject::custom(Custom(e)))?; - let fork_name = builder - .spec - .fork_name_at_epoch(slot.epoch(E::slots_per_epoch())); - let signed_bid = SignedBuilderBid { message, signature }; let resp: ForkVersionedResponse<_> = ForkVersionedResponse { version: Some(fork_name), metadata: Default::default(), diff --git a/beacon_node/execution_layer/src/test_utils/mod.rs b/beacon_node/execution_layer/src/test_utils/mod.rs index ba66a743da4..faf6d4ef0b6 100644 --- a/beacon_node/execution_layer/src/test_utils/mod.rs +++ b/beacon_node/execution_layer/src/test_utils/mod.rs @@ -31,9 +31,7 @@ pub use execution_block_generator::{ mock_el_extra_data, static_valid_tx, Block, ExecutionBlockGenerator, }; pub use hook::Hook; -pub use mock_builder::{ - mock_builder_extra_data, serve as serve_mock_builder, MockBuilder, Operation, -}; +pub use mock_builder::{mock_builder_extra_data, MockBuilder, Operation}; pub use mock_execution_layer::MockExecutionLayer; pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400; From 9bd8ef9c5ba111bed2e5cb0d0e0e57f12e03e789 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Tue, 17 Dec 2024 14:32:46 -0800 Subject: [PATCH 03/12] Return a higher payload value for builder by default --- .../src/test_utils/mock_builder.rs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 56ab404d4b9..2153e6b4af4 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -1,4 +1,4 @@ -use crate::test_utils::DEFAULT_JWT_SECRET; +use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET}; use crate::{Config, ExecutionLayer, PayloadAttributes, PayloadParameters}; use eth2::types::{BlobsBundle, BlockId, StateId, ValidatorId}; use eth2::{BeaconNodeHttpClient, Timeouts, CONSENSUS_VERSION_HEADER}; @@ -536,7 +536,7 @@ impl MockBuilder { let mut message = match payload_response_type { crate::GetPayloadResponseType::Full(payload_response) => { #[allow(clippy::type_complexity)] - let (payload, value, maybe_blobs_bundle, maybe_requests): ( + let (payload, _value, maybe_blobs_bundle, maybe_requests): ( ExecutionPayload, Uint256, Option>, @@ -552,7 +552,8 @@ impl MockBuilder { blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), + pubkey: self.builder_sk.public_key().compress(), execution_requests: maybe_requests.unwrap_or_default(), }), @@ -564,7 +565,7 @@ impl MockBuilder { blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { @@ -572,7 +573,7 @@ impl MockBuilder { .as_capella() .map_err(|_| "incorrect payload variant".to_string())? .into(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { @@ -580,7 +581,7 @@ impl MockBuilder { .as_bellatrix() .map_err(|_| "incorrect payload variant".to_string())? .into(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), @@ -588,7 +589,7 @@ impl MockBuilder { } crate::GetPayloadResponseType::Blinded(payload_response) => { #[allow(clippy::type_complexity)] - let (payload, value, maybe_blobs_bundle, maybe_requests): ( + let (payload, _value, maybe_blobs_bundle, maybe_requests): ( ExecutionPayload, Uint256, Option>, @@ -603,7 +604,7 @@ impl MockBuilder { blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), execution_requests: maybe_requests.unwrap_or_default(), }), @@ -615,7 +616,7 @@ impl MockBuilder { blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { @@ -623,7 +624,7 @@ impl MockBuilder { .as_capella() .map_err(|_| "incorrect payload variant".to_string())? .into(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { @@ -631,7 +632,7 @@ impl MockBuilder { .as_bellatrix() .map_err(|_| "incorrect payload variant".to_string())? .into(), - value, + value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), From c678427780de056815f39e44dad838a7f7913377 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Wed, 18 Dec 2024 18:36:27 -0800 Subject: [PATCH 04/12] Add additional methods --- beacon_node/execution_layer/src/engine_api.rs | 1 + beacon_node/execution_layer/src/lib.rs | 2 +- .../src/test_utils/mock_builder.rs | 571 ++++++++++++------ 3 files changed, 389 insertions(+), 185 deletions(-) diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index 083aaf2e258..94ae2dbb735 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -358,6 +358,7 @@ impl From> } } +#[derive(Debug)] pub enum GetPayloadResponseType { Full(GetPayloadResponse), Blinded(GetPayloadResponse), diff --git a/beacon_node/execution_layer/src/lib.rs b/beacon_node/execution_layer/src/lib.rs index 7976a13e5c5..26ecabaa6f8 100644 --- a/beacon_node/execution_layer/src/lib.rs +++ b/beacon_node/execution_layer/src/lib.rs @@ -320,7 +320,7 @@ impl> BlockProposalContents { pub parent_hash: ExecutionBlockHash, pub parent_gas_limit: u64, diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 2153e6b4af4..6c24309c661 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -1,10 +1,15 @@ use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET}; use crate::{Config, ExecutionLayer, PayloadAttributes, PayloadParameters}; -use eth2::types::{BlobsBundle, BlockId, StateId, ValidatorId}; +use eth2::types::PublishBlockRequest; +use eth2::types::{ + BlobsBundle, BlockId, BroadcastValidation, EventKind, EventTopic, ProposerData, StateId, + ValidatorId, +}; use eth2::{BeaconNodeHttpClient, Timeouts, CONSENSUS_VERSION_HEADER}; use fork_choice::ForkchoiceUpdateParameters; use parking_lot::RwLock; use sensitive_url::SensitiveUrl; +use slog::{debug, error, info, warn, Logger}; use std::collections::HashMap; use std::fmt::Debug; use std::future::Future; @@ -13,20 +18,24 @@ use std::sync::Arc; use std::time::Duration; use task_executor::TaskExecutor; use tempfile::NamedTempFile; +use tokio_stream::StreamExt; use tree_hash::TreeHash; use types::builder_bid::{ BuilderBid, BuilderBidBellatrix, BuilderBidCapella, BuilderBidDeneb, BuilderBidElectra, SignedBuilderBid, }; use types::{ - Address, BeaconState, ChainSpec, EthSpec, ExecPayload, ExecutionPayload, - ExecutionPayloadHeaderRefMut, ExecutionRequests, FixedBytesExtended, ForkName, - ForkVersionedResponse, Hash256, PublicKeyBytes, Signature, SignedBlindedBeaconBlock, - SignedRoot, SignedValidatorRegistrationData, Slot, Uint256, + Address, BeaconState, ChainSpec, Epoch, EthSpec, ExecPayload, ExecutionPayload, + ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionedResponse, Hash256, + PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, + SignedValidatorRegistrationData, Slot, Uint256, }; use types::{ExecutionBlockHash, SecretKey}; use warp::{Filter, Rejection}; +pub const DEFAULT_FEE_RECIPIENT: Address = Address::repeat_byte(42); +pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000; + #[derive(Clone)] pub enum Operation { FeeRecipient(Address), @@ -234,6 +243,17 @@ impl BidStuff for BuilderBid { } } +// Non referenced version of `PayloadParameters` +#[derive(Clone)] +pub struct PayloadParametersCloned { + pub parent_hash: ExecutionBlockHash, + pub parent_gas_limit: u64, + pub proposer_gas_limit: Option, + pub payload_attributes: PayloadAttributes, + pub forkchoice_update_params: ForkchoiceUpdateParameters, + pub current_fork: ForkName, +} + #[derive(Clone)] pub struct MockBuilder { el: ExecutionLayer, @@ -243,6 +263,18 @@ pub struct MockBuilder { builder_sk: SecretKey, operations: Arc>>, invalidate_signatures: Arc>, + genesis_time: Option, + /// Only returns bids for registered validators if set to true. `true` by default. + validate_pubkey: bool, + /// Do not apply any operations if set to `false`. + /// Applying operations might modify the cached header in the execution layer. + /// Use this if you want get_header to return a valid bid that can be eventually submitted as + /// a valid block. + apply_operations: bool, + payload_id_cache: Arc>>, + /// A cache that stores the proposers index for a given epoch + proposers_cache: Arc>>>, + log: Logger, } impl MockBuilder { @@ -270,7 +302,10 @@ impl MockBuilder { let builder = MockBuilder::new( el, BeaconNodeHttpClient::new(beacon_url, Timeouts::set_all(Duration::from_secs(1))), + true, + true, spec, + executor.log().clone(), ); let host: Ipv4Addr = Ipv4Addr::LOCALHOST; let port = 0; @@ -281,7 +316,10 @@ impl MockBuilder { pub fn new( el: ExecutionLayer, beacon_client: BeaconNodeHttpClient, + validate_pubkey: bool, + apply_operations: bool, spec: Arc, + log: Logger, ) -> Self { let sk = SecretKey::random(); Self { @@ -291,8 +329,14 @@ impl MockBuilder { spec, val_registration_cache: Arc::new(RwLock::new(HashMap::new())), builder_sk: sk, + validate_pubkey, operations: Arc::new(RwLock::new(vec![])), invalidate_signatures: Arc::new(RwLock::new(false)), + payload_id_cache: Arc::new(RwLock::new(HashMap::new())), + proposers_cache: Arc::new(RwLock::new(HashMap::new())), + apply_operations, + genesis_time: None, + log, } } @@ -327,8 +371,19 @@ impl MockBuilder { &self, registrations: Vec, ) -> Result<(), String> { + info!( + self.log, + "Registering validators"; + "count" => registrations.len(), + ); for registration in registrations { if !registration.verify_signature(&self.spec) { + error!( + self.log, + "Failed to register validator"; + "error" => "invalid signature", + "validator" => %registration.message.pubkey + ); return Err("invalid signature".to_string()); } self.val_registration_cache @@ -359,15 +414,30 @@ impl MockBuilder { block.message.body.execution_payload.tree_hash_root() } }; + info!( + self.log, + "Submitting blinded beacon block to builder"; + "block_hash" => %root + ); let payload = self .el .get_payload_by_root(&root) .ok_or_else(|| "missing payload for tx root".to_string())?; - let (payload, _) = payload.deconstruct(); - // TODO(pawan): the reconstruction in the beacon client should propagate the - // blobs as well. If that doesn't happen then its probably a bug here + + let (payload, blobs) = payload.deconstruct(); + let full_block = block + .try_into_full_block(Some(payload.clone())) + .ok_or("Internal error, just provided a payload")?; + debug!( + self.log, + "Got full payload, sending to local beacon node for propagation"; + "txs_count" => payload.transactions().len(), + "blob_count" => blobs.as_ref().map(|b| b.commitments.len()) + ); + let publish_block_request = + PublishBlockRequest::new(Arc::new(full_block), blobs.map(|b| (b.proofs, b.blobs))); self.beacon_client - .post_beacon_blinded_blocks(&block) + .post_beacon_blocks_v2(&publish_block_request, Some(BroadcastValidation::Gossip)) .await .map_err(|e| format!("Failed to post blinded block {:?}", e))?; Ok(payload) @@ -379,33 +449,262 @@ impl MockBuilder { parent_hash: ExecutionBlockHash, pubkey: PublicKeyBytes, ) -> Result, String> { + dbg!(&parent_hash); + // Check if the pubkey has registered with the builder if required + if self.validate_pubkey && !self.val_registration_cache.read().contains_key(&pubkey) { + return Err("validator not registered with builder".to_string()); + } + let payload_parameters = { + let mut guard = self.payload_id_cache.write(); + guard.remove(&parent_hash) + }; + + let payload_parameters = match payload_parameters { + Some(params) => { + dbg!("got matching params"); + params + } + None => { + dbg!("no matching params, preparing yet again"); + self.get_payload_params(slot, None, pubkey, None).await? + } + }; + let fork = self.fork_name_at_slot(slot); - let signed_cached_data = self - .val_registration_cache - .read() - .get(&pubkey) - .ok_or_else(|| "missing registration".to_string())? - .clone(); - let cached_data = signed_cached_data.message; + let payload_response_type = self + .el + .get_full_payload_caching(PayloadParameters { + parent_hash: payload_parameters.parent_hash, + parent_gas_limit: payload_parameters.parent_gas_limit, + proposer_gas_limit: payload_parameters.proposer_gas_limit, + payload_attributes: &payload_parameters.payload_attributes, + forkchoice_update_params: &payload_parameters.forkchoice_update_params, + current_fork: payload_parameters.current_fork, + }) + .await + .map_err(|e| format!("couldn't get payload {:?}", e))?; + + let mut message = match payload_response_type { + crate::GetPayloadResponseType::Full(payload_response) => { + #[allow(clippy::type_complexity)] + let (payload, value, maybe_blobs_bundle, maybe_requests): ( + ExecutionPayload, + Uint256, + Option>, + Option>, + ) = payload_response.into(); + match fork { + ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { + header: payload + .as_electra() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments) + .unwrap_or_default(), + value: if !self.apply_operations { + value + } else { + Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) + }, + pubkey: self.builder_sk.public_key().compress(), + execution_requests: maybe_requests.unwrap_or_default(), + }), + ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { + header: payload + .as_deneb() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + blob_kzg_commitments: maybe_blobs_bundle + .map(|b| b.commitments) + .unwrap_or_default(), + value: if !self.apply_operations { + value + } else { + Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) + }, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { + header: payload + .as_capella() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + value: if !self.apply_operations { + value + } else { + Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) + }, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { + header: payload + .as_bellatrix() + .map_err(|_| "incorrect payload variant".to_string())? + .into(), + value: if !self.apply_operations { + value + } else { + Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) + }, + pubkey: self.builder_sk.public_key().compress(), + }), + ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), + } + } + _ => panic!("just requested full payload, cannot get blinded"), + }; + + if self.apply_operations { + self.apply_operations(&mut message); + } + dbg!(&message.value()); + let mut signature = message.sign_builder_message(&self.builder_sk, &self.spec); + + if *self.invalidate_signatures.read() { + signature = Signature::empty(); + }; + let signed_bid = SignedBuilderBid { message, signature }; + Ok(signed_bid) + } + + fn fork_name_at_slot(&self, slot: Slot) -> ForkName { + self.spec.fork_name_at_slot::(slot) + } + + /// Prepare the execution layer for payload creation every slot for the correct + /// proposer index + pub async fn prepare_execution_layer(&self) -> Result<(), String> { + let mut head_event_stream = self + .beacon_client + .get_events::(&[EventTopic::Head]) + .await + .map_err(|e| format!("Failed to get head event {:?}", e))?; + + while let Some(Ok(event)) = head_event_stream.next().await { + match event { + EventKind::Head(head) => { + println!("Got head event {}", head.block); + let next_slot = head.slot + 1; + // Find the next proposer index from the cached data or through a beacon api call + let epoch = next_slot.epoch(E::slots_per_epoch()); + let position_in_slot = next_slot.as_u64() % E::slots_per_epoch(); + let proposer_data = { + let proposers_opt = { + let proposers_cache = self.proposers_cache.read(); + proposers_cache.get(&epoch).cloned() + }; + match proposers_opt { + Some(proposers) => { + proposers.get(position_in_slot as usize).unwrap().clone() + } + None => { + // make a call to the beacon api and populate the cache + let duties: Vec<_> = self + .beacon_client + .get_validator_duties_proposer(epoch) + .await + .map_err(|e| { + format!( + "Failed to get proposer duties for epoch: {}, {:?}", + epoch, e + ) + })? + .data; + let proposer_data = + duties.get(position_in_slot as usize).unwrap().clone(); + self.proposers_cache.write().insert(epoch, duties); + proposer_data + } + } + }; + self.prepare_execution_layer_internal( + head.slot, + head.block, + proposer_data.validator_index, + proposer_data.pubkey, + ) + .await?; + } + e => { + warn!( + self.log, + "Got an unexpected event"; + "event" => %e.topic_name() + ); + } + } + } + Ok(()) + } + + async fn prepare_execution_layer_internal( + &self, + current_slot: Slot, + head_block_root: Hash256, + validator_index: u64, + pubkey: PublicKeyBytes, + ) -> Result<(), String> { + info!(self.log, "Preparing internal"); + dbg!(¤t_slot, &head_block_root, &validator_index, &pubkey); + let next_slot = current_slot + 1; + let payload_parameters = self + .get_payload_params( + next_slot, + Some(head_block_root), + pubkey, + Some(validator_index), + ) + .await?; + + self.payload_id_cache.write().insert( + payload_parameters.parent_hash, + PayloadParametersCloned { + current_fork: payload_parameters.current_fork, + forkchoice_update_params: payload_parameters.forkchoice_update_params, + parent_gas_limit: payload_parameters.parent_gas_limit, + parent_hash: payload_parameters.parent_hash, + payload_attributes: payload_parameters.payload_attributes.clone(), + proposer_gas_limit: payload_parameters.proposer_gas_limit, + }, + ); + Ok(()) + } + + /// Get the `PayloadParameters` for requesting an ExecutionPayload for `slot` + /// for the given `validator_index` and `pubkey`. + async fn get_payload_params( + &self, + slot: Slot, + head_block_root: Option, + pubkey: PublicKeyBytes, + validator_index: Option, + ) -> Result { + let fork = self.fork_name_at_slot(slot); + + let block_id = match head_block_root { + Some(block_root) => BlockId::Root(block_root), + None => BlockId::Head, + }; let head = self .beacon_client - .get_beacon_blocks::(BlockId::Head) + .get_beacon_blocks::(block_id) .await .map_err(|_| "couldn't get head".to_string())? - .ok_or_else(|| "missing head block".to_string())?; + .ok_or_else(|| "missing head block".to_string())? + .data; + + let head_block_root = head_block_root.unwrap_or(head.canonical_root()); - let block = head.data.message(); - let head_block_root = block.tree_hash_root(); - let head_execution_payload = block + let head_execution_payload = head + .message() .body() .execution_payload() .map_err(|_| "pre-merge block".to_string())?; let head_execution_hash = head_execution_payload.block_hash(); + dbg!(&head_execution_hash); let head_gas_limit = head_execution_payload.gas_limit(); - if head_execution_hash != parent_hash { - return Err("head mismatch".to_string()); - } let finalized_execution_hash = self .beacon_client @@ -433,25 +732,26 @@ impl MockBuilder { .map_err(|_| "pre-merge block".to_string())? .block_hash(); - let val_index = self - .beacon_client - .get_beacon_states_validator_id(StateId::Head, &ValidatorId::PublicKey(pubkey)) - .await - .map_err(|_| "couldn't get validator".to_string())? - .ok_or_else(|| "missing validator".to_string())? - .data - .index; - let fee_recipient = cached_data.fee_recipient; + let (fee_recipient, proposer_gas_limit) = + match self.val_registration_cache.read().get(&pubkey) { + Some(cached_data) => ( + cached_data.message.fee_recipient, + cached_data.message.gas_limit, + ), + None => (DEFAULT_FEE_RECIPIENT, DEFAULT_GAS_LIMIT), + }; let slots_since_genesis = slot.as_u64() - self.spec.genesis_slot.as_u64(); - // TODO(pawan): cache this call - let genesis_data = self - .beacon_client - .get_beacon_genesis() - .await - .map_err(|_| "couldn't get beacon genesis".to_string())? - .data; - let genesis_time = genesis_data.genesis_time; + let genesis_time = if let Some(genesis_time) = self.genesis_time { + genesis_time + } else { + self.beacon_client + .get_beacon_genesis() + .await + .map_err(|_| "couldn't get beacon genesis".to_string())? + .data + .genesis_time + }; let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time; let head_state: BeaconState = self @@ -461,6 +761,7 @@ impl MockBuilder { .map_err(|_| "couldn't get state".to_string())? .ok_or_else(|| "missing state".to_string())? .data; + let prev_randao = head_state .get_randao_mix(head_state.current_epoch()) .map_err(|_| "couldn't get prev randao".to_string())?; @@ -501,158 +802,55 @@ impl MockBuilder { } }; + // Tells the execution layer that the `validator_index` is expected to propose + // a block on top of `head_block_root` for the given slot + let val_index = validator_index.unwrap_or( + self.beacon_client + .get_beacon_states_validator_id(StateId::Head, &ValidatorId::PublicKey(pubkey)) + .await + .map_err(|_| "couldn't get validator".to_string())? + .ok_or_else(|| "missing validator".to_string())? + .data + .index, + ); + + println!( + "Inserting proposer slot: {}, head_block_root: {}, val_index: {}", + slot, head_block_root, val_index + ); self.el .insert_proposer(slot, head_block_root, val_index, payload_attributes.clone()) .await; let forkchoice_update_params = ForkchoiceUpdateParameters { - head_root: Hash256::zero(), - head_hash: None, - justified_hash: Some(justified_execution_hash), + head_hash: Some(head_execution_hash), finalized_hash: Some(finalized_execution_hash), + justified_hash: Some(justified_execution_hash), + head_root: head_block_root, }; + dbg!(&forkchoice_update_params); + let status = self + .el + .notify_forkchoice_updated( + head_execution_hash, + justified_execution_hash, + finalized_execution_hash, + slot - 1, + head_block_root, + ) + .await + .map_err(|e| format!("fcu call failed : {:?}", e))?; + dbg!(&status); - let proposer_gas_limit = self - .val_registration_cache - .read() - .get(&pubkey) - .map(|v| v.message.gas_limit); - - let payload_parameters = PayloadParameters { + let payload_parameters = PayloadParametersCloned { parent_hash: head_execution_hash, parent_gas_limit: head_gas_limit, - proposer_gas_limit, - payload_attributes: &payload_attributes, - forkchoice_update_params: &forkchoice_update_params, + proposer_gas_limit: Some(proposer_gas_limit), + payload_attributes, + forkchoice_update_params, current_fork: fork, }; - - let payload_response_type = self - .el - .get_full_payload_caching(payload_parameters) - .await - .map_err(|_| "couldn't get payload".to_string())?; - - let mut message = match payload_response_type { - crate::GetPayloadResponseType::Full(payload_response) => { - #[allow(clippy::type_complexity)] - let (payload, _value, maybe_blobs_bundle, maybe_requests): ( - ExecutionPayload, - Uint256, - Option>, - Option>, - ) = payload_response.into(); - - match fork { - ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { - header: payload - .as_electra() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - - pubkey: self.builder_sk.public_key().compress(), - execution_requests: maybe_requests.unwrap_or_default(), - }), - ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { - header: payload - .as_deneb() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - }), - ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { - header: payload - .as_capella() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - }), - ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { - header: payload - .as_bellatrix() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - }), - ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), - } - } - crate::GetPayloadResponseType::Blinded(payload_response) => { - #[allow(clippy::type_complexity)] - let (payload, _value, maybe_blobs_bundle, maybe_requests): ( - ExecutionPayload, - Uint256, - Option>, - Option>, - ) = payload_response.into(); - match fork { - ForkName::Electra => BuilderBid::Electra(BuilderBidElectra { - header: payload - .as_electra() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - execution_requests: maybe_requests.unwrap_or_default(), - }), - ForkName::Deneb => BuilderBid::Deneb(BuilderBidDeneb { - header: payload - .as_deneb() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - blob_kzg_commitments: maybe_blobs_bundle - .map(|b| b.commitments) - .unwrap_or_default(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - }), - ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { - header: payload - .as_capella() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - }), - ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { - header: payload - .as_bellatrix() - .map_err(|_| "incorrect payload variant".to_string())? - .into(), - value: Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI), - pubkey: self.builder_sk.public_key().compress(), - }), - ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), - } - } - }; - - self.apply_operations(&mut message); - - let mut signature = message.sign_builder_message(&self.builder_sk, &self.spec); - - if *self.invalidate_signatures.read() { - signature = Signature::empty(); - }; - let signed_bid = SignedBuilderBid { message, signature }; - Ok(signed_bid) - } - - fn fork_name_at_slot(&self, slot: Slot) -> ForkName { - self.spec.fork_name_at_slot::(slot) + Ok(payload_parameters) } } @@ -678,11 +876,16 @@ pub fn serve( .and(warp::path::end()) .and(ctx_filter.clone()) .and_then( - |registrations: Vec, builder: MockBuilder| async move { - builder.register_validators(registrations).await.map_err(|e|warp::reject::custom(Custom(e)))?; + |registrations: Vec, + builder: MockBuilder| async move { + builder + .register_validators(registrations) + .await + .map_err(|e| warp::reject::custom(Custom(e)))?; Ok::<_, Rejection>(warp::reply()) }, - ).boxed(); + ) + .boxed(); let blinded_block = prefix From cb9631dd854685d385ddf6f7727d21aed62ba7d7 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Thu, 19 Dec 2024 11:04:48 -0800 Subject: [PATCH 05/12] Cleanup --- .../src/test_utils/mock_builder.rs | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 6c24309c661..4d06b150332 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -449,7 +449,6 @@ impl MockBuilder { parent_hash: ExecutionBlockHash, pubkey: PublicKeyBytes, ) -> Result, String> { - dbg!(&parent_hash); // Check if the pubkey has registered with the builder if required if self.validate_pubkey && !self.val_registration_cache.read().contains_key(&pubkey) { return Err("validator not registered with builder".to_string()); @@ -460,14 +459,8 @@ impl MockBuilder { }; let payload_parameters = match payload_parameters { - Some(params) => { - dbg!("got matching params"); - params - } - None => { - dbg!("no matching params, preparing yet again"); - self.get_payload_params(slot, None, pubkey, None).await? - } + Some(params) => params, + None => self.get_payload_params(slot, None, pubkey, None).await?, }; let fork = self.fork_name_at_slot(slot); @@ -559,7 +552,7 @@ impl MockBuilder { if self.apply_operations { self.apply_operations(&mut message); } - dbg!(&message.value()); + let mut signature = message.sign_builder_message(&self.builder_sk, &self.spec); if *self.invalidate_signatures.read() { @@ -576,6 +569,10 @@ impl MockBuilder { /// Prepare the execution layer for payload creation every slot for the correct /// proposer index pub async fn prepare_execution_layer(&self) -> Result<(), String> { + info!( + self.log, + "Starting a task to prepare the execution layer"; + ); let mut head_event_stream = self .beacon_client .get_events::(&[EventTopic::Head]) @@ -585,7 +582,11 @@ impl MockBuilder { while let Some(Ok(event)) = head_event_stream.next().await { match event { EventKind::Head(head) => { - println!("Got head event {}", head.block); + debug!( + self.log, + "Got a new head event"; + "block_hash" => %head.block + ); let next_slot = head.slot + 1; // Find the next proposer index from the cached data or through a beacon api call let epoch = next_slot.epoch(E::slots_per_epoch()); @@ -596,9 +597,10 @@ impl MockBuilder { proposers_cache.get(&epoch).cloned() }; match proposers_opt { - Some(proposers) => { - proposers.get(position_in_slot as usize).unwrap().clone() - } + Some(proposers) => proposers + .get(position_in_slot as usize) + .expect("position in slot is max epoch size") + .clone(), None => { // make a call to the beacon api and populate the cache let duties: Vec<_> = self @@ -612,8 +614,10 @@ impl MockBuilder { ) })? .data; - let proposer_data = - duties.get(position_in_slot as usize).unwrap().clone(); + let proposer_data = duties + .get(position_in_slot as usize) + .expect("position in slot is max epoch size") + .clone(); self.proposers_cache.write().insert(epoch, duties); proposer_data } @@ -646,8 +650,6 @@ impl MockBuilder { validator_index: u64, pubkey: PublicKeyBytes, ) -> Result<(), String> { - info!(self.log, "Preparing internal"); - dbg!(¤t_slot, &head_block_root, &validator_index, &pubkey); let next_slot = current_slot + 1; let payload_parameters = self .get_payload_params( @@ -703,7 +705,6 @@ impl MockBuilder { .execution_payload() .map_err(|_| "pre-merge block".to_string())?; let head_execution_hash = head_execution_payload.block_hash(); - dbg!(&head_execution_hash); let head_gas_limit = head_execution_payload.gas_limit(); let finalized_execution_hash = self @@ -771,7 +772,7 @@ impl MockBuilder { self.beacon_client .get_expected_withdrawals(&StateId::Head) .await - .unwrap() + .map_err(|e| format!("Failed to get expected withdrawals: {:?}", e))? .data, ) } else { @@ -814,10 +815,6 @@ impl MockBuilder { .index, ); - println!( - "Inserting proposer slot: {}, head_block_root: {}, val_index: {}", - slot, head_block_root, val_index - ); self.el .insert_proposer(slot, head_block_root, val_index, payload_attributes.clone()) .await; @@ -828,8 +825,8 @@ impl MockBuilder { justified_hash: Some(justified_execution_hash), head_root: head_block_root, }; - dbg!(&forkchoice_update_params); - let status = self + + let _status = self .el .notify_forkchoice_updated( head_execution_hash, @@ -840,7 +837,6 @@ impl MockBuilder { ) .await .map_err(|e| format!("fcu call failed : {:?}", e))?; - dbg!(&status); let payload_parameters = PayloadParametersCloned { parent_hash: head_execution_hash, From bfcbd7795ee6356f512235980620d0a712c9020b Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Thu, 19 Dec 2024 18:51:33 -0800 Subject: [PATCH 06/12] Add a flag for always returning a max bid --- .../src/test_utils/mock_builder.rs | 39 +++++++++---------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 4d06b150332..420ceea87ee 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -272,6 +272,8 @@ pub struct MockBuilder { /// a valid block. apply_operations: bool, payload_id_cache: Arc>>, + /// If set to `true`, sets the bid returned by `get_header` to Uint256::MAX + max_bid: bool, /// A cache that stores the proposers index for a given epoch proposers_cache: Arc>>>, log: Logger, @@ -304,6 +306,7 @@ impl MockBuilder { BeaconNodeHttpClient::new(beacon_url, Timeouts::set_all(Duration::from_secs(1))), true, true, + false, spec, executor.log().clone(), ); @@ -318,6 +321,7 @@ impl MockBuilder { beacon_client: BeaconNodeHttpClient, validate_pubkey: bool, apply_operations: bool, + max_bid: bool, spec: Arc, log: Logger, ) -> Self { @@ -335,6 +339,7 @@ impl MockBuilder { payload_id_cache: Arc::new(RwLock::new(HashMap::new())), proposers_cache: Arc::new(RwLock::new(HashMap::new())), apply_operations, + max_bid, genesis_time: None, log, } @@ -496,11 +501,7 @@ impl MockBuilder { blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value: if !self.apply_operations { - value - } else { - Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) - }, + value: self.get_bid_value(value), pubkey: self.builder_sk.public_key().compress(), execution_requests: maybe_requests.unwrap_or_default(), }), @@ -512,11 +513,7 @@ impl MockBuilder { blob_kzg_commitments: maybe_blobs_bundle .map(|b| b.commitments) .unwrap_or_default(), - value: if !self.apply_operations { - value - } else { - Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) - }, + value: self.get_bid_value(value), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Capella => BuilderBid::Capella(BuilderBidCapella { @@ -524,11 +521,7 @@ impl MockBuilder { .as_capella() .map_err(|_| "incorrect payload variant".to_string())? .into(), - value: if !self.apply_operations { - value - } else { - Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) - }, + value: self.get_bid_value(value), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Bellatrix => BuilderBid::Bellatrix(BuilderBidBellatrix { @@ -536,11 +529,7 @@ impl MockBuilder { .as_bellatrix() .map_err(|_| "incorrect payload variant".to_string())? .into(), - value: if !self.apply_operations { - value - } else { - Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) - }, + value: self.get_bid_value(value), pubkey: self.builder_sk.public_key().compress(), }), ForkName::Base | ForkName::Altair => return Err("invalid fork".to_string()), @@ -566,6 +555,16 @@ impl MockBuilder { self.spec.fork_name_at_slot::(slot) } + fn get_bid_value(&self, value: Uint256) -> Uint256 { + if self.max_bid { + Uint256::MAX + } else if !self.apply_operations { + value + } else { + Uint256::from(DEFAULT_BUILDER_PAYLOAD_VALUE_WEI) + } + } + /// Prepare the execution layer for payload creation every slot for the correct /// proposer index pub async fn prepare_execution_layer(&self) -> Result<(), String> { From 91c1cf9d821905b2c63bd443e21d6a57bf9a4496 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Thu, 2 Jan 2025 14:23:44 -0800 Subject: [PATCH 07/12] Add logs for debugging --- .../execution_layer/src/test_utils/mock_builder.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 420ceea87ee..12245d2b93d 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -454,6 +454,7 @@ impl MockBuilder { parent_hash: ExecutionBlockHash, pubkey: PublicKeyBytes, ) -> Result, String> { + info!(self.log, "In get_header"); // Check if the pubkey has registered with the builder if required if self.validate_pubkey && !self.val_registration_cache.read().contains_key(&pubkey) { return Err("validator not registered with builder".to_string()); @@ -468,6 +469,8 @@ impl MockBuilder { None => self.get_payload_params(slot, None, pubkey, None).await?, }; + info!(self.log, "Got payload params"); + let fork = self.fork_name_at_slot(slot); let payload_response_type = self .el @@ -481,6 +484,8 @@ impl MockBuilder { }) .await .map_err(|e| format!("couldn't get payload {:?}", e))?; + info!(self.log, "Got payload message"); + info!(self.log, "fork {}", fork); let mut message = match payload_response_type { crate::GetPayloadResponseType::Full(payload_response) => { @@ -539,8 +544,10 @@ impl MockBuilder { }; if self.apply_operations { + info!(self.log, "Applying operations"); self.apply_operations(&mut message); } + info!(self.log, "Signing builder message"); let mut signature = message.sign_builder_message(&self.builder_sk, &self.spec); @@ -548,6 +555,7 @@ impl MockBuilder { signature = Signature::empty(); }; let signed_bid = SignedBuilderBid { message, signature }; + info!(self.log, "Builder bid {:?}", &signed_bid.message); Ok(signed_bid) } From 8c93b11172431fc0dd03aaddb97ef0e1fc2d9053 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Fri, 3 Jan 2025 14:16:06 -0800 Subject: [PATCH 08/12] Take builder secret key as an argument --- .../src/test_utils/mock_builder.rs | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 12245d2b93d..efaf3642a41 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -35,6 +35,8 @@ use warp::{Filter, Rejection}; pub const DEFAULT_FEE_RECIPIENT: Address = Address::repeat_byte(42); pub const DEFAULT_GAS_LIMIT: u64 = 30_000_000; +pub const DEFAULT_BUILDER_PRIVATE_KEY: &str = + "607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2"; #[derive(Clone)] pub enum Operation { @@ -308,6 +310,7 @@ impl MockBuilder { true, false, spec, + None, executor.log().clone(), ); let host: Ipv4Addr = Ipv4Addr::LOCALHOST; @@ -316,6 +319,7 @@ impl MockBuilder { (builder, server) } + #[allow(clippy::too_many_arguments)] pub fn new( el: ExecutionLayer, beacon_client: BeaconNodeHttpClient, @@ -323,16 +327,30 @@ impl MockBuilder { apply_operations: bool, max_bid: bool, spec: Arc, + sk: Option<&[u8]>, log: Logger, ) -> Self { - let sk = SecretKey::random(); + let builder_sk = if let Some(sk_bytes) = sk { + match SecretKey::deserialize(sk_bytes) { + Ok(sk) => sk, + Err(_) => { + error!( + log, + "Invalid sk_bytes provided, generating random secret key" + ); + SecretKey::random() + } + } + } else { + SecretKey::deserialize(&hex::decode(DEFAULT_BUILDER_PRIVATE_KEY).unwrap()).unwrap() + }; Self { el, beacon_client, // Should keep spec and context consistent somehow spec, val_registration_cache: Arc::new(RwLock::new(HashMap::new())), - builder_sk: sk, + builder_sk, validate_pubkey, operations: Arc::new(RwLock::new(vec![])), invalidate_signatures: Arc::new(RwLock::new(false)), From f15b235447e895f9f4471c3c567531dd986153aa Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Wed, 15 Jan 2025 18:39:41 -0800 Subject: [PATCH 09/12] Change return type for submit_blinded_blocks --- .../src/test_utils/mock_builder.rs | 51 +++++++++++-------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 15054bbb738..3540909fe46 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -2,8 +2,8 @@ use crate::test_utils::{DEFAULT_BUILDER_PAYLOAD_VALUE_WEI, DEFAULT_JWT_SECRET}; use crate::{Config, ExecutionLayer, PayloadAttributes, PayloadParameters}; use eth2::types::PublishBlockRequest; use eth2::types::{ - BlobsBundle, BlockId, BroadcastValidation, EventKind, EventTopic, ProposerData, StateId, - ValidatorId, + BlobsBundle, BlockId, BroadcastValidation, EventKind, EventTopic, FullPayloadContents, + ProposerData, StateId, ValidatorId, }; use eth2::{BeaconNodeHttpClient, Timeouts, CONSENSUS_VERSION_HEADER}; use fork_choice::ForkchoiceUpdateParameters; @@ -444,7 +444,7 @@ impl MockBuilder { pub async fn submit_blinded_block( &self, block: SignedBlindedBeaconBlock, - ) -> Result, String> { + ) -> Result, String> { let root = match &block { SignedBlindedBeaconBlock::Base(_) | types::SignedBeaconBlock::Altair(_) => { return Err("invalid fork".to_string()); @@ -485,13 +485,15 @@ impl MockBuilder { "txs_count" => payload.transactions().len(), "blob_count" => blobs.as_ref().map(|b| b.commitments.len()) ); - let publish_block_request = - PublishBlockRequest::new(Arc::new(full_block), blobs.map(|b| (b.proofs, b.blobs))); + let publish_block_request = PublishBlockRequest::new( + Arc::new(full_block), + blobs.clone().map(|b| (b.proofs, b.blobs)), + ); self.beacon_client .post_beacon_blocks_v2(&publish_block_request, Some(BroadcastValidation::Gossip)) .await .map_err(|e| format!("Failed to post blinded block {:?}", e))?; - Ok(payload) + Ok(FullPayloadContents::new(payload, blobs)) } pub async fn get_header( @@ -512,7 +514,13 @@ impl MockBuilder { let payload_parameters = match payload_parameters { Some(params) => params, - None => self.get_payload_params(slot, None, pubkey, None).await?, + None => { + warn!( + self.log, + "Payload params not cached for parent_hash {}", parent_hash + ); + self.get_payload_params(slot, None, pubkey, None).await? + } }; info!(self.log, "Got payload params"); @@ -530,8 +538,8 @@ impl MockBuilder { }) .await .map_err(|e| format!("couldn't get payload {:?}", e))?; - info!(self.log, "Got payload message"); - info!(self.log, "fork {}", fork); + + info!(self.log, "Got payload message, fork {}", fork); let mut message = match payload_response_type { crate::GetPayloadResponseType::Full(payload_response) => { @@ -613,7 +621,7 @@ impl MockBuilder { signature = Signature::empty(); }; let signed_bid = SignedBuilderBid { message, signature }; - info!(self.log, "Builder bid {:?}", &signed_bid.message); + info!(self.log, "Builder bid {:?}", &signed_bid.message.value()); Ok(signed_bid) } @@ -725,17 +733,9 @@ impl MockBuilder { ) .await?; - self.payload_id_cache.write().insert( - payload_parameters.parent_hash, - PayloadParametersCloned { - current_fork: payload_parameters.current_fork, - forkchoice_update_params: payload_parameters.forkchoice_update_params, - parent_gas_limit: payload_parameters.parent_gas_limit, - parent_hash: payload_parameters.parent_hash, - payload_attributes: payload_parameters.payload_attributes.clone(), - proposer_gas_limit: payload_parameters.proposer_gas_limit, - }, - ); + self.payload_id_cache + .write() + .insert(payload_parameters.parent_hash, payload_parameters); Ok(()) } @@ -804,7 +804,14 @@ impl MockBuilder { cached_data.message.fee_recipient, cached_data.message.gas_limit, ), - None => (DEFAULT_FEE_RECIPIENT, DEFAULT_GAS_LIMIT), + None => { + warn!( + self.log, + "Validator not registered {}, using default fee recipient and gas limits", + pubkey + ); + (DEFAULT_FEE_RECIPIENT, DEFAULT_GAS_LIMIT) + } }; let slots_since_genesis = slot.as_u64() - self.spec.genesis_slot.as_u64(); From 1f7b4a327e95d0c7aea3e28dfd3666c093033d89 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Fri, 17 Jan 2025 11:47:49 -0800 Subject: [PATCH 10/12] Respect gas_limit from validator registration --- beacon_node/beacon_chain/src/test_utils.rs | 3 +- .../src/test_utils/mock_builder.rs | 193 ++++++++++++------ 2 files changed, 133 insertions(+), 63 deletions(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 443cc686ebe..56f578b21bc 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -664,7 +664,7 @@ pub struct BeaconChainHarness { pub runtime: TestRuntime, pub mock_execution_layer: Option>, - pub mock_builder: Option>>, + pub mock_builder: Option>>, pub rng: Mutex, } @@ -722,6 +722,7 @@ where mock_el_url, beacon_url, self.spec.clone(), + self.chain.slot_clock.clone(), self.runtime.task_executor.clone(), ); diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 3540909fe46..037a737164e 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -10,6 +10,7 @@ use fork_choice::ForkchoiceUpdateParameters; use parking_lot::RwLock; use sensitive_url::SensitiveUrl; use slog::{debug, error, info, warn, Logger}; +use slot_clock::SlotClock; use std::collections::HashMap; use std::fmt::Debug; use std::future::Future; @@ -27,7 +28,7 @@ use types::builder_bid::{ use types::{ Address, BeaconState, ChainSpec, Epoch, EthSpec, ExecPayload, ExecutionPayload, ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionedResponse, Hash256, - PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, + ProposerPreparationData, PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, SignedValidatorRegistrationData, Slot, Uint256, }; use types::{ExecutionBlockHash, SecretKey}; @@ -282,7 +283,7 @@ pub struct PayloadParametersCloned { } #[derive(Clone)] -pub struct MockBuilder { +pub struct MockBuilder { el: ExecutionLayer, beacon_client: BeaconNodeHttpClient, spec: Arc, @@ -290,7 +291,6 @@ pub struct MockBuilder { builder_sk: SecretKey, operations: Arc>>, invalidate_signatures: Arc>, - genesis_time: Option, /// Only returns bids for registered validators if set to true. `true` by default. validate_pubkey: bool, /// Do not apply any operations if set to `false`. @@ -303,14 +303,17 @@ pub struct MockBuilder { max_bid: bool, /// A cache that stores the proposers index for a given epoch proposers_cache: Arc>>>, + pubkey_cache: Arc>>, + slot_clock: S, log: Logger, } -impl MockBuilder { +impl MockBuilder { pub fn new_for_testing( mock_el_url: SensitiveUrl, beacon_url: SensitiveUrl, spec: Arc, + slot_clock: S, executor: TaskExecutor, ) -> (Self, (SocketAddr, impl Future)) { let file = NamedTempFile::new().unwrap(); @@ -336,6 +339,7 @@ impl MockBuilder { false, spec, None, + slot_clock, executor.log().clone(), ); let host: Ipv4Addr = Ipv4Addr::LOCALHOST; @@ -353,6 +357,7 @@ impl MockBuilder { max_bid: bool, spec: Arc, sk: Option<&[u8]>, + slot_clock: S, log: Logger, ) -> Self { let builder_sk = if let Some(sk_bytes) = sk { @@ -381,9 +386,10 @@ impl MockBuilder { invalidate_signatures: Arc::new(RwLock::new(false)), payload_id_cache: Arc::new(RwLock::new(HashMap::new())), proposers_cache: Arc::new(RwLock::new(HashMap::new())), + pubkey_cache: Arc::new(RwLock::new(HashMap::new())), apply_operations, max_bid, - genesis_time: None, + slot_clock, log, } } @@ -424,6 +430,13 @@ impl MockBuilder { "Registering validators"; "count" => registrations.len(), ); + + let mut preparation_data = Vec::with_capacity(registrations.len()); + let current_epoch = self + .slot_clock + .now() + .ok_or("Failed to get current epoch".to_string())? + .epoch(E::slots_per_epoch()); for registration in registrations { if !registration.verify_signature(&self.spec) { error!( @@ -432,12 +445,77 @@ impl MockBuilder { "error" => "invalid signature", "validator" => %registration.message.pubkey ); - return Err("invalid signature".to_string()); + continue; } + let pubkey = registration.message.pubkey.clone(); + + // First try to get the validator index from cache + let validator_index = { + let pubkey_cache = self.pubkey_cache.write(); + pubkey_cache.get(&pubkey).copied() + }; + + // Get or fetch the validator index + let validator_index = if let Some(validator_index) = validator_index { + validator_index + } else { + // Do the async fetch without holding any locks + let validator_index = self + .beacon_client + .get_beacon_states_validator_id( + StateId::Head, + &ValidatorId::PublicKey(pubkey.clone()), + ) + .await + .map_err(|e| format!("Failed to get validator index: {:?}", e))? + .ok_or("Beacon node returned 404".to_string())? + .data + .index; + + // Update cache after the fetch + // Note: Doing this to avoid locking between await points + { + let mut pubkey_cache = self.pubkey_cache.write(); + pubkey_cache.insert(pubkey.clone(), validator_index); + } + + validator_index + }; + let prep_data = ( + ProposerPreparationData { + validator_index, + fee_recipient: registration.message.fee_recipient, + }, + Some(registration.message.gas_limit), + ); + info!( + self.log, + "Proposer prep data for {} {:?}", pubkey, prep_data, + ); + preparation_data.push(( + ProposerPreparationData { + validator_index, + fee_recipient: registration.message.fee_recipient, + }, + Some(registration.message.gas_limit), + )); self.val_registration_cache .write() .insert(registration.message.pubkey, registration); } + info!( + self.log, + "Updating proposer preparation for {} validators", + preparation_data.len(), + ); + self.el + .update_proposer_preparation( + current_epoch, + preparation_data + .iter() + .map(|(data, gas_limit)| (data, gas_limit)), + ) + .await; Ok(()) } @@ -815,16 +893,7 @@ impl MockBuilder { }; let slots_since_genesis = slot.as_u64() - self.spec.genesis_slot.as_u64(); - let genesis_time = if let Some(genesis_time) = self.genesis_time { - genesis_time - } else { - self.beacon_client - .get_beacon_genesis() - .await - .map_err(|_| "couldn't get beacon genesis".to_string())? - .data - .genesis_time - }; + let genesis_time = self.slot_clock.genesis_duration().as_secs(); let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time; let head_state: BeaconState = self @@ -926,10 +995,10 @@ impl MockBuilder { /// the requests. /// /// We should eventually move this to axum when we move everything else. -pub fn serve( +pub fn serve( listen_addr: Ipv4Addr, listen_port: u16, - builder: MockBuilder, + builder: MockBuilder, ) -> Result<(SocketAddr, impl Future), crate::test_utils::Error> { let inner_ctx = builder.clone(); let ctx_filter = warp::any().map(move || inner_ctx.clone()); @@ -938,57 +1007,57 @@ pub fn serve( .and(warp::path("v1")) .and(warp::path("builder")); - let validators = prefix - .and(warp::path("validators")) - .and(warp::body::json()) - .and(warp::path::end()) - .and(ctx_filter.clone()) - .and_then( - |registrations: Vec, - builder: MockBuilder| async move { - builder - .register_validators(registrations) - .await - .map_err(|e| warp::reject::custom(Custom(e)))?; - Ok::<_, Rejection>(warp::reply()) - }, - ) - .boxed(); - - let blinded_block = + let validators = prefix - .and(warp::path("blinded_blocks")) + .and(warp::path("validators")) .and(warp::body::json()) - .and(warp::header::header::(CONSENSUS_VERSION_HEADER)) .and(warp::path::end()) .and(ctx_filter.clone()) .and_then( - |block: SignedBlindedBeaconBlock, - fork_name: ForkName, - builder: MockBuilder| async move { - let payload = builder - .submit_blinded_block(block) + |registrations: Vec, + builder: MockBuilder| async move { + builder + .register_validators(registrations) .await .map_err(|e| warp::reject::custom(Custom(e)))?; - let resp: ForkVersionedResponse<_> = ForkVersionedResponse { - version: Some(fork_name), - metadata: Default::default(), - data: payload, - }; - - let json_payload = serde_json::to_string(&resp) - .map_err(|_| reject("coudn't serialize response"))?; - Ok::<_, warp::reject::Rejection>( - warp::http::Response::builder() - .status(200) - .body( - serde_json::to_string(&json_payload) - .map_err(|_| reject("invalid JSON"))?, - ) - .unwrap(), - ) + Ok::<_, Rejection>(warp::reply()) }, - ); + ) + .boxed(); + + let blinded_block = prefix + .and(warp::path("blinded_blocks")) + .and(warp::body::json()) + .and(warp::header::header::(CONSENSUS_VERSION_HEADER)) + .and(warp::path::end()) + .and(ctx_filter.clone()) + .and_then( + |block: SignedBlindedBeaconBlock, + fork_name: ForkName, + builder: MockBuilder| async move { + let payload = builder + .submit_blinded_block(block) + .await + .map_err(|e| warp::reject::custom(Custom(e)))?; + let resp: ForkVersionedResponse<_> = ForkVersionedResponse { + version: Some(fork_name), + metadata: Default::default(), + data: payload, + }; + + let json_payload = serde_json::to_string(&resp) + .map_err(|_| reject("coudn't serialize response"))?; + Ok::<_, warp::reject::Rejection>( + warp::http::Response::builder() + .status(200) + .body( + serde_json::to_string(&json_payload) + .map_err(|_| reject("invalid JSON"))?, + ) + .unwrap(), + ) + }, + ); let status = prefix .and(warp::path("status")) @@ -1011,7 +1080,7 @@ pub fn serve( |slot: Slot, parent_hash: ExecutionBlockHash, pubkey: PublicKeyBytes, - builder: MockBuilder| async move { + builder: MockBuilder| async move { let fork_name = builder.fork_name_at_slot(slot); let signed_bid = builder .get_header(slot, parent_hash, pubkey) From 68f4c27c135ccd8a687c905f4d9680c94c663f54 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Fri, 17 Jan 2025 16:19:51 -0800 Subject: [PATCH 11/12] Revert "Respect gas_limit from validator registration" This reverts commit 1f7b4a327e95d0c7aea3e28dfd3666c093033d89. --- beacon_node/beacon_chain/src/test_utils.rs | 3 +- .../src/test_utils/mock_builder.rs | 193 ++++++------------ 2 files changed, 63 insertions(+), 133 deletions(-) diff --git a/beacon_node/beacon_chain/src/test_utils.rs b/beacon_node/beacon_chain/src/test_utils.rs index 56f578b21bc..443cc686ebe 100644 --- a/beacon_node/beacon_chain/src/test_utils.rs +++ b/beacon_node/beacon_chain/src/test_utils.rs @@ -664,7 +664,7 @@ pub struct BeaconChainHarness { pub runtime: TestRuntime, pub mock_execution_layer: Option>, - pub mock_builder: Option>>, + pub mock_builder: Option>>, pub rng: Mutex, } @@ -722,7 +722,6 @@ where mock_el_url, beacon_url, self.spec.clone(), - self.chain.slot_clock.clone(), self.runtime.task_executor.clone(), ); diff --git a/beacon_node/execution_layer/src/test_utils/mock_builder.rs b/beacon_node/execution_layer/src/test_utils/mock_builder.rs index 037a737164e..3540909fe46 100644 --- a/beacon_node/execution_layer/src/test_utils/mock_builder.rs +++ b/beacon_node/execution_layer/src/test_utils/mock_builder.rs @@ -10,7 +10,6 @@ use fork_choice::ForkchoiceUpdateParameters; use parking_lot::RwLock; use sensitive_url::SensitiveUrl; use slog::{debug, error, info, warn, Logger}; -use slot_clock::SlotClock; use std::collections::HashMap; use std::fmt::Debug; use std::future::Future; @@ -28,7 +27,7 @@ use types::builder_bid::{ use types::{ Address, BeaconState, ChainSpec, Epoch, EthSpec, ExecPayload, ExecutionPayload, ExecutionPayloadHeaderRefMut, ExecutionRequests, ForkName, ForkVersionedResponse, Hash256, - ProposerPreparationData, PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, + PublicKeyBytes, Signature, SignedBlindedBeaconBlock, SignedRoot, SignedValidatorRegistrationData, Slot, Uint256, }; use types::{ExecutionBlockHash, SecretKey}; @@ -283,7 +282,7 @@ pub struct PayloadParametersCloned { } #[derive(Clone)] -pub struct MockBuilder { +pub struct MockBuilder { el: ExecutionLayer, beacon_client: BeaconNodeHttpClient, spec: Arc, @@ -291,6 +290,7 @@ pub struct MockBuilder { builder_sk: SecretKey, operations: Arc>>, invalidate_signatures: Arc>, + genesis_time: Option, /// Only returns bids for registered validators if set to true. `true` by default. validate_pubkey: bool, /// Do not apply any operations if set to `false`. @@ -303,17 +303,14 @@ pub struct MockBuilder { max_bid: bool, /// A cache that stores the proposers index for a given epoch proposers_cache: Arc>>>, - pubkey_cache: Arc>>, - slot_clock: S, log: Logger, } -impl MockBuilder { +impl MockBuilder { pub fn new_for_testing( mock_el_url: SensitiveUrl, beacon_url: SensitiveUrl, spec: Arc, - slot_clock: S, executor: TaskExecutor, ) -> (Self, (SocketAddr, impl Future)) { let file = NamedTempFile::new().unwrap(); @@ -339,7 +336,6 @@ impl MockBuilder { false, spec, None, - slot_clock, executor.log().clone(), ); let host: Ipv4Addr = Ipv4Addr::LOCALHOST; @@ -357,7 +353,6 @@ impl MockBuilder { max_bid: bool, spec: Arc, sk: Option<&[u8]>, - slot_clock: S, log: Logger, ) -> Self { let builder_sk = if let Some(sk_bytes) = sk { @@ -386,10 +381,9 @@ impl MockBuilder { invalidate_signatures: Arc::new(RwLock::new(false)), payload_id_cache: Arc::new(RwLock::new(HashMap::new())), proposers_cache: Arc::new(RwLock::new(HashMap::new())), - pubkey_cache: Arc::new(RwLock::new(HashMap::new())), apply_operations, max_bid, - slot_clock, + genesis_time: None, log, } } @@ -430,13 +424,6 @@ impl MockBuilder { "Registering validators"; "count" => registrations.len(), ); - - let mut preparation_data = Vec::with_capacity(registrations.len()); - let current_epoch = self - .slot_clock - .now() - .ok_or("Failed to get current epoch".to_string())? - .epoch(E::slots_per_epoch()); for registration in registrations { if !registration.verify_signature(&self.spec) { error!( @@ -445,77 +432,12 @@ impl MockBuilder { "error" => "invalid signature", "validator" => %registration.message.pubkey ); - continue; + return Err("invalid signature".to_string()); } - let pubkey = registration.message.pubkey.clone(); - - // First try to get the validator index from cache - let validator_index = { - let pubkey_cache = self.pubkey_cache.write(); - pubkey_cache.get(&pubkey).copied() - }; - - // Get or fetch the validator index - let validator_index = if let Some(validator_index) = validator_index { - validator_index - } else { - // Do the async fetch without holding any locks - let validator_index = self - .beacon_client - .get_beacon_states_validator_id( - StateId::Head, - &ValidatorId::PublicKey(pubkey.clone()), - ) - .await - .map_err(|e| format!("Failed to get validator index: {:?}", e))? - .ok_or("Beacon node returned 404".to_string())? - .data - .index; - - // Update cache after the fetch - // Note: Doing this to avoid locking between await points - { - let mut pubkey_cache = self.pubkey_cache.write(); - pubkey_cache.insert(pubkey.clone(), validator_index); - } - - validator_index - }; - let prep_data = ( - ProposerPreparationData { - validator_index, - fee_recipient: registration.message.fee_recipient, - }, - Some(registration.message.gas_limit), - ); - info!( - self.log, - "Proposer prep data for {} {:?}", pubkey, prep_data, - ); - preparation_data.push(( - ProposerPreparationData { - validator_index, - fee_recipient: registration.message.fee_recipient, - }, - Some(registration.message.gas_limit), - )); self.val_registration_cache .write() .insert(registration.message.pubkey, registration); } - info!( - self.log, - "Updating proposer preparation for {} validators", - preparation_data.len(), - ); - self.el - .update_proposer_preparation( - current_epoch, - preparation_data - .iter() - .map(|(data, gas_limit)| (data, gas_limit)), - ) - .await; Ok(()) } @@ -893,7 +815,16 @@ impl MockBuilder { }; let slots_since_genesis = slot.as_u64() - self.spec.genesis_slot.as_u64(); - let genesis_time = self.slot_clock.genesis_duration().as_secs(); + let genesis_time = if let Some(genesis_time) = self.genesis_time { + genesis_time + } else { + self.beacon_client + .get_beacon_genesis() + .await + .map_err(|_| "couldn't get beacon genesis".to_string())? + .data + .genesis_time + }; let timestamp = (slots_since_genesis * self.spec.seconds_per_slot) + genesis_time; let head_state: BeaconState = self @@ -995,10 +926,10 @@ impl MockBuilder { /// the requests. /// /// We should eventually move this to axum when we move everything else. -pub fn serve( +pub fn serve( listen_addr: Ipv4Addr, listen_port: u16, - builder: MockBuilder, + builder: MockBuilder, ) -> Result<(SocketAddr, impl Future), crate::test_utils::Error> { let inner_ctx = builder.clone(); let ctx_filter = warp::any().map(move || inner_ctx.clone()); @@ -1007,57 +938,57 @@ pub fn serve( .and(warp::path("v1")) .and(warp::path("builder")); - let validators = + let validators = prefix + .and(warp::path("validators")) + .and(warp::body::json()) + .and(warp::path::end()) + .and(ctx_filter.clone()) + .and_then( + |registrations: Vec, + builder: MockBuilder| async move { + builder + .register_validators(registrations) + .await + .map_err(|e| warp::reject::custom(Custom(e)))?; + Ok::<_, Rejection>(warp::reply()) + }, + ) + .boxed(); + + let blinded_block = prefix - .and(warp::path("validators")) + .and(warp::path("blinded_blocks")) .and(warp::body::json()) + .and(warp::header::header::(CONSENSUS_VERSION_HEADER)) .and(warp::path::end()) .and(ctx_filter.clone()) .and_then( - |registrations: Vec, - builder: MockBuilder| async move { - builder - .register_validators(registrations) + |block: SignedBlindedBeaconBlock, + fork_name: ForkName, + builder: MockBuilder| async move { + let payload = builder + .submit_blinded_block(block) .await .map_err(|e| warp::reject::custom(Custom(e)))?; - Ok::<_, Rejection>(warp::reply()) - }, - ) - .boxed(); - - let blinded_block = prefix - .and(warp::path("blinded_blocks")) - .and(warp::body::json()) - .and(warp::header::header::(CONSENSUS_VERSION_HEADER)) - .and(warp::path::end()) - .and(ctx_filter.clone()) - .and_then( - |block: SignedBlindedBeaconBlock, - fork_name: ForkName, - builder: MockBuilder| async move { - let payload = builder - .submit_blinded_block(block) - .await - .map_err(|e| warp::reject::custom(Custom(e)))?; - let resp: ForkVersionedResponse<_> = ForkVersionedResponse { - version: Some(fork_name), - metadata: Default::default(), - data: payload, - }; + let resp: ForkVersionedResponse<_> = ForkVersionedResponse { + version: Some(fork_name), + metadata: Default::default(), + data: payload, + }; - let json_payload = serde_json::to_string(&resp) - .map_err(|_| reject("coudn't serialize response"))?; - Ok::<_, warp::reject::Rejection>( - warp::http::Response::builder() - .status(200) - .body( - serde_json::to_string(&json_payload) - .map_err(|_| reject("invalid JSON"))?, - ) - .unwrap(), - ) - }, - ); + let json_payload = serde_json::to_string(&resp) + .map_err(|_| reject("coudn't serialize response"))?; + Ok::<_, warp::reject::Rejection>( + warp::http::Response::builder() + .status(200) + .body( + serde_json::to_string(&json_payload) + .map_err(|_| reject("invalid JSON"))?, + ) + .unwrap(), + ) + }, + ); let status = prefix .and(warp::path("status")) @@ -1080,7 +1011,7 @@ pub fn serve( |slot: Slot, parent_hash: ExecutionBlockHash, pubkey: PublicKeyBytes, - builder: MockBuilder| async move { + builder: MockBuilder| async move { let fork_name = builder.fork_name_at_slot(slot); let signed_bid = builder .get_header(slot, parent_hash, pubkey) From 0d9c939faef5947668e49a4d3fc36e1c04560c56 Mon Sep 17 00:00:00 2001 From: Pawan Dhananjay Date: Mon, 20 Jan 2025 18:12:56 -0800 Subject: [PATCH 12/12] Remove unnecessary derive --- beacon_node/execution_layer/src/engine_api.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/beacon_node/execution_layer/src/engine_api.rs b/beacon_node/execution_layer/src/engine_api.rs index a08a4a1a83d..b9d878b1f86 100644 --- a/beacon_node/execution_layer/src/engine_api.rs +++ b/beacon_node/execution_layer/src/engine_api.rs @@ -366,7 +366,6 @@ impl From> } } -#[derive(Debug)] pub enum GetPayloadResponseType { Full(GetPayloadResponse), Blinded(GetPayloadResponse),