Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement builder ssz flow #8

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ members = [
async-trait = "0.1"
axum = { version = "0.7", features = ["ws"] }
bytes = "1.6"
eth2 = { git = "https://github.com/sigp/lighthouse.git", rev = "c33307d70287fd3b7a70785f89dadcb737214903" }
eth2 = { git = "https://github.com/eserilev/lighthouse.git", rev = "098d5b5270df3cc5564d573be42965b1e2f623db" }
ethereum_serde_utils = "0.7"
ethereum_ssz = "0.7"
ethereum_ssz_derive = "0.7"
Expand Down
2 changes: 2 additions & 0 deletions builder-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ ethereum-apis-common = { path = "../common" }
reqwest.workspace = true
serde.workspace = true
serde_json.workspace = true
ethereum_ssz.workspace = true
axum.workspace = true
80 changes: 75 additions & 5 deletions builder-client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
use axum::http::HeaderMap;
use axum::http::HeaderValue;
pub use builder_api_types::*;
pub use builder_bid::SignedBuilderBid;
use ethereum_apis_common::ContentType;
pub use ethereum_apis_common::ErrorResponse;
use reqwest::header::CONTENT_TYPE;
use reqwest::Client;
use reqwest::Url;
use serde::de::DeserializeOwned;
use ssz::DecodeError;
use ssz::Encode;

#[derive(Debug)]
pub enum Error {
Reqwest(reqwest::Error),
InvalidJson(serde_json::Error, String),
InvalidSsz(DecodeError),
ServerMessage(ErrorResponse),
StatusCode(reqwest::StatusCode),
InvalidUrl(Url),
Expand All @@ -34,6 +41,34 @@ impl BuilderClient {
}
}

async fn build_response_with_headers<T>(
&self,
response: reqwest::Response,
content_type: ContentType,
fork_name: ForkName,
) -> Result<T, Error>
where
T: DeserializeOwned + ForkVersionDecode,
{
let status = response.status();
let text = response.text().await?;

if status.is_success() {
match content_type {
ContentType::Json => {
serde_json::from_str(&text).map_err(|e| Error::InvalidJson(e, text))
}
ContentType::Ssz => {
T::from_ssz_bytes_by_fork(text.as_bytes(), fork_name).map_err(Error::InvalidSsz)
}
}
} else {
Err(Error::ServerMessage(
serde_json::from_str(&text).map_err(|e| Error::InvalidJson(e, text))?,
))
}
}

async fn build_response<T>(&self, response: reqwest::Response) -> Result<T, Error>
where
T: DeserializeOwned,
Expand Down Expand Up @@ -67,22 +102,50 @@ impl BuilderClient {
pub async fn submit_blinded_block<E: EthSpec>(
&self,
block: &SignedBlindedBeaconBlock<E>,
content_type: ContentType,
fork_name: ForkName,
) -> Result<ExecutionPayload<E>, Error> {
let mut url = self.base_url.clone();
url.path_segments_mut()
.map_err(|_| Error::InvalidUrl(self.base_url.clone()))?
.extend(&["eth", "v1", "builder", "blinded_blocks"]);

let response = self.client.post(url).json(block).send().await?;

self.build_response(response).await
let mut headers = HeaderMap::new();
headers.insert(
CONTENT_TYPE,
HeaderValue::from_str(&content_type.to_string()).unwrap(),
);

let response = match content_type {
ContentType::Json => {
self.client
.post(url)
.headers(headers)
.json(block)
.send()
.await?
}
ContentType::Ssz => {
self.client
.post(url)
.headers(headers)
.body(block.as_ssz_bytes())
.send()
.await?
}
};

self.build_response_with_headers(response, content_type, fork_name)
.await
}

pub async fn get_header<E: EthSpec>(
&self,
slot: Slot,
parent_hash: ExecutionBlockHash,
pubkey: &PublicKeyBytes,
content_type: ContentType,
fork_name: ForkName,
) -> Result<SignedBuilderBid<E>, Error> {
let mut url = self.base_url.clone();
url.path_segments_mut()
Expand All @@ -97,9 +160,16 @@ impl BuilderClient {
&pubkey.to_string(),
]);

let response = self.client.get(url).send().await?;
let mut headers = HeaderMap::new();
headers.insert(
CONTENT_TYPE,
HeaderValue::from_str(&content_type.to_string()).unwrap(),
);

self.build_response(response).await
let response = self.client.get(url).headers(headers).send().await?;

self.build_response_with_headers(response, content_type, fork_name)
.await
}

pub async fn get_status(&self) -> Result<(), Error> {
Expand Down
55 changes: 26 additions & 29 deletions builder-server/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ use axum::{
Json, Router,
};
use builder_api_types::{
eth_spec::EthSpec, fork_versioned_response::EmptyMetadata, ExecutionBlockHash,
ForkVersionedResponse, FullPayloadContents, PublicKeyBytes, SignedBlindedBeaconBlock,
eth_spec::EthSpec, ExecutionBlockHash, PublicKeyBytes, SignedBlindedBeaconBlock,
SignedValidatorRegistrationData, Slot,
};
use ethereum_apis_common::build_response;
use ethereum_apis_common::{
build_response, build_response_with_headers, ContentType, JsonOrSszWithFork,
};
use http::{header::CONTENT_TYPE, HeaderMap};

use crate::builder::Builder;

Expand Down Expand Up @@ -52,39 +54,33 @@ where
}

async fn submit_blinded_block<I, A, E>(
headers: HeaderMap,
State(api_impl): State<I>,
Json(block): Json<SignedBlindedBeaconBlock<E>>,
JsonOrSszWithFork(block): JsonOrSszWithFork<SignedBlindedBeaconBlock<E>>,
) -> Result<Response<Body>, StatusCode>
where
E: EthSpec,
I: AsRef<A> + Send + Sync,
A: Builder<E>,
{
let res = api_impl
.as_ref()
.submit_blinded_block(block)
.await
.map(|payload| {
let fork_name = match &payload {
FullPayloadContents::Payload(payload) => payload.fork_name(),
FullPayloadContents::PayloadAndBlobs(payload_and_blobs) => {
payload_and_blobs.execution_payload.fork_name()
}
};
ForkVersionedResponse {
version: Some(fork_name),
metadata: EmptyMetadata {},
data: payload,
}
});
Comment on lines -68 to -79
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pawanjay176 I've removed this and am instead figuring out the fork name based on block.slot(). I think that should be ok?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah looks good

build_response(res).await
let content_type_header = headers.get(CONTENT_TYPE);
let content_type = content_type_header.and_then(|value| value.to_str().ok());
let content_type = match content_type {
Some("application/octet-stream") => ContentType::Ssz,
_ => ContentType::Json,
};
let slot = block.slot();
let res = api_impl.as_ref().submit_blinded_block(block).await;

build_response_with_headers(res, content_type, api_impl.as_ref().fork_name_at_slot(slot)).await
}

async fn get_status() -> StatusCode {
StatusCode::OK
}

async fn get_header<I, A, E>(
headers: HeaderMap,
State(api_impl): State<I>,
Path((slot, parent_hash, pubkey)): Path<(Slot, ExecutionBlockHash, PublicKeyBytes)>,
) -> Result<Response<Body>, StatusCode>
Expand All @@ -93,14 +89,15 @@ where
I: AsRef<A> + Send + Sync,
A: Builder<E>,
{
let content_type_header = headers.get(CONTENT_TYPE);
let content_type = content_type_header.and_then(|value| value.to_str().ok());
let content_type = match content_type {
Some("application/octet-stream") => ContentType::Ssz,
_ => ContentType::Json,
};
let res = api_impl
.as_ref()
.get_header(slot, parent_hash, pubkey)
.await
.map(|signed_bid| ForkVersionedResponse {
version: Some(api_impl.as_ref().fork_name_at_slot(slot)),
metadata: EmptyMetadata {},
data: signed_bid,
});
build_response(res).await
.await;
build_response_with_headers(res, content_type, api_impl.as_ref().fork_name_at_slot(slot)).await
}
Loading