From a29cadd8fb7d44748c16bbbea70bea84b9497377 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Mon, 19 Aug 2024 16:20:45 +0200 Subject: [PATCH 1/7] skip epochs --- Node/src/main.rs | 1 + Node/src/node/mod.rs | 12 +++++++++++- Node/src/utils/config.rs | 9 +++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/Node/src/main.rs b/Node/src/main.rs index d506b48..f36b66b 100644 --- a/Node/src/main.rs +++ b/Node/src/main.rs @@ -52,6 +52,7 @@ async fn main() -> Result<(), Error> { mev_boost, config.l2_slot_duration_sec, config.validator_bls_pubkey, + config.epochs_to_skip_at_beginning, ) .await?; node.entrypoint().await?; diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index 88e4fae..2da6414 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -29,6 +29,7 @@ pub struct Node { ethereum_l1: Arc, _mev_boost: MevBoost, // temporary unused epoch: Epoch, + epochs_to_skip: Epoch, lookahead: Vec, l2_slot_duration_sec: u64, validator_pubkey: String, @@ -47,7 +48,9 @@ impl Node { mev_boost: MevBoost, l2_slot_duration_sec: u64, validator_pubkey: String, + epochs_to_skip_at_beginning: Epoch, ) -> Result { + let current_epoch = ethereum_l1.slot_clock.get_current_epoch()?; Ok(Self { taiko, node_rx: Some(node_rx), @@ -56,7 +59,8 @@ impl Node { gas_used: 0, ethereum_l1, _mev_boost: mev_boost, - epoch: Epoch::MAX, // it'll be updated in the first preconfirmation loop + epoch: current_epoch, + epochs_to_skip: epochs_to_skip_at_beginning, lookahead: vec![], l2_slot_duration_sec, validator_pubkey, @@ -169,6 +173,12 @@ impl Node { current_epoch ); self.epoch = current_epoch; + if self.epochs_to_skip > 0 { + self.epochs_to_skip = self.epochs_to_skip - 1; + tracing::info!("Preconfirmation skipped at the beginning"); + return Ok(()); + } + self.current_slot_to_preconf = self.next_slot_to_preconf; self.lookahead = self .ethereum_l1 diff --git a/Node/src/utils/config.rs b/Node/src/utils/config.rs index 1e4f7c9..1b1a807 100644 --- a/Node/src/utils/config.rs +++ b/Node/src/utils/config.rs @@ -16,6 +16,7 @@ pub struct Config { pub preconf_registry_expiry_sec: u64, pub contract_addresses: ContractAddresses, pub p2p_network_config: P2PNetworkConfig, + pub epochs_to_skip_at_beginning: u64, } #[derive(Debug)] @@ -185,6 +186,11 @@ impl Config { None }; + let epochs_to_skip_at_beginning = std::env::var("EPOCHS_TO_SKIP_AT_BEGINNING") + .unwrap_or("2".to_string()) + .parse::() + .expect("EPOCHS_TO_SKIP_AT_BEGINNING must be a number"); + // Create P2P network config let p2p_network_config: P2PNetworkConfig = P2PNetworkConfig { local_key: generate_secp256k1(), @@ -214,6 +220,7 @@ impl Config { preconf_registry_expiry_sec, contract_addresses, p2p_network_config, + epochs_to_skip_at_beginning, }; info!( @@ -229,6 +236,7 @@ L2 slot duration: {} Validator pubkey: {} Block proposed receiver timeout: {} Preconf registry expiry seconds: {} +Epochs to skip at the beginning: {} Contract addresses: {:#?} p2p_network_config: {} "#, @@ -242,6 +250,7 @@ p2p_network_config: {} config.validator_bls_pubkey, config.block_proposed_receiver_timeout_sec, config.preconf_registry_expiry_sec, + config.epochs_to_skip_at_beginning, config.contract_addresses, config.p2p_network_config, ); From 1b3b32d517c4f06f3f6f511f5c32efe9b0fc8c7f Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Tue, 20 Aug 2024 16:51:00 +0200 Subject: [PATCH 2/7] Check first and final slots to preconfirm --- Node/src/ethereum_l1/abi/PreconfRegistry.json | 2 +- Node/src/ethereum_l1/execution_layer.rs | 37 ++++++++++ Node/src/node/mod.rs | 71 +++++++++++++------ 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/Node/src/ethereum_l1/abi/PreconfRegistry.json b/Node/src/ethereum_l1/abi/PreconfRegistry.json index 09f2d58..c3335f2 100644 --- a/Node/src/ethereum_l1/abi/PreconfRegistry.json +++ b/Node/src/ethereum_l1/abi/PreconfRegistry.json @@ -1 +1 @@ -[{"type":"constructor","inputs":[{"name":"_preconfServiceManager","type":"address","internalType":"contract IServiceManager"}],"stateMutability":"nonpayable"},{"type":"function","name":"addValidators","inputs":[{"name":"pubkeys","type":"tuple[]","internalType":"struct BLS12381.G1Point[]","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatures","type":"tuple[]","internalType":"struct BLS12381.G2Point[]","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"deregisterPreconfer","inputs":[{"name":"lastIndexWitness","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"preconferToIndex","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"preconferToNonce","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"preconferToPubKeyHashToTimestamp","inputs":[{"name":"","type":"address","internalType":"address"},{"name":"","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"registerPreconfer","inputs":[{"name":"operatorSignature","type":"tuple","internalType":"struct ISignatureUtils.SignatureWithSaltAndExpiry","components":[{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"salt","type":"bytes32","internalType":"bytes32"},{"name":"expiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeValidators","inputs":[{"name":"validatorPubKeyHashes","type":"bytes32[]","internalType":"bytes32[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"PreconferDeregistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"PreconferRegistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"index","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ValidatorAdded","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"compressedPubKey","type":"uint256[2]","indexed":false,"internalType":"uint256[2]"}],"anonymous":false},{"type":"event","name":"ValidatorRemoved","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"validatorPubKeyHash","type":"bytes32","indexed":false,"internalType":"bytes32"}],"anonymous":false},{"type":"error","name":"ArrayLengthMismatch","inputs":[]},{"type":"error","name":"InvalidValidatorPubKeyHash","inputs":[]},{"type":"error","name":"InvalidValidatorSignature","inputs":[]},{"type":"error","name":"LastIndexWitnessIncorrect","inputs":[]},{"type":"error","name":"PreconferAlreadyRegistered","inputs":[]},{"type":"error","name":"PreconferNotRegistered","inputs":[]}] \ No newline at end of file +[{"type":"constructor","inputs":[{"name":"_preconfServiceManager","type":"address","internalType":"contract IServiceManager"}],"stateMutability":"nonpayable"},{"type":"function","name":"addValidators","inputs":[{"name":"addValidatorParams","type":"tuple[]","internalType":"struct IPreconfRegistry.AddValidatorParam[]","components":[{"name":"pubkey","type":"tuple","internalType":"struct BLS12381.G1Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signature","type":"tuple","internalType":"struct BLS12381.G2Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatureExpiry","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"deregisterPreconfer","inputs":[{"name":"lastIndexWitness","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getMessageToSign","inputs":[{"name":"validatorOp","type":"uint8","internalType":"enum IPreconfRegistry.ValidatorOp"},{"name":"expiry","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getNextPreconferIndex","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getPreconferIndex","inputs":[{"name":"preconfer","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getValidator","inputs":[{"name":"pubKeyHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"tuple","internalType":"struct IPreconfRegistry.Validator","components":[{"name":"preconfer","type":"address","internalType":"address"},{"name":"startProposingAt","type":"uint40","internalType":"uint40"},{"name":"stopProposingAt","type":"uint40","internalType":"uint40"}]}],"stateMutability":"view"},{"type":"function","name":"registerPreconfer","inputs":[{"name":"operatorSignature","type":"tuple","internalType":"struct ISignatureUtils.SignatureWithSaltAndExpiry","components":[{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"salt","type":"bytes32","internalType":"bytes32"},{"name":"expiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeValidators","inputs":[{"name":"removeValidatorParams","type":"tuple[]","internalType":"struct IPreconfRegistry.RemoveValidatorParam[]","components":[{"name":"pubkey","type":"tuple","internalType":"struct BLS12381.G1Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signature","type":"tuple","internalType":"struct BLS12381.G2Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatureExpiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"PreconferDeregistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"PreconferRegistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"index","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ValidatorAdded","inputs":[{"name":"pubKeyHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ValidatorRemoved","inputs":[{"name":"pubKeyHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"InvalidValidatorSignature","inputs":[]},{"type":"error","name":"LastIndexWitnessIncorrect","inputs":[]},{"type":"error","name":"PreconferAlreadyRegistered","inputs":[]},{"type":"error","name":"PreconferNotRegistered","inputs":[]},{"type":"error","name":"ValidatorAlreadyActive","inputs":[]},{"type":"error","name":"ValidatorAlreadyInactive","inputs":[]},{"type":"error","name":"ValidatorSignatureExpired","inputs":[]}] \ No newline at end of file diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index f68a4c0..87b7afe 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -46,6 +46,17 @@ 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 startProposingAt: u64, + // Timestamp at which the preconfer must stop proposing for the preconfer + // 2 epochs from validator removal timestamp + pub stopProposingAt: u64, +} + sol!( #[allow(clippy::too_many_arguments)] #[allow(missing_docs)] @@ -132,6 +143,10 @@ impl ExecutionLayer { }) } + pub fn get_avs_node_address(&self) -> [u8; 20] { + self.avs_node_address.into_array() + } + fn parse_contract_addresses( contract_addresses: &config::ContractAddresses, ) -> Result { @@ -307,6 +322,28 @@ impl ExecutionLayer { Ok(()) } + pub async fn get_validator(&self, pubkey: &[u8]) -> Result { + 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(), + startProposingAt: validator._0.startProposingAt, + stopProposingAt: validator._0.stopProposingAt, + }) + } + #[cfg(test)] pub fn new_from_pk( rpc_url: reqwest::Url, diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index 2da6414..a6aa538 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -33,9 +33,10 @@ pub struct Node { lookahead: Vec, l2_slot_duration_sec: u64, validator_pubkey: String, - current_slot_to_preconf: Option, - next_slot_to_preconf: Option, + current_first_slot_to_preconf: Option, + current_final_slot_to_preconf: Option, preconfirmed_blocks: Arc>>, + should_post_lookahead_for_next_epoch: bool, } impl Node { @@ -64,9 +65,10 @@ impl Node { lookahead: vec![], l2_slot_duration_sec, validator_pubkey, - current_slot_to_preconf: None, - next_slot_to_preconf: None, + current_first_slot_to_preconf: None, + current_final_slot_to_preconf: None, preconfirmed_blocks: Arc::new(Mutex::new(HashMap::new())), + should_post_lookahead_for_next_epoch: false, }) } @@ -179,24 +181,60 @@ impl Node { return Ok(()); } - self.current_slot_to_preconf = self.next_slot_to_preconf; + self.find_slots_to_preconfirm().await?; self.lookahead = self .ethereum_l1 .consensus_layer .get_lookahead(self.epoch + 1) .await?; - self.next_slot_to_preconf = self.check_for_the_slot_to_preconf(&self.lookahead); + + if self.should_post_lookahead_for_next_epoch { + // TODO: post lookahead + self.should_post_lookahead_for_next_epoch = false; + } } - if let Some(slot) = self.current_slot_to_preconf { - if slot == self.ethereum_l1.slot_clock.get_current_slot()? { + let current_slot = self.ethereum_l1.slot_clock.get_current_slot()?; + + if let (Some(first_slot), Some(final_slot)) = ( + self.current_first_slot_to_preconf, + self.current_final_slot_to_preconf, + ) { + if current_slot >= first_slot && current_slot <= final_slot { self.preconfirm_block().await?; } } else { - tracing::debug!( - "Not my slot to preconfirm: {}", - self.ethereum_l1.slot_clock.get_current_slot()? - ); + tracing::debug!("Not my slot to preconfirm: {}", current_slot); + } + + Ok(()) + } + + async fn find_slots_to_preconfirm(&mut self) -> Result<(), Error> { + let first_duty = if let Some(duty) = self.lookahead.first() { + duty + } else { + tracing::error!("Empty lookahead"); + return Ok(()); + }; + let mut first_slot_to_preconf = first_duty.slot; + let avs_node_address = self.ethereum_l1.execution_layer.get_avs_node_address(); + self.should_post_lookahead_for_next_epoch = true; + + for duty in self.lookahead.iter() { + let validator = self + .ethereum_l1 + .execution_layer + .get_validator(&duty.public_key.to_vec()) + .await?; + if validator.preconfer == avs_node_address { + self.current_first_slot_to_preconf = Some(first_slot_to_preconf); + self.current_final_slot_to_preconf = Some(duty.slot); + return Ok(()); + } else { + first_slot_to_preconf = duty.slot + 1; + self.should_post_lookahead_for_next_epoch = false; + } } Ok(()) @@ -205,7 +243,7 @@ impl Node { async fn preconfirm_block(&mut self) -> Result<(), Error> { tracing::debug!( "Preconfirming for the slot: {:?}", - self.current_slot_to_preconf + self.current_final_slot_to_preconf ); let pending_tx_lists = self.taiko.get_pending_l2_tx_lists().await?; @@ -268,13 +306,6 @@ impl Node { Ok(()) } - fn check_for_the_slot_to_preconf(&self, lookahead: &[ProposerDuty]) -> Option { - lookahead - .iter() - .find(|duty| duty.public_key.to_string() == self.validator_pubkey) - .map(|duty| duty.slot) - } - async fn send_preconfirmations_to_the_avs_p2p(&self, block: Block) -> Result<(), Error> { self.node_to_p2p_tx .send(block.into()) From ad42441f9f1f97c5510431c986d60e94c4882952 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Wed, 21 Aug 2024 13:17:01 +0200 Subject: [PATCH 3/7] Operator struct for preconfirmations --- Node/src/ethereum_l1/execution_layer.rs | 13 ++- Node/src/main.rs | 1 - Node/src/node/mod.rs | 74 +++++---------- Node/src/node/operator.rs | 117 ++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 58 deletions(-) create mode 100644 Node/src/node/operator.rs diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index 87b7afe..9bd9b20 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -19,6 +19,7 @@ use rand_core::{OsRng, RngCore}; use std::str::FromStr; use std::sync::Arc; +#[allow(unused)] //TODO: remove after used in release code pub struct ExecutionLayer { rpc_url: reqwest::Url, signer: LocalSigner>, @@ -29,16 +30,19 @@ pub struct ExecutionLayer { preconf_registry_expiry_sec: u64, } +#[allow(unused)] //TODO: remove after used in release code pub struct ContractAddresses { pub eigen_layer: EigenLayerContractAddresses, pub avs: AvsContractAddresses, } +#[allow(unused)] //TODO: remove after used in release code pub struct EigenLayerContractAddresses { pub strategy_manager: Address, pub slasher: Address, } +#[allow(unused)] //TODO: remove after used in release code pub struct AvsContractAddresses { pub preconf_task_manager: Address, pub directory: Address, @@ -51,10 +55,10 @@ pub struct Validator { pub preconfer: [u8; 20], // Timestamp at which the preconfer may start proposing for the preconfer // 2 epochs from validator addition timestamp - pub startProposingAt: u64, + pub start_proposing_at: u64, // Timestamp at which the preconfer must stop proposing for the preconfer // 2 epochs from validator removal timestamp - pub stopProposingAt: u64, + pub stop_proposing_at: u64, } sol!( @@ -113,6 +117,7 @@ sol!( "src/ethereum_l1/abi/PreconfRegistry.json" ); +#[allow(dead_code)] //TODO: remove after used in release code impl ExecutionLayer { pub fn new( rpc_url: &str, @@ -339,8 +344,8 @@ impl ExecutionLayer { Ok(Validator { preconfer: validator._0.preconfer.into_array(), - startProposingAt: validator._0.startProposingAt, - stopProposingAt: validator._0.stopProposingAt, + start_proposing_at: validator._0.startProposingAt, + stop_proposing_at: validator._0.stopProposingAt, }) } diff --git a/Node/src/main.rs b/Node/src/main.rs index f36b66b..1258c00 100644 --- a/Node/src/main.rs +++ b/Node/src/main.rs @@ -51,7 +51,6 @@ async fn main() -> Result<(), Error> { ethereum_l1, mev_boost, config.l2_slot_duration_sec, - config.validator_bls_pubkey, config.epochs_to_skip_at_beginning, ) .await?; diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index a6aa538..9629317 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -1,14 +1,12 @@ use crate::{ - ethereum_l1::{ - slot_clock::{Epoch, Slot}, - EthereumL1, - }, + ethereum_l1::{slot_clock::Epoch, EthereumL1}, mev_boost::MevBoost, taiko::{l2_tx_lists::RPCReplyL2TxLists, Taiko}, utils::{block::Block, block_proposed::BlockProposed, commit::L2TxListsCommit}, }; use anyhow::{anyhow as any_err, Error}; use beacon_api_client::ProposerDuty; +use operator::{Operator, Status as OperatorStatus}; use std::{collections::HashMap, sync::Arc}; use tokio::sync::{ mpsc::{Receiver, Sender}, @@ -17,6 +15,7 @@ use tokio::sync::{ use tracing::info; pub mod block_proposed_receiver; +mod operator; const OLDEST_BLOCK_DISTANCE: u64 = 256; @@ -32,11 +31,8 @@ pub struct Node { epochs_to_skip: Epoch, lookahead: Vec, l2_slot_duration_sec: u64, - validator_pubkey: String, - current_first_slot_to_preconf: Option, - current_final_slot_to_preconf: Option, preconfirmed_blocks: Arc>>, - should_post_lookahead_for_next_epoch: bool, + operator: Operator, } impl Node { @@ -48,10 +44,10 @@ impl Node { ethereum_l1: Arc, mev_boost: MevBoost, l2_slot_duration_sec: u64, - validator_pubkey: String, epochs_to_skip_at_beginning: Epoch, ) -> Result { let current_epoch = ethereum_l1.slot_clock.get_current_epoch()?; + let operator = Operator::new(ethereum_l1.clone()); Ok(Self { taiko, node_rx: Some(node_rx), @@ -64,11 +60,8 @@ impl Node { epochs_to_skip: epochs_to_skip_at_beginning, lookahead: vec![], l2_slot_duration_sec, - validator_pubkey, - current_first_slot_to_preconf: None, - current_final_slot_to_preconf: None, preconfirmed_blocks: Arc::new(Mutex::new(HashMap::new())), - should_post_lookahead_for_next_epoch: false, + operator, }) } @@ -181,59 +174,34 @@ impl Node { return Ok(()); } - self.find_slots_to_preconfirm().await?; + self.operator = Operator::new(self.ethereum_l1.clone()); + self.operator + .find_slots_to_preconfirm(&self.lookahead) + .await?; + self.lookahead = self .ethereum_l1 .consensus_layer .get_lookahead(self.epoch + 1) .await?; - if self.should_post_lookahead_for_next_epoch { + if self.operator.should_post_lookahead() { // TODO: post lookahead - self.should_post_lookahead_for_next_epoch = false; } } let current_slot = self.ethereum_l1.slot_clock.get_current_slot()?; - if let (Some(first_slot), Some(final_slot)) = ( - self.current_first_slot_to_preconf, - self.current_final_slot_to_preconf, - ) { - if current_slot >= first_slot && current_slot <= final_slot { + match self.operator.get_status(current_slot) { + OperatorStatus::PreconferAndProposer => { + // TODO: replace with mev-boost forced inclusion list self.preconfirm_block().await?; } - } else { - tracing::debug!("Not my slot to preconfirm: {}", current_slot); - } - - Ok(()) - } - - async fn find_slots_to_preconfirm(&mut self) -> Result<(), Error> { - let first_duty = if let Some(duty) = self.lookahead.first() { - duty - } else { - tracing::error!("Empty lookahead"); - return Ok(()); - }; - let mut first_slot_to_preconf = first_duty.slot; - let avs_node_address = self.ethereum_l1.execution_layer.get_avs_node_address(); - self.should_post_lookahead_for_next_epoch = true; - - for duty in self.lookahead.iter() { - let validator = self - .ethereum_l1 - .execution_layer - .get_validator(&duty.public_key.to_vec()) - .await?; - if validator.preconfer == avs_node_address { - self.current_first_slot_to_preconf = Some(first_slot_to_preconf); - self.current_final_slot_to_preconf = Some(duty.slot); - return Ok(()); - } else { - first_slot_to_preconf = duty.slot + 1; - self.should_post_lookahead_for_next_epoch = false; + OperatorStatus::Preconfer => { + self.preconfirm_block().await?; + } + OperatorStatus::None => { + tracing::debug!("Not my slot to preconfirm: {}", current_slot); } } @@ -243,7 +211,7 @@ impl Node { async fn preconfirm_block(&mut self) -> Result<(), Error> { tracing::debug!( "Preconfirming for the slot: {:?}", - self.current_final_slot_to_preconf + self.ethereum_l1.slot_clock.get_current_slot()? ); let pending_tx_lists = self.taiko.get_pending_l2_tx_lists().await?; diff --git a/Node/src/node/operator.rs b/Node/src/node/operator.rs new file mode 100644 index 0000000..4536fba --- /dev/null +++ b/Node/src/node/operator.rs @@ -0,0 +1,117 @@ +use crate::ethereum_l1::{slot_clock::Slot, EthereumL1}; +use anyhow::Error; +use beacon_api_client::ProposerDuty; +use std::sync::Arc; + +pub struct Operator { + ethereum_l1: Arc, + first_slot_to_preconf: Option, + final_slot_to_preconf: Option, + should_post_lookahead_for_next_epoch: bool, +} + +pub enum Status { + None, + Preconfer, + PreconferAndProposer, // has to force include transactions +} + +impl Operator { + pub fn new(ethereum_l1: Arc) -> Self { + Self { + ethereum_l1, + first_slot_to_preconf: None, + final_slot_to_preconf: None, + should_post_lookahead_for_next_epoch: false, + } + } + + 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) + { + if slot == final_slot { + return Status::PreconferAndProposer; + } + if slot >= first_slot && slot < final_slot { + return Status::Preconfer; + } + } + Status::None + } + + pub fn should_post_lookahead(&self) -> bool { + self.should_post_lookahead_for_next_epoch + } + + pub async fn find_slots_to_preconfirm( + &mut self, + 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 avs_node_address = self.ethereum_l1.execution_layer.get_avs_node_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 == avs_node_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; + } + } + + // no preconfers in the current epoch + if !preconfer_found { + // TODO: ask the contract for the randomly chosen preconfer for whole epoch + } + + Ok(()) + } + + async fn get_preconfer_for_the_slot( + &self, + duty: &ProposerDuty, + slot_begin_timestamp: u64, + ) -> Result, Error> { + let validator = self + .ethereum_l1 + .execution_layer + .get_validator(&duty.public_key.to_vec()) + .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); + } + + Ok(Some(validator.preconfer)) + } +} From 20c2d0ffb32ec73469896f6a2cffba5ae92c6aa9 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Fri, 23 Aug 2024 11:29:33 +0200 Subject: [PATCH 4/7] new ABI for task manager and preconf registry --- Node/src/ethereum_l1/abi/PreconfRegistry.json | 2 +- Node/src/ethereum_l1/abi/PreconfTaskManager.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Node/src/ethereum_l1/abi/PreconfRegistry.json b/Node/src/ethereum_l1/abi/PreconfRegistry.json index c3335f2..965fd55 100644 --- a/Node/src/ethereum_l1/abi/PreconfRegistry.json +++ b/Node/src/ethereum_l1/abi/PreconfRegistry.json @@ -1 +1 @@ -[{"type":"constructor","inputs":[{"name":"_preconfServiceManager","type":"address","internalType":"contract IServiceManager"}],"stateMutability":"nonpayable"},{"type":"function","name":"addValidators","inputs":[{"name":"addValidatorParams","type":"tuple[]","internalType":"struct IPreconfRegistry.AddValidatorParam[]","components":[{"name":"pubkey","type":"tuple","internalType":"struct BLS12381.G1Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signature","type":"tuple","internalType":"struct BLS12381.G2Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatureExpiry","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"deregisterPreconfer","inputs":[{"name":"lastIndexWitness","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getMessageToSign","inputs":[{"name":"validatorOp","type":"uint8","internalType":"enum IPreconfRegistry.ValidatorOp"},{"name":"expiry","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getNextPreconferIndex","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getPreconferIndex","inputs":[{"name":"preconfer","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getValidator","inputs":[{"name":"pubKeyHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"tuple","internalType":"struct IPreconfRegistry.Validator","components":[{"name":"preconfer","type":"address","internalType":"address"},{"name":"startProposingAt","type":"uint40","internalType":"uint40"},{"name":"stopProposingAt","type":"uint40","internalType":"uint40"}]}],"stateMutability":"view"},{"type":"function","name":"registerPreconfer","inputs":[{"name":"operatorSignature","type":"tuple","internalType":"struct ISignatureUtils.SignatureWithSaltAndExpiry","components":[{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"salt","type":"bytes32","internalType":"bytes32"},{"name":"expiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeValidators","inputs":[{"name":"removeValidatorParams","type":"tuple[]","internalType":"struct IPreconfRegistry.RemoveValidatorParam[]","components":[{"name":"pubkey","type":"tuple","internalType":"struct BLS12381.G1Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signature","type":"tuple","internalType":"struct BLS12381.G2Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatureExpiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"PreconferDeregistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"PreconferRegistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"index","type":"uint256","indexed":true,"internalType":"uint256"}],"anonymous":false},{"type":"event","name":"ValidatorAdded","inputs":[{"name":"pubKeyHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ValidatorRemoved","inputs":[{"name":"pubKeyHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"InvalidValidatorSignature","inputs":[]},{"type":"error","name":"LastIndexWitnessIncorrect","inputs":[]},{"type":"error","name":"PreconferAlreadyRegistered","inputs":[]},{"type":"error","name":"PreconferNotRegistered","inputs":[]},{"type":"error","name":"ValidatorAlreadyActive","inputs":[]},{"type":"error","name":"ValidatorAlreadyInactive","inputs":[]},{"type":"error","name":"ValidatorSignatureExpired","inputs":[]}] \ No newline at end of file +[{"type":"constructor","inputs":[{"name":"_preconfServiceManager","type":"address","internalType":"contract IServiceManager"}],"stateMutability":"nonpayable"},{"type":"function","name":"addValidators","inputs":[{"name":"addValidatorParams","type":"tuple[]","internalType":"struct IPreconfRegistry.AddValidatorParam[]","components":[{"name":"pubkey","type":"tuple","internalType":"struct BLS12381.G1Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signature","type":"tuple","internalType":"struct BLS12381.G2Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatureExpiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"deregisterPreconfer","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"getMessageToSign","inputs":[{"name":"validatorOp","type":"uint8","internalType":"enum IPreconfRegistry.ValidatorOp"},{"name":"expiry","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"bytes","internalType":"bytes"}],"stateMutability":"view"},{"type":"function","name":"getNextPreconferIndex","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getPreconferAtIndex","inputs":[{"name":"index","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getPreconferIndex","inputs":[{"name":"preconfer","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getValidator","inputs":[{"name":"pubKeyHash","type":"bytes32","internalType":"bytes32"}],"outputs":[{"name":"","type":"tuple","internalType":"struct IPreconfRegistry.Validator","components":[{"name":"preconfer","type":"address","internalType":"address"},{"name":"startProposingAt","type":"uint40","internalType":"uint40"},{"name":"stopProposingAt","type":"uint40","internalType":"uint40"}]}],"stateMutability":"view"},{"type":"function","name":"registerPreconfer","inputs":[{"name":"operatorSignature","type":"tuple","internalType":"struct ISignatureUtils.SignatureWithSaltAndExpiry","components":[{"name":"signature","type":"bytes","internalType":"bytes"},{"name":"salt","type":"bytes32","internalType":"bytes32"},{"name":"expiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"removeValidators","inputs":[{"name":"removeValidatorParams","type":"tuple[]","internalType":"struct IPreconfRegistry.RemoveValidatorParam[]","components":[{"name":"pubkey","type":"tuple","internalType":"struct BLS12381.G1Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signature","type":"tuple","internalType":"struct BLS12381.G2Point","components":[{"name":"x","type":"uint256[2]","internalType":"uint256[2]"},{"name":"x_I","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y","type":"uint256[2]","internalType":"uint256[2]"},{"name":"y_I","type":"uint256[2]","internalType":"uint256[2]"}]},{"name":"signatureExpiry","type":"uint256","internalType":"uint256"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"PreconferDeregistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"PreconferRegistered","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ValidatorAdded","inputs":[{"name":"pubKeyHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ValidatorRemoved","inputs":[{"name":"pubKeyHash","type":"bytes32","indexed":true,"internalType":"bytes32"},{"name":"preconfer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"InvalidValidatorSignature","inputs":[]},{"type":"error","name":"PreconferAlreadyRegistered","inputs":[]},{"type":"error","name":"PreconferNotRegistered","inputs":[]},{"type":"error","name":"ValidatorAlreadyActive","inputs":[]},{"type":"error","name":"ValidatorAlreadyInactive","inputs":[]},{"type":"error","name":"ValidatorSignatureExpired","inputs":[]}] \ No newline at end of file diff --git a/Node/src/ethereum_l1/abi/PreconfTaskManager.json b/Node/src/ethereum_l1/abi/PreconfTaskManager.json index 4ae6e20..2c21138 100644 --- a/Node/src/ethereum_l1/abi/PreconfTaskManager.json +++ b/Node/src/ethereum_l1/abi/PreconfTaskManager.json @@ -1 +1 @@ -[{"type":"constructor","inputs":[{"name":"_serviceManager","type":"address","internalType":"contract IPreconfServiceManager"},{"name":"_registryCoordinator","type":"address","internalType":"contract IRegistryCoordinator"},{"name":"_indexRegistry","type":"address","internalType":"contract IIndexRegistry"},{"name":"_taikoL1","type":"address","internalType":"contract ITaikoL1"},{"name":"_beaconBlockRootContract","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"getFallbackPreconfer","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"isLookaheadRequired","inputs":[{"name":"epochTimestamp","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"newBlockProposal","inputs":[{"name":"blockParams","type":"bytes","internalType":"bytes"},{"name":"txList","type":"bytes","internalType":"bytes"},{"name":"lookaheadPointer","type":"uint256","internalType":"uint256"},{"name":"lookaheadSetParams","type":"tuple[]","internalType":"struct IPreconfTaskManager.LookaheadSetParam[]","components":[{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"proveIncorrectLookahead","inputs":[{"name":"lookaheadPointer","type":"uint256","internalType":"uint256"},{"name":"slotTimestamp","type":"uint256","internalType":"uint256"},{"name":"expectedValidator","type":"bytes32[8]","internalType":"bytes32[8]"},{"name":"expectedValidatorIndex","type":"uint256","internalType":"uint256"},{"name":"expectedValidatorProof","type":"bytes32[]","internalType":"bytes32[]"},{"name":"actualValidatorIndex","type":"uint256","internalType":"uint256"},{"name":"validatorsRoot","type":"bytes32","internalType":"bytes32"},{"name":"nr_validators","type":"uint256","internalType":"uint256"},{"name":"beaconStateProof","type":"bytes32[]","internalType":"bytes32[]"},{"name":"beaconStateRoot","type":"bytes32","internalType":"bytes32"},{"name":"beaconBlockProofForState","type":"bytes32[]","internalType":"bytes32[]"},{"name":"beaconBlockProofForProposerIndex","type":"bytes32[]","internalType":"bytes32[]"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"proveIncorrectPreconfirmation","inputs":[{"name":"header","type":"tuple","internalType":"struct IPreconfTaskManager.PreconfirmationHeader","components":[{"name":"blockId","type":"uint256","internalType":"uint256"},{"name":"chainId","type":"uint256","internalType":"uint256"},{"name":"txListHash","type":"bytes32","internalType":"bytes32"}]},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"registerConsensusBLSPubKeyHashTreeRoot","inputs":[{"name":"consensusBLSPubKeyHashTreeRoot","type":"bytes32","internalType":"bytes32"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"LookaheadUpdated","inputs":[{"name":"","type":"tuple[]","indexed":false,"internalType":"struct IPreconfTaskManager.LookaheadSetParam[]","components":[{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"anonymous":false},{"type":"event","name":"ProvedIncorrectLookahead","inputs":[{"name":"poster","type":"address","indexed":true,"internalType":"address"},{"name":"timestamp","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"disputer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ProvedIncorrectPreconfirmation","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"blockId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"disputer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"BeaconBlockProofForProposerIndex","inputs":[]},{"type":"error","name":"BeaconBlockProofForStateFailed","inputs":[]},{"type":"error","name":"BeaconStateProofFailed","inputs":[]},{"type":"error","name":"ConsensusBLSPubKeyHashTreeRootAlreadyRegistered","inputs":[]},{"type":"error","name":"ConsensusBLSPubKeyHashTreeRootNotRegistered","inputs":[]},{"type":"error","name":"EntryNotRegisteredInAVS","inputs":[]},{"type":"error","name":"ExpectedAndActualValidatorAreSame","inputs":[]},{"type":"error","name":"ExpectedValidatorIsIncorrect","inputs":[]},{"type":"error","name":"ExpectedValidatorMustNotBeSlashed","inputs":[]},{"type":"error","name":"InvalidLookaheadPointer","inputs":[]},{"type":"error","name":"InvalidSlotTimestamp","inputs":[]},{"type":"error","name":"MissedDisputeWindow","inputs":[]},{"type":"error","name":"PosterAlreadySlashedForTheEpoch","inputs":[]},{"type":"error","name":"PreconfirmationChainIdMismatch","inputs":[]},{"type":"error","name":"PreconfirmationIsCorrect","inputs":[]},{"type":"error","name":"SenderIsNotTheFallbackPreconfer","inputs":[]},{"type":"error","name":"SenderIsNotThePreconfer","inputs":[]},{"type":"error","name":"SenderNotRegisteredInAVS","inputs":[]},{"type":"error","name":"ValidatorProofFailed","inputs":[]}] +[{"type":"constructor","inputs":[{"name":"_serviceManager","type":"address","internalType":"contract IPreconfServiceManager"},{"name":"_registry","type":"address","internalType":"contract IPreconfRegistry"},{"name":"_taikoL1","type":"address","internalType":"contract ITaikoL1"},{"name":"_beaconBlockRootContract","type":"address","internalType":"address"}],"stateMutability":"nonpayable"},{"type":"function","name":"getFallbackPreconfer","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"getLookahead","inputs":[],"outputs":[{"name":"","type":"tuple[64]","internalType":"struct IPreconfTaskManager.LookaheadEntry[64]","components":[{"name":"timestamp","type":"uint48","internalType":"uint48"},{"name":"prevTimestamp","type":"uint48","internalType":"uint48"},{"name":"preconfer","type":"address","internalType":"address"}]}],"stateMutability":"view"},{"type":"function","name":"getLookaheadParamsForEpoch","inputs":[{"name":"epochTimestamp","type":"uint256","internalType":"uint256"},{"name":"validatorBLSPubKeys","type":"bytes[32]","internalType":"bytes[32]"}],"outputs":[{"name":"","type":"tuple[]","internalType":"struct IPreconfTaskManager.LookaheadSetParam[]","components":[{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"stateMutability":"view"},{"type":"function","name":"initialize","inputs":[{"name":"_taikoToken","type":"address","internalType":"contract IERC20"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"isLookaheadRequired","inputs":[{"name":"epochTimestamp","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"bool","internalType":"bool"}],"stateMutability":"view"},{"type":"function","name":"newBlockProposal","inputs":[{"name":"blockParams","type":"bytes","internalType":"bytes"},{"name":"txList","type":"bytes","internalType":"bytes"},{"name":"lookaheadPointer","type":"uint256","internalType":"uint256"},{"name":"lookaheadSetParams","type":"tuple[]","internalType":"struct IPreconfTaskManager.LookaheadSetParam[]","components":[{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"outputs":[],"stateMutability":"payable"},{"type":"function","name":"proveIncorrectLookahead","inputs":[{"name":"lookaheadPointer","type":"uint256","internalType":"uint256"},{"name":"slotTimestamp","type":"uint256","internalType":"uint256"},{"name":"validatorBLSPubKey","type":"bytes","internalType":"bytes"},{"name":"validatorInclusionProof","type":"tuple","internalType":"struct EIP4788.InclusionProof","components":[{"name":"validator","type":"bytes32[8]","internalType":"bytes32[8]"},{"name":"validatorIndex","type":"uint256","internalType":"uint256"},{"name":"validatorProof","type":"bytes32[]","internalType":"bytes32[]"},{"name":"validatorsRoot","type":"bytes32","internalType":"bytes32"},{"name":"nr_validators","type":"uint256","internalType":"uint256"},{"name":"beaconStateProof","type":"bytes32[]","internalType":"bytes32[]"},{"name":"beaconStateRoot","type":"bytes32","internalType":"bytes32"},{"name":"beaconBlockProofForState","type":"bytes32[]","internalType":"bytes32[]"},{"name":"beaconBlockProofForProposerIndex","type":"bytes32[]","internalType":"bytes32[]"}]}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"proveIncorrectPreconfirmation","inputs":[{"name":"taikoBlockMetadata","type":"tuple","internalType":"struct ITaikoL1.BlockMetadata","components":[{"name":"l1Hash","type":"bytes32","internalType":"bytes32"},{"name":"difficulty","type":"bytes32","internalType":"bytes32"},{"name":"blobHash","type":"bytes32","internalType":"bytes32"},{"name":"extraData","type":"bytes32","internalType":"bytes32"},{"name":"depositsHash","type":"bytes32","internalType":"bytes32"},{"name":"coinbase","type":"address","internalType":"address"},{"name":"id","type":"uint64","internalType":"uint64"},{"name":"gasLimit","type":"uint32","internalType":"uint32"},{"name":"timestamp","type":"uint64","internalType":"uint64"},{"name":"l1Height","type":"uint64","internalType":"uint64"},{"name":"minTier","type":"uint16","internalType":"uint16"},{"name":"blobUsed","type":"bool","internalType":"bool"},{"name":"parentMetaHash","type":"bytes32","internalType":"bytes32"},{"name":"sender","type":"address","internalType":"address"}]},{"name":"header","type":"tuple","internalType":"struct IPreconfTaskManager.PreconfirmationHeader","components":[{"name":"blockId","type":"uint256","internalType":"uint256"},{"name":"chainId","type":"uint256","internalType":"uint256"},{"name":"txListHash","type":"bytes32","internalType":"bytes32"}]},{"name":"signature","type":"bytes","internalType":"bytes"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"Initialized","inputs":[{"name":"version","type":"uint8","indexed":false,"internalType":"uint8"}],"anonymous":false},{"type":"event","name":"LookaheadUpdated","inputs":[{"name":"","type":"tuple[]","indexed":false,"internalType":"struct IPreconfTaskManager.LookaheadSetParam[]","components":[{"name":"timestamp","type":"uint256","internalType":"uint256"},{"name":"preconfer","type":"address","internalType":"address"}]}],"anonymous":false},{"type":"event","name":"ProvedIncorrectLookahead","inputs":[{"name":"poster","type":"address","indexed":true,"internalType":"address"},{"name":"timestamp","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"disputer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"event","name":"ProvedIncorrectPreconfirmation","inputs":[{"name":"preconfer","type":"address","indexed":true,"internalType":"address"},{"name":"blockId","type":"uint256","indexed":true,"internalType":"uint256"},{"name":"disputer","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"BeaconBlockProofForProposerIndex","inputs":[]},{"type":"error","name":"BeaconBlockProofForStateFailed","inputs":[]},{"type":"error","name":"BeaconStateProofFailed","inputs":[]},{"type":"error","name":"ExpectedValidatorMustNotBeSlashed","inputs":[]},{"type":"error","name":"InvalidLookaheadPointer","inputs":[]},{"type":"error","name":"InvalidSlotTimestamp","inputs":[]},{"type":"error","name":"InvalidValidatorBLSPubKey","inputs":[]},{"type":"error","name":"LookaheadEntryIsCorrect","inputs":[]},{"type":"error","name":"MetadataMismatch","inputs":[]},{"type":"error","name":"MissedDisputeWindow","inputs":[]},{"type":"error","name":"PosterAlreadySlashedForTheEpoch","inputs":[]},{"type":"error","name":"PreconferNotRegistered","inputs":[]},{"type":"error","name":"PreconfirmationChainIdMismatch","inputs":[]},{"type":"error","name":"PreconfirmationIsCorrect","inputs":[]},{"type":"error","name":"SenderIsNotTheFallbackPreconfer","inputs":[]},{"type":"error","name":"SenderIsNotThePreconfer","inputs":[]},{"type":"error","name":"ValidatorProofFailed","inputs":[]}] \ No newline at end of file From ac4e76dff082909ab8f6c3a22789c87eb0b8138c Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Fri, 23 Aug 2024 12:51:56 +0200 Subject: [PATCH 5/7] get_lookahead_params_for_epoch --- Node/src/ethereum_l1/execution_layer.rs | 60 ++++++++++++++++++++++--- Node/src/node/operator.rs | 4 +- 2 files changed, 55 insertions(+), 9 deletions(-) diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index 712eeee..1ec3628 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -59,6 +59,27 @@ pub struct Validator { pub stop_proposing_at: u64, } +pub struct LookaheadSetParam { + pub timestamp: u64, + pub preconfer: [u8; 20], +} + +impl From<&PreconfTaskManager::LookaheadSetParam> for LookaheadSetParam { + fn from(param: &PreconfTaskManager::LookaheadSetParam) -> Self { + let timestamp = param.timestamp.try_into().unwrap_or_else(|_| { + tracing::error!( + "Failed to convert timestamp to u64 from PreconfTaskManager::LookaheadSetParam" + ); + u64::MAX + }); + + Self { + timestamp, + preconfer: param.preconfer.into_array(), + } + } +} + sol!( #[allow(clippy::too_many_arguments)] #[allow(missing_docs)] @@ -115,7 +136,6 @@ sol!( "src/ethereum_l1/abi/PreconfRegistry.json" ); -#[allow(dead_code)] //TODO: remove after used in release code impl ExecutionLayer { pub fn new( rpc_url: &str, @@ -322,18 +342,20 @@ impl ExecutionLayer { .wallet(self.wallet.clone()) .on_http(self.rpc_url.clone()); - let contract = + let _contract = PreconfTaskManager::new(self.contract_addresses.avs.preconf_task_manager, provider); - let header = PreconfTaskManager::PreconfirmationHeader { + let _header = PreconfTaskManager::PreconfirmationHeader { blockId: U256::from(block_id), chainId: U256::from(chain_id), txListHash: B256::from(tx_list_hash), }; - let signature = Bytes::from(signature); - let builder = contract.proveIncorrectPreconfirmation(header, signature); - let tx_hash = builder.send().await?.watch().await?; - tracing::debug!("Proved incorrect preconfirmation: {tx_hash}"); + let _signature = Bytes::from(signature); + + // TODO: use new paremeter BlockMetadata + // let builder = contract.proveIncorrectPreconfirmation(header, signature); + // let tx_hash = builder.send().await?.watch().await?; + // tracing::debug!("Proved incorrect preconfirmation: {tx_hash}"); Ok(()) } @@ -406,6 +428,30 @@ impl ExecutionLayer { Ok(()) } + pub async fn get_lookahead_params_for_epoch( + &self, + epoch_timestamp: u64, + validator_bls_pub_keys: &[[u8; 48]; 32], + ) -> Result, 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 params = contract + .getLookaheadParamsForEpoch( + U256::from(epoch_timestamp), + validator_bls_pub_keys.map(|key| Bytes::from(key)), + ) + .call() + .await? + ._0; + + Ok(params.iter().map(|param| param.into()).collect::>()) + } + #[cfg(test)] pub fn new_from_pk( rpc_url: reqwest::Url, diff --git a/Node/src/node/operator.rs b/Node/src/node/operator.rs index 131ce22..bab15d0 100644 --- a/Node/src/node/operator.rs +++ b/Node/src/node/operator.rs @@ -57,7 +57,7 @@ impl Operator { let mut first_slot_to_preconf = first_duty.slot; let mut first_preconfer = true; let mut preconfer_found = false; - let avs_node_address = self.ethereum_l1.execution_layer.get_preconfer_address(); + let preconfer_address = self.ethereum_l1.execution_layer.get_preconfer_address(); self.should_post_lookahead_for_next_epoch = false; for duty in lookahead { @@ -68,7 +68,7 @@ impl Operator { ) .await? { - if preconfer == avs_node_address { + 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 { From 83924b51a3e04767356c6d24310870a9d5c0a44c Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Fri, 23 Aug 2024 14:37:48 +0200 Subject: [PATCH 6/7] Operator adjusted to the new PreconfTaskManager interface --- Node/src/ethereum_l1/execution_layer.rs | 64 +++++------- Node/src/ethereum_l1/slot_clock.rs | 10 ++ Node/src/node/mod.rs | 9 +- Node/src/node/operator.rs | 128 +++++++++++------------- Node/src/utils/mod.rs | 1 + Node/src/utils/preconfirmation_proof.rs | 3 +- Node/src/utils/types.rs | 7 ++ 7 files changed, 108 insertions(+), 114 deletions(-) create mode 100644 Node/src/utils/types.rs diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index 1ec3628..67a2229 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -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}, @@ -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 { @@ -74,7 +63,7 @@ impl From<&PreconfTaskManager::LookaheadSetParam> for LookaheadSetParam { }); Self { - timestamp, + _timestamp: timestamp, preconfer: param.preconfer.into_array(), } } @@ -359,28 +348,6 @@ impl ExecutionLayer { Ok(()) } - pub async fn get_validator(&self, pubkey: &[u8]) -> Result { - 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< @@ -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, Error> { let provider = ProviderBuilder::new() .with_recommended_fillers() @@ -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() @@ -452,6 +419,23 @@ impl ExecutionLayer { Ok(params.iter().map(|param| param.into()).collect::>()) } + pub async fn is_lookahead_required(&self, epoch_begin_timestamp: u64) -> Result { + 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, diff --git a/Node/src/ethereum_l1/slot_clock.rs b/Node/src/ethereum_l1/slot_clock.rs index 7dccf9d..dbd685e 100644 --- a/Node/src/ethereum_l1/slot_clock.rs +++ b/Node/src/ethereum_l1/slot_clock.rs @@ -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 { let now = SystemTime::now().duration_since(UNIX_EPOCH)?; self.duration_to_next_slot_from(now) @@ -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 { + let slot = epoch * self.slots_per_epoch; + let start_of_slot = self.start_of(slot)?; + Ok(start_of_slot.as_secs()) + } } #[cfg(test)] diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index 309c74a..b9d526a 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -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 @@ -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?; diff --git a/Node/src/node/operator.rs b/Node/src/node/operator.rs index bab15d0..506abb6 100644 --- a/Node/src/node/operator.rs +++ b/Node/src/node/operator.rs @@ -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, - first_slot_to_preconf: Option, - final_slot_to_preconf: Option, should_post_lookahead_for_next_epoch: bool, + lookahead_params: Vec, + l1_slots_per_epoch: u64, } pub enum Status { @@ -18,26 +21,40 @@ pub enum Status { impl Operator { pub fn new(ethereum_l1: Arc) -> 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 { + 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 { @@ -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 = 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, 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(()) } } diff --git a/Node/src/utils/mod.rs b/Node/src/utils/mod.rs index 6b3105e..bba8594 100644 --- a/Node/src/utils/mod.rs +++ b/Node/src/utils/mod.rs @@ -5,3 +5,4 @@ pub mod preconfirmation_message; pub mod preconfirmation_proof; pub mod rpc_client; pub mod rpc_server; +pub mod types; diff --git a/Node/src/utils/preconfirmation_proof.rs b/Node/src/utils/preconfirmation_proof.rs index fd2b3c4..fa159ff 100644 --- a/Node/src/utils/preconfirmation_proof.rs +++ b/Node/src/utils/preconfirmation_proof.rs @@ -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 for Vec { diff --git a/Node/src/utils/types.rs b/Node/src/utils/types.rs new file mode 100644 index 0000000..ba1605b --- /dev/null +++ b/Node/src/utils/types.rs @@ -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]; From 6559abddd7d2ed2288da4fb9f6f88d54273f4239 Mon Sep 17 00:00:00 2001 From: Maciej Skrzypkowski Date: Fri, 23 Aug 2024 16:23:05 +0200 Subject: [PATCH 7/7] Corrected checking if lookahead posting is required --- Node/src/node/mod.rs | 30 +++++++++++++++++++----------- Node/src/node/operator.rs | 28 ++++++++++++++++++---------- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index b9d526a..1067f00 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -36,7 +36,6 @@ pub struct Node { l2_slot_duration_sec: u64, preconfirmed_blocks: Arc>>, operator: Operator, - // validator_pubkey: String, } impl Node { @@ -225,6 +224,10 @@ impl Node { async fn main_block_preconfirmation_step(&mut self) -> Result<(), Error> { let current_epoch = self.ethereum_l1.slot_clock.get_current_epoch()?; + let current_epoch_timestamp = self + .ethereum_l1 + .slot_clock + .get_epoch_begin_timestamp(current_epoch)?; if current_epoch != self.epoch { tracing::debug!( "Current epoch changed from {} to {}", @@ -235,12 +238,7 @@ impl Node { self.operator = Operator::new(self.ethereum_l1.clone()); self.operator - .find_slots_to_preconfirm( - self.ethereum_l1 - .slot_clock - .get_epoch_begin_timestamp(current_epoch)?, - &self.lookahead, - ) + .find_slots_to_preconfirm(current_epoch_timestamp, &self.lookahead) .await?; self.lookahead = self @@ -248,20 +246,30 @@ impl Node { .consensus_layer .get_lookahead(self.epoch + 1) .await?; - - if self.operator.should_post_lookahead() { - // TODO: post lookahead - } } let current_slot = self.ethereum_l1.slot_clock.get_current_slot()?; match self.operator.get_status(current_slot)? { OperatorStatus::PreconferAndProposer => { + if self + .operator + .should_post_lookahead(current_epoch_timestamp) + .await? + { + // TODO: post lookahead + } // TODO: replace with mev-boost forced inclusion list self.preconfirm_block().await?; } OperatorStatus::Preconfer => { + if self + .operator + .should_post_lookahead(current_epoch_timestamp) + .await? + { + // TODO: post lookahead + } self.preconfirm_block().await?; } OperatorStatus::None => { diff --git a/Node/src/node/operator.rs b/Node/src/node/operator.rs index 506abb6..2acf422 100644 --- a/Node/src/node/operator.rs +++ b/Node/src/node/operator.rs @@ -8,7 +8,7 @@ use std::sync::Arc; pub struct Operator { ethereum_l1: Arc, - should_post_lookahead_for_next_epoch: bool, + lookahead_required_contract_called: bool, lookahead_params: Vec, l1_slots_per_epoch: u64, } @@ -24,7 +24,7 @@ impl Operator { let l1_slots_per_epoch = ethereum_l1.slot_clock.get_slots_per_epoch(); Self { ethereum_l1, - should_post_lookahead_for_next_epoch: false, + lookahead_required_contract_called: false, lookahead_params: vec![], l1_slots_per_epoch, } @@ -57,8 +57,22 @@ impl Operator { != self.ethereum_l1.execution_layer.get_preconfer_address() } - pub fn should_post_lookahead(&self) -> bool { - self.should_post_lookahead_for_next_epoch + pub async fn should_post_lookahead( + &mut self, + epoch_begin_timestamp: u64, + ) -> Result { + if !self.lookahead_required_contract_called { + self.lookahead_required_contract_called = true; + if self + .ethereum_l1 + .execution_layer + .is_lookahead_required(epoch_begin_timestamp) + .await? + { + return Ok(true); + } + } + return Ok(false); } pub async fn find_slots_to_preconfirm( @@ -92,12 +106,6 @@ impl Operator { ) .await?; - self.should_post_lookahead_for_next_epoch = self - .ethereum_l1 - .execution_layer - .is_lookahead_required(epoch_begin_timestamp) - .await?; - Ok(()) } }