From 8fb651b1a0150fccadc9e50a51227a94ccab181c Mon Sep 17 00:00:00 2001 From: nicolas <48695862+merklefruit@users.noreply.github.com> Date: Wed, 17 Jul 2024 11:10:31 +0200 Subject: [PATCH] chore: added commitment balance validation multiple test --- bolt-sidecar/src/client/rpc.rs | 5 +++ bolt-sidecar/src/state/execution.rs | 6 +-- bolt-sidecar/src/state/fetcher.rs | 7 +++ bolt-sidecar/src/state/mod.rs | 67 ++++++++++++++++++++++++++++- 4 files changed, 80 insertions(+), 5 deletions(-) diff --git a/bolt-sidecar/src/client/rpc.rs b/bolt-sidecar/src/client/rpc.rs index deac4704..6acf3246 100644 --- a/bolt-sidecar/src/client/rpc.rs +++ b/bolt-sidecar/src/client/rpc.rs @@ -193,6 +193,11 @@ impl RpcClient { self.0.request("debug_traceCall", params).await } + + /// Send a raw transaction to the network. + pub async fn send_raw_transaction(&self, raw: Bytes) -> TransportResult { + self.0.request("eth_sendRawTransaction", [raw]).await + } } impl Deref for RpcClient { diff --git a/bolt-sidecar/src/state/execution.rs b/bolt-sidecar/src/state/execution.rs index 84d8929b..fc5376e9 100644 --- a/bolt-sidecar/src/state/execution.rs +++ b/bolt-sidecar/src/state/execution.rs @@ -267,7 +267,7 @@ impl ExecutionState { ( nonce_diff_acc + nonce_diff, - balance_diff_acc + balance_diff, + balance_diff_acc.saturating_add(balance_diff), u64::max(highest_slot, slot), ) }, @@ -299,8 +299,8 @@ impl ExecutionState { }; let account_state_with_diffs = AccountState { - transaction_count: account_state.transaction_count + nonce_diff, - balance: account_state.balance - balance_diff, + transaction_count: account_state.transaction_count.saturating_add(nonce_diff), + balance: account_state.balance.saturating_sub(balance_diff), has_code: account_state.has_code, }; diff --git a/bolt-sidecar/src/state/fetcher.rs b/bolt-sidecar/src/state/fetcher.rs index cebf5b02..2513c383 100644 --- a/bolt-sidecar/src/state/fetcher.rs +++ b/bolt-sidecar/src/state/fetcher.rs @@ -217,6 +217,13 @@ impl StateFetcher for StateClient { } } +#[cfg(test)] +impl StateClient { + pub fn inner(&self) -> &RpcClient { + &self.client + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/bolt-sidecar/src/state/mod.rs b/bolt-sidecar/src/state/mod.rs index 1c0eabcb..3b1b9ce3 100644 --- a/bolt-sidecar/src/state/mod.rs +++ b/bolt-sidecar/src/state/mod.rs @@ -68,12 +68,12 @@ impl Future for CommitmentDeadline { #[cfg(test)] mod tests { - use std::num::NonZero; + use std::{num::NonZero, str::FromStr}; use alloy_consensus::constants::ETH_TO_WEI; use alloy_eips::eip2718::Encodable2718; use alloy_network::EthereumWallet; - use alloy_primitives::{uint, Uint}; + use alloy_primitives::{uint, Uint, U256}; use alloy_provider::{network::TransactionBuilder, Provider, ProviderBuilder}; use alloy_signer_local::PrivateKeySigner; use execution::{ExecutionState, ValidationError}; @@ -189,6 +189,69 @@ mod tests { Ok(()) } + #[tokio::test] + async fn test_invalid_inclusion_request_balance_multiple() -> eyre::Result<()> { + let _ = tracing_subscriber::fmt::try_init(); + + let anvil = launch_anvil(); + 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 sender = anvil.addresses().first().unwrap(); + let sender_pk = anvil.keys().first().unwrap(); + let signer = Signer::random(); + + // initialize the state by updating the head once + let slot = client.get_head().await?; + state.update_head(None, slot).await?; + + // Set the sender balance to just enough to pay for 1 transaction + let balance = U256::from_str("500000000000000").unwrap(); // leave just 0.0005 ETH + let sender_account = client.get_account_state(sender, None).await.unwrap(); + let balance_to_burn = sender_account.balance - balance; + + // burn the balance + let tx = default_test_transaction(*sender, Some(0)).with_value(uint!(balance_to_burn)); + let request = create_signed_commitment_request(tx, sender_pk, 10).await?; + let tx_bytes = request + .as_inclusion_request() + .unwrap() + .tx + .envelope_encoded(); + let _ = client.inner().send_raw_transaction(tx_bytes).await?; + + // wait for the transaction to be included to update the sender balance + tokio::time::sleep(Duration::from_secs(2)).await; + let slot = client.get_head().await?; + state.update_head(None, slot).await?; + + // create a new transaction and request a preconfirmation for it + let tx = default_test_transaction(*sender, Some(1)); + let request = create_signed_commitment_request(tx, sender_pk, 10).await?; + + assert!(state.validate_commitment_request(&request).await.is_ok()); + + let message = ConstraintsMessage::build(0, request.as_inclusion_request().unwrap().clone()); + let signature = signer.sign(&message.digest())?.to_string(); + let signed_constraints = SignedConstraints { message, signature }; + state.add_constraint(10, signed_constraints); + + // create a new transaction and request a preconfirmation for it + let tx = default_test_transaction(*sender, Some(3)); + let request = create_signed_commitment_request(tx, sender_pk, 10).await?; + + // this should fail because the balance is insufficient as we spent + // all of it on the previous preconfirmation + assert!(matches!( + state.validate_commitment_request(&request).await, + Err(ValidationError::InsufficientBalance) + )); + + Ok(()) + } + #[tokio::test] async fn test_invalid_inclusion_request_basefee() -> eyre::Result<()> { let _ = tracing_subscriber::fmt::try_init();