Skip to content

Commit

Permalink
Add monitoring exex for op-rbuilder (#365)
Browse files Browse the repository at this point in the history
## 📝 Summary

Adds execution extension for op-rbuilder for monitoring builder
transactions in the block. This ingests the committed chain and emits
metrics to see if the builder transaction has landed a block or not with
the op-rbuilder.

## 💡 Motivation and Context

Used for observability and monitoring blocks built by the op-rbuilder on
optimism.

---

## ✅ I have completed the following steps:

* [x] Run `make lint`
* [x] Run `make test`
* [x] Added tests (if applicable)
  • Loading branch information
avalonche authored Jan 22, 2025
1 parent 3d00697 commit 35f855e
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 4 deletions.
7 changes: 5 additions & 2 deletions Cargo.lock

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

5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-evm-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-execution-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-exex = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-metrics = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-trie-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-payload-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.1.5" }
Expand Down Expand Up @@ -151,9 +153,10 @@ tokio-util = "0.7.12"
url = "2.5.2"

libc = { version = "0.2.161" }
lazy_static = "1.4.0"
tikv-jemallocator = { version = "0.6" }
tracing = "0.1.37"

metrics = { version = "0.24.1" }

eth-sparse-mpt = { path = "crates/eth-sparse-mpt" }
sysperf = { path = "crates/sysperf" }
3 changes: 3 additions & 0 deletions crates/op-rbuilder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ reth-optimism-primitives.workspace = true
reth-cli-util.workspace = true
reth-payload-primitives.workspace = true
reth-evm.workspace = true
reth-exex.workspace = true
reth-chainspec.workspace = true
reth-primitives.workspace = true
reth-node-api.workspace = true
Expand All @@ -24,6 +25,7 @@ reth-node-ethereum.workspace = true
reth-chain-state.workspace = true
reth-execution-types.workspace = true
reth-ethereum-payload-builder.workspace = true
reth-metrics.workspace = true
reth-provider.workspace = true
reth-revm.workspace = true
reth-trie.workspace = true
Expand Down Expand Up @@ -61,6 +63,7 @@ async-trait = { workspace = true }
clap_builder = { workspace = true }
clap.workspace = true
derive_more.workspace = true
metrics.workspace = true

[target.'cfg(unix)'.dependencies]
tikv-jemallocator = { version = "0.6", optional = true }
Expand Down
7 changes: 7 additions & 0 deletions crates/op-rbuilder/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use clap::Parser;
use generator::EmptyBlockPayloadJobGenerator;
use monitoring::Monitoring;
use payload_builder::OpPayloadBuilder as FBPayloadBuilder;
use payload_builder_vanilla::OpPayloadBuilderVanilla;
use reth::builder::Node;
Expand Down Expand Up @@ -31,6 +32,8 @@ use reth_optimism_primitives::OpPrimitives;
use reth_transaction_pool::PoolTransaction;

pub mod generator;
mod metrics;
mod monitoring;
pub mod payload_builder;
mod payload_builder_vanilla;
mod tx_signer;
Expand Down Expand Up @@ -109,6 +112,10 @@ fn main() {
.payload(CustomPayloadBuilder::new(builder_args.builder_signer)),
)
.with_add_ons(op_node.add_ons())
.install_exex("monitoring", move |ctx| {
let builder_signer = builder_args.builder_signer;
async move { Ok(Monitoring::new(ctx, builder_signer).start()) }
})
.launch_with_fn(|builder| {
let launcher = EngineNodeLauncher::new(
builder.task_executor().clone(),
Expand Down
25 changes: 25 additions & 0 deletions crates/op-rbuilder/src/metrics.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use reth_metrics::{metrics::Gauge, Metrics};

/// op-rbuilder metrics
#[derive(Metrics)]
#[metrics(scope = "op_rbuilder")]
pub struct OpRBuilderMetrics {
/// Number of builder built blocks
pub builder_built_blocks: Gauge,
/// Last built block height
pub last_built_block_height: Gauge,
}

impl OpRBuilderMetrics {
pub fn inc_builder_built_blocks(&self) {
self.builder_built_blocks.increment(1);
}

pub fn dec_builder_built_blocks(&self) {
self.builder_built_blocks.decrement(1);
}

pub fn set_last_built_block_height(&self, height: u64) {
self.last_built_block_height.set(height as f64);
}
}
129 changes: 129 additions & 0 deletions crates/op-rbuilder/src/monitoring.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use alloy_consensus::{Transaction, TxReceipt};
use futures_util::TryStreamExt;
use reth::core::primitives::SignedTransaction;
use reth_exex::{ExExContext, ExExEvent};
use reth_node_api::{FullNodeComponents, NodeTypes};
use reth_optimism_primitives::{OpPrimitives, OpTransactionSigned};
use reth_primitives::{Block, SealedBlockWithSenders};
use reth_provider::Chain;
use tracing::info;

use crate::{metrics::OpRBuilderMetrics, tx_signer::Signer};

const OP_BUILDER_TX_PREFIX: &[u8] = b"Block Number:";

pub struct Monitoring<Node: FullNodeComponents> {
ctx: ExExContext<Node>,
builder_signer: Option<Signer>,
metrics: OpRBuilderMetrics,
}

impl<Node> Monitoring<Node>
where
Node: FullNodeComponents<Types: NodeTypes<Primitives = OpPrimitives>>,
{
pub fn new(ctx: ExExContext<Node>, builder_signer: Option<Signer>) -> Self {
Self {
ctx,
builder_signer,
metrics: Default::default(),
}
}

pub async fn start(mut self) -> eyre::Result<()> {
// Process all new chain state notifications
while let Some(notification) = self.ctx.notifications.try_next().await? {
if let Some(reverted_chain) = notification.reverted_chain() {
self.revert(&reverted_chain).await?;
}
if let Some(committed_chain) = notification.committed_chain() {
self.commit(&committed_chain).await?;
self.ctx
.events
.send(ExExEvent::FinishedHeight(committed_chain.tip().num_hash()))?;
}
}

Ok(())
}

/// Process a new chain commit.
///
/// This function decodes the builder tx and then emits metrics
async fn commit(&mut self, chain: &Chain<OpPrimitives>) -> eyre::Result<()> {
info!("Processing new chain commit");
let txs = decode_chain_into_builder_txs(chain, self.builder_signer);

for (block, _) in txs {
self.metrics.inc_builder_built_blocks();
self.metrics.set_last_built_block_height(block.number);
info!(
block_number = block.number,
"Committed block built by builder"
);
}

Ok(())
}

/// Process a chain revert.
///
/// This function decodes all transactions in the block, updates the metrics for builder built blocks
async fn revert(&mut self, chain: &Chain<OpPrimitives>) -> eyre::Result<()> {
info!("Processing new chain revert");
let mut txs = decode_chain_into_builder_txs(chain, self.builder_signer);
// Reverse the order of txs to start reverting from the tip
txs.reverse();

if let Some((block, _)) = txs.last() {
self.metrics.set_last_built_block_height(block.number - 1);
}

for (block, _) in txs {
self.metrics.dec_builder_built_blocks();
info!(
block_number = block.number,
"Reverted block built by builder"
);
}

Ok(())
}
}

/// Decode chain of blocks and filter list to builder txs
fn decode_chain_into_builder_txs(
chain: &Chain<OpPrimitives>,
builder_signer: Option<Signer>,
) -> Vec<(
SealedBlockWithSenders<Block<OpTransactionSigned>>,
OpTransactionSigned,
)> {
chain
// Get all blocks and receipts
.blocks_and_receipts()
// Get all receipts
.flat_map(|(block, receipts)| {
let block_clone = block.clone();
block
.body()
.transactions
.iter()
.zip(receipts.iter().flatten())
.filter_map(move |(tx, receipt)| {
let is_builder_tx = receipt.status()
&& tx.input().starts_with(OP_BUILDER_TX_PREFIX)
&& tx.recover_signer().is_some_and(|signer| {
builder_signer.is_some_and(|bs| signer == bs.address)
});

if is_builder_tx {
// Clone the entire block and the transaction
Some((block_clone.clone(), tx.clone()))
} else {
None
}
})
})
.collect()
}
2 changes: 1 addition & 1 deletion crates/rbuilder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ uuid = { version = "1.6.1", features = ["serde", "v5", "v4"] }
prometheus = "0.13.4"
hyper = { version = "1.3.1", features = ["server", "full"] }
warp = "0.3.7"
lazy_static = "1.4.0"
lazy_static.workspace = true
ctor = "0.2"
toml = "0.8.8"
ahash = "0.8.6"
Expand Down

0 comments on commit 35f855e

Please sign in to comment.