From 1c0f0787e5bc8e932330cfb0aac28dd407286536 Mon Sep 17 00:00:00 2001 From: Tumas Date: Tue, 23 Apr 2024 17:57:55 +0300 Subject: [PATCH] Add builder_boost_factor param to /eth/v3/validator/blocks{slot} --- Cargo.lock | 1 + http_api/src/standard.rs | 26 +++++++++++++++++++---- ssz/src/uint256.rs | 5 +++++ validator/Cargo.toml | 1 + validator/src/lib.rs | 4 +++- validator/src/messages.rs | 2 +- validator/src/misc.rs | 5 +++++ validator/src/validator.rs | 42 +++++++++++++++++++++++++++++++++----- 8 files changed, 75 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1dca1328..c88d7c4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8245,6 +8245,7 @@ dependencies = [ "itertools 0.12.1", "keymanager", "log", + "nonzero_ext", "once_cell", "operation_pools", "p2p", diff --git a/http_api/src/standard.rs b/http_api/src/standard.rs index 9c9fcda8..a3133838 100644 --- a/http_api/src/standard.rs +++ b/http_api/src/standard.rs @@ -80,7 +80,10 @@ use types::{ preset::Preset, traits::{BeaconState as _, SignedBeaconBlock as _}, }; -use validator::{ApiToValidator, ValidatorBlindedBlock, ValidatorConfig, ValidatorProposerData}; +use validator::{ + ApiToValidator, ValidatorBlindedBlock, ValidatorConfig, ValidatorProposerData, + DEFAULT_BUILDER_BOOST_FACTOR, +}; use crate::{ block_id, @@ -161,7 +164,7 @@ pub struct PoolAttestationQuery { } #[derive(Deserialize)] -// Allow custom fields in ValidatorBlockQuery. +// Allow custom fields in `ValidatorBlockQuery`. // This is required for Lodestar interoperability. // #[serde(deny_unknown_fields)] pub struct ValidatorBlockQuery { @@ -171,6 +174,18 @@ pub struct ValidatorBlockQuery { skip_randao_verification: bool, } +#[derive(Deserialize)] +// Allow custom fields in `ValidatorBlockQueryV3`. +// This is required for Lodestar interoperability. +// #[serde(deny_unknown_fields)] +pub struct ValidatorBlockQueryV3 { + randao_reveal: SignatureBytes, + graffiti: Option, + #[serde(default, with = "serde_utils::bool_as_empty_string")] + skip_randao_verification: bool, + builder_boost_factor: Option, +} + #[derive(Deserialize)] #[serde(deny_unknown_fields)] pub struct SyncCommitteeContributionQuery { @@ -1849,6 +1864,7 @@ pub async fn validator_blinded_block( randao_reveal, slot, skip_randao_verification, + DEFAULT_BUILDER_BOOST_FACTOR.get(), ) .send(&api_to_validator_tx); @@ -1908,13 +1924,14 @@ pub async fn validator_block_v3( State(controller): State>, State(api_to_validator_tx): State>>, EthPath(slot): EthPath, - EthQuery(query): EthQuery, + EthQuery(query): EthQuery, headers: HeaderMap, ) -> Result, P>, (), JsonOrSsz>, Error> { - let ValidatorBlockQuery { + let ValidatorBlockQueryV3 { randao_reveal, graffiti, skip_randao_verification, + builder_boost_factor, } = query; if skip_randao_verification && !randao_reveal.is_empty() { @@ -1930,6 +1947,7 @@ pub async fn validator_block_v3( randao_reveal, slot, skip_randao_verification, + builder_boost_factor.unwrap_or_else(|| DEFAULT_BUILDER_BOOST_FACTOR.get()), ) .send(&api_to_validator_tx); diff --git a/ssz/src/uint256.rs b/ssz/src/uint256.rs index 78cdf2d6..86913859 100644 --- a/ssz/src/uint256.rs +++ b/ssz/src/uint256.rs @@ -266,6 +266,11 @@ impl Uint256 { ])) } + #[must_use] + pub fn saturating_mul(self, rhs: Self) -> Self { + Self(self.into_raw().saturating_mul(rhs.into_raw())) + } + const fn into_raw(self) -> RawUint256 { self.0 } diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 497c7598..cd4ab32f 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -34,6 +34,7 @@ http_api_utils = { workspace = true } itertools = { workspace = true } keymanager = { workspace = true } log = { workspace = true } +nonzero_ext = { workspace = true } once_cell = { workspace = true } operation_pools = { workspace = true } p2p = { workspace = true } diff --git a/validator/src/lib.rs b/validator/src/lib.rs index 1a5d51bd..d8fbadb8 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -1,7 +1,9 @@ pub use crate::{ api::{run_validator_api, ValidatorApiConfig}, messages::{ApiToValidator, ValidatorToApi, ValidatorToLiveness}, - misc::{ProposerData as ValidatorProposerData, ValidatorBlindedBlock}, + misc::{ + ProposerData as ValidatorProposerData, ValidatorBlindedBlock, DEFAULT_BUILDER_BOOST_FACTOR, + }, validator::{Channels as ValidatorChannels, Validator}, validator_config::ValidatorConfig, }; diff --git a/validator/src/messages.rs b/validator/src/messages.rs index b6566b17..1d53b979 100644 --- a/validator/src/messages.rs +++ b/validator/src/messages.rs @@ -27,7 +27,7 @@ pub type BlindedBlockSender

= pub enum ApiToValidator { ProduceBeaconBlock(BeaconBlockSender

, H256, SignatureBytes, Slot, bool), - ProduceBlindedBeaconBlock(BlindedBlockSender

, H256, SignatureBytes, Slot, bool), + ProduceBlindedBeaconBlock(BlindedBlockSender

, H256, SignatureBytes, Slot, bool, u64), AttesterSlashing(Sender, Box>), ProposerSlashing(Sender, Box), PublishSignedBlindedBlock( diff --git a/validator/src/misc.rs b/validator/src/misc.rs index d0b54a0d..77152ffa 100644 --- a/validator/src/misc.rs +++ b/validator/src/misc.rs @@ -1,4 +1,7 @@ +use core::num::NonZeroU64; + use bls::{PublicKeyBytes, SignatureBytes}; +use nonzero_ext::nonzero; use serde::{Deserialize, Serialize}; use ssz::{BitVector, Size, SszHash, SszSize, SszWrite, WriteError, H256}; use typenum::U1; @@ -11,6 +14,8 @@ use types::{ traits::{BeaconBlock as _, PostDenebBeaconBlockBody}, }; +pub const DEFAULT_BUILDER_BOOST_FACTOR: NonZeroU64 = nonzero!(100_u64); + #[allow(clippy::struct_field_names)] pub struct Aggregator { pub aggregator_index: ValidatorIndex, diff --git a/validator/src/validator.rs b/validator/src/validator.rs index 758be202..73b498dd 100644 --- a/validator/src/validator.rs +++ b/validator/src/validator.rs @@ -1,6 +1,6 @@ //! -use core::ops::ControlFlow; +use core::ops::{ControlFlow, Div as _}; use std::{ collections::{BTreeMap, BTreeSet, HashMap, HashSet}, error::Error as StdError, @@ -98,7 +98,9 @@ use types::{ BeaconBlock as Phase0BeaconBlock, BeaconBlockBody as Phase0BeaconBlockBody, Checkpoint, ProposerSlashing, SignedAggregateAndProof, SignedVoluntaryExit, }, - primitives::{Epoch, ExecutionAddress, ExecutionBlockHash, Slot, ValidatorIndex, H256}, + primitives::{ + Epoch, ExecutionAddress, ExecutionBlockHash, Slot, Uint256, ValidatorIndex, H256, + }, }, preset::Preset, traits::{ @@ -111,7 +113,10 @@ use crate::{ messages::{ ApiToValidator, BeaconBlockSender, BlindedBlockSender, ValidatorToApi, ValidatorToLiveness, }, - misc::{Aggregator, ProposerData, SyncCommitteeMember, ValidatorBlindedBlock}, + misc::{ + Aggregator, ProposerData, SyncCommitteeMember, ValidatorBlindedBlock, + DEFAULT_BUILDER_BOOST_FACTOR, + }, own_beacon_committee_members::{BeaconCommitteeMember, OwnBeaconCommitteeMembers}, own_sync_committee_subscriptions::OwnSyncCommitteeSubscriptions, slot_head::SlotHead, @@ -413,6 +418,7 @@ impl Validator { randao_reveal, slot, skip_randao_verification, + builder_boost_factor, ) => { self.produce_blinded_beacon_block( sender, @@ -420,6 +426,7 @@ impl Validator { randao_reveal, slot, skip_randao_verification, + builder_boost_factor, ).await }, ApiToValidator::ProposerSlashing(sender, proposer_slashing) => { @@ -955,6 +962,7 @@ impl Validator { .pipe(Some) } + #[allow(clippy::too_many_arguments)] async fn build_blinded_beacon_block( &mut self, slot_head: &SlotHead

, @@ -963,6 +971,7 @@ impl Validator { graffiti: H256, execution_payload_header_handle: Option>>>>, skip_randao_verification: bool, + builder_boost_factor: u64, ) -> Result, P>>> { let Some(beacon_block) = self .build_beacon_block( @@ -982,7 +991,7 @@ impl Validator { match header_handle.await? { Ok(Some(response)) => { let blob_kzg_commitments = response.blob_kzg_commitments().cloned(); - let mev = response.mev(); + let builder_mev = response.mev(); if let Some(blinded_block) = self.blinded_block_from_beacon_block( slot_head, @@ -991,6 +1000,26 @@ impl Validator { blob_kzg_commitments, skip_randao_verification, ) { + if let Some(local_mev) = beacon_block.mev { + let builder_boost_factor = Uint256::from_u64(builder_boost_factor); + + let boosted_builder_mev = builder_mev + .div(DEFAULT_BUILDER_BOOST_FACTOR) + .saturating_mul(builder_boost_factor); + + if local_mev >= boosted_builder_mev { + info!( + "using more profitable local payload: \ + local MEV: {local_mev}, builder MEV: {builder_mev}, \ + boosted builder MEV: {boosted_builder_mev}, builder_boost_factor: {builder_boost_factor}", + ); + + return Ok(Some( + beacon_block.map(ValidatorBlindedBlock::BeaconBlock), + )); + } + } + let block = ValidatorBlindedBlock::BlindedBeaconBlock { blinded_block, execution_payload: Box::new( @@ -1005,7 +1034,7 @@ impl Validator { None, beacon_block.proofs, beacon_block.blobs, - Some(mev), + Some(builder_mev), ))); } } @@ -1271,6 +1300,7 @@ impl Validator { randao_reveal: SignatureBytes, slot: Slot, skip_randao_verification: bool, + builder_boost_factor: u64, ) -> bool { let Some(slot_head) = self.safe_slot_head(slot).await else { return sender.send(Ok(None)).is_ok(); @@ -1294,6 +1324,7 @@ impl Validator { graffiti, execution_payload_header_handle, skip_randao_verification, + builder_boost_factor, ) .await; @@ -1370,6 +1401,7 @@ impl Validator { graffiti, execution_payload_header_handle, false, + DEFAULT_BUILDER_BOOST_FACTOR.get(), ) .await?;