github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/powchain/service_test.go (about) 1 package powchain 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "math/big" 9 "strings" 10 "sync" 11 "testing" 12 "time" 13 14 "github.com/ethereum/go-ethereum" 15 "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" 16 "github.com/ethereum/go-ethereum/common" 17 gethTypes "github.com/ethereum/go-ethereum/core/types" 18 "github.com/prysmaticlabs/prysm/beacon-chain/cache/depositcache" 19 dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" 20 mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing" 21 contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" 22 protodb "github.com/prysmaticlabs/prysm/proto/beacon/db" 23 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 24 "github.com/prysmaticlabs/prysm/shared/clientstats" 25 "github.com/prysmaticlabs/prysm/shared/event" 26 "github.com/prysmaticlabs/prysm/shared/httputils" 27 "github.com/prysmaticlabs/prysm/shared/params" 28 "github.com/prysmaticlabs/prysm/shared/testutil" 29 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 30 "github.com/prysmaticlabs/prysm/shared/testutil/require" 31 logTest "github.com/sirupsen/logrus/hooks/test" 32 ) 33 34 var _ ChainStartFetcher = (*Service)(nil) 35 var _ ChainInfoFetcher = (*Service)(nil) 36 var _ POWBlockFetcher = (*Service)(nil) 37 var _ Chain = (*Service)(nil) 38 39 type goodLogger struct { 40 backend *backends.SimulatedBackend 41 } 42 43 func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) { 44 if g.backend == nil { 45 return new(event.Feed).Subscribe(ch), nil 46 } 47 return g.backend.SubscribeFilterLogs(ctx, q, ch) 48 } 49 50 func (g *goodLogger) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]gethTypes.Log, error) { 51 if g.backend == nil { 52 logs := make([]gethTypes.Log, 3) 53 for i := 0; i < len(logs); i++ { 54 logs[i].Address = common.Address{} 55 logs[i].Topics = make([]common.Hash, 5) 56 logs[i].Topics[0] = common.Hash{'a'} 57 logs[i].Topics[1] = common.Hash{'b'} 58 logs[i].Topics[2] = common.Hash{'c'} 59 60 } 61 return logs, nil 62 } 63 return g.backend.FilterLogs(ctx, q) 64 } 65 66 type goodNotifier struct { 67 MockStateFeed *event.Feed 68 } 69 70 func (g *goodNotifier) StateFeed() *event.Feed { 71 if g.MockStateFeed == nil { 72 g.MockStateFeed = new(event.Feed) 73 } 74 return g.MockStateFeed 75 } 76 77 type goodFetcher struct { 78 backend *backends.SimulatedBackend 79 } 80 81 func (g *goodFetcher) HeaderByHash(_ context.Context, hash common.Hash) (*gethTypes.Header, error) { 82 if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) { 83 return nil, fmt.Errorf("expected block hash to be nonzero %v", hash) 84 } 85 if g.backend == nil { 86 return &gethTypes.Header{ 87 Number: big.NewInt(0), 88 }, nil 89 } 90 header := g.backend.Blockchain().GetHeaderByHash(hash) 91 if header == nil { 92 return nil, errors.New("nil header returned") 93 } 94 return header, nil 95 96 } 97 98 func (g *goodFetcher) HeaderByNumber(_ context.Context, number *big.Int) (*gethTypes.Header, error) { 99 if g.backend == nil { 100 return &gethTypes.Header{ 101 Number: big.NewInt(15), 102 Time: 150, 103 }, nil 104 } 105 var header *gethTypes.Header 106 if number == nil { 107 header = g.backend.Blockchain().CurrentHeader() 108 } else { 109 header = g.backend.Blockchain().GetHeaderByNumber(number.Uint64()) 110 } 111 if header == nil { 112 return nil, errors.New("nil header returned") 113 } 114 return header, nil 115 } 116 117 func (g *goodFetcher) SyncProgress(_ context.Context) (*ethereum.SyncProgress, error) { 118 return nil, nil 119 } 120 121 var depositsReqForChainStart = 64 122 123 func TestStart_OK(t *testing.T) { 124 hook := logTest.NewGlobal() 125 beaconDB := dbutil.SetupDB(t) 126 testAcc, err := contracts.Setup() 127 require.NoError(t, err, "Unable to set up simulated backend") 128 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 129 HttpEndpoints: []string{endpoint}, 130 DepositContract: testAcc.ContractAddr, 131 BeaconDB: beaconDB, 132 }) 133 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 134 web3Service = setDefaultMocks(web3Service) 135 web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend} 136 web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) 137 require.NoError(t, err) 138 testAcc.Backend.Commit() 139 140 web3Service.Start() 141 if len(hook.Entries) > 0 { 142 msg := hook.LastEntry().Message 143 want := "Could not connect to ETH1.0 chain RPC client" 144 if strings.Contains(want, msg) { 145 t.Errorf("incorrect log, expected %s, got %s", want, msg) 146 } 147 } 148 hook.Reset() 149 web3Service.cancel() 150 } 151 152 func TestStart_NoHttpEndpointDefinedFails_WithoutChainStarted(t *testing.T) { 153 hook := logTest.NewGlobal() 154 beaconDB := dbutil.SetupDB(t) 155 testAcc, err := contracts.Setup() 156 require.NoError(t, err, "Unable to set up simulated backend") 157 s, err := NewService(context.Background(), &Web3ServiceConfig{ 158 HttpEndpoints: []string{""}, // No endpoint defined! 159 DepositContract: testAcc.ContractAddr, 160 BeaconDB: beaconDB, 161 }) 162 require.NoError(t, err) 163 // Set custom exit func so test can proceed 164 log.Logger.ExitFunc = func(i int) { 165 panic(i) 166 } 167 defer func() { 168 log.Logger.ExitFunc = nil 169 }() 170 wg := new(sync.WaitGroup) 171 wg.Add(1) 172 // Expect Start function to fail from a fatal call due 173 // to no state existing. 174 go func() { 175 defer func() { 176 if r := recover(); r != nil { 177 wg.Done() 178 } 179 }() 180 s.Start() 181 }() 182 testutil.WaitTimeout(wg, time.Second) 183 require.LogsContain(t, hook, "cannot create genesis state: no eth1 http endpoint defined") 184 hook.Reset() 185 } 186 187 func TestStart_NoHttpEndpointDefinedSucceeds_WithGenesisState(t *testing.T) { 188 hook := logTest.NewGlobal() 189 beaconDB := dbutil.SetupDB(t) 190 testAcc, err := contracts.Setup() 191 require.NoError(t, err, "Unable to set up simulated backend") 192 st, _ := testutil.DeterministicGenesisState(t, 10) 193 b := testutil.NewBeaconBlock() 194 genRoot, err := b.HashTreeRoot() 195 require.NoError(t, err) 196 require.NoError(t, beaconDB.SaveState(context.Background(), st, genRoot)) 197 require.NoError(t, beaconDB.SaveGenesisBlockRoot(context.Background(), genRoot)) 198 depositCache, err := depositcache.New() 199 require.NoError(t, err) 200 s, err := NewService(context.Background(), &Web3ServiceConfig{ 201 HttpEndpoints: []string{""}, // No endpoint defined! 202 DepositContract: testAcc.ContractAddr, 203 BeaconDB: beaconDB, 204 DepositCache: depositCache, 205 }) 206 require.NoError(t, err) 207 208 wg := new(sync.WaitGroup) 209 wg.Add(1) 210 211 go func() { 212 s.Start() 213 wg.Done() 214 }() 215 s.cancel() 216 testutil.WaitTimeout(wg, time.Second) 217 require.LogsDoNotContain(t, hook, "cannot create genesis state: no eth1 http endpoint defined") 218 hook.Reset() 219 } 220 221 func TestStart_NoHttpEndpointDefinedSucceeds_WithChainStarted(t *testing.T) { 222 hook := logTest.NewGlobal() 223 beaconDB := dbutil.SetupDB(t) 224 testAcc, err := contracts.Setup() 225 require.NoError(t, err, "Unable to set up simulated backend") 226 227 require.NoError(t, beaconDB.SavePowchainData(context.Background(), &protodb.ETH1ChainData{ 228 ChainstartData: &protodb.ChainStartData{Chainstarted: true}, 229 Trie: &protodb.SparseMerkleTrie{}, 230 })) 231 s, err := NewService(context.Background(), &Web3ServiceConfig{ 232 HttpEndpoints: []string{""}, // No endpoint defined! 233 DepositContract: testAcc.ContractAddr, 234 BeaconDB: beaconDB, 235 }) 236 require.NoError(t, err) 237 238 s.Start() 239 require.LogsDoNotContain(t, hook, "cannot create genesis state: no eth1 http endpoint defined") 240 hook.Reset() 241 } 242 243 func TestStop_OK(t *testing.T) { 244 hook := logTest.NewGlobal() 245 testAcc, err := contracts.Setup() 246 require.NoError(t, err, "Unable to set up simulated backend") 247 beaconDB := dbutil.SetupDB(t) 248 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 249 HttpEndpoints: []string{endpoint}, 250 DepositContract: testAcc.ContractAddr, 251 BeaconDB: beaconDB, 252 }) 253 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 254 web3Service = setDefaultMocks(web3Service) 255 web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) 256 require.NoError(t, err) 257 258 testAcc.Backend.Commit() 259 260 err = web3Service.Stop() 261 require.NoError(t, err, "Unable to stop web3 ETH1.0 chain service") 262 263 // The context should have been canceled. 264 assert.NotNil(t, web3Service.ctx.Err(), "Context wasnt canceled") 265 266 hook.Reset() 267 } 268 269 func TestService_Eth1Synced(t *testing.T) { 270 testAcc, err := contracts.Setup() 271 require.NoError(t, err, "Unable to set up simulated backend") 272 beaconDB := dbutil.SetupDB(t) 273 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 274 HttpEndpoints: []string{endpoint}, 275 DepositContract: testAcc.ContractAddr, 276 BeaconDB: beaconDB, 277 }) 278 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 279 web3Service = setDefaultMocks(web3Service) 280 web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) 281 require.NoError(t, err) 282 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 283 284 currTime := testAcc.Backend.Blockchain().CurrentHeader().Time 285 now := time.Now() 286 assert.NoError(t, testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0)))) 287 testAcc.Backend.Commit() 288 289 synced, err := web3Service.isEth1NodeSynced() 290 require.NoError(t, err) 291 assert.Equal(t, true, synced, "Expected eth1 nodes to be synced") 292 } 293 294 func TestFollowBlock_OK(t *testing.T) { 295 testAcc, err := contracts.Setup() 296 require.NoError(t, err, "Unable to set up simulated backend") 297 beaconDB := dbutil.SetupDB(t) 298 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 299 HttpEndpoints: []string{endpoint}, 300 DepositContract: testAcc.ContractAddr, 301 BeaconDB: beaconDB, 302 }) 303 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 304 305 // simulated backend sets eth1 block 306 // time as 10 seconds 307 conf := params.BeaconConfig() 308 conf.SecondsPerETH1Block = 10 309 params.OverrideBeaconConfig(conf) 310 defer params.UseMainnetConfig() 311 312 web3Service = setDefaultMocks(web3Service) 313 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 314 baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64() 315 // process follow_distance blocks 316 for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ { 317 testAcc.Backend.Commit() 318 } 319 // set current height 320 web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64() 321 web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time() 322 323 h, err := web3Service.followBlockHeight(context.Background()) 324 require.NoError(t, err) 325 assert.Equal(t, baseHeight, h, "Unexpected block height") 326 numToForward := uint64(2) 327 expectedHeight := numToForward + baseHeight 328 // forward 2 blocks 329 for i := uint64(0); i < numToForward; i++ { 330 testAcc.Backend.Commit() 331 } 332 // set current height 333 web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64() 334 web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time() 335 336 h, err = web3Service.followBlockHeight(context.Background()) 337 require.NoError(t, err) 338 assert.Equal(t, expectedHeight, h, "Unexpected block height") 339 } 340 341 func TestStatus(t *testing.T) { 342 now := time.Now() 343 344 beforeFiveMinutesAgo := uint64(now.Add(-5*time.Minute - 30*time.Second).Unix()) 345 afterFiveMinutesAgo := uint64(now.Add(-5*time.Minute + 30*time.Second).Unix()) 346 347 testCases := map[*Service]string{ 348 // "status is ok" cases 349 {}: "", 350 {isRunning: true, latestEth1Data: &protodb.LatestETH1Data{BlockTime: afterFiveMinutesAgo}}: "", 351 {isRunning: false, latestEth1Data: &protodb.LatestETH1Data{BlockTime: beforeFiveMinutesAgo}}: "", 352 {isRunning: false, runError: errors.New("test runError")}: "", 353 // "status is error" cases 354 {isRunning: true, runError: errors.New("test runError")}: "test runError", 355 } 356 357 for web3ServiceState, wantedErrorText := range testCases { 358 status := web3ServiceState.Status() 359 if status == nil { 360 assert.Equal(t, "", wantedErrorText) 361 362 } else { 363 assert.Equal(t, wantedErrorText, status.Error()) 364 } 365 } 366 } 367 368 func TestHandlePanic_OK(t *testing.T) { 369 hook := logTest.NewGlobal() 370 beaconDB := dbutil.SetupDB(t) 371 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 372 HttpEndpoints: []string{endpoint}, 373 BeaconDB: beaconDB, 374 }) 375 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 376 // nil eth1DataFetcher would panic if cached value not used 377 web3Service.eth1DataFetcher = nil 378 web3Service.processBlockHeader(nil) 379 require.LogsContain(t, hook, "Panicked when handling data from ETH 1.0 Chain!") 380 } 381 382 func TestLogTillGenesis_OK(t *testing.T) { 383 // Reset the var at the end of the test. 384 currPeriod := logPeriod 385 logPeriod = 1 * time.Second 386 defer func() { 387 logPeriod = currPeriod 388 }() 389 390 orgConfig := params.BeaconConfig().Copy() 391 cfg := params.BeaconConfig() 392 cfg.Eth1FollowDistance = 5 393 params.OverrideBeaconConfig(cfg) 394 defer func() { 395 params.OverrideBeaconConfig(orgConfig) 396 }() 397 398 orgNetworkConfig := params.BeaconNetworkConfig().Copy() 399 nCfg := params.BeaconNetworkConfig() 400 nCfg.ContractDeploymentBlock = 0 401 params.OverrideBeaconNetworkConfig(nCfg) 402 defer func() { 403 params.OverrideBeaconNetworkConfig(orgNetworkConfig) 404 }() 405 406 hook := logTest.NewGlobal() 407 testAcc, err := contracts.Setup() 408 require.NoError(t, err, "Unable to set up simulated backend") 409 beaconDB := dbutil.SetupDB(t) 410 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 411 HttpEndpoints: []string{endpoint}, 412 DepositContract: testAcc.ContractAddr, 413 BeaconDB: beaconDB, 414 }) 415 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 416 web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) 417 require.NoError(t, err) 418 419 web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend} 420 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 421 web3Service.httpLogger = testAcc.Backend 422 for i := 0; i < 30; i++ { 423 testAcc.Backend.Commit() 424 } 425 web3Service.latestEth1Data = &protodb.LatestETH1Data{LastRequestedBlock: 0} 426 // Spin off to a separate routine 427 go web3Service.run(web3Service.ctx.Done()) 428 // Wait for 2 seconds so that the 429 // info is logged. 430 time.Sleep(2 * time.Second) 431 web3Service.cancel() 432 assert.LogsContain(t, hook, "Currently waiting for chainstart") 433 } 434 435 func TestInitDepositCache_OK(t *testing.T) { 436 ctrs := []*protodb.DepositContainer{ 437 {Index: 0, Eth1BlockHeight: 2, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("A")}}}, 438 {Index: 1, Eth1BlockHeight: 4, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("B")}}}, 439 {Index: 2, Eth1BlockHeight: 6, Deposit: ðpb.Deposit{Proof: [][]byte{[]byte("c")}}}, 440 } 441 gs, _ := testutil.DeterministicGenesisState(t, 1) 442 beaconDB := dbutil.SetupDB(t) 443 s := &Service{ 444 chainStartData: &protodb.ChainStartData{Chainstarted: false}, 445 preGenesisState: gs, 446 cfg: &Web3ServiceConfig{BeaconDB: beaconDB}, 447 } 448 var err error 449 s.cfg.DepositCache, err = depositcache.New() 450 require.NoError(t, err) 451 require.NoError(t, s.initDepositCaches(context.Background(), ctrs)) 452 453 require.Equal(t, 0, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil))) 454 455 blockRootA := [32]byte{'a'} 456 457 emptyState, err := testutil.NewBeaconState() 458 require.NoError(t, err) 459 require.NoError(t, s.cfg.BeaconDB.SaveGenesisBlockRoot(context.Background(), blockRootA)) 460 require.NoError(t, s.cfg.BeaconDB.SaveState(context.Background(), emptyState, blockRootA)) 461 s.chainStartData.Chainstarted = true 462 require.NoError(t, s.initDepositCaches(context.Background(), ctrs)) 463 require.Equal(t, 3, len(s.cfg.DepositCache.PendingContainers(context.Background(), nil))) 464 } 465 466 func TestNewService_EarliestVotingBlock(t *testing.T) { 467 testAcc, err := contracts.Setup() 468 require.NoError(t, err, "Unable to set up simulated backend") 469 beaconDB := dbutil.SetupDB(t) 470 web3Service, err := NewService(context.Background(), &Web3ServiceConfig{ 471 HttpEndpoints: []string{endpoint}, 472 DepositContract: testAcc.ContractAddr, 473 BeaconDB: beaconDB, 474 }) 475 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 476 web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} 477 // simulated backend sets eth1 block 478 // time as 10 seconds 479 conf := params.BeaconConfig() 480 conf.SecondsPerETH1Block = 10 481 conf.Eth1FollowDistance = 50 482 params.OverrideBeaconConfig(conf) 483 defer params.UseMainnetConfig() 484 485 // Genesis not set 486 followBlock := uint64(2000) 487 blk, err := web3Service.determineEarliestVotingBlock(context.Background(), followBlock) 488 require.NoError(t, err) 489 assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block") 490 491 // Genesis is set. 492 493 numToForward := 1500 494 // forward 1500 blocks 495 for i := 0; i < numToForward; i++ { 496 testAcc.Backend.Commit() 497 } 498 currTime := testAcc.Backend.Blockchain().CurrentHeader().Time 499 now := time.Now() 500 err = testAcc.Backend.AdjustTime(now.Sub(time.Unix(int64(currTime), 0))) 501 require.NoError(t, err) 502 testAcc.Backend.Commit() 503 504 currTime = testAcc.Backend.Blockchain().CurrentHeader().Time 505 web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentHeader().Number.Uint64() 506 web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentHeader().Time 507 web3Service.chainStartData.GenesisTime = currTime 508 509 // With a current slot of zero, only request follow_blocks behind. 510 blk, err = web3Service.determineEarliestVotingBlock(context.Background(), followBlock) 511 require.NoError(t, err) 512 assert.Equal(t, followBlock-conf.Eth1FollowDistance, blk, "unexpected earliest voting block") 513 514 } 515 516 func TestNewService_Eth1HeaderRequLimit(t *testing.T) { 517 testAcc, err := contracts.Setup() 518 require.NoError(t, err, "Unable to set up simulated backend") 519 beaconDB := dbutil.SetupDB(t) 520 521 s1, err := NewService(context.Background(), &Web3ServiceConfig{ 522 HttpEndpoints: []string{endpoint}, 523 DepositContract: testAcc.ContractAddr, 524 BeaconDB: beaconDB, 525 }) 526 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 527 assert.Equal(t, defaultEth1HeaderReqLimit, s1.cfg.Eth1HeaderReqLimit, "default eth1 header request limit not set") 528 529 s2, err := NewService(context.Background(), &Web3ServiceConfig{ 530 HttpEndpoints: []string{endpoint}, 531 DepositContract: testAcc.ContractAddr, 532 BeaconDB: beaconDB, 533 Eth1HeaderReqLimit: uint64(150), 534 }) 535 require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 536 assert.Equal(t, uint64(150), s2.cfg.Eth1HeaderReqLimit, "unable to set eth1HeaderRequestLimit") 537 } 538 539 type mockBSUpdater struct { 540 lastBS clientstats.BeaconNodeStats 541 } 542 543 func (mbs *mockBSUpdater) Update(bs clientstats.BeaconNodeStats) { 544 mbs.lastBS = bs 545 } 546 547 var _ BeaconNodeStatsUpdater = &mockBSUpdater{} 548 549 func TestServiceFallbackCorrectly(t *testing.T) { 550 firstEndpoint := "A" 551 secondEndpoint := "B" 552 553 testAcc, err := contracts.Setup() 554 require.NoError(t, err, "Unable to set up simulated backend") 555 beaconDB := dbutil.SetupDB(t) 556 557 mbs := &mockBSUpdater{} 558 s1, err := NewService(context.Background(), &Web3ServiceConfig{ 559 HttpEndpoints: []string{firstEndpoint}, 560 DepositContract: testAcc.ContractAddr, 561 BeaconDB: beaconDB, 562 BeaconNodeStatsUpdater: mbs, 563 }) 564 s1.bsUpdater = mbs 565 require.NoError(t, err) 566 567 assert.Equal(t, firstEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint") 568 // Stay at the first endpoint. 569 s1.fallbackToNextEndpoint() 570 assert.Equal(t, firstEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint") 571 assert.Equal(t, false, mbs.lastBS.SyncEth1FallbackConfigured, "SyncEth1FallbackConfigured in clientstats update should be false when only 1 endpoint is configured") 572 573 s1.httpEndpoints = append(s1.httpEndpoints, httputils.Endpoint{Url: secondEndpoint}) 574 575 s1.fallbackToNextEndpoint() 576 assert.Equal(t, secondEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint") 577 assert.Equal(t, true, mbs.lastBS.SyncEth1FallbackConfigured, "SyncEth1FallbackConfigured in clientstats update should be true when > 1 endpoint is configured") 578 579 thirdEndpoint := "C" 580 fourthEndpoint := "D" 581 582 s1.httpEndpoints = append(s1.httpEndpoints, httputils.Endpoint{Url: thirdEndpoint}, httputils.Endpoint{Url: fourthEndpoint}) 583 584 s1.fallbackToNextEndpoint() 585 assert.Equal(t, thirdEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint") 586 587 s1.fallbackToNextEndpoint() 588 assert.Equal(t, fourthEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint") 589 590 // Rollover correctly back to the first endpoint 591 s1.fallbackToNextEndpoint() 592 assert.Equal(t, firstEndpoint, s1.currHttpEndpoint.Url, "Unexpected http endpoint") 593 } 594 595 func TestDedupEndpoints(t *testing.T) { 596 assert.DeepEqual(t, []string{"A"}, dedupEndpoints([]string{"A"}), "did not dedup correctly") 597 assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "B"}), "did not dedup correctly") 598 assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "A", "A", "B"}), "did not dedup correctly") 599 assert.DeepEqual(t, []string{"A", "B"}, dedupEndpoints([]string{"A", "A", "A", "B", "B"}), "did not dedup correctly") 600 } 601 602 func Test_batchRequestHeaders_UnderflowChecks(t *testing.T) { 603 srv := &Service{} 604 start := uint64(101) 605 end := uint64(100) 606 _, err := srv.batchRequestHeaders(start, end) 607 require.ErrorContains(t, "cannot be >", err) 608 609 start = uint64(200) 610 end = uint64(100) 611 _, err = srv.batchRequestHeaders(start, end) 612 require.ErrorContains(t, "cannot be >", err) 613 } 614 615 func TestService_EnsureConsistentPowchainData(t *testing.T) { 616 beaconDB := dbutil.SetupDB(t) 617 cache, err := depositcache.New() 618 require.NoError(t, err) 619 620 s1, err := NewService(context.Background(), &Web3ServiceConfig{ 621 BeaconDB: beaconDB, 622 DepositCache: cache, 623 }) 624 require.NoError(t, err) 625 genState, err := testutil.NewBeaconState() 626 require.NoError(t, err) 627 assert.NoError(t, genState.SetSlot(1000)) 628 629 require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState)) 630 require.NoError(t, s1.ensureValidPowchainData(context.Background())) 631 632 eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background()) 633 assert.NoError(t, err) 634 635 assert.NotNil(t, eth1Data) 636 assert.Equal(t, true, eth1Data.ChainstartData.Chainstarted) 637 } 638 639 func TestService_InitializeCorrectly(t *testing.T) { 640 beaconDB := dbutil.SetupDB(t) 641 cache, err := depositcache.New() 642 require.NoError(t, err) 643 644 s1, err := NewService(context.Background(), &Web3ServiceConfig{ 645 BeaconDB: beaconDB, 646 DepositCache: cache, 647 }) 648 require.NoError(t, err) 649 genState, err := testutil.NewBeaconState() 650 require.NoError(t, err) 651 assert.NoError(t, genState.SetSlot(1000)) 652 653 require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState)) 654 require.NoError(t, s1.ensureValidPowchainData(context.Background())) 655 656 eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background()) 657 assert.NoError(t, err) 658 659 assert.NoError(t, s1.initializeEth1Data(context.Background(), eth1Data)) 660 assert.Equal(t, int64(-1), s1.lastReceivedMerkleIndex, "received incorrect last received merkle index") 661 } 662 663 func TestService_EnsureValidPowchainData(t *testing.T) { 664 beaconDB := dbutil.SetupDB(t) 665 cache, err := depositcache.New() 666 require.NoError(t, err) 667 668 s1, err := NewService(context.Background(), &Web3ServiceConfig{ 669 BeaconDB: beaconDB, 670 DepositCache: cache, 671 }) 672 require.NoError(t, err) 673 genState, err := testutil.NewBeaconState() 674 require.NoError(t, err) 675 assert.NoError(t, genState.SetSlot(1000)) 676 677 require.NoError(t, s1.cfg.BeaconDB.SaveGenesisData(context.Background(), genState)) 678 679 err = s1.cfg.BeaconDB.SavePowchainData(context.Background(), &protodb.ETH1ChainData{ 680 ChainstartData: &protodb.ChainStartData{Chainstarted: true}, 681 DepositContainers: []*protodb.DepositContainer{{Index: 1}}, 682 }) 683 require.NoError(t, err) 684 require.NoError(t, s1.ensureValidPowchainData(context.Background())) 685 686 eth1Data, err := s1.cfg.BeaconDB.PowchainData(context.Background()) 687 assert.NoError(t, err) 688 689 assert.NotNil(t, eth1Data) 690 assert.Equal(t, 0, len(eth1Data.DepositContainers)) 691 } 692 693 func TestService_ValidateDepositContainers(t *testing.T) { 694 beaconDB := dbutil.SetupDB(t) 695 cache, err := depositcache.New() 696 require.NoError(t, err) 697 698 s1, err := NewService(context.Background(), &Web3ServiceConfig{ 699 BeaconDB: beaconDB, 700 DepositCache: cache, 701 }) 702 require.NoError(t, err) 703 704 var tt = []struct { 705 name string 706 ctrsFunc func() []*protodb.DepositContainer 707 expectedRes bool 708 }{ 709 { 710 name: "zero containers", 711 ctrsFunc: func() []*protodb.DepositContainer { 712 return make([]*protodb.DepositContainer, 0) 713 }, 714 expectedRes: true, 715 }, 716 { 717 name: "ordered containers", 718 ctrsFunc: func() []*protodb.DepositContainer { 719 ctrs := make([]*protodb.DepositContainer, 0) 720 for i := 0; i < 10; i++ { 721 ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)}) 722 } 723 return ctrs 724 }, 725 expectedRes: true, 726 }, 727 { 728 name: "0th container missing", 729 ctrsFunc: func() []*protodb.DepositContainer { 730 ctrs := make([]*protodb.DepositContainer, 0) 731 for i := 1; i < 10; i++ { 732 ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)}) 733 } 734 return ctrs 735 }, 736 expectedRes: false, 737 }, 738 { 739 name: "skipped containers", 740 ctrsFunc: func() []*protodb.DepositContainer { 741 ctrs := make([]*protodb.DepositContainer, 0) 742 for i := 0; i < 10; i++ { 743 if i == 5 || i == 7 { 744 continue 745 } 746 ctrs = append(ctrs, &protodb.DepositContainer{Index: int64(i), Eth1BlockHeight: uint64(i + 10)}) 747 } 748 return ctrs 749 }, 750 expectedRes: false, 751 }, 752 } 753 754 for _, test := range tt { 755 assert.Equal(t, test.expectedRes, s1.validateDepositContainers(test.ctrsFunc())) 756 } 757 } 758 759 func TestTimestampIsChecked(t *testing.T) { 760 timestamp := uint64(time.Now().Unix()) 761 assert.Equal(t, false, eth1HeadIsBehind(timestamp)) 762 763 // Give an older timestmap beyond threshold. 764 timestamp = uint64(time.Now().Add(-eth1Threshold).Add(-1 * time.Minute).Unix()) 765 assert.Equal(t, true, eth1HeadIsBehind(timestamp)) 766 }