@@ -6,11 +6,12 @@ use alloy_signer::Signer;
66use alloy_signer_local:: PrivateKeySigner ;
77use futures:: StreamExt ;
88use reth_chainspec:: EthChainSpec ;
9- use reth_network:: { NetworkConfigBuilder , PeersInfo } ;
9+ use reth_network:: { NetworkConfigBuilder , Peers , PeersInfo } ;
1010use reth_network_api:: block:: EthWireProvider ;
1111use reth_rpc_api:: EthApiServer ;
1212use reth_scroll_chainspec:: SCROLL_DEV ;
1313use reth_scroll_node:: ScrollNetworkPrimitives ;
14+ use reth_scroll_primitives:: ScrollBlock ;
1415use reth_tokio_util:: EventStream ;
1516use rollup_node:: {
1617 constants:: SCROLL_GAS_LIMIT ,
@@ -22,16 +23,19 @@ use rollup_node::{
2223 GasPriceOracleArgs , L1ProviderArgs , NetworkArgs as ScrollNetworkArgs , RollupNodeContext ,
2324 ScrollRollupNodeConfig , SequencerArgs ,
2425} ;
25- use rollup_node_manager:: { RollupManagerCommand , RollupManagerEvent , RollupManagerHandle } ;
26+ use rollup_node_manager:: { RollupManagerCommand , RollupManagerEvent } ;
2627use rollup_node_primitives:: { sig_encode_hash, BatchCommitData , ConsensusUpdate } ;
2728use rollup_node_providers:: BlobSource ;
2829use rollup_node_sequencer:: L1MessageInclusionMode ;
2930use rollup_node_watcher:: L1Notification ;
3031use scroll_alloy_consensus:: TxL1Message ;
3132use scroll_network:: { NewBlockWithPeer , SCROLL_MAINNET } ;
3233use scroll_wire:: { ScrollWireConfig , ScrollWireProtocolHandler } ;
33- use std:: { path:: PathBuf , sync:: Arc } ;
34- use tokio:: sync:: { oneshot, Mutex } ;
34+ use std:: { path:: PathBuf , sync:: Arc , time:: Duration } ;
35+ use tokio:: {
36+ sync:: { oneshot, Mutex } ,
37+ time,
38+ } ;
3539use tracing:: trace;
3640
3741#[ tokio:: test]
@@ -63,7 +67,7 @@ async fn can_bridge_l1_messages() -> eyre::Result<()> {
6367 let ( mut nodes, _tasks, _wallet) = setup_engine ( node_args, 1 , chain_spec, false , false ) . await ?;
6468 let node = nodes. pop ( ) . unwrap ( ) ;
6569
66- let rnm_handle: RollupManagerHandle = node. inner . add_ons_handle . rollup_manager_handle . clone ( ) ;
70+ let rnm_handle = node. inner . add_ons_handle . rollup_manager_handle . clone ( ) ;
6771 let mut rnm_events = rnm_handle. get_event_listener ( ) . await ?;
6872 let l1_watcher_tx = node. inner . add_ons_handle . l1_watcher_tx . clone ( ) . unwrap ( ) ;
6973
@@ -167,6 +171,93 @@ async fn can_sequence_and_gossip_blocks() {
167171 . await ;
168172}
169173
174+ #[ tokio:: test]
175+ async fn can_penalize_peer_for_invalid_block ( ) {
176+ reth_tracing:: init_test_tracing ( ) ;
177+
178+ // create 2 nodes
179+ let chain_spec = ( * SCROLL_DEV ) . clone ( ) ;
180+ let rollup_manager_args = ScrollRollupNodeConfig {
181+ test : true ,
182+ network_args : ScrollNetworkArgs {
183+ enable_eth_scroll_wire_bridge : true ,
184+ enable_scroll_wire : true ,
185+ sequencer_url : None ,
186+ } ,
187+ database_args : DatabaseArgs { path : Some ( PathBuf :: from ( "sqlite::memory:" ) ) } ,
188+ l1_provider_args : L1ProviderArgs :: default ( ) ,
189+ engine_driver_args : EngineDriverArgs :: default ( ) ,
190+ sequencer_args : SequencerArgs {
191+ sequencer_enabled : true ,
192+ block_time : 0 ,
193+ l1_message_inclusion_mode : L1MessageInclusionMode :: BlockDepth ( 0 ) ,
194+ payload_building_duration : 1000 ,
195+ ..SequencerArgs :: default ( )
196+ } ,
197+ beacon_provider_args : BeaconProviderArgs {
198+ blob_source : BlobSource :: Mock ,
199+ ..Default :: default ( )
200+ } ,
201+ signer_args : Default :: default ( ) ,
202+ gas_price_oracle_args : GasPriceOracleArgs :: default ( ) ,
203+ consensus_args : ConsensusArgs :: noop ( ) ,
204+ } ;
205+
206+ let ( nodes, _tasks, _) =
207+ setup_engine ( rollup_manager_args, 2 , chain_spec, false , false ) . await . unwrap ( ) ;
208+
209+ let node0_rmn_handle = nodes[ 0 ] . inner . add_ons_handle . rollup_manager_handle . clone ( ) ;
210+ let node0_network_handle = node0_rmn_handle. get_network_handle ( ) . await . unwrap ( ) ;
211+ let node0_id = node0_network_handle. inner ( ) . peer_id ( ) ;
212+
213+ let node1_rnm_handle = nodes[ 1 ] . inner . add_ons_handle . rollup_manager_handle . clone ( ) ;
214+ let node1_network_handle = node1_rnm_handle. get_network_handle ( ) . await . unwrap ( ) ;
215+
216+ // get initial reputation of node0 from pov of node1
217+ let initial_reputation =
218+ node1_network_handle. inner ( ) . reputation_by_id ( * node0_id) . await . unwrap ( ) . unwrap ( ) ;
219+ assert_eq ! ( initial_reputation, 0 ) ;
220+
221+ // create invalid block
222+ let block = ScrollBlock :: default ( ) ;
223+
224+ // send invalid block from node0 to node1. We don't care about the signature here since we use a
225+ // NoopConsensus in the test.
226+ node0_network_handle. announce_block ( block, Signature :: new ( U256 :: from ( 1 ) , U256 :: from ( 1 ) , false ) ) ;
227+
228+ eventually (
229+ Duration :: from_secs ( 5 ) ,
230+ Duration :: from_millis ( 10 ) ,
231+ "Peer0 reputation should be lower after sending invalid block" ,
232+ || async {
233+ // check that the node0 is penalized on node1
234+ let slashed_reputation =
235+ node1_network_handle. inner ( ) . reputation_by_id ( * node0_id) . await . unwrap ( ) . unwrap ( ) ;
236+ slashed_reputation < initial_reputation
237+ } ,
238+ )
239+ . await ;
240+ }
241+
242+ /// Helper function to wait until a predicate is true or a timeout occurs.
243+ pub async fn eventually < F , Fut > ( timeout : Duration , tick : Duration , message : & str , mut predicate : F )
244+ where
245+ F : FnMut ( ) -> Fut ,
246+ Fut : std:: future:: Future < Output = bool > ,
247+ {
248+ let mut interval = time:: interval ( tick) ;
249+ let start = time:: Instant :: now ( ) ;
250+ loop {
251+ if predicate ( ) . await {
252+ return ;
253+ }
254+
255+ assert ! ( start. elapsed( ) <= timeout, "Timeout while waiting for condition: {message}" ) ;
256+
257+ interval. tick ( ) . await ;
258+ }
259+ }
260+
170261#[ allow( clippy:: large_stack_frames) ]
171262#[ tokio:: test]
172263async fn can_sequence_and_gossip_transactions ( ) {
0 commit comments