From adb2415e26d66f0b7cae7bf90c5bf4f127f6362f Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 9 Nov 2023 12:33:53 +0100 Subject: [PATCH 1/4] add consensus version header to `getPayload` request --- Cargo.lock | 4 ++-- Cargo.toml | 4 ++-- .../src/blinded_block_provider/api/client.rs | 19 ++++++++++++++++--- 3 files changed, 20 insertions(+), 7 deletions(-) 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-rs/src/blinded_block_provider/api/client.rs b/mev-rs/src/blinded_block_provider/api/client.rs index 7452aee0..7e192f20 100644 --- a/mev-rs/src/blinded_block_provider/api/client.rs +++ b/mev-rs/src/blinded_block_provider/api/client.rs @@ -5,10 +5,10 @@ use crate::{ }, 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. @@ -64,7 +64,20 @@ impl Client { &self, signed_block: &SignedBlindedBeaconBlock, ) -> Result { - let response = self.api.http_post("/eth/v1/builder/blinded_blocks", signed_block).await?; + 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)?; From c699492ad120b669a04e54a80861c4fb1ac45f62 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 9 Nov 2023 14:19:48 +0100 Subject: [PATCH 2/4] update `BuilderBid` type for deneb --- mev-boost-rs/tests/identity_builder.rs | 7 ++++- mev-relay-rs/src/relay.rs | 7 ++++- mev-rs/src/serde.rs | 1 + mev-rs/src/types/builder_bid.rs | 37 +++++++++++++++++++++++++- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/mev-boost-rs/tests/identity_builder.rs b/mev-boost-rs/tests/identity_builder.rs index 8e7018b1..5b822108 100644 --- a/mev-boost-rs/tests/identity_builder.rs +++ b/mev-boost-rs/tests/identity_builder.rs @@ -85,7 +85,12 @@ impl BlindedBlockProvider for IdentityBuilder { (ExecutionPayload::Capella(payload), header) }; - let mut builder_bid = BuilderBid { header, value, public_key: self.public_key.clone() }; + let mut builder_bid = BuilderBid { + header, + blob_kzg_commitments: None, + 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 }; diff --git a/mev-relay-rs/src/relay.rs b/mev-relay-rs/src/relay.rs index bced36c9..0ba964af 100644 --- a/mev-relay-rs/src/relay.rs +++ b/mev-relay-rs/src/relay.rs @@ -416,7 +416,12 @@ impl Relay { } } let header = to_header(&mut execution_payload)?; - let mut bid = BuilderBid { header, value, public_key: self.public_key.clone() }; + let mut bid = BuilderBid { + header, + blob_kzg_commitments: None, + value, + public_key: self.public_key.clone(), + }; let signature = sign_builder_message(&mut bid, &self.secret_key, &self.context)?; let signed_builder_bid = SignedBuilderBid { message: bid, signature }; diff --git a/mev-rs/src/serde.rs b/mev-rs/src/serde.rs index ab335830..d68ba917 100644 --- a/mev-rs/src/serde.rs +++ b/mev-rs/src/serde.rs @@ -11,6 +11,7 @@ mod tests { let signed_bid = SignedBuilderBid { message: BuilderBid { header: ExecutionPayloadHeader::Deneb(Default::default()), + blob_kzg_commitments: None, value: U256::from(234), public_key: Default::default(), }, diff --git a/mev-rs/src/types/builder_bid.rs b/mev-rs/src/types/builder_bid.rs index cee6faf6..5ede68ef 100644 --- a/mev-rs/src/types/builder_bid.rs +++ b/mev-rs/src/types/builder_bid.rs @@ -3,6 +3,7 @@ use crate::{ types::ExecutionPayloadHeader, }; use ethereum_consensus::{ + deneb::{mainnet::MAX_BLOB_COMMITMENTS_PER_BLOCK, polynomial_commitments::KzgCommitment}, primitives::{BlsPublicKey, BlsSignature, U256}, ssz::prelude::*, state_transition::Context, @@ -10,10 +11,12 @@ use ethereum_consensus::{ }; use std::fmt; -#[derive(Debug, Clone, SimpleSerialize)] +#[derive(Debug, Clone, SimpleSerialize, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct BuilderBid { pub header: ExecutionPayloadHeader, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub blob_kzg_commitments: Option>, #[serde(with = "crate::serde::as_str")] pub value: U256, #[serde(rename = "pubkey")] @@ -100,6 +103,7 @@ mod tests { let public_key = key.public_key(); let mut builder_bid = BuilderBid { header: ExecutionPayloadHeader::Deneb(Default::default()), + blob_kzg_commitments: Some(Default::default()), value: U256::from(234234), public_key, }; @@ -116,4 +120,35 @@ mod tests { let context = Context::for_sepolia(); signed_builder_bid.verify_signature(&context).expect("is valid signature"); } + + #[test] + fn test_polymorphic_builder_bid() { + let mut rng = thread_rng(); + let key = SecretKey::random(&mut rng).unwrap(); + let public_key = key.public_key(); + + let pre_deneb_bid = BuilderBid { + header: ExecutionPayloadHeader::Capella(Default::default()), + blob_kzg_commitments: None, + value: U256::from(234), + public_key: public_key.clone(), + }; + let pre_deneb_str = serde_json::to_string_pretty(&pre_deneb_bid).unwrap(); + let pre_deneb_recovered: BuilderBid = serde_json::from_str(&pre_deneb_str).unwrap(); + assert_eq!(pre_deneb_bid, pre_deneb_recovered); + + let mut commitments = List::::default(); + commitments.push(Default::default()); + commitments.push(Default::default()); + commitments.push(Default::default()); + let deneb_bid = BuilderBid { + header: ExecutionPayloadHeader::Deneb(Default::default()), + blob_kzg_commitments: Some(commitments), + value: U256::from(567), + public_key, + }; + let deneb_str = serde_json::to_string_pretty(&deneb_bid).unwrap(); + let deneb_recovered: BuilderBid = serde_json::from_str(&deneb_str).unwrap(); + assert_eq!(deneb_bid, deneb_recovered); + } } From fa5618a28ee4f160533ff848223fd53a10268201 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 9 Nov 2023 15:58:30 +0100 Subject: [PATCH 3/4] support polymorphic return type in `open_bid` post-Deneb --- mev-boost-rs/src/relay_mux.rs | 10 +++---- mev-boost-rs/tests/identity_builder.rs | 7 +++-- mev-boost-rs/tests/integration.rs | 3 +- mev-relay-rs/src/relay.rs | 13 +++++---- .../src/blinded_block_provider/api/client.rs | 29 +++++++++++++++---- .../src/blinded_block_provider/api/server.rs | 18 ++++++++---- mev-rs/src/blinded_block_provider/mod.rs | 4 +-- mev-rs/src/types/auction_contents.rs | 24 +++++++++++++++ mev-rs/src/types/mod.rs | 2 ++ 9 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 mev-rs/src/types/auction_contents.rs diff --git a/mev-boost-rs/src/relay_mux.rs b/mev-boost-rs/src/relay_mux.rs index 29710169..5a43fc2d 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, @@ -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 5b822108..2134b837 100644 --- a/mev-boost-rs/tests/identity_builder.rs +++ b/mev-boost-rs/tests/identity_builder.rs @@ -11,7 +11,7 @@ use mev_rs::{ blinded_block_provider::BlindedBlockProvider, signing::sign_builder_message, types::{ - AuctionRequest, BuilderBid, ExecutionPayload, ExecutionPayloadHeader, + AuctionContents, AuctionRequest, BuilderBid, ExecutionPayload, ExecutionPayloadHeader, SignedBlindedBeaconBlock, SignedBuilderBid, }, Error, @@ -102,9 +102,10 @@ 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(); + Ok(AuctionContents { execution_payload, blobs_bundle: None }) } } diff --git a/mev-boost-rs/tests/integration.rs b/mev-boost-rs/tests/integration.rs index 9c242b6d..0df97a04 100644 --- a/mev-boost-rs/tests/integration.rs +++ b/mev-boost-rs/tests/integration.rs @@ -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 0ba964af..377e716b 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, + AuctionContents, AuctionRequest, BidTrace, BuilderBid, ExecutionPayload, + ExecutionPayloadHeader, ProposerSchedule, SignedBidSubmission, SignedBlindedBeaconBlock, + SignedBuilderBid, SignedValidatorRegistration, }, BlindedBlockProvider, BlindedBlockRelayer, Error, ProposerScheduler, RelayError, ValidatorRegistry, @@ -486,7 +486,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(); @@ -538,7 +538,10 @@ 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()) + Ok(AuctionContents { + execution_payload: local_payload.clone(), + blobs_bundle: None, + }) } } 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 7e192f20..92b23202 100644 --- a/mev-rs/src/blinded_block_provider/api/client.rs +++ b/mev-rs/src/blinded_block_provider/api/client.rs @@ -1,7 +1,7 @@ use crate::{ types::{ - AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, SignedBuilderBid, - SignedValidatorRegistration, + AuctionContents, AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, + SignedBuilderBid, SignedValidatorRegistration, }, Error, }; @@ -10,6 +10,7 @@ use beacon_api_client::{ api_error_or_ok, mainnet::Client as BeaconApiClient, ApiResult, Error as ApiError, VersionedValue, ETH_CONSENSUS_VERSION_HEADER, }; +use ethereum_consensus::Fork; /// A `Client` for a service implementing the Builder APIs. /// Note that `Client` does not implement the `BlindedBlockProvider` trait so that @@ -63,7 +64,7 @@ impl Client { pub async fn open_bid( &self, signed_block: &SignedBlindedBeaconBlock, - ) -> Result { + ) -> Result { let endpoint = self .api .endpoint @@ -79,11 +80,27 @@ impl Client { .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::Ok(result) => parse_auction_contents(result.version, result.data), ApiResult::Err(err) => Err(ApiError::from(err).into()), } } } + +fn parse_auction_contents( + version: Fork, + data: serde_json::Value, +) -> Result { + match version { + Fork::Deneb => Ok(serde_json::from_value(data).map_err(beacon_api_client::Error::from)?), + _ => { + let execution_payload: ExecutionPayload = + serde_json::from_value(data).map_err(beacon_api_client::Error::from)?; + Ok(AuctionContents { execution_payload, blobs_bundle: None }) + } + } +} diff --git a/mev-rs/src/blinded_block_provider/api/server.rs b/mev-rs/src/blinded_block_provider/api/server.rs index 765ee251..894e1c4e 100644 --- a/mev-rs/src/blinded_block_provider/api/server.rs +++ b/mev-rs/src/blinded_block_provider/api/server.rs @@ -2,8 +2,7 @@ use crate::{ blinded_block_provider::BlindedBlockProvider, error::Error, types::{ - AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, SignedBuilderBid, - SignedValidatorRegistration, + AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, }, }; use axum::{ @@ -13,7 +12,8 @@ use axum::{ routing::{get, post, IntoMakeService}, Router, }; -use beacon_api_client::VersionedValue; +use beacon_api_client::{Error as ApiClientError, VersionedValue}; +use ethereum_consensus::Fork; use hyper::server::conn::AddrIncoming; use std::net::{Ipv4Addr, SocketAddr}; use tokio::task::JoinHandle; @@ -48,13 +48,19 @@ 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 data = match version { + Fork::Deneb => serde_json::to_value(auction_contents).map_err(ApiClientError::from)?, + _ => serde_json::to_value(auction_contents.execution_payload) + .map_err(ApiClientError::from)?, + }; + let response = VersionedValue { version, data, 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/types/auction_contents.rs b/mev-rs/src/types/auction_contents.rs new file mode 100644 index 00000000..c884c437 --- /dev/null +++ b/mev-rs/src/types/auction_contents.rs @@ -0,0 +1,24 @@ +use crate::types::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, + #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] + pub blobs_bundle: Option, +} diff --git a/mev-rs/src/types/mod.rs b/mev-rs/src/types/mod.rs index c4eb9081..0f81ec3f 100644 --- a/mev-rs/src/types/mod.rs +++ b/mev-rs/src/types/mod.rs @@ -1,8 +1,10 @@ +mod auction_contents; mod auction_request; mod block_submission; mod builder_bid; mod proposer_schedule; +pub use auction_contents::*; pub use auction_request::*; pub use block_submission::*; pub use builder_bid::*; From a1c1c80a0a5daf718605bb3e409ef421c39ad0d3 Mon Sep 17 00:00:00 2001 From: Alex Stokes Date: Thu, 9 Nov 2023 16:42:05 +0100 Subject: [PATCH 4/4] move to enum-variant-per-fork style of polymorphism --- mev-boost-rs/src/relay_mux.rs | 10 +- mev-boost-rs/tests/identity_builder.rs | 35 ++-- mev-boost-rs/tests/integration.rs | 8 +- mev-relay-rs/src/relay.rs | 33 ++-- .../src/blinded_block_provider/api/client.rs | 23 +-- .../src/blinded_block_provider/api/server.rs | 17 +- mev-rs/src/serde.rs | 8 +- mev-rs/src/types/auction_contents.rs | 103 +++++++++-- mev-rs/src/types/builder_bid.rs | 165 ++++++++++++------ mev-rs/src/types/mod.rs | 4 +- 10 files changed, 271 insertions(+), 135 deletions(-) diff --git a/mev-boost-rs/src/relay_mux.rs b/mev-boost-rs/src/relay_mux.rs index 5a43fc2d..51f68907 100644 --- a/mev-boost-rs/src/relay_mux.rs +++ b/mev-boost-rs/src/relay_mux.rs @@ -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()); } } @@ -270,7 +270,7 @@ impl BlindedBlockProvider for RelayMux { for (relay, response) in responses.into_iter() { match response { Ok(auction_contents) => { - let block_hash = auction_contents.execution_payload.block_hash(); + 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(auction_contents) diff --git a/mev-boost-rs/tests/identity_builder.rs b/mev-boost-rs/tests/identity_builder.rs index 2134b837..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::{ - AuctionContents, 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,15 +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, - blob_kzg_commitments: None, - 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 }; @@ -106,6 +111,12 @@ impl BlindedBlockProvider for IdentityBuilder { let slot = signed_block.message().slot(); let state = self.bids.lock().unwrap(); let execution_payload = state.get(&slot).cloned().unwrap(); - Ok(AuctionContents { execution_payload, blobs_bundle: None }) + 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 0df97a04..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() @@ -213,7 +213,7 @@ async fn propose_block( beacon_node.check_status().await.unwrap(); let auction_contents = beacon_node.open_bid(&signed_block).await.unwrap(); - let payload = auction_contents.execution_payload; + 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 377e716b..d306bf53 100644 --- a/mev-relay-rs/src/relay.rs +++ b/mev-relay-rs/src/relay.rs @@ -17,7 +17,7 @@ use ethereum_consensus::{ use mev_rs::{ signing::sign_builder_message, types::{ - AuctionContents, AuctionRequest, BidTrace, BuilderBid, ExecutionPayload, + builder_bid, AuctionContents, AuctionRequest, BidTrace, BuilderBid, ExecutionPayload, ExecutionPayloadHeader, ProposerSchedule, SignedBidSubmission, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, }, @@ -416,11 +416,19 @@ impl Relay { } } let header = to_header(&mut execution_payload)?; - let mut bid = BuilderBid { - header, - blob_kzg_commitments: None, - 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 }; @@ -511,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()) @@ -538,10 +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(AuctionContents { - execution_payload: local_payload.clone(), - blobs_bundle: None, - }) + 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 92b23202..7c1b997d 100644 --- a/mev-rs/src/blinded_block_provider/api/client.rs +++ b/mev-rs/src/blinded_block_provider/api/client.rs @@ -1,7 +1,7 @@ use crate::{ types::{ - AuctionContents, AuctionRequest, ExecutionPayload, SignedBlindedBeaconBlock, - SignedBuilderBid, SignedValidatorRegistration, + AuctionContents, AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, + SignedValidatorRegistration, }, Error, }; @@ -10,7 +10,6 @@ use beacon_api_client::{ api_error_or_ok, mainnet::Client as BeaconApiClient, ApiResult, Error as ApiError, VersionedValue, ETH_CONSENSUS_VERSION_HEADER, }; -use ethereum_consensus::Fork; /// A `Client` for a service implementing the Builder APIs. /// Note that `Client` does not implement the `BlindedBlockProvider` trait so that @@ -81,26 +80,12 @@ impl Client { .map_err(beacon_api_client::Error::Http)?; let result = response - .json::>>() + .json::>>() .await .map_err(beacon_api_client::Error::Http)?; match result { - ApiResult::Ok(result) => parse_auction_contents(result.version, result.data), + ApiResult::Ok(result) => Ok(result.data), ApiResult::Err(err) => Err(ApiError::from(err).into()), } } } - -fn parse_auction_contents( - version: Fork, - data: serde_json::Value, -) -> Result { - match version { - Fork::Deneb => Ok(serde_json::from_value(data).map_err(beacon_api_client::Error::from)?), - _ => { - let execution_payload: ExecutionPayload = - serde_json::from_value(data).map_err(beacon_api_client::Error::from)?; - Ok(AuctionContents { execution_payload, blobs_bundle: None }) - } - } -} diff --git a/mev-rs/src/blinded_block_provider/api/server.rs b/mev-rs/src/blinded_block_provider/api/server.rs index 894e1c4e..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,8 @@ use crate::{ blinded_block_provider::BlindedBlockProvider, error::Error, types::{ - AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, SignedValidatorRegistration, + AuctionContents, AuctionRequest, SignedBlindedBeaconBlock, SignedBuilderBid, + SignedValidatorRegistration, }, }; use axum::{ @@ -12,8 +13,7 @@ use axum::{ routing::{get, post, IntoMakeService}, Router, }; -use beacon_api_client::{Error as ApiClientError, VersionedValue}; -use ethereum_consensus::Fork; +use beacon_api_client::VersionedValue; use hyper::server::conn::AddrIncoming; use std::net::{Ipv4Addr, SocketAddr}; use tokio::task::JoinHandle; @@ -48,19 +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> { +) -> Result>, Error> { let auction_contents = builder.open_bid(&mut block).await?; - let payload = &auction_contents.execution_payload; + 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 data = match version { - Fork::Deneb => serde_json::to_value(auction_contents).map_err(ApiClientError::from)?, - _ => serde_json::to_value(auction_contents.execution_payload) - .map_err(ApiClientError::from)?, - }; - let response = VersionedValue { version, data, meta: Default::default() }; + let response = VersionedValue { version, data: auction_contents, meta: Default::default() }; Ok(Json(response)) } diff --git a/mev-rs/src/serde.rs b/mev-rs/src/serde.rs index d68ba917..985626e1 100644 --- a/mev-rs/src/serde.rs +++ b/mev-rs/src/serde.rs @@ -4,17 +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: None, + 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 index c884c437..acccca85 100644 --- a/mev-rs/src/types/auction_contents.rs +++ b/mev-rs/src/types/auction_contents.rs @@ -1,24 +1,91 @@ use crate::types::ExecutionPayload; -use ethereum_consensus::{ - deneb::{ - mainnet::{Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK}, - polynomial_commitments::{KzgCommitment, KzgProof}, - }, - ssz::prelude::*, -}; +use ethereum_consensus::Fork; -#[derive(Debug)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct BlobsBundle { - commitments: List, - proofs: List, - blobs: List, +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::Deserialize))] -pub struct AuctionContents { - pub execution_payload: ExecutionPayload, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub blobs_bundle: Option, +#[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 5ede68ef..10ab267d 100644 --- a/mev-rs/src/types/builder_bid.rs +++ b/mev-rs/src/types/builder_bid.rs @@ -4,26 +4,124 @@ use crate::{ }; use ethereum_consensus::{ deneb::{mainnet::MAX_BLOB_COMMITMENTS_PER_BLOCK, polynomial_commitments::KzgCommitment}, - primitives::{BlsPublicKey, BlsSignature, U256}, + primitives::{BlsPublicKey, BlsSignature}, ssz::prelude::*, state_transition::Context, Error, Fork, }; use std::fmt; +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::Deserialize))] -pub struct BuilderBid { - pub header: ExecutionPayloadHeader, - #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))] - pub blob_kzg_commitments: Option>, - #[serde(with = "crate::serde::as_str")] - pub value: U256, - #[serde(rename = "pubkey")] - pub public_key: BlsPublicKey, +#[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, @@ -42,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:?}") } } @@ -57,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) } } @@ -101,15 +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()), - blob_kzg_commitments: Some(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"); } @@ -120,35 +218,4 @@ mod tests { let context = Context::for_sepolia(); signed_builder_bid.verify_signature(&context).expect("is valid signature"); } - - #[test] - fn test_polymorphic_builder_bid() { - let mut rng = thread_rng(); - let key = SecretKey::random(&mut rng).unwrap(); - let public_key = key.public_key(); - - let pre_deneb_bid = BuilderBid { - header: ExecutionPayloadHeader::Capella(Default::default()), - blob_kzg_commitments: None, - value: U256::from(234), - public_key: public_key.clone(), - }; - let pre_deneb_str = serde_json::to_string_pretty(&pre_deneb_bid).unwrap(); - let pre_deneb_recovered: BuilderBid = serde_json::from_str(&pre_deneb_str).unwrap(); - assert_eq!(pre_deneb_bid, pre_deneb_recovered); - - let mut commitments = List::::default(); - commitments.push(Default::default()); - commitments.push(Default::default()); - commitments.push(Default::default()); - let deneb_bid = BuilderBid { - header: ExecutionPayloadHeader::Deneb(Default::default()), - blob_kzg_commitments: Some(commitments), - value: U256::from(567), - public_key, - }; - let deneb_str = serde_json::to_string_pretty(&deneb_bid).unwrap(); - let deneb_recovered: BuilderBid = serde_json::from_str(&deneb_str).unwrap(); - assert_eq!(deneb_bid, deneb_recovered); - } } diff --git a/mev-rs/src/types/mod.rs b/mev-rs/src/types/mod.rs index 0f81ec3f..52f6114d 100644 --- a/mev-rs/src/types/mod.rs +++ b/mev-rs/src/types/mod.rs @@ -1,13 +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},