Skip to content

Commit 0e7bd51

Browse files
authored
Add revert protection and integration tests (#435)
## πŸ“ Summary Adds revert protection and integration tests to the transactions in the payload builder of the op-rbuilder. ## πŸ’‘ Motivation and Context --- ## βœ… I have completed the following steps: * [x] Run `make lint` * [x] Run `make test` * [x] Added tests (if applicable)
1 parent bb35cba commit 0e7bd51

File tree

4 files changed

+145
-12
lines changed

4 files changed

+145
-12
lines changed

β€Žcrates/op-rbuilder/src/integration/integration_test.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@ mod tests {
44
op_rbuilder::OpRbuilderConfig, op_reth::OpRethConfig, IntegrationFramework,
55
};
66
use crate::tester::{BlockGenerator, EngineApi};
7+
use crate::tx_signer::Signer;
8+
use alloy_consensus::TxEip1559;
9+
use alloy_eips::eip1559::MIN_PROTOCOL_BASE_FEE;
10+
use alloy_eips::eip2718::Encodable2718;
11+
use alloy_primitives::hex;
712
use alloy_provider::Identity;
813
use alloy_provider::{Provider, ProviderBuilder};
914
use alloy_rpc_types_eth::BlockTransactionsKind;
15+
use op_alloy_consensus::OpTypedTransaction;
1016
use op_alloy_network::Optimism;
17+
use std::cmp::max;
1118
use std::path::PathBuf;
1219
use uuid::Uuid;
1320

@@ -83,4 +90,128 @@ mod tests {
8390

8491
Ok(())
8592
}
93+
94+
#[tokio::test]
95+
async fn integration_test_revert_protection() -> eyre::Result<()> {
96+
// This is a simple test using the integration framework to test that the chain
97+
// produces blocks.
98+
let mut framework = IntegrationFramework::new().unwrap();
99+
100+
// we are going to use a genesis file pre-generated before the test
101+
let mut genesis_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
102+
genesis_path.push("../../genesis.json");
103+
assert!(genesis_path.exists());
104+
105+
// create the builder
106+
let builder_data_dir = std::env::temp_dir().join(Uuid::new_v4().to_string());
107+
let op_rbuilder_config = OpRbuilderConfig::new()
108+
.chain_config_path(genesis_path.clone())
109+
.data_dir(builder_data_dir)
110+
.auth_rpc_port(1244)
111+
.network_port(1245)
112+
.http_port(1248)
113+
.with_builder_private_key(BUILDER_PRIVATE_KEY);
114+
115+
// create the validation reth node
116+
let reth_data_dir = std::env::temp_dir().join(Uuid::new_v4().to_string());
117+
let reth = OpRethConfig::new()
118+
.chain_config_path(genesis_path)
119+
.data_dir(reth_data_dir)
120+
.auth_rpc_port(1246)
121+
.network_port(1247);
122+
123+
framework.start("op-reth", &reth).await.unwrap();
124+
125+
let _ = framework
126+
.start("op-rbuilder", &op_rbuilder_config)
127+
.await
128+
.unwrap();
129+
130+
let engine_api = EngineApi::new("http://localhost:1244").unwrap();
131+
let validation_api = EngineApi::new("http://localhost:1246").unwrap();
132+
133+
let mut generator = BlockGenerator::new(&engine_api, Some(&validation_api), false, 1);
134+
let latest_block = generator.init().await?;
135+
136+
let provider = ProviderBuilder::<Identity, Identity, Optimism>::default()
137+
.on_http("http://localhost:1248".parse()?);
138+
139+
let mut base_fee = max(
140+
latest_block.header.base_fee_per_gas.unwrap(),
141+
MIN_PROTOCOL_BASE_FEE,
142+
);
143+
for _ in 0..10 {
144+
// Get builder's address
145+
let known_wallet = Signer::try_from_secret(BUILDER_PRIVATE_KEY.parse()?)?;
146+
let builder_address = known_wallet.address;
147+
// Get current nonce from chain
148+
let nonce = provider.get_transaction_count(builder_address).await?;
149+
// Transaction from builder should succeed
150+
let tx_request = OpTypedTransaction::Eip1559(TxEip1559 {
151+
chain_id: 901,
152+
nonce,
153+
gas_limit: 210000,
154+
max_fee_per_gas: base_fee.into(),
155+
..Default::default()
156+
});
157+
let signed_tx = known_wallet.sign_tx(tx_request)?;
158+
let known_tx = provider
159+
.send_raw_transaction(signed_tx.encoded_2718().as_slice())
160+
.await?;
161+
162+
// Create a reverting transaction
163+
let tx_request = OpTypedTransaction::Eip1559(TxEip1559 {
164+
chain_id: 901,
165+
nonce: nonce + 1,
166+
gas_limit: 300000,
167+
max_fee_per_gas: base_fee.into(),
168+
input: hex!("60006000fd").into(), // PUSH1 0x00 PUSH1 0x00 REVERT
169+
..Default::default()
170+
});
171+
let signed_tx = known_wallet.sign_tx(tx_request)?;
172+
let reverting_tx = provider
173+
.send_raw_transaction(signed_tx.encoded_2718().as_slice())
174+
.await?;
175+
176+
let block_hash = generator.generate_block().await?;
177+
178+
// query the block and the transactions inside the block
179+
let block = provider
180+
.get_block_by_hash(block_hash, BlockTransactionsKind::Hashes)
181+
.await?
182+
.expect("block");
183+
184+
// Verify known transaction is included
185+
assert!(
186+
block
187+
.transactions
188+
.hashes()
189+
.any(|hash| hash == *known_tx.tx_hash()),
190+
"successful transaction missing from block"
191+
);
192+
193+
// Verify reverted transaction is NOT included
194+
assert!(
195+
!block
196+
.transactions
197+
.hashes()
198+
.any(|hash| hash == *reverting_tx.tx_hash()),
199+
"reverted transaction unexpectedly included in block"
200+
);
201+
for hash in block.transactions.hashes() {
202+
let receipt = provider
203+
.get_transaction_receipt(hash)
204+
.await?
205+
.expect("receipt");
206+
let success = receipt.inner.inner.status();
207+
assert!(success);
208+
}
209+
base_fee = max(
210+
block.header.base_fee_per_gas.unwrap(),
211+
MIN_PROTOCOL_BASE_FEE,
212+
);
213+
}
214+
215+
Ok(())
216+
}
86217
}

β€Žcrates/op-rbuilder/src/payload_builder_vanilla.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,16 +1091,8 @@ where
10911091
num_txs_simulated_success += 1;
10921092
} else {
10931093
num_txs_simulated_fail += 1;
1094+
continue;
10941095
}
1095-
self.metrics
1096-
.payload_num_tx_simulated
1097-
.record(num_txs_simulated as f64);
1098-
self.metrics
1099-
.payload_num_tx_simulated_success
1100-
.record(num_txs_simulated_success as f64);
1101-
self.metrics
1102-
.payload_num_tx_simulated_fail
1103-
.record(num_txs_simulated_fail as f64);
11041096

11051097
// commit changes
11061098
evm.db_mut().commit(state);
@@ -1132,6 +1124,15 @@ where
11321124
self.metrics
11331125
.payload_num_tx_considered
11341126
.record(num_txs_considered as f64);
1127+
self.metrics
1128+
.payload_num_tx_simulated
1129+
.record(num_txs_simulated as f64);
1130+
self.metrics
1131+
.payload_num_tx_simulated_success
1132+
.record(num_txs_simulated_success as f64);
1133+
self.metrics
1134+
.payload_num_tx_simulated_fail
1135+
.record(num_txs_simulated_fail as f64);
11351136

11361137
Ok(None)
11371138
}

β€Žcrates/op-rbuilder/src/tester/mod.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use alloy_rpc_types_engine::ExecutionPayloadV2;
1212
use alloy_rpc_types_engine::PayloadAttributes;
1313
use alloy_rpc_types_engine::PayloadStatusEnum;
1414
use alloy_rpc_types_engine::{ExecutionPayloadV3, ForkchoiceUpdated, PayloadStatus};
15+
use alloy_rpc_types_eth::Block;
1516
use jsonrpsee::core::RpcResult;
1617
use jsonrpsee::http_client::{transport::HttpBackend, HttpClient};
1718
use jsonrpsee::proc_macros::rpc;
@@ -212,7 +213,7 @@ impl<'a> BlockGenerator<'a> {
212213
}
213214

214215
/// Initialize the block generator by fetching the latest block
215-
pub async fn init(&mut self) -> eyre::Result<()> {
216+
pub async fn init(&mut self) -> eyre::Result<Block> {
216217
let latest_block = self.engine_api.latest().await?.expect("block not found");
217218
self.latest_hash = latest_block.header.hash;
218219

@@ -221,7 +222,7 @@ impl<'a> BlockGenerator<'a> {
221222
self.sync_validation_node(validation_api).await?;
222223
}
223224

224-
Ok(())
225+
Ok(latest_block)
225226
}
226227

227228
/// Sync the validation node to the current state

β€Žcrates/rbuilder/benches/benchmarks/txpool_fetcher.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use alloy_network::{EthereumWallet, TransactionBuilder};
22
use alloy_node_bindings::Anvil;
33
use alloy_primitives::U256;
4-
use alloy_provider::{Provider, ProviderBuilder};
4+
use alloy_provider::ProviderBuilder;
55
use alloy_rpc_types::TransactionRequest;
66
use alloy_signer_local::PrivateKeySigner;
77
use criterion::{criterion_group, Criterion};

0 commit comments

Comments
Β (0)