Remove Geth Bindings From Prysm (#11586)

* check in changes

* gaz

* preston's review

* comment

* fix up

* remove test

* gaz

* preston's review

* fix it
This commit is contained in:
Nishant Das
2022-11-17 17:16:19 +08:00
committed by GitHub
parent f02ad8a68b
commit 08d63a0cd0
16 changed files with 373 additions and 204 deletions

View File

@@ -129,7 +129,6 @@ go_test(
"@com_github_ethereum_go_ethereum//core/beacon:go_default_library", "@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//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//rpc: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_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library", "@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library", "@com_github_prometheus_client_golang//prometheus:go_default_library",

View File

@@ -6,7 +6,6 @@ import (
"sync" "sync"
"github.com/ethereum/go-ethereum/common" "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"
"github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" "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 // 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 // the desired behavior is that the blocks with the highest header number should
// be present in the cache. // be present in the cache.
func (c *headerCache) AddHeader(hdr *gethTypes.Header) error { func (c *headerCache) AddHeader(hdr *types.HeaderInfo) error {
c.lock.Lock() c.lock.Lock()
defer c.lock.Unlock() defer c.lock.Unlock()
hInfo, err := types.HeaderToHeaderInfo(hdr) hInfo := hdr.Copy()
if err != nil {
return err
}
if err := c.hashCache.AddIfNotPresent(hInfo); err != nil { if err := c.hashCache.AddIfNotPresent(hInfo); err != nil {
return err return err

View File

@@ -5,8 +5,8 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "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/beacon-chain/execution/types"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v3/testing/assert" "github.com/prysmaticlabs/prysm/v3/testing/assert"
"github.com/prysmaticlabs/prysm/v3/testing/require" "github.com/prysmaticlabs/prysm/v3/testing/require"
) )
@@ -44,34 +44,30 @@ func TestHeightKeyFn_InvalidObj(t *testing.T) {
func TestBlockCache_byHash(t *testing.T) { func TestBlockCache_byHash(t *testing.T) {
cache := newHeaderCache() cache := newHeaderCache()
header := &gethTypes.Header{ header := &types.HeaderInfo{
ParentHash: common.HexToHash("0x12345"), Number: big.NewInt(55),
Number: big.NewInt(55),
} }
exists, _, err := cache.HeaderInfoByHash(header.Hash)
exists, _, err := cache.HeaderInfoByHash(header.Hash())
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, false, exists, "Expected block info not to exist in empty cache") assert.Equal(t, false, exists, "Expected block info not to exist in empty cache")
err = cache.AddHeader(header) err = cache.AddHeader(header)
require.NoError(t, err) require.NoError(t, err)
exists, fetchedInfo, err := cache.HeaderInfoByHash(header.Hash()) exists, fetchedInfo, err := cache.HeaderInfoByHash(header.Hash)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, true, exists, "Expected headerInfo to exist") 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, 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) { func TestBlockCache_byHeight(t *testing.T) {
cache := newHeaderCache() cache := newHeaderCache()
header := &gethTypes.Header{ header := &types.HeaderInfo{
ParentHash: common.HexToHash("0x12345"), Number: big.NewInt(55),
Number: big.NewInt(55),
} }
exists, _, err := cache.HeaderInfoByHeight(header.Number) exists, _, err := cache.HeaderInfoByHeight(header.Number)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, false, exists, "Expected block info not to exist in empty cache") 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, 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, 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() cache := newHeaderCache()
for i := int64(0); i < int64(maxCacheSize+10); i++ { for i := int64(0); i < int64(maxCacheSize+10); i++ {
header := &gethTypes.Header{ header := &types.HeaderInfo{
Number: big.NewInt(i), Number: big.NewInt(i),
Hash: common.Hash(bytesutil.ToBytes32(bytesutil.Bytes32(uint64(i)))),
} }
err := cache.AddHeader(header) err := cache.AddHeader(header)
require.NoError(t, err) require.NoError(t, err)

View File

@@ -35,7 +35,7 @@ func (s *Service) BlockExists(ctx context.Context, hash common.Hash) (bool, *big
return true, hdrInfo.Number, nil return true, hdrInfo.Number, nil
} }
span.AddAttributes(trace.BoolAttribute("blockCacheHit", false)) span.AddAttributes(trace.BoolAttribute("blockCacheHit", false))
header, err := s.eth1DataFetcher.HeaderByHash(ctx, hash) header, err := s.HeaderByHash(ctx, hash)
if err != nil { if err != nil {
return false, big.NewInt(0), errors.Wrap(err, "could not query block with given hash") 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)) span.AddAttributes(trace.BoolAttribute("headerCacheHit", false))
if s.eth1DataFetcher == nil { if s.rpcClient == nil {
err := errors.New("nil eth1DataFetcher") err := errors.New("nil rpc client")
tracing.AnnotateError(span, err) tracing.AnnotateError(span, err)
return [32]byte{}, err return [32]byte{}, err
} }
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, height) header, err := s.HeaderByNumber(ctx, height)
if err != nil { if err != nil {
return [32]byte{}, errors.Wrap(err, fmt.Sprintf("could not query header with height %d", height.Uint64())) 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 { if err := s.headerCache.AddHeader(header); err != nil {
return [32]byte{}, err return [32]byte{}, err
} }
return header.Hash(), nil return header.Hash, nil
} }
// BlockTimeByHeight fetches an eth1 block timestamp by its height. // BlockTimeByHeight fetches an eth1 block timestamp by its height.
func (s *Service) BlockTimeByHeight(ctx context.Context, height *big.Int) (uint64, error) { func (s *Service) BlockTimeByHeight(ctx context.Context, height *big.Int) (uint64, error) {
ctx, span := trace.StartSpan(ctx, "powchain.BlockTimeByHeight") ctx, span := trace.StartSpan(ctx, "powchain.BlockTimeByHeight")
defer span.End() defer span.End()
if s.eth1DataFetcher == nil { if s.rpcClient == nil {
err := errors.New("nil eth1DataFetcher") err := errors.New("nil rpc client")
tracing.AnnotateError(span, err) tracing.AnnotateError(span, err)
return 0, err return 0, err
} }
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, height) header, err := s.HeaderByNumber(ctx, height)
if err != nil { if err != nil {
return 0, errors.Wrap(err, fmt.Sprintf("could not query block with height %d", height.Uint64())) 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 return nil, err
} }
if !exists { if !exists {
blk, err := s.eth1DataFetcher.HeaderByNumber(ctx, bn) hdr, err := s.HeaderByNumber(ctx, bn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if blk == nil { if hdr == nil {
return nil, errors.Errorf("header with the number %d does not exist", bNum) return nil, errors.Errorf("header with the number %d does not exist", bNum)
} }
if err := s.headerCache.AddHeader(blk); err != nil { if err := s.headerCache.AddHeader(hdr); err != nil {
return nil, err
}
info, err = types.HeaderToHeaderInfo(blk)
if err != nil {
return nil, err return nil, err
} }
info = hdr
} }
return info, nil return info, nil
} }

View File

@@ -9,9 +9,9 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gethTypes "github.com/ethereum/go-ethereum/core/types" gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/trie"
dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing" dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/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" contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit"
"github.com/prysmaticlabs/prysm/v3/contracts/deposit/mock" "github.com/prysmaticlabs/prysm/v3/contracts/deposit/mock"
"github.com/prysmaticlabs/prysm/v3/testing/assert" "github.com/prysmaticlabs/prysm/v3/testing/assert"
@@ -19,7 +19,6 @@ import (
) )
func setDefaultMocks(service *Service) *Service { func setDefaultMocks(service *Service) *Service {
service.eth1DataFetcher = &goodFetcher{}
service.httpLogger = &goodLogger{} service.httpLogger = &goodLogger{}
service.cfg.stateNotifier = &goodNotifier{} service.cfg.stateNotifier = &goodNotifier{}
return service return service
@@ -44,7 +43,6 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
require.NoError(t, err) require.NoError(t, err)
@@ -57,7 +55,7 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
<-exitRoutine <-exitRoutine
}() }()
header, err := web3Service.eth1DataFetcher.HeaderByNumber(web3Service.ctx, nil) header, err := web3Service.HeaderByNumber(web3Service.ctx, nil)
require.NoError(t, err) require.NoError(t, err)
tickerChan := make(chan time.Time) tickerChan := make(chan time.Time)
@@ -67,7 +65,7 @@ func TestLatestMainchainInfo_OK(t *testing.T) {
exitRoutine <- true exitRoutine <- true
assert.Equal(t, web3Service.latestEth1Data.BlockHeight, header.Number.Uint64()) 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) 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") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.rpcClient = &mockExecution.RPCClient{}
ctx := context.Background() ctx := context.Background()
header := &gethTypes.Header{ 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") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.eth1DataFetcher = nil web3Service.rpcClient = nil
ctx := context.Background() ctx := context.Background()
_, err = web3Service.BlockHashByHeight(ctx, big.NewInt(0)) _, 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) { func TestBlockExists_ValidHash(t *testing.T) {
beaconDB := dbutil.SetupDB(t) beaconDB := dbutil.SetupDB(t)
testAcc, err := mock.Setup()
require.NoError(t, err, "Unable to set up simulated backend")
server, endpoint, err := mockExecution.SetupRPCServer() server, endpoint, err := mockExecution.SetupRPCServer()
require.NoError(t, err) require.NoError(t, err)
t.Cleanup(func() { 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") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
block := gethTypes.NewBlock( testAcc.Backend.Commit()
&gethTypes.Header{ block, err := testAcc.Backend.BlockByNumber(context.Background(), big.NewInt(0))
Number: big.NewInt(0), assert.NoError(t, err)
},
[]*gethTypes.Transaction{},
[]*gethTypes.Header{},
[]*gethTypes.Receipt{},
new(trie.Trie),
)
exists, height, err := web3Service.BlockExists(context.Background(), block.Hash()) exists, height, err := web3Service.BlockExists(context.Background(), block.Hash())
require.NoError(t, err, "Could not get block hash with given height") require.NoError(t, err, "Could not get block hash with given height")
@@ -191,17 +186,15 @@ func TestBlockExists_UsesCachedBlockInfo(t *testing.T) {
WithDatabase(beaconDB), WithDatabase(beaconDB),
) )
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") 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), Number: big.NewInt(0),
} }
err = web3Service.headerCache.AddHeader(header) err = web3Service.headerCache.AddHeader(header)
require.NoError(t, err) 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.NoError(t, err, "Could not get block hash with given height")
require.Equal(t, true, exists) require.Equal(t, true, exists)
require.Equal(t, 0, height.Cmp(header.Number)) require.Equal(t, 0, height.Cmp(header.Number))
@@ -222,7 +215,7 @@ func TestService_BlockNumberByTimestamp(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
for i := 0; i < 200; i++ { for i := 0; i < 200; i++ {
testAcc.Backend.Commit() testAcc.Backend.Commit()
@@ -254,7 +247,7 @@ func TestService_BlockNumberByTimestampLessTargetTime(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
for i := 0; i < 200; i++ { for i := 0; i < 200; i++ {
testAcc.Backend.Commit() testAcc.Backend.Commit()
@@ -292,7 +285,7 @@ func TestService_BlockNumberByTimestampMoreTargetTime(t *testing.T) {
) )
require.NoError(t, err) require.NoError(t, err)
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
for i := 0; i < 200; i++ { for i := 0; i < 200; i++ {
testAcc.Backend.Commit() 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") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.eth1DataFetcher = nil web3Service.rpcClient = nil
ctx := context.Background() ctx := context.Background()
_, err = web3Service.BlockTimeByHeight(ctx, big.NewInt(0)) _, err = web3Service.BlockTimeByHeight(ctx, big.NewInt(0))
require.ErrorContains(t, "nil eth1DataFetcher", err) require.ErrorContains(t, "nil rpc client", err)
} }

View File

@@ -8,11 +8,13 @@ import (
"strings" "strings"
"time" "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gethRPC "github.com/ethereum/go-ethereum/rpc" gethRPC "github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams"
"github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" "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 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 // ReconstructFullBellatrixBlock takes in a blinded beacon block and reconstructs
// a beacon block with a full execution payload via the engine API. // a beacon block with a full execution payload via the engine API.
func (s *Service) ReconstructFullBellatrixBlock( func (s *Service) ReconstructFullBellatrixBlock(
@@ -610,3 +632,22 @@ func buildEmptyExecutionPayload() *pb.ExecutionPayload {
ExtraData: make([]byte, 0), 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)
}

View File

@@ -11,10 +11,12 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gethtypes "github.com/ethereum/go-ethereum/core/types" gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
gethRPC "github.com/ethereum/go-ethereum/rpc"
"github.com/holiman/uint256" "github.com/holiman/uint256"
"github.com/pkg/errors" "github.com/pkg/errors"
mocks "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/testing" 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/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
pb "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" 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/require"
"github.com/prysmaticlabs/prysm/v3/testing/util" "github.com/prysmaticlabs/prysm/v3/testing/util"
"google.golang.org/protobuf/proto" "google.golang.org/protobuf/proto"
@@ -36,6 +39,18 @@ var (
_ = EngineCaller(&mocks.EngineClient{}) _ = 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) { func TestClient_IPC(t *testing.T) {
server := newTestIPCServer(t) server := newTestIPCServer(t)
defer server.Stop() 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{} type testEngineService struct{}
func (*testEngineService) NoArgsRets() {} func (*testEngineService) NoArgsRets() {}

View File

@@ -17,6 +17,7 @@ import (
statefeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/state" statefeed "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers"
coreState "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/transition" 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" statenative "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/config/params"
contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit" contracts "github.com/prysmaticlabs/prysm/v3/contracts/deposit"
@@ -284,7 +285,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
currentBlockNum = deploymentBlock currentBlockNum = deploymentBlock
} }
// To store all blocks. // To store all blocks.
headersMap := make(map[uint64]*gethtypes.Header) headersMap := make(map[uint64]*types.HeaderInfo)
rawLogCount, err := s.depositContractCaller.GetDepositCount(&bind.CallOpts{}) rawLogCount, err := s.depositContractCaller.GetDepositCount(&bind.CallOpts{})
if err != nil { if err != nil {
return err return err
@@ -341,7 +342,7 @@ func (s *Service) processPastLogs(ctx context.Context) error {
return nil 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 // Batch request the desired headers and store them in a
// map for quick access. // map for quick access.
requestHeaders := func(startBlk uint64, endBlk uint64) error { requestHeaders := func(startBlk uint64, endBlk uint64) error {
@@ -499,11 +500,11 @@ func (s *Service) processChainStartFromBlockNum(ctx context.Context, blkNum *big
return nil return nil
} }
func (s *Service) processChainStartFromHeader(ctx context.Context, header *gethtypes.Header) { func (s *Service) processChainStartFromHeader(ctx context.Context, header *types.HeaderInfo) {
s.processChainStartIfReady(ctx, header.Hash(), header.Number, header.Time) 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 { requestHeaders func(uint64, uint64) error) error {
for i := start; i <= end; i++ { for i := start; i <= end; i++ {
if !s.chainStartData.Chainstarted { if !s.chainStartData.Chainstarted {

View File

@@ -308,6 +308,7 @@ func TestProcessETH2GenesisLog(t *testing.T) {
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
require.NoError(t, err) require.NoError(t, err)
params.SetupTestConfigCleanup(t) params.SetupTestConfigCleanup(t)
bConfig := params.MinimalSpecConfig().Copy() bConfig := params.MinimalSpecConfig().Copy()
@@ -403,7 +404,6 @@ func TestProcessETH2GenesisLog_CorrectNumOfDeposits(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
web3Service.httpLogger = testAcc.Backend web3Service.httpLogger = testAcc.Backend
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
web3Service.latestEth1Data.LastRequestedBlock = 0 web3Service.latestEth1Data.LastRequestedBlock = 0
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64() web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time() web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time()
@@ -501,7 +501,6 @@ func TestProcessETH2GenesisLog_LargePeriodOfNoLogs(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
web3Service.httpLogger = testAcc.Backend web3Service.httpLogger = testAcc.Backend
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
web3Service.latestEth1Data.LastRequestedBlock = 0 web3Service.latestEth1Data.LastRequestedBlock = 0
web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64() web3Service.latestEth1Data.BlockHeight = testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
web3Service.latestEth1Data.BlockTime = testAcc.Backend.Blockchain().CurrentBlock().Time() 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) require.NoError(t, err)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: eth1Backend.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: eth1Backend.Backend}
web3Service.eth1DataFetcher = &goodFetcher{backend: eth1Backend.Backend}
web3Service.httpLogger = &goodLogger{backend: eth1Backend.Backend} web3Service.httpLogger = &goodLogger{backend: eth1Backend.Backend}
params.SetupTestConfigCleanup(t) params.SetupTestConfigCleanup(t)
bConfig := params.MinimalSpecConfig().Copy() bConfig := params.MinimalSpecConfig().Copy()

View File

@@ -26,7 +26,6 @@ func (s *Service) setupExecutionClientConnections(ctx context.Context, currEndpo
fetcher := ethclient.NewClient(client) fetcher := ethclient.NewClient(client)
s.rpcClient = client s.rpcClient = client
s.httpLogger = fetcher s.httpLogger = fetcher
s.eth1DataFetcher = fetcher
depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, fetcher) depositContractCaller, err := contracts.NewDepositContractCaller(s.cfg.depositContractAddr, fetcher)
if err != nil { if err != nil {

View File

@@ -16,7 +16,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gethTypes "github.com/ethereum/go-ethereum/core/types"
gethRPC "github.com/ethereum/go-ethereum/rpc" gethRPC "github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
@@ -97,14 +96,6 @@ type Chain interface {
POWBlockFetcher 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. // RPCClient defines the rpc methods required to interact with the eth1 node.
type RPCClient interface { type RPCClient interface {
Close() Close()
@@ -154,7 +145,6 @@ type Service struct {
cancel context.CancelFunc cancel context.CancelFunc
eth1HeadTicker *time.Ticker eth1HeadTicker *time.Ticker
httpLogger bind.ContractFilterer httpLogger bind.ContractFilterer
eth1DataFetcher RPCDataFetcher
rpcClient RPCClient rpcClient RPCClient
headerCache *headerCache // cache to store block hash/block height. headerCache *headerCache // cache to store block hash/block height.
latestEth1Data *ethpb.LatestETH1Data latestEth1Data *ethpb.LatestETH1Data
@@ -264,9 +254,6 @@ func (s *Service) Stop() error {
if s.rpcClient != nil { if s.rpcClient != nil {
s.rpcClient.Close() s.rpcClient.Close()
} }
if s.eth1DataFetcher != nil {
s.eth1DataFetcher.Close()
}
return nil 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 // processBlockHeader adds a newly observed eth1 block to the block cache and
// updates the latest blockHeight, blockHash, and blockTime properties of the service. // 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() defer safelyHandlePanic()
blockNumberGauge.Set(float64(header.Number.Int64())) blockNumberGauge.Set(float64(header.Number.Int64()))
s.latestEth1DataLock.Lock() s.latestEth1DataLock.Lock()
s.latestEth1Data.BlockHeight = header.Number.Uint64() s.latestEth1Data.BlockHeight = header.Number.Uint64()
s.latestEth1Data.BlockHash = header.Hash().Bytes() s.latestEth1Data.BlockHash = header.Hash.Bytes()
s.latestEth1Data.BlockTime = header.Time s.latestEth1Data.BlockTime = header.Time
s.latestEth1DataLock.Unlock() s.latestEth1DataLock.Unlock()
log.WithFields(logrus.Fields{ log.WithFields(logrus.Fields{
"blockNumber": s.latestEth1Data.BlockHeight, "blockNumber": s.latestEth1Data.BlockHeight,
"blockHash": hexutil.Encode(s.latestEth1Data.BlockHash), "blockHash": hexutil.Encode(s.latestEth1Data.BlockHash),
"difficulty": header.Difficulty.String(),
}).Debug("Latest eth1 chain event") }).Debug("Latest eth1 chain event")
} }
// batchRequestHeaders requests the block range specified in the arguments. Instead of requesting // 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. // 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 { if startBlock > endBlock {
return nil, fmt.Errorf("start block height %d cannot be > end block height %d", startBlock, endBlock) return nil, fmt.Errorf("start block height %d cannot be > end block height %d", startBlock, endBlock)
} }
requestRange := (endBlock - startBlock) + 1 requestRange := (endBlock - startBlock) + 1
elems := make([]gethRPC.BatchElem, 0, requestRange) elems := make([]gethRPC.BatchElem, 0, requestRange)
headers := make([]*gethTypes.Header, 0, requestRange) headers := make([]*types.HeaderInfo, 0, requestRange)
errs := make([]error, 0, requestRange) errs := make([]error, 0, requestRange)
if requestRange == 0 { if requestRange == 0 {
return headers, nil return headers, nil
} }
for i := startBlock; i <= endBlock; i++ { for i := startBlock; i <= endBlock; i++ {
header := &gethTypes.Header{} header := &types.HeaderInfo{}
err := error(nil) err := error(nil)
elems = append(elems, gethRPC.BatchElem{ elems = append(elems, gethRPC.BatchElem{
Method: "eth_getBlockByNumber", Method: "eth_getBlockByNumber",
@@ -523,7 +509,7 @@ func (s *Service) initPOWService() {
return return
default: default:
ctx := s.ctx ctx := s.ctx
header, err := s.eth1DataFetcher.HeaderByNumber(ctx, nil) header, err := s.HeaderByNumber(ctx, nil)
if err != nil { if err != nil {
s.retryExecutionClientConnection(ctx, err) s.retryExecutionClientConnection(ctx, err)
errorLogger(err, "Unable to retrieve latest execution client header") errorLogger(err, "Unable to retrieve latest execution client header")
@@ -532,7 +518,7 @@ func (s *Service) initPOWService() {
s.latestEth1DataLock.Lock() s.latestEth1DataLock.Lock()
s.latestEth1Data.BlockHeight = header.Number.Uint64() s.latestEth1Data.BlockHeight = header.Number.Uint64()
s.latestEth1Data.BlockHash = header.Hash().Bytes() s.latestEth1Data.BlockHash = header.Hash.Bytes()
s.latestEth1Data.BlockTime = header.Time s.latestEth1Data.BlockTime = header.Time
s.latestEth1DataLock.Unlock() s.latestEth1DataLock.Unlock()
@@ -562,7 +548,7 @@ func (s *Service) initPOWService() {
// In the event our provided chainstart data references a non-existent block hash, // In the event our provided chainstart data references a non-existent block hash,
// we assume the genesis block to be 0. // we assume the genesis block to be 0.
if genHash != [32]byte{} { if genHash != [32]byte{} {
genHeader, err := s.eth1DataFetcher.HeaderByHash(ctx, genHash) genHeader, err := s.HeaderByHash(ctx, genHash)
if err != nil { if err != nil {
s.retryExecutionClientConnection(ctx, err) s.retryExecutionClientConnection(ctx, err)
errorLogger(err, "Unable to retrieve proof-of-stake genesis block data") 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") log.Debug("Context closed, exiting goroutine")
return return
case <-s.eth1HeadTicker.C: case <-s.eth1HeadTicker.C:
head, err := s.eth1DataFetcher.HeaderByNumber(s.ctx, nil) head, err := s.HeaderByNumber(s.ctx, nil)
if err != nil { if err != nil {
s.pollConnectionStatus(s.ctx) s.pollConnectionStatus(s.ctx)
log.WithError(err).Debug("Could not fetch latest eth1 header") log.WithError(err).Debug("Could not fetch latest eth1 header")

View File

@@ -1,9 +1,7 @@
package execution package execution
import ( import (
"bytes"
"context" "context"
"fmt"
"math/big" "math/big"
"strings" "strings"
"testing" "testing"
@@ -20,6 +18,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositcache" "github.com/prysmaticlabs/prysm/v3/beacon-chain/cache/depositcache"
dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing" dbutil "github.com/prysmaticlabs/prysm/v3/beacon-chain/db/testing"
mockExecution "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/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" 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/beacon-chain/state/stategen"
"github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/config/params"
@@ -80,56 +79,6 @@ func (g *goodNotifier) StateFeed() *event.Feed {
return g.MockStateFeed 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 var depositsReqForChainStart = 64
func TestStart_OK(t *testing.T) { func TestStart_OK(t *testing.T) {
@@ -229,7 +178,6 @@ func TestService_Eth1Synced(t *testing.T) {
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend) web3Service.depositContractCaller, err = contracts.NewDepositContractCaller(testAcc.ContractAddr, testAcc.Backend)
require.NoError(t, err) require.NoError(t, err)
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
currTime := testAcc.Backend.Blockchain().CurrentHeader().Time currTime := testAcc.Backend.Blockchain().CurrentHeader().Time
now := time.Now() now := time.Now()
@@ -261,7 +209,7 @@ func TestFollowBlock_OK(t *testing.T) {
params.OverrideBeaconConfig(conf) params.OverrideBeaconConfig(conf)
web3Service = setDefaultMocks(web3Service) web3Service = setDefaultMocks(web3Service)
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64() baseHeight := testAcc.Backend.Blockchain().CurrentBlock().NumberU64()
// process follow_distance blocks // process follow_distance blocks
for i := 0; i < int(params.BeaconConfig().Eth1FollowDistance); i++ { 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") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
// nil eth1DataFetcher would panic if cached value not used // nil eth1DataFetcher would panic if cached value not used
web3Service.eth1DataFetcher = nil web3Service.rpcClient = nil
web3Service.processBlockHeader(nil) web3Service.processBlockHeader(nil)
require.LogsContain(t, hook, "Panicked when handling data from ETH 1.0 Chain!") 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) require.NoError(t, err)
web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend} web3Service.rpcClient = &mockExecution.RPCClient{Backend: testAcc.Backend}
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
web3Service.httpLogger = testAcc.Backend web3Service.httpLogger = testAcc.Backend
for i := 0; i < 30; i++ { for i := 0; i < 30; i++ {
testAcc.Backend.Commit() testAcc.Backend.Commit()
@@ -506,7 +453,6 @@ func TestNewService_EarliestVotingBlock(t *testing.T) {
WithDatabase(beaconDB), WithDatabase(beaconDB),
) )
require.NoError(t, err, "unable to setup web3 ETH1.0 chain service") require.NoError(t, err, "unable to setup web3 ETH1.0 chain service")
web3Service.eth1DataFetcher = &goodFetcher{backend: testAcc.Backend}
// simulated backend sets eth1 block // simulated backend sets eth1 block
// time as 10 seconds // time as 10 seconds
params.SetupTestConfigCleanup(t) params.SetupTestConfigCleanup(t)
@@ -800,18 +746,23 @@ func TestService_CacheBlockHeaders(t *testing.T) {
func TestService_FollowBlock(t *testing.T) { func TestService_FollowBlock(t *testing.T) {
followTime := params.BeaconConfig().Eth1FollowDistance * params.BeaconConfig().SecondsPerETH1Block followTime := params.BeaconConfig().Eth1FollowDistance * params.BeaconConfig().SecondsPerETH1Block
followTime += 10000 followTime += 10000
bMap := make(map[uint64]*gethTypes.Header) bMap := make(map[uint64]*types.HeaderInfo)
for i := uint64(3000); i > 0; i-- { for i := uint64(3000); i > 0; i-- {
bMap[i] = &gethTypes.Header{ h := &gethTypes.Header{
Number: big.NewInt(int64(i)), Number: big.NewInt(int64(i)),
Time: followTime + (i * 40), Time: followTime + (i * 40),
} }
bMap[i] = &types.HeaderInfo{
Number: h.Number,
Hash: h.Hash(),
Time: h.Time,
}
} }
s := &Service{ s := &Service{
cfg: &config{eth1HeaderReqLimit: 1000}, cfg: &config{eth1HeaderReqLimit: 1000},
eth1DataFetcher: &goodFetcher{blockNumMap: bMap}, rpcClient: &mockExecution.RPCClient{BlockNumMap: bMap},
headerCache: newHeaderCache(), headerCache: newHeaderCache(),
latestEth1Data: &ethpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000}, latestEth1Data: &ethpb.LatestETH1Data{BlockTime: (3000 * 40) + followTime, BlockHeight: 3000},
} }
h, err := s.followedBlockHeight(context.Background()) h, err := s.followedBlockHeight(context.Background())
assert.NoError(t, err) assert.NoError(t, err)
@@ -839,7 +790,7 @@ func (s *slowRPCClient) BatchCall(b []rpc.BatchElem) error {
return err return err
} }
h := &gethTypes.Header{Number: num} h := &gethTypes.Header{Number: num}
*e.Result.(*gethTypes.Header) = *h *e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Hash: h.Hash()}
} }
return nil return nil
} }

View File

@@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
gethTypes "github.com/ethereum/go-ethereum/core/types" gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v3/async/event" "github.com/prysmaticlabs/prysm/v3/async/event"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types" "github.com/prysmaticlabs/prysm/v3/beacon-chain/execution/types"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
@@ -140,12 +141,92 @@ func (m *Chain) ETH1ConnectionErrors() []error {
// RPCClient defines the mock rpc client. // RPCClient defines the mock rpc client.
type RPCClient struct { type RPCClient struct {
Backend *backends.SimulatedBackend Backend *backends.SimulatedBackend
BlockNumMap map[uint64]*types.HeaderInfo
} }
func (*RPCClient) Close() {} 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 return nil
} }
@@ -164,7 +245,7 @@ func (r *RPCClient) BatchCall(b []rpc.BatchElem) error {
if err != nil { if err != nil {
return err return err
} }
*e.Result.(*gethTypes.Header) = *h *e.Result.(*types.HeaderInfo) = types.HeaderInfo{Number: h.Number, Time: h.Time, Hash: h.Hash()}
} }
return nil return nil

View File

@@ -8,7 +8,7 @@ go_library(
deps = [ deps = [
"//encoding/bytesutil:go_default_library", "//encoding/bytesutil:go_default_library",
"@com_github_ethereum_go_ethereum//common: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"], srcs = ["eth1_types_test.go"],
embed = [":go_default_library"], embed = [":go_default_library"],
deps = [ deps = [
"//testing/assert:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library", "@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
], ],
) )

View File

@@ -1,33 +1,20 @@
package types package types
import ( import (
"encoding/json"
"errors" "errors"
"math/big" "math/big"
"github.com/ethereum/go-ethereum/common" "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" "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
) )
// HeaderInfo specifies the block header information in the ETH 1.0 chain. // HeaderInfo specifies the block header information in the ETH 1.0 chain.
type HeaderInfo struct { type HeaderInfo struct {
Number *big.Int Number *big.Int `json:"number"`
Hash common.Hash Hash common.Hash `json:"hash"`
Time uint64 Time uint64 `json:"timestamp"`
}
// 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
} }
// Copy sends out a copy of the current header info. // Copy sends out a copy of the current header info.
@@ -38,3 +25,44 @@ func (h *HeaderInfo) Copy() *HeaderInfo {
Time: h.Time, 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
}

View File

@@ -1,53 +1,68 @@
package types package types
import ( import (
"math"
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "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) { func TestRoundtrip_HeaderInfo(t *testing.T) {
type args struct {
hdr *gethTypes.Header
}
tests := []struct { tests := []struct {
name string name string
args args hInfo HeaderInfo
want *HeaderInfo
wantErr bool wantErr bool
}{ }{
{ {
name: "OK", name: "normal header object",
args: args{hdr: &gethTypes.Header{ hInfo: HeaderInfo{
Number: big.NewInt(500), Number: big.NewInt(1000),
Time: 2345,
}},
want: &HeaderInfo{
Number: big.NewInt(500),
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}, 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: "nil number", name: "large header object",
args: args{hdr: &gethTypes.Header{ hInfo: HeaderInfo{
Time: 2345, 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: "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, wantErr: true,
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { 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 { 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 return
} }
if !reflect.DeepEqual(got, tt.want) { if tt.wantErr {
t.Errorf("headerToHeaderInfo() got = %v, want %v", got, tt.want) return
}
if !reflect.DeepEqual(*newH, tt.hInfo) {
t.Errorf("MarshalJSON() got = %v, want %v", newH, tt.hInfo)
} }
}) })
} }