github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/sync/update_sync_test.go (about)

     1  // Copyright 2024 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 sync
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/ethereum/go-ethereum/beacon/light/request"
    23  	"github.com/ethereum/go-ethereum/beacon/types"
    24  )
    25  
    26  func TestCheckpointInit(t *testing.T) {
    27  	chain := &TestCommitteeChain{}
    28  	checkpoint := &types.BootstrapData{Header: types.Header{Slot: 0x2000*4 + 0x1000}} // period 4
    29  	checkpointHash := checkpoint.Header.Hash()
    30  	chkInit := NewCheckpointInit(chain, checkpointHash)
    31  	ts := NewTestScheduler(t, chkInit)
    32  	// add 2 servers
    33  	ts.AddServer(testServer1, 1)
    34  	ts.AddServer(testServer2, 1)
    35  
    36  	// expect bootstrap request to server 1
    37  	ts.Run(1, testServer1, ReqCheckpointData(checkpointHash))
    38  
    39  	// server 1 times out; expect request to server 2
    40  	ts.RequestEvent(request.EvTimeout, ts.Request(1, 1), nil)
    41  	ts.Run(2, testServer2, ReqCheckpointData(checkpointHash))
    42  
    43  	// invalid response from server 2; expect init state to still be false
    44  	ts.RequestEvent(request.EvResponse, ts.Request(2, 1), &types.BootstrapData{Header: types.Header{Slot: 123456}})
    45  	ts.ExpFail(testServer2)
    46  	ts.Run(3)
    47  	chain.ExpInit(t, false)
    48  
    49  	// server 1 fails (hard timeout)
    50  	ts.RequestEvent(request.EvFail, ts.Request(1, 1), nil)
    51  	ts.Run(4)
    52  	chain.ExpInit(t, false)
    53  
    54  	// server 3 is registered; expect bootstrap request to server 3
    55  	ts.AddServer(testServer3, 1)
    56  	ts.Run(5, testServer3, ReqCheckpointData(checkpointHash))
    57  
    58  	// valid response from server 3; expect chain to be initialized
    59  	ts.RequestEvent(request.EvResponse, ts.Request(5, 1), checkpoint)
    60  	ts.Run(6)
    61  	chain.ExpInit(t, true)
    62  }
    63  
    64  func TestUpdateSyncParallel(t *testing.T) {
    65  	chain := &TestCommitteeChain{}
    66  	chain.SetNextSyncPeriod(0)
    67  	updateSync := NewForwardUpdateSync(chain)
    68  	ts := NewTestScheduler(t, updateSync)
    69  	// add 2 servers, head at period 100; allow 3-3 parallel requests for each
    70  	ts.AddServer(testServer1, 3)
    71  	ts.ServerEvent(EvNewOptimisticUpdate, testServer1, types.OptimisticUpdate{SignatureSlot: 0x2000*100 + 0x1000})
    72  	ts.AddServer(testServer2, 3)
    73  	ts.ServerEvent(EvNewOptimisticUpdate, testServer2, types.OptimisticUpdate{SignatureSlot: 0x2000*100 + 0x1000})
    74  
    75  	// expect 6 requests to be sent
    76  	ts.Run(1,
    77  		testServer1, ReqUpdates{FirstPeriod: 0, Count: 8},
    78  		testServer1, ReqUpdates{FirstPeriod: 8, Count: 8},
    79  		testServer1, ReqUpdates{FirstPeriod: 16, Count: 8},
    80  		testServer2, ReqUpdates{FirstPeriod: 24, Count: 8},
    81  		testServer2, ReqUpdates{FirstPeriod: 32, Count: 8},
    82  		testServer2, ReqUpdates{FirstPeriod: 40, Count: 8})
    83  
    84  	// valid response to request 1; expect 8 periods synced and a new request started
    85  	ts.RequestEvent(request.EvResponse, ts.Request(1, 1), testRespUpdate(ts.Request(1, 1)))
    86  	ts.AddAllowance(testServer1, 1)
    87  	ts.Run(2, testServer1, ReqUpdates{FirstPeriod: 48, Count: 8})
    88  	chain.ExpNextSyncPeriod(t, 8)
    89  
    90  	// valid response to requests 4 and 5
    91  	ts.RequestEvent(request.EvResponse, ts.Request(1, 4), testRespUpdate(ts.Request(1, 4)))
    92  	ts.RequestEvent(request.EvResponse, ts.Request(1, 5), testRespUpdate(ts.Request(1, 5)))
    93  	ts.AddAllowance(testServer2, 2)
    94  	// expect 2 more requests but no sync progress (responses 4 and 5 cannot be added before 2 and 3)
    95  	ts.Run(3,
    96  		testServer2, ReqUpdates{FirstPeriod: 56, Count: 8},
    97  		testServer2, ReqUpdates{FirstPeriod: 64, Count: 8})
    98  	chain.ExpNextSyncPeriod(t, 8)
    99  
   100  	// soft timeout for requests 2 and 3 (server 1 is overloaded)
   101  	ts.RequestEvent(request.EvTimeout, ts.Request(1, 2), nil)
   102  	ts.RequestEvent(request.EvTimeout, ts.Request(1, 3), nil)
   103  	// no allowance, no more requests
   104  	ts.Run(4)
   105  
   106  	// valid response to requests 6 and 8 and 9
   107  	ts.RequestEvent(request.EvResponse, ts.Request(1, 6), testRespUpdate(ts.Request(1, 6)))
   108  	ts.RequestEvent(request.EvResponse, ts.Request(3, 1), testRespUpdate(ts.Request(3, 1)))
   109  	ts.RequestEvent(request.EvResponse, ts.Request(3, 2), testRespUpdate(ts.Request(3, 2)))
   110  	ts.AddAllowance(testServer2, 3)
   111  	// server 2 can now resend requests 2 and 3 (timed out by server 1) and also send a new one
   112  	ts.Run(5,
   113  		testServer2, ReqUpdates{FirstPeriod: 8, Count: 8},
   114  		testServer2, ReqUpdates{FirstPeriod: 16, Count: 8},
   115  		testServer2, ReqUpdates{FirstPeriod: 72, Count: 8})
   116  
   117  	// server 1 finally answers timed out request 2
   118  	ts.RequestEvent(request.EvResponse, ts.Request(1, 2), testRespUpdate(ts.Request(1, 2)))
   119  	ts.AddAllowance(testServer1, 1)
   120  	// expect sync progress and one new request
   121  	ts.Run(6, testServer1, ReqUpdates{FirstPeriod: 80, Count: 8})
   122  	chain.ExpNextSyncPeriod(t, 16)
   123  
   124  	// server 2 answers requests 11 and 12 (resends of requests 2 and 3)
   125  	ts.RequestEvent(request.EvResponse, ts.Request(5, 1), testRespUpdate(ts.Request(5, 1)))
   126  	ts.RequestEvent(request.EvResponse, ts.Request(5, 2), testRespUpdate(ts.Request(5, 2)))
   127  	ts.AddAllowance(testServer2, 2)
   128  	ts.Run(7,
   129  		testServer2, ReqUpdates{FirstPeriod: 88, Count: 8},
   130  		testServer2, ReqUpdates{FirstPeriod: 96, Count: 4})
   131  	// finally the gap is filled, update can process responses up to req6
   132  	chain.ExpNextSyncPeriod(t, 48)
   133  
   134  	// all remaining requests are answered
   135  	ts.RequestEvent(request.EvResponse, ts.Request(1, 3), testRespUpdate(ts.Request(1, 3)))
   136  	ts.RequestEvent(request.EvResponse, ts.Request(2, 1), testRespUpdate(ts.Request(2, 1)))
   137  	ts.RequestEvent(request.EvResponse, ts.Request(5, 3), testRespUpdate(ts.Request(5, 3)))
   138  	ts.RequestEvent(request.EvResponse, ts.Request(6, 1), testRespUpdate(ts.Request(6, 1)))
   139  	ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testRespUpdate(ts.Request(7, 1)))
   140  	ts.RequestEvent(request.EvResponse, ts.Request(7, 2), testRespUpdate(ts.Request(7, 2)))
   141  	ts.Run(8)
   142  	// expect chain to be fully synced
   143  	chain.ExpNextSyncPeriod(t, 100)
   144  }
   145  
   146  func TestUpdateSyncDifferentHeads(t *testing.T) {
   147  	chain := &TestCommitteeChain{}
   148  	chain.SetNextSyncPeriod(10)
   149  	updateSync := NewForwardUpdateSync(chain)
   150  	ts := NewTestScheduler(t, updateSync)
   151  	// add 3 servers with different announced head periods
   152  	ts.AddServer(testServer1, 1)
   153  	ts.ServerEvent(EvNewOptimisticUpdate, testServer1, types.OptimisticUpdate{SignatureSlot: 0x2000*15 + 0x1000})
   154  	ts.AddServer(testServer2, 1)
   155  	ts.ServerEvent(EvNewOptimisticUpdate, testServer2, types.OptimisticUpdate{SignatureSlot: 0x2000*16 + 0x1000})
   156  	ts.AddServer(testServer3, 1)
   157  	ts.ServerEvent(EvNewOptimisticUpdate, testServer3, types.OptimisticUpdate{SignatureSlot: 0x2000*17 + 0x1000})
   158  
   159  	// expect request to the best announced head
   160  	ts.Run(1, testServer3, ReqUpdates{FirstPeriod: 10, Count: 7})
   161  
   162  	// request times out, expect request to the next best head
   163  	ts.RequestEvent(request.EvTimeout, ts.Request(1, 1), nil)
   164  	ts.Run(2, testServer2, ReqUpdates{FirstPeriod: 10, Count: 6})
   165  
   166  	// request times out, expect request to the last available server
   167  	ts.RequestEvent(request.EvTimeout, ts.Request(2, 1), nil)
   168  	ts.Run(3, testServer1, ReqUpdates{FirstPeriod: 10, Count: 5})
   169  
   170  	// valid response to request 3, expect chain synced to period 15
   171  	ts.RequestEvent(request.EvResponse, ts.Request(3, 1), testRespUpdate(ts.Request(3, 1)))
   172  	ts.AddAllowance(testServer1, 1)
   173  	ts.Run(4)
   174  	chain.ExpNextSyncPeriod(t, 15)
   175  
   176  	// invalid response to request 1, server can only deliver updates up to period 15 despite announced head
   177  	truncated := ts.Request(1, 1)
   178  	truncated.request = ReqUpdates{FirstPeriod: 10, Count: 5}
   179  	ts.RequestEvent(request.EvResponse, ts.Request(1, 1), testRespUpdate(truncated))
   180  	ts.ExpFail(testServer3)
   181  	ts.Run(5)
   182  	// expect no progress of chain head
   183  	chain.ExpNextSyncPeriod(t, 15)
   184  
   185  	// valid response to request 2, expect chain synced to period 16
   186  	ts.RequestEvent(request.EvResponse, ts.Request(2, 1), testRespUpdate(ts.Request(2, 1)))
   187  	ts.AddAllowance(testServer2, 1)
   188  	ts.Run(6)
   189  	chain.ExpNextSyncPeriod(t, 16)
   190  
   191  	// a new server is registered with announced head period 17
   192  	ts.AddServer(testServer4, 1)
   193  	ts.ServerEvent(EvNewOptimisticUpdate, testServer4, types.OptimisticUpdate{SignatureSlot: 0x2000*17 + 0x1000})
   194  	// expect request to sync one more period
   195  	ts.Run(7, testServer4, ReqUpdates{FirstPeriod: 16, Count: 1})
   196  
   197  	// valid response, expect chain synced to period 17
   198  	ts.RequestEvent(request.EvResponse, ts.Request(7, 1), testRespUpdate(ts.Request(7, 1)))
   199  	ts.AddAllowance(testServer4, 1)
   200  	ts.Run(8)
   201  	chain.ExpNextSyncPeriod(t, 17)
   202  }
   203  
   204  func testRespUpdate(request requestWithID) request.Response {
   205  	var resp RespUpdates
   206  	if request.request == nil {
   207  		return resp
   208  	}
   209  	req := request.request.(ReqUpdates)
   210  	resp.Updates = make([]*types.LightClientUpdate, int(req.Count))
   211  	resp.Committees = make([]*types.SerializedSyncCommittee, int(req.Count))
   212  	period := req.FirstPeriod
   213  	for i := range resp.Updates {
   214  		resp.Updates[i] = &types.LightClientUpdate{AttestedHeader: types.SignedHeader{Header: types.Header{Slot: 0x2000*period + 0x1000}}}
   215  		resp.Committees[i] = new(types.SerializedSyncCommittee)
   216  		period++
   217  	}
   218  	return resp
   219  }