Skip to content

Commit d0d4882

Browse files
authored
Merge pull request #34 from flashbots/e2e/monitor-txn
Add e2e test to check that the builder emits a log when a transaction is discarded
2 parents 2a7ae1d + 5544143 commit d0d4882

File tree

4 files changed

+105
-16
lines changed

4 files changed

+105
-16
lines changed

crates/op-rbuilder/src/integration/integration_test.rs

+63-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
mod tests {
33
use crate::{
44
integration::{
5-
op_rbuilder::OpRbuilderConfig, op_reth::OpRethConfig, IntegrationFramework, TestHarness,
5+
op_rbuilder::OpRbuilderConfig, op_reth::OpRethConfig, IntegrationFramework,
6+
TestHarness, TestHarnessBuilder,
67
},
78
tester::{BlockGenerator, EngineApi},
89
tx_signer::Signer,
@@ -94,10 +95,66 @@ mod tests {
9495
Ok(())
9596
}
9697

98+
#[tokio::test]
99+
#[cfg(not(feature = "flashblocks"))]
100+
async fn integration_test_monitor_transaction_drops() -> eyre::Result<()> {
101+
// This test ensures that the transactions that get reverted an not included in the block
102+
// are emitted as a log on the builder.
103+
let harness = TestHarnessBuilder::new("integration_test_monitor_transaction_drops")
104+
.with_revert_protection()
105+
.build()
106+
.await?;
107+
108+
let mut generator = harness.block_generator().await?;
109+
110+
// send 10 reverting transactions
111+
let mut pending_txn = Vec::new();
112+
for _ in 0..10 {
113+
let txn = harness.send_revert_transaction().await?;
114+
pending_txn.push(txn);
115+
}
116+
117+
// generate 10 blocks
118+
for _ in 0..10 {
119+
let block_hash = generator.generate_block().await?;
120+
121+
// query the block and the transactions inside the block
122+
let block = harness
123+
.provider()?
124+
.get_block_by_hash(block_hash)
125+
.await?
126+
.expect("block");
127+
128+
// blocks should only include two transactions (deposit + builder)
129+
assert_eq!(block.transactions.len(), 2);
130+
}
131+
132+
// check that the builder emitted logs for the reverted transactions
133+
// with the monitoring logic
134+
// TODO: this is not ideal, lets find a different way to detect this
135+
// Each time a transaction is dropped, it emits a log like this
136+
// 'Transaction event received target="monitoring" tx_hash="<tx_hash>" kind="discarded"'
137+
let builder_logs = std::fs::read_to_string(harness.builder_log_path)?;
138+
139+
for txn in pending_txn {
140+
let txn_log = format!(
141+
"Transaction event received target=\"monitoring\" tx_hash=\"{}\" kind=\"discarded\"",
142+
txn.tx_hash()
143+
);
144+
145+
assert!(builder_logs.contains(txn_log.as_str()));
146+
}
147+
148+
Ok(())
149+
}
150+
97151
#[tokio::test]
98152
#[cfg(not(feature = "flashblocks"))]
99153
async fn integration_test_revert_protection_disabled() -> eyre::Result<()> {
100-
let harness = TestHarness::new("integration_test_revert_protection_disabled").await;
154+
let harness = TestHarnessBuilder::new("integration_test_revert_protection_disabled")
155+
.build()
156+
.await?;
157+
101158
let mut generator = harness.block_generator().await?;
102159

103160
let txn1 = harness.send_valid_transaction().await?;
@@ -386,13 +443,15 @@ mod tests {
386443
#[tokio::test]
387444
#[cfg(not(feature = "flashblocks"))]
388445
async fn integration_test_get_payload_close_to_fcu() -> eyre::Result<()> {
389-
let test_harness = TestHarness::new("integration_test_get_payload_close_to_fcu").await;
446+
let test_harness = TestHarnessBuilder::new("integration_test_get_payload_close_to_fcu")
447+
.build()
448+
.await?;
390449
let mut block_generator = test_harness.block_generator().await?;
391450

392451
// add some transactions to the pool so that the builder is busy when we send the fcu/getPayload requests
393452
for _ in 0..10 {
394453
// Note, for this test it is okay if they are not valid
395-
test_harness.send_valid_transaction().await?;
454+
let _ = test_harness.send_valid_transaction().await?;
396455
}
397456

398457
// TODO: In the fail case scenario, this hangs forever, but it should return an error

crates/op-rbuilder/src/integration/mod.rs

+37-12
Original file line numberDiff line numberDiff line change
@@ -219,16 +219,26 @@ impl Drop for IntegrationFramework {
219219
const BUILDER_PRIVATE_KEY: &str =
220220
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
221221

222-
pub struct TestHarness {
223-
_framework: IntegrationFramework,
224-
builder_auth_rpc_port: u16,
225-
builder_http_port: u16,
226-
validator_auth_rpc_port: u16,
222+
pub struct TestHarnessBuilder {
223+
name: String,
224+
use_revert_protection: bool,
227225
}
228226

229-
impl TestHarness {
230-
pub async fn new(name: &str) -> Self {
231-
let mut framework = IntegrationFramework::new(name).unwrap();
227+
impl TestHarnessBuilder {
228+
pub fn new(name: &str) -> Self {
229+
Self {
230+
name: name.to_string(),
231+
use_revert_protection: false,
232+
}
233+
}
234+
235+
pub fn with_revert_protection(mut self) -> Self {
236+
self.use_revert_protection = true;
237+
self
238+
}
239+
240+
pub async fn build(self) -> eyre::Result<TestHarness> {
241+
let mut framework = IntegrationFramework::new(&self.name).unwrap();
232242

233243
// we are going to use a genesis file pre-generated before the test
234244
let mut genesis_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
@@ -245,7 +255,8 @@ impl TestHarness {
245255
.auth_rpc_port(builder_auth_rpc_port)
246256
.network_port(get_available_port())
247257
.http_port(builder_http_port)
248-
.with_builder_private_key(BUILDER_PRIVATE_KEY);
258+
.with_builder_private_key(BUILDER_PRIVATE_KEY)
259+
.with_revert_protection(self.use_revert_protection);
249260

250261
// create the validation reth node
251262

@@ -259,19 +270,33 @@ impl TestHarness {
259270

260271
framework.start("op-reth", &reth).await.unwrap();
261272

262-
let _ = framework
273+
let builder = framework
263274
.start("op-rbuilder", &op_rbuilder_config)
264275
.await
265276
.unwrap();
266277

267-
Self {
278+
let builder_log_path = builder.log_path.clone();
279+
280+
Ok(TestHarness {
268281
_framework: framework,
269282
builder_auth_rpc_port,
270283
builder_http_port,
271284
validator_auth_rpc_port,
272-
}
285+
builder_log_path,
286+
})
273287
}
288+
}
289+
290+
pub struct TestHarness {
291+
_framework: IntegrationFramework,
292+
builder_auth_rpc_port: u16,
293+
builder_http_port: u16,
294+
validator_auth_rpc_port: u16,
295+
#[allow(dead_code)] // I think this is due to some feature flag conflicts
296+
builder_log_path: PathBuf,
297+
}
274298

299+
impl TestHarness {
275300
pub async fn send_valid_transaction(
276301
&self,
277302
) -> eyre::Result<PendingTransactionBuilder<Optimism>> {

crates/op-rbuilder/src/integration/op_rbuilder.rs

+3
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ impl Service for OpRbuilderConfig {
116116
.arg("--datadir")
117117
.arg(self.data_dir.as_ref().expect("data_dir not set"))
118118
.arg("--disable-discovery")
119+
.arg("--color")
120+
.arg("never")
121+
.arg("--builder.log-pool-transactions")
119122
.arg("--port")
120123
.arg(self.network_port.expect("network_port not set").to_string())
121124
.arg("--ipcdisable");

crates/op-rbuilder/src/integration/op_reth.rs

+2
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ impl Service for OpRethConfig {
8080
.arg("--datadir")
8181
.arg(self.data_dir.as_ref().expect("data_dir not set"))
8282
.arg("--disable-discovery")
83+
.arg("--color")
84+
.arg("never")
8385
.arg("--port")
8486
.arg(self.network_port.expect("network_port not set").to_string())
8587
.arg("--ipcdisable");

0 commit comments

Comments
 (0)