Skip to content

Commit

Permalink
feat: Use alloy types for mevboost client (#15)
Browse files Browse the repository at this point in the history
## πŸ“ Summary

This PR replaces the custom types in the MevBoost client with the
counterparts in the alloy library.

---

## βœ… I have completed the following steps:

* [x] Run `make lint`
* [x] Run `make test`
* [x] Added tests (if applicable)
  • Loading branch information
ferranbt authored Jul 15, 2024
1 parent 786de8c commit a9e53e3
Show file tree
Hide file tree
Showing 10 changed files with 573 additions and 587 deletions.
312 changes: 214 additions & 98 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ revm-primitives = { version = "3.1.0", features = [
], default-features = false }
revm-inspectors = { git = "https://github.com/paradigmxyz/evm-inspectors", rev = "dc614ee" }

ethereum_ssz_derive = "0.5"
ethereum_ssz = "0.5"

alloy-primitives = "0.7.2"
alloy-rlp = "0.3.4"
alloy-chains = { version = "0.1.15", feature = ["serde", "rlp", "arbitrary"] }
Expand All @@ -57,4 +60,11 @@ alloy-transport = { git = "https://github.com/alloy-rs/alloy", version = "0.1" }
alloy-node-bindings = { git = "https://github.com/alloy-rs/alloy", version = "0.1" }
alloy-consensus = { git = "https://github.com/alloy-rs/alloy", version = "0.1", features = ["kzg"] }
alloy-serde = { git = "https://github.com/alloy-rs/alloy", version = "0.1" }
alloy-rpc-types-beacon = { git = "https://github.com/alloy-rs/alloy", version = "0.1", features = [
"ssz",
] }
alloy-rpc-types-engine = { git = "https://github.com/alloy-rs/alloy", version = "0.1", features = [
"ssz",
] }
alloy-rpc-types-eth = { git = "https://github.com/alloy-rs/alloy", version = "0.1" }
alloy-signer-local = { git = "https://github.com/alloy-rs/alloy", version = "0.1" }
6 changes: 6 additions & 0 deletions crates/rbuilder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ alloy-json-rpc.workspace = true
alloy-transport-http.workspace = true
alloy-network.workspace = true
alloy-transport.workspace = true
alloy-rpc-types-beacon.workspace = true
alloy-rpc-types-engine.workspace = true
alloy-rpc-types-eth.workspace = true
alloy-node-bindings.workspace = true
alloy-consensus.workspace = true
alloy-serde.workspace = true
alloy-signer-local.workspace = true

ethereum_ssz_derive.workspace = true
ethereum_ssz.workspace = true

test_utils = { path = "src/test_utils" }

reqwest = { version = "0.11.20", features = ["blocking"] }
Expand Down
85 changes: 79 additions & 6 deletions crates/rbuilder/benches/benchmarks/mev_boost.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use alloy_primitives::{hex, BlockHash, U256};
use alloy_rlp::Decodable;
use criterion::{criterion_group, Criterion};

use ethereum_consensus::ssz::prelude::serialize;
use primitive_types::H384;
use rbuilder::mev_boost::{
marshal_deneb_submit_block_request, DenebSubmitBlockRequest, TestDataGenerator,
rpc::TestDataGenerator, sign_block_for_relay, BLSBlockSigner, DenebSubmitBlockRequest,
};
use reth::primitives::{BlobTransactionSidecar, SealedBlock, SEPOLIA};
use reth_primitives::SealedHeader;
use std::{fs, path::PathBuf, sync::Arc};

fn mev_boost_serialize_submit_block(data: DenebSubmitBlockRequest) {
let marshalled_data = marshal_deneb_submit_block_request(&data).unwrap();
serialize(&marshalled_data).unwrap();
data.as_ssz_bytes();
}

fn bench_mevboost_serialization(c: &mut Criterion) {
Expand Down Expand Up @@ -37,4 +40,74 @@ fn bench_mevboost_serialization(c: &mut Criterion) {
group.finish();
}

criterion_group!(serialization, bench_mevboost_serialization);
fn bench_mevboost_sign(c: &mut Criterion) {
let mut generator = TestDataGenerator::default();

let blob_rlp = fs::read_to_string(
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("benches/blob_data/blob1.txt"),
)
.unwrap();

let blob_rlp = hex::decode(blob_rlp).unwrap();
let blob = BlobTransactionSidecar::decode(&mut blob_rlp.as_slice()).unwrap();

let sealed_block = SealedBlock::default();
let signer = BLSBlockSigner::test_signer();
let mut blobs = vec![];
for _ in 0..3 {
blobs.push(Arc::new(blob.clone()));
}

let chain_spec = SEPOLIA.clone();
let payload = generator.create_payload_attribute_data();

let mut group = c.benchmark_group("MEV-Boost Sign block for relay");

// This benchmark is here to have a baseline for Deneb (with blobs)
group.bench_function("Capella", |b| {
b.iter(|| {
let _ = sign_block_for_relay(
&signer,
&sealed_block,
&blobs,
&chain_spec,
&payload,
H384::default(),
U256::default(),
)
.unwrap();
})
});

// Create a sealed block that is after the Cancun hard fork in Sepolia
// this is, a timestamp higher than 1706655072
let mut sealed_block_deneb = SealedBlock::default();
let mut header = sealed_block_deneb.header().clone();
header.timestamp = 2706655072;
header.blob_gas_used = Some(64);
header.excess_blob_gas = Some(64);
sealed_block_deneb.header = SealedHeader::new(header.clone(), BlockHash::default());

group.bench_function("Deneb", |b| {
b.iter(|| {
let _ = sign_block_for_relay(
&signer,
&sealed_block_deneb,
&blobs,
&chain_spec,
&payload,
H384::default(),
U256::default(),
)
.unwrap();
})
});

group.finish();
}

criterion_group!(
serialization,
bench_mevboost_serialization,
bench_mevboost_sign
);
1 change: 1 addition & 0 deletions crates/rbuilder/benches/blob_data/blob1.txt

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions crates/rbuilder/src/backtest/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,7 @@ mod test {
max_priority_fee_per_gas: Some(22),
max_fee_per_blob_gas: None,
other: Default::default(),
authorization_list: None,
}
}
}
3 changes: 1 addition & 2 deletions crates/rbuilder/src/bin/relay-sender.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! This app is intended to be used for testing against a fake-relay I made (https://github.com/ZanCorDX/fake-relay) without needing all the builder or relay running
use rbuilder::mev_boost::{RelayClient, SubmitBlockRequest, TestDataGenerator};
use rbuilder::mev_boost::{rpc::TestDataGenerator, RelayClient, SubmitBlockRequest};
use std::str::FromStr;
use url::Url;

Expand Down
134 changes: 44 additions & 90 deletions crates/rbuilder/src/mev_boost/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,21 @@ pub mod sign_payload;

use super::utils::u256decimal_serde_helper;

use alloy_primitives::{Address, BlockHash, Bloom, Bytes, B256, U256};
use ethereum_consensus::ssz::prelude::serialize;
use alloy_primitives::{Address, BlockHash, Bytes, U256};
use alloy_rpc_types_beacon::relay::{BidTrace, SignedBidSubmissionV2, SignedBidSubmissionV3};
use flate2::{write::GzEncoder, Compression};
use primitive_types::H384;
use reqwest::{
header::{HeaderMap, HeaderValue, AUTHORIZATION, CONTENT_ENCODING, CONTENT_TYPE},
Body, Response, StatusCode,
};
use reth::primitives::BlobTransactionSidecar;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, DisplayFromStr};
use std::{io::Write, str::FromStr, sync::Arc};
use ssz::Encode;
use std::{io::Write, str::FromStr};
use thiserror::Error;
use url::Url;

pub use rpc::*;
pub use sign_payload::*;

const JSON_CONTENT_TYPE: &str = "application/json";
Expand Down Expand Up @@ -259,6 +258,32 @@ pub struct ValidatorSlotData {
pub entry: ValidatorRegistration,
}

#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Too many txs")]
TooManyTxs,
#[error("Tx to big")]
TxTooBig,
#[error("Extra data too big")]
ExtraDataTooBig,
#[error("Too many withdrawals")]
TooManyWithdrawals,
#[error("Invalid signature")]
InvalidSignature,
#[error("Wrong KzgCommitment size")]
WrongKzgCommitmentSize,
#[error("Too many KzgCommitment")]
TooManyKzgCommitments,
#[error("Too many blobs")]
TooManyBlobs,
#[error("Blob to big")]
BlobTooBig,
#[error("Wrong proof size")]
WrongProofSize,
#[error("Too many proofs")]
TooManyProofs,
}

#[derive(Debug, thiserror::Error)]
pub enum SubmitBlockErr {
#[error("Relay error: {0}")]
Expand All @@ -275,7 +300,7 @@ pub enum SubmitBlockErr {
SimError(String),
#[error("RPC conversion Error")]
/// RPC validates the submissions (eg: limit of txs) much more that our model.
RPCConversionError(rpc::Error),
RPCConversionError(Error),
#[error("RPC serialization failed {0}")]
RPCSerializationError(String),
#[error("Invalid header")]
Expand Down Expand Up @@ -438,16 +463,15 @@ impl RelayClient {
url
};

let data =
marshal_submit_block_request(data).map_err(SubmitBlockErr::RPCConversionError)?;

let mut builder = self.client.post(url.clone());
let mut headers = HeaderMap::new();
// SSZ vs JSON
let (mut body_data, content_type) = if ssz {
(
serialize(&data)
.map_err(|e| SubmitBlockErr::RPCSerializationError(e.to_string()))?,
match data {
SubmitBlockRequest::Capella(data) => data.0.as_ssz_bytes(),
SubmitBlockRequest::Deneb(data) => data.0.as_ssz_bytes(),
},
SSZ_CONTENT_TYPE,
)
} else {
Expand Down Expand Up @@ -568,87 +592,17 @@ impl RelayClient {
}
}

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BidTrace {
#[serde_as(as = "DisplayFromStr")]
pub slot: u64,
pub parent_hash: BlockHash,
pub block_hash: BlockHash,
pub builder_pubkey: H384,
pub proposer_pubkey: H384,
pub proposer_fee_recipient: Address,
#[serde_as(as = "DisplayFromStr")]
pub gas_limit: u64,
#[serde_as(as = "DisplayFromStr")]
pub gas_used: u64,
#[serde(with = "u256decimal_serde_helper")]
pub value: U256,
}

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CapellaExecutionPayload {
pub parent_hash: BlockHash,
pub fee_recipient: Address,
pub state_root: B256,
pub receipts_root: B256,
pub logs_bloom: Bloom,
pub prev_randao: B256,
#[serde_as(as = "DisplayFromStr")]
pub block_number: u64,
#[serde_as(as = "DisplayFromStr")]
pub gas_limit: u64,
#[serde_as(as = "DisplayFromStr")]
pub gas_used: u64,
#[serde_as(as = "DisplayFromStr")]
pub timestamp: u64,
pub extra_data: Bytes,
#[serde(with = "u256decimal_serde_helper")]
pub base_fee_per_gas: U256,
pub block_hash: BlockHash,
pub transactions: Vec<Bytes>,
pub withdrawals: Vec<Withdrawal>,
}

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DenebExecutionPayload {
#[serde(flatten)]
pub capella_payload: CapellaExecutionPayload,
#[serde_as(as = "DisplayFromStr")]
pub blob_gas_used: u64,
#[serde_as(as = "DisplayFromStr")]
pub excess_blob_gas: u64,
}
pub struct DenebSubmitBlockRequest(SignedBidSubmissionV3);

#[serde_as]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Withdrawal {
#[serde_as(as = "DisplayFromStr")]
index: u64,
#[serde_as(as = "DisplayFromStr")]
validator_index: u64,
address: Address,
#[serde_as(as = "DisplayFromStr")]
amount: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CapellaSubmitBlockRequest {
pub message: BidTrace,
pub execution_payload: CapellaExecutionPayload,
pub signature: Bytes,
impl DenebSubmitBlockRequest {
pub fn as_ssz_bytes(&self) -> Vec<u8> {
self.0.as_ssz_bytes()
}
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DenebSubmitBlockRequest {
pub message: BidTrace,
pub execution_payload: DenebExecutionPayload,
/// Sidecars for the txs included in execution_payload
pub txs_blobs_sidecars: Vec<Arc<BlobTransactionSidecar>>,
pub signature: Bytes,
}
pub struct CapellaSubmitBlockRequest(SignedBidSubmissionV2);

#[derive(Debug, Clone, Serialize)]
#[serde(untagged)]
Expand All @@ -660,15 +614,15 @@ pub enum SubmitBlockRequest {
impl SubmitBlockRequest {
pub fn bid_trace(&self) -> BidTrace {
match self {
SubmitBlockRequest::Capella(req) => req.message.clone(),
SubmitBlockRequest::Deneb(req) => req.message.clone(),
SubmitBlockRequest::Capella(req) => req.0.message.clone(),
SubmitBlockRequest::Deneb(req) => req.0.message.clone(),
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use super::{rpc::TestDataGenerator, *};
use crate::mev_boost::fake_mev_boost_relay::FakeMevBoostRelay;

use std::str::FromStr;
Expand Down
Loading

0 comments on commit a9e53e3

Please sign in to comment.