erigon-pulse/cmd/rpcdaemon/services/eth_backend.go

156 lines
4.9 KiB
Go

package services
import (
"context"
"errors"
"fmt"
"io"
"github.com/ledgerwatch/erigon-lib/gointerfaces"
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/cmd/rpcdaemon/interfaces"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/ethdb/privateapi"
"github.com/ledgerwatch/log/v3"
"google.golang.org/grpc"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
// ApiBackend - interface which must be used by API layer
// implementation can work with local Ethereum object or with Remote (grpc-based) one
// this is reason why all methods are accepting context and returning error
type ApiBackend interface {
Etherbase(ctx context.Context) (common.Address, error)
NetVersion(ctx context.Context) (uint64, error)
NetPeerCount(ctx context.Context) (uint64, error)
ProtocolVersion(ctx context.Context) (uint64, error)
ClientVersion(ctx context.Context) (string, error)
Subscribe(ctx context.Context, cb func(*remote.SubscribeReply)) error
BlockWithSenders(ctx context.Context, tx kv.Tx, hash common.Hash, blockHeight uint64) (block *types.Block, senders []common.Address, err error)
}
type RemoteBackend struct {
remoteEthBackend remote.ETHBACKENDClient
log log.Logger
version gointerfaces.Version
db kv.RoDB
blockReader interfaces.BlockReader
}
func NewRemoteBackend(cc grpc.ClientConnInterface, db kv.RoDB, blockReader interfaces.BlockReader) *RemoteBackend {
return &RemoteBackend{
remoteEthBackend: remote.NewETHBACKENDClient(cc),
version: gointerfaces.VersionFromProto(privateapi.EthBackendAPIVersion),
log: log.New("remote_service", "eth_backend"),
db: db,
blockReader: blockReader,
}
}
func (back *RemoteBackend) EnsureVersionCompatibility() bool {
versionReply, err := back.remoteEthBackend.Version(context.Background(), &emptypb.Empty{}, grpc.WaitForReady(true))
if err != nil {
back.log.Error("getting Version", "error", err)
return false
}
if !gointerfaces.EnsureVersion(back.version, versionReply) {
back.log.Error("incompatible interface versions", "client", back.version.String(),
"server", fmt.Sprintf("%d.%d.%d", versionReply.Major, versionReply.Minor, versionReply.Patch))
return false
}
back.log.Info("interfaces compatible", "client", back.version.String(),
"server", fmt.Sprintf("%d.%d.%d", versionReply.Major, versionReply.Minor, versionReply.Patch))
return true
}
func (back *RemoteBackend) Etherbase(ctx context.Context) (common.Address, error) {
res, err := back.remoteEthBackend.Etherbase(ctx, &remote.EtherbaseRequest{})
if err != nil {
if s, ok := status.FromError(err); ok {
return common.Address{}, errors.New(s.Message())
}
return common.Address{}, err
}
return gointerfaces.ConvertH160toAddress(res.Address), nil
}
func (back *RemoteBackend) NetVersion(ctx context.Context) (uint64, error) {
res, err := back.remoteEthBackend.NetVersion(ctx, &remote.NetVersionRequest{})
if err != nil {
if s, ok := status.FromError(err); ok {
return 0, errors.New(s.Message())
}
return 0, err
}
return res.Id, nil
}
func (back *RemoteBackend) NetPeerCount(ctx context.Context) (uint64, error) {
res, err := back.remoteEthBackend.NetPeerCount(ctx, &remote.NetPeerCountRequest{})
if err != nil {
if s, ok := status.FromError(err); ok {
return 0, errors.New(s.Message())
}
return 0, err
}
return res.Count, nil
}
func (back *RemoteBackend) ProtocolVersion(ctx context.Context) (uint64, error) {
res, err := back.remoteEthBackend.ProtocolVersion(ctx, &remote.ProtocolVersionRequest{})
if err != nil {
if s, ok := status.FromError(err); ok {
return 0, errors.New(s.Message())
}
return 0, err
}
return res.Id, nil
}
func (back *RemoteBackend) ClientVersion(ctx context.Context) (string, error) {
res, err := back.remoteEthBackend.ClientVersion(ctx, &remote.ClientVersionRequest{})
if err != nil {
if s, ok := status.FromError(err); ok {
return "", errors.New(s.Message())
}
return "", err
}
return res.NodeName, nil
}
func (back *RemoteBackend) Subscribe(ctx context.Context, onNewEvent func(*remote.SubscribeReply)) error {
subscription, err := back.remoteEthBackend.Subscribe(ctx, &remote.SubscribeRequest{}, grpc.WaitForReady(true))
if err != nil {
if s, ok := status.FromError(err); ok {
return errors.New(s.Message())
}
return err
}
for {
event, err := subscription.Recv()
if err == io.EOF {
log.Info("rpcdaemon: the subscription channel was closed")
break
}
if err != nil {
return err
}
onNewEvent(event)
}
return nil
}
func (back *RemoteBackend) BlockWithSenders(ctx context.Context, tx kv.Tx, hash common.Hash, blockHeight uint64) (block *types.Block, senders []common.Address, err error) {
return back.blockReader.BlockWithSenders(ctx, tx, hash, blockHeight)
}