Skip to content

Commit d11c6f1

Browse files
committed
spike
1 parent 7aab20f commit d11c6f1

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
@@ -35,3 +35,4 @@ op-revm.workspace = true
3535
revm-context-interface.workspace = true
3636
alloy-signer-local.workspace = true
3737
reth-optimism-evm.workspace = true
38+
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,6 +9,7 @@ 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_audit::{BundleEvent, BundleEventPublisher};
1415
use tips_core::{Bundle, BundleHash, BundleWithMetadata, CancelBundle};
@@ -17,6 +18,38 @@ use tracing::{info, warn};
1718
use crate::queue::QueuePublisher;
1819
use crate::validation::{AccountInfoLookup, L1BlockInfoLookup, validate_bundle, validate_tx};
1920

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

70104
let bundle_hash = bundle_with_metadata.bundle_hash();
71105
if let Err(e) = self
@@ -117,6 +151,7 @@ where
117151
reverting_tx_hashes: vec![transaction.tx_hash()],
118152
..Default::default()
119153
};
154+
self.meter_bundle(&bundle).await?;
120155

121156
let bundle_with_metadata = BundleWithMetadata::load(bundle)
122157
.map_err(|e| EthApiError::InvalidParams(e.to_string()).into_rpc_err())?;
@@ -191,7 +226,7 @@ where
191226
Ok(transaction)
192227
}
193228

194-
async fn validate_bundle(&self, bundle: Bundle) -> RpcResult<BundleWithMetadata> {
229+
async fn validate_bundle(&self, bundle: &Bundle) -> RpcResult<BundleWithMetadata> {
195230
if bundle.txs.is_empty() {
196231
return Err(
197232
EthApiError::InvalidParams("Bundle cannot have empty transactions".into())
@@ -208,8 +243,25 @@ where
208243
let transaction = self.validate_tx(tx_data).await?;
209244
total_gas = total_gas.saturating_add(transaction.gas_limit());
210245
}
211-
validate_bundle(&bundle, total_gas, tx_hashes)?;
246+
validate_bundle(bundle, total_gas, tx_hashes)?;
212247

213248
Ok(bundle_with_metadata)
214249
}
250+
251+
async fn meter_bundle(&self, bundle: &Bundle) -> RpcResult<()> {
252+
let res: MeterBundleResponse = self
253+
.provider
254+
.client()
255+
.request("base_meterBundle", (bundle,))
256+
.await
257+
.map_err(|e| EthApiError::InvalidParams(e.to_string()).into_rpc_err())?;
258+
259+
// if simulation takes longer than 2s, we don't include and just error to user
260+
if res.total_execution_time_us > 2000000 {
261+
return Err(
262+
EthApiError::InvalidParams("Bundle simulation took too long".into()).into_rpc_err(),
263+
);
264+
}
265+
Ok(())
266+
}
215267
}

0 commit comments

Comments
 (0)