diff --git a/beacon-chain/execution/BUILD.bazel b/beacon-chain/execution/BUILD.bazel index cae0d87e8e4a..51d801a032c5 100644 --- a/beacon-chain/execution/BUILD.bazel +++ b/beacon-chain/execution/BUILD.bazel @@ -129,7 +129,6 @@ go_test( "@com_github_ethereum_go_ethereum//core/beacon:go_default_library", "@com_github_ethereum_go_ethereum//core/types:go_default_library", "@com_github_ethereum_go_ethereum//rpc:go_default_library", - "@com_github_ethereum_go_ethereum//trie:go_default_library", "@com_github_holiman_uint256//:go_default_library", "@com_github_pkg_errors//:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library", diff --git a/beacon-chain/execution/block_cache.go b/beacon-chain/execution/block_cache.go index f19856aba37c..a7d774e75c4f 100644 --- a/beacon-chain/execution/block_cache.go +++ b/beacon-chain/execution/block_cache.go @@ -6,7 +6,6 @@ import ( "sync" "github.com/ethereum/go-ethereum/common" - gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" @@ -133,14 +132,11 @@ func (c *headerCache) HeaderInfoByHeight(height *big.Int) (bool, *types.HeaderIn // size limit. This method should be called in sequential header number order if // the desired behavior is that the blocks with the highest header number should // be present in the cache. -func (c *headerCache) AddHeader(hdr *gethTypes.Header) error { +func (c *headerCache) AddHeader(hdr *types.HeaderInfo) error { c.lock.Lock() defer c.lock.Unlock() - hInfo, err := types.HeaderToHeaderInfo(hdr) - if err != nil { - return err - } + hInfo := hdr.Copy() if err := c.hashCache.AddIfNotPresent(hInfo); err != nil { return err diff --git a/beacon-chain/execution/block_cache_test.go b/beacon-chain/execution/block_cache_test.go index e9288a3a920b..293a35e8f22d 100644 --- a/beacon-chain/execution/block_cache_test.go +++ b/beacon-chain/execution/block_cache_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" - gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" + "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v3/testing/assert" "github.com/prysmaticlabs/prysm/v3/testing/require" ) @@ -44,34 +44,30 @@ func TestHeightKeyFn_InvalidObj(t *testing.T) { func TestBlockCache_byHash(t *testing.T) { cache := newHeaderCache() - header := &gethTypes.Header{ - ParentHash: common.HexToHash("0x12345"), - Number: big.NewInt(55), + header := &types.HeaderInfo{ + Number: big.NewInt(55), } - - exists, _, err := cache.HeaderInfoByHash(header.Hash()) + exists, _, err := cache.HeaderInfoByHash(header.Hash) require.NoError(t, err) assert.Equal(t, false, exists, "Expected block info not to exist in empty cache") err = cache.AddHeader(header) require.NoError(t, err) - exists, fetchedInfo, err := cache.HeaderInfoByHash(header.Hash()) + exists, fetchedInfo, err := cache.HeaderInfoByHash(header.Hash) require.NoError(t, err) assert.Equal(t, true, exists, "Expected headerInfo to exist") assert.Equal(t, 0, fetchedInfo.Number.Cmp(header.Number), "Expected fetched info number to be equal") - assert.Equal(t, header.Hash(), fetchedInfo.Hash, "Expected hash to be equal") + assert.Equal(t, header.Hash, fetchedInfo.Hash, "Expected hash to be equal") } func TestBlockCache_byHeight(t *testing.T) { cache := newHeaderCache() - header := &gethTypes.Header{ - ParentHash: common.HexToHash("0x12345"), - Number: big.NewInt(55), + header := &types.HeaderInfo{ + Number: big.NewInt(55), } - exists, _, err := cache.HeaderInfoByHeight(header.Number) require.NoError(t, err) assert.Equal(t, false, exists, "Expected block info not to exist in empty cache") @@ -84,7 +80,7 @@ func TestBlockCache_byHeight(t *testing.T) { assert.Equal(t, true, exists, "Expected headerInfo to exist") assert.Equal(t, 0, fetchedInfo.Number.Cmp(header.Number), "Expected fetched info number to be equal") - assert.Equal(t, header.Hash(), fetchedInfo.Hash, "Expected hash to be equal") + assert.Equal(t, header.Hash, fetchedInfo.Hash, "Expected hash to be equal") } @@ -92,8 +88,9 @@ func TestBlockCache_maxSize(t *testing.T) { cache := newHeaderCache() for i := int64(0); i < int64(maxCacheSize+10); i++ { - header := &gethTypes.Header{ + header := &types.HeaderInfo{ Number: big.NewInt(i), + Hash: common.Hash(bytesutil.ToBytes32(bytesutil.Bytes32(uint64(i)))), } err := cache.AddHeader(header) require.NoError(t, err) diff --git a/beacon-chain/execution/block_reader.go b/beacon-chain/execution/block_reader.go index 9f95d0913b7d..389749ead1b4 100644 --- a/beacon-chain/execution/block_reader.go +++ b/beacon-chain/execution/block_reader.go @@ -35,7 +35,7 @@ func (s *Service) BlockExists(ctx context.Context, hash common.Hash) (bool, *big return true, hdrInfo.Number, nil } span.AddAttributes(trace.BoolAttribute("blockCacheHit", false)) - header, err := s.eth1DataFetcher.HeaderByHash(ctx, hash) + header, err := s.HeaderByHash(ctx, hash) if err != nil { return false, big.NewInt(0), errors.Wrap(err, "could not query block with given hash") } @@ -61,33 +61,33 @@ func (s *Service) BlockHashByHeight(ctx context.Context, height *big.Int) (commo } span.AddAttributes(trace.BoolAttribute("headerCacheHit", false)) - if s.eth1DataFetcher == nil { - err := errors.New("nil eth1DataFetcher") + if s.rpcClient == nil { + err := errors.New("nil rpc client") tracing.AnnotateError(span, err) return [32]byte{}, err } - header, err := s.eth1DataFetcher.HeaderByNumber(ctx, height) + header, err := s.HeaderByNumber(ctx, height) if err != nil { return [32]byte{}, errors.Wrap(err, fmt.Sprintf("could not query header with height %d", height.Uint64())) } if err := s.headerCache.AddHeader(header); err != nil { return [32]byte{}, err } - return header.Hash(), nil + return header.Hash, nil } // BlockTimeByHeight fetches an eth1 block timestamp by its height. func (s *Service) BlockTimeByHeight(ctx context.Context, height *big.Int) (uint64, error) { ctx, span := trace.StartSpan(ctx, "powchain.BlockTimeByHeight") defer span.End() - if s.eth1DataFetcher == nil { - err := errors.New("nil eth1DataFetcher") + if s.rpcClient == nil { + err := errors.New("nil rpc client") tracing.AnnotateError(span, err) return 0, err } - header, err := s.eth1DataFetcher.HeaderByNumber(ctx, height) + header, err := s.HeaderByNumber(ctx, height) if err != nil { return 0, errors.Wrap(err, fmt.Sprintf("could not query block with height %d", height.Uint64())) } @@ -207,20 +207,17 @@ func (s *Service) retrieveHeaderInfo(ctx context.Context, bNum uint64) (*types.H return nil, err } if !exists { - blk, err := s.eth1DataFetcher.HeaderByNumber(ctx, bn) + hdr, err := s.HeaderByNumber(ctx, bn) if err != nil { return nil, err } - if blk == nil { + if hdr == nil { return nil, errors.Errorf("header with the number %d does not exist", bNum) } - if err := s.headerCache.AddHeader(blk); err != nil { - return nil, err - } - info, err = types.HeaderToHeaderInfo(blk) - if err != nil { + if err := s.headerCache.AddHeader(hdr); err != nil { return nil, err } + info = hdr } return info, nil } diff --git a/beacon-chain/execution/block_reader_test.go b/beacon-chain/execution/block_reader_test.go index 8ad02634ede4..7ed3bfaf07af 100644 --- a/beacon-chain/execution/block_reader_test.go +++ b/beacon-chain/execution/block_reader_test.go @@ -9,9 +9,9 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/trie" dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing" mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing" + "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit" "github.com/prysmaticlabs/prysm/v3/contracts/deposit/mock" "github.com/prysmaticlabs/prysm/v3/testing/assert" @@ -19,7 +19,6 @@ import ( ) func setDefaultMocks(service *Service) *Service { - service.eth1DataFetcher = &goodFetcher{} service.httpLogger = &goodLogger{} service.cfg.stateNotifier = &goodNotifier{} return service @@ -44,7 +43,6 @@ func TestLatestMainchainInfo_OK(t *testing.T) { web3Service = setDefaultMocks(web3Service) web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) require.NoError(t, err) @@ -57,7 +55,7 @@ func TestLatestMainchainInfo_OK(t *testing.T) { <-exitRoutine }() - header, err := web3Service.eth1DataFetcher.HeaderByNumber(web3Service.ctx, nil) + header, err := web3Service.HeaderByNumber(web3Service.ctx, nil) require.NoError(t, err) tickerChan := make(chan time.Time) @@ -67,7 +65,7 @@ func TestLatestMainchainInfo_OK(t *testing.T) { exitRoutine <- true assert.Equal(t, web3Service.latestEth1Data.BlockHeight, header.Number.Uint64()) - assert.Equal(t, hexutil.Encode(web3Service.latestEth1Data.BlockHash), header.Hash().Hex()) + assert.Equal(t, hexutil.Encode(web3Service.latestEth1Data.BlockHash), header.Hash.Hex()) assert.Equal(t, web3Service.latestEth1Data.BlockTime, header.Time) } @@ -85,6 +83,7 @@ func TestBlockHashByHeight_ReturnsHash(t *testing.T) { require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") web3Service = setDefaultMocks(web3Service) + web3Service.rpcClient = &mockExecution.RPCClient{} ctx := context.Background() header := &gethTypes.Header{ @@ -117,15 +116,17 @@ func TestBlockHashByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) { require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") web3Service = setDefaultMocks(web3Service) - web3Service.eth1DataFetcher = nil + web3Service.rpcClient = nil ctx := context.Background() _, err = web3Service.BlockHashByHeight(ctx, big.NewInt(0)) - require.ErrorContains(t, "nil eth1DataFetcher", err) + require.ErrorContains(t, "nil rpc client", err) } func TestBlockExists_ValidHash(t *testing.T) { beaconDB := dbutil.SetupDB(t) + testAcc, err := mock.Setup() + require.NoError(t, err, "Unable to set up simulated backend") server, endpoint, err := mockExecution.SetupRPCServer() require.NoError(t, err) t.Cleanup(func() { @@ -138,16 +139,10 @@ func TestBlockExists_ValidHash(t *testing.T) { require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") web3Service = setDefaultMocks(web3Service) - - block := gethTypes.NewBlock( - &gethTypes.Header{ - Number: big.NewInt(0), - }, - []*gethTypes.Transaction{}, - []*gethTypes.Header{}, - []*gethTypes.Receipt{}, - new(trie.Trie), - ) + web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} + testAcc.Backend.Commit() + block, err := testAcc.Backend.BlockByNumber(context.Background(), big.NewInt(0)) + assert.NoError(t, err) exists, height, err := web3Service.BlockExists(context.Background(), block.Hash()) require.NoError(t, err, "Could not get block hash with given height") @@ -191,17 +186,15 @@ func TestBlockExists_UsesCachedBlockInfo(t *testing.T) { WithDatabase(beaconDB), ) require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") - // nil eth1DataFetcher would panic if cached value not used - web3Service.eth1DataFetcher = nil - header := &gethTypes.Header{ + header := &types.HeaderInfo{ Number: big.NewInt(0), } err = web3Service.headerCache.AddHeader(header) require.NoError(t, err) - exists, height, err := web3Service.BlockExists(context.Background(), header.Hash()) + exists, height, err := web3Service.BlockExists(context.Background(), header.Hash) require.NoError(t, err, "Could not get block hash with given height") require.Equal(t, true, exists) require.Equal(t, 0, height.Cmp(header.Number)) @@ -222,7 +215,7 @@ func TestService_BlockNumberByTimestamp(t *testing.T) { ) require.NoError(t, err) web3Service = setDefaultMocks(web3Service) - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} + web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} for i := 0; i < 200; i++ { testAcc.Backend.Commit() @@ -254,7 +247,7 @@ func TestService_BlockNumberByTimestampLessTargetTime(t *testing.T) { ) require.NoError(t, err) web3Service = setDefaultMocks(web3Service) - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} + web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} for i := 0; i < 200; i++ { testAcc.Backend.Commit() @@ -292,7 +285,7 @@ func TestService_BlockNumberByTimestampMoreTargetTime(t *testing.T) { ) require.NoError(t, err) web3Service = setDefaultMocks(web3Service) - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} + web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} for i := 0; i < 200; i++ { testAcc.Backend.Commit() @@ -329,9 +322,9 @@ func TestService_BlockTimeByHeight_ReturnsError_WhenNoEth1Client(t *testing.T) { require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") web3Service = setDefaultMocks(web3Service) - web3Service.eth1DataFetcher = nil + web3Service.rpcClient = nil ctx := context.Background() _, err = web3Service.BlockTimeByHeight(ctx, big.NewInt(0)) - require.ErrorContains(t, "nil eth1DataFetcher", err) + require.ErrorContains(t, "nil rpc client", err) } diff --git a/beacon-chain/execution/engine_client.go b/beacon-chain/execution/engine_client.go index 29e763d327ff..0e3218055efb 100644 --- a/beacon-chain/execution/engine_client.go +++ b/beacon-chain/execution/engine_client.go @@ -8,11 +8,13 @@ import ( "strings" "time" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" gethRPC "github.com/ethereum/go-ethereum/rpc" "github.com/holiman/uint256" "github.com/pkg/errors" + "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" "github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" @@ -354,6 +356,26 @@ func (s *Service) ExecutionBlocksByHashes(ctx context.Context, hashes []common.H return execBlks, nil } +// HeaderByHash returns the relevant header details for the provided block hash. +func (s *Service) HeaderByHash(ctx context.Context, hash common.Hash) (*types.HeaderInfo, error) { + var hdr *types.HeaderInfo + err := s.rpcClient.CallContext(ctx, &hdr, ExecutionBlockByHashMethod, hash, false /* no transactions */) + if err == nil && hdr == nil { + err = ethereum.NotFound + } + return hdr, err +} + +// HeaderByNumber returns the relevant header details for the provided block number. +func (s *Service) HeaderByNumber(ctx context.Context, number *big.Int) (*types.HeaderInfo, error) { + var hdr *types.HeaderInfo + err := s.rpcClient.CallContext(ctx, &hdr, ExecutionBlockByNumberMethod, toBlockNumArg(number), false /* no transactions */) + if err == nil && hdr == nil { + err = ethereum.NotFound + } + return hdr, err +} + // ReconstructFullBellatrixBlock takes in a blinded beacon block and reconstructs // a beacon block with a full execution payload via the engine API. func (s *Service) ReconstructFullBellatrixBlock( @@ -610,3 +632,22 @@ func buildEmptyExecutionPayload() *pb.ExecutionPayload { ExtraData: make([]byte, 0), } } + +func toBlockNumArg(number *big.Int) string { + if number == nil { + return "latest" + } + pending := big.NewInt(-1) + if number.Cmp(pending) == 0 { + return "pending" + } + finalized := big.NewInt(int64(gethRPC.FinalizedBlockNumber)) + if number.Cmp(finalized) == 0 { + return "finalized" + } + safe := big.NewInt(int64(gethRPC.SafeBlockNumber)) + if number.Cmp(safe) == 0 { + return "safe" + } + return hexutil.EncodeBig(number) +} diff --git a/beacon-chain/execution/engine_client_test.go b/beacon-chain/execution/engine_client_test.go index 835f27556202..06a282c9d697 100644 --- a/beacon-chain/execution/engine_client_test.go +++ b/beacon-chain/execution/engine_client_test.go @@ -11,10 +11,12 @@ import ( "strings" "testing" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" gethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + gethRPC "github.com/ethereum/go-ethereum/rpc" "github.com/holiman/uint256" "github.com/pkg/errors" mocks "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing" @@ -24,6 +26,7 @@ import ( "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" pb "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" + "github.com/prysmaticlabs/prysm/v3/testing/assert" "github.com/prysmaticlabs/prysm/v3/testing/require" "github.com/prysmaticlabs/prysm/v3/testing/util" "google.golang.org/protobuf/proto" @@ -36,6 +39,18 @@ var ( _ = EngineCaller(&mocks.EngineClient{}) ) +type RPCClientBad struct { +} + +func (RPCClientBad) Close() {} +func (RPCClientBad) BatchCall([]gethRPC.BatchElem) error { + return errors.New("rpc client is not initialized") +} + +func (RPCClientBad) CallContext(context.Context, interface{}, string, ...interface{}) error { + return ethereum.NotFound +} + func TestClient_IPC(t *testing.T) { server := newTestIPCServer(t) defer server.Stop() @@ -1215,6 +1230,78 @@ func Test_fullPayloadFromExecutionBlock(t *testing.T) { } } +func TestHeaderByHash_NotFound(t *testing.T) { + srv := &Service{} + srv.rpcClient = RPCClientBad{} + + _, err := srv.HeaderByHash(context.Background(), common.Hash([32]byte{})) + assert.Equal(t, ethereum.NotFound, err) +} + +func TestHeaderByNumber_NotFound(t *testing.T) { + srv := &Service{} + srv.rpcClient = RPCClientBad{} + + _, err := srv.HeaderByNumber(context.Background(), big.NewInt(100)) + assert.Equal(t, ethereum.NotFound, err) +} + +func TestToBlockNumArg(t *testing.T) { + tests := []struct { + name string + number *big.Int + want string + }{ + { + name: "genesis", + number: big.NewInt(0), + want: "0x0", + }, + { + name: "near genesis block", + number: big.NewInt(300), + want: "0x12c", + }, + { + name: "current block", + number: big.NewInt(15838075), + want: "0xf1ab7b", + }, + { + name: "far off block", + number: big.NewInt(12032894823020), + want: "0xaf1a06bea6c", + }, + { + name: "latest block", + number: nil, + want: "latest", + }, + { + name: "pending block", + number: big.NewInt(-1), + want: "pending", + }, + { + name: "finalized block", + number: big.NewInt(-3), + want: "finalized", + }, + { + name: "safe block", + number: big.NewInt(-4), + want: "safe", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := toBlockNumArg(tt.number); got != tt.want { + t.Errorf("toBlockNumArg() = %v, want %v", got, tt.want) + } + }) + } +} + type testEngineService struct{} func (*testEngineService) NoArgsRets() {} diff --git a/beacon-chain/execution/log_processing.go b/beacon-chain/execution/log_processing.go index 98d5544966f9..fc3367f736d2 100644 --- a/beacon-chain/execution/log_processing.go +++ b/beacon-chain/execution/log_processing.go @@ -17,6 +17,7 @@ import ( statefeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/state" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers" coreState "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition" + "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" statenative "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native" "github.com/prysmaticlabs/prysm/v3/config/params" contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit" @@ -284,7 +285,7 @@ func (s *Service) processPastLogs(ctx context.Context) error { currentBlockNum = deploymentBlock } // To store all blocks. - headersMap := make(map[uint64]*gethtypes.Header) + headersMap := make(map[uint64]*types.HeaderInfo) rawLogCount, err := s.depositContractCaller.GetDepositCount(&bind.CallOpts{}) if err != nil { return err @@ -341,7 +342,7 @@ func (s *Service) processPastLogs(ctx context.Context) error { return nil } -func (s *Service) processBlockInBatch(ctx context.Context, currentBlockNum uint64, latestFollowHeight uint64, batchSize uint64, additiveFactor uint64, logCount uint64, headersMap map[uint64]*gethtypes.Header) (uint64, uint64, error) { +func (s *Service) processBlockInBatch(ctx context.Context, currentBlockNum uint64, latestFollowHeight uint64, batchSize uint64, additiveFactor uint64, logCount uint64, headersMap map[uint64]*types.HeaderInfo) (uint64, uint64, error) { // Batch request the desired headers and store them in a // map for quick access. requestHeaders := func(startBlk uint64, endBlk uint64) error { @@ -499,11 +500,11 @@ func (s *Service) processChainStartFromBlockNum(ctx context.Context, blkNum *big return nil } -func (s *Service) processChainStartFromHeader(ctx context.Context, header *gethtypes.Header) { - s.processChainStartIfReady(ctx, header.Hash(), header.Number, header.Time) +func (s *Service) processChainStartFromHeader(ctx context.Context, header *types.HeaderInfo) { + s.processChainStartIfReady(ctx, header.Hash, header.Number, header.Time) } -func (s *Service) checkHeaderRange(ctx context.Context, start, end uint64, headersMap map[uint64]*gethtypes.Header, +func (s *Service) checkHeaderRange(ctx context.Context, start, end uint64, headersMap map[uint64]*types.HeaderInfo, requestHeaders func(uint64, uint64) error) error { for i := start; i <= end; i++ { if !s.chainStartData.Chainstarted { diff --git a/beacon-chain/execution/log_processing_test.go b/beacon-chain/execution/log_processing_test.go index 26b96d1b7c14..620eaefd856f 100644 --- a/beacon-chain/execution/log_processing_test.go +++ b/beacon-chain/execution/log_processing_test.go @@ -308,6 +308,7 @@ func TestProcessETH2GenesisLog(t *testing.T) { require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") web3Service = setDefaultMocks(web3Service) web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) + web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} require.NoError(t, err) params.SetupTestConfigCleanup(t) bConfig := params.MinimalSpecConfig().Copy() @@ -403,7 +404,6 @@ func TestProcessETH2GenesisLog_CorrectNumOfDeposits(t *testing.T) { require.NoError(t, err) web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} web3Service.httpLogger = testAcc.Backend - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.latestEth1Data.LastRequestedBlock = 0 web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64() web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time() @@ -501,7 +501,6 @@ func TestProcessETH2GenesisLog_LargePeriodOfNoLogs(t *testing.T) { require.NoError(t, err) web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} web3Service.httpLogger = testAcc.Backend - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.latestEth1Data.LastRequestedBlock = 0 web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64() web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time() @@ -613,7 +612,6 @@ func newPowchainService(t *testing.T, eth1Backend *mock.TestAccount, beaconDB db require.NoError(t, err) web3Service.rpcClient = &mockExecution.RPCClient{Backend: eth1Backend.Backend} - web3Service.eth1DataFetcher = &goodFetcher{backend: eth1Backend.Backend} web3Service.httpLogger = &goodLogger{backend: eth1Backend.Backend} params.SetupTestConfigCleanup(t) bConfig := params.MinimalSpecConfig().Copy() diff --git a/beacon-chain/execution/rpc_connection.go b/beacon-chain/execution/rpc_connection.go index 8b8375d7409f..250d7e4d41da 100644 --- a/beacon-chain/execution/rpc_connection.go +++ b/beacon-chain/execution/rpc_connection.go @@ -26,7 +26,6 @@ func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpo fetcher := ethclient.NewClient(client) s.rpcClient = client s.httpLogger = fetcher - s.eth1DataFetcher = fetcher depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, fetcher) if err != nil { diff --git a/beacon-chain/execution/service.go b/beacon-chain/execution/service.go index 0a3fe3459e29..9043a50b363c 100644 --- a/beacon-chain/execution/service.go +++ b/beacon-chain/execution/service.go @@ -16,7 +16,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - gethTypes "github.com/ethereum/go-ethereum/core/types" gethRPC "github.com/ethereum/go-ethereum/rpc" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" @@ -97,14 +96,6 @@ type Chain interface { POWBlockFetcher } -// RPCDataFetcher defines a subset of methods conformed to by ETH1.0 RPC clients for -// fetching eth1 data from the clients. -type RPCDataFetcher interface { - Close() - HeaderByNumber(ctx context.Context, number *big.Int) (*gethTypes.Header, error) - HeaderByHash(ctx context.Context, hash common.Hash) (*gethTypes.Header, error) -} - // RPCClient defines the rpc methods required to interact with the eth1 node. type RPCClient interface { Close() @@ -154,7 +145,6 @@ type Service struct { cancel context.CancelFunc eth1HeadTicker *time.Ticker httpLogger bind.ContractFilterer - eth1DataFetcher RPCDataFetcher rpcClient RPCClient headerCache *headerCache // cache to store block hash/block height. latestEth1Data *ethpb.LatestETH1Data @@ -264,9 +254,6 @@ func (s *Service) Stop() error { if s.rpcClient != nil { s.rpcClient.Close() } - if s.eth1DataFetcher != nil { - s.eth1DataFetcher.Close() - } return nil } @@ -398,36 +385,35 @@ func (s *Service) initDepositCaches(ctx context.Context, ctrs []*ethpb.DepositCo // processBlockHeader adds a newly observed eth1 block to the block cache and // updates the latest blockHeight, blockHash, and blockTime properties of the service. -func (s *Service) processBlockHeader(header *gethTypes.Header) { +func (s *Service) processBlockHeader(header *types.HeaderInfo) { defer safelyHandlePanic() blockNumberGauge.Set(float64(header.Number.Int64())) s.latestEth1DataLock.Lock() s.latestEth1Data.BlockHeight = header.Number.Uint64() - s.latestEth1Data.BlockHash = header.Hash().Bytes() + s.latestEth1Data.BlockHash = header.Hash.Bytes() s.latestEth1Data.BlockTime = header.Time s.latestEth1DataLock.Unlock() log.WithFields(logrus.Fields{ "blockNumber": s.latestEth1Data.BlockHeight, "blockHash": hexutil.Encode(s.latestEth1Data.BlockHash), - "difficulty": header.Difficulty.String(), }).Debug("Latest eth1 chain event") } // batchRequestHeaders requests the block range specified in the arguments. Instead of requesting // each block in one call, it batches all requests into a single rpc call. -func (s *Service) batchRequestHeaders(startBlock, endBlock uint64) ([]*gethTypes.Header, error) { +func (s *Service) batchRequestHeaders(startBlock, endBlock uint64) ([]*types.HeaderInfo, error) { if startBlock > endBlock { return nil, fmt.Errorf("start block height %d cannot be > end block height %d", startBlock, endBlock) } requestRange := (endBlock - startBlock) + 1 elems := make([]gethRPC.BatchElem, 0, requestRange) - headers := make([]*gethTypes.Header, 0, requestRange) + headers := make([]*types.HeaderInfo, 0, requestRange) errs := make([]error, 0, requestRange) if requestRange == 0 { return headers, nil } for i := startBlock; i <= endBlock; i++ { - header := &gethTypes.Header{} + header := &types.HeaderInfo{} err := error(nil) elems = append(elems, gethRPC.BatchElem{ Method: "eth_getBlockByNumber", @@ -523,7 +509,7 @@ func (s *Service) initPOWService() { return default: ctx := s.ctx - header, err := s.eth1DataFetcher.HeaderByNumber(ctx, nil) + header, err := s.HeaderByNumber(ctx, nil) if err != nil { s.retryExecutionClientConnection(ctx, err) errorLogger(err, "Unable to retrieve latest execution client header") @@ -532,7 +518,7 @@ func (s *Service) initPOWService() { s.latestEth1DataLock.Lock() s.latestEth1Data.BlockHeight = header.Number.Uint64() - s.latestEth1Data.BlockHash = header.Hash().Bytes() + s.latestEth1Data.BlockHash = header.Hash.Bytes() s.latestEth1Data.BlockTime = header.Time s.latestEth1DataLock.Unlock() @@ -562,7 +548,7 @@ func (s *Service) initPOWService() { // In the event our provided chainstart data references a non-existent block hash, // we assume the genesis block to be 0. if genHash != [32]byte{} { - genHeader, err := s.eth1DataFetcher.HeaderByHash(ctx, genHash) + genHeader, err := s.HeaderByHash(ctx, genHash) if err != nil { s.retryExecutionClientConnection(ctx, err) errorLogger(err, "Unable to retrieve proof-of-stake genesis block data") @@ -601,7 +587,7 @@ func (s *Service) run(done <-chan struct{}) { log.Debug("Context closed, exiting goroutine") return case <-s.eth1HeadTicker.C: - head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil) + head, err := s.HeaderByNumber(s.ctx, nil) if err != nil { s.pollConnectionStatus(s.ctx) log.WithError(err).Debug("Could not fetch latest eth1 header") diff --git a/beacon-chain/execution/service_test.go b/beacon-chain/execution/service_test.go index ca6c616830bd..85182c88d380 100644 --- a/beacon-chain/execution/service_test.go +++ b/beacon-chain/execution/service_test.go @@ -1,9 +1,7 @@ package execution import ( - "bytes" "context" - "fmt" "math/big" "strings" "testing" @@ -20,6 +18,7 @@ import ( "github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositcache" dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing" mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing" + "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" doublylinkedtree "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/doubly-linked-tree" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/stategen" "github.com/prysmaticlabs/prysm/v3/config/params" @@ -80,56 +79,6 @@ func (g *goodNotifier) StateFeed() *event.Feed { return g.MockStateFeed } -type goodFetcher struct { - backend *backends.SimulatedBackend - blockNumMap map[uint64]*gethTypes.Header -} - -func (_ *goodFetcher) Close() {} - -func (g *goodFetcher) HeaderByHash(_ context.Context, hash common.Hash) (*gethTypes.Header, error) { - if bytes.Equal(hash.Bytes(), common.BytesToHash([]byte{0}).Bytes()) { - return nil, fmt.Errorf("expected block hash to be nonzero %v", hash) - } - if g.backend == nil { - return &gethTypes.Header{ - Number: big.NewInt(0), - }, nil - } - header := g.backend.Blockchain().GetHeaderByHash(hash) - if header == nil { - return nil, errors.New("nil header returned") - } - return header, nil - -} - -func (g *goodFetcher) HeaderByNumber(_ context.Context, number *big.Int) (*gethTypes.Header, error) { - if g.backend == nil && g.blockNumMap == nil { - return &gethTypes.Header{ - Number: big.NewInt(15), - Time: 150, - }, nil - } - if g.blockNumMap != nil { - return g.blockNumMap[number.Uint64()], nil - } - var header *gethTypes.Header - if number == nil { - header = g.backend.Blockchain().CurrentHeader() - } else { - header = g.backend.Blockchain().GetHeaderByNumber(number.Uint64()) - } - if header == nil { - return nil, errors.New("nil header returned") - } - return header, nil -} - -func (_ *goodFetcher) SyncProgress(_ context.Context) (*ethereum.SyncProgress, error) { - return nil, nil -} - var depositsReqForChainStart = 64 func TestStart_OK(t *testing.T) { @@ -229,7 +178,6 @@ func TestService_Eth1Synced(t *testing.T) { web3Service = setDefaultMocks(web3Service) web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) require.NoError(t, err) - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} currTime := testAcc.Backend.Blockchain().CurrentHeader().Time now := time.Now() @@ -261,7 +209,7 @@ func TestFollowBlock_OK(t *testing.T) { params.OverrideBeaconConfig(conf) web3Service = setDefaultMocks(web3Service) - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} + web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64() // process follow_distance blocks for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ { @@ -330,7 +278,7 @@ func TestHandlePanic_OK(t *testing.T) { ) require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") // nil eth1DataFetcher would panic if cached value not used - web3Service.eth1DataFetcher = nil + web3Service.rpcClient = nil web3Service.processBlockHeader(nil) require.LogsContain(t, hook, "Panicked when handling data from ETH 1.0 Chain!") } @@ -371,7 +319,6 @@ func TestLogTillGenesis_OK(t *testing.T) { require.NoError(t, err) web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.httpLogger = testAcc.Backend for i := 0; i < 30; i++ { testAcc.Backend.Commit() @@ -506,7 +453,6 @@ func TestNewService_EarliestVotingBlock(t *testing.T) { WithDatabase(beaconDB), ) require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") - web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} // simulated backend sets eth1 block // time as 10 seconds params.SetupTestConfigCleanup(t) @@ -800,18 +746,23 @@ func TestService_CacheBlockHeaders(t *testing.T) { func TestService_FollowBlock(t *testing.T) { followTime := params.BeaconConfig().Eth1FollowDistance * params.BeaconConfig().SecondsPerETH1Block followTime += 10000 - bMap := make(map[uint64]*gethTypes.Header) + bMap := make(map[uint64]*types.HeaderInfo) for i := uint64(3000); i > 0; i-- { - bMap[i] = &gethTypes.Header{ + h := &gethTypes.Header{ Number: big.NewInt(int64(i)), Time: followTime + (i * 40), } + bMap[i] = &types.HeaderInfo{ + Number: h.Number, + Hash: h.Hash(), + Time: h.Time, + } } s := &Service{ - cfg: &config{eth1HeaderReqLimit: 1000}, - eth1DataFetcher: &goodFetcher{blockNumMap: bMap}, - headerCache: newHeaderCache(), - latestEth1Data: ðpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000}, + cfg: &config{eth1HeaderReqLimit: 1000}, + rpcClient: &mockExecution.RPCClient{BlockNumMap: bMap}, + headerCache: newHeaderCache(), + latestEth1Data: ðpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000}, } h, err := s.followedBlockHeight(context.Background()) assert.NoError(t, err) @@ -839,7 +790,7 @@ func (s *slowRPCClient) BatchCall(b []rpc.BatchElem) error { return err } h := &gethTypes.Header{Number: num} - *e.Result.(*gethTypes.Header) = *h + *e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Hash: h.Hash()} } return nil } diff --git a/beacon-chain/execution/testing/mock_execution_chain.go b/beacon-chain/execution/testing/mock_execution_chain.go index e99e8ccad5ee..b0fc49b21f36 100644 --- a/beacon-chain/execution/testing/mock_execution_chain.go +++ b/beacon-chain/execution/testing/mock_execution_chain.go @@ -14,6 +14,7 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" gethTypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v3/async/event" "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" @@ -140,12 +141,92 @@ func (m *Chain) ETH1ConnectionErrors() []error { // RPCClient defines the mock rpc client. type RPCClient struct { - Backend *backends.SimulatedBackend + Backend *backends.SimulatedBackend + BlockNumMap map[uint64]*types.HeaderInfo } func (*RPCClient) Close() {} -func (*RPCClient) CallContext(_ context.Context, _ interface{}, _ string, _ ...interface{}) error { +func (r *RPCClient) CallContext(ctx context.Context, obj interface{}, methodName string, args ...interface{}) error { + if r.BlockNumMap != nil && methodName == "eth_getBlockByNumber" { + val, ok := args[0].(string) + if !ok { + return errors.Errorf("wrong argument type provided: %T", args[0]) + } + num, err := hexutil.DecodeBig(val) + if err != nil { + return err + } + b := r.BlockNumMap[num.Uint64()] + assertedObj, ok := obj.(**types.HeaderInfo) + if !ok { + return errors.Errorf("wrong argument type provided: %T", obj) + } + *assertedObj = b + return nil + } + if r.Backend == nil && methodName == "eth_getBlockByNumber" { + h := &gethTypes.Header{ + Number: big.NewInt(15), + Time: 150, + } + assertedObj, ok := obj.(**types.HeaderInfo) + if !ok { + return errors.Errorf("wrong argument type provided: %T", obj) + } + *assertedObj = &types.HeaderInfo{ + Hash: h.Hash(), + Number: h.Number, + Time: h.Time, + } + return nil + } + switch methodName { + case "eth_getBlockByNumber": + val, ok := args[0].(string) + if !ok { + return errors.Errorf("wrong argument type provided: %T", args[0]) + } + var num *big.Int + var err error + if val != "latest" { + num, err = hexutil.DecodeBig(val) + if err != nil { + return err + } + } + h, err := r.Backend.HeaderByNumber(ctx, num) + if err != nil { + return err + } + assertedObj, ok := obj.(**types.HeaderInfo) + if !ok { + return errors.Errorf("wrong argument type provided: %T", obj) + } + *assertedObj = &types.HeaderInfo{ + Hash: h.Hash(), + Number: h.Number, + Time: h.Time, + } + case "eth_getBlockByHash": + val, ok := args[0].(common.Hash) + if !ok { + return errors.Errorf("wrong argument type provided: %T", args[0]) + } + h, err := r.Backend.HeaderByHash(ctx, val) + if err != nil { + return err + } + assertedObj, ok := obj.(**types.HeaderInfo) + if !ok { + return errors.Errorf("wrong argument type provided: %T", obj) + } + *assertedObj = &types.HeaderInfo{ + Hash: h.Hash(), + Number: h.Number, + Time: h.Time, + } + } return nil } @@ -164,7 +245,7 @@ func (r *RPCClient) BatchCall(b []rpc.BatchElem) error { if err != nil { return err } - *e.Result.(*gethTypes.Header) = *h + *e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Time: h.Time, Hash: h.Hash()} } return nil diff --git a/beacon-chain/execution/types/BUILD.bazel b/beacon-chain/execution/types/BUILD.bazel index cf4de15ad476..bd0f2a9afe56 100644 --- a/beacon-chain/execution/types/BUILD.bazel +++ b/beacon-chain/execution/types/BUILD.bazel @@ -8,7 +8,7 @@ go_library( deps = [ "//encoding/bytesutil:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", - "@com_github_ethereum_go_ethereum//core/types:go_default_library", + "@com_github_ethereum_go_ethereum//common/hexutil:go_default_library", ], ) @@ -17,7 +17,7 @@ go_test( srcs = ["eth1_types_test.go"], embed = [":go_default_library"], deps = [ + "//testing/assert:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library", - "@com_github_ethereum_go_ethereum//core/types:go_default_library", ], ) diff --git a/beacon-chain/execution/types/eth1_types.go b/beacon-chain/execution/types/eth1_types.go index 22d06588d5d2..e083215fddb7 100644 --- a/beacon-chain/execution/types/eth1_types.go +++ b/beacon-chain/execution/types/eth1_types.go @@ -1,33 +1,20 @@ package types import ( + "encoding/json" "errors" "math/big" "github.com/ethereum/go-ethereum/common" - gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" ) // HeaderInfo specifies the block header information in the ETH 1.0 chain. type HeaderInfo struct { - Number *big.Int - Hash common.Hash - Time uint64 -} - -// HeaderToHeaderInfo converts an eth1 header to a header metadata type. -func HeaderToHeaderInfo(hdr *gethTypes.Header) (*HeaderInfo, error) { - if hdr.Number == nil { - // A nil number will panic when calling *big.Int.Set(...) - return nil, errors.New("cannot convert block header with nil block number") - } - - return &HeaderInfo{ - Hash: hdr.Hash(), - Number: new(big.Int).Set(hdr.Number), - Time: hdr.Time, - }, nil + Number *big.Int `json:"number"` + Hash common.Hash `json:"hash"` + Time uint64 `json:"timestamp"` } // Copy sends out a copy of the current header info. @@ -38,3 +25,44 @@ func (h *HeaderInfo) Copy() *HeaderInfo { Time: h.Time, } } + +// MarshalJSON marshals as JSON. +func (h *HeaderInfo) MarshalJSON() ([]byte, error) { + type HeaderInfoJson struct { + Number *hexutil.Big `json:"number"` + Hash common.Hash `json:"hash"` + Time hexutil.Uint64 `json:"timestamp"` + } + var enc HeaderInfoJson + + enc.Number = (*hexutil.Big)(h.Number) + enc.Hash = h.Hash + enc.Time = hexutil.Uint64(h.Time) + return json.Marshal(enc) +} + +// UnmarshalJSON unmarshals from JSON. +func (h *HeaderInfo) UnmarshalJSON(data []byte) error { + type HeaderInfoJson struct { + Number *hexutil.Big `json:"number"` + Hash *common.Hash `json:"hash"` + Time *hexutil.Uint64 `json:"timestamp"` + } + var dec HeaderInfoJson + if err := json.Unmarshal(data, &dec); err != nil { + return err + } + if dec.Time == nil { + return errors.New("missing required field 'timestamp'") + } + h.Time = uint64(*dec.Time) + if dec.Number == nil { + return errors.New("missing required field 'number'") + } + h.Number = (*big.Int)(dec.Number) + if dec.Hash == nil { + return errors.New("missing required field 'hash'") + } + h.Hash = *dec.Hash + return nil +} diff --git a/beacon-chain/execution/types/eth1_types_test.go b/beacon-chain/execution/types/eth1_types_test.go index d2a75668a5a5..c8236820298f 100644 --- a/beacon-chain/execution/types/eth1_types_test.go +++ b/beacon-chain/execution/types/eth1_types_test.go @@ -1,53 +1,68 @@ package types import ( + "math" "math/big" "reflect" "testing" "github.com/ethereum/go-ethereum/common" - gethTypes "github.com/ethereum/go-ethereum/core/types" + "github.com/prysmaticlabs/prysm/v3/testing/assert" ) -func Test_headerToHeaderInfo(t *testing.T) { - type args struct { - hdr *gethTypes.Header - } +func TestRoundtrip_HeaderInfo(t *testing.T) { tests := []struct { name string - args args - want *HeaderInfo + hInfo HeaderInfo wantErr bool }{ { - name: "OK", - args: args{hdr: &gethTypes.Header{ - Number: big.NewInt(500), - Time: 2345, - }}, - want: &HeaderInfo{ - Number: big.NewInt(500), + name: "normal header object", + hInfo: HeaderInfo{ + Number: big.NewInt(1000), Hash: common.Hash{239, 10, 13, 71, 156, 192, 23, 93, 73, 154, 255, 209, 163, 204, 129, 12, 179, 183, 65, 70, 205, 200, 57, 12, 17, 211, 209, 4, 104, 133, 73, 86}, - Time: 2345, + Time: 1000, + }, + wantErr: false, + }, + { + name: "large header object", + hInfo: HeaderInfo{ + Number: big.NewInt(10023982389238920), + Hash: common.Hash{192, 19, 18, 71, 156, 239, 23, 93, 73, 17, 255, 209, 163, 204, 129, 12, 179, 129, 65, 70, 209, 200, 57, 12, 17, 211, 209, 4, 104, 57, 73, 86}, + Time: math.MaxUint64, }, + wantErr: false, }, { - name: "nil number", - args: args{hdr: &gethTypes.Header{ - Time: 2345, - }}, + name: "missing number", + hInfo: HeaderInfo{ + Hash: common.Hash{192, 19, 18, 71, 156, 239, 23, 93, 73, 17, 255, 209, 163, 204, 129, 12, 179, 129, 65, 70, 209, 200, 57, 12, 17, 211, 209, 4, 104, 57, 73, 86}, + Time: math.MaxUint64, + }, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := HeaderToHeaderInfo(tt.args.hdr) + h := &HeaderInfo{ + Number: tt.hInfo.Number, + Hash: tt.hInfo.Hash, + Time: tt.hInfo.Time, + } + recv, err := h.MarshalJSON() + assert.NoError(t, err) + newH := &HeaderInfo{} + err = newH.UnmarshalJSON(recv) if (err != nil) != tt.wantErr { - t.Errorf("headerToHeaderInfo() error = %v, wantErr %v", err, tt.wantErr) + t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantErr { return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("headerToHeaderInfo() got = %v, want %v", got, tt.want) + if !reflect.DeepEqual(*newH, tt.hInfo) { + t.Errorf("MarshalJSON() got = %v, want %v", newH, tt.hInfo) } }) }