diff --git a/Cargo.lock b/Cargo.lock index 9dc6eb13..c348f6e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,7 +391,7 @@ dependencies = [ [[package]] name = "beacon-api-client" version = "0.1.0" -source = "git+https://github.com/ralexstokes/beacon-api-client?rev=287d4dd#287d4dd3027b24614be838833cb89345b550350e" +source = "git+https://github.com/ralexstokes/beacon-api-client?rev=56a290c#56a290ca9d2c67086917a0929cdf2fe35e5f917f" dependencies = [ "clap", "ethereum-consensus", @@ -860,7 +860,7 @@ dependencies = [ [[package]] name = "ethereum-consensus" version = "0.1.1" -source = "git+https://github.com/ralexstokes/ethereum-consensus?rev=e380108#e380108d15fcc40349927fdf3d11c71f9edb67c2" +source = "git+https://github.com/ralexstokes/ethereum-consensus?rev=56418ea#56418ea4c376470c7d75eb12f75bc882e1a92ca6" dependencies = [ "async-stream", "blst", diff --git a/Cargo.toml b/Cargo.toml index 01184587..5b4c026b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,6 @@ members = ["bin/mev", "mev-boost-rs", "mev-relay-rs", "mev-build-rs", "mev-rs"] default-members = ["bin/mev"] [workspace.dependencies] -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "e380108" } -beacon-api-client = { git = "https://github.com/ralexstokes/beacon-api-client", rev = "287d4dd" } +ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus", rev = "56418ea" } +beacon-api-client = { git = "https://github.com/ralexstokes/beacon-api-client", rev = "56a290c" } ssz_rs = "0.9.0" diff --git a/mev-boost-rs/tests/integration.rs b/mev-boost-rs/tests/integration.rs index 99312b45..575950cf 100644 --- a/mev-boost-rs/tests/integration.rs +++ b/mev-boost-rs/tests/integration.rs @@ -271,6 +271,7 @@ async fn propose_block( assert_eq!(payload.parent_hash, parent_hash); assert_eq!(payload.fee_recipient, proposer.fee_recipient); } + _ => unimplemented!(), } beacon_node.check_status().await.unwrap(); diff --git a/mev-relay-rs/src/relay.rs b/mev-relay-rs/src/relay.rs index 6c9c008d..5660a16c 100644 --- a/mev-relay-rs/src/relay.rs +++ b/mev-relay-rs/src/relay.rs @@ -207,6 +207,7 @@ impl BlindedBlockProvider for Relay { let signed_bid = capella::SignedBuilderBid { message: bid, signature }; Ok(SignedBuilderBid::Capella(signed_bid)) } + ExecutionPayloadHeader::Deneb(_header) => unimplemented!(), } } diff --git a/mev-rs/src/blinded_block_provider/api/client.rs b/mev-rs/src/blinded_block_provider/api/client.rs index bafb9589..86ef582a 100644 --- a/mev-rs/src/blinded_block_provider/api/client.rs +++ b/mev-rs/src/blinded_block_provider/api/client.rs @@ -71,6 +71,9 @@ impl Client { SignedBlindedBeaconBlock::Capella(signed_block) => { self.api.http_post("/eth/v1/builder/blinded_blocks", signed_block).await? } + SignedBlindedBeaconBlock::Deneb(signed_block) => { + self.api.http_post("/eth/v1/builder/blinded_blocks", signed_block).await? + } }; let result: ApiResult> = diff --git a/mev-rs/src/engine_api_proxy/types.rs b/mev-rs/src/engine_api_proxy/types.rs index 92a6b46d..9a5f75f3 100644 --- a/mev-rs/src/engine_api_proxy/types.rs +++ b/mev-rs/src/engine_api_proxy/types.rs @@ -2,6 +2,8 @@ use ethereum_consensus::{ bellatrix::mainnet::{ Transaction, BYTES_PER_LOGS_BLOOM, MAX_EXTRA_DATA_BYTES, MAX_TRANSACTIONS_PER_PAYLOAD, }, + deneb::mainnet::{Blob, MAX_BLOBS_PER_BLOCK}, + kzg::{KzgCommitment, KzgProof}, primitives::{Bytes32, ExecutionAddress, Hash32}, ssz::{ByteList, ByteVector}, }; @@ -145,12 +147,23 @@ pub enum ExecutionPayload { #[derive(Deserialize, Debug)] #[serde(rename_all = "camelCase")] +// TODO: maybe rename this to `GetPayloadV2Response` for consistency with the V3 response type? pub struct ExecutionPayloadWithValue { pub execution_payload: ExecutionPayload, #[serde(deserialize_with = "u256_from_be_hex")] pub block_value: U256, } +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct GetPayloadV3Response { + pub execution_payload: ExecutionPayloadV3, + #[serde(deserialize_with = "u256_from_be_hex")] + pub block_value: U256, + pub blobs_bundle: BlobsBundleV1, + pub should_override_builder: bool, +} + #[derive(Clone, Debug, Deserialize)] #[serde(rename_all = "camelCase")] #[serde(deny_unknown_fields)] @@ -202,6 +215,43 @@ pub struct ExecutionPayloadV2 { pub withdrawals: Vec, } +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ExecutionPayloadV3 { + pub parent_hash: Hash32, + pub fee_recipient: ExecutionAddress, + pub state_root: Bytes32, + pub receipts_root: Bytes32, + pub logs_bloom: ByteVector, + pub prev_randao: Bytes32, + #[serde(deserialize_with = "u64_from_hex")] + pub block_number: u64, + #[serde(deserialize_with = "u64_from_hex")] + pub gas_limit: u64, + #[serde(deserialize_with = "u64_from_hex")] + pub gas_used: u64, + #[serde(deserialize_with = "u64_from_hex")] + pub timestamp: u64, + pub extra_data: ByteList, + #[serde(deserialize_with = "u256_from_be_hex")] + pub base_fee_per_gas: U256, + pub block_hash: Hash32, + pub transactions: List, + pub withdrawals: Vec, + #[serde(deserialize_with = "u64_from_hex")] + pub data_gas_used: u64, + #[serde(deserialize_with = "u64_from_hex")] + pub excess_data_gas: u64, +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct BlobsBundleV1 { + pub commitments: List, + pub proofs: List, + pub blobs: List, +} + #[cfg(test)] mod tests { use super::*; diff --git a/mev-rs/src/types/deneb.rs b/mev-rs/src/types/deneb.rs new file mode 100644 index 00000000..aae0505d --- /dev/null +++ b/mev-rs/src/types/deneb.rs @@ -0,0 +1,61 @@ +pub use ethereum_consensus::{builder::SignedValidatorRegistration, deneb::mainnet as spec}; +use ethereum_consensus::{ + deneb::mainnet::MAX_BLOBS_PER_BLOCK, + kzg::{KzgCommitment, KzgProof}, + primitives::{BlsPublicKey, BlsSignature, Root, U256}, +}; +use ssz_rs::prelude::*; + +// NOTE: type alias here to call out the important types clearly, in lieu of just `pub use ...` +pub type ExecutionPayload = spec::ExecutionPayload; +pub type ExecutionPayloadHeader = spec::ExecutionPayloadHeader; +pub type SignedBlindedBeaconBlock = spec::SignedBlindedBeaconBlock; +pub type SignedBlindedBlobSidecar = spec::SignedBlindedBlobSidecar; +pub type Blob = spec::Blob; + +#[derive(Debug, Default, Clone, SimpleSerialize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BuilderBid { + pub header: spec::ExecutionPayloadHeader, + pub blinded_blobs_bundle: BlindedBlobsBundle, + pub value: U256, + #[serde(rename = "pubkey")] + pub public_key: BlsPublicKey, +} + +#[derive(Debug, Default, Clone, SimpleSerialize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BlindedBlobsBundle { + pub commitments: List, + pub proofs: List, + pub blob_roots: List, +} + +#[derive(Debug, Default, Clone, SimpleSerialize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedBuilderBid { + pub message: BuilderBid, + pub signature: BlsSignature, +} + +#[derive(Debug, Default, Clone, SimpleSerialize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct SignedBlindedBlockAndBlobSidecars { + pub signed_blinded_block: SignedBlindedBeaconBlock, + pub signed_blinded_blob_sidecars: List, +} + +#[derive(Debug, Default, Clone, SimpleSerialize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct BlobsBundle { + pub commitments: List, + pub proofs: List, + pub blobs: List, +} + +#[derive(Debug, Default, Clone, SimpleSerialize)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct ExecutionPayloadAndBlobsBundle { + pub execution_payload: ExecutionPayload, + pub blobs_bundle: BlobsBundle, +} diff --git a/mev-rs/src/types/mod.rs b/mev-rs/src/types/mod.rs index a0ebffd5..6aabf78a 100644 --- a/mev-rs/src/types/mod.rs +++ b/mev-rs/src/types/mod.rs @@ -1,5 +1,6 @@ pub mod bellatrix; pub mod capella; +pub mod deneb; use crate::signing::{ sign_builder_message, verify_signed_builder_message, verify_signed_consensus_message, @@ -36,6 +37,7 @@ impl std::fmt::Display for BidRequest { pub enum BuilderBid { Bellatrix(bellatrix::BuilderBid), Capella(capella::BuilderBid), + Deneb(deneb::BuilderBid), } impl From<(ExecutionPayloadHeader, U256, &BlsPublicKey)> for BuilderBid { @@ -53,6 +55,7 @@ impl From<(ExecutionPayloadHeader, U256, &BlsPublicKey)> for BuilderBid { value, public_key: public_key.clone(), }), + ExecutionPayloadHeader::Deneb(_header) => unimplemented!(), } } } @@ -80,6 +83,12 @@ impl BuilderBid { }); Ok(signed_bid) } + BuilderBid::Deneb(mut bid) => { + let signature = sign_builder_message(&mut bid, secret_key, context)?; + let signed_bid = + SignedBuilderBid::Deneb(deneb::SignedBuilderBid { message: bid, signature }); + Ok(signed_bid) + } } } } @@ -91,6 +100,7 @@ impl BuilderBid { pub enum SignedBuilderBid { Bellatrix(bellatrix::SignedBuilderBid), Capella(capella::SignedBuilderBid), + Deneb(deneb::SignedBuilderBid), } impl std::fmt::Display for SignedBuilderBid { @@ -106,6 +116,7 @@ impl SignedBuilderBid { match self { Self::Bellatrix(bid) => &bid.message.value, Self::Capella(bid) => &bid.message.value, + Self::Deneb(bid) => &bid.message.value, } } @@ -113,6 +124,7 @@ impl SignedBuilderBid { match self { Self::Bellatrix(bid) => &bid.message.public_key, Self::Capella(bid) => &bid.message.public_key, + Self::Deneb(bid) => &bid.message.public_key, } } @@ -120,6 +132,7 @@ impl SignedBuilderBid { match self { Self::Bellatrix(bid) => &bid.message.header.block_hash, Self::Capella(bid) => &bid.message.header.block_hash, + Self::Deneb(bid) => &bid.message.header.block_hash, } } @@ -127,6 +140,7 @@ impl SignedBuilderBid { match self { Self::Bellatrix(bid) => &bid.message.header.parent_hash, Self::Capella(bid) => &bid.message.header.parent_hash, + Self::Deneb(bid) => &bid.message.header.parent_hash, } } @@ -145,6 +159,12 @@ impl SignedBuilderBid { &public_key, context, ), + Self::Deneb(bid) => verify_signed_builder_message( + &mut bid.message, + &bid.signature, + &public_key, + context, + ), } } } @@ -156,6 +176,7 @@ impl SignedBuilderBid { pub enum SignedBlindedBeaconBlock { Bellatrix(bellatrix::SignedBlindedBeaconBlock), Capella(capella::SignedBlindedBeaconBlock), + Deneb(deneb::SignedBlindedBlockAndBlobSidecars), } impl SignedBlindedBeaconBlock { @@ -163,6 +184,7 @@ impl SignedBlindedBeaconBlock { match self { Self::Bellatrix(block) => block.message.slot, Self::Capella(block) => block.message.slot, + Self::Deneb(block_and_blobs) => block_and_blobs.signed_blinded_block.message.slot, } } @@ -170,6 +192,9 @@ impl SignedBlindedBeaconBlock { match self { Self::Bellatrix(block) => block.message.proposer_index, Self::Capella(block) => block.message.proposer_index, + Self::Deneb(block_and_blobs) => { + block_and_blobs.signed_blinded_block.message.proposer_index + } } } @@ -177,6 +202,14 @@ impl SignedBlindedBeaconBlock { match self { Self::Bellatrix(block) => &block.message.body.execution_payload_header.block_hash, Self::Capella(block) => &block.message.body.execution_payload_header.block_hash, + Self::Deneb(block_and_blobs) => { + &block_and_blobs + .signed_blinded_block + .message + .body + .execution_payload_header + .block_hash + } } } @@ -184,6 +217,14 @@ impl SignedBlindedBeaconBlock { match self { Self::Bellatrix(block) => &block.message.body.execution_payload_header.parent_hash, Self::Capella(block) => &block.message.body.execution_payload_header.parent_hash, + Self::Deneb(block_and_blobs) => { + &block_and_blobs + .signed_blinded_block + .message + .body + .execution_payload_header + .parent_hash + } } } @@ -216,6 +257,18 @@ impl SignedBlindedBeaconBlock { Some(genesis_validators_root), ) } + Self::Deneb(block_and_blobs) => { + let block = &mut block_and_blobs.signed_blinded_block; + let slot = block.message.slot; + verify_signed_consensus_message( + &mut block.message, + &block.signature, + public_key, + context, + Some(slot), + Some(genesis_validators_root), + ) + } } } } @@ -227,6 +280,7 @@ impl SignedBlindedBeaconBlock { pub enum ExecutionPayload { Bellatrix(bellatrix::ExecutionPayload), Capella(capella::ExecutionPayload), + Deneb(deneb::ExecutionPayloadAndBlobsBundle), } impl Default for ExecutionPayload { @@ -240,6 +294,7 @@ impl ExecutionPayload { match self { Self::Bellatrix(payload) => &payload.block_hash, Self::Capella(payload) => &payload.block_hash, + Self::Deneb(payload_and_blobs) => &payload_and_blobs.execution_payload.block_hash, } } @@ -247,6 +302,7 @@ impl ExecutionPayload { match self { Self::Bellatrix(payload) => payload.gas_limit, Self::Capella(payload) => payload.gas_limit, + Self::Deneb(payload_and_blobs) => payload_and_blobs.execution_payload.gas_limit, } } } @@ -264,6 +320,12 @@ impl TryFrom<&mut ExecutionPayload> for ExecutionPayloadHeader { let header = capella::ExecutionPayloadHeader::try_from(payload)?; Ok(Self::Capella(header)) } + ExecutionPayload::Deneb(payload_and_blobs) => { + let header = deneb::ExecutionPayloadHeader::try_from( + &mut payload_and_blobs.execution_payload, + )?; + Ok(Self::Deneb(header)) + } } } } @@ -272,6 +334,7 @@ impl TryFrom<&mut ExecutionPayload> for ExecutionPayloadHeader { pub enum ExecutionPayloadHeader { Bellatrix(bellatrix::ExecutionPayloadHeader), Capella(capella::ExecutionPayloadHeader), + Deneb(deneb::ExecutionPayloadHeader), } impl ExecutionPayloadHeader { @@ -279,6 +342,7 @@ impl ExecutionPayloadHeader { match self { Self::Bellatrix(header) => &header.block_hash, Self::Capella(header) => &header.block_hash, + Self::Deneb(header) => &header.block_hash, } } }