Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
02acf38
refactor(signer)!: generalize for other signature schemes than BLS
Aug 9, 2024
a7c76df
feat(signer)!: add support for ECDSA proxy keys
Aug 13, 2024
5530567
Merge branch 'main' into feat/ecdsa-proxy-keys
Aug 13, 2024
a5450b6
chore(clippy): infallible instead of fallible conversions
Aug 14, 2024
a9ff828
chore(signer): resolve TODOs
Aug 14, 2024
4e96742
refactor(signer)!: move around
Aug 14, 2024
4b283e2
chore(signer)!: rename associated type and type alias
Aug 14, 2024
9d70712
choer(signer): rename client `Signature` type
Aug 14, 2024
287da33
refactor(signer)!: remove default type parameter in `Signer`
Aug 14, 2024
431f189
chore(clippy): remove unnecessary nested reference
Aug 14, 2024
8041e0f
chore: remove commented code
Aug 15, 2024
23437a2
refactor(signer/ecdsa): derive unnecessary trait impls
Aug 15, 2024
9078e25
style: remove unnecessary constraint duplication
Aug 15, 2024
377aa78
refactor(signer)!: rename module `signers` to `signer` and constrain …
Aug 15, 2024
b83841e
refactor(loader): unify `ConsensusSigner` type alias usage
Aug 15, 2024
7e1bf44
chore!: remove forgotten debug
Aug 20, 2024
eba97b0
refactor(signer)!: [WIP] simplify proxy keys
Aug 21, 2024
ad6d373
refactor(signer)!: separate BLS and ECDSA types in client SDK
Aug 21, 2024
fede09c
chore(signer)!: reformat & clean up
Aug 21, 2024
68fce43
refactor(signer/manager): remove enum `GenericProxySigner`
Aug 22, 2024
a7d53a1
refactor(signer)!: make `SignedProxyDelegation` generic in the public…
Aug 22, 2024
780299e
refactor(signer/request): make `SignProxyRequest` generic in the publ…
Aug 23, 2024
7ddcd3a
refactor(signer/manager)!: split generic `get_delegation` into two se…
Aug 23, 2024
b8f228b
refactor(signer/client): remove unnecessary `Vec<u8>` deserialization
Aug 27, 2024
635f620
feat(signer): add tests for ECDSA proxies
Aug 27, 2024
dd864ec
refactor(signer/api)!: proper struct serde
Aug 28, 2024
b3286d5
refactor(signer): remove unnecessary module
Aug 28, 2024
7dedbcf
refactor(signer)!: remove `GenericPubkey`
Aug 28, 2024
f075db9
refactor(signer/request): move `GetPubkeysResponse`
Aug 28, 2024
9c4f412
refactor(signer): reduce code duplication around `SignRequest`
Aug 28, 2024
4e652d9
style/refactor(signer): reduce method size
Aug 28, 2024
9907059
refactor(deps)!: revert version updates
Aug 28, 2024
0ff2091
chore(deps)!: remove unused deps
Aug 28, 2024
dea0b0c
chore(signer): add TODO for `BlsPublicKey` wrapper
Aug 29, 2024
5fc50ae
refactor(signer)!: re-export request types properly from prelude
Aug 29, 2024
0bcd2d7
feat(signer/api): update api docs
Aug 29, 2024
f103c8d
refactor(signer/client): re-export bls types and rename methods for c…
Aug 29, 2024
da550d3
chore(docs): update md files in book
Aug 29, 2024
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
11 changes: 7 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ cb-signer = { path = "crates/signer" }
alloy = { version = "0.2.0", features = ["rpc-types-beacon"] }
ethereum_ssz = "0.5"
ethereum_ssz_derive = "0.5.3"
ssz_types = "0.5"
ssz_types = "0.6.0"
ethereum_serde_utils = "0.5.2"
ethereum-types = "0.14.1"

Expand Down Expand Up @@ -59,9 +59,12 @@ prometheus = "0.13.4"

# crypto
blst = "0.3.11"
tree_hash = "0.5"
tree_hash_derive = "0.5"
tree_hash = "0.6.0"
tree_hash_derive = "0.6.0"
Comment thread
David-Petrov marked this conversation as resolved.
Outdated
eth2_keystore = { git = "https://github.com/sigp/lighthouse", rev = "9e12c21f268c80a3f002ae0ca27477f9f512eb6f" }
elliptic-curve = { version = "0.13", features = ["serde"] }
generic-array = { version = "0.14.7", features = ["serde"] }
k256 = "0.13"

# docker
docker-compose-types = "0.12.0"
Expand All @@ -79,4 +82,4 @@ dotenvy = "0.15.7"
indexmap = "2.2.6"
lazy_static = "1.5.0"
bimap = { version = "0.6.3", features = ["serde"] }
derive_more = "0.99.18"
derive_more = { version = "1.0.0", features = ["from", "into", "deref", "display"] }
3 changes: 3 additions & 0 deletions crates/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ blst.workspace = true
tree_hash.workspace = true
tree_hash_derive.workspace = true
eth2_keystore.workspace = true
elliptic-curve.workspace = true
generic-array.workspace = true
k256.workspace = true

# misc
thiserror.workspace = true
Expand Down
37 changes: 27 additions & 10 deletions crates/common/src/commit/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::sync::Arc;
use std::{fmt, sync::Arc};

use alloy::rpc::types::beacon::{BlsPublicKey, BlsSignature};
use alloy::rpc::types::beacon::BlsPublicKey;
use eyre::WrapErr;
use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION};
use serde::{Deserialize, Serialize};
Expand All @@ -10,12 +10,12 @@ use super::{
error::SignerClientError,
request::{GenerateProxyRequest, SignRequest, SignedProxyDelegation},
};
use crate::DEFAULT_REQUEST_TIMEOUT;
use crate::{signer::GenericPubkey, DEFAULT_REQUEST_TIMEOUT};

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

/// Client used by commit modules to request signatures via the Signer API
Expand Down Expand Up @@ -45,7 +45,8 @@ impl SignerClient {
}

/// Request a list of validator pubkeys for which signatures can be
/// requested. TODO: add more docs on how proxy keys work
/// 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?;
Expand All @@ -69,7 +70,7 @@ impl SignerClient {
pub async fn request_signature(
Comment thread
David-Petrov marked this conversation as resolved.
Outdated
&self,
request: &SignRequest,
) -> Result<BlsSignature, SignerClientError> {
) -> Result<Signature, SignerClientError> {
let url = format!("{}{}", self.url, REQUEST_SIGNATURE_PATH);
let res = self.client.post(&url).json(&request).send().await?;

Expand All @@ -83,17 +84,17 @@ impl SignerClient {
});
}

let signature: BlsSignature = serde_json::from_slice(&response_bytes)?;
let signature: Vec<u8> = serde_json::from_slice(&response_bytes)?;
Comment thread
David-Petrov marked this conversation as resolved.
Outdated

Ok(signature)
Ok(Signature(signature))
}

pub async fn generate_proxy_key(
&self,
pubkey: BlsPublicKey,
request: &GenerateProxyRequest,
) -> Result<SignedProxyDelegation, SignerClientError> {
let url = format!("{}{}", self.url, GENERATE_PROXY_KEY_PATH);
let request = GenerateProxyRequest::new(pubkey);
println!("{}", serde_json::to_string(&request).unwrap());
let res = self.client.post(&url).json(&request).send().await?;

let status = res.status();
Expand All @@ -111,3 +112,19 @@ impl SignerClient {
Ok(signed_proxy_delegation)
}
}

// NOTE(David):
// For now, this is a simple displayable wrapper around vec, serving as a
// client-side type. It can be further deliberated whether a separate
// client-side type is preferrable over re-using an SDK in `common::signer`.
pub struct Signature(Vec<u8>);

impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x")?;
for byte in &self.0 {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
49 changes: 39 additions & 10 deletions crates/common/src/commit/request.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
use std::fmt;

use alloy::rpc::types::beacon::{BlsPublicKey, BlsSignature};
use serde::{Deserialize, Serialize};
use ssz_derive::{Decode, Encode};
use tree_hash::TreeHash;
use tree_hash_derive::TreeHash;

use crate::{error::BlstErrorWrapper, signature::verify_signed_builder_message, types::Chain};
use crate::{
error::BlstErrorWrapper, signature::verify_signed_builder_message, signer::GenericPubkey,
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 {
pub delegator: BlsPublicKey,
pub proxy: BlsPublicKey,
pub proxy: GenericPubkey,
}

impl fmt::Display for ProxyDelegation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Delegator: {}\nProxy: {}", self.delegator, self.proxy)
}
}

// TODO: might need to adapt the SignedProxyDelegation so that it goes through
// web3 signer
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct SignedProxyDelegation {
pub message: ProxyDelegation,
Expand All @@ -32,19 +43,28 @@ impl SignedProxyDelegation {
}
}

impl fmt::Display for SignedProxyDelegation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}\nSignature: {}", self.message, self.signature)
}
}

// TODO(David): Consider splitting `SignRequest` into two: ConsensusSignRequest
Comment thread
David-Petrov marked this conversation as resolved.
Outdated
// and ProxySignRequest. For better type safety (to avoid the Vec<u8>
// generalisation) and avoid the is_proxy flag
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SignRequest {
pub pubkey: BlsPublicKey,
pub pubkey: Vec<u8>,
pub is_proxy: bool,
pub object_root: [u8; 32],
}

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

pub fn builder(pubkey: BlsPublicKey) -> Self {
pub fn builder(pubkey: Vec<u8>) -> Self {
Self::new(pubkey, false, [0; 32])
}

Expand All @@ -61,13 +81,22 @@ impl SignRequest {
}
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum EncryptionScheme {
#[serde(rename = "bls")]
Bls,
#[serde(rename = "ecdsa")]
Ecdsa,
}

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

impl GenerateProxyRequest {
pub fn new(pubkey: BlsPublicKey) -> Self {
GenerateProxyRequest { pubkey }
pub fn new(consensus_pubkey: BlsPublicKey, scheme: EncryptionScheme) -> Self {
GenerateProxyRequest { consensus_pubkey, scheme }
}
}
24 changes: 14 additions & 10 deletions crates/common/src/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use std::fs;

use alloy::{primitives::hex::FromHex, rpc::types::beacon::BlsPublicKey};
use eth2_keystore::Keystore;
use eyre::eyre;
use eyre::{eyre, Context};
use serde::{de, Deserialize, Deserializer, Serialize};

use crate::{
config::{load_env_var, SIGNER_DIR_KEYS_ENV, SIGNER_DIR_SECRETS_ENV, SIGNER_KEYS_ENV},
signer::Signer,
signer::ConsensusSigner,
};

#[derive(Debug, Serialize, Deserialize, Clone)]
Expand All @@ -24,12 +24,12 @@ pub enum SignerLoader {
}

impl SignerLoader {
pub fn load_keys(self) -> eyre::Result<Vec<Signer>> {
pub fn load_keys(self) -> eyre::Result<Vec<ConsensusSigner>> {
// TODO: add flag to support also native loader
self.load_from_env()
}

pub fn load_from_env(self) -> eyre::Result<Vec<Signer>> {
pub fn load_from_env(self) -> eyre::Result<Vec<ConsensusSigner>> {
Ok(match self {
SignerLoader::File { .. } => {
let path = load_env_var(SIGNER_KEYS_ENV)?;
Expand All @@ -39,15 +39,16 @@ impl SignerLoader {
let keys: Vec<FileKey> = serde_json::from_str(&file)?;

keys.into_iter()
.map(|k| Signer::new_from_bytes(&k.secret_key))
.collect::<eyre::Result<Vec<Signer>>>()?
.map(|k| ConsensusSigner::new_from_bytes(&k.secret_key))
.collect::<Result<_, _>>()
.context("failed to load signers")?
}
SignerLoader::ValidatorsDir { .. } => {
// TODO: hacky way to load for now, we should support reading the
// definitions.yml file
let keys_path = load_env_var(SIGNER_DIR_KEYS_ENV)?;
let secrets_path = load_env_var(SIGNER_DIR_SECRETS_ENV)?;
load_secrets_and_keys(keys_path, secrets_path).expect("failed to load signers")
load_secrets_and_keys(keys_path, secrets_path).context("failed to load signers")?
}
})
}
Expand All @@ -71,7 +72,10 @@ impl<'de> Deserialize<'de> for FileKey {
}
}

fn load_secrets_and_keys(keys_path: String, secrets_path: String) -> eyre::Result<Vec<Signer>> {
fn load_secrets_and_keys(
keys_path: String,
secrets_path: String,
) -> eyre::Result<Vec<ConsensusSigner>> {
let entries = fs::read_dir(keys_path.clone())?;

let mut signers = Vec::new();
Expand All @@ -98,12 +102,12 @@ fn load_secrets_and_keys(keys_path: String, secrets_path: String) -> eyre::Resul
Ok(signers)
}

fn load_one(ks_path: String, pw_path: String) -> eyre::Result<Signer> {
fn load_one(ks_path: String, pw_path: String) -> eyre::Result<ConsensusSigner> {
let keystore = Keystore::from_json_file(ks_path).map_err(|_| eyre!("failed reading json"))?;
let password = fs::read(pw_path)?;
let key =
keystore.decrypt_keypair(&password).map_err(|_| eyre!("failed decrypting keypair"))?;
Signer::new_from_bytes(key.sk.serialize().as_bytes())
ConsensusSigner::new_from_bytes(key.sk.serialize().as_bytes())
}

#[cfg(test)]
Expand Down
Loading