Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
78b54f2
wip: adding tracing
chatton Jan 6, 2026
ae87a59
chore: only having the first tracing decorator
chatton Jan 6, 2026
fdd943b
chore: remove comment
chatton Jan 6, 2026
74d4a5f
deps: adding pin to genproto version
chatton Jan 6, 2026
d796589
chore: ensuring errors reported, adding unit tests
chatton Jan 7, 2026
d52f187
chore: add check to validate basic
chatton Jan 7, 2026
b2b3218
chore: modified default
chatton Jan 7, 2026
41bce54
chore: adding logging of possible error
chatton Jan 7, 2026
fd34073
chore: updated flag test
chatton Jan 7, 2026
fd7425a
chore: bump endpoint to correct port
chatton Jan 7, 2026
f30a577
wip: adding propagating client to engine and eth client
chatton Jan 7, 2026
570509b
chore: simplify construction of rpc opts
chatton Jan 7, 2026
caa0684
chore: address PR feedback
chatton Jan 7, 2026
c154f23
chore: ensure consistent propagation settings
chatton Jan 8, 2026
607f4a3
chore: adding interface for engine client and tracing implementation
chatton Jan 8, 2026
c5d7c41
chore: mrege main
chatton Jan 8, 2026
80e2b17
chore: refactored wiring to use bool
chatton Jan 8, 2026
07a45b6
chore: tidy all fix
chatton Jan 8, 2026
423bb15
chore: fix go mod conflicts
chatton Jan 8, 2026
3e373ce
chore: addressing PR feedback
chatton Jan 8, 2026
ed217d7
chore: adding eth client tracing
chatton Jan 8, 2026
17eb5aa
chore: merge main
chatton Jan 8, 2026
931d2ac
chore: add payload id as attribute
chatton Jan 8, 2026
ee2d158
chore: handle merge conflicts
chatton Jan 8, 2026
8d7fc84
Merge branch 'cian/add-tracing-part-3' into cian/add-tracing-part-4
chatton Jan 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions execution/evm/engine_rpc_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package evm

import (
"context"

"github.com/ethereum/go-ethereum/beacon/engine"
"github.com/ethereum/go-ethereum/rpc"
)

var _ EngineRPCClient = (*engineRPCClient)(nil)

// engineRPCClient is the concrete implementation wrapping *rpc.Client.
type engineRPCClient struct {
client *rpc.Client
}

// NewEngineRPCClient creates a new Engine API client.
func NewEngineRPCClient(client *rpc.Client) EngineRPCClient {
return &engineRPCClient{client: client}
}

func (e *engineRPCClient) ForkchoiceUpdated(ctx context.Context, state engine.ForkchoiceStateV1, args map[string]any) (*engine.ForkChoiceResponse, error) {
var result engine.ForkChoiceResponse
err := e.client.CallContext(ctx, &result, "engine_forkchoiceUpdatedV3", state, args)
if err != nil {
return nil, err
}
return &result, nil
}

func (e *engineRPCClient) GetPayload(ctx context.Context, payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
var result engine.ExecutionPayloadEnvelope
err := e.client.CallContext(ctx, &result, "engine_getPayloadV4", payloadID)
if err != nil {
return nil, err
}
return &result, nil
}

func (e *engineRPCClient) NewPayload(ctx context.Context, payload *engine.ExecutableData, blobHashes []string, parentBeaconBlockRoot string, executionRequests [][]byte) (*engine.PayloadStatusV1, error) {
var result engine.PayloadStatusV1
err := e.client.CallContext(ctx, &result, "engine_newPayloadV4", payload, blobHashes, parentBeaconBlockRoot, executionRequests)
if err != nil {
return nil, err
}
return &result, nil
}
122 changes: 122 additions & 0 deletions execution/evm/engine_rpc_tracing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package evm

import (
"context"

"github.com/ethereum/go-ethereum/beacon/engine"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)

var _ EngineRPCClient = (*tracedEngineRPCClient)(nil)

// tracedEngineRPCClient wraps an EngineRPCClient and records spans.
type tracedEngineRPCClient struct {
inner EngineRPCClient
tracer trace.Tracer
}

// withTracingEngineRPCClient decorates an EngineRPCClient with OpenTelemetry spans.
func withTracingEngineRPCClient(inner EngineRPCClient) EngineRPCClient {
return &tracedEngineRPCClient{
inner: inner,
tracer: otel.Tracer("ev-node/execution/engine-rpc"),
}
}

func (t *tracedEngineRPCClient) ForkchoiceUpdated(ctx context.Context, state engine.ForkchoiceStateV1, args map[string]any) (*engine.ForkChoiceResponse, error) {
ctx, span := t.tracer.Start(ctx, "Engine.ForkchoiceUpdated",
trace.WithAttributes(
attribute.String("method", "engine_forkchoiceUpdatedV3"),
attribute.String("head_block_hash", state.HeadBlockHash.Hex()),
attribute.String("safe_block_hash", state.SafeBlockHash.Hex()),
attribute.String("finalized_block_hash", state.FinalizedBlockHash.Hex()),
),
)
defer span.End()

result, err := t.inner.ForkchoiceUpdated(ctx, state, args)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}

attributes := []attribute.KeyValue{
attribute.String("payload_status", result.PayloadStatus.Status),
}

if result.PayloadID != nil {
attributes = append(attributes, attribute.String("payload_id", result.PayloadID.String()))
}

if result.PayloadStatus.LatestValidHash != nil {
attributes = append(attributes, attribute.String("latest_valid_hash", result.PayloadStatus.LatestValidHash.Hex()))
}

span.SetAttributes(
attributes...,
)

return result, nil
}

func (t *tracedEngineRPCClient) GetPayload(ctx context.Context, payloadID engine.PayloadID) (*engine.ExecutionPayloadEnvelope, error) {
ctx, span := t.tracer.Start(ctx, "Engine.GetPayload",
trace.WithAttributes(
attribute.String("method", "engine_getPayloadV4"),
attribute.String("payload_id", payloadID.String()),
),
)
defer span.End()

result, err := t.inner.GetPayload(ctx, payloadID)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}

span.SetAttributes(
attribute.Int64("block_number", int64(result.ExecutionPayload.Number)),
attribute.String("block_hash", result.ExecutionPayload.BlockHash.Hex()),
attribute.String("state_root", result.ExecutionPayload.StateRoot.Hex()),
attribute.Int("tx_count", len(result.ExecutionPayload.Transactions)),
attribute.Int64("gas_used", int64(result.ExecutionPayload.GasUsed)),
)

return result, nil
}

func (t *tracedEngineRPCClient) NewPayload(ctx context.Context, payload *engine.ExecutableData, blobHashes []string, parentBeaconBlockRoot string, executionRequests [][]byte) (*engine.PayloadStatusV1, error) {
ctx, span := t.tracer.Start(ctx, "Engine.NewPayload",
trace.WithAttributes(
attribute.String("method", "engine_newPayloadV4"),
attribute.Int64("block_number", int64(payload.Number)),
attribute.String("block_hash", payload.BlockHash.Hex()),
attribute.String("parent_hash", payload.ParentHash.Hex()),
attribute.Int("tx_count", len(payload.Transactions)),
attribute.Int64("gas_used", int64(payload.GasUsed)),
),
)
defer span.End()

result, err := t.inner.NewPayload(ctx, payload, blobHashes, parentBeaconBlockRoot, executionRequests)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}

attributes := []attribute.KeyValue{attribute.String("payload_status", result.Status)}

if result.LatestValidHash != nil {
attributes = append(attributes, attribute.String("latest_valid_hash", result.LatestValidHash.Hex()))
}

span.SetAttributes(attributes...)

return result, nil
}
30 changes: 30 additions & 0 deletions execution/evm/eth_rpc_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package evm

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
)

type ethRPCClient struct {
client *ethclient.Client
}

func NewEthRPCClient(client *ethclient.Client) EthRPCClient {
return &ethRPCClient{client: client}
}

func (e *ethRPCClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
return e.client.HeaderByNumber(ctx, number)
}

func (e *ethRPCClient) GetTxs(ctx context.Context) ([]string, error) {
var result []string
err := e.client.Client().CallContext(ctx, &result, "txpoolExt_getTxs")
if err != nil {
return nil, err
}
return result, nil
}
82 changes: 82 additions & 0 deletions execution/evm/eth_rpc_tracing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package evm

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/core/types"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)

// tracedEthRPCClient wraps an EthRPCClient and records spans for observability.
type tracedEthRPCClient struct {
inner EthRPCClient
tracer trace.Tracer
}

// withTracingEthRPCClient decorates an EthRPCClient with OpenTelemetry tracing.
func withTracingEthRPCClient(inner EthRPCClient) EthRPCClient {
return &tracedEthRPCClient{
inner: inner,
tracer: otel.Tracer("ev-node/execution/eth-rpc"),
}
}

func (t *tracedEthRPCClient) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
var blockNumber string
if number == nil {
blockNumber = "latest"
} else {
blockNumber = number.String()
}

ctx, span := t.tracer.Start(ctx, "Eth.GetBlockByNumber",
trace.WithAttributes(
attribute.String("method", "eth_getBlockByNumber"),
attribute.String("block_number", blockNumber),
),
)
defer span.End()

result, err := t.inner.HeaderByNumber(ctx, number)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}

span.SetAttributes(
attribute.String("block_hash", result.Hash().Hex()),
attribute.String("state_root", result.Root.Hex()),
attribute.Int64("gas_limit", int64(result.GasLimit)),
attribute.Int64("gas_used", int64(result.GasUsed)),
attribute.Int64("timestamp", int64(result.Time)),
)

return result, nil
}

func (t *tracedEthRPCClient) GetTxs(ctx context.Context) ([]string, error) {
ctx, span := t.tracer.Start(ctx, "TxPool.GetTxs",
trace.WithAttributes(
attribute.String("method", "txpoolExt_getTxs"),
),
)
defer span.End()

result, err := t.inner.GetTxs(ctx)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())
return nil, err
}

span.SetAttributes(
attribute.Int("tx_count", len(result)),
)

return result, nil
}
Loading
Loading