@@ -42,6 +42,7 @@ type node struct {
4242 ExecutionClientTTD * big.Int
4343 BeaconNodeTTD * big.Int
4444 TestVerificationNode bool
45+ DisableStartup bool
4546 ChainGenerator ChainGenerator
4647 Chain []* types.Block
4748}
@@ -562,106 +563,205 @@ func forkchoiceResponseSpoof(method string, status PayloadStatusV1, payloadID *P
562563 }, nil
563564}
564565
565- // List of Hashes that can be accessed concurrently
566- type SyncHashes struct {
567- Hashes []common.Hash
568- Lock * sync.Mutex
566+ type EngineResponse struct {
567+ Status PayloadStatus
568+ LatestValidHash * common.Hash
569569}
570570
571- func NewSyncHashes (hashes ... common.Hash ) * SyncHashes {
572- newSyncHashes := & SyncHashes {
573- Hashes : make ([]common.Hash , 0 ),
574- Lock : & sync.Mutex {},
571+ type EngineResponseHash struct {
572+ Response * EngineResponse
573+ Hash common.Hash
574+ }
575+
576+ //
577+ type EngineResponseMocker struct {
578+ Lock sync.Mutex
579+ DefaultResponse * EngineResponse
580+ HashToResponse map [common.Hash ]* EngineResponse
581+ HashPassthrough map [common.Hash ]bool
582+ NewPayloadCalled chan EngineResponseHash
583+ ForkchoiceUpdatedCalled chan EngineResponseHash
584+ Mocking bool
585+ }
586+
587+ func NewEngineResponseMocker (defaultResponse * EngineResponse , perHashResponses ... * EngineResponseHash ) * EngineResponseMocker {
588+ e := & EngineResponseMocker {
589+ DefaultResponse : defaultResponse ,
590+ HashToResponse : make (map [common.Hash ]* EngineResponse ),
591+ HashPassthrough : make (map [common.Hash ]bool ),
592+ NewPayloadCalled : make (chan EngineResponseHash ),
593+ ForkchoiceUpdatedCalled : make (chan EngineResponseHash ),
594+ Mocking : true ,
575595 }
576- for _ , h := range hashes {
577- newSyncHashes . Hashes = append ( newSyncHashes . Hashes , h )
596+ for _ , r := range perHashResponses {
597+ e . AddResponse ( r . Hash , r . Response )
578598 }
579- return newSyncHashes
599+ return e
580600}
581601
582- func (syncHashes * SyncHashes ) Contains ( hash common.Hash ) bool {
583- syncHashes .Lock .Lock ()
584- defer syncHashes .Lock .Unlock ()
585- if syncHashes . Hashes == nil {
586- return false
602+ func (e * EngineResponseMocker ) AddResponse ( h common.Hash , r * EngineResponse ) {
603+ e .Lock .Lock ()
604+ defer e .Lock .Unlock ()
605+ if e . HashToResponse == nil {
606+ e . HashToResponse = make ( map [common. Hash ] * EngineResponse )
587607 }
588- for _ , h := range syncHashes .Hashes {
589- if h == hash {
590- return true
591- }
608+ e .HashToResponse [h ] = r
609+ }
610+
611+ func (e * EngineResponseMocker ) AddPassthrough (h common.Hash , pass bool ) {
612+ e .Lock .Lock ()
613+ defer e .Lock .Unlock ()
614+ e .HashPassthrough [h ] = pass
615+ }
616+
617+ func (e * EngineResponseMocker ) CanPassthrough (h common.Hash ) bool {
618+ e .Lock .Lock ()
619+ defer e .Lock .Unlock ()
620+ if pass , ok := e .HashPassthrough [h ]; ok && pass {
621+ return true
592622 }
593623 return false
594624}
595625
596- func (syncHashes * SyncHashes ) Add (hash common.Hash ) {
597- syncHashes .Lock .Lock ()
598- defer syncHashes .Lock .Unlock ()
599- syncHashes .Hashes = append (syncHashes .Hashes , hash )
626+ func (e * EngineResponseMocker ) GetResponse (h common.Hash ) * EngineResponse {
627+ e .Lock .Lock ()
628+ defer e .Lock .Unlock ()
629+ if e .HashToResponse != nil {
630+ if r , ok := e .HashToResponse [h ]; ok {
631+ return r
632+ }
633+ }
634+ return e .DefaultResponse
600635}
601636
602- // Generate a callback that invalidates either a call to `engine_forkchoiceUpdatedV1` or `engine_newPayloadV1`
603- // for all hashes with given exceptions, and a given LatestValidHash.
604- func InvalidateExecutionPayloads (method string , exceptions * SyncHashes , latestValidHash * common.Hash , invalidated chan <- common.Hash ) func ([]byte , []byte ) * proxy.Spoof {
605- if method == EngineForkchoiceUpdatedV1 {
606- return func (res []byte , req []byte ) * proxy.Spoof {
607- var (
608- fcState ForkchoiceStateV1
609- pAttr PayloadAttributesV1
610- spoof * proxy.Spoof
611- err error
612- )
613- err = UnmarshalFromJsonRPCRequest (req , & fcState , & pAttr )
637+ func (e * EngineResponseMocker ) SetDefaultResponse (r * EngineResponse ) {
638+ e .Lock .Lock ()
639+ defer e .Lock .Unlock ()
640+ e .DefaultResponse = r
641+ }
642+
643+ func (e * EngineResponseMocker ) AddGetPayloadPassthroughToProxy (p * Proxy ) {
644+ p .AddResponseCallback (EngineGetPayloadV1 , func (res []byte , req []byte ) * proxy.Spoof {
645+ // Hash of the payload built is being added to the passthrough list
646+ var (
647+ payload ExecutableDataV1
648+ )
649+ err := UnmarshalFromJsonRPCResponse (res , & payload )
650+ if err != nil {
651+ panic (err )
652+ }
653+ e .AddPassthrough (payload .BlockHash , true )
654+ return nil
655+ })
656+ }
657+
658+ func (e * EngineResponseMocker ) AddNewPayloadCallbackToProxy (p * Proxy ) {
659+ p .AddResponseCallback (EngineNewPayloadV1 , func (res []byte , req []byte ) * proxy.Spoof {
660+ var (
661+ payload ExecutableDataV1
662+ status PayloadStatusV1
663+ spoof * proxy.Spoof
664+ err error
665+ )
666+ err = UnmarshalFromJsonRPCRequest (req , & payload )
667+ if err != nil {
668+ panic (err )
669+ }
670+ err = UnmarshalFromJsonRPCResponse (res , & status )
671+ if err != nil {
672+ panic (err )
673+ }
674+ if r := e .GetResponse (payload .BlockHash ); e .Mocking && ! e .CanPassthrough (payload .BlockHash ) && r != nil {
675+ // We are mocking this specific response, either with a hash specific response, or the default response
676+ spoof , err = payloadStatusSpoof (EngineNewPayloadV1 , & PayloadStatusV1 {
677+ Status : r .Status ,
678+ LatestValidHash : r .LatestValidHash ,
679+ ValidationError : nil ,
680+ })
614681 if err != nil {
615682 panic (err )
616683 }
617- if ! exceptions .Contains (fcState .HeadBlockHash ) {
618- spoof , err = forkchoiceResponseSpoof (EngineForkchoiceUpdatedV1 , PayloadStatusV1 {
619- Status : Invalid ,
620- LatestValidHash : latestValidHash ,
621- ValidationError : nil ,
622- }, nil )
623- if err != nil {
624- panic (err )
625- }
626- select {
627- case invalidated <- fcState .HeadBlockHash :
628- default :
629- }
630- return spoof
684+ select {
685+ case e .NewPayloadCalled <- EngineResponseHash {
686+ Response : r ,
687+ Hash : payload .BlockHash ,
688+ }:
689+ default :
690+ }
691+ return spoof
692+ } else {
693+ select {
694+ case e .NewPayloadCalled <- EngineResponseHash {
695+ Response : & EngineResponse {
696+ Status : status .Status ,
697+ LatestValidHash : status .LatestValidHash ,
698+ },
699+ Hash : payload .BlockHash ,
700+ }:
701+ default :
631702 }
632- return nil
633703 }
634- }
635- if method == EngineNewPayloadV1 {
636- return func (res []byte , req []byte ) * proxy.Spoof {
637- var (
638- payload ExecutableDataV1
639- spoof * proxy.Spoof
640- err error
641- )
642- err = UnmarshalFromJsonRPCRequest (req , & payload )
704+ return nil
705+ })
706+ }
707+
708+ func (e * EngineResponseMocker ) AddForkchoiceUpdatedCallbackToProxy (p * Proxy ) {
709+ p .AddResponseCallback (EngineForkchoiceUpdatedV1 , func (res []byte , req []byte ) * proxy.Spoof {
710+ var (
711+ fcState ForkchoiceStateV1
712+ pAttr PayloadAttributesV1
713+ fResp ForkChoiceResponse
714+ spoof * proxy.Spoof
715+ err error
716+ )
717+ err = UnmarshalFromJsonRPCRequest (req , & fcState , & pAttr )
718+ if err != nil {
719+ panic (err )
720+ }
721+ err = UnmarshalFromJsonRPCResponse (res , & fResp )
722+ if err != nil {
723+ panic (err )
724+ }
725+
726+ if r := e .GetResponse (fcState .HeadBlockHash ); e .Mocking && ! e .CanPassthrough (fcState .HeadBlockHash ) && r != nil {
727+ // We are mocking this specific response, either with a hash specific response, or the default response
728+ spoof , err = forkchoiceResponseSpoof (EngineForkchoiceUpdatedV1 , PayloadStatusV1 {
729+ Status : r .Status ,
730+ LatestValidHash : r .LatestValidHash ,
731+ ValidationError : nil ,
732+ }, nil )
643733 if err != nil {
644734 panic (err )
645735 }
646- if ! exceptions .Contains (payload .BlockHash ) {
647- spoof , err = payloadStatusSpoof (EngineNewPayloadV1 , & PayloadStatusV1 {
648- Status : Invalid ,
649- LatestValidHash : latestValidHash ,
650- ValidationError : nil ,
651- })
652- if err != nil {
653- panic (err )
654- }
655- select {
656- case invalidated <- payload .BlockHash :
657- default :
658- }
659- return spoof
736+
737+ select {
738+ case e .ForkchoiceUpdatedCalled <- EngineResponseHash {
739+ Response : r ,
740+ Hash : fcState .HeadBlockHash ,
741+ }:
742+ default :
743+ }
744+ return spoof
745+ } else {
746+ // Let the original response pass through
747+ select {
748+ case e .ForkchoiceUpdatedCalled <- EngineResponseHash {
749+ Response : & EngineResponse {
750+ Status : fResp .PayloadStatus .Status ,
751+ LatestValidHash : fResp .PayloadStatus .LatestValidHash ,
752+ },
753+ Hash : fcState .HeadBlockHash ,
754+ }:
755+ default :
660756 }
661- return nil
662757 }
663- }
664- panic (fmt .Errorf ("ERROR: Invalid method to generate callback: %s" , method ))
758+ return nil
759+ })
760+ }
761+
762+ func (e * EngineResponseMocker ) AddCallbacksToProxy (p * Proxy ) {
763+ e .AddForkchoiceUpdatedCallbackToProxy (p )
764+ e .AddNewPayloadCallbackToProxy (p )
665765}
666766
667767// Generates a callback that detects when a ForkchoiceUpdated with Payload Attributes fails.
@@ -716,14 +816,19 @@ func combine(a, b *proxy.Spoof) *proxy.Spoof {
716816 return a
717817}
718818
819+ func ContextWithSlotsTimeout (parent context.Context , t * Testnet , slots beacon.Slot ) (context.Context , context.CancelFunc ) {
820+ timeout := time .Duration (uint64 (slots )* uint64 (t .spec .SECONDS_PER_SLOT )) * time .Second
821+ return context .WithTimeout (parent , timeout )
822+ }
823+
719824// Try to approximate how much time until the merge based on current time, bellatrix fork epoch,
720825// TTD, execution clients' consensus mechanism, current total difficulty.
721826// This function is used to calculate timeouts, so it will always return a pessimistic value.
722827func SlotsUntilMerge (t * Testnet , c * Config ) beacon.Slot {
723828 l := make ([]beacon.Slot , 0 )
724829 l = append (l , SlotsUntilBellatrix (t .genesisTime , t .spec ))
725830
726- for i , e := range t .eth1 {
831+ for i , e := range t .ExecutionClients (). Running () {
727832 l = append (l , beacon .Slot (TimeUntilTerminalBlock (e , c .Eth1Consensus , c .TerminalTotalDifficulty , c .Nodes [i ])/ uint64 (t .spec .SECONDS_PER_SLOT )))
728833 }
729834
@@ -755,7 +860,7 @@ func SlotsUntilBellatrix(genesisTime beacon.Timestamp, spec *beacon.Spec) beacon
755860 return s
756861}
757862
758- func TimeUntilTerminalBlock (e * Eth1Node , c setup.Eth1Consensus , defaultTTD * big.Int , n node ) uint64 {
863+ func TimeUntilTerminalBlock (e * ExecutionClient , c setup.Eth1Consensus , defaultTTD * big.Int , n node ) uint64 {
759864 var ttd = defaultTTD
760865 if n .ExecutionClientTTD != nil {
761866 ttd = n .ExecutionClientTTD
0 commit comments