diff --git a/Cargo.lock b/Cargo.lock index 36bedafa..50eb9923 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -752,7 +752,7 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "beacon-api-client" version = "0.1.0" -source = "git+https://github.com/ralexstokes/ethereum-consensus?rev=b9baee3f3b9fc76bd9b5bc22b78edc05446af815#b9baee3f3b9fc76bd9b5bc22b78edc05446af815" +source = "git+https://github.com/ralexstokes/ethereum-consensus?rev=50ebe81afdcd751e265e05647b2109834ce1424e#50ebe81afdcd751e265e05647b2109834ce1424e" dependencies = [ "clap", "ethereum-consensus", @@ -2425,7 +2425,7 @@ dependencies = [ [[package]] name = "ethereum-consensus" version = "0.1.1" -source = "git+https://github.com/ralexstokes/ethereum-consensus?rev=b9baee3f3b9fc76bd9b5bc22b78edc05446af815#b9baee3f3b9fc76bd9b5bc22b78edc05446af815" +source = "git+https://github.com/ralexstokes/ethereum-consensus?rev=50ebe81afdcd751e265e05647b2109834ce1424e#50ebe81afdcd751e265e05647b2109834ce1424e" dependencies = [ "async-stream", "blst", diff --git a/Cargo.toml b/Cargo.toml index 7f686513..cc970988 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,8 +14,8 @@ default-members = ["bin/mev"] version = "0.3.0" [workspace.dependencies] -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "b9baee3f3b9fc76bd9b5bc22b78edc05446af815" } -beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "b9baee3f3b9fc76bd9b5bc22b78edc05446af815" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "50ebe81afdcd751e265e05647b2109834ce1424e" } +beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "50ebe81afdcd751e265e05647b2109834ce1424e" } reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", rev = "0a3884ba81579a775a0305be3a6621cd6782176a" } reth-primitives = { git = "https://github.com/paradigmxyz/reth", rev = "0a3884ba81579a775a0305be3a6621cd6782176a" } diff --git a/mev-boost-rs/src/relay_mux.rs b/mev-boost-rs/src/relay_mux.rs index 29710169..51f68907 100644 --- a/mev-boost-rs/src/relay_mux.rs +++ b/mev-boost-rs/src/relay_mux.rs @@ -7,7 +7,7 @@ use futures::{stream, StreamExt}; use mev_rs::{ relay::Relay, types::{ - AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, SignedBuilderBid, + AuctionContents, AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, }, BlindedBlockProvider, BoostError, Error, @@ -39,7 +39,7 @@ fn validate_bid( public_key: &BlsPublicKey, context: &Context, ) -> Result<(), Error> { - let bid_public_key = &bid.message.public_key; + let bid_public_key = bid.message.public_key(); if bid_public_key != public_key { return Err(BoostError::BidPublicKeyMismatch { bid: bid_public_key.clone(), @@ -200,7 +200,7 @@ impl BlindedBlockProvider for RelayMux { // TODO: change `value` so it does the copy internally let mut best_bid_indices = - select_best_bids(bids.iter().map(|(_, bid)| bid.message.value).enumerate()); + select_best_bids(bids.iter().map(|(_, bid)| bid.message.value()).enumerate()); // if multiple distinct bids with same bid value, break tie by randomly picking one let mut rng = rand::thread_rng(); @@ -210,12 +210,12 @@ impl BlindedBlockProvider for RelayMux { best_bid_indices.split_first().expect("there is at least one bid"); let (best_relay, best_bid) = &bids[*best_bid_index]; - let best_block_hash = best_bid.message.header.block_hash(); + let best_block_hash = best_bid.message.header().block_hash(); let mut best_relays = vec![best_relay.clone()]; for bid_index in rest { let (relay, bid) = &bids[*bid_index]; - if bid.message.header.block_hash() == best_block_hash { + if bid.message.header().block_hash() == best_block_hash { best_relays.push(relay.clone()); } } @@ -241,7 +241,7 @@ impl BlindedBlockProvider for RelayMux { async fn open_bid( &self, signed_block: &mut SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let (auction_request, relays) = { let mut state = self.state.lock(); let key = bid_key_from(signed_block, &state.latest_pubkey); @@ -269,11 +269,11 @@ impl BlindedBlockProvider for RelayMux { let expected_block_hash = payload_header.block_hash(); for (relay, response) in responses.into_iter() { match response { - Ok(payload) => { - let block_hash = payload.block_hash(); + Ok(auction_contents) => { + let block_hash = auction_contents.execution_payload().block_hash(); if block_hash == expected_block_hash { info!(%auction_request, %block_hash, %relay, "acquired payload"); - return Ok(payload) + return Ok(auction_contents) } else { warn!(?block_hash, ?expected_block_hash, %relay, "incorrect block hash delivered by relay"); } diff --git a/mev-boost-rs/tests/identity_builder.rs b/mev-boost-rs/tests/identity_builder.rs index 8e7018b1..68b45216 100644 --- a/mev-boost-rs/tests/identity_builder.rs +++ b/mev-boost-rs/tests/identity_builder.rs @@ -6,13 +6,14 @@ use ethereum_consensus::{ crypto::SecretKey, primitives::{BlsPublicKey, Slot, U256}, state_transition::Context, + Fork, }; use mev_rs::{ blinded_block_provider::BlindedBlockProvider, signing::sign_builder_message, types::{ - AuctionRequest, BuilderBid, ExecutionPayload, ExecutionPayloadHeader, - SignedBlindedBeaconBlock, SignedBuilderBid, + builder_bid, AuctionContents, AuctionRequest, BuilderBid, ExecutionPayload, + ExecutionPayloadHeader, SignedBlindedBeaconBlock, SignedBuilderBid, }, Error, }; @@ -61,7 +62,7 @@ impl BlindedBlockProvider for IdentityBuilder { let state = self.registrations.lock().unwrap(); let preferences = state.get(public_key).unwrap(); let value = U256::from(1337); - let (payload, header) = if *slot < capella_fork_slot { + let (payload, mut builder_bid) = if *slot < capella_fork_slot { let mut payload = bellatrix::ExecutionPayload { parent_hash: parent_hash.clone(), fee_recipient: preferences.fee_recipient.clone(), @@ -71,7 +72,12 @@ impl BlindedBlockProvider for IdentityBuilder { let header = ExecutionPayloadHeader::Bellatrix( bellatrix::ExecutionPayloadHeader::try_from(&mut payload).unwrap(), ); - (ExecutionPayload::Bellatrix(payload), header) + let builder_bid = BuilderBid::Bellatrix(builder_bid::bellatrix::BuilderBid { + header, + value, + public_key: self.public_key.clone(), + }); + (ExecutionPayload::Bellatrix(payload), builder_bid) } else { let mut payload = capella::ExecutionPayload { parent_hash: parent_hash.clone(), @@ -82,10 +88,14 @@ impl BlindedBlockProvider for IdentityBuilder { let header = ExecutionPayloadHeader::Capella( capella::ExecutionPayloadHeader::try_from(&mut payload).unwrap(), ); - (ExecutionPayload::Capella(payload), header) + let builder_bid = BuilderBid::Capella(builder_bid::capella::BuilderBid { + header, + value, + public_key: self.public_key.clone(), + }); + (ExecutionPayload::Capella(payload), builder_bid) }; - let mut builder_bid = BuilderBid { header, value, public_key: self.public_key.clone() }; let signature = sign_builder_message(&mut builder_bid, &self.signing_key, &self.context).unwrap(); let signed_builder_bid = SignedBuilderBid { message: builder_bid, signature }; @@ -97,9 +107,16 @@ impl BlindedBlockProvider for IdentityBuilder { async fn open_bid( &self, signed_block: &mut SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let slot = signed_block.message().slot(); let state = self.bids.lock().unwrap(); - Ok(state.get(&slot).cloned().unwrap()) + let execution_payload = state.get(&slot).cloned().unwrap(); + let auction_contents = match signed_block.message().version() { + Fork::Bellatrix => AuctionContents::Bellatrix(execution_payload), + Fork::Capella => AuctionContents::Capella(execution_payload), + Fork::Deneb => unimplemented!(), + _ => unreachable!("fork not reachable from this type"), + }; + Ok(auction_contents) } } diff --git a/mev-boost-rs/tests/integration.rs b/mev-boost-rs/tests/integration.rs index 9c242b6d..1fcde8b3 100644 --- a/mev-boost-rs/tests/integration.rs +++ b/mev-boost-rs/tests/integration.rs @@ -151,12 +151,12 @@ async fn propose_block( public_key: proposer.validator.public_key.clone(), }; let signed_bid = beacon_node.fetch_best_bid(&request).await.unwrap(); - let bid_parent_hash = signed_bid.message.header.parent_hash(); + let bid_parent_hash = signed_bid.message.header().parent_hash(); assert_eq!(bid_parent_hash, &parent_hash); let signed_block = match fork { Fork::Bellatrix => { - let header = signed_bid.message.header.bellatrix().unwrap().clone(); + let header = signed_bid.message.header().bellatrix().unwrap().clone(); let beacon_block_body = bellatrix::BlindedBeaconBlockBody { execution_payload_header: header, ..Default::default() @@ -182,7 +182,7 @@ async fn propose_block( SignedBlindedBeaconBlock::Bellatrix(signed_block) } Fork::Capella => { - let header = signed_bid.message.header.capella().unwrap().clone(); + let header = signed_bid.message.header().capella().unwrap().clone(); let beacon_block_body = capella::BlindedBeaconBlockBody { execution_payload_header: header, ..Default::default() @@ -212,7 +212,8 @@ async fn propose_block( beacon_node.check_status().await.unwrap(); - let payload = beacon_node.open_bid(&signed_block).await.unwrap(); + let auction_contents = beacon_node.open_bid(&signed_block).await.unwrap(); + let payload = auction_contents.execution_payload(); let payload_parent_hash = payload.parent_hash(); assert_eq!(payload_parent_hash, &parent_hash); diff --git a/mev-relay-rs/src/relay.rs b/mev-relay-rs/src/relay.rs index bced36c9..d306bf53 100644 --- a/mev-relay-rs/src/relay.rs +++ b/mev-relay-rs/src/relay.rs @@ -17,9 +17,9 @@ use ethereum_consensus::{ use mev_rs::{ signing::sign_builder_message, types::{ - AuctionRequest, BidTrace, BuilderBid, ExecutionPayload, ExecutionPayloadHeader, - ProposerSchedule, SignedBidSubmission, SignedBlindedBeaconBlock, SignedBuilderBid, - SignedValidatorRegistration, + builder_bid, AuctionContents, AuctionRequest, BidTrace, BuilderBid, ExecutionPayload, + ExecutionPayloadHeader, ProposerSchedule, SignedBidSubmission, SignedBlindedBeaconBlock, + SignedBuilderBid, SignedValidatorRegistration, }, BlindedBlockProvider, BlindedBlockRelayer, Error, ProposerScheduler, RelayError, ValidatorRegistry, @@ -416,7 +416,20 @@ impl Relay { } } let header = to_header(&mut execution_payload)?; - let mut bid = BuilderBid { header, value, public_key: self.public_key.clone() }; + let mut bid = match header.version() { + Fork::Bellatrix => BuilderBid::Bellatrix(builder_bid::bellatrix::BuilderBid { + header, + value, + public_key: self.public_key.clone(), + }), + Fork::Capella => BuilderBid::Capella(builder_bid::capella::BuilderBid { + header, + value, + public_key: self.public_key.clone(), + }), + Fork::Deneb => unimplemented!(), + _ => unreachable!("this fork is not reachable from this type"), + }; let signature = sign_builder_message(&mut bid, &self.secret_key, &self.context)?; let signed_builder_bid = SignedBuilderBid { message: bid, signature }; @@ -481,7 +494,7 @@ impl BlindedBlockProvider for Relay { async fn open_bid( &self, signed_block: &mut SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let auction_request = { let block = signed_block.message(); let slot = block.slot(); @@ -506,7 +519,7 @@ impl BlindedBlockProvider for Relay { let block = signed_block.message(); let body = block.body(); let execution_payload_header = body.execution_payload_header(); - let local_header = &auction_context.signed_builder_bid.message.header; + let local_header = auction_context.signed_builder_bid.message.header(); if let Err(err) = validate_header_equality(local_header, execution_payload_header) { warn!(%err, %auction_request, "invalid incoming signed blinded beacon block"); return Err(RelayError::InvalidSignedBlindedBeaconBlock.into()) @@ -533,7 +546,13 @@ impl BlindedBlockProvider for Relay { let local_payload = &auction_context.execution_payload; let block_hash = local_payload.block_hash(); info!(%auction_request, %block_root, %block_hash, "returning local payload"); - Ok(local_payload.clone()) + let auction_contents = match local_payload.version() { + Fork::Bellatrix => AuctionContents::Bellatrix(local_payload.clone()), + Fork::Capella => AuctionContents::Capella(local_payload.clone()), + Fork::Deneb => unimplemented!(), + _ => unreachable!("fork not reachable from type"), + }; + Ok(auction_contents) } } Err(err) => { diff --git a/mev-rs/src/blinded_block_provider/api/client.rs b/mev-rs/src/blinded_block_provider/api/client.rs index 7452aee0..7c1b997d 100644 --- a/mev-rs/src/blinded_block_provider/api/client.rs +++ b/mev-rs/src/blinded_block_provider/api/client.rs @@ -1,14 +1,14 @@ use crate::{ types::{ - AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, SignedBuilderBid, + AuctionContents, AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, }, Error, }; -use axum::http::StatusCode; +use axum::http::{Method, StatusCode}; use beacon_api_client::{ api_error_or_ok, mainnet::Client as BeaconApiClient, ApiResult, Error as ApiError, - VersionedValue, + VersionedValue, ETH_CONSENSUS_VERSION_HEADER, }; /// A `Client` for a service implementing the Builder APIs. @@ -63,11 +63,26 @@ impl Client { pub async fn open_bid( &self, signed_block: &SignedBlindedBeaconBlock, - ) -> Result { - let response = self.api.http_post("/eth/v1/builder/blinded_blocks", signed_block).await?; + ) -> Result { + let endpoint = self + .api + .endpoint + .join("/eth/v1/builder/blinded_blocks") + .map_err(beacon_api_client::Error::Url)?; + let response = self + .api + .http + .request(Method::POST, endpoint) + .header(ETH_CONSENSUS_VERSION_HEADER, signed_block.version().to_string()) + .json(signed_block) + .send() + .await + .map_err(beacon_api_client::Error::Http)?; - let result: ApiResult> = - response.json().await.map_err(beacon_api_client::Error::Http)?; + let result = response + .json::>>() + .await + .map_err(beacon_api_client::Error::Http)?; match result { ApiResult::Ok(result) => Ok(result.data), ApiResult::Err(err) => Err(ApiError::from(err).into()), diff --git a/mev-rs/src/blinded_block_provider/api/server.rs b/mev-rs/src/blinded_block_provider/api/server.rs index 765ee251..377b6a6e 100644 --- a/mev-rs/src/blinded_block_provider/api/server.rs +++ b/mev-rs/src/blinded_block_provider/api/server.rs @@ -2,7 +2,7 @@ use crate::{ blinded_block_provider::BlindedBlockProvider, error::Error, types::{ - AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, SignedBuilderBid, + AuctionContents, AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, }, }; @@ -48,13 +48,14 @@ pub(crate) async fn handle_fetch_bid( pub(crate) async fn handle_open_bid( State(builder): State, Json(mut block): Json, -) -> Result>, Error> { - let payload = builder.open_bid(&mut block).await?; +) -> Result>, Error> { + let auction_contents = builder.open_bid(&mut block).await?; + let payload = auction_contents.execution_payload(); let block_hash = payload.block_hash(); let slot = block.message().slot(); trace!(%slot, %block_hash, "returning payload"); let version = payload.version(); - let response = VersionedValue { version, data: payload, meta: Default::default() }; + let response = VersionedValue { version, data: auction_contents, meta: Default::default() }; Ok(Json(response)) } diff --git a/mev-rs/src/blinded_block_provider/mod.rs b/mev-rs/src/blinded_block_provider/mod.rs index d0a2bb4d..64c97c49 100644 --- a/mev-rs/src/blinded_block_provider/mod.rs +++ b/mev-rs/src/blinded_block_provider/mod.rs @@ -7,7 +7,7 @@ pub use {api::client::Client, api::server::Server}; use crate::{ error::Error, types::{ - AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, SignedBuilderBid, + AuctionContents, AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, }, }; @@ -28,5 +28,5 @@ pub trait BlindedBlockProvider { async fn open_bid( &self, signed_block: &mut SignedBlindedBeaconBlock, - ) -> Result; + ) -> Result; } diff --git a/mev-rs/src/serde.rs b/mev-rs/src/serde.rs index ab335830..985626e1 100644 --- a/mev-rs/src/serde.rs +++ b/mev-rs/src/serde.rs @@ -4,16 +4,17 @@ pub(crate) use ethereum_consensus::serde::as_str; mod tests { use ethereum_consensus::{primitives::U256, types::mainnet::ExecutionPayloadHeader}; - use crate::types::{AuctionRequest, BuilderBid, SignedBuilderBid}; + use crate::types::{builder_bid::deneb, AuctionRequest, BuilderBid, SignedBuilderBid}; #[test] fn test_fmt() { let signed_bid = SignedBuilderBid { - message: BuilderBid { + message: BuilderBid::Deneb(deneb::BuilderBid { header: ExecutionPayloadHeader::Deneb(Default::default()), + blob_kzg_commitments: Default::default(), value: U256::from(234), public_key: Default::default(), - }, + }), signature: Default::default(), }; diff --git a/mev-rs/src/types/auction_contents.rs b/mev-rs/src/types/auction_contents.rs new file mode 100644 index 00000000..acccca85 --- /dev/null +++ b/mev-rs/src/types/auction_contents.rs @@ -0,0 +1,91 @@ +use crate::types::ExecutionPayload; +use ethereum_consensus::Fork; + +pub mod bellatrix { + use super::ExecutionPayload; + + pub type AuctionContents = ExecutionPayload; +} + +pub mod capella { + pub use super::bellatrix::*; +} + +pub mod deneb { + use super::ExecutionPayload; + use ethereum_consensus::{ + deneb::{ + mainnet::{Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK}, + polynomial_commitments::{KzgCommitment, KzgProof}, + }, + ssz::prelude::*, + }; + + #[derive(Debug)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct BlobsBundle { + commitments: List, + proofs: List, + blobs: List, + } + + #[derive(Debug)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct AuctionContents { + pub execution_payload: ExecutionPayload, + pub blobs_bundle: BlobsBundle, + } +} + +#[derive(Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[serde(untagged)] +pub enum AuctionContents { + Bellatrix(bellatrix::AuctionContents), + Capella(capella::AuctionContents), + Deneb(deneb::AuctionContents), +} + +impl<'de> serde::Deserialize<'de> for AuctionContents { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = serde_json::Value::deserialize(deserializer)?; + if let Ok(inner) = <_ as serde::Deserialize>::deserialize(&value) { + return Ok(Self::Deneb(inner)) + } + if let Ok(inner) = <_ as serde::Deserialize>::deserialize(&value) { + return Ok(Self::Capella(inner)) + } + if let Ok(inner) = <_ as serde::Deserialize>::deserialize(&value) { + return Ok(Self::Bellatrix(inner)) + } + Err(serde::de::Error::custom("no variant could be deserialized from input")) + } +} + +impl AuctionContents { + pub fn version(&self) -> Fork { + match self { + Self::Bellatrix(..) => Fork::Bellatrix, + Self::Capella(..) => Fork::Capella, + Self::Deneb(..) => Fork::Deneb, + } + } + + pub fn execution_payload(&self) -> &ExecutionPayload { + match self { + Self::Bellatrix(inner) => inner, + Self::Capella(inner) => inner, + Self::Deneb(inner) => &inner.execution_payload, + } + } + + pub fn blobs_bundle(&self) -> Option<&deneb::BlobsBundle> { + match self { + Self::Deneb(inner) => Some(&inner.blobs_bundle), + _ => None, + } + } +} diff --git a/mev-rs/src/types/builder_bid.rs b/mev-rs/src/types/builder_bid.rs index cee6faf6..10ab267d 100644 --- a/mev-rs/src/types/builder_bid.rs +++ b/mev-rs/src/types/builder_bid.rs @@ -3,24 +3,125 @@ use crate::{ types::ExecutionPayloadHeader, }; use ethereum_consensus::{ - primitives::{BlsPublicKey, BlsSignature, U256}, + deneb::{mainnet::MAX_BLOB_COMMITMENTS_PER_BLOCK, polynomial_commitments::KzgCommitment}, + primitives::{BlsPublicKey, BlsSignature}, ssz::prelude::*, state_transition::Context, Error, Fork, }; use std::fmt; -#[derive(Debug, Clone, SimpleSerialize)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BuilderBid { - pub header: ExecutionPayloadHeader, - #[serde(with = "crate::serde::as_str")] - pub value: U256, - #[serde(rename = "pubkey")] - pub public_key: BlsPublicKey, +pub mod bellatrix { + use super::ExecutionPayloadHeader; + use ethereum_consensus::{ + primitives::{BlsPublicKey, U256}, + ssz::prelude::*, + }; + #[derive(Debug, Clone, SimpleSerialize, PartialEq, Eq)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct BuilderBid { + pub header: ExecutionPayloadHeader, + #[serde(with = "crate::serde::as_str")] + pub value: U256, + #[serde(rename = "pubkey")] + pub public_key: BlsPublicKey, + } +} + +pub mod capella { + pub use super::bellatrix::*; +} + +pub mod deneb { + use super::{KzgCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK}; + use crate::types::ExecutionPayloadHeader; + use ethereum_consensus::{ + primitives::{BlsPublicKey, U256}, + ssz::prelude::*, + }; + #[derive(Debug, Clone, SimpleSerialize, PartialEq, Eq)] + #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] + pub struct BuilderBid { + pub header: ExecutionPayloadHeader, + pub blob_kzg_commitments: List, + #[serde(with = "crate::serde::as_str")] + pub value: U256, + #[serde(rename = "pubkey")] + pub public_key: BlsPublicKey, + } +} + +#[derive(Debug, Clone, SimpleSerialize, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize))] +#[serde(untagged)] +#[ssz(transparent)] +pub enum BuilderBid { + Bellatrix(bellatrix::BuilderBid), + Capella(capella::BuilderBid), + Deneb(deneb::BuilderBid), +} + +impl<'de> serde::Deserialize<'de> for BuilderBid { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let value = serde_json::Value::deserialize(deserializer)?; + if let Ok(inner) = <_ as serde::Deserialize>::deserialize(&value) { + return Ok(Self::Deneb(inner)) + } + if let Ok(inner) = <_ as serde::Deserialize>::deserialize(&value) { + return Ok(Self::Capella(inner)) + } + if let Ok(inner) = <_ as serde::Deserialize>::deserialize(&value) { + return Ok(Self::Bellatrix(inner)) + } + Err(serde::de::Error::custom("no variant could be deserialized from input")) + } } impl BuilderBid { + pub fn version(&self) -> Fork { + match self { + Self::Bellatrix(..) => Fork::Bellatrix, + Self::Capella(..) => Fork::Capella, + Self::Deneb(..) => Fork::Deneb, + } + } + + pub fn header(&self) -> &ExecutionPayloadHeader { + match self { + Self::Bellatrix(inner) => &inner.header, + Self::Capella(inner) => &inner.header, + Self::Deneb(inner) => &inner.header, + } + } + + pub fn blob_kzg_commitments( + &self, + ) -> Option<&List> { + match self { + Self::Deneb(inner) => Some(&inner.blob_kzg_commitments), + _ => None, + } + } + + pub fn value(&self) -> U256 { + match self { + Self::Bellatrix(inner) => inner.value, + Self::Capella(inner) => inner.value, + Self::Deneb(inner) => inner.value, + } + } + + pub fn public_key(&self) -> &BlsPublicKey { + match self { + Self::Bellatrix(inner) => &inner.public_key, + Self::Capella(inner) => &inner.public_key, + Self::Deneb(inner) => &inner.public_key, + } + } + pub fn sign( mut self, secret_key: &SecretKey, @@ -39,14 +140,14 @@ pub struct SignedBuilderBid { impl SignedBuilderBid { pub fn version(&self) -> Fork { - self.message.header.version() + self.message.version() } } impl fmt::Display for SignedBuilderBid { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let block_hash = self.message.header.block_hash(); - let value = &self.message.value; + let block_hash = self.message.header().block_hash(); + let value = self.message.value(); write!(f, "block hash {block_hash} and value {value:?}") } } @@ -54,7 +155,7 @@ impl fmt::Display for SignedBuilderBid { impl SignedBuilderBid { pub fn verify_signature(&mut self, context: &Context) -> Result<(), Error> { let signing_root = compute_builder_signing_root(&mut self.message, context)?; - let public_key = &self.message.public_key; + let public_key = self.message.public_key(); verify_signature(public_key, signing_root.as_ref(), &self.signature) } } @@ -98,14 +199,15 @@ mod tests { let mut rng = thread_rng(); let key = SecretKey::random(&mut rng).unwrap(); let public_key = key.public_key(); - let mut builder_bid = BuilderBid { - header: ExecutionPayloadHeader::Deneb(Default::default()), + let mut builder_bid = capella::BuilderBid { + header: ExecutionPayloadHeader::Capella(Default::default()), value: U256::from(234234), public_key, }; let context = Context::for_holesky(); let signature = sign_builder_message(&mut builder_bid, &key, &context).unwrap(); - let mut signed_builder_bid = SignedBuilderBid { message: builder_bid, signature }; + let mut signed_builder_bid = + SignedBuilderBid { message: BuilderBid::Capella(builder_bid), signature }; signed_builder_bid.verify_signature(&context).expect("is valid signature"); } diff --git a/mev-rs/src/types/mod.rs b/mev-rs/src/types/mod.rs index c4eb9081..52f6114d 100644 --- a/mev-rs/src/types/mod.rs +++ b/mev-rs/src/types/mod.rs @@ -1,11 +1,13 @@ +mod auction_contents; mod auction_request; mod block_submission; -mod builder_bid; +pub mod builder_bid; mod proposer_schedule; +pub use auction_contents::*; pub use auction_request::*; pub use block_submission::*; -pub use builder_bid::*; +pub use builder_bid::{BuilderBid, SignedBuilderBid}; pub use ethereum_consensus::{ builder::SignedValidatorRegistration, types::mainnet::{ExecutionPayload, ExecutionPayloadHeader, SignedBlindedBeaconBlock},