Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions bin/pbs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use cb_common::{
config::load_pbs_config,
pbs::DenebSpec,
utils::{initialize_pbs_tracing_log, wait_for_signal},
};
use cb_pbs::{DefaultBuilderApi, PbsService, PbsState};
Expand All @@ -21,7 +20,7 @@ async fn main() -> Result<()> {

PbsService::init_metrics(pbs_config.chain)?;
let state = PbsState::new(pbs_config);
let server = PbsService::run::<_, DenebSpec, DefaultBuilderApi>(state);
let server = PbsService::run::<_, DefaultBuilderApi>(state);

tokio::select! {
maybe_err = server => {
Expand Down
49 changes: 16 additions & 33 deletions crates/common/src/pbs/event.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{marker::PhantomData, net::SocketAddr};
use std::net::SocketAddr;

use alloy::{primitives::B256, rpc::types::beacon::relay::ValidatorRegistration};
use async_trait::async_trait;
Expand All @@ -16,22 +16,21 @@ use tracing::{error, info, trace};
use url::Url;

use super::{
EthSpec, GetHeaderParams, GetHeaderResponse, SignedBlindedBeaconBlock,
SubmitBlindedBlockResponse,
GetHeaderParams, GetHeaderResponse, SignedBlindedBeaconBlock, SubmitBlindedBlockResponse,
};
use crate::{
config::{load_optional_env_var, BUILDER_URLS_ENV},
pbs::BUILDER_EVENTS_PATH,
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BuilderEvent<T: EthSpec> {
pub enum BuilderEvent {
GetHeaderRequest(GetHeaderParams),
GetHeaderResponse(Box<Option<GetHeaderResponse<T>>>),
GetHeaderResponse(Box<Option<GetHeaderResponse>>),
GetStatusEvent,
GetStatusResponse,
SubmitBlockRequest(Box<SignedBlindedBeaconBlock<T>>),
SubmitBlockResponse(Box<SubmitBlindedBlockResponse<T>>),
SubmitBlockRequest(Box<SignedBlindedBeaconBlock>),
SubmitBlockResponse(Box<SubmitBlindedBlockResponse>),
MissedPayload {
/// Hash for the block for which no payload was received
block_hash: B256,
Expand Down Expand Up @@ -70,7 +69,7 @@ impl BuilderEventPublisher {
.transpose()
}

pub fn publish<T: EthSpec>(&self, event: BuilderEvent<T>) {
pub fn publish(&self, event: BuilderEvent) {
for endpoint in self.endpoints.clone() {
let client = self.client.clone();
let event = event.clone();
Expand All @@ -95,30 +94,21 @@ impl BuilderEventPublisher {
}
}

pub struct BuilderEventClient<T, S>
where
T: OnBuilderApiEvent<S> + Clone + Send + Sync + 'static,
S: EthSpec + Clone + Send + Sync + 'static + for<'de> Deserialize<'de>,
{
pub struct BuilderEventClient<T: OnBuilderApiEvent> {
pub port: u16,
pub processor: T,
_phantom: PhantomData<S>,
}

impl<T, S> BuilderEventClient<T, S>
where
T: OnBuilderApiEvent<S> + Clone + Send + Sync + 'static,
S: EthSpec + Clone + Send + Sync + 'static + for<'de> Deserialize<'de>,
{
impl<T: OnBuilderApiEvent + Clone + Send + Sync + 'static> BuilderEventClient<T> {
pub fn new(port: u16, processor: T) -> Self {
Self { port, processor, _phantom: PhantomData }
Self { port, processor }
}

pub async fn run(self) -> eyre::Result<()> {
info!("Starting builder events server on port {}", self.port);

let router = axum::Router::new()
.route(BUILDER_EVENTS_PATH, post(handle_builder_event::<T, S>))
.route(BUILDER_EVENTS_PATH, post(handle_builder_event::<T>))
.with_state(self.processor);
let address = SocketAddr::from(([0, 0, 0, 0], self.port));
let listener = TcpListener::bind(&address).await?;
Expand All @@ -129,24 +119,17 @@ where
}
}

async fn handle_builder_event<T, S>(
async fn handle_builder_event<T: OnBuilderApiEvent>(
State(processor): State<T>,
Json(event): Json<BuilderEvent<S>>,
) -> Response
where
T: OnBuilderApiEvent<S> + Clone + Send + Sync + 'static,
S: EthSpec + Clone + Send + Sync + 'static + for<'de> Deserialize<'de>,
{
Json(event): Json<BuilderEvent>,
) -> Response {
trace!("Handling builder event");
processor.on_builder_api_event(event).await;
StatusCode::OK.into_response()
}

#[async_trait]
/// This is what modules are expected to implement to process BuilderApi events
pub trait OnBuilderApiEvent<T>
where
T: EthSpec + Clone + Send + Sync + 'static + for<'de> Deserialize<'de>,
{
async fn on_builder_api_event(&self, event: BuilderEvent<T>);
pub trait OnBuilderApiEvent {
async fn on_builder_api_event(&self, event: BuilderEvent);
}
92 changes: 71 additions & 21 deletions crates/common/src/pbs/types/beacon_block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,59 +3,105 @@ use serde::{Deserialize, Serialize};

use super::{
blinded_block_body::BlindedBeaconBlockBody, blobs_bundle::BlobsBundle,
execution_payload::ExecutionPayload, spec::EthSpec, utils::VersionedResponse,
execution_payload::ExecutionPayload, utils::VersionedResponse, DenebSpec, ElectraSpec, EthSpec,
};

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
/// Sent to relays in submit_block
pub struct SignedBlindedBeaconBlock<T: EthSpec> {
pub message: BlindedBeaconBlock<T>,
pub struct SignedBlindedBeaconBlock {
pub message: BlindedBeaconBlock,
pub signature: BlsSignature,
}

impl<T: EthSpec> SignedBlindedBeaconBlock<T> {
impl SignedBlindedBeaconBlock {
pub fn block_hash(&self) -> B256 {
self.message.body.execution_payload_header.block_hash
match &self.message.body {
VersionedBlindedBeaconBlockBody::Deneb(body) => {
body.execution_payload_header.block_hash
}
VersionedBlindedBeaconBlockBody::Electra(body) => {
body.execution_payload_header.block_hash
}
}
}
}

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(bound = "T: EthSpec")]
pub struct BlindedBeaconBlock<T: EthSpec> {
pub struct BlindedBeaconBlock {
#[serde(with = "serde_utils::quoted_u64")]
pub slot: u64,
#[serde(with = "serde_utils::quoted_u64")]
pub proposer_index: u64,
pub parent_root: B256,
pub state_root: B256,
pub body: BlindedBeaconBlockBody<T>,
pub body: VersionedBlindedBeaconBlockBody,
}

// TODO: check deserialization
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum VersionedBlindedBeaconBlockBody {
Electra(BlindedBeaconBlockBody<ElectraSpec>),
Deneb(BlindedBeaconBlockBody<DenebSpec>),
}

impl Default for VersionedBlindedBeaconBlockBody {
fn default() -> Self {
VersionedBlindedBeaconBlockBody::Deneb(BlindedBeaconBlockBody::default())
}
}

impl VersionedBlindedBeaconBlockBody {
pub fn block_hash(&self) -> B256 {
match self {
VersionedBlindedBeaconBlockBody::Deneb(body) => {
body.execution_payload_header.block_hash
}
VersionedBlindedBeaconBlockBody::Electra(body) => {
body.execution_payload_header.block_hash
}
}
}

pub fn num_commitments(&self) -> usize {
match self {
VersionedBlindedBeaconBlockBody::Deneb(body) => body.blob_kzg_commitments.len(),
VersionedBlindedBeaconBlockBody::Electra(body) => body.blob_kzg_commitments.len(),
}
}

pub fn version(&self) -> &str {
match self {
VersionedBlindedBeaconBlockBody::Deneb(_) => "deneb",
VersionedBlindedBeaconBlockBody::Electra(_) => "electra",
}
}
}
/// Returned by relay in submit_block
#[allow(type_alias_bounds)]
pub type SubmitBlindedBlockResponse<T: EthSpec> = VersionedResponse<PayloadAndBlobs<T>>;
pub type SubmitBlindedBlockResponse =
VersionedResponse<PayloadAndBlobs<DenebSpec>, PayloadAndBlobs<ElectraSpec>>;

#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct PayloadAndBlobs<T: EthSpec> {
pub execution_payload: ExecutionPayload<T>,
pub blobs_bundle: Option<BlobsBundle<T>>,
}

impl<T: EthSpec> SubmitBlindedBlockResponse<T> {
impl SubmitBlindedBlockResponse {
pub fn block_hash(&self) -> B256 {
self.data.execution_payload.block_hash
match self {
SubmitBlindedBlockResponse::Deneb(payload) => payload.execution_payload.block_hash,
SubmitBlindedBlockResponse::Electra(payload) => payload.execution_payload.block_hash,
}
}
}

#[cfg(test)]
mod tests {
use serde_json::json;

use super::{SignedBlindedBeaconBlock, SubmitBlindedBlockResponse};
use crate::{
pbs::{DenebSpec, ElectraSpec},
utils::test_encode_decode,
};
use super::SignedBlindedBeaconBlock;
use crate::utils::test_encode_decode;

#[test]
// this is from the builder api spec, but with sync_committee_bits fixed to
Expand Down Expand Up @@ -255,7 +301,8 @@ mod tests {
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
}"#;

test_encode_decode::<SignedBlindedBeaconBlock<DenebSpec>>(&data);
let x = test_encode_decode::<SignedBlindedBeaconBlock>(&data);
println!("{:?}", x.message.body.version());
}

#[test]
Expand Down Expand Up @@ -569,7 +616,8 @@ mod tests {
"signature": "0x8c3095fd9d3a18e43ceeb7648281e16bb03044839dffea796432c4e5a1372bef22c11a98a31e0c1c5389b98cc6d45917170a0f1634bcf152d896f360dc599fabba2ec4de77898b5dff080fa1628482bdbad5b37d2e64fea3d8721095186cfe50"
}"#;

test_encode_decode::<SignedBlindedBeaconBlock<DenebSpec>>(&data);
let x = test_encode_decode::<SignedBlindedBeaconBlock>(&data);
println!("{:?}", x.message.body.version());
}

#[test]
Expand Down Expand Up @@ -627,7 +675,8 @@ mod tests {
}
}).to_string();

test_encode_decode::<SubmitBlindedBlockResponse<DenebSpec>>(&data);
let x = test_encode_decode::<SignedBlindedBeaconBlock>(&data);
println!("{:?}", x.message.body.version());
}

#[test]
Expand Down Expand Up @@ -706,6 +755,7 @@ mod tests {
"signature": "0x94cd72a70a0b424f68145115a9a52f6c8557fb40ec8b67c26cbb9b324b72756624a59e8bbe11b78acbbb8e9c606035d10c0dab9a3f4177d7e6954f8ea1863d0b0b00007fb420b4b4cf52e065bda0ad32af0d3a71bd6938180bab6dd1af754d2f"
}"#;

test_encode_decode::<SignedBlindedBeaconBlock<ElectraSpec>>(&data);
let x = test_encode_decode::<SignedBlindedBeaconBlock>(&data);
println!("{:?}", x.message.body.version());
}
}
Loading