erigon-pulse/cl/phase1/execution_client/execution_engine.go
a cda14447ad
[caplin] pkg refactor (#7507)
Co-authored-by: Giulio <giulio.rebuffo@gmail.com>
2023-05-13 23:44:07 +02:00

149 lines
4.5 KiB
Go

package execution_client
import (
"context"
"fmt"
"time"
"github.com/holiman/uint256"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/gointerfaces"
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
"github.com/ledgerwatch/erigon-lib/gointerfaces/types"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/ethdb/privateapi"
)
var errContextExceeded = "rpc error: code = DeadlineExceeded desc = context deadline exceeded"
// ExecutionEngine is used only for syncing up very close to chain tip and to stay in sync.
// It pretty much mimics engine API.
type ExecutionEngine interface {
NewPayload(payload *cltypes.Eth1Block) error
ForkChoiceUpdate(finalized libcommon.Hash, head libcommon.Hash) error
}
// ExecutionEnginePhase1 is just the normal engine api.
type ExecutionEnginePhase1 struct {
// Either execution server or client
executionServer remote.ETHBACKENDServer
executionClient remote.ETHBACKENDClient
ctx context.Context
}
// NewExecutionEnginePhase1FromServer use ethbackend server
func NewExecutionEnginePhase1FromServer(ctx context.Context, executionServer remote.ETHBACKENDServer) *ExecutionEnginePhase1 {
return &ExecutionEnginePhase1{
executionServer: executionServer,
ctx: ctx,
}
}
// NewExecutionEnginePhase1FromServer use ethbackend client
func NewExecutionEnginePhase1FromClient(ctx context.Context, executionClient remote.ETHBACKENDClient) *ExecutionEnginePhase1 {
return &ExecutionEnginePhase1{
executionClient: executionClient,
ctx: ctx,
}
}
func (e *ExecutionEnginePhase1) NewPayload(payload *cltypes.Eth1Block) error {
grpcMessage, err := convertPayloadToGrpc(payload)
if err != nil {
return err
}
ctx, cancel := context.WithTimeout(e.ctx, 3*time.Second)
defer cancel()
var status *remote.EnginePayloadStatus
if e.executionServer != nil {
status, err = e.executionServer.EngineNewPayload(ctx, grpcMessage)
} else if e.executionClient != nil {
status, err = e.executionClient.EngineNewPayload(ctx, grpcMessage)
}
// Ignore timeouts
if err != nil {
if err.Error() == errContextExceeded {
return nil
}
return err
}
if status.Status == remote.EngineStatus_INVALID {
return fmt.Errorf("invalid block")
}
if status.Status == remote.EngineStatus_INVALID_BLOCK_HASH {
return fmt.Errorf("invalid block hash")
}
return err
}
func (e *ExecutionEnginePhase1) ForkChoiceUpdate(finalized libcommon.Hash, head libcommon.Hash) error {
grpcMessage := &remote.EngineForkChoiceUpdatedRequest{
ForkchoiceState: &remote.EngineForkChoiceState{
HeadBlockHash: gointerfaces.ConvertHashToH256(head),
SafeBlockHash: gointerfaces.ConvertHashToH256(head),
FinalizedBlockHash: gointerfaces.ConvertHashToH256(finalized),
},
}
var err error
ctx, cancel := context.WithTimeout(e.ctx, 3*time.Second)
defer cancel()
if e.executionClient != nil {
_, err = e.executionClient.EngineForkChoiceUpdated(ctx, grpcMessage)
} else if e.executionServer != nil {
_, err = e.executionServer.EngineForkChoiceUpdated(ctx, grpcMessage)
}
// Ignore timeouts
if err != nil && err.Error() == errContextExceeded {
return nil
}
return err
}
// ExecutionEnginePhase2 is "real merge" (TODO).
type ExecutionEnginePhase2 struct {
}
func convertPayloadToGrpc(e *cltypes.Eth1Block) (*types.ExecutionPayload, error) {
var baseFee *uint256.Int
header, err := e.RlpHeader()
if err != nil {
return nil, err
}
if header.BaseFee != nil {
var overflow bool
baseFee, overflow = uint256.FromBig(header.BaseFee)
if overflow {
panic("NewPayload BaseFeePerGas overflow")
}
}
res := &types.ExecutionPayload{
Version: 1,
ParentHash: gointerfaces.ConvertHashToH256(header.ParentHash),
Coinbase: gointerfaces.ConvertAddressToH160(header.Coinbase),
StateRoot: gointerfaces.ConvertHashToH256(header.Root),
ReceiptRoot: gointerfaces.ConvertHashToH256(header.ReceiptHash),
LogsBloom: gointerfaces.ConvertBytesToH2048(header.Bloom[:]),
PrevRandao: gointerfaces.ConvertHashToH256(header.MixDigest),
BlockNumber: header.Number.Uint64(),
GasLimit: header.GasLimit,
GasUsed: header.GasUsed,
Timestamp: header.Time,
ExtraData: header.Extra,
BaseFeePerGas: gointerfaces.ConvertUint256IntToH256(baseFee),
BlockHash: gointerfaces.ConvertHashToH256(e.BlockHash),
Transactions: e.Transactions,
Withdrawals: privateapi.ConvertWithdrawalsToRpc(e.Withdrawals),
}
if e.Withdrawals != nil {
res.Version = 2
res.Withdrawals = privateapi.ConvertWithdrawalsToRpc(e.Withdrawals)
}
return res, nil
}