Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 3 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ base-reth-transaction-tracing = { path = "crates/transaction-tracing" }

# base/tips
# Note: default-features = false avoids version conflicts with reth's alloy/op-alloy dependencies
tips-core = { git = "https://github.com/base/tips", rev = "27674ae051a86033ece61ae24434aeacdb28ce73", default-features = false }
tips-core = { git = "https://github.com/base/tips", rev = "98c9ab49419e62352fe29cf79873141aaa3eb956", default-features = false }

# reth
reth = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.0" }
Expand Down
18 changes: 7 additions & 11 deletions crates/metering/src/meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use reth_optimism_evm::{OpEvmConfig, OpNextBlockEnvAttributes};
use reth_primitives_traits::SealedHeader;
use std::sync::Arc;
use std::time::Instant;
use tips_core::types::{BundleExtensions, BundleTxs, ParsedBundle};

use crate::TransactionResult;

Expand All @@ -28,15 +29,14 @@ const BLOCK_TIME: u64 = 2; // 2 seconds per block
pub fn meter_bundle<SP>(
state_provider: SP,
chain_spec: Arc<OpChainSpec>,
decoded_txs: Vec<op_alloy_consensus::OpTxEnvelope>,
bundle: ParsedBundle,
header: &SealedHeader,
bundle_with_metadata: &tips_core::types::BundleWithMetadata,
) -> EyreResult<(Vec<TransactionResult>, u64, U256, B256, u128)>
where
SP: reth_provider::StateProvider,
{
// Get bundle hash from BundleWithMetadata
let bundle_hash = bundle_with_metadata.bundle_hash();
// Get bundle hash
let bundle_hash = bundle.bundle_hash();

// Create state database
let state_db = reth::revm::database::StateProviderDatabase::new(state_provider);
Expand All @@ -47,8 +47,7 @@ where

// Set up next block attributes
// Use bundle.min_timestamp if provided, otherwise use header timestamp + BLOCK_TIME
let timestamp = bundle_with_metadata
.bundle()
let timestamp = bundle
.min_timestamp
.unwrap_or_else(|| header.timestamp() + BLOCK_TIME);
let attributes = OpNextBlockEnvAttributes {
Expand All @@ -72,19 +71,16 @@ where

builder.apply_pre_execution_changes()?;

for tx in decoded_txs {
for tx in bundle.transactions() {
let tx_start = Instant::now();
let tx_hash = tx.tx_hash();
let from = tx.recover_signer()?;
let to = tx.to();
let value = tx.value();
let gas_price = tx.max_fee_per_gas();

let recovered_tx =
alloy_consensus::transaction::Recovered::new_unchecked(tx.clone(), from);

let gas_used = builder
.execute_transaction(recovered_tx)
.execute_transaction(tx.clone())
.map_err(|e| eyre!("Transaction {} execution failed: {}", tx_hash, e))?;

let gas_fees = U256::from(gas_used) * U256::from(gas_price);
Expand Down
31 changes: 7 additions & 24 deletions crates/metering/src/rpc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use alloy_consensus::Header;
use alloy_eips::BlockNumberOrTag;
use alloy_eips::eip2718::Decodable2718;
use alloy_primitives::U256;
use jsonrpsee::{
core::{RpcResult, async_trait},
Expand All @@ -9,7 +8,7 @@ use jsonrpsee::{
use reth::providers::BlockReaderIdExt;
use reth_optimism_chainspec::OpChainSpec;
use reth_provider::{ChainSpecProvider, StateProviderFactory};
use tips_core::types::{Bundle, BundleWithMetadata, MeterBundleResponse};
use tips_core::types::{Bundle, MeterBundleResponse, ParsedBundle};
use tracing::{error, info};

use crate::meter_bundle;
Expand Down Expand Up @@ -53,8 +52,8 @@ where
{
async fn meter_bundle(&self, bundle: Bundle) -> RpcResult<MeterBundleResponse> {
info!(
num_transactions = bundle.txs.len(),
block_number = bundle.block_number,
num_transactions = &bundle.txs.len(),
block_number = &bundle.block_number,
"Starting bundle metering"
);

Expand All @@ -77,26 +76,10 @@ where
)
})?;

// Manually decode transactions to OpTxEnvelope (op-alloy 0.20) instead of using
// BundleWithMetadata.transactions() which returns op-alloy 0.21 types incompatible with reth.
// TODO: Remove this workaround after reth updates to op-alloy 0.21 (already on main, awaiting release)
let mut decoded_txs = Vec::new();
for tx_bytes in &bundle.txs {
let mut reader = tx_bytes.as_ref();
let tx = op_alloy_consensus::OpTxEnvelope::decode_2718(&mut reader).map_err(|e| {
jsonrpsee::types::ErrorObjectOwned::owned(
jsonrpsee::types::ErrorCode::InvalidParams.code(),
format!("Failed to decode transaction: {}", e),
None::<()>,
)
})?;
decoded_txs.push(tx);
}

let bundle_with_metadata = BundleWithMetadata::load(bundle.clone()).map_err(|e| {
let parsed_bundle = ParsedBundle::try_from(bundle).map_err(|e| {
jsonrpsee::types::ErrorObjectOwned::owned(
jsonrpsee::types::ErrorCode::InvalidParams.code(),
format!("Failed to load bundle metadata: {}", e),
format!("Failed to parse bundle: {}", e),
None::<()>,
)
})?;
Expand All @@ -119,9 +102,8 @@ where
meter_bundle(
state_provider,
self.provider.chain_spec().clone(),
decoded_txs,
parsed_bundle,
&header,
&bundle_with_metadata,
)
.map_err(|e| {
error!(error = %e, "Bundle metering failed");
Expand Down Expand Up @@ -155,6 +137,7 @@ where
gas_fees: total_gas_fees.to_string(),
results,
state_block_number: header.number,
state_flashblock_index: None,
total_gas_used,
total_execution_time_us: total_execution_time,
})
Expand Down
22 changes: 9 additions & 13 deletions crates/metering/src/tests/meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use reth_primitives_traits::SealedHeader;
use reth_provider::{HeaderProvider, StateProviderFactory, providers::BlockchainProvider};
use reth_testing_utils::generators::generate_keys;
use reth_transaction_pool::test_utils::TransactionBuilder;
use tips_core::types::{Bundle, BundleWithMetadata};
use tips_core::types::{Bundle, ParsedBundle};

use super::utils::create_provider_factory;
use crate::meter_bundle;
Expand Down Expand Up @@ -123,7 +123,7 @@ fn envelope_from_signed(tx: &OpTransactionSigned) -> eyre::Result<OpTxEnvelope>
Ok(tx.clone().into())
}

fn create_bundle_with_metadata(envelopes: Vec<OpTxEnvelope>) -> eyre::Result<BundleWithMetadata> {
fn create_parsed_bundle(envelopes: Vec<OpTxEnvelope>) -> eyre::Result<ParsedBundle> {
let txs: Vec<Bytes> = envelopes
.iter()
.map(|env| Bytes::from(env.encoded_2718()))
Expand All @@ -141,7 +141,7 @@ fn create_bundle_with_metadata(envelopes: Vec<OpTxEnvelope>) -> eyre::Result<Bun
dropping_tx_hashes: vec![],
};

BundleWithMetadata::load(bundle).map_err(|e| eyre::eyre!(e))
ParsedBundle::try_from(bundle).map_err(|e| eyre::eyre!(e))
}

#[test]
Expand All @@ -153,15 +153,14 @@ fn meter_bundle_empty_transactions() -> eyre::Result<()> {
.state_by_block_hash(harness.header.hash())
.context("getting state provider")?;

let bundle_with_metadata = create_bundle_with_metadata(Vec::new())?;
let parsed_bundle = create_parsed_bundle(Vec::new())?;

let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) =
meter_bundle(
state_provider,
harness.chain_spec.clone(),
Vec::new(),
parsed_bundle,
&harness.header,
&bundle_with_metadata,
)?;

assert!(results.is_empty());
Expand Down Expand Up @@ -201,15 +200,14 @@ fn meter_bundle_single_transaction() -> eyre::Result<()> {
.state_by_block_hash(harness.header.hash())
.context("getting state provider")?;

let bundle_with_metadata = create_bundle_with_metadata(vec![envelope.clone()])?;
let parsed_bundle = create_parsed_bundle(vec![envelope.clone()])?;

let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) =
meter_bundle(
state_provider,
harness.chain_spec.clone(),
vec![envelope],
parsed_bundle,
&harness.header,
&bundle_with_metadata,
)?;

assert_eq!(results.len(), 1);
Expand Down Expand Up @@ -296,16 +294,14 @@ fn meter_bundle_multiple_transactions() -> eyre::Result<()> {
.state_by_block_hash(harness.header.hash())
.context("getting state provider")?;

let bundle_with_metadata =
create_bundle_with_metadata(vec![envelope_1.clone(), envelope_2.clone()])?;
let parsed_bundle = create_parsed_bundle(vec![envelope_1.clone(), envelope_2.clone()])?;

let (results, total_gas_used, total_gas_fees, bundle_hash, total_execution_time) =
meter_bundle(
state_provider,
harness.chain_spec.clone(),
vec![envelope_1, envelope_2],
parsed_bundle,
&harness.header,
&bundle_with_metadata,
)?;

assert_eq!(results.len(), 2);
Expand Down