@@ -3469,6 +3469,266 @@ impl ApiTester {
3469
3469
self
3470
3470
}
3471
3471
3472
+ pub async fn test_blinded_block_production ( & self ) {
3473
+ let fork = self . chain . canonical_head . cached_head ( ) . head_fork ( ) ;
3474
+ let genesis_validators_root = self . chain . genesis_validators_root ;
3475
+
3476
+ for _ in 0 ..E :: slots_per_epoch ( ) * 3 {
3477
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3478
+ let epoch = self . chain . epoch ( ) . unwrap ( ) ;
3479
+
3480
+ let proposer_pubkey_bytes = self
3481
+ . client
3482
+ . get_validator_duties_proposer ( epoch)
3483
+ . await
3484
+ . unwrap ( )
3485
+ . data
3486
+ . into_iter ( )
3487
+ . find ( |duty| duty. slot == slot)
3488
+ . map ( |duty| duty. pubkey )
3489
+ . unwrap ( ) ;
3490
+ let proposer_pubkey = ( & proposer_pubkey_bytes) . try_into ( ) . unwrap ( ) ;
3491
+
3492
+ let sk = self
3493
+ . validator_keypairs ( )
3494
+ . iter ( )
3495
+ . find ( |kp| kp. pk == proposer_pubkey)
3496
+ . map ( |kp| kp. sk . clone ( ) )
3497
+ . unwrap ( ) ;
3498
+
3499
+ let randao_reveal = {
3500
+ let domain = self . chain . spec . get_domain (
3501
+ epoch,
3502
+ Domain :: Randao ,
3503
+ & fork,
3504
+ genesis_validators_root,
3505
+ ) ;
3506
+ let message = epoch. signing_root ( domain) ;
3507
+ sk. sign ( message) . into ( )
3508
+ } ;
3509
+
3510
+ let ( response, metadata) = self
3511
+ . client
3512
+ . get_validator_blocks_v3 :: < E > ( slot, & randao_reveal, None , None )
3513
+ . await
3514
+ . unwrap ( ) ;
3515
+
3516
+ match response. data {
3517
+ ProduceBlockV3Response :: Blinded ( blinded_block) => {
3518
+ assert ! ( metadata. execution_payload_blinded) ;
3519
+ assert_eq ! (
3520
+ metadata. consensus_version,
3521
+ blinded_block. to_ref( ) . fork_name( & self . chain. spec) . unwrap( )
3522
+ ) ;
3523
+ let signed_blinded_block =
3524
+ blinded_block. sign ( & sk, & fork, genesis_validators_root, & self . chain . spec ) ;
3525
+
3526
+ self . client
3527
+ . post_beacon_blinded_blocks ( & signed_blinded_block)
3528
+ . await
3529
+ . unwrap ( ) ;
3530
+
3531
+ let head_block = self . chain . head_beacon_block ( ) . clone_as_blinded ( ) ;
3532
+ assert_eq ! ( head_block, signed_blinded_block) ;
3533
+
3534
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3535
+ }
3536
+ ProduceBlockV3Response :: Full ( block_contents) => {
3537
+ assert ! ( !metadata. execution_payload_blinded) ;
3538
+ assert_eq ! (
3539
+ metadata. consensus_version,
3540
+ block_contents
3541
+ . block( )
3542
+ . to_ref( )
3543
+ . fork_name( & self . chain. spec)
3544
+ . unwrap( )
3545
+ ) ;
3546
+ let signed_block_contents =
3547
+ block_contents. sign ( & sk, & fork, genesis_validators_root, & self . chain . spec ) ;
3548
+
3549
+ self . client
3550
+ . post_beacon_blocks ( & signed_block_contents)
3551
+ . await
3552
+ . unwrap ( ) ;
3553
+
3554
+ assert_eq ! (
3555
+ self . chain. head_beacon_block( ) ,
3556
+ * signed_block_contents. signed_block( )
3557
+ ) ;
3558
+
3559
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3560
+ }
3561
+ }
3562
+ }
3563
+ }
3564
+
3565
+ pub async fn test_blinded_block_production_ssz ( & self ) {
3566
+ let fork = self . chain . canonical_head . cached_head ( ) . head_fork ( ) ;
3567
+ let genesis_validators_root = self . chain . genesis_validators_root ;
3568
+
3569
+ for _ in 0 ..E :: slots_per_epoch ( ) * 3 {
3570
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3571
+ let epoch = self . chain . epoch ( ) . unwrap ( ) ;
3572
+
3573
+ let proposer_pubkey_bytes = self
3574
+ . client
3575
+ . get_validator_duties_proposer ( epoch)
3576
+ . await
3577
+ . unwrap ( )
3578
+ . data
3579
+ . into_iter ( )
3580
+ . find ( |duty| duty. slot == slot)
3581
+ . map ( |duty| duty. pubkey )
3582
+ . unwrap ( ) ;
3583
+ let proposer_pubkey = ( & proposer_pubkey_bytes) . try_into ( ) . unwrap ( ) ;
3584
+
3585
+ let sk = self
3586
+ . validator_keypairs ( )
3587
+ . iter ( )
3588
+ . find ( |kp| kp. pk == proposer_pubkey)
3589
+ . map ( |kp| kp. sk . clone ( ) )
3590
+ . unwrap ( ) ;
3591
+
3592
+ let randao_reveal = {
3593
+ let domain = self . chain . spec . get_domain (
3594
+ epoch,
3595
+ Domain :: Randao ,
3596
+ & fork,
3597
+ genesis_validators_root,
3598
+ ) ;
3599
+ let message = epoch. signing_root ( domain) ;
3600
+ sk. sign ( message) . into ( )
3601
+ } ;
3602
+
3603
+ let block_contents_bytes = self
3604
+ . client
3605
+ . get_validator_blocks_ssz :: < E > ( slot, & randao_reveal, None )
3606
+ . await
3607
+ . unwrap ( )
3608
+ . expect ( "block bytes" ) ;
3609
+
3610
+ let block_contents =
3611
+ FullBlockContents :: < E > :: from_ssz_bytes ( & block_contents_bytes, & self . chain . spec )
3612
+ . expect ( "block contents bytes can be decoded" ) ;
3613
+
3614
+ let signed_block_contents =
3615
+ block_contents. sign ( & sk, & fork, genesis_validators_root, & self . chain . spec ) ;
3616
+
3617
+ self . client
3618
+ . post_beacon_blinded_blocks_ssz (
3619
+ & signed_block_contents. signed_block ( ) . clone_as_blinded ( ) ,
3620
+ )
3621
+ . await
3622
+ . unwrap ( ) ;
3623
+
3624
+ let head_block = self
3625
+ . client
3626
+ . get_beacon_blocks ( CoreBlockId :: Head )
3627
+ . await
3628
+ . unwrap ( )
3629
+ . unwrap ( )
3630
+ . into_data ( ) ;
3631
+
3632
+ let signed_block = signed_block_contents. signed_block ( ) ;
3633
+ assert_eq ! ( head_block, * * signed_block) ;
3634
+
3635
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3636
+ }
3637
+ }
3638
+
3639
+ pub async fn test_blinded_block_production_no_verify_randao ( self ) -> Self {
3640
+ for _ in 0 ..E :: slots_per_epoch ( ) {
3641
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3642
+
3643
+ let ( response, _metadata) = self
3644
+ . client
3645
+ . get_validator_blocks_v3_modular :: < E > (
3646
+ slot,
3647
+ & Signature :: infinity ( ) . unwrap ( ) . into ( ) ,
3648
+ None ,
3649
+ SkipRandaoVerification :: Yes ,
3650
+ None ,
3651
+ )
3652
+ . await
3653
+ . unwrap ( ) ;
3654
+
3655
+ match response. data {
3656
+ ProduceBlockV3Response :: Blinded ( blinded_block) => {
3657
+ assert_eq ! ( blinded_block. slot( ) , slot) ;
3658
+ }
3659
+ ProduceBlockV3Response :: Full ( full_block) => {
3660
+ assert_eq ! ( full_block. block( ) . slot( ) , slot) ;
3661
+ }
3662
+ }
3663
+
3664
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3665
+ }
3666
+
3667
+ self
3668
+ }
3669
+
3670
+ pub async fn test_blinded_block_production_verify_randao_invalid ( self ) -> Self {
3671
+ let fork = self . chain . canonical_head . cached_head ( ) . head_fork ( ) ;
3672
+ let genesis_validators_root = self . chain . genesis_validators_root ;
3673
+
3674
+ for _ in 0 ..E :: slots_per_epoch ( ) {
3675
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3676
+ let epoch = self . chain . epoch ( ) . unwrap ( ) ;
3677
+
3678
+ let proposer_pubkey_bytes = self
3679
+ . client
3680
+ . get_validator_duties_proposer ( epoch)
3681
+ . await
3682
+ . unwrap ( )
3683
+ . data
3684
+ . into_iter ( )
3685
+ . find ( |duty| duty. slot == slot)
3686
+ . map ( |duty| duty. pubkey )
3687
+ . unwrap ( ) ;
3688
+ let proposer_pubkey = ( & proposer_pubkey_bytes) . try_into ( ) . unwrap ( ) ;
3689
+
3690
+ let sk = self
3691
+ . validator_keypairs ( )
3692
+ . iter ( )
3693
+ . find ( |kp| kp. pk == proposer_pubkey)
3694
+ . map ( |kp| kp. sk . clone ( ) )
3695
+ . unwrap ( ) ;
3696
+
3697
+ let bad_randao_reveal = {
3698
+ let domain = self . chain . spec . get_domain (
3699
+ epoch,
3700
+ Domain :: Randao ,
3701
+ & fork,
3702
+ genesis_validators_root,
3703
+ ) ;
3704
+ let message = ( epoch + 1 ) . signing_root ( domain) ;
3705
+ sk. sign ( message) . into ( )
3706
+ } ;
3707
+
3708
+ // Check failure with full randao verification enabled.
3709
+ self . client
3710
+ . get_validator_blocks_v3 :: < E > ( slot, & bad_randao_reveal, None , None )
3711
+ . await
3712
+ . unwrap_err ( ) ;
3713
+
3714
+ // Check failure with `skip_randao_verification` (requires infinity sig).
3715
+ self . client
3716
+ . get_validator_blocks_v3_modular :: < E > (
3717
+ slot,
3718
+ & bad_randao_reveal,
3719
+ None ,
3720
+ SkipRandaoVerification :: Yes ,
3721
+ None ,
3722
+ )
3723
+ . await
3724
+ . unwrap_err ( ) ;
3725
+
3726
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3727
+ }
3728
+
3729
+ self
3730
+ }
3731
+
3472
3732
pub async fn test_get_validator_attestation_data ( self ) -> Self {
3473
3733
let mut state = self . chain . head_beacon_state_cloned ( ) ;
3474
3734
let slot = state. slot ( ) ;
@@ -4124,7 +4384,7 @@ impl ApiTester {
4124
4384
ProduceBlockV3Response :: Blinded ( payload) => {
4125
4385
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
4126
4386
}
4127
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
4387
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
4128
4388
} ;
4129
4389
4130
4390
let expected_fee_recipient = Address :: from_low_u64_be ( proposer_index) ;
@@ -4297,7 +4557,7 @@ impl ApiTester {
4297
4557
ProduceBlockV3Response :: Blinded ( payload) => {
4298
4558
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
4299
4559
}
4300
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
4560
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
4301
4561
} ;
4302
4562
4303
4563
assert_eq ! ( payload. fee_recipient( ) , test_fee_recipient) ;
@@ -4889,7 +5149,7 @@ impl ApiTester {
4889
5149
ProduceBlockV3Response :: Blinded ( payload) => {
4890
5150
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
4891
5151
}
4892
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
5152
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
4893
5153
} ;
4894
5154
4895
5155
// This cache should not be populated because fallback should not have been used.
@@ -5078,7 +5338,7 @@ impl ApiTester {
5078
5338
ProduceBlockV3Response :: Blinded ( payload) => {
5079
5339
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
5080
5340
}
5081
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
5341
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
5082
5342
} ;
5083
5343
5084
5344
// This cache should not be populated because fallback should not have been used.
@@ -5493,7 +5753,7 @@ impl ApiTester {
5493
5753
ProduceBlockV3Response :: Blinded ( payload) => {
5494
5754
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
5495
5755
}
5496
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
5756
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
5497
5757
} ;
5498
5758
5499
5759
// The builder's payload should've been chosen, so this cache should not be populated
@@ -6760,6 +7020,83 @@ async fn block_production_v3_ssz_with_skip_slots() {
6760
7020
. await ;
6761
7021
}
6762
7022
7023
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7024
+ async fn blinded_block_production_full_payload_premerge ( ) {
7025
+ ApiTester :: new ( ) . await . test_blinded_block_production ( ) . await ;
7026
+ }
7027
+
7028
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7029
+ async fn blinded_block_production_ssz_full_payload_premerge ( ) {
7030
+ ApiTester :: new ( )
7031
+ . await
7032
+ . test_blinded_block_production_ssz ( )
7033
+ . await ;
7034
+ }
7035
+
7036
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7037
+ async fn blinded_block_production_with_skip_slots_full_payload_premerge ( ) {
7038
+ ApiTester :: new ( )
7039
+ . await
7040
+ . skip_slots ( E :: slots_per_epoch ( ) * 2 )
7041
+ . test_blinded_block_production ( )
7042
+ . await ;
7043
+ }
7044
+
7045
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7046
+ async fn blinded_block_production_ssz_with_skip_slots_full_payload_premerge ( ) {
7047
+ ApiTester :: new ( )
7048
+ . await
7049
+ . skip_slots ( E :: slots_per_epoch ( ) * 2 )
7050
+ . test_blinded_block_production_ssz ( )
7051
+ . await ;
7052
+ }
7053
+
7054
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7055
+ async fn blinded_block_production_no_verify_randao_full_payload_premerge ( ) {
7056
+ ApiTester :: new ( )
7057
+ . await
7058
+ . test_blinded_block_production_no_verify_randao ( )
7059
+ . await ;
7060
+ }
7061
+
7062
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7063
+ async fn blinded_block_production_verify_randao_invalid_full_payload_premerge ( ) {
7064
+ ApiTester :: new ( )
7065
+ . await
7066
+ . test_blinded_block_production_verify_randao_invalid ( )
7067
+ . await ;
7068
+ }
7069
+
7070
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7071
+ async fn blinded_block_production_blinded_payload_premerge ( ) {
7072
+ ApiTester :: new ( ) . await . test_blinded_block_production ( ) . await ;
7073
+ }
7074
+
7075
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7076
+ async fn blinded_block_production_with_skip_slots_blinded_payload_premerge ( ) {
7077
+ ApiTester :: new ( )
7078
+ . await
7079
+ . skip_slots ( E :: slots_per_epoch ( ) * 2 )
7080
+ . test_blinded_block_production ( )
7081
+ . await ;
7082
+ }
7083
+
7084
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7085
+ async fn blinded_block_production_no_verify_randao_blinded_payload_premerge ( ) {
7086
+ ApiTester :: new ( )
7087
+ . await
7088
+ . test_blinded_block_production_no_verify_randao ( )
7089
+ . await ;
7090
+ }
7091
+
7092
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7093
+ async fn blinded_block_production_verify_randao_invalid_blinded_payload_premerge ( ) {
7094
+ ApiTester :: new ( )
7095
+ . await
7096
+ . test_blinded_block_production_verify_randao_invalid ( )
7097
+ . await ;
7098
+ }
7099
+
6763
7100
#[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
6764
7101
async fn get_validator_attestation_data ( ) {
6765
7102
ApiTester :: new ( )
0 commit comments