diff --git a/src/config.rs b/src/config.rs index be110d6..c90d995 100644 --- a/src/config.rs +++ b/src/config.rs @@ -20,8 +20,9 @@ const QUINCEY_URL: &str = "QUINCEY_URL"; const BUILDER_PORT: &str = "BUILDER_PORT"; const SEQUENCER_KEY: &str = "SEQUENCER_KEY"; // empty (to use Quincey) OR AWS key ID (to use AWS signer) OR raw private key (to use local signer) const BUILDER_KEY: &str = "BUILDER_KEY"; // AWS key ID (to use AWS signer) OR raw private key (to use local signer) -const INCOMING_TRANSACTIONS_BUFFER: &str = "INCOMING_TRANSACTIONS_BUFFER"; const BLOCK_CONFIRMATION_BUFFER: &str = "BLOCK_CONFIRMATION_BUFFER"; +const CHAIN_OFFSET: &str = "CHAIN_OFFSET"; +const TARGET_SLOT_TIME: &str = "TARGET_SLOT_TIME"; const BUILDER_REWARDS_ADDRESS: &str = "BUILDER_REWARDS_ADDRESS"; const ROLLUP_BLOCK_GAS_LIMIT: &str = "ROLLUP_BLOCK_GAS_LIMIT"; const TX_POOL_URL: &str = "TX_POOL_URL"; @@ -57,10 +58,12 @@ pub struct BuilderConfig { pub sequencer_key: Option, /// Key to access Builder transaction submission wallet - AWS Key ID _OR_ local private key. pub builder_key: String, - /// Buffer in seconds that Builder will wait & accept incoming transactions before bundling them and submitting as a block. - pub incoming_transactions_buffer: u64, /// Buffer in seconds in which the `submitBlock` transaction must confirm on the Host chain. pub block_confirmation_buffer: u64, + /// The offset between Unix time and the chain's block times. For Holesky, this is 0; for Ethereum, 11. + pub chain_offset: u64, + /// The slot time at which the Builder should begin building a block. 0 to begin at the very start of the slot; 6 to begin in the middle; etc. + pub target_slot_time: u64, /// Address on Rollup to which Builder will receive user transaction fees. pub builder_rewards_address: Address, /// Gas limit for RU block. @@ -147,8 +150,9 @@ impl BuilderConfig { builder_port: load_u16(BUILDER_PORT)?, sequencer_key: load_string_option(SEQUENCER_KEY), builder_key: load_string(BUILDER_KEY)?, - incoming_transactions_buffer: load_u64(INCOMING_TRANSACTIONS_BUFFER)?, block_confirmation_buffer: load_u64(BLOCK_CONFIRMATION_BUFFER)?, + chain_offset: load_u64(CHAIN_OFFSET)?, + target_slot_time: load_u64(TARGET_SLOT_TIME)?, builder_rewards_address: load_address(BUILDER_REWARDS_ADDRESS)?, rollup_block_gas_limit: load_u64(ROLLUP_BLOCK_GAS_LIMIT)?, tx_pool_url: load_url(TX_POOL_URL)?, diff --git a/src/tasks/block.rs b/src/tasks/block.rs index 2a25c3d..0c08e0e 100644 --- a/src/tasks/block.rs +++ b/src/tasks/block.rs @@ -4,6 +4,7 @@ use alloy::{ }; use alloy_primitives::{keccak256, Bytes, B256}; use alloy_rlp::Buf; +use std::time::{SystemTime, UNIX_EPOCH}; use std::{sync::OnceLock, time::Duration}; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::Instrument; @@ -14,6 +15,9 @@ use super::oauth::Authenticator; use super::tx_poller::TxPoller; use crate::config::BuilderConfig; +/// Ethereum's slot time in seconds. +pub const ETHEREUM_SLOT_TIME: u64 = 12; + #[derive(Debug, Default, Clone)] /// A block in progress. pub struct InProgressBlock { @@ -108,7 +112,6 @@ impl InProgressBlock { /// BlockBuilder is a task that periodically builds a block then sends it for signing and submission. pub struct BlockBuilder { - pub incoming_transactions_buffer: u64, pub config: BuilderConfig, pub tx_poller: TxPoller, pub bundle_poller: BundlePoller, @@ -119,7 +122,6 @@ impl BlockBuilder { pub fn new(config: &BuilderConfig, authenticator: Authenticator) -> Self { Self { config: config.clone(), - incoming_transactions_buffer: config.incoming_transactions_buffer, tx_poller: TxPoller::new(config), bundle_poller: BundlePoller::new(config, authenticator), } @@ -155,6 +157,18 @@ impl BlockBuilder { self.bundle_poller.evict(); } + // calculate the duration in seconds until the beginning of the next block slot. + fn secs_to_next_slot(&mut self) -> u64 { + let curr_timestamp: u64 = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + let current_slot_time = (curr_timestamp - self.config.chain_offset) % ETHEREUM_SLOT_TIME; + (ETHEREUM_SLOT_TIME - current_slot_time) % ETHEREUM_SLOT_TIME + } + + // add a buffer to the beginning of the block slot. + fn secs_to_next_target(&mut self) -> u64 { + self.secs_to_next_slot() + self.config.target_slot_time + } + /// Spawn the block builder task, returning the inbound channel to it, and /// a handle to the running task. pub fn spawn(mut self, outbound: mpsc::UnboundedSender) -> JoinHandle<()> { @@ -162,8 +176,8 @@ impl BlockBuilder { async move { loop { // sleep the buffer time - tokio::time::sleep(Duration::from_secs(self.incoming_transactions_buffer)) - .await; + tokio::time::sleep(Duration::from_secs(self.secs_to_next_target())).await; + tracing::trace!("beginning block build cycle"); // Build a block let mut in_progress = InProgressBlock::default(); @@ -178,6 +192,8 @@ impl BlockBuilder { tracing::debug!("downstream task gone"); break; } + } else { + tracing::debug!("no transactions, skipping block submission"); } } } diff --git a/src/tasks/oauth.rs b/src/tasks/oauth.rs index 76b49a4..b9e5d45 100644 --- a/src/tasks/oauth.rs +++ b/src/tasks/oauth.rs @@ -145,8 +145,9 @@ mod tests { builder_port: 8080, sequencer_key: None, builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - incoming_transactions_buffer: 1, block_confirmation_buffer: 1, + chain_offset: 0, + target_slot_time: 1, builder_rewards_address: Address::default(), rollup_block_gas_limit: 100_000, tx_pool_url: "http://localhost:9000/".into(), diff --git a/tests/bundle_poller_test.rs b/tests/bundle_poller_test.rs index 9b3d701..47a9906 100644 --- a/tests/bundle_poller_test.rs +++ b/tests/bundle_poller_test.rs @@ -26,8 +26,9 @@ mod tests { builder_port: 8080, sequencer_key: None, builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - incoming_transactions_buffer: 1, block_confirmation_buffer: 1, + chain_offset: 0, + target_slot_time: 1, builder_rewards_address: Address::default(), rollup_block_gas_limit: 100_000, tx_pool_url: "http://localhost:9000/".into(), diff --git a/tests/tx_poller_test.rs b/tests/tx_poller_test.rs index 8a1ad20..98d018d 100644 --- a/tests/tx_poller_test.rs +++ b/tests/tx_poller_test.rs @@ -74,8 +74,9 @@ mod tests { builder_port: 8080, sequencer_key: None, builder_key: "0000000000000000000000000000000000000000000000000000000000000000".into(), - incoming_transactions_buffer: 1, block_confirmation_buffer: 1, + chain_offset: 0, + target_slot_time: 1, builder_rewards_address: Address::default(), rollup_block_gas_limit: 100_000, tx_pool_url: "http://localhost:9000/".into(),