From 1470eb8824038ed10139f7e1424c7333769c9ea8 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 12:17:30 +0530 Subject: [PATCH 01/12] feat(sidecar): max committed gas wip --- bolt-sidecar/src/builder/template.rs | 12 ++++++++++++ bolt-sidecar/src/state/execution.rs | 15 +++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index f424c431d..68f9112e9 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -111,6 +111,18 @@ impl BlockTemplate { .fold(0, |acc, sc| acc + sc.message.constraints.len()) } + /// Returns the committed gas in the block template. + #[inline] + pub fn committed_gas(&self) -> U256 { + self.signed_constraints_list + .iter() + .fold(U256::ZERO, |acc, sc| { + acc + sc.message.constraints.iter().fold(U256::ZERO, |acc, c| { + acc + max_transaction_cost(&c.transaction) + }) + }) + } + /// Returns the blob count of the block template. #[inline] pub fn blob_count(&self) -> usize { diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 3f6b043d7..4ab08d938 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -60,6 +60,9 @@ pub enum ValidationError { /// The maximum commitments have been reached for the slot. #[error("Max commitments reached for slot {0}: {1}")] MaxCommitmentsReachedForSlot(u64, usize), + /// The maximum committed gas has been reached for the slot. + #[error("Max committed gas reached for slot {0}: {1}")] + MaxCommittedGasReachedForSlot(u64, u64), /// The signature is invalid. #[error("Signature error: {0:?}")] Signature(#[from] SignatureError), @@ -113,6 +116,8 @@ pub struct ExecutionState { chain_id: u64, /// The maximum number of commitments per slot. max_commitments_per_slot: NonZero, + /// The maximum committed gas per slot. + max_committed_gas_per_slot: NonZero, /// The KZG settings for validating blobs. kzg_settings: EnvKzgSettings, /// The state fetcher client. @@ -145,6 +150,7 @@ impl ExecutionState { pub async fn new( client: C, max_commitments_per_slot: NonZero, + max_committed_gas_per_slot: NonZero, ) -> Result { let (basefee, blob_basefee, block_number, chain_id) = tokio::try_join!( client.get_basefee(None), @@ -159,6 +165,7 @@ impl ExecutionState { block_number, chain_id, max_commitments_per_slot, + max_committed_gas_per_slot, client, slot: 0, account_states: HashMap::new(), @@ -208,6 +215,14 @@ impl ExecutionState { self.max_commitments_per_slot.get(), )); } + + // Check if the committed gas exceeds the maximum + if template.committed_gas().to::() >= self.max_committed_gas_per_slot.get() { + return Err(ValidationError::MaxCommittedGasReachedForSlot( + self.slot, + self.max_committed_gas_per_slot.get(), + )); + } } // Check if the transaction size exceeds the maximum From c408c2b5c04d7333948b0ce56d21deff23934ec7 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 12:38:38 +0530 Subject: [PATCH 02/12] chore(sidecar): fix mutable borrow conflict --- bolt-sidecar/src/state/execution.rs | 37 +++++++++++++++++++---------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 4ab08d938..0be10e9f4 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -207,20 +207,23 @@ impl ExecutionState { return Err(ValidationError::ChainIdMismatch); } - // Check if there is room for more commitments + let max_commitments_per_slot = self.max_commitments_per_slot.get(); + let max_committed_gas_per_slot = self.max_committed_gas_per_slot.get(); + + // Check if there is room for more commitments and gas in the block template if let Some(template) = self.get_block_template(target_slot) { - if template.transactions_len() >= self.max_commitments_per_slot.get() { + if template.transactions_len() >= max_commitments_per_slot { return Err(ValidationError::MaxCommitmentsReachedForSlot( self.slot, - self.max_commitments_per_slot.get(), + max_commitments_per_slot, )); } // Check if the committed gas exceeds the maximum - if template.committed_gas().to::() >= self.max_committed_gas_per_slot.get() { + if template.committed_gas().to::() >= max_committed_gas_per_slot { return Err(ValidationError::MaxCommittedGasReachedForSlot( self.slot, - self.max_committed_gas_per_slot.get(), + max_committed_gas_per_slot, )); } } @@ -476,7 +479,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -502,7 +506,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -543,7 +548,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -596,7 +602,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -627,7 +634,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -690,7 +698,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let basefee = state.basefee(); @@ -725,7 +734,8 @@ mod tests { let provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -792,7 +802,8 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let max_comms = NonZero::new(10).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms).await?; + let max_gas = NonZero::new(10_000_000).unwrap(); + let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); From 28a17c5d086b132d9d7ad86054c2e73b519cf58e Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 12:39:04 +0530 Subject: [PATCH 03/12] chore(sidecar): add max gas to cli --- bolt-sidecar/bin/sidecar.rs | 8 ++++++-- bolt-sidecar/src/config/mod.rs | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 04cde0908..47eb1a912 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -28,8 +28,12 @@ async fn main() -> eyre::Result<()> { let signer = Signer::new(config.private_key.clone().unwrap()); let state_client = StateClient::new(config.execution_api_url.clone()); - let mut execution_state = - ExecutionState::new(state_client, config.limits.max_commitments_per_slot).await?; + let mut execution_state = ExecutionState::new( + state_client, + config.limits.max_commitments_per_slot, + config.limits.max_committed_gas_per_slot, + ) + .await?; let mevboost_client = MevBoostClient::new(config.mevboost_url.clone()); let beacon_client = BeaconClient::new(config.beacon_api_url.clone()); diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index d0f60d595..e80318825 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -47,6 +47,9 @@ pub struct Opts { /// Max number of commitments to accept per block #[clap(short = 'm', long)] pub(super) max_commitments: Option>, + /// Max committed gas per slot + #[clap(short = 'g', long)] + pub(super) max_committed_gas: Option>, /// Validator indexes of connected validators that the sidecar /// should accept commitments on behalf of. Accepted values: /// - a comma-separated list of indexes (e.g. "1,2,3,4") @@ -137,12 +140,14 @@ impl Default for Config { pub struct Limits { /// Maximum number of commitments to accept per block pub max_commitments_per_slot: NonZero, + pub max_committed_gas_per_slot: NonZero, } impl Default for Limits { fn default() -> Self { Self { max_commitments_per_slot: NonZero::new(6).expect("Valid non-zero"), + max_committed_gas_per_slot: NonZero::new(10_000_000).expect("Valid non-zero"), } } } @@ -169,6 +174,10 @@ impl TryFrom for Config { config.limits.max_commitments_per_slot = max_commitments; } + if let Some(max_committed_gas) = opts.max_committed_gas { + config.limits.max_committed_gas_per_slot = max_committed_gas; + } + config.commit_boost_url = opts .signing .commit_boost_url From 2c15ceccf2dc9859283481100c8693554d36653b Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 13:42:20 +0530 Subject: [PATCH 04/12] chore(sidecar): clean code and address review --- bolt-sidecar/bin/sidecar.rs | 7 +-- bolt-sidecar/src/builder/template.rs | 16 +++--- bolt-sidecar/src/state/execution.rs | 77 ++++++++++++++++------------ 3 files changed, 54 insertions(+), 46 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 47eb1a912..0fa50d7d3 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -28,12 +28,7 @@ async fn main() -> eyre::Result<()> { let signer = Signer::new(config.private_key.clone().unwrap()); let state_client = StateClient::new(config.execution_api_url.clone()); - let mut execution_state = ExecutionState::new( - state_client, - config.limits.max_commitments_per_slot, - config.limits.max_committed_gas_per_slot, - ) - .await?; + let mut execution_state = ExecutionState::new(state_client, config.limits.clone()).await?; let mevboost_client = MevBoostClient::new(config.mevboost_url.clone()); let beacon_client = BeaconClient::new(config.beacon_api_url.clone()); diff --git a/bolt-sidecar/src/builder/template.rs b/bolt-sidecar/src/builder/template.rs index 68f9112e9..fdd59d755 100644 --- a/bolt-sidecar/src/builder/template.rs +++ b/bolt-sidecar/src/builder/template.rs @@ -113,14 +113,14 @@ impl BlockTemplate { /// Returns the committed gas in the block template. #[inline] - pub fn committed_gas(&self) -> U256 { - self.signed_constraints_list - .iter() - .fold(U256::ZERO, |acc, sc| { - acc + sc.message.constraints.iter().fold(U256::ZERO, |acc, c| { - acc + max_transaction_cost(&c.transaction) - }) - }) + pub fn committed_gas(&self) -> u64 { + self.signed_constraints_list.iter().fold(0, |acc, sc| { + acc + sc + .message + .constraints + .iter() + .fold(0, |acc, c| acc + &c.transaction.gas_limit()) + }) } /// Returns the blob count of the block template. diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 0be10e9f4..26efd123c 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -10,6 +10,7 @@ use thiserror::Error; use crate::{ builder::BlockTemplate, common::{calculate_max_basefee, validate_transaction}, + config::Limits, primitives::{AccountState, CommitmentRequest, SignedConstraints, Slot, TransactionExt}, }; @@ -147,11 +148,7 @@ impl Default for ValidationParams { impl ExecutionState { /// Creates a new state with the given client, initializing the /// basefee and head block number. - pub async fn new( - client: C, - max_commitments_per_slot: NonZero, - max_committed_gas_per_slot: NonZero, - ) -> Result { + pub async fn new(client: C, limits: Limits) -> Result { let (basefee, blob_basefee, block_number, chain_id) = tokio::try_join!( client.get_basefee(None), client.get_blob_basefee(None), @@ -164,8 +161,8 @@ impl ExecutionState { blob_basefee, block_number, chain_id, - max_commitments_per_slot, - max_committed_gas_per_slot, + max_commitments_per_slot: limits.max_commitments_per_slot, + max_committed_gas_per_slot: limits.max_committed_gas_per_slot, client, slot: 0, account_states: HashMap::new(), @@ -220,7 +217,7 @@ impl ExecutionState { } // Check if the committed gas exceeds the maximum - if template.committed_gas().to::() >= max_committed_gas_per_slot { + if template.committed_gas() + req.tx.gas_limit() >= max_committed_gas_per_slot { return Err(ValidationError::MaxCommittedGasReachedForSlot( self.slot, max_committed_gas_per_slot, @@ -478,9 +475,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -505,9 +504,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -547,9 +548,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -601,9 +604,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -633,9 +638,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -697,9 +704,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let basefee = state.basefee(); @@ -733,9 +742,11 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -801,9 +812,11 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let max_comms = NonZero::new(10).unwrap(); - let max_gas = NonZero::new(10_000_000).unwrap(); - let mut state = ExecutionState::new(client.clone(), max_comms, max_gas).await?; + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); From 7dd705a99688703a07566e336822ca499204bd8b Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 16:26:11 +0530 Subject: [PATCH 05/12] chore(sidecar): add tests and fix bug --- bolt-sidecar/src/state/execution.rs | 113 ++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 7 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 26efd123c..a4e83d53a 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -204,25 +204,32 @@ impl ExecutionState { return Err(ValidationError::ChainIdMismatch); } - let max_commitments_per_slot = self.max_commitments_per_slot.get(); - let max_committed_gas_per_slot = self.max_committed_gas_per_slot.get(); - // Check if there is room for more commitments and gas in the block template if let Some(template) = self.get_block_template(target_slot) { - if template.transactions_len() >= max_commitments_per_slot { + if template.transactions_len() >= self.max_commitments_per_slot.get() { return Err(ValidationError::MaxCommitmentsReachedForSlot( self.slot, - max_commitments_per_slot, + self.max_commitments_per_slot.get(), )); } + } + if let Some(template) = self.get_block_template(target_slot) { // Check if the committed gas exceeds the maximum - if template.committed_gas() + req.tx.gas_limit() >= max_committed_gas_per_slot { + if template.committed_gas() + req.tx.gas_limit() + >= self.max_committed_gas_per_slot.get() + { return Err(ValidationError::MaxCommittedGasReachedForSlot( self.slot, - max_committed_gas_per_slot, + self.max_committed_gas_per_slot.get(), )); } + } else if req.tx.gas_limit() >= self.max_committed_gas_per_slot.get() { + // Check if the committed gas limit itself exceeds the maximum when template is None + return Err(ValidationError::MaxCommittedGasReachedForSlot( + self.slot, + self.max_committed_gas_per_slot.get(), + )); } // Check if the transaction size exceeds the maximum @@ -734,6 +741,38 @@ mod tests { Ok(()) } + #[tokio::test] + async fn test_invalid_inclusion_request_with_excess_gas() -> eyre::Result<()> { + let _ = tracing_subscriber::fmt::try_init(); + + let anvil = launch_anvil(); + let client = StateClient::new(anvil.endpoint_url()); + + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; + + let sender = anvil.addresses().first().unwrap(); + let sender_pk = anvil.keys().first().unwrap(); + + // initialize the state by updating the head once + let slot = client.get_head().await?; + state.update_head(None, slot).await?; + + let tx = default_test_transaction(*sender, None).with_gas_limit(6_000_000); + + let request = create_signed_commitment_request(tx, sender_pk, 10).await?; + + assert!(matches!( + state.validate_commitment_request(&request).await, + Err(ValidationError::MaxCommittedGasReachedForSlot(_, 5_000_000)) + )); + + Ok(()) + } + #[tokio::test] async fn test_invalidate_inclusion_request() -> eyre::Result<()> { let _ = tracing_subscriber::fmt::try_init(); @@ -856,4 +895,64 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn test_invalidate_inclusion_request_with_excess_gas() -> eyre::Result<()> { + let _ = tracing_subscriber::fmt::try_init(); + + let anvil = launch_anvil(); + let client = StateClient::new(anvil.endpoint_url()); + + let limits: Limits = Limits { + max_commitments_per_slot: NonZero::new(10).unwrap(), + max_committed_gas_per_slot: NonZero::new(5_000_000).unwrap(), + }; + let mut state = ExecutionState::new(client.clone(), limits).await?; + + let sender = anvil.addresses().first().unwrap(); + let sender_pk = anvil.keys().first().unwrap(); + + // initialize the state by updating the head once + let slot = client.get_head().await?; + state.update_head(None, slot).await?; + + let tx = default_test_transaction(*sender, None).with_gas_limit(4_999_999); + + // build the signed transaction for submission later + let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); + let signer: EthereumWallet = wallet.into(); + + let target_slot = 10; + let request = create_signed_commitment_request(tx, sender_pk, target_slot).await?; + let inclusion_request = request.as_inclusion_request().unwrap().clone(); + + assert!(state.validate_commitment_request(&request).await.is_ok()); + + let bls_signer = Signer::random(); + let message = ConstraintsMessage::build(0, inclusion_request); + let signature = bls_signer.sign(&message.digest()).unwrap().to_string(); + let signed_constraints = SignedConstraints { message, signature }; + + state.add_constraint(target_slot, signed_constraints); + + assert!( + state + .get_block_template(target_slot) + .unwrap() + .transactions_len() + == 1 + ); + + // This tx will exceed the committed gas limit + let tx = default_test_transaction(*sender, Some(1)); + + let request = create_signed_commitment_request(tx, sender_pk, 10).await?; + + assert!(matches!( + state.validate_commitment_request(&request).await, + Err(ValidationError::MaxCommittedGasReachedForSlot(_, 5_000_000)) + )); + + Ok(()) + } } From bdbe371005c6f42ec49ca9fec08cc82b2a152b4a Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 16:26:47 +0530 Subject: [PATCH 06/12] chore(sidecar): test --- bolt-sidecar/src/state/execution.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index a4e83d53a..73fac2969 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -918,10 +918,6 @@ mod tests { let tx = default_test_transaction(*sender, None).with_gas_limit(4_999_999); - // build the signed transaction for submission later - let wallet: PrivateKeySigner = anvil.keys()[0].clone().into(); - let signer: EthereumWallet = wallet.into(); - let target_slot = 10; let request = create_signed_commitment_request(tx, sender_pk, target_slot).await?; let inclusion_request = request.as_inclusion_request().unwrap().clone(); From 75f4dfe297342b90854e24ba261db4d8cbeccadc Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 16:28:38 +0530 Subject: [PATCH 07/12] chore(sidecar): test --- bolt-sidecar/src/state/execution.rs | 42 +++++------------------------ 1 file changed, 7 insertions(+), 35 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 73fac2969..34a51608f 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -482,11 +482,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -511,11 +507,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -555,11 +547,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -645,11 +633,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -711,11 +695,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let basefee = state.basefee(); @@ -781,11 +761,7 @@ mod tests { let client = StateClient::new(anvil.endpoint_url()); let provider = ProviderBuilder::new().on_http(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); @@ -851,11 +827,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap(); From 0a2d652c24165c8f4a8999b1b9ff09e60f526acf Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 16:44:20 +0530 Subject: [PATCH 08/12] misc: update comment --- bolt-sidecar/src/state/execution.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 34a51608f..c5aff4f76 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -204,7 +204,7 @@ impl ExecutionState { return Err(ValidationError::ChainIdMismatch); } - // Check if there is room for more commitments and gas in the block template + // Check if there is room for more commitments if let Some(template) = self.get_block_template(target_slot) { if template.transactions_len() >= self.max_commitments_per_slot.get() { return Err(ValidationError::MaxCommitmentsReachedForSlot( @@ -214,6 +214,7 @@ impl ExecutionState { } } + // Check if the committed gas exceeds the maximum if let Some(template) = self.get_block_template(target_slot) { // Check if the committed gas exceeds the maximum if template.committed_gas() + req.tx.gas_limit() From c36b542030b42781683079782bee70fb7e9b21f0 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 17:12:38 +0530 Subject: [PATCH 09/12] chore(sidecar): refactor excess gas check Co-authored-by: Lorenzo --- bolt-sidecar/src/state/execution.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index c5aff4f76..dcc910029 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -224,15 +224,14 @@ impl ExecutionState { self.slot, self.max_committed_gas_per_slot.get(), )); - } - } else if req.tx.gas_limit() >= self.max_committed_gas_per_slot.get() { - // Check if the committed gas limit itself exceeds the maximum when template is None + // Check if the committed gas exceeds the maximum + let template_committed_gas = self.get_block_template(target_slot).map(|t| t.committed_gas()).unwrap_or(0); + if template_committed_gas + req.tx.gas_limit() >= self.max_committed_gas_per_slot.get() { return Err(ValidationError::MaxCommittedGasReachedForSlot( self.slot, self.max_committed_gas_per_slot.get(), )); } - // Check if the transaction size exceeds the maximum if req.tx.size() > self.validation_params.max_tx_input_bytes { return Err(ValidationError::TransactionSizeTooHigh); From fbe85592901b9eae5a10e19a6571de12dfb2343a Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 17:15:55 +0530 Subject: [PATCH 10/12] chore(sidecar): fix refactor --- bolt-sidecar/src/state/execution.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index dcc910029..ae75bad2a 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -215,23 +215,17 @@ impl ExecutionState { } // Check if the committed gas exceeds the maximum - if let Some(template) = self.get_block_template(target_slot) { - // Check if the committed gas exceeds the maximum - if template.committed_gas() + req.tx.gas_limit() - >= self.max_committed_gas_per_slot.get() - { - return Err(ValidationError::MaxCommittedGasReachedForSlot( - self.slot, - self.max_committed_gas_per_slot.get(), - )); - // Check if the committed gas exceeds the maximum - let template_committed_gas = self.get_block_template(target_slot).map(|t| t.committed_gas()).unwrap_or(0); + let template_committed_gas = self + .get_block_template(target_slot) + .map(|t| t.committed_gas()) + .unwrap_or(0); if template_committed_gas + req.tx.gas_limit() >= self.max_committed_gas_per_slot.get() { return Err(ValidationError::MaxCommittedGasReachedForSlot( self.slot, self.max_committed_gas_per_slot.get(), )); } + // Check if the transaction size exceeds the maximum if req.tx.size() > self.validation_params.max_tx_input_bytes { return Err(ValidationError::TransactionSizeTooHigh); From 6e3aae95ccd2da8d7d542acdce38f4ac9bc0a90e Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 17:43:46 +0530 Subject: [PATCH 11/12] chore(sidecar): solve nits --- bolt-sidecar/bin/sidecar.rs | 2 +- bolt-sidecar/src/config/mod.rs | 2 +- bolt-sidecar/src/state/execution.rs | 21 ++++++++++----------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/bolt-sidecar/bin/sidecar.rs b/bolt-sidecar/bin/sidecar.rs index 0fa50d7d3..8a49b1cf6 100644 --- a/bolt-sidecar/bin/sidecar.rs +++ b/bolt-sidecar/bin/sidecar.rs @@ -28,7 +28,7 @@ async fn main() -> eyre::Result<()> { let signer = Signer::new(config.private_key.clone().unwrap()); let state_client = StateClient::new(config.execution_api_url.clone()); - let mut execution_state = ExecutionState::new(state_client, config.limits.clone()).await?; + let mut execution_state = ExecutionState::new(state_client, config.limits).await?; let mevboost_client = MevBoostClient::new(config.mevboost_url.clone()); let beacon_client = BeaconClient::new(config.beacon_api_url.clone()); diff --git a/bolt-sidecar/src/config/mod.rs b/bolt-sidecar/src/config/mod.rs index e80318825..5c96e1669 100644 --- a/bolt-sidecar/src/config/mod.rs +++ b/bolt-sidecar/src/config/mod.rs @@ -136,7 +136,7 @@ impl Default for Config { } /// Limits for the sidecar. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct Limits { /// Maximum number of commitments to accept per block pub max_commitments_per_slot: NonZero, diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index ae75bad2a..0d7c8b160 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -4,7 +4,7 @@ use alloy_transport::TransportError; use reth_primitives::{ revm_primitives::EnvKzgSettings, BlobTransactionValidationError, PooledTransactionsElement, }; -use std::{collections::HashMap, num::NonZero}; +use std::collections::HashMap; use thiserror::Error; use crate::{ @@ -115,10 +115,8 @@ pub struct ExecutionState { block_templates: HashMap, /// The chain ID of the chain (constant). chain_id: u64, - /// The maximum number of commitments per slot. - max_commitments_per_slot: NonZero, - /// The maximum committed gas per slot. - max_committed_gas_per_slot: NonZero, + /// The limits set for the sidecar. + limits: Limits, /// The KZG settings for validating blobs. kzg_settings: EnvKzgSettings, /// The state fetcher client. @@ -161,8 +159,7 @@ impl ExecutionState { blob_basefee, block_number, chain_id, - max_commitments_per_slot: limits.max_commitments_per_slot, - max_committed_gas_per_slot: limits.max_committed_gas_per_slot, + limits, client, slot: 0, account_states: HashMap::new(), @@ -206,10 +203,10 @@ impl ExecutionState { // Check if there is room for more commitments if let Some(template) = self.get_block_template(target_slot) { - if template.transactions_len() >= self.max_commitments_per_slot.get() { + if template.transactions_len() >= self.limits.max_commitments_per_slot.get() { return Err(ValidationError::MaxCommitmentsReachedForSlot( self.slot, - self.max_commitments_per_slot.get(), + self.limits.max_commitments_per_slot.get(), )); } } @@ -219,10 +216,12 @@ impl ExecutionState { .get_block_template(target_slot) .map(|t| t.committed_gas()) .unwrap_or(0); - if template_committed_gas + req.tx.gas_limit() >= self.max_committed_gas_per_slot.get() { + if template_committed_gas + req.tx.gas_limit() + >= self.limits.max_committed_gas_per_slot.get() + { return Err(ValidationError::MaxCommittedGasReachedForSlot( self.slot, - self.max_committed_gas_per_slot.get(), + self.limits.max_committed_gas_per_slot.get(), )); } From d2e6a8e31a809288fbf5ac9d38ef26856539a6a4 Mon Sep 17 00:00:00 2001 From: Naman Garg <0708ng@gmail.com> Date: Fri, 19 Jul 2024 17:54:42 +0530 Subject: [PATCH 12/12] misc: default limit --- bolt-sidecar/src/state/execution.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 0d7c8b160..6d4e0f568 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -592,11 +592,7 @@ mod tests { let anvil = launch_anvil(); let client = StateClient::new(anvil.endpoint_url()); - let limits: Limits = Limits { - max_commitments_per_slot: NonZero::new(10).unwrap(), - max_committed_gas_per_slot: NonZero::new(10_000_000).unwrap(), - }; - let mut state = ExecutionState::new(client.clone(), limits).await?; + let mut state = ExecutionState::new(client.clone(), Limits::default()).await?; let sender = anvil.addresses().first().unwrap(); let sender_pk = anvil.keys().first().unwrap();