Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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 @@ -35,7 +35,7 @@ ethereum_serde_utils = "0.5.2"
ethereum-types = "0.14.1"

# networking
axum = "0.7.5"
axum = { version = "0.7.5", features = ["macros"] }
axum-extra = { version = "0.9.3", features = ["typed-header"] }
reqwest = { version = "0.12.4", features = ["json"] }
headers = "0.4.0"
Expand Down
138 changes: 138 additions & 0 deletions api/signer-api.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
openapi: "3.0.2"
info:
title: Signer API
version: "0.1.0"
description: API that allows commit modules to request generic signatures from validators
tags:
- name: Signer
paths:
/signer/v1/get_pubkeys:
get:
summary: Get a list of public keys for which signatures may be requested
tags:
- Signer
responses:
"200":
description: A list of Bls pubkeys
content:
application/json:
schema:
type: object
properties:
consensus:
description: Consensus validator pubkeys
type: array
items:
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
proxy:
description: Proxy validator pubkeys
type: array
items:
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"

"500":
description: Internal error
content:
application/json:
schema:
type: object
required:
- code
- message
properties:
code:
type: number
example: 500
message:
type: string
example: "Internal error"

/signer/v1/request_signature:
post:
summary: Send a signature request
tags:
- Signer
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
id:
description: The module ID
type: string
example: "MY_MODULE_ID"
pubkey:
description: BLS public key of validator
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
is_proxy:
description: Whether the request is for a proxy pubkey
type: boolean
example: false
object_root:
description: The root of the object to be signed
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{64}$"
example: "0x3e9f4a78b5c21d64f0b8e3d9a7f5c02b4d1e67a3c8f29b5d6e4a3b1c8f72e6d9"
responses:
"200":
description: Successs
content:
application/json:
schema:
type: string
description: The validator signature
format: hex
pattern: "^0x[a-fA-F0-9]{192}$"
example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989a3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
"404":
description: Unknown value (pubkey, module id)
content:
application/json:
schema:
type: object
required:
- code
- message
properties:
code:
type: number
example: 404
message:
type: string
example: "Unknown pubkey"
"500":
description: Internal error
content:
application/json:
schema:
type: object
required:
- code
- message
properties:
code:
type: number
example: 500
message:
type: string
example: "Internal error"
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
16 changes: 11 additions & 5 deletions crates/cli/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::process::Stdio;
use std::{collections::HashMap, process::Stdio};

use cb_common::{
config::{CommitBoostConfig, CONFIG_PATH_ENV, MODULE_ID_ENV},
Expand Down Expand Up @@ -69,9 +69,12 @@ impl Args {
Command::Start { config: config_path } => {
let config = CommitBoostConfig::from_file(&config_path);

if let Some(modules) = config.modules {
let signer_config = config.signer.expect("missing signer config with modules");
// TODO: fix with config
let pbs_jwt = "MY_PBS_TOKEN";
let signer_config = config.signer.expect("missing signer config with modules");
let signer_address = signer_config.address;

if let Some(modules) = config.modules {
// this mocks the commit boost client starting containers, processes etc
let mut child_handles = Vec::with_capacity(modules.len());

Expand All @@ -86,7 +89,9 @@ impl Args {
}

// start signing server
tokio::spawn(SigningService::run(config.chain, signer_config));
// TODO: generate jwt for each module id
let jwts = HashMap::new();
tokio::spawn(SigningService::run(config.chain, signer_config, jwts));
}

// start pbs server
Expand All @@ -102,7 +107,8 @@ impl Args {
eprintln!("Process failed with status: {}", cmd.status);
}
} else {
let state = BuilderState::<()>::new(config.chain, config.pbs);
let state =
BuilderState::<()>::new(config.chain, config.pbs, signer_address, pbs_jwt);
PbsService::run::<(), DefaultBuilderApi>(state).await;
}
}
Expand Down
3 changes: 3 additions & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ blst.workspace = true
tree_hash.workspace = true
tree_hash_derive.workspace = true
rand.workspace = true

reqwest.workspace = true
thiserror.workspace = true
85 changes: 85 additions & 0 deletions crates/common/src/commit/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::{net::SocketAddr, sync::Arc};

use alloy_rpc_types_beacon::{BlsPublicKey, BlsSignature};
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
use serde::{Deserialize, Serialize};

use super::{
constants::{GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH},
error::SignerClientError,
request::SignRequest,
};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct GetPubkeysResponse {
pub consensus: Vec<BlsPublicKey>,
pub proxy: Vec<BlsPublicKey>,
}

/// Client used by commit modules to request signatures via the Signer API
#[derive(Debug, Clone)]
pub struct SignerClient {
/// Url endpoint of the Signer Module
url: Arc<str>,
client: reqwest::Client,
}

impl SignerClient {
/// Create a new SignerClient
pub fn new(signer_address: SocketAddr, jwt: &str) -> Result<Self, SignerClientError> {
let url = format!("http://{}", signer_address);
let mut headers = HeaderMap::new();

let mut auth_value = HeaderValue::from_str(&format!("Bearer {}", jwt))?;
auth_value.set_sensitive(true);
headers.insert(AUTHORIZATION, auth_value);

let client = reqwest::ClientBuilder::new().default_headers(headers).build()?;

Ok(Self { url: url.into(), client })
}

/// Request a list of validator pubkeys for which signatures can be requested.
/// TODO: add more docs on how proxy keys work
pub async fn get_pubkeys(&self) -> Result<GetPubkeysResponse, SignerClientError> {
let url = format!("{}{}", self.url, GET_PUBKEYS_PATH);
let res = self.client.get(&url).send().await?;

let status = res.status();
let response_bytes = res.bytes().await?;

if !status.is_success() {
return Err(SignerClientError::FailedRequest {
status: status.as_u16(),
error_msg: String::from_utf8_lossy(&response_bytes).into_owned(),
});
}

let parsed_response: GetPubkeysResponse = serde_json::from_slice(&response_bytes)?;

Ok(parsed_response)
}

/// Send a signature request
pub async fn request_signature(
&self,
request: &SignRequest,
) -> Result<BlsSignature, SignerClientError> {
let url = format!("{}{}", self.url, REQUEST_SIGNATURE_PATH);
let res = self.client.post(&url).json(&request).send().await?;

let status = res.status();
let response_bytes = res.bytes().await?;

if !status.is_success() {
return Err(SignerClientError::FailedRequest {
status: status.as_u16(),
error_msg: String::from_utf8_lossy(&response_bytes).into_owned(),
});
}

let signature: BlsSignature = serde_json::from_slice(&response_bytes)?;

Ok(signature)
}
}
2 changes: 2 additions & 0 deletions crates/common/src/commit/constants.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const GET_PUBKEYS_PATH: &str = "/signer/v1/get_pubkeys";
pub const REQUEST_SIGNATURE_PATH: &str = "/signer/v1/request_signature";
14 changes: 14 additions & 0 deletions crates/common/src/commit/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#[derive(Debug, thiserror::Error)]
pub enum SignerClientError {
#[error("reqwest error: {0}")]
ReqwestError(#[from] reqwest::Error),

#[error("invalid header value: {0}")]
InvalidHeader(#[from] reqwest::header::InvalidHeaderValue),

#[error("failed request: status {status} msg {error_msg}")]
FailedRequest { status: u16, error_msg: String },

#[error("serde decode error: {0}")]
SerdeDecodeError(#[from] serde_json::Error),
}
4 changes: 4 additions & 0 deletions crates/common/src/commit/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
pub mod client;
pub mod constants;
pub mod error;
pub mod request;
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use alloy_rpc_types_beacon::{BlsPublicKey, BlsSignature};
use blst::BLST_ERROR;
use cb_common::{signature::verify_signed_builder_message, types::Chain};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;

use crate::{signature::verify_signed_builder_message, types::Chain};

// TODO: might need to adapt the SignedProxyDelegation so that it goes through web3 signer
#[derive(Debug, Clone, Copy, Serialize, Deserialize, Encode, Decode, TreeHash)]
pub struct ProxyDelegation {
Expand Down
1 change: 1 addition & 0 deletions crates/common/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct SignerConfig {
/// Where to start signing server
pub address: SocketAddr,

/// Which keys to load
pub loader: SignerLoader,
}

Expand Down
1 change: 1 addition & 0 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod commit;
pub mod config;
pub mod constants;
pub mod pbs;
Expand Down
3 changes: 0 additions & 3 deletions crates/common/src/pbs/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,4 @@ pub const HEADER_KEY_SLOT_UUID: &str = "X-MEVBoost-SlotID";
pub const HEADER_KEY_VERSION: &str = "X-MEVBoost-Version"; // do we need to use this
pub const HEADER_START_TIME_UNIX_MS: &str = "X-MEVBoost-StartTimeUnixMS";

pub const COMMIT_BOOST_API: &str = "/commit-boost/v1";
pub const SIGN_REQUEST_PATH: &str = "/signer/sign";
pub const PUBKEYS_PATH: &str = "/signer/pubkeys";
pub const BUILDER_EVENTS_PATH: &str = "/events";
3 changes: 2 additions & 1 deletion crates/crypto/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ ethereum_ssz.workspace = true
ethereum_ssz_derive.workspace = true

axum.workspace = true

axum-extra.workspace = true
headers.workspace = true
# crypto
blst.workspace = true
tree_hash.workspace = true
Expand Down
26 changes: 21 additions & 5 deletions crates/crypto/src/error.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,34 @@
use alloy_rpc_types_beacon::BlsPublicKey;
use axum::response::IntoResponse;
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use thiserror::Error;

#[derive(Debug, Error)]
pub enum SignError {
pub enum SignerModuleError {
#[error("unauthorized")]
Unauthorized,

#[error("unknown module id: {0}")]
UnknownModuleId(String),

#[error("unknown consensus signer: {0}")]
UnknownConsensusSigner(BlsPublicKey),

#[error("unknown proxy signer: {0}")]
UnknownProxySigner(BlsPublicKey),
}

impl IntoResponse for SignError {
fn into_response(self) -> axum::response::Response {
todo!()
impl IntoResponse for SignerModuleError {
fn into_response(self) -> Response {
let status = match self {
SignerModuleError::Unauthorized => StatusCode::UNAUTHORIZED,
SignerModuleError::UnknownModuleId(_) => StatusCode::NOT_FOUND,
SignerModuleError::UnknownConsensusSigner(_) => StatusCode::NOT_FOUND,
SignerModuleError::UnknownProxySigner(_) => StatusCode::NOT_FOUND,
};

(status, self.to_string()).into_response()
}
}
1 change: 0 additions & 1 deletion crates/crypto/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
pub mod error;
pub mod manager;
pub mod service;
pub mod types;
Loading