github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/powchain/block_reader_test.go (about)

     1  package powchain
     2  
     3  import (
     4  	"context"
     5  	"math/big"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/ethereum/go-ethereum/common"
    10  	"github.com/ethereum/go-ethereum/common/hexutil"
    11  	gethTypes "github.com/ethereum/go-ethereum/core/types"
    12  	"github.com/ethereum/go-ethereum/trie"
    13  	dbutil "github.com/prysmaticlabs/prysm/beacon-chain/db/testing"
    14  	mockPOW "github.com/prysmaticlabs/prysm/beacon-chain/powchain/testing"
    15  	contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
    16  	"github.com/prysmaticlabs/prysm/shared/testutil/assert"
    17  	"github.com/prysmaticlabs/prysm/shared/testutil/require"
    18  )
    19  
    20  var endpoint = "http://127.0.0.1"
    21  
    22  func setDefaultMocks(service *Service) *Service {
    23  	service.eth1DataFetcher = &goodFetcher{}
    24  	service.httpLogger = &goodLogger{}
    25  	service.cfg.StateNotifier = &goodNotifier{}
    26  	return service
    27  }
    28  
    29  func TestLatestMainchainInfo_OK(t *testing.T) {
    30  	testAcc, err := contracts.Setup()
    31  	require.NoError(t, err, "Unable to set up simulated backend")
    32  
    33  	beaconDB := dbutil.SetupDB(t)
    34  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
    35  		HttpEndpoints:   []string{endpoint},
    36  		DepositContract: testAcc.ContractAddr,
    37  		BeaconDB:        beaconDB,
    38  	})
    39  	require.NoError(t, err, "Unable to setup web3 ETH1.0 chain service")
    40  
    41  	web3Service = setDefaultMocks(web3Service)
    42  	web3Service.rpcClient = &mockPOW.RPCClient{Backend: testAcc.Backend}
    43  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
    44  
    45  	web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
    46  	require.NoError(t, err)
    47  	testAcc.Backend.Commit()
    48  
    49  	exitRoutine := make(chan bool)
    50  
    51  	go func() {
    52  		web3Service.run(web3Service.ctx.Done())
    53  		<-exitRoutine
    54  	}()
    55  
    56  	header, err := web3Service.eth1DataFetcher.HeaderByNumber(web3Service.ctx, nil)
    57  	require.NoError(t, err)
    58  
    59  	tickerChan := make(chan time.Time)
    60  	web3Service.headTicker = &time.Ticker{C: tickerChan}
    61  	tickerChan <- time.Now()
    62  	web3Service.cancel()
    63  	exitRoutine <- true
    64  
    65  	assert.Equal(t, web3Service.latestEth1Data.BlockHeight, header.Number.Uint64())
    66  	assert.Equal(t, hexutil.Encode(web3Service.latestEth1Data.BlockHash), header.Hash().Hex())
    67  	assert.Equal(t, web3Service.latestEth1Data.BlockTime, header.Time)
    68  }
    69  
    70  func TestBlockHashByHeight_ReturnsHash(t *testing.T) {
    71  	beaconDB := dbutil.SetupDB(t)
    72  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
    73  		HttpEndpoints: []string{endpoint},
    74  		BeaconDB:      beaconDB,
    75  	})
    76  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
    77  
    78  	web3Service = setDefaultMocks(web3Service)
    79  	ctx := context.Background()
    80  
    81  	header := &gethTypes.Header{
    82  		Number: big.NewInt(15),
    83  		Time:   150,
    84  	}
    85  
    86  	wanted := header.Hash()
    87  
    88  	hash, err := web3Service.BlockHashByHeight(ctx, big.NewInt(0))
    89  	require.NoError(t, err, "Could not get block hash with given height")
    90  	require.DeepEqual(t, wanted.Bytes(), hash.Bytes(), "Block hash did not equal expected hash")
    91  
    92  	exists, _, err := web3Service.headerCache.HeaderInfoByHash(wanted)
    93  	require.NoError(t, err)
    94  	require.Equal(t, true, exists, "Expected block info to be cached")
    95  }
    96  
    97  func TestBlockHashByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) {
    98  	beaconDB := dbutil.SetupDB(t)
    99  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   100  		HttpEndpoints: []string{endpoint},
   101  		BeaconDB:      beaconDB,
   102  	})
   103  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   104  
   105  	web3Service = setDefaultMocks(web3Service)
   106  	web3Service.eth1DataFetcher = nil
   107  	ctx := context.Background()
   108  
   109  	_, err = web3Service.BlockHashByHeight(ctx, big.NewInt(0))
   110  	require.ErrorContains(t, "nil eth1DataFetcher", err)
   111  }
   112  
   113  func TestBlockExists_ValidHash(t *testing.T) {
   114  	beaconDB := dbutil.SetupDB(t)
   115  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   116  		HttpEndpoints: []string{endpoint},
   117  		BeaconDB:      beaconDB,
   118  	})
   119  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   120  
   121  	web3Service = setDefaultMocks(web3Service)
   122  
   123  	block := gethTypes.NewBlock(
   124  		&gethTypes.Header{
   125  			Number: big.NewInt(0),
   126  		},
   127  		[]*gethTypes.Transaction{},
   128  		[]*gethTypes.Header{},
   129  		[]*gethTypes.Receipt{},
   130  		new(trie.Trie),
   131  	)
   132  
   133  	exists, height, err := web3Service.BlockExists(context.Background(), block.Hash())
   134  	require.NoError(t, err, "Could not get block hash with given height")
   135  	require.Equal(t, true, exists)
   136  	require.Equal(t, 0, height.Cmp(block.Number()))
   137  
   138  	exists, _, err = web3Service.headerCache.HeaderInfoByHeight(height)
   139  	require.NoError(t, err)
   140  	require.Equal(t, true, exists, "Expected block to be cached")
   141  
   142  }
   143  
   144  func TestBlockExists_InvalidHash(t *testing.T) {
   145  	beaconDB := dbutil.SetupDB(t)
   146  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   147  		HttpEndpoints: []string{endpoint},
   148  		BeaconDB:      beaconDB,
   149  	})
   150  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   151  
   152  	web3Service = setDefaultMocks(web3Service)
   153  
   154  	_, _, err = web3Service.BlockExists(context.Background(), common.BytesToHash([]byte{0}))
   155  	require.NotNil(t, err, "Expected BlockExists to error with invalid hash")
   156  }
   157  
   158  func TestBlockExists_UsesCachedBlockInfo(t *testing.T) {
   159  	beaconDB := dbutil.SetupDB(t)
   160  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   161  		HttpEndpoints: []string{endpoint},
   162  		BeaconDB:      beaconDB,
   163  	})
   164  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   165  	// nil eth1DataFetcher would panic if cached value not used
   166  	web3Service.eth1DataFetcher = nil
   167  
   168  	header := &gethTypes.Header{
   169  		Number: big.NewInt(0),
   170  	}
   171  
   172  	err = web3Service.headerCache.AddHeader(header)
   173  	require.NoError(t, err)
   174  
   175  	exists, height, err := web3Service.BlockExists(context.Background(), header.Hash())
   176  	require.NoError(t, err, "Could not get block hash with given height")
   177  	require.Equal(t, true, exists)
   178  	require.Equal(t, 0, height.Cmp(header.Number))
   179  }
   180  
   181  func TestBlockExistsWithCache_UsesCachedHeaderInfo(t *testing.T) {
   182  	beaconDB := dbutil.SetupDB(t)
   183  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   184  		HttpEndpoints: []string{endpoint},
   185  		BeaconDB:      beaconDB,
   186  	})
   187  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   188  
   189  	header := &gethTypes.Header{
   190  		Number: big.NewInt(0),
   191  	}
   192  
   193  	err = web3Service.headerCache.AddHeader(header)
   194  	require.NoError(t, err)
   195  
   196  	exists, height, err := web3Service.BlockExistsWithCache(context.Background(), header.Hash())
   197  	require.NoError(t, err, "Could not get block hash with given height")
   198  	require.Equal(t, true, exists)
   199  	require.Equal(t, 0, height.Cmp(header.Number))
   200  }
   201  
   202  func TestBlockExistsWithCache_HeaderNotCached(t *testing.T) {
   203  	beaconDB := dbutil.SetupDB(t)
   204  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   205  		HttpEndpoints: []string{endpoint},
   206  		BeaconDB:      beaconDB,
   207  	})
   208  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   209  
   210  	exists, height, err := web3Service.BlockExistsWithCache(context.Background(), common.BytesToHash([]byte("hash")))
   211  	require.NoError(t, err, "Could not get block hash with given height")
   212  	require.Equal(t, false, exists)
   213  	require.Equal(t, (*big.Int)(nil), height)
   214  }
   215  
   216  func TestService_BlockNumberByTimestamp(t *testing.T) {
   217  	beaconDB := dbutil.SetupDB(t)
   218  	testAcc, err := contracts.Setup()
   219  	require.NoError(t, err, "Unable to set up simulated backend")
   220  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   221  		HttpEndpoints: []string{endpoint},
   222  		BeaconDB:      beaconDB,
   223  	})
   224  	require.NoError(t, err)
   225  	web3Service = setDefaultMocks(web3Service)
   226  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   227  
   228  	for i := 0; i < 200; i++ {
   229  		testAcc.Backend.Commit()
   230  	}
   231  	ctx := context.Background()
   232  	hd, err := testAcc.Backend.HeaderByNumber(ctx, nil)
   233  	require.NoError(t, err)
   234  	web3Service.latestEth1Data.BlockTime = hd.Time
   235  	web3Service.latestEth1Data.BlockHeight = hd.Number.Uint64()
   236  	blk, err := web3Service.BlockByTimestamp(ctx, 1000 /* time */)
   237  	require.NoError(t, err)
   238  	if blk.Number.Cmp(big.NewInt(0)) == 0 {
   239  		t.Error("Returned a block with zero number, expected to be non zero")
   240  	}
   241  }
   242  
   243  func TestService_BlockNumberByTimestampLessTargetTime(t *testing.T) {
   244  	beaconDB := dbutil.SetupDB(t)
   245  	testAcc, err := contracts.Setup()
   246  	require.NoError(t, err, "Unable to set up simulated backend")
   247  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   248  		HttpEndpoints: []string{endpoint},
   249  		BeaconDB:      beaconDB,
   250  	})
   251  	require.NoError(t, err)
   252  	web3Service = setDefaultMocks(web3Service)
   253  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   254  
   255  	for i := 0; i < 200; i++ {
   256  		testAcc.Backend.Commit()
   257  	}
   258  	ctx := context.Background()
   259  	hd, err := testAcc.Backend.HeaderByNumber(ctx, nil)
   260  	require.NoError(t, err)
   261  	web3Service.latestEth1Data.BlockTime = hd.Time
   262  	// Use extremely small deadline to illustrate that context deadlines are respected.
   263  	ctx, cancel := context.WithTimeout(ctx, 100*time.Nanosecond)
   264  	defer cancel()
   265  
   266  	// Provide an unattainable target time
   267  	_, err = web3Service.findLessTargetEth1Block(ctx, hd.Number, hd.Time/2)
   268  	require.ErrorContains(t, context.DeadlineExceeded.Error(), err)
   269  
   270  	// Provide an attainable target time
   271  	blk, err := web3Service.findLessTargetEth1Block(context.Background(), hd.Number, hd.Time-5)
   272  	require.NoError(t, err)
   273  	require.NotEqual(t, hd.Number.Uint64(), blk.Number.Uint64(), "retrieved block is not less than the head")
   274  }
   275  
   276  func TestService_BlockNumberByTimestampMoreTargetTime(t *testing.T) {
   277  	beaconDB := dbutil.SetupDB(t)
   278  	testAcc, err := contracts.Setup()
   279  	require.NoError(t, err, "Unable to set up simulated backend")
   280  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   281  		HttpEndpoints: []string{endpoint},
   282  		BeaconDB:      beaconDB,
   283  	})
   284  	require.NoError(t, err)
   285  	web3Service = setDefaultMocks(web3Service)
   286  	web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
   287  
   288  	for i := 0; i < 200; i++ {
   289  		testAcc.Backend.Commit()
   290  	}
   291  	ctx := context.Background()
   292  	hd, err := testAcc.Backend.HeaderByNumber(ctx, nil)
   293  	require.NoError(t, err)
   294  	web3Service.latestEth1Data.BlockTime = hd.Time
   295  	// Use extremely small deadline to illustrate that context deadlines are respected.
   296  	ctx, cancel := context.WithTimeout(ctx, 100*time.Nanosecond)
   297  	defer cancel()
   298  
   299  	// Provide an unattainable target time with respect to head
   300  	_, err = web3Service.findMoreTargetEth1Block(ctx, big.NewInt(0).Div(hd.Number, big.NewInt(2)), hd.Time)
   301  	require.ErrorContains(t, context.DeadlineExceeded.Error(), err)
   302  
   303  	// Provide an attainable target time with respect to head
   304  	blk, err := web3Service.findMoreTargetEth1Block(context.Background(), big.NewInt(0).Sub(hd.Number, big.NewInt(5)), hd.Time)
   305  	require.NoError(t, err)
   306  	require.Equal(t, hd.Number.Uint64(), blk.Number.Uint64(), "retrieved block is not equal to the head")
   307  }
   308  
   309  func TestService_BlockTimeByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) {
   310  	beaconDB := dbutil.SetupDB(t)
   311  	web3Service, err := NewService(context.Background(), &Web3ServiceConfig{
   312  		HttpEndpoints: []string{endpoint},
   313  		BeaconDB:      beaconDB,
   314  	})
   315  	require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
   316  
   317  	web3Service = setDefaultMocks(web3Service)
   318  	web3Service.eth1DataFetcher = nil
   319  	ctx := context.Background()
   320  
   321  	_, err = web3Service.BlockTimeByHeight(ctx, big.NewInt(0))
   322  	require.ErrorContains(t, "nil eth1DataFetcher", err)
   323  }