github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/sync/test_helpers.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 sync
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	"github.com/ethereum/go-ethereum/beacon/light"
    24  	"github.com/ethereum/go-ethereum/beacon/light/request"
    25  	"github.com/ethereum/go-ethereum/beacon/types"
    26  )
    27  
    28  type requestWithID struct {
    29  	sid     request.ServerAndID
    30  	request request.Request
    31  }
    32  
    33  type TestScheduler struct {
    34  	t         *testing.T
    35  	module    request.Module
    36  	events    []request.Event
    37  	servers   []request.Server
    38  	allowance map[request.Server]int
    39  	sent      map[int][]requestWithID
    40  	testIndex int
    41  	expFail   map[request.Server]int // expected Server.Fail calls during next Run
    42  	lastId    request.ID
    43  }
    44  
    45  func NewTestScheduler(t *testing.T, module request.Module) *TestScheduler {
    46  	return &TestScheduler{
    47  		t:         t,
    48  		module:    module,
    49  		allowance: make(map[request.Server]int),
    50  		expFail:   make(map[request.Server]int),
    51  		sent:      make(map[int][]requestWithID),
    52  	}
    53  }
    54  
    55  func (ts *TestScheduler) Run(testIndex int, exp ...any) {
    56  	expReqs := make([]requestWithID, len(exp)/2)
    57  	id := ts.lastId
    58  	for i := range expReqs {
    59  		id++
    60  		expReqs[i] = requestWithID{
    61  			sid:     request.ServerAndID{Server: exp[i*2].(request.Server), ID: id},
    62  			request: exp[i*2+1].(request.Request),
    63  		}
    64  	}
    65  	if len(expReqs) == 0 {
    66  		expReqs = nil
    67  	}
    68  
    69  	ts.testIndex = testIndex
    70  	ts.module.Process(ts, ts.events)
    71  	ts.events = nil
    72  
    73  	for server, count := range ts.expFail {
    74  		delete(ts.expFail, server)
    75  		if count == 0 {
    76  			continue
    77  		}
    78  		ts.t.Errorf("Missing %d Server.Fail(s) from server %s in test case #%d", count, server.Name(), testIndex)
    79  	}
    80  
    81  	if !reflect.DeepEqual(ts.sent[testIndex], expReqs) {
    82  		ts.t.Errorf("Wrong sent requests in test case #%d (expected %v, got %v)", testIndex, expReqs, ts.sent[testIndex])
    83  	}
    84  }
    85  
    86  func (ts *TestScheduler) CanSendTo() (cs []request.Server) {
    87  	for _, server := range ts.servers {
    88  		if ts.allowance[server] > 0 {
    89  			cs = append(cs, server)
    90  		}
    91  	}
    92  	return
    93  }
    94  
    95  func (ts *TestScheduler) Send(server request.Server, req request.Request) request.ID {
    96  	ts.lastId++
    97  	ts.sent[ts.testIndex] = append(ts.sent[ts.testIndex], requestWithID{
    98  		sid:     request.ServerAndID{Server: server, ID: ts.lastId},
    99  		request: req,
   100  	})
   101  	ts.allowance[server]--
   102  	return ts.lastId
   103  }
   104  
   105  func (ts *TestScheduler) Fail(server request.Server, desc string) {
   106  	if ts.expFail[server] == 0 {
   107  		ts.t.Errorf("Unexpected Fail from server %s in test case #%d: %s", server.Name(), ts.testIndex, desc)
   108  		return
   109  	}
   110  	ts.expFail[server]--
   111  }
   112  
   113  func (ts *TestScheduler) Request(testIndex, reqIndex int) requestWithID {
   114  	if len(ts.sent[testIndex]) < reqIndex {
   115  		ts.t.Errorf("Missing request from test case %d index %d", testIndex, reqIndex)
   116  		return requestWithID{}
   117  	}
   118  	return ts.sent[testIndex][reqIndex-1]
   119  }
   120  
   121  func (ts *TestScheduler) ServerEvent(evType *request.EventType, server request.Server, data any) {
   122  	ts.events = append(ts.events, request.Event{
   123  		Type:   evType,
   124  		Server: server,
   125  		Data:   data,
   126  	})
   127  }
   128  
   129  func (ts *TestScheduler) RequestEvent(evType *request.EventType, req requestWithID, resp request.Response) {
   130  	if req.request == nil {
   131  		return
   132  	}
   133  	ts.events = append(ts.events, request.Event{
   134  		Type:   evType,
   135  		Server: req.sid.Server,
   136  		Data: request.RequestResponse{
   137  			ID:       req.sid.ID,
   138  			Request:  req.request,
   139  			Response: resp,
   140  		},
   141  	})
   142  }
   143  
   144  func (ts *TestScheduler) AddServer(server request.Server, allowance int) {
   145  	ts.servers = append(ts.servers, server)
   146  	ts.allowance[server] = allowance
   147  	ts.ServerEvent(request.EvRegistered, server, nil)
   148  }
   149  
   150  func (ts *TestScheduler) RemoveServer(server request.Server) {
   151  	ts.servers = append(ts.servers, server)
   152  	for i, s := range ts.servers {
   153  		if s == server {
   154  			copy(ts.servers[i:len(ts.servers)-1], ts.servers[i+1:])
   155  			ts.servers = ts.servers[:len(ts.servers)-1]
   156  			break
   157  		}
   158  	}
   159  	delete(ts.allowance, server)
   160  	ts.ServerEvent(request.EvUnregistered, server, nil)
   161  }
   162  
   163  func (ts *TestScheduler) AddAllowance(server request.Server, allowance int) {
   164  	ts.allowance[server] += allowance
   165  }
   166  
   167  func (ts *TestScheduler) ExpFail(server request.Server) {
   168  	ts.expFail[server]++
   169  }
   170  
   171  type TestCommitteeChain struct {
   172  	fsp, nsp uint64
   173  	init     bool
   174  }
   175  
   176  func (t *TestCommitteeChain) CheckpointInit(bootstrap types.BootstrapData) error {
   177  	t.fsp, t.nsp, t.init = bootstrap.Header.SyncPeriod(), bootstrap.Header.SyncPeriod()+2, true
   178  	return nil
   179  }
   180  
   181  func (t *TestCommitteeChain) InsertUpdate(update *types.LightClientUpdate, nextCommittee *types.SerializedSyncCommittee) error {
   182  	period := update.AttestedHeader.Header.SyncPeriod()
   183  	if period < t.fsp || period > t.nsp || !t.init {
   184  		return light.ErrInvalidPeriod
   185  	}
   186  	if period == t.nsp {
   187  		t.nsp++
   188  	}
   189  	return nil
   190  }
   191  
   192  func (t *TestCommitteeChain) NextSyncPeriod() (uint64, bool) {
   193  	return t.nsp, t.init
   194  }
   195  
   196  func (tc *TestCommitteeChain) ExpInit(t *testing.T, ExpInit bool) {
   197  	if tc.init != ExpInit {
   198  		t.Errorf("Incorrect init flag (expected %v, got %v)", ExpInit, tc.init)
   199  	}
   200  }
   201  
   202  func (t *TestCommitteeChain) SetNextSyncPeriod(nsp uint64) {
   203  	t.init, t.nsp = true, nsp
   204  }
   205  
   206  func (tc *TestCommitteeChain) ExpNextSyncPeriod(t *testing.T, expNsp uint64) {
   207  	tc.ExpInit(t, true)
   208  	if tc.nsp != expNsp {
   209  		t.Errorf("Incorrect NextSyncPeriod (expected %d, got %d)", expNsp, tc.nsp)
   210  	}
   211  }
   212  
   213  type TestHeadTracker struct {
   214  	phead     types.HeadInfo
   215  	validated []types.OptimisticUpdate
   216  	finality  types.FinalityUpdate
   217  }
   218  
   219  func (ht *TestHeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, error) {
   220  	ht.validated = append(ht.validated, update)
   221  	return true, nil
   222  }
   223  
   224  func (ht *TestHeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error) {
   225  	ht.finality = update
   226  	return true, nil
   227  }
   228  
   229  func (ht *TestHeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
   230  	return ht.finality, ht.finality.Attested.Header != (types.Header{})
   231  }
   232  
   233  func (ht *TestHeadTracker) ExpValidated(t *testing.T, tci int, expHeads []types.OptimisticUpdate) {
   234  	for i, expHead := range expHeads {
   235  		if i >= len(ht.validated) {
   236  			t.Errorf("Missing validated head in test case #%d index #%d (expected {slot %d blockRoot %x}, got none)", tci, i, expHead.Attested.Header.Slot, expHead.Attested.Header.Hash())
   237  			continue
   238  		}
   239  		if !reflect.DeepEqual(ht.validated[i], expHead) {
   240  			vhead := ht.validated[i].Attested.Header
   241  			t.Errorf("Wrong validated head in test case #%d index #%d (expected {slot %d blockRoot %x}, got {slot %d blockRoot %x})", tci, i, expHead.Attested.Header.Slot, expHead.Attested.Header.Hash(), vhead.Slot, vhead.Hash())
   242  		}
   243  	}
   244  	for i := len(expHeads); i < len(ht.validated); i++ {
   245  		vhead := ht.validated[i].Attested.Header
   246  		t.Errorf("Unexpected validated head in test case #%d index #%d (expected none, got {slot %d blockRoot %x})", tci, i, vhead.Slot, vhead.Hash())
   247  	}
   248  	ht.validated = nil
   249  }
   250  
   251  func (ht *TestHeadTracker) SetPrefetchHead(head types.HeadInfo) {
   252  	ht.phead = head
   253  }
   254  
   255  func (ht *TestHeadTracker) ExpPrefetch(t *testing.T, tci int, exp types.HeadInfo) {
   256  	if ht.phead != exp {
   257  		t.Errorf("Wrong prefetch head in test case #%d (expected {slot %d blockRoot %x}, got {slot %d blockRoot %x})", tci, exp.Slot, exp.BlockRoot, ht.phead.Slot, ht.phead.BlockRoot)
   258  	}
   259  }