@@ -323,32 +323,105 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
323
323
)
324
324
}
325
325
326
- for validator_pubkey in proposers {
327
- let builder_boost_factor = self . get_builder_boost_factor ( & validator_pubkey) ;
328
- let service = self . clone ( ) ;
329
- let log = log. clone ( ) ;
330
- self . inner . context . executor . spawn (
331
- async move {
332
- let result = service
333
- . publish_block ( slot, validator_pubkey, builder_boost_factor)
334
- . await ;
335
-
336
- match result {
337
- Ok ( _) => { }
338
- Err ( BlockError :: Recoverable ( e) ) | Err ( BlockError :: Irrecoverable ( e) ) => {
339
- error ! (
340
- log,
341
- "Error whilst producing block" ;
342
- "error" => ?e,
343
- "block_slot" => ?slot,
344
- "info" => "block v3 proposal failed, this error may or may not result in a missed block"
345
- ) ;
326
+ if self . validator_store . produce_block_v3 ( ) {
327
+ for validator_pubkey in proposers {
328
+ let builder_boost_factor = self . get_builder_boost_factor ( & validator_pubkey) ;
329
+ let service = self . clone ( ) ;
330
+ let log = log. clone ( ) ;
331
+ self . inner . context . executor . spawn (
332
+ async move {
333
+ let result = service
334
+ . publish_block_v3 ( slot, validator_pubkey, builder_boost_factor)
335
+ . await ;
336
+
337
+ match result {
338
+ Ok ( _) => { }
339
+ Err ( BlockError :: Recoverable ( e) ) | Err ( BlockError :: Irrecoverable ( e) ) => {
340
+ error ! (
341
+ log,
342
+ "Error whilst producing block" ;
343
+ "error" => ?e,
344
+ "block_slot" => ?slot,
345
+ "info" => "block v3 proposal failed, this error may or may not result in a missed block"
346
+ ) ;
347
+ }
346
348
}
347
- }
348
- } ,
349
- "block service" ,
350
- )
349
+ } ,
350
+ "block service" ,
351
+ )
352
+ }
353
+ } else {
354
+ for validator_pubkey in proposers {
355
+ let builder_proposals = self
356
+ . validator_store
357
+ . get_builder_proposals ( & validator_pubkey) ;
358
+ let service = self . clone ( ) ;
359
+ let log = log. clone ( ) ;
360
+ self . inner . context . executor . spawn (
361
+ async move {
362
+ if builder_proposals {
363
+ let result = service
364
+ . publish_block ( slot, validator_pubkey, true )
365
+ . await ;
366
+
367
+ match result {
368
+ Err ( BlockError :: Recoverable ( e) ) => {
369
+ error ! (
370
+ log,
371
+ "Error whilst producing block" ;
372
+ "error" => ?e,
373
+ "block_slot" => ?slot,
374
+ "info" => "blinded proposal failed, attempting full block"
375
+ ) ;
376
+ if let Err ( e) = service
377
+ . publish_block ( slot, validator_pubkey, false )
378
+ . await
379
+ {
380
+ // Log a `crit` since a full block
381
+ // (non-builder) proposal failed.
382
+ crit ! (
383
+ log,
384
+ "Error whilst producing block" ;
385
+ "error" => ?e,
386
+ "block_slot" => ?slot,
387
+ "info" => "full block attempted after a blinded failure" ,
388
+ ) ;
389
+ }
390
+ }
391
+ Err ( BlockError :: Irrecoverable ( e) ) => {
392
+ // Only log an `error` since it's common for
393
+ // builders to timeout on their response, only
394
+ // to publish the block successfully themselves.
395
+ error ! (
396
+ log,
397
+ "Error whilst producing block" ;
398
+ "error" => ?e,
399
+ "block_slot" => ?slot,
400
+ "info" => "this error may or may not result in a missed block" ,
401
+ )
402
+ }
403
+ Ok ( _) => { }
404
+ } ;
405
+ } else if let Err ( e) = service
406
+ . publish_block ( slot, validator_pubkey, false )
407
+ . await
408
+ {
409
+ // Log a `crit` since a full block (non-builder)
410
+ // proposal failed.
411
+ crit ! (
412
+ log,
413
+ "Error whilst producing block" ;
414
+ "message" => ?e,
415
+ "block_slot" => ?slot,
416
+ "info" => "proposal did not use a builder" ,
417
+ ) ;
418
+ }
419
+ } ,
420
+ "block service" ,
421
+ )
422
+ }
351
423
}
424
+
352
425
Ok ( ( ) )
353
426
}
354
427
@@ -440,7 +513,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
440
513
Ok ( ( ) )
441
514
}
442
515
443
- async fn publish_block (
516
+ async fn publish_block_v3 (
444
517
self ,
445
518
slot : Slot ,
446
519
validator_pubkey : PublicKeyBytes ,
@@ -511,7 +584,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
511
584
& metrics:: BLOCK_SERVICE_TIMES ,
512
585
& [ metrics:: BEACON_BLOCK_HTTP_GET ] ,
513
586
) ;
514
- let block_response = Self :: get_validator_block (
587
+ let block_response = Self :: get_validator_block_v3 (
515
588
beacon_node,
516
589
slot,
517
590
randao_reveal_ref,
@@ -546,6 +619,100 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
546
619
Ok ( ( ) )
547
620
}
548
621
622
+ /// Produce a block at the given slot for validator_pubkey
623
+ async fn publish_block (
624
+ & self ,
625
+ slot : Slot ,
626
+ validator_pubkey : PublicKeyBytes ,
627
+ builder_proposal : bool ,
628
+ ) -> Result < ( ) , BlockError > {
629
+ let log = self . context . log ( ) ;
630
+ let _timer =
631
+ metrics:: start_timer_vec ( & metrics:: BLOCK_SERVICE_TIMES , & [ metrics:: BEACON_BLOCK ] ) ;
632
+
633
+ let randao_reveal = match self
634
+ . validator_store
635
+ . randao_reveal ( validator_pubkey, slot. epoch ( E :: slots_per_epoch ( ) ) )
636
+ . await
637
+ {
638
+ Ok ( signature) => signature. into ( ) ,
639
+ Err ( ValidatorStoreError :: UnknownPubkey ( pubkey) ) => {
640
+ // A pubkey can be missing when a validator was recently removed
641
+ // via the API.
642
+ warn ! (
643
+ log,
644
+ "Missing pubkey for block" ;
645
+ "info" => "a validator may have recently been removed from this VC" ,
646
+ "pubkey" => ?pubkey,
647
+ "slot" => ?slot
648
+ ) ;
649
+ return Ok ( ( ) ) ;
650
+ }
651
+ Err ( e) => {
652
+ return Err ( BlockError :: Recoverable ( format ! (
653
+ "Unable to sign block: {:?}" ,
654
+ e
655
+ ) ) )
656
+ }
657
+ } ;
658
+
659
+ let graffiti = determine_graffiti (
660
+ & validator_pubkey,
661
+ log,
662
+ self . graffiti_file . clone ( ) ,
663
+ self . validator_store . graffiti ( & validator_pubkey) ,
664
+ self . graffiti ,
665
+ ) ;
666
+
667
+ let randao_reveal_ref = & randao_reveal;
668
+ let self_ref = & self ;
669
+ let proposer_index = self . validator_store . validator_index ( & validator_pubkey) ;
670
+ let proposer_fallback = ProposerFallback {
671
+ beacon_nodes : self . beacon_nodes . clone ( ) ,
672
+ proposer_nodes : self . proposer_nodes . clone ( ) ,
673
+ } ;
674
+
675
+ info ! (
676
+ log,
677
+ "Requesting unsigned block" ;
678
+ "slot" => slot. as_u64( ) ,
679
+ ) ;
680
+
681
+ // Request block from first responsive beacon node.
682
+ //
683
+ // Try the proposer nodes last, since it's likely that they don't have a
684
+ // great view of attestations on the network.
685
+ let unsigned_block = proposer_fallback
686
+ . request_proposers_last (
687
+ RequireSynced :: No ,
688
+ OfflineOnFailure :: Yes ,
689
+ move |beacon_node| {
690
+ Self :: get_validator_block (
691
+ beacon_node,
692
+ slot,
693
+ randao_reveal_ref,
694
+ graffiti,
695
+ proposer_index,
696
+ builder_proposal,
697
+ log,
698
+ )
699
+ } ,
700
+ )
701
+ . await ?;
702
+
703
+ self_ref
704
+ . sign_and_publish_block (
705
+ proposer_fallback,
706
+ slot,
707
+ graffiti,
708
+ & validator_pubkey,
709
+ unsigned_block,
710
+ )
711
+ . await ?;
712
+
713
+ Ok ( ( ) )
714
+ }
715
+
549
716
async fn publish_signed_block_contents (
550
717
& self ,
551
718
signed_block : & SignedBlock < E > ,
@@ -578,7 +745,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
578
745
Ok :: < _ , BlockError > ( ( ) )
579
746
}
580
747
581
- async fn get_validator_block (
748
+ async fn get_validator_block_v3 (
582
749
beacon_node : & BeaconNodeHttpClient ,
583
750
slot : Slot ,
584
751
randao_reveal_ref : & SignatureBytes ,
@@ -621,6 +788,65 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
621
788
Ok :: < _ , BlockError > ( unsigned_block)
622
789
}
623
790
791
+ async fn get_validator_block (
792
+ beacon_node : & BeaconNodeHttpClient ,
793
+ slot : Slot ,
794
+ randao_reveal_ref : & SignatureBytes ,
795
+ graffiti : Option < Graffiti > ,
796
+ proposer_index : Option < u64 > ,
797
+ builder_proposal : bool ,
798
+ log : & Logger ,
799
+ ) -> Result < UnsignedBlock < E > , BlockError > {
800
+ let unsigned_block = if !builder_proposal {
801
+ let _get_timer = metrics:: start_timer_vec (
802
+ & metrics:: BLOCK_SERVICE_TIMES ,
803
+ & [ metrics:: BEACON_BLOCK_HTTP_GET ] ,
804
+ ) ;
805
+ UnsignedBlock :: Full (
806
+ beacon_node
807
+ . get_validator_blocks :: < E > ( slot, randao_reveal_ref, graffiti. as_ref ( ) )
808
+ . await
809
+ . map_err ( |e| {
810
+ BlockError :: Recoverable ( format ! (
811
+ "Error from beacon node when producing block: {:?}" ,
812
+ e
813
+ ) )
814
+ } ) ?
815
+ . data ,
816
+ )
817
+ } else {
818
+ let _get_timer = metrics:: start_timer_vec (
819
+ & metrics:: BLOCK_SERVICE_TIMES ,
820
+ & [ metrics:: BLINDED_BEACON_BLOCK_HTTP_GET ] ,
821
+ ) ;
822
+ UnsignedBlock :: Blinded (
823
+ beacon_node
824
+ . get_validator_blinded_blocks :: < E > ( slot, randao_reveal_ref, graffiti. as_ref ( ) )
825
+ . await
826
+ . map_err ( |e| {
827
+ BlockError :: Recoverable ( format ! (
828
+ "Error from beacon node when producing block: {:?}" ,
829
+ e
830
+ ) )
831
+ } ) ?
832
+ . data ,
833
+ )
834
+ } ;
835
+
836
+ info ! (
837
+ log,
838
+ "Received unsigned block" ;
839
+ "slot" => slot. as_u64( ) ,
840
+ ) ;
841
+ if proposer_index != Some ( unsigned_block. proposer_index ( ) ) {
842
+ return Err ( BlockError :: Recoverable (
843
+ "Proposer index does not match block proposer. Beacon chain re-orged" . to_string ( ) ,
844
+ ) ) ;
845
+ }
846
+
847
+ Ok :: < _ , BlockError > ( unsigned_block)
848
+ }
849
+
624
850
/// Returns the builder boost factor of the given public key.
625
851
/// The priority order for fetching this value is:
626
852
///
0 commit comments