Skip to content

Commit

Permalink
blinded payload test case passing
Browse files Browse the repository at this point in the history
  • Loading branch information
eserilev committed Sep 5, 2023
1 parent 5be3113 commit ae2b1d3
Show file tree
Hide file tree
Showing 3 changed files with 294 additions and 47 deletions.
285 changes: 282 additions & 3 deletions beacon_node/execution_layer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ impl<T: EthSpec> ExecutionLayer<T> {
block_production_version: BlockProductionVersion,
) -> Result<BlockProposalContentsType<T>, Error> {
let payload_result_type = match block_production_version {
BlockProductionVersion::V3 | BlockProductionVersion::BlindedV2 => match self
BlockProductionVersion::V3 => match self
.determine_and_fetch_payload(
parent_hash,
payload_attributes,
Expand All @@ -675,6 +675,21 @@ impl<T: EthSpec> ExecutionLayer<T> {
return Err(e);
}
},
BlockProductionVersion::BlindedV2 => {
let _timer = metrics::start_timer_vec(
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
&[metrics::GET_BLINDED_PAYLOAD],
);
self.get_blinded_payload_v2(
parent_hash,
payload_attributes,
forkchoice_update_params,
builder_params,
current_fork,
spec,
)
.await?
},
BlockProductionVersion::FullV2 => self
.get_full_payload_with_v3(
parent_hash,
Expand Down Expand Up @@ -738,7 +753,6 @@ impl<T: EthSpec> ExecutionLayer<T> {
) -> Result<BlockProposalContents<T, Payload>, Error> {
let payload_result = match Payload::block_type() {
BlockType::Blinded => {
println!("BLOCK TYPE IS BLINDED");
let _timer = metrics::start_timer_vec(
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
&[metrics::GET_BLINDED_PAYLOAD],
Expand All @@ -754,7 +768,6 @@ impl<T: EthSpec> ExecutionLayer<T> {
.await
}
BlockType::Full => {
println!("BLOCK TYPE IS FULL");
let _timer = metrics::start_timer_vec(
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
&[metrics::GET_PAYLOAD],
Expand Down Expand Up @@ -1085,6 +1098,272 @@ impl<T: EthSpec> ExecutionLayer<T> {
.map(ProvenancedPayload::Local)
}

async fn get_blinded_payload_v2(
&self,
parent_hash: ExecutionBlockHash,
payload_attributes: &PayloadAttributes,
forkchoice_update_params: ForkchoiceUpdateParameters,
builder_params: BuilderParams,
current_fork: ForkName,
spec: &ChainSpec,
) -> Result<ProvenancedPayload<BlockProposalContentsType<T>>, Error> {
if let Some(builder) = self.builder() {
let slot = builder_params.slot;
let pubkey = builder_params.pubkey;

match builder_params.chain_health {
ChainHealth::Healthy => {
info!(
self.log(),
"Requesting blinded header from connected builder";
"slot" => ?slot,
"pubkey" => ?pubkey,
"parent_hash" => ?parent_hash,
);

// Wait for the builder *and* local EL to produce a payload (or return an error).
let ((relay_result, relay_duration), (local_result, local_duration)) = tokio::join!(
timed_future(metrics::GET_BLINDED_PAYLOAD_BUILDER, async {
builder
.get_builder_header::<T, BlindedPayload<T>>(slot, parent_hash, &pubkey)
.await
}),
timed_future(metrics::GET_BLINDED_PAYLOAD_LOCAL, async {
self.get_full_payload_caching::<BlindedPayload<T>>(
parent_hash,
payload_attributes,
forkchoice_update_params,
current_fork,
)
.await
})
);

info!(
self.log(),
"Requested blinded execution payload";
"relay_fee_recipient" => match &relay_result {
Ok(Some(r)) => format!("{:?}", r.data.message.header.fee_recipient()),
Ok(None) => "empty response".to_string(),
Err(_) => "request failed".to_string(),
},
"relay_response_ms" => relay_duration.as_millis(),
"local_fee_recipient" => match &local_result {
Ok(proposal_contents) => format!("{:?}", proposal_contents.payload().fee_recipient()),
Err(_) => "request failed".to_string()
},
"local_response_ms" => local_duration.as_millis(),
"parent_hash" => ?parent_hash,
);

return match (relay_result, local_result) {
(Err(e), Ok(local)) => {
warn!(
self.log(),
"Builder error when requesting payload";
"info" => "falling back to local execution client",
"relay_error" => ?e,
"local_block_hash" => ?local.payload().block_hash(),
"parent_hash" => ?parent_hash,
);
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Blinded(local)))
}
(Ok(None), Ok(local)) => {
info!(
self.log(),
"Builder did not return a payload";
"info" => "falling back to local execution client",
"local_block_hash" => ?local.payload().block_hash(),
"parent_hash" => ?parent_hash,
);
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Blinded(local)))
}
(Ok(Some(relay)), Ok(local)) => {
let header = &relay.data.message.header;

info!(
self.log(),
"Received local and builder payloads";
"relay_block_hash" => ?header.block_hash(),
"local_block_hash" => ?local.payload().block_hash(),
"parent_hash" => ?parent_hash,
);

let relay_value = relay.data.message.value;
let local_value = *local.block_value();
if !self.inner.always_prefer_builder_payload {
if local_value >= relay_value {
info!(
self.log(),
"Local block is more profitable than relay block";
"local_block_value" => %local_value,
"relay_value" => %relay_value
);
return Ok(ProvenancedPayload::Local(BlockProposalContentsType::Blinded(local)));
} else {
info!(
self.log(),
"Relay block is more profitable than local block";
"local_block_value" => %local_value,
"relay_value" => %relay_value
);
}
}

match verify_builder_bid(
&relay,
parent_hash,
payload_attributes,
Some(local.payload().block_number()),
self.inner.builder_profit_threshold,
current_fork,
spec,
) {
Ok(()) => Ok(ProvenancedPayload::Builder(
BlockProposalContentsType::Blinded(
BlockProposalContents::Payload {
payload: relay.data.message.header,
block_value: relay.data.message.value,
_phantom: PhantomData,
}),
)),
Err(reason) if !reason.payload_invalid() => {
info!(
self.log(),
"Builder payload ignored";
"info" => "using local payload",
"reason" => %reason,
"relay_block_hash" => ?header.block_hash(),
"parent_hash" => ?parent_hash,
);
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Blinded(local)))
}
Err(reason) => {
metrics::inc_counter_vec(
&metrics::EXECUTION_LAYER_GET_PAYLOAD_BUILDER_REJECTIONS,
&[reason.as_ref().as_ref()],
);
warn!(
self.log(),
"Builder returned invalid payload";
"info" => "using local payload",
"reason" => %reason,
"relay_block_hash" => ?header.block_hash(),
"parent_hash" => ?parent_hash,
);
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Blinded(local)))
}
}
}
(Ok(Some(relay)), Err(local_error)) => {
let header = &relay.data.message.header;

info!(
self.log(),
"Received builder payload with local error";
"relay_block_hash" => ?header.block_hash(),
"local_error" => ?local_error,
"parent_hash" => ?parent_hash,
);

match verify_builder_bid(
&relay,
parent_hash,
payload_attributes,
None,
self.inner.builder_profit_threshold,
current_fork,
spec,
) {
Ok(()) => Ok(ProvenancedPayload::Builder(
BlockProposalContentsType::Blinded(
BlockProposalContents::Payload {
payload: relay.data.message.header,
block_value: relay.data.message.value,
_phantom: PhantomData,
}),
)),
// If the payload is valid then use it. The local EE failed
// to produce a payload so we have no alternative.
Err(e) if !e.payload_invalid() => Ok(ProvenancedPayload::Builder(
BlockProposalContentsType::Blinded(
BlockProposalContents::Payload {
payload: relay.data.message.header,
block_value: relay.data.message.value,
_phantom: PhantomData,
}),
)),
Err(reason) => {
metrics::inc_counter_vec(
&metrics::EXECUTION_LAYER_GET_PAYLOAD_BUILDER_REJECTIONS,
&[reason.as_ref().as_ref()],
);
crit!(
self.log(),
"Builder returned invalid payload";
"info" => "no local payload either - unable to propose block",
"reason" => %reason,
"relay_block_hash" => ?header.block_hash(),
"parent_hash" => ?parent_hash,
);
Err(Error::CannotProduceHeader)
}
}
}
(Err(relay_error), Err(local_error)) => {
crit!(
self.log(),
"Unable to produce execution payload";
"info" => "the local EL and builder both failed - unable to propose block",
"relay_error" => ?relay_error,
"local_error" => ?local_error,
"parent_hash" => ?parent_hash,
);

Err(Error::CannotProduceHeader)
}
(Ok(None), Err(local_error)) => {
crit!(
self.log(),
"Unable to produce execution payload";
"info" => "the local EL failed and the builder returned nothing - \
the block proposal will be missed",
"local_error" => ?local_error,
"parent_hash" => ?parent_hash,
);

Err(Error::CannotProduceHeader)
}
};
}
ChainHealth::Unhealthy(condition) => info!(
self.log(),
"Chain is unhealthy, using local payload";
"info" => "this helps protect the network. the --builder-fallback flags \
can adjust the expected health conditions.",
"failed_condition" => ?condition
),
// Intentional no-op, so we never attempt builder API proposals pre-merge.
ChainHealth::PreMerge => (),
ChainHealth::Optimistic => info!(
self.log(),
"Chain is optimistic; can't build payload";
"info" => "the local execution engine is syncing and the builder network \
cannot safely be used - unable to propose block"
),
}
}
println!("YOOOO");
let payload = self.get_full_payload_caching::<BlindedPayload<T>>(
parent_hash,
payload_attributes,
forkchoice_update_params,
current_fork,
)
.await?;
Ok(ProvenancedPayload::Local(BlockProposalContentsType::Blinded(payload)))
}

async fn get_blinded_payload<Payload: AbstractExecPayload<T>>(
&self,
parent_hash: ExecutionBlockHash,
Expand Down
41 changes: 2 additions & 39 deletions beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod validator;
mod validator_inclusion;
mod version;

use crate::validator::{produce_block_v2, produce_block_v3};
use crate::validator::{produce_block_v2, produce_blinded_block_v2, produce_block_v3};
use beacon_chain::{
attestation_verification::VerifiedAttestation, observed_operations::ObservationOutcome,
validator_monitor::timestamp_now, AttestationError as AttnError, BeaconChain, BeaconChainError,
Expand Down Expand Up @@ -3060,44 +3060,7 @@ pub fn serve<T: BeaconChainTypes>(
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
let randao_reveal = query.randao_reveal.decompress().map_err(|e| {
warp_utils::reject::custom_bad_request(format!(
"randao reveal is not a valid BLS signature: {:?}",
e
))
})?;

let randao_verification =
if query.skip_randao_verification == SkipRandaoVerification::Yes {
if !randao_reveal.is_infinity() {
return Err(warp_utils::reject::custom_bad_request(
"randao_reveal must be point-at-infinity if verification is skipped"
.into()
));
}
ProduceBlockVerification::NoVerification
} else {
ProduceBlockVerification::VerifyRandao
};

let (block, _) = chain
.produce_block_with_verification::<BlindedPayload<T::EthSpec>>(
randao_reveal,
slot,
query.graffiti.map(Into::into),
randao_verification,
)
.await
.map_err(warp_utils::reject::block_production_error)?;
let fork_name = block
.to_ref()
.fork_name(&chain.spec)
.map_err(inconsistent_fork_rejection)?;

// Pose as a V2 endpoint so we return the fork `version`.
fork_versioned_response(V2, fork_name, block)
.map(|response| warp::reply::json(&response).into_response())
.map(|res| add_consensus_version_header(res, fork_name))
produce_blinded_block_v2(EndpointVersion(2), chain, slot, query).await
})
},
);
Expand Down
Loading

0 comments on commit ae2b1d3

Please sign in to comment.