github.com/ethereum/go-ethereum@v1.16.1/beacon/blsync/block_sync_test.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package blsync
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/ethereum/go-ethereum/beacon/light/request"
    23  	"github.com/ethereum/go-ethereum/beacon/light/sync"
    24  	"github.com/ethereum/go-ethereum/beacon/types"
    25  	"github.com/ethereum/go-ethereum/common"
    26  	zrntcommon "github.com/protolambda/zrnt/eth2/beacon/common"
    27  	"github.com/protolambda/zrnt/eth2/beacon/deneb"
    28  )
    29  
    30  var (
    31  	testServer1 = testServer("testServer1")
    32  	testServer2 = testServer("testServer2")
    33  
    34  	testBlock1 = types.NewBeaconBlock(&deneb.BeaconBlock{
    35  		Slot: 123,
    36  		Body: deneb.BeaconBlockBody{
    37  			ExecutionPayload: deneb.ExecutionPayload{
    38  				BlockNumber: 456,
    39  				BlockHash:   zrntcommon.Hash32(common.HexToHash("905ac721c4058d9ed40b27b6b9c1bdd10d4333e4f3d9769100bf9dfb80e5d1f6")),
    40  			},
    41  		},
    42  	})
    43  	testBlock2 = types.NewBeaconBlock(&deneb.BeaconBlock{
    44  		Slot: 124,
    45  		Body: deneb.BeaconBlockBody{
    46  			ExecutionPayload: deneb.ExecutionPayload{
    47  				BlockNumber: 457,
    48  				BlockHash:   zrntcommon.Hash32(common.HexToHash("011703f39c664efc1c6cf5f49ca09b595581eec572d4dfddd3d6179a9e63e655")),
    49  			},
    50  		},
    51  	})
    52  )
    53  
    54  type testServer string
    55  
    56  func (t testServer) Name() string {
    57  	return string(t)
    58  }
    59  
    60  func TestBlockSync(t *testing.T) {
    61  	ht := &testHeadTracker{}
    62  	blockSync := newBeaconBlockSync(ht)
    63  	headCh := make(chan types.ChainHeadEvent, 16)
    64  	blockSync.SubscribeChainHead(headCh)
    65  	ts := sync.NewTestScheduler(t, blockSync)
    66  	ts.AddServer(testServer1, 1)
    67  	ts.AddServer(testServer2, 1)
    68  
    69  	expHeadBlock := func(expHead *types.BeaconBlock) {
    70  		t.Helper()
    71  		var expNumber, headNumber uint64
    72  		if expHead != nil {
    73  			p, err := expHead.ExecutionPayload()
    74  			if err != nil {
    75  				t.Fatalf("expHead.ExecutionPayload() failed: %v", err)
    76  			}
    77  			expNumber = p.NumberU64()
    78  		}
    79  		select {
    80  		case event := <-headCh:
    81  			headNumber = event.Block.NumberU64()
    82  		default:
    83  		}
    84  		if headNumber != expNumber {
    85  			t.Errorf("Wrong head block, expected block number %d, got %d)", expNumber, headNumber)
    86  		}
    87  	}
    88  
    89  	// no block requests expected until head tracker knows about a head
    90  	ts.Run(1)
    91  	expHeadBlock(nil)
    92  
    93  	// set block 1 as prefetch head, announced by server 2
    94  	head1 := blockHeadInfo(testBlock1)
    95  	ht.prefetch = head1
    96  	ts.ServerEvent(sync.EvNewHead, testServer2, head1)
    97  
    98  	// expect request to server 2 which has announced the head
    99  	ts.Run(2, testServer2, sync.ReqBeaconBlock(head1.BlockRoot))
   100  
   101  	// valid response
   102  	ts.RequestEvent(request.EvResponse, ts.Request(2, 1), testBlock1)
   103  	ts.AddAllowance(testServer2, 1)
   104  	ts.Run(3)
   105  	// head block still not expected as the fetched block is not the validated head yet
   106  	expHeadBlock(nil)
   107  
   108  	// set as validated head, expect no further requests but block 1 set as head block
   109  	ht.validated.Header = testBlock1.Header()
   110  	ts.Run(4)
   111  	expHeadBlock(testBlock1)
   112  
   113  	// set block 2 as prefetch head, announced by server 1
   114  	head2 := blockHeadInfo(testBlock2)
   115  	ht.prefetch = head2
   116  	ts.ServerEvent(sync.EvNewHead, testServer1, head2)
   117  	// expect request to server 1
   118  	ts.Run(5, testServer1, sync.ReqBeaconBlock(head2.BlockRoot))
   119  
   120  	// req2 fails, no further requests expected because server 2 has not announced it
   121  	ts.RequestEvent(request.EvFail, ts.Request(5, 1), nil)
   122  	ts.Run(6)
   123  
   124  	// set as validated head before retrieving block; now it's assumed to be available from server 2 too
   125  	ht.validated.Header = testBlock2.Header()
   126  	// expect req2 retry to server 2
   127  	ts.Run(7, testServer2, sync.ReqBeaconBlock(head2.BlockRoot))
   128  	// now head block should be unavailable again
   129  	expHeadBlock(nil)
   130  
   131  	// valid response, now head block should be block 2 immediately as it is already validated
   132  	ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testBlock2)
   133  	ts.Run(8)
   134  	expHeadBlock(testBlock2)
   135  }
   136  
   137  type testHeadTracker struct {
   138  	prefetch  types.HeadInfo
   139  	validated types.SignedHeader
   140  }
   141  
   142  func (h *testHeadTracker) PrefetchHead() types.HeadInfo {
   143  	return h.prefetch
   144  }
   145  
   146  func (h *testHeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) {
   147  	return types.OptimisticUpdate{
   148  		Attested:      types.HeaderWithExecProof{Header: h.validated.Header},
   149  		Signature:     h.validated.Signature,
   150  		SignatureSlot: h.validated.SignatureSlot,
   151  	}, h.validated.Header != (types.Header{})
   152  }
   153  
   154  // TODO add test case for finality
   155  func (h *testHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
   156  	finalized := types.NewExecutionHeader(new(deneb.ExecutionPayloadHeader))
   157  	return types.FinalityUpdate{
   158  		Attested:      types.HeaderWithExecProof{Header: h.validated.Header},
   159  		Finalized:     types.HeaderWithExecProof{PayloadHeader: finalized},
   160  		Signature:     h.validated.Signature,
   161  		SignatureSlot: h.validated.SignatureSlot,
   162  	}, h.validated.Header != (types.Header{})
   163  }