Skip to content

Commit

Permalink
Operator adjusted to the new PreconfTaskManager interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mskrzypkows committed Aug 23, 2024
1 parent ac4e76d commit 83924b5
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 114 deletions.
64 changes: 24 additions & 40 deletions Node/src/ethereum_l1/execution_layer.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::slot_clock::SlotClock;
use crate::utils::config;
use crate::utils::{config, types::*};
use alloy::{
contract::EventPoller,
network::{Ethereum, EthereumWallet, NetworkWallet},
Expand Down Expand Up @@ -48,20 +48,9 @@ pub struct AvsContractAddresses {
pub preconf_registry: Address,
}

pub struct Validator {
// Preconfer that the validator proposer blocks for
pub preconfer: [u8; 20],
// Timestamp at which the preconfer may start proposing for the preconfer
// 2 epochs from validator addition timestamp
pub start_proposing_at: u64,
// Timestamp at which the preconfer must stop proposing for the preconfer
// 2 epochs from validator removal timestamp
pub stop_proposing_at: u64,
}

pub struct LookaheadSetParam {
pub timestamp: u64,
pub preconfer: [u8; 20],
pub _timestamp: u64,
pub preconfer: PreconferAddress,
}

impl From<&PreconfTaskManager::LookaheadSetParam> for LookaheadSetParam {
Expand All @@ -74,7 +63,7 @@ impl From<&PreconfTaskManager::LookaheadSetParam> for LookaheadSetParam {
});

Self {
timestamp,
_timestamp: timestamp,
preconfer: param.preconfer.into_array(),
}
}
Expand Down Expand Up @@ -359,28 +348,6 @@ impl ExecutionLayer {
Ok(())
}

pub async fn get_validator(&self, pubkey: &[u8]) -> Result<Validator, Error> {
let provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(self.wallet.clone())
.on_http(self.rpc_url.clone());
let preconf_registry =
PreconfRegistry::new(self.contract_addresses.avs.preconf_registry, provider);

let pubkey: [u8; 32] = pubkey[..32].try_into()?;

let validator = preconf_registry
.getValidator(FixedBytes::from(pubkey))
.call()
.await?;

Ok(Validator {
preconfer: validator._0.preconfer.into_array(),
start_proposing_at: validator._0.startProposingAt,
stop_proposing_at: validator._0.stopProposingAt,
})
}

pub async fn watch_for_registered_event(
&self,
) -> Result<
Expand Down Expand Up @@ -430,8 +397,8 @@ impl ExecutionLayer {

pub async fn get_lookahead_params_for_epoch(
&self,
epoch_timestamp: u64,
validator_bls_pub_keys: &[[u8; 48]; 32],
epoch_begin_timestamp: u64,
validator_bls_pub_keys: &[BLSCompressedPublicKey; 32],
) -> Result<Vec<LookaheadSetParam>, Error> {
let provider = ProviderBuilder::new()
.with_recommended_fillers()
Expand All @@ -442,7 +409,7 @@ impl ExecutionLayer {

let params = contract
.getLookaheadParamsForEpoch(
U256::from(epoch_timestamp),
U256::from(epoch_begin_timestamp),
validator_bls_pub_keys.map(|key| Bytes::from(key)),
)
.call()
Expand All @@ -452,6 +419,23 @@ impl ExecutionLayer {
Ok(params.iter().map(|param| param.into()).collect::<Vec<_>>())
}

pub async fn is_lookahead_required(&self, epoch_begin_timestamp: u64) -> Result<bool, Error> {
let provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(self.wallet.clone())
.on_http(self.rpc_url.clone());

let contract =
PreconfTaskManager::new(self.contract_addresses.avs.preconf_task_manager, provider);

let is_required = contract
.isLookaheadRequired(U256::from(epoch_begin_timestamp))
.call()
.await?;

Ok(is_required._0)
}

#[cfg(test)]
pub fn new_from_pk(
rpc_url: reqwest::Url,
Expand Down
10 changes: 10 additions & 0 deletions Node/src/ethereum_l1/slot_clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ impl SlotClock {
}
}

pub fn get_slots_per_epoch(&self) -> u64 {
self.slots_per_epoch
}

fn duration_to_next_slot(&self) -> Result<Duration, Error> {
let now = SystemTime::now().duration_since(UNIX_EPOCH)?;
self.duration_to_next_slot_from(now)
Expand Down Expand Up @@ -101,6 +105,12 @@ impl SlotClock {
let slot = self.slot_of(now)?;
Ok(slot / self.slots_per_epoch)
}

pub fn get_epoch_begin_timestamp(&self, epoch: Epoch) -> Result<u64, Error> {
let slot = epoch * self.slots_per_epoch;
let start_of_slot = self.start_of(slot)?;
Ok(start_of_slot.as_secs())
}
}

#[cfg(test)]
Expand Down
9 changes: 7 additions & 2 deletions Node/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,12 @@ impl Node {

self.operator = Operator::new(self.ethereum_l1.clone());
self.operator
.find_slots_to_preconfirm(&self.lookahead)
.find_slots_to_preconfirm(
self.ethereum_l1
.slot_clock
.get_epoch_begin_timestamp(current_epoch)?,
&self.lookahead,
)
.await?;

self.lookahead = self
Expand All @@ -251,7 +256,7 @@ impl Node {

let current_slot = self.ethereum_l1.slot_clock.get_current_slot()?;

match self.operator.get_status(current_slot) {
match self.operator.get_status(current_slot)? {
OperatorStatus::PreconferAndProposer => {
// TODO: replace with mev-boost forced inclusion list
self.preconfirm_block().await?;
Expand Down
128 changes: 57 additions & 71 deletions Node/src/node/operator.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use crate::ethereum_l1::{slot_clock::Slot, EthereumL1};
use crate::{
ethereum_l1::{execution_layer::LookaheadSetParam, slot_clock::Slot, EthereumL1},
utils::types::*,
};
use anyhow::Error;
use beacon_api_client::ProposerDuty;
use std::sync::Arc;

pub struct Operator {
ethereum_l1: Arc<EthereumL1>,
first_slot_to_preconf: Option<Slot>,
final_slot_to_preconf: Option<Slot>,
should_post_lookahead_for_next_epoch: bool,
lookahead_params: Vec<LookaheadSetParam>,
l1_slots_per_epoch: u64,
}

pub enum Status {
Expand All @@ -18,26 +21,40 @@ pub enum Status {

impl Operator {
pub fn new(ethereum_l1: Arc<EthereumL1>) -> Self {
let l1_slots_per_epoch = ethereum_l1.slot_clock.get_slots_per_epoch();
Self {
ethereum_l1,
first_slot_to_preconf: None,
final_slot_to_preconf: None,
should_post_lookahead_for_next_epoch: false,
lookahead_params: vec![],
l1_slots_per_epoch,
}
}

pub fn get_status(&self, slot: Slot) -> Status {
if let (Some(first_slot), Some(final_slot)) =
(self.first_slot_to_preconf, self.final_slot_to_preconf)
pub fn get_status(&self, slot: Slot) -> Result<Status, Error> {
if self.lookahead_params.len() < self.l1_slots_per_epoch as usize {
return Err(anyhow::anyhow!(
"Operator::get_status: Not enough lookahead params"
));
}

let slot = slot % self.l1_slots_per_epoch;

if self.lookahead_params[slot as usize].preconfer
== self.ethereum_l1.execution_layer.get_preconfer_address()
{
if slot == final_slot {
return Status::PreconferAndProposer;
}
if slot >= first_slot && slot < final_slot {
return Status::Preconfer;
if self.is_the_final_slot_to_preconf(slot) {
return Ok(Status::PreconferAndProposer);
}
return Ok(Status::Preconfer);
}
Status::None

Ok(Status::None)
}

fn is_the_final_slot_to_preconf(&self, slot_mod_slots_per_epoch: Slot) -> bool {
slot_mod_slots_per_epoch == self.l1_slots_per_epoch - 1
|| self.lookahead_params[(slot_mod_slots_per_epoch + 1) as usize].preconfer
!= self.ethereum_l1.execution_layer.get_preconfer_address()
}

pub fn should_post_lookahead(&self) -> bool {
Expand All @@ -46,72 +63,41 @@ impl Operator {

pub async fn find_slots_to_preconfirm(
&mut self,
epoch_begin_timestamp: u64,
lookahead: &[ProposerDuty],
) -> Result<(), Error> {
let first_duty = if let Some(duty) = lookahead.first() {
duty
} else {
tracing::error!("Empty lookahead");
return Ok(());
};
let mut first_slot_to_preconf = first_duty.slot;
let mut first_preconfer = true;
let mut preconfer_found = false;
let preconfer_address = self.ethereum_l1.execution_layer.get_preconfer_address();
self.should_post_lookahead_for_next_epoch = false;

for duty in lookahead {
if let Some(preconfer) = self
.get_preconfer_for_the_slot(
duty,
self.ethereum_l1.slot_clock.start_of(duty.slot)?.as_secs(),
)
.await?
{
if preconfer == preconfer_address {
self.first_slot_to_preconf = Some(first_slot_to_preconf);
self.final_slot_to_preconf = Some(duty.slot);
if first_preconfer {
self.should_post_lookahead_for_next_epoch = true;
}
return Ok(());
}
first_preconfer = false;
first_slot_to_preconf = duty.slot + 1;
preconfer_found = true;
}
if lookahead.len() != self.l1_slots_per_epoch as usize {
return Err(anyhow::anyhow!(
"Operator::find_slots_to_preconfirm: unexpected number of proposer duties in the lookahead"
));
}

// no preconfers in the current epoch
if !preconfer_found {
// TODO: ask the contract for the randomly chosen preconfer for whole epoch
}
let slots = self.l1_slots_per_epoch as usize;
let validator_bls_pub_keys: Vec<BLSCompressedPublicKey> = lookahead
.iter()
.take(slots)
.map(|key| {
let mut array = [0u8; 48];
array.copy_from_slice(&key.public_key);
array
})
.collect();

Ok(())
}

async fn get_preconfer_for_the_slot(
&self,
duty: &ProposerDuty,
slot_begin_timestamp: u64,
) -> Result<Option<[u8; 20]>, Error> {
let validator = self
self.lookahead_params = self
.ethereum_l1
.execution_layer
.get_validator(&duty.public_key.to_vec())
.get_lookahead_params_for_epoch(
epoch_begin_timestamp,
validator_bls_pub_keys.as_slice().try_into()?,
)
.await?;

if validator.preconfer == [0u8; 20] {
return Ok(None);
}

if slot_begin_timestamp < validator.start_proposing_at
|| (validator.stop_proposing_at != 0
&& slot_begin_timestamp > validator.stop_proposing_at)
{
return Ok(None);
}
self.should_post_lookahead_for_next_epoch = self
.ethereum_l1
.execution_layer
.is_lookahead_required(epoch_begin_timestamp)
.await?;

Ok(Some(validator.preconfer))
Ok(())
}
}
1 change: 1 addition & 0 deletions Node/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ pub mod preconfirmation_message;
pub mod preconfirmation_proof;
pub mod rpc_client;
pub mod rpc_server;
pub mod types;
3 changes: 2 additions & 1 deletion Node/src/utils/preconfirmation_proof.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use crate::utils::types::ECDSASignature;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PreconfirmationProof {
pub commit_hash: [u8; 32],
#[serde(with = "serde_bytes")]
pub signature: [u8; 65], // ECDSA 65 bytes signature
pub signature: ECDSASignature,
}

impl From<PreconfirmationProof> for Vec<u8> {
Expand Down
7 changes: 7 additions & 0 deletions Node/src/utils/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub type PreconferAddress = [u8; 20];
pub type ECDSASignature = [u8; 65]; // ECDSA 65 bytes signature
pub type BLSCompressedPublicKey = [u8; 48];

// TODO for future usage
// pub type BLSUncompressedPublicKey = [u8; 96];
// pub type BLSSignature = [u8; 96];

0 comments on commit 83924b5

Please sign in to comment.