@@ -5,13 +5,17 @@ use reth_chainspec::{ChainSpecBuilder, MAINNET};
55use reth_e2e_test_utils:: {
66 node:: NodeTestContext , transaction:: TransactionTestContext , wallet:: Wallet ,
77} ;
8+ use reth_ethereum_engine_primitives:: BlobSidecars ;
89use reth_ethereum_primitives:: PooledTransactionVariant ;
910use reth_node_builder:: { NodeBuilder , NodeHandle } ;
1011use reth_node_core:: { args:: RpcServerArgs , node_config:: NodeConfig } ;
1112use reth_node_ethereum:: EthereumNode ;
1213use reth_tasks:: TaskManager ;
1314use reth_transaction_pool:: TransactionPool ;
14- use std:: sync:: Arc ;
15+ use std:: {
16+ sync:: Arc ,
17+ time:: { Duration , SystemTime , UNIX_EPOCH } ,
18+ } ;
1519
1620#[ tokio:: test]
1721async fn can_handle_blobs ( ) -> eyre:: Result < ( ) > {
@@ -136,3 +140,113 @@ async fn can_send_legacy_sidecar_post_activation() -> eyre::Result<()> {
136140
137141 Ok ( ( ) )
138142}
143+
144+ #[ tokio:: test]
145+ async fn blob_conversion_at_osaka ( ) -> eyre:: Result < ( ) > {
146+ reth_tracing:: init_test_tracing ( ) ;
147+ let tasks = TaskManager :: current ( ) ;
148+ let exec = tasks. executor ( ) ;
149+
150+ let current_timestamp = SystemTime :: now ( ) . duration_since ( UNIX_EPOCH ) . unwrap ( ) . as_secs ( ) ;
151+ // Osaka activates in 2 slots
152+ let osaka_timestamp = current_timestamp + 24 ;
153+
154+ let genesis: Genesis = serde_json:: from_str ( include_str ! ( "../assets/genesis.json" ) ) . unwrap ( ) ;
155+ let chain_spec = Arc :: new (
156+ ChainSpecBuilder :: default ( )
157+ . chain ( MAINNET . chain )
158+ . genesis ( genesis)
159+ . prague_activated ( )
160+ . with_osaka_at ( osaka_timestamp)
161+ . build ( ) ,
162+ ) ;
163+ let genesis_hash = chain_spec. genesis_hash ( ) ;
164+ let node_config = NodeConfig :: test ( )
165+ . with_chain ( chain_spec)
166+ . with_unused_ports ( )
167+ . with_rpc ( RpcServerArgs :: default ( ) . with_unused_ports ( ) . with_http ( ) ) ;
168+ let NodeHandle { node, node_exit_future : _ } = NodeBuilder :: new ( node_config. clone ( ) )
169+ . testing_node ( exec. clone ( ) )
170+ . node ( EthereumNode :: default ( ) )
171+ . launch ( )
172+ . await ?;
173+
174+ let mut node = NodeTestContext :: new ( node, eth_payload_attributes) . await ?;
175+
176+ let mut wallets = Wallet :: new ( 3 ) . wallet_gen ( ) ;
177+ let first = wallets. pop ( ) . unwrap ( ) ;
178+ let second = wallets. pop ( ) . unwrap ( ) ;
179+
180+ // build a dummy payload at `current_timestamp`
181+ let raw_tx = TransactionTestContext :: transfer_tx_bytes ( 1 , wallets. pop ( ) . unwrap ( ) ) . await ;
182+ node. rpc . inject_tx ( raw_tx) . await ?;
183+ node. payload . timestamp = current_timestamp - 1 ;
184+ node. advance_block ( ) . await ?;
185+
186+ // build blob txs
187+ let first_blob = TransactionTestContext :: tx_with_blobs_bytes ( 1 , first. clone ( ) ) . await ?;
188+ let second_blob = TransactionTestContext :: tx_with_blobs_bytes ( 1 , second. clone ( ) ) . await ?;
189+
190+ // assert both txs have legacy sidecars
191+ assert ! ( PooledTransactionVariant :: decode_2718_exact( & first_blob)
192+ . unwrap( )
193+ . as_eip4844( )
194+ . unwrap( )
195+ . tx( )
196+ . sidecar
197+ . is_eip4844( ) ) ;
198+ assert ! ( PooledTransactionVariant :: decode_2718_exact( & second_blob)
199+ . unwrap( )
200+ . as_eip4844( )
201+ . unwrap( )
202+ . tx( )
203+ . sidecar
204+ . is_eip4844( ) ) ;
205+
206+ // inject first blob tx to the pool
207+ let blob_tx_hash = node. rpc . inject_tx ( first_blob) . await ?;
208+ // fetch it from rpc
209+ let envelope = node. rpc . envelope_by_hash ( blob_tx_hash) . await ?;
210+ // assert that it still has a legacy sidecar
211+ assert ! ( envelope. as_eip4844( ) . unwrap( ) . tx( ) . sidecar( ) . unwrap( ) . is_eip4844( ) ) ;
212+ // validate sidecar
213+ TransactionTestContext :: validate_sidecar ( envelope) ;
214+
215+ // build last Prague payload
216+ node. payload . timestamp = current_timestamp + 11 ;
217+ let prague_payload = node. new_payload ( ) . await ?;
218+ assert ! ( matches!( prague_payload. sidecars( ) , BlobSidecars :: Eip4844 ( _) ) ) ;
219+
220+ // inject second blob tx to the pool
221+ let blob_tx_hash = node. rpc . inject_tx ( second_blob) . await ?;
222+ // fetch it from rpc
223+ let envelope = node. rpc . envelope_by_hash ( blob_tx_hash) . await ?;
224+ // assert that it still has a legacy sidecar
225+ assert ! ( envelope. as_eip4844( ) . unwrap( ) . tx( ) . sidecar( ) . unwrap( ) . is_eip4844( ) ) ;
226+ // validate sidecar
227+ TransactionTestContext :: validate_sidecar ( envelope) ;
228+
229+ tokio:: time:: sleep ( Duration :: from_secs ( 11 ) ) . await ;
230+
231+ // fetch second blob tx from rpc again
232+ let envelope = node. rpc . envelope_by_hash ( blob_tx_hash) . await ?;
233+ // assert that it was converted to eip7594
234+ assert ! ( envelope. as_eip4844( ) . unwrap( ) . tx( ) . sidecar( ) . unwrap( ) . is_eip7594( ) ) ;
235+ // validate sidecar
236+ TransactionTestContext :: validate_sidecar ( envelope) ;
237+
238+ // submit the Prague payload
239+ node. update_forkchoice ( genesis_hash, node. submit_payload ( prague_payload) . await ?) . await ?;
240+
241+ // Build first Osaka payload
242+ node. payload . timestamp = osaka_timestamp - 1 ;
243+ let osaka_payload = node. new_payload ( ) . await ?;
244+
245+ // Assert that it includes the second blob tx with eip7594 sidecar
246+ assert ! ( osaka_payload. block( ) . body( ) . transactions( ) . any( |tx| * tx. hash( ) == blob_tx_hash) ) ;
247+ assert ! ( matches!( osaka_payload. sidecars( ) , BlobSidecars :: Eip7594 ( _) ) ) ;
248+
249+ node. update_forkchoice ( genesis_hash, node. submit_payload ( osaka_payload) . await ?) . await ?;
250+
251+ Ok ( ( ) )
252+ }
0 commit comments