77 "fmt"
88 "io"
99 "net/http"
10+ "runtime/debug"
1011 "strconv"
1112 "time"
1213
@@ -20,7 +21,8 @@ import (
2021 "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/operation"
2122 statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
2223 "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"
2426 "github.com/prysmaticlabs/prysm/v5/config/params"
2527 "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
2628 payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
@@ -352,9 +354,18 @@ func writeLazyReaderWithRecover(w *streamingResponseWriterController, lr lazyRea
352354 if r := recover (); r != nil {
353355 log .WithField ("panic" , r ).Error ("Recovered from panic while writing event to client." )
354356 err = errWriterUnusable
357+ debug .PrintStack ()
355358 }
356359 }()
360+ if lr == nil {
361+ log .Warn ("Event stream skipping a nil lazy event reader callback" )
362+ return nil
363+ }
357364 r := lr ()
365+ if r == nil {
366+ log .Warn ("Event stream skipping a nil event reader" )
367+ return nil
368+ }
358369 out , err := io .ReadAll (r )
359370 if err != nil {
360371 return err
@@ -600,62 +611,45 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi
600611
601612var errUnsupportedPayloadAttribute = errors .New ("cannot compute payload attributes pre-Bellatrix" )
602613
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 ()
605616 if v < version .Bellatrix {
606617 return nil , errors .Wrapf (errUnsupportedPayloadAttribute , "%s is not supported" , version .String (v ))
607618 }
608619
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- }
622620 feeRecpt := params .BeaconConfig ().DefaultFeeRecipient .Bytes ()
623- tValidator , exists := s .TrackedValidatorsCache .Validator (proposerIndex )
621+ tValidator , exists := s .TrackedValidatorsCache .Validator (proposer )
624622 if exists {
625623 feeRecpt = tValidator .FeeRecipient [:]
626624 }
627625
628626 if v == version .Bellatrix {
629627 return payloadattribute .New (& engine.PayloadAttributes {
630628 Timestamp : timestamp ,
631- PrevRandao : prevRando ,
629+ PrevRandao : randao ,
632630 SuggestedFeeRecipient : feeRecpt ,
633631 })
634632 }
635633
636- w , _ , err := ev . HeadState .ExpectedWithdrawals ()
634+ w , _ , err := st .ExpectedWithdrawals ()
637635 if err != nil {
638636 return nil , errors .Wrap (err , "could not get withdrawals from head state" )
639637 }
640638 if v == version .Capella {
641639 return payloadattribute .New (& engine.PayloadAttributesV2 {
642640 Timestamp : timestamp ,
643- PrevRandao : prevRando ,
641+ PrevRandao : randao ,
644642 SuggestedFeeRecipient : feeRecpt ,
645643 Withdrawals : w ,
646644 })
647645 }
648646
649- pr , err := ev .HeadBlock .Block ().HashTreeRoot ()
650- if err != nil {
651- return nil , errors .Wrap (err , "could not compute head block root" )
652- }
653647 return payloadattribute .New (& engine.PayloadAttributesV3 {
654648 Timestamp : timestamp ,
655- PrevRandao : prevRando ,
649+ PrevRandao : randao ,
656650 SuggestedFeeRecipient : feeRecpt ,
657651 Withdrawals : w ,
658- ParentBeaconBlockRoot : pr [:],
652+ ParentBeaconBlockRoot : root [:],
659653 })
660654}
661655
@@ -665,37 +659,75 @@ type asyncPayloadAttrData struct {
665659 err error
666660}
667661
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+
668673func (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
688678 }
689679
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 )
693705 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 " )
695707 }
696- ev .Attributer = attr
697708 }
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
699731}
700732
701733// 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
704736 ctx , cancel := context .WithTimeout (ctx , payloadAttributeTimeout )
705737 edc := make (chan asyncPayloadAttrData )
706738 go func () {
707- d := asyncPayloadAttrData {
708- version : version .String (ev .HeadState .Version ()),
709- }
710-
739+ d := asyncPayloadAttrData {}
711740 defer func () {
712741 edc <- d
713742 }()
@@ -716,6 +745,7 @@ func (s *Server) payloadAttributesReader(ctx context.Context, ev payloadattribut
716745 d .err = errors .Wrap (err , "Could not fill event data" )
717746 return
718747 }
748+ d .version = version .String (ev .HeadBlock .Version ())
719749 attributesBytes , err := marshalAttributes (ev .Attributer )
720750 if err != nil {
721751 d .err = errors .Wrap (err , "errors marshaling payload attributes to json" )
0 commit comments