7
7
"fmt"
8
8
"io"
9
9
"net/http"
10
+ "runtime/debug"
10
11
"strconv"
11
12
"time"
12
13
@@ -20,7 +21,8 @@ import (
20
21
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/operation"
21
22
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
22
23
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
23
- chaintime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
24
+ "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
25
+ "github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
24
26
"github.com/prysmaticlabs/prysm/v5/config/params"
25
27
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
26
28
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
@@ -352,9 +354,18 @@ func writeLazyReaderWithRecover(w *streamingResponseWriterController, lr lazyRea
352
354
if r := recover (); r != nil {
353
355
log .WithField ("panic" , r ).Error ("Recovered from panic while writing event to client." )
354
356
err = errWriterUnusable
357
+ debug .PrintStack ()
355
358
}
356
359
}()
360
+ if lr == nil {
361
+ log .Warn ("Event stream skipping a nil lazy event reader callback" )
362
+ return nil
363
+ }
357
364
r := lr ()
365
+ if r == nil {
366
+ log .Warn ("Event stream skipping a nil event reader" )
367
+ return nil
368
+ }
358
369
out , err := io .ReadAll (r )
359
370
if err != nil {
360
371
return err
@@ -600,62 +611,45 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi
600
611
601
612
var errUnsupportedPayloadAttribute = errors .New ("cannot compute payload attributes pre-Bellatrix" )
602
613
603
- func (s * Server ) computePayloadAttributes (ctx context.Context , ev payloadattribute. EventData ) (payloadattribute.Attributer , error ) {
604
- v := ev . HeadState .Version ()
614
+ func (s * Server ) computePayloadAttributes (ctx context.Context , st state. ReadOnlyBeaconState , root [ 32 ] byte , proposer primitives. ValidatorIndex , timestamp uint64 , randao [] byte ) (payloadattribute.Attributer , error ) {
615
+ v := st .Version ()
605
616
if v < version .Bellatrix {
606
617
return nil , errors .Wrapf (errUnsupportedPayloadAttribute , "%s is not supported" , version .String (v ))
607
618
}
608
619
609
- t , err := slots .ToTime (ev .HeadState .GenesisTime (), ev .HeadState .Slot ())
610
- if err != nil {
611
- return nil , errors .Wrap (err , "could not get head state slot time" )
612
- }
613
- timestamp := uint64 (t .Unix ())
614
- prevRando , err := helpers .RandaoMix (ev .HeadState , chaintime .CurrentEpoch (ev .HeadState ))
615
- if err != nil {
616
- return nil , errors .Wrap (err , "could not get head state randao mix" )
617
- }
618
- proposerIndex , err := helpers .BeaconProposerIndex (ctx , ev .HeadState )
619
- if err != nil {
620
- return nil , errors .Wrap (err , "could not get head state proposer index" )
621
- }
622
620
feeRecpt := params .BeaconConfig ().DefaultFeeRecipient .Bytes ()
623
- tValidator , exists := s .TrackedValidatorsCache .Validator (proposerIndex )
621
+ tValidator , exists := s .TrackedValidatorsCache .Validator (proposer )
624
622
if exists {
625
623
feeRecpt = tValidator .FeeRecipient [:]
626
624
}
627
625
628
626
if v == version .Bellatrix {
629
627
return payloadattribute .New (& engine.PayloadAttributes {
630
628
Timestamp : timestamp ,
631
- PrevRandao : prevRando ,
629
+ PrevRandao : randao ,
632
630
SuggestedFeeRecipient : feeRecpt ,
633
631
})
634
632
}
635
633
636
- w , _ , err := ev . HeadState .ExpectedWithdrawals ()
634
+ w , _ , err := st .ExpectedWithdrawals ()
637
635
if err != nil {
638
636
return nil , errors .Wrap (err , "could not get withdrawals from head state" )
639
637
}
640
638
if v == version .Capella {
641
639
return payloadattribute .New (& engine.PayloadAttributesV2 {
642
640
Timestamp : timestamp ,
643
- PrevRandao : prevRando ,
641
+ PrevRandao : randao ,
644
642
SuggestedFeeRecipient : feeRecpt ,
645
643
Withdrawals : w ,
646
644
})
647
645
}
648
646
649
- pr , err := ev .HeadBlock .Block ().HashTreeRoot ()
650
- if err != nil {
651
- return nil , errors .Wrap (err , "could not compute head block root" )
652
- }
653
647
return payloadattribute .New (& engine.PayloadAttributesV3 {
654
648
Timestamp : timestamp ,
655
- PrevRandao : prevRando ,
649
+ PrevRandao : randao ,
656
650
SuggestedFeeRecipient : feeRecpt ,
657
651
Withdrawals : w ,
658
- ParentBeaconBlockRoot : pr [:],
652
+ ParentBeaconBlockRoot : root [:],
659
653
})
660
654
}
661
655
@@ -665,37 +659,75 @@ type asyncPayloadAttrData struct {
665
659
err error
666
660
}
667
661
662
+ var zeroRoot [32 ]byte
663
+
664
+ // needsFill allows tests to provide filled EventData values. An ordinary event data value fired by the blockchain package will have
665
+ // all of the checked fields empty, so the logical short circuit should hit immediately.
666
+ func needsFill (ev payloadattribute.EventData ) bool {
667
+ return ev .HeadState == nil || ev .HeadState .IsNil () || ev .HeadState .LatestBlockHeader () == nil ||
668
+ ev .HeadBlock == nil || ev .HeadBlock .IsNil () ||
669
+ ev .HeadRoot == zeroRoot || len (ev .ParentBlockRoot ) == 0 || len (ev .ParentBlockHash ) == 0 ||
670
+ ev .Attributer == nil || ev .Attributer .IsEmpty ()
671
+ }
672
+
668
673
func (s * Server ) fillEventData (ctx context.Context , ev payloadattribute.EventData ) (payloadattribute.EventData , error ) {
669
- if ev .HeadBlock == nil || ev .HeadBlock .IsNil () {
670
- hb , err := s .HeadFetcher .HeadBlock (ctx )
671
- if err != nil {
672
- return ev , errors .Wrap (err , "Could not look up head block" )
673
- }
674
- root , err := hb .Block ().HashTreeRoot ()
675
- if err != nil {
676
- return ev , errors .Wrap (err , "Could not compute head block root" )
677
- }
678
- if ev .HeadRoot != root {
679
- return ev , errors .Wrap (err , "head root changed before payload attribute event handler execution" )
680
- }
681
- ev .HeadBlock = hb
682
- payload , err := hb .Block ().Body ().Execution ()
683
- if err != nil {
684
- return ev , errors .Wrap (err , "Could not get execution payload for head block" )
685
- }
686
- ev .ParentBlockHash = payload .BlockHash ()
687
- ev .ParentBlockNumber = payload .BlockNumber ()
674
+ var err error
675
+
676
+ if ! needsFill (ev ) {
677
+ return ev , nil
688
678
}
689
679
690
- attr := ev .Attributer
691
- if attr == nil || attr .IsEmpty () {
692
- attr , err := s .computePayloadAttributes (ctx , ev )
680
+ ev .HeadState , err = s .HeadFetcher .HeadState (ctx )
681
+ if err != nil {
682
+ return ev , errors .Wrap (err , "could not get head state" )
683
+ }
684
+
685
+ ev .HeadBlock , err = s .HeadFetcher .HeadBlock (ctx )
686
+ if err != nil {
687
+ return ev , errors .Wrap (err , "could not look up head block" )
688
+ }
689
+ ev .HeadRoot , err = ev .HeadBlock .Block ().HashTreeRoot ()
690
+ if err != nil {
691
+ return ev , errors .Wrap (err , "could not compute head block root" )
692
+ }
693
+ pr := ev .HeadBlock .Block ().ParentRoot ()
694
+ ev .ParentBlockRoot = pr [:]
695
+
696
+ hsr , err := ev .HeadState .LatestBlockHeader ().HashTreeRoot ()
697
+ if err != nil {
698
+ return ev , errors .Wrap (err , "could not compute latest block header root" )
699
+ }
700
+
701
+ pse := slots .ToEpoch (ev .ProposalSlot )
702
+ st := ev .HeadState
703
+ if slots .ToEpoch (st .Slot ()) != pse {
704
+ st , err = transition .ProcessSlotsUsingNextSlotCache (ctx , st , hsr [:], ev .ProposalSlot )
693
705
if err != nil {
694
- return ev , errors .Wrap (err , "Could not compute payload attributes " )
706
+ return ev , errors .Wrap (err , "could not run process blocks on head state into the proposal slot epoch " )
695
707
}
696
- ev .Attributer = attr
697
708
}
698
- return ev , nil
709
+ ev .ProposerIndex , err = helpers .BeaconProposerIndexAtSlot (ctx , st , ev .ProposalSlot )
710
+ if err != nil {
711
+ return ev , errors .Wrap (err , "failed to compute proposer index" )
712
+ }
713
+ randao , err := helpers .RandaoMix (st , pse )
714
+ if err != nil {
715
+ return ev , errors .Wrap (err , "could not get head state randado" )
716
+ }
717
+
718
+ payload , err := ev .HeadBlock .Block ().Body ().Execution ()
719
+ if err != nil {
720
+ return ev , errors .Wrap (err , "could not get execution payload for head block" )
721
+ }
722
+ ev .ParentBlockHash = payload .BlockHash ()
723
+ ev .ParentBlockNumber = payload .BlockNumber ()
724
+
725
+ t , err := slots .ToTime (st .GenesisTime (), ev .ProposalSlot )
726
+ if err != nil {
727
+ return ev , errors .Wrap (err , "could not get head state slot time" )
728
+ }
729
+ ev .Attributer , err = s .computePayloadAttributes (ctx , st , hsr , ev .ProposerIndex , uint64 (t .Unix ()), randao )
730
+ return ev , err
699
731
}
700
732
701
733
// This event stream is intended to be used by builders and relays.
@@ -704,10 +736,7 @@ func (s *Server) payloadAttributesReader(ctx context.Context, ev payloadattribut
704
736
ctx , cancel := context .WithTimeout (ctx , payloadAttributeTimeout )
705
737
edc := make (chan asyncPayloadAttrData )
706
738
go func () {
707
- d := asyncPayloadAttrData {
708
- version : version .String (ev .HeadState .Version ()),
709
- }
710
-
739
+ d := asyncPayloadAttrData {}
711
740
defer func () {
712
741
edc <- d
713
742
}()
@@ -716,6 +745,7 @@ func (s *Server) payloadAttributesReader(ctx context.Context, ev payloadattribut
716
745
d .err = errors .Wrap (err , "Could not fill event data" )
717
746
return
718
747
}
748
+ d .version = version .String (ev .HeadBlock .Version ())
719
749
attributesBytes , err := marshalAttributes (ev .Attributer )
720
750
if err != nil {
721
751
d .err = errors .Wrap (err , "errors marshaling payload attributes to json" )
0 commit comments