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(sidecar): configurable ValidatorIndex #101

Merged
merged 14 commits into from
Jul 3, 2024
18 changes: 10 additions & 8 deletions bolt-sidecar/bin/sidecar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ async fn main() -> eyre::Result<()> {

let (api_events, mut api_events_rx) = mpsc::channel(1024);
let shutdown_tx = json_rpc::start_server(&config, api_events).await?;
let consensus_state = ConsensusState::new(&config.beacon_api_url);
let consensus_state = ConsensusState::new(&config.beacon_api_url, &config.validator_indexes);

let builder_proxy_config = BuilderProxyConfig {
mevboost_url: config.mevboost_url,
Expand Down Expand Up @@ -69,11 +69,14 @@ async fn main() -> eyre::Result<()> {
tracing::info!("Received commitment request: {:?}", event.request);
let request = event.request;

if let Err (e) = consensus_state.validate_request(&CommitmentRequest::Inclusion(request.clone())) {
tracing::error!("Failed to validate request: {:?}", e);
let _ = event.response.send(Err(ApiError::Custom(e.to_string())));
continue;
}
let validator_index = match consensus_state.validate_request(&CommitmentRequest::Inclusion(request.clone())) {
Ok(index) => index,
Err(e) => {
tracing::error!("Failed to validate request: {:?}", e);
let _ = event.response.send(Err(ApiError::Custom(e.to_string())));
continue;
}
};

if let Err(e) = execution_state
.try_commit(&CommitmentRequest::Inclusion(request.clone()))
Expand All @@ -90,8 +93,7 @@ async fn main() -> eyre::Result<()> {
);

// parse the request into constraints and sign them with the sidecar signer
// TODO: get the validator index from somewhere
let message = ConstraintsMessage::build(0, request.slot, request.clone());
let message = ConstraintsMessage::build(validator_index, request.slot, request.clone());

let signature = signer.sign(&message.digest())?;
let signed_constraints: BatchedSignedConstraints =
Expand Down
8 changes: 8 additions & 0 deletions bolt-sidecar/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ pub struct Opts {
/// Max commitments to accept per block
#[clap(short = 'm', long)]
pub(super) max_commitments: Option<usize>,
/// Validator indexes
#[clap(short = 'v', long, value_parser, num_args = 0.., value_delimiter = ',')]
namn-grg marked this conversation as resolved.
Show resolved Hide resolved
pub(super) validator_indexes: Vec<u64>,
/// Signing options
#[clap(flatten)]
pub(super) signing: SigningOpts,
Expand Down Expand Up @@ -68,6 +71,8 @@ pub struct Config {
pub mevboost_proxy_port: u16,
/// Limits for the sidecar
pub limits: Limits,
/// Validator indexes
pub validator_indexes: Vec<u64>,
}

impl Default for Config {
Expand All @@ -82,6 +87,7 @@ impl Default for Config {
private_key: Some(random_bls_secret()),
mevboost_proxy_port: 18551,
limits: Limits::default(),
validator_indexes: Vec::new(),
}
}
}
Expand Down Expand Up @@ -127,6 +133,8 @@ impl TryFrom<Opts> for Config {
config.beacon_api_url = opts.beacon_api_url.trim_end_matches('/').to_string();
config.mevboost_url = opts.mevboost_url.trim_end_matches('/').to_string();

config.validator_indexes = opts.validator_indexes;

Ok(config)
}
}
Expand Down
68 changes: 65 additions & 3 deletions bolt-sidecar/src/state/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum ConsensusError {
InvalidSlot(Slot),
#[error("Inclusion deadline exceeded")]
DeadlineExceeded,
#[error("Validator not found in the slot")]
ValidatorNotFound,
}

pub struct Epoch {
Expand All @@ -34,11 +36,12 @@ pub struct ConsensusState {
epoch: Epoch,
// Timestamp when the current slot is received
timestamp: u64,
validator_indexes: Vec<u64>,
}

impl ConsensusState {
/// Create a new `ConsensusState` with the given beacon client HTTP URL.
pub fn new(url: &str) -> Self {
pub fn new(url: &str, validator_indexes: &[u64]) -> Self {
let url = Url::parse(url).expect("valid beacon client URL");
let beacon_api_client = Client::new(url);

Expand All @@ -51,6 +54,7 @@ impl ConsensusState {
proposer_duties: vec![],
},
timestamp: 0,
validator_indexes: validator_indexes.to_vec(),
}
}

Expand All @@ -59,7 +63,7 @@ impl ConsensusState {
/// 2. The request hasn't passed the slot deadline.
///
/// TODO: Integrate with the registry to check if we are registered.
pub fn validate_request(&self, request: &CommitmentRequest) -> Result<(), ConsensusError> {
pub fn validate_request(&self, request: &CommitmentRequest) -> Result<u64, ConsensusError> {
let CommitmentRequest::Inclusion(req) = request;

// Check if the slot is in the current epoch
Expand All @@ -72,7 +76,14 @@ impl ConsensusState {
return Err(ConsensusError::DeadlineExceeded);
}

Ok(())
// Find the validator index for the given slot
let validator_index = find_validator_index_for_slot(
&self.validator_indexes,
&self.epoch.proposer_duties,
req.slot,
)?;

Ok(validator_index)
}

/// Update the latest head and fetch the relevant data from the beacon chain.
Expand Down Expand Up @@ -109,6 +120,10 @@ impl ConsensusState {
self.epoch.proposer_duties = duties.1;
Ok(())
}

pub fn get_epoch(&self) -> &Epoch {
&self.epoch
}
}

/// Get the current timestamp.
Expand All @@ -118,3 +133,50 @@ fn current_timestamp() -> u64 {
.unwrap()
.as_secs()
}

/// Filters the proposer duties and returns the validator index for a given slot
/// if it doesn't exists then returns 0 by default.
pub fn find_validator_index_for_slot(
namn-grg marked this conversation as resolved.
Show resolved Hide resolved
validator_indexes: &[u64],
proposer_duties: &[ProposerDuty],
slot: u64,
) -> Result<u64, ConsensusError> {
proposer_duties
.iter()
.find(|&duty| {
duty.slot == slot && validator_indexes.contains(&(duty.validator_index as u64))
})
.map(|duty| duty.validator_index as u64)
.ok_or(ConsensusError::ValidatorNotFound)
}

#[cfg(test)]
mod tests {
use super::*;
use beacon_api_client::ProposerDuty;

#[test]
fn test_filter_index() {
let validator_indexes = vec![11, 22, 33];
let proposer_duties = vec![
ProposerDuty {
public_key: Default::default(),
slot: 1,
validator_index: 11,
},
ProposerDuty {
public_key: Default::default(),
slot: 2,
validator_index: 22,
},
ProposerDuty {
public_key: Default::default(),
slot: 3,
validator_index: 33,
},
];

let result = find_validator_index_for_slot(&validator_indexes, &proposer_duties, 2);
assert_eq!(result.unwrap(), 22);
}
}
5 changes: 0 additions & 5 deletions bolt-sidecar/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,6 @@ pub enum StateError {
Rpc(#[from] TransportError),
}

#[derive(Debug, Clone)]
struct ProposerDuties {
assigned_slots: Vec<u64>,
}

#[cfg(test)]
mod tests {
use alloy_consensus::constants::ETH_TO_WEI;
Expand Down