Skip to content

Commit 2ea9cc9

Browse files
committed
spike
1 parent 27674ae commit 2ea9cc9

File tree

3 files changed

+58
-4
lines changed

3 files changed

+58
-4
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ingress-rpc/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ op-revm.workspace = true
3434
revm-context-interface.workspace = true
3535
alloy-signer-local.workspace = true
3636
reth-optimism-evm.workspace = true
37+
serde.workspace = true

crates/ingress-rpc/src/service.rs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloy_consensus::transaction::Recovered;
22
use alloy_consensus::{Transaction, transaction::SignerRecoverable};
3-
use alloy_primitives::{B256, Bytes};
3+
use alloy_primitives::{Address, B256, Bytes, TxHash};
44
use alloy_provider::{Provider, RootProvider, network::eip2718::Decodable2718};
55
use jsonrpsee::{
66
core::{RpcResult, async_trait},
@@ -9,13 +9,46 @@ use jsonrpsee::{
99
use op_alloy_consensus::OpTxEnvelope;
1010
use op_alloy_network::Optimism;
1111
use reth_rpc_eth_types::EthApiError;
12+
use serde::{Deserialize, Serialize};
1213
use std::time::{SystemTime, UNIX_EPOCH};
1314
use tips_core::{Bundle, BundleHash, BundleWithMetadata, CancelBundle};
1415
use tracing::{info, warn};
1516

1617
use crate::queue::QueuePublisher;
1718
use crate::validation::{AccountInfoLookup, L1BlockInfoLookup, validate_bundle, validate_tx};
1819

20+
#[derive(Debug, Clone, Serialize, Deserialize)]
21+
#[serde(rename_all = "camelCase")]
22+
pub struct TransactionResult {
23+
pub coinbase_diff: String,
24+
pub eth_sent_to_coinbase: String,
25+
pub from_address: Address,
26+
pub gas_fees: String,
27+
pub gas_price: String,
28+
pub gas_used: u64,
29+
pub to_address: Option<Address>,
30+
pub tx_hash: TxHash,
31+
pub value: String,
32+
/// Resource metering: execution time for this tx in microseconds
33+
pub execution_time_us: u128,
34+
}
35+
36+
/// Response for base_meterBundle
37+
#[derive(Debug, Clone, Serialize, Deserialize)]
38+
#[serde(rename_all = "camelCase")]
39+
pub struct MeterBundleResponse {
40+
pub bundle_gas_price: String,
41+
pub bundle_hash: B256,
42+
pub coinbase_diff: String,
43+
pub eth_sent_to_coinbase: String,
44+
pub gas_fees: String,
45+
pub results: Vec<TransactionResult>,
46+
pub state_block_number: u64,
47+
pub total_gas_used: u64,
48+
/// Resource metering: total execution time in microseconds
49+
pub total_execution_time_us: u128,
50+
}
51+
1952
#[rpc(server, namespace = "eth")]
2053
pub trait IngressApi {
2154
/// `eth_sendBundle` can be used to send your bundles to the builder.
@@ -60,7 +93,8 @@ where
6093
Queue: QueuePublisher + Sync + Send + 'static,
6194
{
6295
async fn send_bundle(&self, bundle: Bundle) -> RpcResult<BundleHash> {
63-
let bundle_with_metadata = self.validate_bundle(bundle).await?;
96+
let bundle_with_metadata = self.validate_bundle(&bundle).await?;
97+
self.meter_bundle(&bundle).await?;
6498

6599
let bundle_hash = bundle_with_metadata.bundle_hash();
66100
if let Err(e) = self
@@ -104,6 +138,7 @@ where
104138
reverting_tx_hashes: vec![transaction.tx_hash()],
105139
..Default::default()
106140
};
141+
self.meter_bundle(&bundle).await?;
107142

108143
let bundle_with_metadata = BundleWithMetadata::load(bundle)
109144
.map_err(|e| EthApiError::InvalidParams(e.to_string()).into_rpc_err())?;
@@ -168,7 +203,7 @@ where
168203
Ok(transaction)
169204
}
170205

171-
async fn validate_bundle(&self, bundle: Bundle) -> RpcResult<BundleWithMetadata> {
206+
async fn validate_bundle(&self, bundle: &Bundle) -> RpcResult<BundleWithMetadata> {
172207
if bundle.txs.is_empty() {
173208
return Err(
174209
EthApiError::InvalidParams("Bundle cannot have empty transactions".into())
@@ -185,8 +220,25 @@ where
185220
let transaction = self.validate_tx(tx_data).await?;
186221
total_gas = total_gas.saturating_add(transaction.gas_limit());
187222
}
188-
validate_bundle(&bundle, total_gas, tx_hashes)?;
223+
validate_bundle(bundle, total_gas, tx_hashes)?;
189224

190225
Ok(bundle_with_metadata)
191226
}
227+
228+
async fn meter_bundle(&self, bundle: &Bundle) -> RpcResult<()> {
229+
let res: MeterBundleResponse = self
230+
.provider
231+
.client()
232+
.request("base_meterBundle", (bundle,))
233+
.await
234+
.map_err(|e| EthApiError::InvalidParams(e.to_string()).into_rpc_err())?;
235+
236+
// if simulation takes longer than 2s, we don't include and just error to user
237+
if res.total_execution_time_us > 2000000 {
238+
return Err(
239+
EthApiError::InvalidParams("Bundle simulation took too long".into()).into_rpc_err(),
240+
);
241+
}
242+
Ok(())
243+
}
192244
}

0 commit comments

Comments
 (0)