@@ -3644,6 +3644,266 @@ impl ApiTester {
3644
3644
self
3645
3645
}
3646
3646
3647
+ pub async fn test_blinded_block_production ( & self ) {
3648
+ let fork = self . chain . canonical_head . cached_head ( ) . head_fork ( ) ;
3649
+ let genesis_validators_root = self . chain . genesis_validators_root ;
3650
+
3651
+ for _ in 0 ..E :: slots_per_epoch ( ) * 3 {
3652
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3653
+ let epoch = self . chain . epoch ( ) . unwrap ( ) ;
3654
+
3655
+ let proposer_pubkey_bytes = self
3656
+ . client
3657
+ . get_validator_duties_proposer ( epoch)
3658
+ . await
3659
+ . unwrap ( )
3660
+ . data
3661
+ . into_iter ( )
3662
+ . find ( |duty| duty. slot == slot)
3663
+ . map ( |duty| duty. pubkey )
3664
+ . unwrap ( ) ;
3665
+ let proposer_pubkey = ( & proposer_pubkey_bytes) . try_into ( ) . unwrap ( ) ;
3666
+
3667
+ let sk = self
3668
+ . validator_keypairs ( )
3669
+ . iter ( )
3670
+ . find ( |kp| kp. pk == proposer_pubkey)
3671
+ . map ( |kp| kp. sk . clone ( ) )
3672
+ . unwrap ( ) ;
3673
+
3674
+ let randao_reveal = {
3675
+ let domain = self . chain . spec . get_domain (
3676
+ epoch,
3677
+ Domain :: Randao ,
3678
+ & fork,
3679
+ genesis_validators_root,
3680
+ ) ;
3681
+ let message = epoch. signing_root ( domain) ;
3682
+ sk. sign ( message) . into ( )
3683
+ } ;
3684
+
3685
+ let ( response, metadata) = self
3686
+ . client
3687
+ . get_validator_blocks_v3 :: < E > ( slot, & randao_reveal, None , None )
3688
+ . await
3689
+ . unwrap ( ) ;
3690
+
3691
+ match response. data {
3692
+ ProduceBlockV3Response :: Blinded ( blinded_block) => {
3693
+ assert ! ( metadata. execution_payload_blinded) ;
3694
+ assert_eq ! (
3695
+ metadata. consensus_version,
3696
+ blinded_block. to_ref( ) . fork_name( & self . chain. spec) . unwrap( )
3697
+ ) ;
3698
+ let signed_blinded_block =
3699
+ blinded_block. sign ( & sk, & fork, genesis_validators_root, & self . chain . spec ) ;
3700
+
3701
+ self . client
3702
+ . post_beacon_blinded_blocks ( & signed_blinded_block)
3703
+ . await
3704
+ . unwrap ( ) ;
3705
+
3706
+ let head_block = self . chain . head_beacon_block ( ) . clone_as_blinded ( ) ;
3707
+ assert_eq ! ( head_block, signed_blinded_block) ;
3708
+
3709
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3710
+ }
3711
+ ProduceBlockV3Response :: Full ( block_contents) => {
3712
+ assert ! ( !metadata. execution_payload_blinded) ;
3713
+ assert_eq ! (
3714
+ metadata. consensus_version,
3715
+ block_contents
3716
+ . block( )
3717
+ . to_ref( )
3718
+ . fork_name( & self . chain. spec)
3719
+ . unwrap( )
3720
+ ) ;
3721
+ let signed_block_contents =
3722
+ block_contents. sign ( & sk, & fork, genesis_validators_root, & self . chain . spec ) ;
3723
+
3724
+ self . client
3725
+ . post_beacon_blocks ( & signed_block_contents)
3726
+ . await
3727
+ . unwrap ( ) ;
3728
+
3729
+ assert_eq ! (
3730
+ self . chain. head_beacon_block( ) ,
3731
+ * signed_block_contents. signed_block( )
3732
+ ) ;
3733
+
3734
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3735
+ }
3736
+ }
3737
+ }
3738
+ }
3739
+
3740
+ pub async fn test_blinded_block_production_ssz ( & self ) {
3741
+ let fork = self . chain . canonical_head . cached_head ( ) . head_fork ( ) ;
3742
+ let genesis_validators_root = self . chain . genesis_validators_root ;
3743
+
3744
+ for _ in 0 ..E :: slots_per_epoch ( ) * 3 {
3745
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3746
+ let epoch = self . chain . epoch ( ) . unwrap ( ) ;
3747
+
3748
+ let proposer_pubkey_bytes = self
3749
+ . client
3750
+ . get_validator_duties_proposer ( epoch)
3751
+ . await
3752
+ . unwrap ( )
3753
+ . data
3754
+ . into_iter ( )
3755
+ . find ( |duty| duty. slot == slot)
3756
+ . map ( |duty| duty. pubkey )
3757
+ . unwrap ( ) ;
3758
+ let proposer_pubkey = ( & proposer_pubkey_bytes) . try_into ( ) . unwrap ( ) ;
3759
+
3760
+ let sk = self
3761
+ . validator_keypairs ( )
3762
+ . iter ( )
3763
+ . find ( |kp| kp. pk == proposer_pubkey)
3764
+ . map ( |kp| kp. sk . clone ( ) )
3765
+ . unwrap ( ) ;
3766
+
3767
+ let randao_reveal = {
3768
+ let domain = self . chain . spec . get_domain (
3769
+ epoch,
3770
+ Domain :: Randao ,
3771
+ & fork,
3772
+ genesis_validators_root,
3773
+ ) ;
3774
+ let message = epoch. signing_root ( domain) ;
3775
+ sk. sign ( message) . into ( )
3776
+ } ;
3777
+
3778
+ let block_contents_bytes = self
3779
+ . client
3780
+ . get_validator_blocks_ssz :: < E > ( slot, & randao_reveal, None )
3781
+ . await
3782
+ . unwrap ( )
3783
+ . expect ( "block bytes" ) ;
3784
+
3785
+ let block_contents =
3786
+ FullBlockContents :: < E > :: from_ssz_bytes ( & block_contents_bytes, & self . chain . spec )
3787
+ . expect ( "block contents bytes can be decoded" ) ;
3788
+
3789
+ let signed_block_contents =
3790
+ block_contents. sign ( & sk, & fork, genesis_validators_root, & self . chain . spec ) ;
3791
+
3792
+ self . client
3793
+ . post_beacon_blinded_blocks_ssz (
3794
+ & signed_block_contents. signed_block ( ) . clone_as_blinded ( ) ,
3795
+ )
3796
+ . await
3797
+ . unwrap ( ) ;
3798
+
3799
+ let head_block = self
3800
+ . client
3801
+ . get_beacon_blocks ( CoreBlockId :: Head )
3802
+ . await
3803
+ . unwrap ( )
3804
+ . unwrap ( )
3805
+ . into_data ( ) ;
3806
+
3807
+ let signed_block = signed_block_contents. signed_block ( ) ;
3808
+ assert_eq ! ( head_block, * * signed_block) ;
3809
+
3810
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3811
+ }
3812
+ }
3813
+
3814
+ pub async fn test_blinded_block_production_no_verify_randao ( self ) -> Self {
3815
+ for _ in 0 ..E :: slots_per_epoch ( ) {
3816
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3817
+
3818
+ let ( response, _metadata) = self
3819
+ . client
3820
+ . get_validator_blocks_v3_modular :: < E > (
3821
+ slot,
3822
+ & Signature :: infinity ( ) . unwrap ( ) . into ( ) ,
3823
+ None ,
3824
+ SkipRandaoVerification :: Yes ,
3825
+ None ,
3826
+ )
3827
+ . await
3828
+ . unwrap ( ) ;
3829
+
3830
+ match response. data {
3831
+ ProduceBlockV3Response :: Blinded ( blinded_block) => {
3832
+ assert_eq ! ( blinded_block. slot( ) , slot) ;
3833
+ }
3834
+ ProduceBlockV3Response :: Full ( full_block) => {
3835
+ assert_eq ! ( full_block. block( ) . slot( ) , slot) ;
3836
+ }
3837
+ }
3838
+
3839
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3840
+ }
3841
+
3842
+ self
3843
+ }
3844
+
3845
+ pub async fn test_blinded_block_production_verify_randao_invalid ( self ) -> Self {
3846
+ let fork = self . chain . canonical_head . cached_head ( ) . head_fork ( ) ;
3847
+ let genesis_validators_root = self . chain . genesis_validators_root ;
3848
+
3849
+ for _ in 0 ..E :: slots_per_epoch ( ) {
3850
+ let slot = self . chain . slot ( ) . unwrap ( ) ;
3851
+ let epoch = self . chain . epoch ( ) . unwrap ( ) ;
3852
+
3853
+ let proposer_pubkey_bytes = self
3854
+ . client
3855
+ . get_validator_duties_proposer ( epoch)
3856
+ . await
3857
+ . unwrap ( )
3858
+ . data
3859
+ . into_iter ( )
3860
+ . find ( |duty| duty. slot == slot)
3861
+ . map ( |duty| duty. pubkey )
3862
+ . unwrap ( ) ;
3863
+ let proposer_pubkey = ( & proposer_pubkey_bytes) . try_into ( ) . unwrap ( ) ;
3864
+
3865
+ let sk = self
3866
+ . validator_keypairs ( )
3867
+ . iter ( )
3868
+ . find ( |kp| kp. pk == proposer_pubkey)
3869
+ . map ( |kp| kp. sk . clone ( ) )
3870
+ . unwrap ( ) ;
3871
+
3872
+ let bad_randao_reveal = {
3873
+ let domain = self . chain . spec . get_domain (
3874
+ epoch,
3875
+ Domain :: Randao ,
3876
+ & fork,
3877
+ genesis_validators_root,
3878
+ ) ;
3879
+ let message = ( epoch + 1 ) . signing_root ( domain) ;
3880
+ sk. sign ( message) . into ( )
3881
+ } ;
3882
+
3883
+ // Check failure with full randao verification enabled.
3884
+ self . client
3885
+ . get_validator_blocks_v3 :: < E > ( slot, & bad_randao_reveal, None , None )
3886
+ . await
3887
+ . unwrap_err ( ) ;
3888
+
3889
+ // Check failure with `skip_randao_verification` (requires infinity sig).
3890
+ self . client
3891
+ . get_validator_blocks_v3_modular :: < E > (
3892
+ slot,
3893
+ & bad_randao_reveal,
3894
+ None ,
3895
+ SkipRandaoVerification :: Yes ,
3896
+ None ,
3897
+ )
3898
+ . await
3899
+ . unwrap_err ( ) ;
3900
+
3901
+ self . chain . slot_clock . set_slot ( slot. as_u64 ( ) + 1 ) ;
3902
+ }
3903
+
3904
+ self
3905
+ }
3906
+
3647
3907
pub async fn test_get_validator_attestation_data ( self ) -> Self {
3648
3908
let mut state = self . chain . head_beacon_state_cloned ( ) ;
3649
3909
let slot = state. slot ( ) ;
@@ -4337,7 +4597,7 @@ impl ApiTester {
4337
4597
ProduceBlockV3Response :: Blinded ( payload) => {
4338
4598
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
4339
4599
}
4340
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
4600
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
4341
4601
} ;
4342
4602
4343
4603
let expected_fee_recipient = Address :: from_low_u64_be ( proposer_index) ;
@@ -4510,7 +4770,7 @@ impl ApiTester {
4510
4770
ProduceBlockV3Response :: Blinded ( payload) => {
4511
4771
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
4512
4772
}
4513
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
4773
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
4514
4774
} ;
4515
4775
4516
4776
assert_eq ! ( payload. fee_recipient( ) , test_fee_recipient) ;
@@ -5102,7 +5362,7 @@ impl ApiTester {
5102
5362
ProduceBlockV3Response :: Blinded ( payload) => {
5103
5363
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
5104
5364
}
5105
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
5365
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
5106
5366
} ;
5107
5367
5108
5368
// This cache should not be populated because fallback should not have been used.
@@ -5291,7 +5551,7 @@ impl ApiTester {
5291
5551
ProduceBlockV3Response :: Blinded ( payload) => {
5292
5552
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
5293
5553
}
5294
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
5554
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
5295
5555
} ;
5296
5556
5297
5557
// This cache should not be populated because fallback should not have been used.
@@ -5706,7 +5966,7 @@ impl ApiTester {
5706
5966
ProduceBlockV3Response :: Blinded ( payload) => {
5707
5967
payload. body ( ) . execution_payload ( ) . unwrap ( ) . into ( )
5708
5968
}
5709
- ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a full payload" ) ,
5969
+ ProduceBlockV3Response :: Full ( _) => panic ! ( "Expecting a blinded payload" ) ,
5710
5970
} ;
5711
5971
5712
5972
// The builder's payload should've been chosen, so this cache should not be populated
@@ -7018,6 +7278,83 @@ async fn block_production_v3_ssz_with_skip_slots() {
7018
7278
. await ;
7019
7279
}
7020
7280
7281
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7282
+ async fn blinded_block_production_full_payload_premerge ( ) {
7283
+ ApiTester :: new ( ) . await . test_blinded_block_production ( ) . await ;
7284
+ }
7285
+
7286
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7287
+ async fn blinded_block_production_ssz_full_payload_premerge ( ) {
7288
+ ApiTester :: new ( )
7289
+ . await
7290
+ . test_blinded_block_production_ssz ( )
7291
+ . await ;
7292
+ }
7293
+
7294
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7295
+ async fn blinded_block_production_with_skip_slots_full_payload_premerge ( ) {
7296
+ ApiTester :: new ( )
7297
+ . await
7298
+ . skip_slots ( E :: slots_per_epoch ( ) * 2 )
7299
+ . test_blinded_block_production ( )
7300
+ . await ;
7301
+ }
7302
+
7303
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7304
+ async fn blinded_block_production_ssz_with_skip_slots_full_payload_premerge ( ) {
7305
+ ApiTester :: new ( )
7306
+ . await
7307
+ . skip_slots ( E :: slots_per_epoch ( ) * 2 )
7308
+ . test_blinded_block_production_ssz ( )
7309
+ . await ;
7310
+ }
7311
+
7312
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7313
+ async fn blinded_block_production_no_verify_randao_full_payload_premerge ( ) {
7314
+ ApiTester :: new ( )
7315
+ . await
7316
+ . test_blinded_block_production_no_verify_randao ( )
7317
+ . await ;
7318
+ }
7319
+
7320
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7321
+ async fn blinded_block_production_verify_randao_invalid_full_payload_premerge ( ) {
7322
+ ApiTester :: new ( )
7323
+ . await
7324
+ . test_blinded_block_production_verify_randao_invalid ( )
7325
+ . await ;
7326
+ }
7327
+
7328
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7329
+ async fn blinded_block_production_blinded_payload_premerge ( ) {
7330
+ ApiTester :: new ( ) . await . test_blinded_block_production ( ) . await ;
7331
+ }
7332
+
7333
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7334
+ async fn blinded_block_production_with_skip_slots_blinded_payload_premerge ( ) {
7335
+ ApiTester :: new ( )
7336
+ . await
7337
+ . skip_slots ( E :: slots_per_epoch ( ) * 2 )
7338
+ . test_blinded_block_production ( )
7339
+ . await ;
7340
+ }
7341
+
7342
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7343
+ async fn blinded_block_production_no_verify_randao_blinded_payload_premerge ( ) {
7344
+ ApiTester :: new ( )
7345
+ . await
7346
+ . test_blinded_block_production_no_verify_randao ( )
7347
+ . await ;
7348
+ }
7349
+
7350
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7351
+ async fn blinded_block_production_verify_randao_invalid_blinded_payload_premerge ( ) {
7352
+ ApiTester :: new ( )
7353
+ . await
7354
+ . test_blinded_block_production_verify_randao_invalid ( )
7355
+ . await ;
7356
+ }
7357
+
7021
7358
#[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
7022
7359
async fn get_validator_attestation_data ( ) {
7023
7360
ApiTester :: new ( )
0 commit comments