From 2edf4962784703c4f0f0a9052c9704f27c3c4f8f Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Mon, 26 Aug 2024 02:56:51 +0200 Subject: [PATCH 1/7] Prepare transaction list to force push to MEV Boost --- Node/src/ethereum_l1/execution_layer.rs | 103 ++++++++++++++++++------ Node/src/ethereum_l1/mod.rs | 9 ++- Node/src/node/block_porposer.rs | 26 ++++++ Node/src/node/mod.rs | 102 ++++++++++++++++++----- Node/src/node/operator.rs | 2 +- 5 files changed, 194 insertions(+), 48 deletions(-) create mode 100644 Node/src/node/block_porposer.rs diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index 67a2229..f8fdd4b 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -1,10 +1,11 @@ use super::slot_clock::SlotClock; use crate::utils::{config, types::*}; use alloy::{ + consensus::TypedTransaction, contract::EventPoller, network::{Ethereum, EthereumWallet, NetworkWallet}, primitives::{Address, Bytes, FixedBytes, B256, U256}, - providers::ProviderBuilder, + providers::{Provider, ProviderBuilder}, signers::{ local::{LocalSigner, PrivateKeySigner}, Signature, SignerSync, @@ -29,6 +30,7 @@ pub struct ExecutionLayer { contract_addresses: ContractAddresses, slot_clock: Arc, preconf_registry_expiry_sec: u64, + chain_id: u64, } pub struct ContractAddresses { @@ -126,7 +128,7 @@ sol!( ); impl ExecutionLayer { - pub fn new( + pub async fn new( rpc_url: &str, avs_node_ecdsa_private_key: &str, contract_addresses: &config::ContractAddresses, @@ -144,6 +146,9 @@ impl ExecutionLayer { let contract_addresses = Self::parse_contract_addresses(contract_addresses) .map_err(|e| Error::msg(format!("Failed to parse contract addresses: {}", e)))?; + let provider = ProviderBuilder::new().on_http(rpc_url.parse()?); + let chain_id = provider.get_chain_id().await?; + Ok(Self { rpc_url: rpc_url.parse()?, signer, @@ -152,6 +157,7 @@ impl ExecutionLayer { contract_addresses, slot_clock, preconf_registry_expiry_sec, + chain_id, }) } @@ -179,18 +185,17 @@ impl ExecutionLayer { pub async fn propose_new_block( &self, + nonce: u64, tx_list: Vec, parent_meta_hash: [u8; 32], lookahead_set: Vec, - ) -> Result<(), Error> { - let provider = ProviderBuilder::new() - .with_recommended_fillers() - .wallet(self.wallet.clone()) - .on_http(self.rpc_url.clone()); + ) -> Result, Error> { + let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); let contract = - PreconfTaskManager::new(self.contract_addresses.avs.preconf_task_manager, provider); + PreconfTaskManager::new(self.contract_addresses.avs.preconf_task_manager, &provider); + // TODO fix let block_params = BlockParams { assignedProver: Address::ZERO, coinbase: >::default_signer_address( @@ -207,6 +212,8 @@ impl ExecutionLayer { let encoded_block_params = Bytes::from(BlockParams::abi_encode_sequence(&block_params)); let tx_list = Bytes::from(tx_list); + + // create lookahead set let lookahead_set_param = lookahead_set .iter() .map(|duty| { @@ -217,17 +224,48 @@ impl ExecutionLayer { }) .collect::, Error>>()?; - let builder = contract.newBlockProposal( - encoded_block_params, - tx_list, - U256::from(0), //TODO: Replace it with the proper lookaheadPointer when the contract is ready. - lookahead_set_param, - ); + // TODO check gas parameters + let builder = contract + .newBlockProposal( + encoded_block_params, + tx_list, + U256::from(0), //TODO: Replace it with the proper lookaheadPointer when the contract is ready. + lookahead_set_param, + ) + .chain_id(self.chain_id) + .nonce(nonce) //TODO how to get it? + .gas(50_000) + .max_fee_per_gas(20_000_000_000) + .max_priority_fee_per_gas(1_000_000_000); + + // Build transaction + let tx = builder.as_ref().clone().build_typed_tx(); + let Ok(TypedTransaction::Eip1559(mut tx)) = tx else { + // TODO fix + panic!("Not EIP1559 transaction"); + }; - let tx_hash = builder.send().await?.watch().await?; - tracing::debug!("Proposed new block: {tx_hash}"); + // Sign transaction + let signature = self + .wallet + .default_signer() + .sign_transaction(&mut tx) + .await?; - Ok(()) + // Encode transaction + let mut buf = vec![]; + tx.encode_with_signature(&signature, &mut buf, false); + + // Send transaction + let pending = provider + .send_raw_transaction(&buf) + .await? + .register() + .await?; + + tracing::debug!("Proposed new block, with hash {}", pending.tx_hash()); + + Ok(buf) } pub async fn register_preconfer(&self) -> Result<(), Error> { @@ -319,6 +357,15 @@ impl ExecutionLayer { Ok(address) } + pub async fn get_preconfer_nonce(&self) -> Result { + let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); + + let nonce = provider + .get_transaction_count(self.preconfer_address) + .await?; + Ok(nonce) + } + pub async fn prove_incorrect_preconfirmation( &self, block_id: u64, @@ -410,7 +457,7 @@ impl ExecutionLayer { let params = contract .getLookaheadParamsForEpoch( U256::from(epoch_begin_timestamp), - validator_bls_pub_keys.map(|key| Bytes::from(key)), + validator_bls_pub_keys.map(Bytes::from), ) .call() .await? @@ -437,7 +484,7 @@ impl ExecutionLayer { } #[cfg(test)] - pub fn new_from_pk( + pub async fn new_from_pk( rpc_url: reqwest::Url, private_key: elliptic_curve::SecretKey, ) -> Result { @@ -445,6 +492,9 @@ impl ExecutionLayer { let wallet = EthereumWallet::from(signer.clone()); let clock = SlotClock::new(0u64, 0u64, 12u64, 32u64); + let provider = ProviderBuilder::new().on_http(rpc_url.clone()); + let chain_id = provider.get_chain_id().await?; + Ok(Self { rpc_url, signer, @@ -465,6 +515,7 @@ impl ExecutionLayer { }, }, preconf_registry_expiry_sec: 120, + chain_id, }) } @@ -521,7 +572,9 @@ mod tests { let anvil = Anvil::new().try_spawn().unwrap(); let rpc_url: reqwest::Url = anvil.endpoint().parse().unwrap(); let private_key = anvil.keys()[0].clone(); - let el = ExecutionLayer::new_from_pk(rpc_url, private_key).unwrap(); + let el = ExecutionLayer::new_from_pk(rpc_url, private_key) + .await + .unwrap(); el.call_test_contract().await.unwrap(); } @@ -530,9 +583,11 @@ mod tests { let anvil = Anvil::new().try_spawn().unwrap(); let rpc_url: reqwest::Url = anvil.endpoint().parse().unwrap(); let private_key = anvil.keys()[0].clone(); - let el = ExecutionLayer::new_from_pk(rpc_url, private_key).unwrap(); + let el = ExecutionLayer::new_from_pk(rpc_url, private_key) + .await + .unwrap(); - el.propose_new_block(vec![0; 32], [0; 32], vec![]) + el.propose_new_block(0, vec![0; 32], [0; 32], vec![]) .await .unwrap(); } @@ -541,7 +596,9 @@ mod tests { let anvil = Anvil::new().try_spawn().unwrap(); let rpc_url: reqwest::Url = anvil.endpoint().parse().unwrap(); let private_key = anvil.keys()[0].clone(); - let el = ExecutionLayer::new_from_pk(rpc_url, private_key).unwrap(); + let el = ExecutionLayer::new_from_pk(rpc_url, private_key) + .await + .unwrap(); let result = el.register_preconfer().await; assert!(result.is_ok(), "Register method failed: {:?}", result.err()); diff --git a/Node/src/ethereum_l1/mod.rs b/Node/src/ethereum_l1/mod.rs index d1be9f5..03e0cc1 100644 --- a/Node/src/ethereum_l1/mod.rs +++ b/Node/src/ethereum_l1/mod.rs @@ -39,7 +39,8 @@ impl EthereumL1 { contract_addresses, slot_clock.clone(), preconf_registry_expiry_sec, - )?; + ) + .await?; Ok(Self { slot_clock, @@ -64,9 +65,11 @@ mod tests { let anvil = Anvil::new().try_spawn().unwrap(); let rpc_url: reqwest::Url = anvil.endpoint().parse().unwrap(); let private_key = anvil.keys()[0].clone(); - let el = ExecutionLayer::new_from_pk(rpc_url, private_key).unwrap(); + let el = ExecutionLayer::new_from_pk(rpc_url, private_key) + .await + .unwrap(); - el.propose_new_block(vec![0; 32], [0; 32], duties) + el.propose_new_block(0, vec![0; 32], [0; 32], duties) .await .unwrap(); } diff --git a/Node/src/node/block_porposer.rs b/Node/src/node/block_porposer.rs new file mode 100644 index 0000000..4b530f3 --- /dev/null +++ b/Node/src/node/block_porposer.rs @@ -0,0 +1,26 @@ +pub struct BlockProposer { + nonce: u64, + block_id: u64, +} + +impl BlockProposer { + pub fn new() -> Self { + Self { + nonce: 0, + block_id: 0, + } + } + + pub fn start_propose(&mut self, nonce: u64, block_id: u64) { + self.nonce = nonce; + self.block_id = block_id; + } + + pub fn propose_next(&mut self) -> (u64, u64) { + let nonce = self.nonce; + let block_id = self.block_id; + self.nonce += 1; + self.block_id += 1; + (nonce, block_id) + } +} diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index 1067f00..ce9720b 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -11,15 +11,21 @@ use crate::{ 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 std::sync::atomic::Ordering; +use std::{ + collections::HashMap, + sync::{atomic::AtomicBool, Arc}, +}; use tokio::sync::{ mpsc::{Receiver, Sender}, Mutex, }; use tracing::info; +mod block_porposer; pub mod block_proposed_receiver; mod operator; +use block_porposer::BlockProposer; const OLDEST_BLOCK_DISTANCE: u64 = 256; @@ -35,7 +41,10 @@ pub struct Node { lookahead: Vec, l2_slot_duration_sec: u64, preconfirmed_blocks: Arc>>, + is_preconfer_now: Arc, + preconfirmation_txs: Arc>>>, // block_id -> tx operator: Operator, + block_proposer: BlockProposer, } impl Node { @@ -62,7 +71,10 @@ impl Node { lookahead: vec![], l2_slot_duration_sec, preconfirmed_blocks: Arc::new(Mutex::new(HashMap::new())), + is_preconfer_now: Arc::new(AtomicBool::new(false)), + preconfirmation_txs: Arc::new(Mutex::new(HashMap::new())), operator, + block_proposer: BlockProposer::new(), }) } @@ -79,6 +91,8 @@ impl Node { let preconfirmed_blocks = self.preconfirmed_blocks.clone(); let ethereum_l1 = self.ethereum_l1.clone(); let taiko = self.taiko.clone(); + let is_preconfer_now = self.is_preconfer_now.clone(); + let preconfirmation_txs = self.preconfirmation_txs.clone(); if let Some(node_rx) = self.node_rx.take() { let p2p_to_node_rx = self.p2p_to_node_rx.take().unwrap(); tokio::spawn(async move { @@ -88,6 +102,8 @@ impl Node { preconfirmed_blocks, ethereum_l1, taiko, + is_preconfer_now, + preconfirmation_txs, ) .await; }); @@ -102,22 +118,33 @@ impl Node { preconfirmed_blocks: Arc>>, ethereum_l1: Arc, taiko: Arc, + is_preconfer_now: Arc, + preconfirmation_txs: Arc>>>, ) { loop { tokio::select! { Some(block_proposed) = node_rx.recv() => { - tracing::debug!("Node received block proposed event: {:?}", block_proposed); - if let Err(e) = Self::check_preconfirmed_blocks_correctness(&preconfirmed_blocks, taiko.chain_id, &block_proposed, ethereum_l1.clone()).await { - tracing::error!("Failed to check preconfirmed blocks correctness: {}", e); - } - if let Err(e) = Self::clean_old_blocks(&preconfirmed_blocks, block_proposed.block_id).await { - tracing::error!("Failed to clean old blocks: {}", e); + if !is_preconfer_now.load(Ordering::Acquire) { + tracing::debug!("Node received block proposed event: {:?}", block_proposed); + if let Err(e) = Self::check_preconfirmed_blocks_correctness(&preconfirmed_blocks, taiko.chain_id, &block_proposed, ethereum_l1.clone()).await { + tracing::error!("Failed to check preconfirmed blocks correctness: {}", e); + } + if let Err(e) = Self::clean_old_blocks(&preconfirmed_blocks, block_proposed.block_id).await { + tracing::error!("Failed to clean old blocks: {}", e); + } + } else { + tracing::debug!("Node is Preconfer and received block proposed event: {:?}", block_proposed); + preconfirmation_txs.lock().await.remove(&block_proposed.block_id); } }, Some(p2p_message) = p2p_to_node_rx.recv() => { - let msg: PreconfirmationMessage = p2p_message.into(); - tracing::debug!("Node received message from p2p: {:?}", msg); - Self::check_preconfirmation_message(msg, &preconfirmed_blocks, ethereum_l1.clone(), taiko.clone()).await; + if !is_preconfer_now.load(Ordering::Acquire) { + let msg: PreconfirmationMessage = p2p_message.into(); + tracing::debug!("Node received message from p2p: {:?}", msg); + Self::check_preconfirmation_message(msg, &preconfirmed_blocks, ethereum_l1.clone(), taiko.clone()).await; + } else { + tracing::debug!("Node is Preconfer and received message from p2p: {:?}", p2p_message); + } } } } @@ -252,23 +279,28 @@ impl Node { match self.operator.get_status(current_slot)? { OperatorStatus::PreconferAndProposer => { - if self - .operator - .should_post_lookahead(current_epoch_timestamp) - .await? - { - // TODO: post lookahead + if self.is_preconfer_now.load(Ordering::Acquire) { + self.is_preconfer_now.store(false, Ordering::Release); + + let mut preconfirmation_txs = self.preconfirmation_txs.lock().await; + if !preconfirmation_txs.is_empty() { + + // TODO: call mev-boost force inclusion list + preconfirmation_txs.clear(); + } } - // TODO: replace with mev-boost forced inclusion list - self.preconfirm_block().await?; } OperatorStatus::Preconfer => { + if !self.is_preconfer_now.load(Ordering::Acquire) { + self.is_preconfer_now.store(true, Ordering::Release); + self.start_propose().await?; + } if self .operator .should_post_lookahead(current_epoch_timestamp) .await? { - // TODO: post lookahead + // TODO: build lookahead } self.preconfirm_block().await?; } @@ -280,6 +312,25 @@ impl Node { Ok(()) } + async fn start_propose(&mut self) -> Result<(), Error> { + // get L2 block id + // TODO check that it returns real L2 height + let pending_tx_lists = self.taiko.get_pending_l2_tx_lists().await?; + if pending_tx_lists.tx_list_bytes.is_empty() { + return Ok(()); + } + let new_block_height = pending_tx_lists.parent_block_id + 1; + // get L1 preconfer wallet nonce + let nonce = self + .ethereum_l1 + .execution_layer + .get_preconfer_nonce() + .await?; + + self.block_proposer.start_propose(nonce, new_block_height); + Ok(()) + } + async fn preconfirm_block(&mut self) -> Result<(), Error> { tracing::debug!( "Preconfirming for the slot: {:?}", @@ -291,7 +342,8 @@ impl Node { return Ok(()); } - let new_block_height = pending_tx_lists.parent_block_id + 1; + let (nonce, new_block_height) = self.block_proposer.propose_next(); + let (commit_hash, signature) = self.generate_commit_hash_and_signature(&pending_tx_lists, new_block_height)?; @@ -311,15 +363,23 @@ impl Node { self.taiko .advance_head_to_new_l2_block(pending_tx_lists.tx_lists, self.gas_used) .await?; - self.ethereum_l1 + let tx = self + .ethereum_l1 .execution_layer .propose_new_block( + nonce, pending_tx_lists.tx_list_bytes[0].clone(), //TODO: handle rest tx lists pending_tx_lists.parent_meta_hash, std::mem::take(&mut self.lookahead), ) .await?; + // insert transaction + self.preconfirmation_txs + .lock() + .await + .insert(new_block_height, tx); + self.preconfirmed_blocks .lock() .await diff --git a/Node/src/node/operator.rs b/Node/src/node/operator.rs index 2acf422..172da6c 100644 --- a/Node/src/node/operator.rs +++ b/Node/src/node/operator.rs @@ -72,7 +72,7 @@ impl Operator { return Ok(true); } } - return Ok(false); + Ok(false) } pub async fn find_slots_to_preconfirm( From d5b6f68f03b19bbc55494066e89073437e801ef8 Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Mon, 26 Aug 2024 21:41:53 +0200 Subject: [PATCH 2/7] Implement force inclusion call with MEV-Boost --- Node/src/ethereum_l1/execution_layer.rs | 8 +++++ Node/src/ethereum_l1/mod.rs | 2 ++ Node/src/main.rs | 2 ++ Node/src/mev_boost/constraints.rs | 48 +++++++++++++++++++++++++ Node/src/mev_boost/mod.rs | 47 ++++++++++++++++++++---- Node/src/node/mod.rs | 23 +++++++++--- Node/src/utils/config.rs | 7 ++++ 7 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 Node/src/mev_boost/constraints.rs diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index f8fdd4b..5f1b282 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -24,6 +24,7 @@ use std::sync::Arc; pub struct ExecutionLayer { rpc_url: reqwest::Url, + validator_index: u64, signer: LocalSigner>, wallet: EthereumWallet, preconfer_address: Address, @@ -134,6 +135,7 @@ impl ExecutionLayer { contract_addresses: &config::ContractAddresses, slot_clock: Arc, preconf_registry_expiry_sec: u64, + validator_index: u64, ) -> Result { tracing::debug!("Creating ExecutionLayer with RPC URL: {}", rpc_url); @@ -151,6 +153,7 @@ impl ExecutionLayer { Ok(Self { rpc_url: rpc_url.parse()?, + validator_index, signer, wallet, preconfer_address, @@ -497,6 +500,7 @@ impl ExecutionLayer { Ok(Self { rpc_url, + validator_index: 0, signer, wallet, preconfer_address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" // some random address for test @@ -559,6 +563,10 @@ impl ExecutionLayer { Ok(()) } + + pub fn get_validator_index(&self) -> u64 { + self.validator_index + } } #[cfg(test)] diff --git a/Node/src/ethereum_l1/mod.rs b/Node/src/ethereum_l1/mod.rs index 03e0cc1..a988cbf 100644 --- a/Node/src/ethereum_l1/mod.rs +++ b/Node/src/ethereum_l1/mod.rs @@ -23,6 +23,7 @@ impl EthereumL1 { slot_duration_sec: u64, slots_per_epoch: u64, preconf_registry_expiry_sec: u64, + validator_index: u64, ) -> Result { let consensus_layer = ConsensusLayer::new(consensus_rpc_url)?; let genesis_details = consensus_layer.get_genesis_details().await?; @@ -39,6 +40,7 @@ impl EthereumL1 { contract_addresses, slot_clock.clone(), preconf_registry_expiry_sec, + validator_index, ) .await?; diff --git a/Node/src/main.rs b/Node/src/main.rs index 3d91d4e..207ef82 100644 --- a/Node/src/main.rs +++ b/Node/src/main.rs @@ -34,6 +34,7 @@ async fn main() -> Result<(), Error> { config.l1_slot_duration_sec, config.l1_slots_per_epoch, config.preconf_registry_expiry_sec, + config.validator_index, ) .await?; @@ -60,6 +61,7 @@ async fn main() -> Result<(), Error> { BlockProposedEventReceiver::new(taiko.clone(), node_tx.clone()); BlockProposedEventReceiver::start(block_proposed_event_checker).await; let ethereum_l1 = Arc::new(ethereum_l1); + let node = node::Node::new( node_rx, node_to_p2p_tx, diff --git a/Node/src/mev_boost/constraints.rs b/Node/src/mev_boost/constraints.rs new file mode 100644 index 0000000..3030c6c --- /dev/null +++ b/Node/src/mev_boost/constraints.rs @@ -0,0 +1,48 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone)] +pub struct Constraint { + tx: String, + index: Option, +} + +impl Constraint { + pub fn new(tx: String, index: Option) -> Self { + Self { tx, index } + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct ConstraintsMessage { + validator_index: u64, + slot: u64, + constraints: Vec, +} + +impl ConstraintsMessage { + pub fn new(validator_index: u64, slot: u64, constraints: Vec) -> Self { + Self { + validator_index, + slot, + constraints, + } + } +} + +#[derive(Serialize, Deserialize, Clone)] +pub struct SignedConstraints { + message: ConstraintsMessage, + signature: String, +} + +impl SignedConstraints { + pub fn new(message: ConstraintsMessage, signature: String) -> Self { + Self { message, signature } + } +} + +impl From for Vec { + fn from(val: ConstraintsMessage) -> Self { + bincode::serialize(&val).expect("MEV Boost message serialization failed") + } +} diff --git a/Node/src/mev_boost/mod.rs b/Node/src/mev_boost/mod.rs index 388d95c..226f1ce 100644 --- a/Node/src/mev_boost/mod.rs +++ b/Node/src/mev_boost/mod.rs @@ -1,20 +1,55 @@ -#![allow(unused)] //TODO remove after the EthereumL1 is used in release code - +use crate::ethereum_l1::EthereumL1; use crate::utils::rpc_client::RpcClient; +use anyhow::Error; +use std::sync::Arc; + +pub mod constraints; +use constraints::{Constraint, ConstraintsMessage, SignedConstraints}; pub struct MevBoost { - _rpc_client: RpcClient, + rpc_client: RpcClient, } impl MevBoost { pub fn new(rpc_url: &str) -> Self { let rpc_client = RpcClient::new(rpc_url); Self { - _rpc_client: rpc_client, + rpc_client, } } - pub fn send_transaction(&self, _tx: &[u8], _validator_index: u64, _slot: u64) { - //TODO: implement + pub async fn force_inclusion( + &self, + constraints: Vec, + ethereum_l1: Arc, + ) -> Result<(), Error> { + // Prepare the message + // TODO check slot id value + let slot_id = ethereum_l1.slot_clock.get_current_slot()?; + let validator_index = ethereum_l1.execution_layer.get_validator_index(); + + let message = ConstraintsMessage::new(validator_index, slot_id, constraints); + + let data_to_sign: Vec = message.clone().into(); + + // Sign the message + // TODO: Determine if the transaction data needs to be signed as a JSON string. + let signature = ethereum_l1 + .execution_layer + .sign_message_with_private_ecdsa_key(&data_to_sign)?; + + // Prepare data to send + let signed_constraints = SignedConstraints::new(message, hex::encode(signature)); + let json_data = serde_json::to_value(&signed_constraints).unwrap(); + + // https://chainbound.github.io/bolt-docs/api/builder#ethv1builderconstraints + let method = "/eth/v1/builder/constraints"; + // Make rpc request + self.rpc_client + .call_method(method, vec![json_data]) + .await + .unwrap(); + + Ok(()) } } diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index ce9720b..ab287fe 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -1,6 +1,6 @@ use crate::{ ethereum_l1::{slot_clock::Epoch, EthereumL1}, - mev_boost::MevBoost, + mev_boost::{self, MevBoost}, taiko::{l2_tx_lists::RPCReplyL2TxLists, Taiko}, utils::{ block_proposed::BlockProposed, commit::L2TxListsCommit, @@ -8,6 +8,7 @@ use crate::{ preconfirmation_proof::PreconfirmationProof, }, }; +use alloy::hex; use anyhow::{anyhow as any_err, Error}; use beacon_api_client::ProposerDuty; use operator::{Operator, Status as OperatorStatus}; @@ -27,6 +28,8 @@ pub mod block_proposed_receiver; mod operator; use block_porposer::BlockProposer; +use mev_boost::constraints::Constraint; + const OLDEST_BLOCK_DISTANCE: u64 = 256; pub struct Node { @@ -36,7 +39,7 @@ pub struct Node { p2p_to_node_rx: Option>>, gas_used: u64, ethereum_l1: Arc, - _mev_boost: MevBoost, // temporary unused + mev_boost: MevBoost, // temporary unused epoch: Epoch, lookahead: Vec, l2_slot_duration_sec: u64, @@ -66,7 +69,7 @@ impl Node { p2p_to_node_rx: Some(p2p_to_node_rx), gas_used: 0, ethereum_l1, - _mev_boost: mev_boost, + mev_boost, epoch: current_epoch, lookahead: vec![], l2_slot_duration_sec, @@ -237,9 +240,9 @@ impl Node { } async fn preconfirmation_loop(&mut self) { + // TODO syncronize with slot clock let mut interval = tokio::time::interval(std::time::Duration::from_secs(self.l2_slot_duration_sec)); - loop { interval.tick().await; @@ -280,12 +283,22 @@ impl Node { match self.operator.get_status(current_slot)? { OperatorStatus::PreconferAndProposer => { if self.is_preconfer_now.load(Ordering::Acquire) { + // TODO change behavior self.is_preconfer_now.store(false, Ordering::Release); let mut preconfirmation_txs = self.preconfirmation_txs.lock().await; if !preconfirmation_txs.is_empty() { + // Build constraints + let constraints: Vec = preconfirmation_txs + .iter() + .map(|(_, value)| Constraint::new(hex::encode(value), None)) + .collect(); + + self + .mev_boost + .force_inclusion(constraints, self.ethereum_l1.clone()) + .await?; - // TODO: call mev-boost force inclusion list preconfirmation_txs.clear(); } } diff --git a/Node/src/utils/config.rs b/Node/src/utils/config.rs index 34bcd79..4367811 100644 --- a/Node/src/utils/config.rs +++ b/Node/src/utils/config.rs @@ -17,6 +17,7 @@ pub struct Config { pub contract_addresses: ContractAddresses, pub p2p_network_config: P2PNetworkConfig, pub taiko_chain_id: u64, + pub validator_index: u64, } #[derive(Debug)] @@ -207,6 +208,11 @@ impl Config { }) .expect("TAIKO_CHAIN_ID must be a number"); + let validator_index = std::env::var("VALIDATOR_INDEX") + .expect("VALIDATOR_INDEX env variable must be set") + .parse::() + .expect("VALIDATOR_INDEX must be a number"); + let config = Self { taiko_proposer_url: std::env::var("TAIKO_PROPOSER_URL") .unwrap_or("http://127.0.0.1:1234".to_string()), @@ -227,6 +233,7 @@ impl Config { contract_addresses, p2p_network_config, taiko_chain_id, + validator_index, }; info!( From f1280af99cd911c7eba3efce9919bdd79f28469d Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Mon, 26 Aug 2024 22:23:37 +0200 Subject: [PATCH 3/7] Change behavior when acting as a proposer --- Node/src/ethereum_l1/execution_layer.rs | 19 +++++---- Node/src/ethereum_l1/mod.rs | 2 +- Node/src/mev_boost/mod.rs | 4 +- Node/src/node/block_porposer.rs | 11 ++++++ Node/src/node/mod.rs | 51 ++++++++++++++++--------- 5 files changed, 57 insertions(+), 30 deletions(-) diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index 5f1b282..39a842b 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -192,6 +192,7 @@ impl ExecutionLayer { tx_list: Vec, parent_meta_hash: [u8; 32], lookahead_set: Vec, + is_send: bool, ) -> Result, Error> { let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); @@ -260,13 +261,15 @@ impl ExecutionLayer { tx.encode_with_signature(&signature, &mut buf, false); // Send transaction - let pending = provider - .send_raw_transaction(&buf) - .await? - .register() - .await?; - - tracing::debug!("Proposed new block, with hash {}", pending.tx_hash()); + if is_send { + let pending = provider + .send_raw_transaction(&buf) + .await? + .register() + .await?; + + tracing::debug!("Proposed new block, with hash {}", pending.tx_hash()); + } Ok(buf) } @@ -595,7 +598,7 @@ mod tests { .await .unwrap(); - el.propose_new_block(0, vec![0; 32], [0; 32], vec![]) + el.propose_new_block(0, vec![0; 32], [0; 32], vec![], true) .await .unwrap(); } diff --git a/Node/src/ethereum_l1/mod.rs b/Node/src/ethereum_l1/mod.rs index a988cbf..1824b83 100644 --- a/Node/src/ethereum_l1/mod.rs +++ b/Node/src/ethereum_l1/mod.rs @@ -71,7 +71,7 @@ mod tests { .await .unwrap(); - el.propose_new_block(0, vec![0; 32], [0; 32], duties) + el.propose_new_block(0, vec![0; 32], [0; 32], duties, true) .await .unwrap(); } diff --git a/Node/src/mev_boost/mod.rs b/Node/src/mev_boost/mod.rs index 226f1ce..6ac1cb8 100644 --- a/Node/src/mev_boost/mod.rs +++ b/Node/src/mev_boost/mod.rs @@ -13,9 +13,7 @@ pub struct MevBoost { impl MevBoost { pub fn new(rpc_url: &str) -> Self { let rpc_client = RpcClient::new(rpc_url); - Self { - rpc_client, - } + Self { rpc_client } } pub async fn force_inclusion( diff --git a/Node/src/node/block_porposer.rs b/Node/src/node/block_porposer.rs index 4b530f3..f41c9bc 100644 --- a/Node/src/node/block_porposer.rs +++ b/Node/src/node/block_porposer.rs @@ -1,6 +1,7 @@ pub struct BlockProposer { nonce: u64, block_id: u64, + final_perconfirmation_count: u8, } impl BlockProposer { @@ -8,12 +9,14 @@ impl BlockProposer { Self { nonce: 0, block_id: 0, + final_perconfirmation_count: 0, } } pub fn start_propose(&mut self, nonce: u64, block_id: u64) { self.nonce = nonce; self.block_id = block_id; + self.final_perconfirmation_count = 0; } pub fn propose_next(&mut self) -> (u64, u64) { @@ -23,4 +26,12 @@ impl BlockProposer { self.block_id += 1; (nonce, block_id) } + + pub fn increment_final_perconfirmation(&mut self) { + self.final_perconfirmation_count += 1; + } + + pub fn is_final_perconfirmation(&self) -> bool { + self.final_perconfirmation_count >= 3 + } } diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index ab287fe..ba276a1 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -254,16 +254,18 @@ 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 {}", self.epoch, current_epoch ); + 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)?; + self.epoch = current_epoch; self.operator = Operator::new(self.ethereum_l1.clone()); @@ -282,8 +284,9 @@ impl Node { match self.operator.get_status(current_slot)? { OperatorStatus::PreconferAndProposer => { - if self.is_preconfer_now.load(Ordering::Acquire) { - // TODO change behavior + self.preconfirm_block(current_epoch, false).await?; + if self.block_proposer.is_final_perconfirmation() { + // Last(4th) perconfirmation when we are proposer and preconfer self.is_preconfer_now.store(false, Ordering::Release); let mut preconfirmation_txs = self.preconfirmation_txs.lock().await; @@ -294,13 +297,15 @@ impl Node { .map(|(_, value)| Constraint::new(hex::encode(value), None)) .collect(); - self - .mev_boost + self.mev_boost .force_inclusion(constraints, self.ethereum_l1.clone()) .await?; preconfirmation_txs.clear(); } + } else { + // Increment perconfirmations count when we are proposer and preconfer + self.block_proposer.increment_final_perconfirmation(); } } OperatorStatus::Preconfer => { @@ -308,14 +313,8 @@ impl Node { self.is_preconfer_now.store(true, Ordering::Release); self.start_propose().await?; } - if self - .operator - .should_post_lookahead(current_epoch_timestamp) - .await? - { - // TODO: build lookahead - } - self.preconfirm_block().await?; + + self.preconfirm_block(current_epoch, true).await?; } OperatorStatus::None => { tracing::debug!("Not my slot to preconfirm: {}", current_slot); @@ -330,7 +329,9 @@ impl Node { // TODO check that it returns real L2 height let pending_tx_lists = self.taiko.get_pending_l2_tx_lists().await?; if pending_tx_lists.tx_list_bytes.is_empty() { - return Ok(()); + return Err(Error::msg( + "Can't initialize new block proposal without txs", + )); } let new_block_height = pending_tx_lists.parent_block_id + 1; // get L1 preconfer wallet nonce @@ -344,12 +345,25 @@ impl Node { Ok(()) } - async fn preconfirm_block(&mut self) -> Result<(), Error> { + async fn preconfirm_block(&mut self, current_epoch: u64, is_send: bool) -> Result<(), Error> { tracing::debug!( "Preconfirming for the slot: {:?}", self.ethereum_l1.slot_clock.get_current_slot()? ); + let current_epoch_timestamp = self + .ethereum_l1 + .slot_clock + .get_epoch_begin_timestamp(current_epoch)?; + + if self + .operator + .should_post_lookahead(current_epoch_timestamp) + .await? + { + // TODO: build lookahead + } + let pending_tx_lists = self.taiko.get_pending_l2_tx_lists().await?; if pending_tx_lists.tx_list_bytes.is_empty() { return Ok(()); @@ -384,6 +398,7 @@ impl Node { pending_tx_lists.tx_list_bytes[0].clone(), //TODO: handle rest tx lists pending_tx_lists.parent_meta_hash, std::mem::take(&mut self.lookahead), + is_send, ) .await?; From 6cff5207717d0331db9ed96724a4ae6af311756e Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Mon, 26 Aug 2024 22:30:41 +0200 Subject: [PATCH 4/7] Fix hex data format for MEV-Boost --- Node/src/mev_boost/mod.rs | 3 ++- Node/src/node/mod.rs | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Node/src/mev_boost/mod.rs b/Node/src/mev_boost/mod.rs index 6ac1cb8..73cf873 100644 --- a/Node/src/mev_boost/mod.rs +++ b/Node/src/mev_boost/mod.rs @@ -37,7 +37,8 @@ impl MevBoost { .sign_message_with_private_ecdsa_key(&data_to_sign)?; // Prepare data to send - let signed_constraints = SignedConstraints::new(message, hex::encode(signature)); + let signed_constraints = + SignedConstraints::new(message, format!("0x{}", hex::encode(signature))); let json_data = serde_json::to_value(&signed_constraints).unwrap(); // https://chainbound.github.io/bolt-docs/api/builder#ethv1builderconstraints diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index ba276a1..838ba66 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -294,7 +294,9 @@ impl Node { // Build constraints let constraints: Vec = preconfirmation_txs .iter() - .map(|(_, value)| Constraint::new(hex::encode(value), None)) + .map(|(_, value)| { + Constraint::new(format!("0x{}", hex::encode(value)), None) + }) .collect(); self.mev_boost From 1b877995f8d59fb66602254dd7dfafa193a268fa Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Tue, 27 Aug 2024 10:36:28 +0200 Subject: [PATCH 5/7] Improve naming --- Node/src/ethereum_l1/execution_layer.rs | 9 --------- Node/src/ethereum_l1/mod.rs | 2 -- Node/src/main.rs | 3 +-- Node/src/mev_boost/mod.rs | 11 +++++++---- ...ck_porposer.rs => block_porposer_helper.rs} | 18 +++++++++--------- Node/src/node/mod.rs | 14 +++++++------- 6 files changed, 24 insertions(+), 33 deletions(-) rename Node/src/node/{block_porposer.rs => block_porposer_helper.rs} (53%) diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index 39a842b..de6263e 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -24,7 +24,6 @@ use std::sync::Arc; pub struct ExecutionLayer { rpc_url: reqwest::Url, - validator_index: u64, signer: LocalSigner>, wallet: EthereumWallet, preconfer_address: Address, @@ -135,7 +134,6 @@ impl ExecutionLayer { contract_addresses: &config::ContractAddresses, slot_clock: Arc, preconf_registry_expiry_sec: u64, - validator_index: u64, ) -> Result { tracing::debug!("Creating ExecutionLayer with RPC URL: {}", rpc_url); @@ -153,7 +151,6 @@ impl ExecutionLayer { Ok(Self { rpc_url: rpc_url.parse()?, - validator_index, signer, wallet, preconfer_address, @@ -199,7 +196,6 @@ impl ExecutionLayer { let contract = PreconfTaskManager::new(self.contract_addresses.avs.preconf_task_manager, &provider); - // TODO fix let block_params = BlockParams { assignedProver: Address::ZERO, coinbase: >::default_signer_address( @@ -503,7 +499,6 @@ impl ExecutionLayer { Ok(Self { rpc_url, - validator_index: 0, signer, wallet, preconfer_address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" // some random address for test @@ -566,10 +561,6 @@ impl ExecutionLayer { Ok(()) } - - pub fn get_validator_index(&self) -> u64 { - self.validator_index - } } #[cfg(test)] diff --git a/Node/src/ethereum_l1/mod.rs b/Node/src/ethereum_l1/mod.rs index 1824b83..2d34719 100644 --- a/Node/src/ethereum_l1/mod.rs +++ b/Node/src/ethereum_l1/mod.rs @@ -23,7 +23,6 @@ impl EthereumL1 { slot_duration_sec: u64, slots_per_epoch: u64, preconf_registry_expiry_sec: u64, - validator_index: u64, ) -> Result { let consensus_layer = ConsensusLayer::new(consensus_rpc_url)?; let genesis_details = consensus_layer.get_genesis_details().await?; @@ -40,7 +39,6 @@ impl EthereumL1 { contract_addresses, slot_clock.clone(), preconf_registry_expiry_sec, - validator_index, ) .await?; diff --git a/Node/src/main.rs b/Node/src/main.rs index 207ef82..e7ea335 100644 --- a/Node/src/main.rs +++ b/Node/src/main.rs @@ -34,7 +34,6 @@ async fn main() -> Result<(), Error> { config.l1_slot_duration_sec, config.l1_slots_per_epoch, config.preconf_registry_expiry_sec, - config.validator_index, ) .await?; @@ -56,7 +55,7 @@ async fn main() -> Result<(), Error> { config.taiko_chain_id, )); - let mev_boost = mev_boost::MevBoost::new(&config.mev_boost_url); + let mev_boost = mev_boost::MevBoost::new(&config.mev_boost_url, config.validator_index); let block_proposed_event_checker = BlockProposedEventReceiver::new(taiko.clone(), node_tx.clone()); BlockProposedEventReceiver::start(block_proposed_event_checker).await; diff --git a/Node/src/mev_boost/mod.rs b/Node/src/mev_boost/mod.rs index 73cf873..c0bd630 100644 --- a/Node/src/mev_boost/mod.rs +++ b/Node/src/mev_boost/mod.rs @@ -8,12 +8,16 @@ use constraints::{Constraint, ConstraintsMessage, SignedConstraints}; pub struct MevBoost { rpc_client: RpcClient, + validator_index: u64, } impl MevBoost { - pub fn new(rpc_url: &str) -> Self { + pub fn new(rpc_url: &str, validator_index: u64) -> Self { let rpc_client = RpcClient::new(rpc_url); - Self { rpc_client } + Self { + rpc_client, + validator_index, + } } pub async fn force_inclusion( @@ -24,9 +28,8 @@ impl MevBoost { // Prepare the message // TODO check slot id value let slot_id = ethereum_l1.slot_clock.get_current_slot()?; - let validator_index = ethereum_l1.execution_layer.get_validator_index(); - let message = ConstraintsMessage::new(validator_index, slot_id, constraints); + let message = ConstraintsMessage::new(self.validator_index, slot_id, constraints); let data_to_sign: Vec = message.clone().into(); diff --git a/Node/src/node/block_porposer.rs b/Node/src/node/block_porposer_helper.rs similarity index 53% rename from Node/src/node/block_porposer.rs rename to Node/src/node/block_porposer_helper.rs index f41c9bc..e418a88 100644 --- a/Node/src/node/block_porposer.rs +++ b/Node/src/node/block_porposer_helper.rs @@ -1,22 +1,22 @@ -pub struct BlockProposer { +pub struct BlockProposerHelper { nonce: u64, block_id: u64, - final_perconfirmation_count: u8, + final_slot_perconfirmation_count: u8, } -impl BlockProposer { +impl BlockProposerHelper { pub fn new() -> Self { Self { nonce: 0, block_id: 0, - final_perconfirmation_count: 0, + final_slot_perconfirmation_count: 0, } } pub fn start_propose(&mut self, nonce: u64, block_id: u64) { self.nonce = nonce; self.block_id = block_id; - self.final_perconfirmation_count = 0; + self.final_slot_perconfirmation_count = 0; } pub fn propose_next(&mut self) -> (u64, u64) { @@ -27,11 +27,11 @@ impl BlockProposer { (nonce, block_id) } - pub fn increment_final_perconfirmation(&mut self) { - self.final_perconfirmation_count += 1; + pub fn increment_final_slot_perconfirmation(&mut self) { + self.final_slot_perconfirmation_count += 1; } - pub fn is_final_perconfirmation(&self) -> bool { - self.final_perconfirmation_count >= 3 + pub fn is_last_final_slot_perconfirmation(&self) -> bool { + self.final_slot_perconfirmation_count >= 3 } } diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index 838ba66..0b587b0 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -23,10 +23,10 @@ use tokio::sync::{ }; use tracing::info; -mod block_porposer; +mod block_porposer_helper; pub mod block_proposed_receiver; mod operator; -use block_porposer::BlockProposer; +use block_porposer_helper::BlockProposerHelper; use mev_boost::constraints::Constraint; @@ -39,7 +39,7 @@ pub struct Node { p2p_to_node_rx: Option>>, gas_used: u64, ethereum_l1: Arc, - mev_boost: MevBoost, // temporary unused + mev_boost: MevBoost, epoch: Epoch, lookahead: Vec, l2_slot_duration_sec: u64, @@ -47,7 +47,7 @@ pub struct Node { is_preconfer_now: Arc, preconfirmation_txs: Arc>>>, // block_id -> tx operator: Operator, - block_proposer: BlockProposer, + block_proposer: BlockProposerHelper, } impl Node { @@ -77,7 +77,7 @@ impl Node { is_preconfer_now: Arc::new(AtomicBool::new(false)), preconfirmation_txs: Arc::new(Mutex::new(HashMap::new())), operator, - block_proposer: BlockProposer::new(), + block_proposer: BlockProposerHelper::new(), }) } @@ -285,7 +285,7 @@ impl Node { match self.operator.get_status(current_slot)? { OperatorStatus::PreconferAndProposer => { self.preconfirm_block(current_epoch, false).await?; - if self.block_proposer.is_final_perconfirmation() { + if self.block_proposer.is_last_final_slot_perconfirmation() { // Last(4th) perconfirmation when we are proposer and preconfer self.is_preconfer_now.store(false, Ordering::Release); @@ -307,7 +307,7 @@ impl Node { } } else { // Increment perconfirmations count when we are proposer and preconfer - self.block_proposer.increment_final_perconfirmation(); + self.block_proposer.increment_final_slot_perconfirmation(); } } OperatorStatus::Preconfer => { From e8d91764e24498398b01f264327d86ecb065db74 Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Tue, 27 Aug 2024 11:50:32 +0200 Subject: [PATCH 6/7] Fixes from review --- Node/src/ethereum_l1/execution_layer.rs | 4 +- Node/src/node/mod.rs | 73 ++++++++++++------------- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/Node/src/ethereum_l1/execution_layer.rs b/Node/src/ethereum_l1/execution_layer.rs index de6263e..f069713 100644 --- a/Node/src/ethereum_l1/execution_layer.rs +++ b/Node/src/ethereum_l1/execution_layer.rs @@ -189,7 +189,7 @@ impl ExecutionLayer { tx_list: Vec, parent_meta_hash: [u8; 32], lookahead_set: Vec, - is_send: bool, + send_to_contract: bool, ) -> Result, Error> { let provider = ProviderBuilder::new().on_http(self.rpc_url.clone()); @@ -257,7 +257,7 @@ impl ExecutionLayer { tx.encode_with_signature(&signature, &mut buf, false); // Send transaction - if is_send { + if send_to_contract { let pending = provider .send_raw_transaction(&buf) .await? diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index 0b587b0..a23ac65 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -260,7 +260,6 @@ impl Node { self.epoch, current_epoch ); - let current_epoch = self.ethereum_l1.slot_clock.get_current_epoch()?; let current_epoch_timestamp = self .ethereum_l1 .slot_clock @@ -284,31 +283,7 @@ impl Node { match self.operator.get_status(current_slot)? { OperatorStatus::PreconferAndProposer => { - self.preconfirm_block(current_epoch, false).await?; - if self.block_proposer.is_last_final_slot_perconfirmation() { - // Last(4th) perconfirmation when we are proposer and preconfer - self.is_preconfer_now.store(false, Ordering::Release); - - let mut preconfirmation_txs = self.preconfirmation_txs.lock().await; - if !preconfirmation_txs.is_empty() { - // Build constraints - let constraints: Vec = preconfirmation_txs - .iter() - .map(|(_, value)| { - Constraint::new(format!("0x{}", hex::encode(value)), None) - }) - .collect(); - - self.mev_boost - .force_inclusion(constraints, self.ethereum_l1.clone()) - .await?; - - preconfirmation_txs.clear(); - } - } else { - // Increment perconfirmations count when we are proposer and preconfer - self.block_proposer.increment_final_slot_perconfirmation(); - } + self.preconfirm_last_slot().await?; } OperatorStatus::Preconfer => { if !self.is_preconfer_now.load(Ordering::Acquire) { @@ -316,7 +291,7 @@ impl Node { self.start_propose().await?; } - self.preconfirm_block(current_epoch, true).await?; + self.preconfirm_block(true).await?; } OperatorStatus::None => { tracing::debug!("Not my slot to preconfirm: {}", current_slot); @@ -326,16 +301,38 @@ impl Node { Ok(()) } + async fn preconfirm_last_slot(&mut self) -> Result<(), Error> { + self.preconfirm_block(false).await?; + if self.block_proposer.is_last_final_slot_perconfirmation() { + // Last(4th) perconfirmation when we are proposer and preconfer + self.is_preconfer_now.store(false, Ordering::Release); + + let mut preconfirmation_txs = self.preconfirmation_txs.lock().await; + if !preconfirmation_txs.is_empty() { + // Build constraints + let constraints: Vec = preconfirmation_txs + .iter() + .map(|(_, value)| Constraint::new(format!("0x{}", hex::encode(value)), None)) + .collect(); + + self.mev_boost + .force_inclusion(constraints, self.ethereum_l1.clone()) + .await?; + + preconfirmation_txs.clear(); + } + } else { + // Increment perconfirmations count when we are proposer and preconfer + self.block_proposer.increment_final_slot_perconfirmation(); + } + + Ok(()) + } + async fn start_propose(&mut self) -> Result<(), Error> { // get L2 block id - // TODO check that it returns real L2 height - let pending_tx_lists = self.taiko.get_pending_l2_tx_lists().await?; - if pending_tx_lists.tx_list_bytes.is_empty() { - return Err(Error::msg( - "Can't initialize new block proposal without txs", - )); - } - let new_block_height = pending_tx_lists.parent_block_id + 1; + // TODO get L2 height + let new_block_height = 0 + 1; // get L1 preconfer wallet nonce let nonce = self .ethereum_l1 @@ -347,7 +344,7 @@ impl Node { Ok(()) } - async fn preconfirm_block(&mut self, current_epoch: u64, is_send: bool) -> Result<(), Error> { + async fn preconfirm_block(&mut self, send_to_contract: bool) -> Result<(), Error> { tracing::debug!( "Preconfirming for the slot: {:?}", self.ethereum_l1.slot_clock.get_current_slot()? @@ -356,7 +353,7 @@ impl Node { let current_epoch_timestamp = self .ethereum_l1 .slot_clock - .get_epoch_begin_timestamp(current_epoch)?; + .get_epoch_begin_timestamp(self.epoch)?; if self .operator @@ -400,7 +397,7 @@ impl Node { pending_tx_lists.tx_list_bytes[0].clone(), //TODO: handle rest tx lists pending_tx_lists.parent_meta_hash, std::mem::take(&mut self.lookahead), - is_send, + send_to_contract, ) .await?; From bcc200d143bbeaa6738142946c6b7589dce0de4f Mon Sep 17 00:00:00 2001 From: mikhailUshakoff Date: Tue, 27 Aug 2024 12:51:53 +0200 Subject: [PATCH 7/7] Fix retrieval of new block height --- Node/src/node/mod.rs | 24 ++++++++++--------- ...er_helper.rs => preconfirmation_helper.rs} | 15 ++++-------- 2 files changed, 18 insertions(+), 21 deletions(-) rename Node/src/node/{block_porposer_helper.rs => preconfirmation_helper.rs} (62%) diff --git a/Node/src/node/mod.rs b/Node/src/node/mod.rs index a23ac65..1dd1122 100644 --- a/Node/src/node/mod.rs +++ b/Node/src/node/mod.rs @@ -23,10 +23,10 @@ use tokio::sync::{ }; use tracing::info; -mod block_porposer_helper; pub mod block_proposed_receiver; mod operator; -use block_porposer_helper::BlockProposerHelper; +mod preconfirmation_helper; +use preconfirmation_helper::PreconfirmationHelper; use mev_boost::constraints::Constraint; @@ -47,7 +47,7 @@ pub struct Node { is_preconfer_now: Arc, preconfirmation_txs: Arc>>>, // block_id -> tx operator: Operator, - block_proposer: BlockProposerHelper, + preconfirmation_helper: PreconfirmationHelper, } impl Node { @@ -77,7 +77,7 @@ impl Node { is_preconfer_now: Arc::new(AtomicBool::new(false)), preconfirmation_txs: Arc::new(Mutex::new(HashMap::new())), operator, - block_proposer: BlockProposerHelper::new(), + preconfirmation_helper: PreconfirmationHelper::new(), }) } @@ -303,7 +303,10 @@ impl Node { async fn preconfirm_last_slot(&mut self) -> Result<(), Error> { self.preconfirm_block(false).await?; - if self.block_proposer.is_last_final_slot_perconfirmation() { + if self + .preconfirmation_helper + .is_last_final_slot_perconfirmation() + { // Last(4th) perconfirmation when we are proposer and preconfer self.is_preconfer_now.store(false, Ordering::Release); @@ -323,16 +326,14 @@ impl Node { } } else { // Increment perconfirmations count when we are proposer and preconfer - self.block_proposer.increment_final_slot_perconfirmation(); + self.preconfirmation_helper + .increment_final_slot_perconfirmation(); } Ok(()) } async fn start_propose(&mut self) -> Result<(), Error> { - // get L2 block id - // TODO get L2 height - let new_block_height = 0 + 1; // get L1 preconfer wallet nonce let nonce = self .ethereum_l1 @@ -340,7 +341,7 @@ impl Node { .get_preconfer_nonce() .await?; - self.block_proposer.start_propose(nonce, new_block_height); + self.preconfirmation_helper.init(nonce); Ok(()) } @@ -368,7 +369,8 @@ impl Node { return Ok(()); } - let (nonce, new_block_height) = self.block_proposer.propose_next(); + let new_block_height = pending_tx_lists.parent_block_id + 1; + let nonce = self.preconfirmation_helper.get_next_nonce(); let (commit_hash, signature) = self.generate_commit_hash_and_signature(&pending_tx_lists, new_block_height)?; diff --git a/Node/src/node/block_porposer_helper.rs b/Node/src/node/preconfirmation_helper.rs similarity index 62% rename from Node/src/node/block_porposer_helper.rs rename to Node/src/node/preconfirmation_helper.rs index e418a88..6eb3246 100644 --- a/Node/src/node/block_porposer_helper.rs +++ b/Node/src/node/preconfirmation_helper.rs @@ -1,30 +1,25 @@ -pub struct BlockProposerHelper { +pub struct PreconfirmationHelper { nonce: u64, - block_id: u64, final_slot_perconfirmation_count: u8, } -impl BlockProposerHelper { +impl PreconfirmationHelper { pub fn new() -> Self { Self { nonce: 0, - block_id: 0, final_slot_perconfirmation_count: 0, } } - pub fn start_propose(&mut self, nonce: u64, block_id: u64) { + pub fn init(&mut self, nonce: u64) { self.nonce = nonce; - self.block_id = block_id; self.final_slot_perconfirmation_count = 0; } - pub fn propose_next(&mut self) -> (u64, u64) { + pub fn get_next_nonce(&mut self) -> u64 { let nonce = self.nonce; - let block_id = self.block_id; self.nonce += 1; - self.block_id += 1; - (nonce, block_id) + nonce } pub fn increment_final_slot_perconfirmation(&mut self) {