mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-03 17:44:29 +00:00
7d35c6b737
See https://github.com/ethereum/EIPs/pull/7354 & https://github.com/ethereum/consensus-specs/pull/3461. Prerequisite: https://github.com/ledgerwatch/erigon-lib/pull/1058
166 lines
5.2 KiB
Go
166 lines
5.2 KiB
Go
package execution_client
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
|
"github.com/ledgerwatch/erigon/cl/clparams"
|
|
"github.com/ledgerwatch/erigon/cl/cltypes"
|
|
"github.com/ledgerwatch/erigon/cl/phase1/execution_client/rpc_helper"
|
|
"github.com/ledgerwatch/erigon/common/hexutil"
|
|
"github.com/ledgerwatch/erigon/rpc"
|
|
"github.com/ledgerwatch/erigon/turbo/engineapi/engine_types"
|
|
"github.com/ledgerwatch/log/v3"
|
|
)
|
|
|
|
const DefaultRPCHTTPTimeout = time.Second * 30
|
|
|
|
type ExecutionClientRpc struct {
|
|
client *rpc.Client
|
|
ctx context.Context
|
|
addr string
|
|
jwtSecret []byte
|
|
}
|
|
|
|
func NewExecutionClientRPC(ctx context.Context, jwtSecret []byte, addr string, port int) (*ExecutionClientRpc, error) {
|
|
roundTripper := rpc_helper.NewJWTRoundTripper(jwtSecret)
|
|
client := &http.Client{Timeout: DefaultRPCHTTPTimeout, Transport: roundTripper}
|
|
|
|
isHTTPpecified := strings.HasPrefix(addr, "http")
|
|
isHTTPSpecified := strings.HasPrefix(addr, "https")
|
|
protocol := ""
|
|
if isHTTPSpecified {
|
|
protocol = "https://"
|
|
} else if !isHTTPpecified {
|
|
protocol = "http://"
|
|
}
|
|
rpcClient, err := rpc.DialHTTPWithClient(fmt.Sprintf("%s%s:%d", protocol, addr, port), client, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ExecutionClientRpc{
|
|
client: rpcClient,
|
|
ctx: ctx,
|
|
addr: addr,
|
|
jwtSecret: jwtSecret,
|
|
}, nil
|
|
}
|
|
|
|
func (cc *ExecutionClientRpc) NewPayload(payload *cltypes.Eth1Block) (invalid bool, err error) {
|
|
if payload == nil {
|
|
return
|
|
}
|
|
|
|
reversedBaseFeePerGas := libcommon.Copy(payload.BaseFeePerGas[:])
|
|
for i, j := 0, len(reversedBaseFeePerGas)-1; i < j; i, j = i+1, j-1 {
|
|
reversedBaseFeePerGas[i], reversedBaseFeePerGas[j] = reversedBaseFeePerGas[j], reversedBaseFeePerGas[i]
|
|
}
|
|
baseFee := new(big.Int).SetBytes(reversedBaseFeePerGas)
|
|
var engineMethod string
|
|
// determine the engine method
|
|
switch payload.Version() {
|
|
case clparams.BellatrixVersion:
|
|
engineMethod = rpc_helper.EngineNewPayloadV1
|
|
case clparams.CapellaVersion:
|
|
engineMethod = rpc_helper.EngineNewPayloadV2
|
|
case clparams.DenebVersion:
|
|
engineMethod = rpc_helper.EngineNewPayloadV3
|
|
default:
|
|
err = fmt.Errorf("invalid payload version")
|
|
return
|
|
}
|
|
|
|
request := engine_types.ExecutionPayload{
|
|
ParentHash: payload.ParentHash,
|
|
FeeRecipient: payload.FeeRecipient,
|
|
StateRoot: payload.StateRoot,
|
|
ReceiptsRoot: payload.ReceiptsRoot,
|
|
LogsBloom: payload.LogsBloom[:],
|
|
PrevRandao: payload.PrevRandao,
|
|
BlockNumber: hexutil.Uint64(payload.BlockNumber),
|
|
GasLimit: hexutil.Uint64(payload.GasLimit),
|
|
GasUsed: hexutil.Uint64(payload.GasUsed),
|
|
Timestamp: hexutil.Uint64(payload.Time),
|
|
ExtraData: payload.Extra.Bytes(),
|
|
BlockHash: payload.BlockHash,
|
|
}
|
|
|
|
request.BaseFeePerGas = new(hexutil.Big)
|
|
*request.BaseFeePerGas = hexutil.Big(*baseFee)
|
|
payloadBody := payload.Body()
|
|
// Setup transactionbody
|
|
request.Withdrawals = payloadBody.Withdrawals
|
|
|
|
for _, bytesTransaction := range payloadBody.Transactions {
|
|
request.Transactions = append(request.Transactions, bytesTransaction)
|
|
}
|
|
// Process Deneb
|
|
if payload.Version() >= clparams.DenebVersion {
|
|
request.BlobGasUsed = new(hexutil.Uint64)
|
|
request.ExcessBlobGas = new(hexutil.Uint64)
|
|
*request.BlobGasUsed = hexutil.Uint64(payload.BlobGasUsed)
|
|
*request.ExcessBlobGas = hexutil.Uint64(payload.ExcessBlobGas)
|
|
}
|
|
|
|
payloadStatus := &engine_types.PayloadStatus{} // As it is done in the rpcdaemon
|
|
log.Debug("[ExecutionClientRpc] Calling EL", "method", engineMethod)
|
|
err = cc.client.CallContext(cc.ctx, &payloadStatus, engineMethod, request)
|
|
if err != nil {
|
|
err = fmt.Errorf("execution Client RPC failed to retrieve the NewPayload status response, err: %w", err)
|
|
return
|
|
}
|
|
|
|
invalid = payloadStatus.Status == engine_types.InvalidStatus || payloadStatus.Status == engine_types.InvalidBlockHashStatus
|
|
err = checkPayloadStatus(payloadStatus)
|
|
if payloadStatus.Status == engine_types.AcceptedStatus {
|
|
log.Info("[ExecutionClientRpc] New block accepted")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (cc *ExecutionClientRpc) ForkChoiceUpdate(finalized libcommon.Hash, head libcommon.Hash) error {
|
|
forkChoiceRequest := engine_types.ForkChoiceState{
|
|
HeadHash: head,
|
|
SafeBlockHash: head,
|
|
FinalizedBlockHash: finalized,
|
|
}
|
|
forkChoiceResp := &engine_types.ForkChoiceUpdatedResponse{}
|
|
log.Debug("[ExecutionClientRpc] Calling EL", "method", rpc_helper.ForkChoiceUpdatedV1)
|
|
|
|
err := cc.client.CallContext(cc.ctx, forkChoiceResp, rpc_helper.ForkChoiceUpdatedV1, forkChoiceRequest)
|
|
if err != nil {
|
|
return fmt.Errorf("execution Client RPC failed to retrieve ForkChoiceUpdate response, err: %w", err)
|
|
}
|
|
// Ignore timeouts
|
|
if err != nil && err.Error() == errContextExceeded {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return checkPayloadStatus(forkChoiceResp.PayloadStatus)
|
|
}
|
|
|
|
func checkPayloadStatus(payloadStatus *engine_types.PayloadStatus) error {
|
|
if payloadStatus == nil {
|
|
return fmt.Errorf("empty payloadStatus")
|
|
}
|
|
|
|
validationError := payloadStatus.ValidationError
|
|
if validationError != nil {
|
|
return validationError.Error()
|
|
}
|
|
|
|
if payloadStatus.Status != engine_types.ValidStatus && payloadStatus.Status != engine_types.AcceptedStatus {
|
|
return fmt.Errorf("status: %s", payloadStatus.Status)
|
|
}
|
|
return nil
|
|
}
|