Skip to content

Commit

Permalink
feat: added constraint pubkey selection logic
Browse files Browse the repository at this point in the history
  • Loading branch information
merklefruit committed Oct 14, 2024
1 parent 8130b68 commit bd08ed5
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 37 deletions.
12 changes: 11 additions & 1 deletion bolt-sidecar/src/client/constraints_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
use axum::http::StatusCode;
use beacon_api_client::VersionedValue;
use ethereum_consensus::{
builder::SignedValidatorRegistration, deneb::mainnet::SignedBlindedBeaconBlock, Fork,
builder::SignedValidatorRegistration, crypto::PublicKey as BlsPublicKey,
deneb::mainnet::SignedBlindedBeaconBlock, Fork,
};
use reqwest::Url;
use tracing::error;
Expand Down Expand Up @@ -48,6 +49,15 @@ impl ConstraintsClient {
self.delegations.extend(delegations);
}

/// Finds all delegations for the given public key.
pub fn find_delegatees(&self, pubkey: &BlsPublicKey) -> Vec<&BlsPublicKey> {
self.delegations
.iter()
.filter(|d| d.message.delegatee_pubkey == *pubkey)
.map(|d| &d.message.delegatee_pubkey)
.collect::<Vec<_>>()
}

fn endpoint(&self, path: &str) -> Url {
self.url.join(path).unwrap_or_else(|e| {
error!(err = ?e, "Failed to join path: {} with url: {}", path, self.url);
Expand Down
72 changes: 50 additions & 22 deletions bolt-sidecar/src/driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,32 @@ use crate::{
};

/// The driver for the sidecar, responsible for managing the main event loop.
///
/// The reponsibilities of the driver include:
/// - Handling incoming API events
/// - Updating the execution state based on new beacon chain heads
/// - Submitting constraints to the constraints service at the commitment deadline
/// - Building local payloads for the beacon chain
/// - Responding to requests to fetch a local payload
/// - Updating the consensus state based on the beacon chain clock
pub struct SidecarDriver<C, ECDSA> {
/// Head tracker for monitoring the beacon chain clock
head_tracker: HeadTracker,
/// Execution state for tracking the current head and block templates
execution: ExecutionState<C>,
/// Consensus state for tracking the current slot and validator indexes
consensus: ConsensusState,
/// Signer for creating constraints
constraint_signer: SignerBLS,
/// Signer for creating commitment responses
commitment_signer: ECDSA,
/// Local block builder for creating local payloads
local_builder: LocalBuilder,
/// Client for interacting with the constraints service
constraints_client: ConstraintsClient,
/// Channel for receiving incoming API events
api_events_rx: mpsc::Receiver<CommitmentEvent>,
/// Channel for receiving requests to fetch a local payload
payload_requests_rx: mpsc::Receiver<FetchPayloadRequest>,
/// Stream of slots made from the consensus clock
slot_stream: SlotStream<SystemTimeProvider>,
Expand Down Expand Up @@ -110,20 +127,13 @@ impl SidecarDriver<StateClient, CommitBoostSigner> {
let state_client = StateClient::new(opts.execution_api_url.clone());

let commit_boost_signer = CommitBoostSigner::new(
opts.signing
.commit_boost_address
.expect("CommitBoost URL must be provided")
.to_string(),
&opts.signing.commit_boost_jwt_hex.clone().expect("CommitBoost JWT must be provided"),
)
.await?;
opts.signing.commit_boost_address.expect("CommitBoost URL").to_string(),
&opts.signing.commit_boost_jwt_hex.clone().expect("CommitBoost JWT"),
)?;

let cb_bls_signer = SignerBLS::CommitBoost(commit_boost_signer.clone());

// Commitment responses are signed with commit-boost signer
let commitment_signer = commit_boost_signer.clone();

Self::from_components(opts, cb_bls_signer, commitment_signer, state_client).await
Self::from_components(opts, cb_bls_signer, commit_boost_signer, state_client).await
}
}

Expand Down Expand Up @@ -237,7 +247,7 @@ impl<C: StateFetcher, ECDSA: SignerECDSA> SidecarDriver<C, ECDSA> {
let start = Instant::now();

let validator_pubkey = match self.consensus.validate_request(&request) {
Ok(index) => index,
Ok(pubkey) => pubkey,
Err(err) => {
error!(?err, "Consensus: failed to validate request");
let _ = response.send(Err(CommitmentError::Consensus(err)));
Expand All @@ -262,13 +272,28 @@ impl<C: StateFetcher, ECDSA: SignerECDSA> SidecarDriver<C, ECDSA> {
"Validation against execution state passed"
);

// parse the request into constraints and sign them
let slot = inclusion_request.slot;

let pubkey = match self.constraint_signer {
SignerBLS::Local(ref signer) => signer.pubkey(),
SignerBLS::CommitBoost(ref signer) => signer.pubkey(),
SignerBLS::Keystore(_) => validator_pubkey.clone(),
let delegatees = self.constraints_client.find_delegatees(&validator_pubkey);
let available_pubkeys = self.constraint_signer.available_pubkeys();

// Pick a pubkey to sign constraints with.
//
// Rationale:
// - If there are no delegatee keys, try to use the validator key directly if available.
// - If there are delegatee keys, try to use the first one that is available in the list.
let pubkey = if delegatees.is_empty() {
if available_pubkeys.contains(&validator_pubkey) {
validator_pubkey.clone()
} else {
error!(%target_slot, %validator_pubkey, "No authorized private key available to sign constraints");
let _ = response.send(Err(CommitmentError::Internal));
return;
}
} else if let Some(delegatee) = available_pubkeys.iter().find(|k| delegatees.contains(k)) {
delegatee.clone()
} else {
error!(%target_slot, "No delegatee key found, unable to sign constraints");
let _ = response.send(Err(CommitmentError::Internal));
return;
};

// NOTE: we iterate over the transactions in the request and generate a signed constraint
Expand All @@ -277,7 +302,7 @@ impl<C: StateFetcher, ECDSA: SignerECDSA> SidecarDriver<C, ECDSA> {
// with no ordering guarantees.
for tx in inclusion_request.txs {
let tx_type = tx.tx_type();
let message = ConstraintsMessage::from_transaction(pubkey.clone(), slot, tx);
let message = ConstraintsMessage::from_transaction(pubkey.clone(), target_slot, tx);
let digest = message.digest();

let signature = match self.constraint_signer {
Expand All @@ -298,12 +323,15 @@ impl<C: StateFetcher, ECDSA: SignerECDSA> SidecarDriver<C, ECDSA> {
};

ApiMetrics::increment_transactions_preconfirmed(tx_type);
self.execution.add_constraint(slot, signed_constraints);
self.execution.add_constraint(target_slot, signed_constraints);
}

// Create a commitment by signing the request
match request.commit_and_sign(&self.commitment_signer).await {
Ok(commitment) => response.send(Ok(commitment)).ok(),
Ok(commitment) => {
info!(target_slot, elapsed = ?start.elapsed(), "Commitment signed and sent");
response.send(Ok(commitment)).ok()
}
Err(err) => {
error!(?err, "Failed to sign commitment");
response.send(Err(CommitmentError::Internal)).ok()
Expand Down
6 changes: 3 additions & 3 deletions bolt-sidecar/src/signer/commit_boost.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ pub enum CommitBoostError {
#[allow(unused)]
impl CommitBoostSigner {
/// Create a new [CommitBoostSigner] instance
pub async fn new(signer_server_address: String, jwt: &str) -> SignerResult<Self> {
pub fn new(signer_server_address: String, jwt: &str) -> SignerResult<Self> {
let signer_client =
SignerClient::new(signer_server_address, jwt).map_err(CommitBoostError::Other)?;

Expand Down Expand Up @@ -178,7 +178,7 @@ mod test {
return Ok(());
}
};
let signer = CommitBoostSigner::new(signer_server_address, &jwt_hex).await.unwrap();
let signer = CommitBoostSigner::new(signer_server_address, &jwt_hex).unwrap();

// Generate random data for the test
let mut rng = rand::thread_rng();
Expand Down Expand Up @@ -208,7 +208,7 @@ mod test {
return Ok(());
}
};
let signer = CommitBoostSigner::new(signer_server_address, &jwt_hex).await.unwrap();
let signer = CommitBoostSigner::new(signer_server_address, &jwt_hex).unwrap();
let pubkey = signer.get_proxy_ecdsa_pubkey();

// Generate random data for the test
Expand Down
14 changes: 14 additions & 0 deletions bolt-sidecar/src/signer/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use std::{

use alloy::rpc::types::beacon::constants::BLS_PUBLIC_KEY_BYTES_LEN;

use ethereum_consensus::crypto::PublicKey as BlsPublicKey;
use lighthouse_bls::Keypair;
use lighthouse_eth2_keystore::Keystore;
use ssz::Encode;
Expand Down Expand Up @@ -41,6 +42,7 @@ pub struct KeystoreSigner {
}

impl KeystoreSigner {
/// Creates a new `KeystoreSigner` from the keystore files in the `keys_path` directory.
pub fn new(keys_path: Option<&str>, password: &[u8], chain: ChainConfig) -> SignerResult<Self> {
let keystores_paths = keystore_paths(keys_path)?;
let mut keypairs = Vec::with_capacity(keystores_paths.len());
Expand All @@ -57,6 +59,17 @@ impl KeystoreSigner {
Ok(Self { keypairs, chain })
}

/// Returns the public keys of the keypairs in the keystore.
pub fn pubkeys(&self) -> Vec<BlsPublicKey> {
self.keypairs
.iter()
.map(|kp| {
BlsPublicKey::try_from(kp.pk.serialize().to_vec().as_ref()).expect("valid pubkey")
})
.collect::<Vec<_>>()
}

/// Signs a message with the keystore signer and the Commit Boost domain
pub fn sign_commit_boost_root(
&self,
root: [u8; 32],
Expand All @@ -65,6 +78,7 @@ impl KeystoreSigner {
self.sign_root(root, public_key, self.chain.commit_boost_domain())
}

/// Signs a message with the keystore signer.
fn sign_root(
&self,
root: [u8; 32],
Expand Down
35 changes: 24 additions & 11 deletions bolt-sidecar/src/signer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use ethereum_consensus::crypto::bls::PublicKey as BlsPublicKey;

pub mod commit_boost;
use commit_boost::CommitBoostSigner;

Expand All @@ -7,17 +9,6 @@ use keystore::KeystoreSigner;
pub mod local;
use local::LocalSigner;

/// Signer for BLS signatures.
#[derive(Debug, Clone)]
pub enum SignerBLS {
/// Local signer with a BLS secret key.
Local(LocalSigner),
/// Signer from Commit-Boost.
CommitBoost(CommitBoostSigner),
/// Signer consisting of multiple keypairs loaded from ERC-2335 keystores files.
Keystore(KeystoreSigner),
}

/// Error in the signer.
#[derive(Debug, thiserror::Error)]
pub enum SignerError {
Expand All @@ -30,3 +21,25 @@ pub enum SignerError {
}

pub type SignerResult<T> = std::result::Result<T, SignerError>;

/// Signer for BLS signatures.
#[derive(Debug, Clone)]
pub enum SignerBLS {
/// Local signer with a BLS secret key.
Local(LocalSigner),
/// Signer from Commit-Boost.
CommitBoost(CommitBoostSigner),
/// Signer consisting of multiple keypairs loaded from ERC-2335 keystores files.
Keystore(KeystoreSigner),
}

impl SignerBLS {
/// Returns all the public keys available for signing.
pub fn available_pubkeys(&self) -> Vec<BlsPublicKey> {
match self {
SignerBLS::Local(signer) => vec![signer.pubkey()],
SignerBLS::CommitBoost(signer) => vec![signer.pubkey()],
SignerBLS::Keystore(signer) => signer.pubkeys(),
}
}
}

0 comments on commit bd08ed5

Please sign in to comment.