@@ -3,11 +3,17 @@ use std::time::Duration;
3
3
use eyre:: Result ;
4
4
use futures_util:: { stream:: FuturesUnordered , StreamExt } ;
5
5
use iroha_config_base:: toml:: WriteExt ;
6
+ use iroha_data_model:: {
7
+ asset:: AssetDefinition , isi:: Register , parameter:: BlockParameter , prelude:: * ,
8
+ } ;
6
9
use iroha_test_network:: {
7
10
genesis_factory, once_blocks_sync, Network , NetworkBuilder , PeerLifecycleEvent ,
8
11
} ;
12
+ use iroha_test_samples:: ALICE_ID ;
13
+ use nonzero_ext:: nonzero;
14
+ use rand:: { prelude:: SliceRandom , thread_rng} ;
9
15
use relay:: P2pRelay ;
10
- use tokio:: { self , time:: timeout} ;
16
+ use tokio:: { self , task :: spawn_blocking , time:: timeout} ;
11
17
12
18
mod relay {
13
19
use std:: {
@@ -361,3 +367,153 @@ async fn suspending_works() -> Result<()> {
361
367
362
368
Ok ( ( ) )
363
369
}
370
+
371
+ // ======= ACTUAL TESTS BEGIN HERE =======
372
+
373
+ struct UnstableNetwork {
374
+ n_peers : usize ,
375
+ n_faulty_peers : usize ,
376
+ n_transactions : usize ,
377
+ force_soft_fork : bool ,
378
+ }
379
+
380
+ impl UnstableNetwork {
381
+ async fn run ( self ) -> Result < ( ) > {
382
+ assert ! ( self . n_peers > self . n_faulty_peers) ;
383
+
384
+ let account_id = ALICE_ID . clone ( ) ;
385
+ let asset_definition_id: AssetDefinitionId = "camomile#wonderland" . parse ( ) . expect ( "Valid" ) ;
386
+
387
+ let network = NetworkBuilder :: new ( )
388
+ . with_peers ( self . n_peers )
389
+ . with_config ( |cfg| {
390
+ if self . force_soft_fork {
391
+ cfg. write ( [ "sumeragi" , "debug_force_soft_fork" ] , true ) ;
392
+ }
393
+ } )
394
+ . with_genesis_instruction ( SetParameter ( Parameter :: Block (
395
+ BlockParameter :: MaxTransactions ( nonzero ! ( 1u64 ) ) ,
396
+ ) ) )
397
+ . build ( ) ;
398
+ let mut relay = start_network_with_relay ( & network) . await ?;
399
+
400
+ relay. start ( ) ;
401
+ {
402
+ let client = network. client ( ) ;
403
+ let isi =
404
+ Register :: asset_definition ( AssetDefinition :: numeric ( asset_definition_id. clone ( ) ) ) ;
405
+ spawn_blocking ( move || client. submit_blocking ( isi) ) . await ??;
406
+ }
407
+ let init_blocks = 2 ;
408
+ network. ensure_blocks ( init_blocks) . await ?;
409
+
410
+ for i in 0 ..self . n_transactions {
411
+ // Make random peers faulty.
412
+ let faulty: Vec < _ > = network
413
+ . peers ( )
414
+ . choose_multiple ( & mut thread_rng ( ) , self . n_faulty_peers )
415
+ . map ( |peer| peer. id ( ) )
416
+ . collect ( ) ;
417
+ for peer in & faulty {
418
+ relay. suspend ( peer) . activate ( ) ;
419
+ }
420
+
421
+ // When minted
422
+ let quantity = Numeric :: ONE ;
423
+ let mint_asset = Mint :: asset_numeric (
424
+ quantity,
425
+ AssetId :: new ( asset_definition_id. clone ( ) , account_id. clone ( ) ) ,
426
+ ) ;
427
+ let client = network
428
+ . peers ( )
429
+ . iter ( )
430
+ . find ( |x| faulty. contains ( & x. id ( ) ) )
431
+ . expect ( "there should be some working peers" )
432
+ . client ( ) ;
433
+ spawn_blocking ( move || client. submit_blocking ( mint_asset) ) . await ??;
434
+
435
+ // Then all non-faulty peers get the new block
436
+ timeout (
437
+ network. sync_timeout ( ) ,
438
+ once_blocks_sync (
439
+ network. peers ( ) . iter ( ) . filter ( |x| !faulty. contains ( & x. id ( ) ) ) ,
440
+ init_blocks + ( i as u64 ) ,
441
+ ) ,
442
+ )
443
+ . await ?;
444
+
445
+ // Return all peers to normal function.
446
+ for peer in & faulty {
447
+ relay. suspend ( peer) . deactivate ( ) ;
448
+ }
449
+ }
450
+
451
+ // When network is sync at last
452
+ network
453
+ . ensure_blocks ( init_blocks + self . n_transactions as u64 )
454
+ . await ?;
455
+
456
+ // Then there are N assets minted
457
+ let client = network. client ( ) ;
458
+ let asset = spawn_blocking ( move || {
459
+ client
460
+ . query ( FindAssets )
461
+ . filter_with ( |asset| asset. id . definition_id . eq ( asset_definition_id) )
462
+ . execute_all ( )
463
+ } )
464
+ . await ??
465
+ . into_iter ( )
466
+ . next ( )
467
+ . expect ( "there should be 1 result" ) ;
468
+ assert_eq ! (
469
+ asset. value,
470
+ AssetValue :: Numeric ( Numeric :: new( self . n_transactions as u128 + 1 , 0 ) )
471
+ ) ;
472
+
473
+ Ok ( ( ) )
474
+ }
475
+ }
476
+
477
+ #[ tokio:: test]
478
+ async fn unstable_network_5_peers_1_fault ( ) -> Result < ( ) > {
479
+ UnstableNetwork {
480
+ n_peers : 5 ,
481
+ n_faulty_peers : 1 ,
482
+ n_transactions : 20 ,
483
+ force_soft_fork : false ,
484
+ }
485
+ . run ( )
486
+ . await
487
+ }
488
+
489
+ // #[tokio::test]
490
+ // async fn soft_fork() {
491
+ // let n_peers = 4;
492
+ // let n_transactions = 20;
493
+ // unstable_network(n_peers, 0, n_transactions, true, 10_830);
494
+ // }
495
+
496
+ #[ tokio:: test]
497
+ async fn unstable_network_8_peers_1_fault ( ) -> Result < ( ) > {
498
+ UnstableNetwork {
499
+ n_peers : 8 ,
500
+ n_faulty_peers : 1 ,
501
+ n_transactions : 20 ,
502
+ force_soft_fork : false ,
503
+ }
504
+ . run ( )
505
+ . await
506
+ }
507
+
508
+ #[ tokio:: test]
509
+ // #[ignore = "This test does not guarantee to have positive outcome given a fixed time."]
510
+ async fn unstable_network_9_peers_2_faults ( ) -> Result < ( ) > {
511
+ UnstableNetwork {
512
+ n_peers : 9 ,
513
+ n_faulty_peers : 2 ,
514
+ n_transactions : 5 ,
515
+ force_soft_fork : false ,
516
+ }
517
+ . run ( )
518
+ . await
519
+ }
0 commit comments