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

feat(signer)!: add proxy keys #39

Merged
merged 17 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
6 changes: 5 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
target
target
Dockerfile
.dockerignore
.git
.gitignore
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,4 @@ rand = "0.8.5"
dotenvy = "0.15.7"
indexmap = "2.2.6"
lazy_static = "1.5.0"
bimap = { version = "0.6.3", features = ["serde"] }
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A new Ethereum validator sidecar focused on standardizing the last mile of communication between validators and third-party protocols.

[Docs](https://commit-boost.github.io/commit-boost-client/) |
[Docs](https://commit-boost.github.io/commit-boost-client/) |
[Twitter](https://x.com/Commit_Boost)

## Overview
Expand Down Expand Up @@ -51,7 +51,7 @@ async fn main() {
let pubkey = *pubkeys.consensus.first().unwrap();

let datagram = Datagram { data: 42 };
let request = SignRequest::builder(config.id, pubkey).with_msg(&datagram);
let request = SignRequest::builder(pubkey).with_msg(&datagram);
let signature = config
.signer_client
.request_signature(&request)
Expand Down
92 changes: 86 additions & 6 deletions api/signer-api.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ paths:
summary: Get a list of public keys for which signatures may be requested
tags:
- Signer
security:
- BearerAuth: []
responses:
"200":
description: A list of Bls pubkeys
Expand All @@ -35,7 +37,6 @@ paths:
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"

"500":
description: Internal error
content:
Expand Down Expand Up @@ -67,10 +68,6 @@ paths:
schema:
type: object
properties:
id:
description: The module ID
type: string
example: "MY_MODULE_ID"
pubkey:
description: BLS public key of validator
type: string
Expand Down Expand Up @@ -99,7 +96,89 @@ paths:
pattern: "^0x[a-fA-F0-9]{192}$"
example: "0xa3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989a3ffa9241f78279f1af04644cb8c79c2d8f02bcf0e28e2f186f6dcccac0a869c2be441fda50f0dea895cfce2e53f0989"
"404":
description: Unknown value (pubkey, module id)
description: Unknown value (pubkey, etc.)
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"

/signer/v1/generate_proxy_key:
post:
summary: Request a proxy key be generated for a specific consensus pubkey
tags:
- Signer
security:
- BearerAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
pubkey:
description: a validator BLS public key for which to generate a proxy key
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118"
responses:
"200":
description: Successs
content:
application/json:
schema:
type: object
properties:
proxy_delegation:
type: object
properties:
delegator:
description: the validator BLS public key for which the proxy key was generated (the same one as requested)
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0xac5e059177afc33263e95d0be0690138b9a1d79a6e19018086a0362e0c30a50bf9e05a08cb44785724d0b2718c5c7118"
proxy:
description: the generated proxy public key
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{96}$"
example: "0x8a481a7a51c430a9bafa64366bc4934f5880f5f1d97646f91680936a53f2a268fdde5369430a2b4bb700c5f82cfbab3f"
signature:
description: The signature of the proxy delegation
type: string
format: hex
pattern: "^0x[a-fA-F0-9]{192}$"
example: "0xabfacf1cd17d80abfc6fa6b8e534ab25cdb1f95a855706ef604672c8695401a84c7834008e57925d4259c551b7c03d1a16f05b082294fadcba802a61a5cccfb5e96dd1dce4c9dac3f6d15254495019146346670be1f374a67cb0cda2aaf72d00"
"404":
description: Unknown value (pubkey, etc.)
content:
application/json:
schema:
Expand Down Expand Up @@ -130,6 +209,7 @@ paths:
message:
type: string
example: "Internal error"

components:
securitySchemes:
BearerAuth:
Expand Down
2 changes: 2 additions & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,5 @@ thiserror.workspace = true
eyre.workspace = true
url.workspace = true
rand.workspace = true
bimap.workspace = true
derive_more = "0.99.18"
David-Petrov marked this conversation as resolved.
Show resolved Hide resolved
27 changes: 25 additions & 2 deletions crates/common/src/commit/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
use serde::{Deserialize, Serialize};

use super::{
constants::{GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH},
constants::{GENERATE_PROXY_KEY_PATH, GET_PUBKEYS_PATH, REQUEST_SIGNATURE_PATH},
error::SignerClientError,
request::SignRequest,
request::{GenerateProxyRequest, SignRequest, SignedProxyDelegation},
};
use crate::DEFAULT_REQUEST_TIMEOUT;

Expand Down Expand Up @@ -87,4 +87,27 @@ impl SignerClient {

Ok(signature)
}

pub async fn generate_proxy_key(
&self,
pubkey: BlsPublicKey,
) -> Result<SignedProxyDelegation, SignerClientError> {
let url = format!("{}{}", self.url, GENERATE_PROXY_KEY_PATH);
let request = GenerateProxyRequest::new(pubkey);
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 signed_proxy_delegation = serde_json::from_slice(&response_bytes)?;

Ok(signed_proxy_delegation)
}
}
1 change: 1 addition & 0 deletions crates/common/src/commit/constants.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub const GET_PUBKEYS_PATH: &str = "/signer/v1/get_pubkeys";
pub const REQUEST_SIGNATURE_PATH: &str = "/signer/v1/request_signature";
pub const GENERATE_PROXY_KEY_PATH: &str = "/signer/v1/generate_proxy_key";
2 changes: 1 addition & 1 deletion crates/common/src/commit/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pub enum SignerClientError {
#[error("invalid header value: {0}")]
InvalidHeader(#[from] reqwest::header::InvalidHeaderValue),

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

#[error("serde decode error: {0}")]
Expand Down
27 changes: 18 additions & 9 deletions crates/common/src/commit/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub struct ProxyDelegation {

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct SignedProxyDelegation {
pub message: ProxyDelegation,
pub proxy_delegation: ProxyDelegation,
David-Petrov marked this conversation as resolved.
Show resolved Hide resolved
/// Signature of message with the delegator keypair
pub signature: BlsSignature,
}
Expand All @@ -25,33 +25,31 @@ impl SignedProxyDelegation {
pub fn validate(&self, chain: Chain) -> Result<(), BlstErrorWrapper> {
verify_signed_builder_message(
chain,
&self.message.delegator,
&self.message,
&self.proxy_delegation.delegator,
&self.proxy_delegation,
&self.signature,
)
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignRequest {
pub id: String,
pub pubkey: BlsPublicKey,
pub is_proxy: bool,
pub object_root: [u8; 32],
}

impl SignRequest {
pub fn new(
id: impl Into<String>,
pubkey: BlsPublicKey,
is_proxy: bool,
object_root: [u8; 32],
) -> SignRequest {
Self { id: id.into(), pubkey, is_proxy, object_root }
Self { pubkey, is_proxy, object_root }
}

pub fn builder(id: impl Into<String>, pubkey: BlsPublicKey) -> Self {
Self::new(id, pubkey, false, [0; 32])
pub fn builder(pubkey: BlsPublicKey) -> Self {
Self::new(pubkey, false, [0; 32])
}

pub fn is_proxy(self) -> Self {
Expand All @@ -63,6 +61,17 @@ impl SignRequest {
}

pub fn with_msg(self, msg: &impl TreeHash) -> Self {
Self { object_root: msg.tree_hash_root().0, ..self }
self.with_root(msg.tree_hash_root().0)
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GenerateProxyRequest {
pub pubkey: BlsPublicKey,
}

impl GenerateProxyRequest {
pub fn new(pubkey: BlsPublicKey) -> Self {
GenerateProxyRequest { pubkey }
}
}
14 changes: 7 additions & 7 deletions crates/common/src/config/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
utils::load_file_from_env,
BUILDER_SERVER_ENV,
},
types::Chain,
types::{Chain, Jwt, ModuleId},
};

#[derive(Debug, Deserialize, Serialize)]
Expand All @@ -25,7 +25,7 @@ pub enum ModuleKind {
#[derive(Debug, Deserialize, Serialize)]
pub struct StaticModuleConfig {
/// Unique id of the module
pub id: String,
pub id: ModuleId,
/// Docker image of the module
pub docker_image: String,
/// Type of the module
Expand All @@ -37,7 +37,7 @@ pub struct StaticModuleConfig {
#[derive(Debug)]
pub struct StartCommitModuleConfig<T = ()> {
/// Unique id of the module
pub id: String,
pub id: ModuleId,
/// Chain spec
pub chain: Chain,
/// Signer client to call Signer API
Expand All @@ -52,8 +52,8 @@ pub struct StartCommitModuleConfig<T = ()> {
/// - [MODULE_JWT_ENV] - the jwt token for the module
// TODO: add metrics url here
pub fn load_commit_module_config<T: DeserializeOwned>() -> Result<StartCommitModuleConfig<T>> {
let module_id = load_env_var(MODULE_ID_ENV)?;
let module_jwt = load_env_var(MODULE_JWT_ENV)?;
let module_id = ModuleId(load_env_var(MODULE_ID_ENV)?);
let module_jwt = Jwt(load_env_var(MODULE_JWT_ENV)?);
let signer_server_address = load_env_var(SIGNER_SERVER_ENV)?;

#[derive(Debug, Deserialize)]
Expand Down Expand Up @@ -111,7 +111,7 @@ pub fn load_commit_module_config<T: DeserializeOwned>() -> Result<StartCommitMod
#[derive(Debug)]
pub struct StartBuilderModuleConfig<T> {
/// Unique id of the module
pub id: String,
pub id: ModuleId,
/// Chain spec
pub chain: Chain,
/// Where to listen for Builder events
Expand All @@ -122,7 +122,7 @@ pub struct StartBuilderModuleConfig<T> {

pub fn load_builder_module_config<T: DeserializeOwned>() -> eyre::Result<StartBuilderModuleConfig<T>>
{
let module_id = load_env_var(MODULE_ID_ENV)?;
let module_id = ModuleId(load_env_var(MODULE_ID_ENV)?);
let builder_events_port: u16 = load_env_var(BUILDER_SERVER_ENV)?.parse()?;

#[derive(Debug, Deserialize)]
Expand Down
6 changes: 3 additions & 3 deletions crates/common/src/config/signer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use bimap::BiHashMap;

use eyre::Result;
use serde::{Deserialize, Serialize};
Expand All @@ -8,7 +8,7 @@ use super::{
utils::{load_env_var, load_jwts},
CommitBoostConfig,
};
use crate::{loader::SignerLoader, types::Chain};
use crate::{loader::SignerLoader, types::{Chain, Jwt, ModuleId}};

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SignerConfig {
Expand All @@ -28,7 +28,7 @@ pub struct StartSignerConfig {
pub chain: Chain,
pub loader: SignerLoader,
pub server_port: u16,
pub jwts: HashMap<String, String>,
pub jwts: BiHashMap<ModuleId, Jwt>,
}

impl StartSignerConfig {
Expand Down
9 changes: 5 additions & 4 deletions crates/common/src/config/utils.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::collections::HashMap;

use bimap::BiHashMap;
use eyre::{Context, Result};
use serde::de::DeserializeOwned;

use crate::types::{Jwt, ModuleId};

use super::constants::JWTS_ENV;

pub fn load_env_var(env: &str) -> Result<String> {
Expand All @@ -20,8 +21,8 @@ pub fn load_file_from_env<T: DeserializeOwned>(env: &str) -> Result<T> {
load_from_file(&path)
}

/// Loads a map of module id -> jwt token from a json env
pub fn load_jwts() -> Result<HashMap<String, String>> {
/// Loads a bidirectional map of module id <-> jwt token from a json env
pub fn load_jwts() -> Result<BiHashMap<ModuleId, Jwt>> {
let jwts = std::env::var(JWTS_ENV).wrap_err(format!("{JWTS_ENV} is not set"))?;
serde_json::from_str(&jwts).wrap_err("could not deserialize json from string")
}
Loading
Loading