From 200f4d6ff1688c4342549fa531188a3f3d511d20 Mon Sep 17 00:00:00 2001 From: chatton Date: Wed, 14 Jan 2026 16:20:52 +0000 Subject: [PATCH 01/12] feat: add sequencer tracing instrumentation Add OpenTelemetry tracing for the core Sequencer interface. This traces all three main operations: - SubmitBatchTxs: tracks tx count and batch size - GetNextBatch: tracks tx count, forced inclusion count, batch size - VerifyBatch: tracks batch data count and verification result The tracing wrapper can be used with any Sequencer implementation (single, based, etc.) via WithTracingSequencer(). --- block/components.go | 6 + pkg/telemetry/sequencer_tracing.go | 128 +++++++++++ pkg/telemetry/sequencer_tracing_test.go | 293 ++++++++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 pkg/telemetry/sequencer_tracing.go create mode 100644 pkg/telemetry/sequencer_tracing_test.go diff --git a/block/components.go b/block/components.go index 3d276206b..f37c10737 100644 --- a/block/components.go +++ b/block/components.go @@ -17,6 +17,7 @@ import ( coreexecutor "github.com/evstack/ev-node/core/execution" coresequencer "github.com/evstack/ev-node/core/sequencer" "github.com/evstack/ev-node/pkg/config" + "github.com/evstack/ev-node/pkg/telemetry" "github.com/evstack/ev-node/pkg/genesis" "github.com/evstack/ev-node/pkg/signer" "github.com/evstack/ev-node/pkg/store" @@ -205,6 +206,11 @@ func NewAggregatorComponents( // error channel for critical failures errorCh := make(chan error, 1) + // wrap sequencer with tracing if enabled + if config.Instrumentation.IsTracingEnabled() { + sequencer = telemetry.WithTracingSequencer(sequencer) + } + executor, err := executing.NewExecutor( store, exec, diff --git a/pkg/telemetry/sequencer_tracing.go b/pkg/telemetry/sequencer_tracing.go new file mode 100644 index 000000000..ce6902e67 --- /dev/null +++ b/pkg/telemetry/sequencer_tracing.go @@ -0,0 +1,128 @@ +package telemetry + +import ( + "context" + "encoding/hex" + + coresequencer "github.com/evstack/ev-node/core/sequencer" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +var _ coresequencer.Sequencer = (*tracedSequencer)(nil) + +// tracedSequencer decorates a Sequencer with OpenTelemetry spans. +type tracedSequencer struct { + inner coresequencer.Sequencer + tracer trace.Tracer +} + +// WithTracingSequencer decorates the provided Sequencer with tracing spans. +func WithTracingSequencer(inner coresequencer.Sequencer) coresequencer.Sequencer { + return &tracedSequencer{ + inner: inner, + tracer: otel.Tracer("ev-node/sequencer"), + } +} + +func (t *tracedSequencer) SubmitBatchTxs(ctx context.Context, req coresequencer.SubmitBatchTxsRequest) (*coresequencer.SubmitBatchTxsResponse, error) { + txCount := 0 + totalBytes := 0 + if req.Batch != nil { + txCount = len(req.Batch.Transactions) + for _, tx := range req.Batch.Transactions { + totalBytes += len(tx) + } + } + + ctx, span := t.tracer.Start(ctx, "Sequencer.SubmitBatchTxs", + trace.WithAttributes( + attribute.String("chain.id", hex.EncodeToString(req.Id)), + attribute.Int("tx.count", txCount), + attribute.Int("batch.size_bytes", totalBytes), + ), + ) + defer span.End() + + res, err := t.inner.SubmitBatchTxs(ctx, req) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, err + } + + return res, nil +} + +func (t *tracedSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) { + ctx, span := t.tracer.Start(ctx, "Sequencer.GetNextBatch", + trace.WithAttributes( + attribute.String("chain.id", hex.EncodeToString(req.Id)), + attribute.Int64("max_bytes", int64(req.MaxBytes)), + ), + ) + defer span.End() + + res, err := t.inner.GetNextBatch(ctx, req) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, err + } + + if res.Batch != nil { + txCount := len(res.Batch.Transactions) + forcedCount := 0 + for _, forced := range res.Batch.ForceIncludedMask { + if forced { + forcedCount++ + } + } + totalBytes := 0 + for _, tx := range res.Batch.Transactions { + totalBytes += len(tx) + } + + span.SetAttributes( + attribute.Int("tx.count", txCount), + attribute.Int("forced_inclusion.count", forcedCount), + attribute.Int("batch.size_bytes", totalBytes), + attribute.Int64("timestamp", res.Timestamp.Unix()), + ) + } + + return res, nil +} + +func (t *tracedSequencer) VerifyBatch(ctx context.Context, req coresequencer.VerifyBatchRequest) (*coresequencer.VerifyBatchResponse, error) { + ctx, span := t.tracer.Start(ctx, "Sequencer.VerifyBatch", + trace.WithAttributes( + attribute.String("chain.id", hex.EncodeToString(req.Id)), + attribute.Int("batch_data.count", len(req.BatchData)), + ), + ) + defer span.End() + + res, err := t.inner.VerifyBatch(ctx, req) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, err + } + + span.SetAttributes( + attribute.Bool("verified", res.Status), + ) + + return res, nil +} + +func (t *tracedSequencer) SetDAHeight(height uint64) { + t.inner.SetDAHeight(height) +} + +func (t *tracedSequencer) GetDAHeight() uint64 { + return t.inner.GetDAHeight() +} diff --git a/pkg/telemetry/sequencer_tracing_test.go b/pkg/telemetry/sequencer_tracing_test.go new file mode 100644 index 000000000..244024075 --- /dev/null +++ b/pkg/telemetry/sequencer_tracing_test.go @@ -0,0 +1,293 @@ +package telemetry + +import ( + "context" + "errors" + "testing" + "time" + + coresequencer "github.com/evstack/ev-node/core/sequencer" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" +) + +type mockSequencer struct { + submitBatchTxsFn func(context.Context, coresequencer.SubmitBatchTxsRequest) (*coresequencer.SubmitBatchTxsResponse, error) + getNextBatchFn func(context.Context, coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) + verifyBatchFn func(context.Context, coresequencer.VerifyBatchRequest) (*coresequencer.VerifyBatchResponse, error) + daHeight uint64 +} + +func (m *mockSequencer) SubmitBatchTxs(ctx context.Context, req coresequencer.SubmitBatchTxsRequest) (*coresequencer.SubmitBatchTxsResponse, error) { + if m.submitBatchTxsFn != nil { + return m.submitBatchTxsFn(ctx, req) + } + return &coresequencer.SubmitBatchTxsResponse{}, nil +} + +func (m *mockSequencer) GetNextBatch(ctx context.Context, req coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) { + if m.getNextBatchFn != nil { + return m.getNextBatchFn(ctx, req) + } + return &coresequencer.GetNextBatchResponse{ + Batch: &coresequencer.Batch{}, + Timestamp: time.Now(), + }, nil +} + +func (m *mockSequencer) VerifyBatch(ctx context.Context, req coresequencer.VerifyBatchRequest) (*coresequencer.VerifyBatchResponse, error) { + if m.verifyBatchFn != nil { + return m.verifyBatchFn(ctx, req) + } + return &coresequencer.VerifyBatchResponse{Status: true}, nil +} + +func (m *mockSequencer) SetDAHeight(height uint64) { + m.daHeight = height +} + +func (m *mockSequencer) GetDAHeight() uint64 { + return m.daHeight +} + +var _ coresequencer.Sequencer = (*mockSequencer)(nil) + +func setupSequencerTrace(t *testing.T, inner coresequencer.Sequencer) (coresequencer.Sequencer, *tracetest.SpanRecorder) { + t.Helper() + sr := tracetest.NewSpanRecorder() + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) + otel.SetTracerProvider(tp) + return WithTracingSequencer(inner), sr +} + +func TestTracedSequencer_SubmitBatchTxs_Success(t *testing.T) { + mock := &mockSequencer{ + submitBatchTxsFn: func(ctx context.Context, req coresequencer.SubmitBatchTxsRequest) (*coresequencer.SubmitBatchTxsResponse, error) { + return &coresequencer.SubmitBatchTxsResponse{}, nil + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test-chain"), + Batch: &coresequencer.Batch{ + Transactions: [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3")}, + }, + } + + res, err := seq.SubmitBatchTxs(ctx, req) + require.NoError(t, err) + require.NotNil(t, res) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Sequencer.SubmitBatchTxs", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + requireSequencerAttribute(t, attrs, "tx.count", 3) + requireSequencerAttribute(t, attrs, "batch.size_bytes", 9) // "tx1" + "tx2" + "tx3" = 9 bytes +} + +func TestTracedSequencer_SubmitBatchTxs_Error(t *testing.T) { + mock := &mockSequencer{ + submitBatchTxsFn: func(ctx context.Context, req coresequencer.SubmitBatchTxsRequest) (*coresequencer.SubmitBatchTxsResponse, error) { + return nil, errors.New("queue full") + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.SubmitBatchTxsRequest{ + Id: []byte("test-chain"), + Batch: &coresequencer.Batch{Transactions: [][]byte{[]byte("tx1")}}, + } + + _, err := seq.SubmitBatchTxs(ctx, req) + require.Error(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) +} + +func TestTracedSequencer_GetNextBatch_Success(t *testing.T) { + mock := &mockSequencer{ + getNextBatchFn: func(ctx context.Context, req coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) { + return &coresequencer.GetNextBatchResponse{ + Batch: &coresequencer.Batch{ + Transactions: [][]byte{[]byte("tx1"), []byte("forced-tx")}, + ForceIncludedMask: []bool{false, true}, + }, + Timestamp: time.Unix(1700000000, 0), + }, nil + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.GetNextBatchRequest{ + Id: []byte("test-chain"), + MaxBytes: 1000, + } + + res, err := seq.GetNextBatch(ctx, req) + require.NoError(t, err) + require.NotNil(t, res) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Sequencer.GetNextBatch", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + requireSequencerAttribute(t, attrs, "tx.count", 2) + requireSequencerAttribute(t, attrs, "forced_inclusion.count", 1) + requireSequencerAttribute(t, attrs, "max_bytes", int64(1000)) +} + +func TestTracedSequencer_GetNextBatch_Error(t *testing.T) { + mock := &mockSequencer{ + getNextBatchFn: func(ctx context.Context, req coresequencer.GetNextBatchRequest) (*coresequencer.GetNextBatchResponse, error) { + return nil, errors.New("failed to fetch from DA") + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.GetNextBatchRequest{ + Id: []byte("test-chain"), + MaxBytes: 1000, + } + + _, err := seq.GetNextBatch(ctx, req) + require.Error(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) +} + +func TestTracedSequencer_VerifyBatch_Success(t *testing.T) { + mock := &mockSequencer{ + verifyBatchFn: func(ctx context.Context, req coresequencer.VerifyBatchRequest) (*coresequencer.VerifyBatchResponse, error) { + return &coresequencer.VerifyBatchResponse{Status: true}, nil + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.VerifyBatchRequest{ + Id: []byte("test-chain"), + BatchData: [][]byte{[]byte("proof1"), []byte("proof2")}, + } + + res, err := seq.VerifyBatch(ctx, req) + require.NoError(t, err) + require.NotNil(t, res) + require.True(t, res.Status) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Sequencer.VerifyBatch", span.Name()) + + attrs := span.Attributes() + requireSequencerAttribute(t, attrs, "batch_data.count", 2) + requireSequencerAttribute(t, attrs, "verified", true) +} + +func TestTracedSequencer_VerifyBatch_Failure(t *testing.T) { + mock := &mockSequencer{ + verifyBatchFn: func(ctx context.Context, req coresequencer.VerifyBatchRequest) (*coresequencer.VerifyBatchResponse, error) { + return &coresequencer.VerifyBatchResponse{Status: false}, nil + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.VerifyBatchRequest{ + Id: []byte("test-chain"), + BatchData: [][]byte{[]byte("invalid-proof")}, + } + + res, err := seq.VerifyBatch(ctx, req) + require.NoError(t, err) + require.NotNil(t, res) + require.False(t, res.Status) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + + attrs := span.Attributes() + requireSequencerAttribute(t, attrs, "verified", false) +} + +func TestTracedSequencer_VerifyBatch_Error(t *testing.T) { + mock := &mockSequencer{ + verifyBatchFn: func(ctx context.Context, req coresequencer.VerifyBatchRequest) (*coresequencer.VerifyBatchResponse, error) { + return nil, errors.New("failed to get proofs") + }, + } + seq, sr := setupSequencerTrace(t, mock) + ctx := context.Background() + + req := coresequencer.VerifyBatchRequest{ + Id: []byte("test-chain"), + BatchData: [][]byte{[]byte("proof")}, + } + + _, err := seq.VerifyBatch(ctx, req) + require.Error(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) +} + +func TestTracedSequencer_DAHeightPassthrough(t *testing.T) { + mock := &mockSequencer{} + seq, _ := setupSequencerTrace(t, mock) + + seq.SetDAHeight(100) + require.Equal(t, uint64(100), seq.GetDAHeight()) + + seq.SetDAHeight(200) + require.Equal(t, uint64(200), seq.GetDAHeight()) +} + +func requireSequencerAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { + t.Helper() + found := false + for _, attr := range attrs { + if string(attr.Key) == key { + found = true + switch v := expected.(type) { + case string: + require.Equal(t, v, attr.Value.AsString()) + case int64: + require.Equal(t, v, attr.Value.AsInt64()) + case int: + require.Equal(t, int64(v), attr.Value.AsInt64()) + case bool: + require.Equal(t, v, attr.Value.AsBool()) + default: + t.Fatalf("unsupported attribute type: %T", expected) + } + break + } + } + require.True(t, found, "attribute %s not found", key) +} From 1ef693a891c38fa9f3e8efd31d1a23fcff7fb220 Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 08:47:49 +0000 Subject: [PATCH 02/12] chore: using helper fn instead of having it duplicated --- block/components.go | 2 +- block/internal/da/tracing_test.go | 37 +++------------ block/internal/executing/tracing_test.go | 38 ++++------------ block/internal/syncing/tracing_test.go | 42 ++++------------- execution/evm/eth_rpc_tracing_test.go | 48 ++++++-------------- pkg/rpc/server/tracing_test.go | 58 +++++++----------------- pkg/telemetry/executor_tracing_test.go | 46 +++++-------------- pkg/telemetry/sequencer_tracing_test.go | 42 ++++------------- pkg/telemetry/testutil/attributes.go | 34 ++++++++++++++ 9 files changed, 110 insertions(+), 237 deletions(-) create mode 100644 pkg/telemetry/testutil/attributes.go diff --git a/block/components.go b/block/components.go index f37c10737..a7a883bb3 100644 --- a/block/components.go +++ b/block/components.go @@ -17,10 +17,10 @@ import ( coreexecutor "github.com/evstack/ev-node/core/execution" coresequencer "github.com/evstack/ev-node/core/sequencer" "github.com/evstack/ev-node/pkg/config" - "github.com/evstack/ev-node/pkg/telemetry" "github.com/evstack/ev-node/pkg/genesis" "github.com/evstack/ev-node/pkg/signer" "github.com/evstack/ev-node/pkg/store" + "github.com/evstack/ev-node/pkg/telemetry" "github.com/evstack/ev-node/types" ) diff --git a/block/internal/da/tracing_test.go b/block/internal/da/tracing_test.go index ca288770c..ea01c9e42 100644 --- a/block/internal/da/tracing_test.go +++ b/block/internal/da/tracing_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" datypes "github.com/evstack/ev-node/pkg/da/types" + "github.com/evstack/ev-node/pkg/telemetry/testutil" ) // mockFullClient provides function hooks for testing the tracing decorator. @@ -87,8 +87,8 @@ func TestTracedDA_Submit_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "blob.count", 2) - requireAttribute(t, attrs, "blob.total_size_bytes", 3) + testutil.RequireAttribute(t, attrs, "blob.count", 2) + testutil.RequireAttribute(t, attrs, "blob.total_size_bytes", 3) // namespace hex string length assertion // 2 bytes = 4 hex characters foundNS := false @@ -134,8 +134,8 @@ func TestTracedDA_Retrieve_Success(t *testing.T) { span := spans[0] require.Equal(t, "DA.Retrieve", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "ns.length", 1) - requireAttribute(t, attrs, "blob.count", 2) + testutil.RequireAttribute(t, attrs, "ns.length", 1) + testutil.RequireAttribute(t, attrs, "blob.count", 2) } func TestTracedDA_Retrieve_Error(t *testing.T) { @@ -174,8 +174,8 @@ func TestTracedDA_Get_Success(t *testing.T) { span := spans[0] require.Equal(t, "DA.Get", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "id.count", 2) - requireAttribute(t, attrs, "blob.count", 2) + testutil.RequireAttribute(t, attrs, "id.count", 2) + testutil.RequireAttribute(t, attrs, "blob.count", 2) } func TestTracedDA_Get_Error(t *testing.T) { @@ -197,26 +197,3 @@ func TestTracedDA_Get_Error(t *testing.T) { require.Equal(t, codes.Error, span.Status().Code) require.Equal(t, "get failed", span.Status().Description) } - -// helper copied from eth tracing tests -func requireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/block/internal/executing/tracing_test.go b/block/internal/executing/tracing_test.go index c5c08ec74..fa5f4bebc 100644 --- a/block/internal/executing/tracing_test.go +++ b/block/internal/executing/tracing_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" coresequencer "github.com/evstack/ev-node/core/sequencer" + "github.com/evstack/ev-node/pkg/telemetry/testutil" "github.com/evstack/ev-node/types" ) @@ -131,7 +131,7 @@ func TestTracedBlockProducer_RetrieveBatch_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "batch.tx_count", 2) + testutil.RequireAttribute(t, attrs, "batch.tx_count", 2) } func TestTracedBlockProducer_RetrieveBatch_Error(t *testing.T) { @@ -180,8 +180,8 @@ func TestTracedBlockProducer_CreateBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(100)) - requireAttribute(t, attrs, "tx.count", 3) + testutil.RequireAttribute(t, attrs, "block.height", int64(100)) + testutil.RequireAttribute(t, attrs, "tx.count", 3) } func TestTracedBlockProducer_CreateBlock_Error(t *testing.T) { @@ -234,9 +234,9 @@ func TestTracedBlockProducer_ApplyBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(50)) - requireAttribute(t, attrs, "tx.count", 2) - requireAttribute(t, attrs, "state_root", "deadbeef") + testutil.RequireAttribute(t, attrs, "block.height", int64(50)) + testutil.RequireAttribute(t, attrs, "tx.count", 2) + testutil.RequireAttribute(t, attrs, "state_root", "deadbeef") } func TestTracedBlockProducer_ApplyBlock_Error(t *testing.T) { @@ -291,7 +291,7 @@ func TestTracedBlockProducer_ValidateBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(75)) + testutil.RequireAttribute(t, attrs, "block.height", int64(75)) } func TestTracedBlockProducer_ValidateBlock_Error(t *testing.T) { @@ -320,25 +320,3 @@ func TestTracedBlockProducer_ValidateBlock_Error(t *testing.T) { require.Equal(t, codes.Error, span.Status().Code) require.Equal(t, "validation failed", span.Status().Description) } - -func requireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/block/internal/syncing/tracing_test.go b/block/internal/syncing/tracing_test.go index d0d398301..679f3f7a3 100644 --- a/block/internal/syncing/tracing_test.go +++ b/block/internal/syncing/tracing_test.go @@ -7,12 +7,12 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "github.com/evstack/ev-node/block/internal/common" + "github.com/evstack/ev-node/pkg/telemetry/testutil" "github.com/evstack/ev-node/types" ) @@ -92,9 +92,9 @@ func TestTracedBlockSyncer_TrySyncNextBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(100)) - requireAttribute(t, attrs, "da.height", int64(50)) - requireAttribute(t, attrs, "source", string(common.SourceDA)) + testutil.RequireAttribute(t, attrs, "block.height", int64(100)) + testutil.RequireAttribute(t, attrs, "da.height", int64(50)) + testutil.RequireAttribute(t, attrs, "source", string(common.SourceDA)) } func TestTracedBlockSyncer_TrySyncNextBlock_Error(t *testing.T) { @@ -159,9 +159,9 @@ func TestTracedBlockSyncer_ApplyBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(50)) - requireAttribute(t, attrs, "tx.count", 2) - requireAttribute(t, attrs, "state_root", "deadbeef") + testutil.RequireAttribute(t, attrs, "block.height", int64(50)) + testutil.RequireAttribute(t, attrs, "tx.count", 2) + testutil.RequireAttribute(t, attrs, "state_root", "deadbeef") } func TestTracedBlockSyncer_ApplyBlock_Error(t *testing.T) { @@ -216,7 +216,7 @@ func TestTracedBlockSyncer_ValidateBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(75)) + testutil.RequireAttribute(t, attrs, "block.height", int64(75)) } func TestTracedBlockSyncer_ValidateBlock_Error(t *testing.T) { @@ -274,8 +274,8 @@ func TestTracedBlockSyncer_VerifyForcedInclusionTxs_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(100)) - requireAttribute(t, attrs, "da.height", int64(50)) + testutil.RequireAttribute(t, attrs, "block.height", int64(100)) + testutil.RequireAttribute(t, attrs, "da.height", int64(50)) } func TestTracedBlockSyncer_VerifyForcedInclusionTxs_Error(t *testing.T) { @@ -305,25 +305,3 @@ func TestTracedBlockSyncer_VerifyForcedInclusionTxs_Error(t *testing.T) { require.Equal(t, codes.Error, span.Status().Code) require.Equal(t, "forced inclusion verification failed", span.Status().Description) } - -func requireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/execution/evm/eth_rpc_tracing_test.go b/execution/evm/eth_rpc_tracing_test.go index 832a03fef..4d33e899b 100644 --- a/execution/evm/eth_rpc_tracing_test.go +++ b/execution/evm/eth_rpc_tracing_test.go @@ -9,10 +9,11 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/evstack/ev-node/pkg/telemetry/testutil" ) // setupTestEthRPCTracing creates a traced eth RPC client with an in-memory span recorder @@ -90,13 +91,13 @@ func TestTracedEthRPCClient_HeaderByNumber_Success(t *testing.T) { // verify attributes attrs := span.Attributes() - requireAttribute(t, attrs, "method", "eth_getBlockByNumber") - requireAttribute(t, attrs, "block_number", "100") - requireAttribute(t, attrs, "block_hash", expectedHeader.Hash().Hex()) - requireAttribute(t, attrs, "state_root", expectedHeader.Root.Hex()) - requireAttribute(t, attrs, "gas_limit", int64(expectedHeader.GasLimit)) - requireAttribute(t, attrs, "gas_used", int64(expectedHeader.GasUsed)) - requireAttribute(t, attrs, "timestamp", int64(expectedHeader.Time)) + testutil.RequireAttribute(t, attrs, "method", "eth_getBlockByNumber") + testutil.RequireAttribute(t, attrs, "block_number", "100") + testutil.RequireAttribute(t, attrs, "block_hash", expectedHeader.Hash().Hex()) + testutil.RequireAttribute(t, attrs, "state_root", expectedHeader.Root.Hex()) + testutil.RequireAttribute(t, attrs, "gas_limit", int64(expectedHeader.GasLimit)) + testutil.RequireAttribute(t, attrs, "gas_used", int64(expectedHeader.GasUsed)) + testutil.RequireAttribute(t, attrs, "timestamp", int64(expectedHeader.Time)) } func TestTracedEthRPCClient_HeaderByNumber_Latest(t *testing.T) { @@ -131,7 +132,7 @@ func TestTracedEthRPCClient_HeaderByNumber_Latest(t *testing.T) { // verify block_number is "latest" when nil attrs := span.Attributes() - requireAttribute(t, attrs, "block_number", "latest") + testutil.RequireAttribute(t, attrs, "block_number", "latest") } func TestTracedEthRPCClient_HeaderByNumber_Error(t *testing.T) { @@ -206,8 +207,8 @@ func TestTracedEthRPCClient_GetTxs_Success(t *testing.T) { // verify attributes attrs := span.Attributes() - requireAttribute(t, attrs, "method", "txpoolExt_getTxs") - requireAttribute(t, attrs, "tx_count", len(expectedTxs)) + testutil.RequireAttribute(t, attrs, "method", "txpoolExt_getTxs") + testutil.RequireAttribute(t, attrs, "tx_count", len(expectedTxs)) } func TestTracedEthRPCClient_GetTxs_EmptyPool(t *testing.T) { @@ -235,7 +236,7 @@ func TestTracedEthRPCClient_GetTxs_EmptyPool(t *testing.T) { // verify tx_count is 0 attrs := span.Attributes() - requireAttribute(t, attrs, "tx_count", 0) + testutil.RequireAttribute(t, attrs, "tx_count", 0) } func TestTracedEthRPCClient_GetTxs_Error(t *testing.T) { @@ -276,26 +277,3 @@ func TestTracedEthRPCClient_GetTxs_Error(t *testing.T) { require.NotEqual(t, "tx_count", string(attr.Key)) } } - -// requireAttribute is a helper to check span attributes -func requireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/pkg/rpc/server/tracing_test.go b/pkg/rpc/server/tracing_test.go index f950f9e28..a64180d9f 100644 --- a/pkg/rpc/server/tracing_test.go +++ b/pkg/rpc/server/tracing_test.go @@ -9,13 +9,13 @@ import ( "connectrpc.com/connect" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/evstack/ev-node/pkg/telemetry/testutil" pb "github.com/evstack/ev-node/types/pb/evnode/v1" "github.com/evstack/ev-node/types/pb/evnode/v1/v1connect" ) @@ -174,9 +174,9 @@ func TestTracedStoreService_GetBlock_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "height", int64(10)) - requireAttribute(t, attrs, "found", true) - requireAttribute(t, attrs, "tx_count", 2) + testutil.RequireAttribute(t, attrs, "height", int64(10)) + testutil.RequireAttribute(t, attrs, "found", true) + testutil.RequireAttribute(t, attrs, "tx_count", 2) } func TestTracedStoreService_GetBlock_Error(t *testing.T) { @@ -228,9 +228,9 @@ func TestTracedStoreService_GetState_Success(t *testing.T) { require.Equal(t, "StoreService.GetState", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "height", int64(100)) - requireAttribute(t, attrs, "app_hash", "aabb") - requireAttribute(t, attrs, "da_height", int64(50)) + testutil.RequireAttribute(t, attrs, "height", int64(100)) + testutil.RequireAttribute(t, attrs, "app_hash", "aabb") + testutil.RequireAttribute(t, attrs, "da_height", int64(50)) } func TestTracedStoreService_GetMetadata_Success(t *testing.T) { @@ -258,8 +258,8 @@ func TestTracedStoreService_GetMetadata_Success(t *testing.T) { require.Equal(t, "StoreService.GetMetadata", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "key", "test_key") - requireAttribute(t, attrs, "value_size_bytes", 14) + testutil.RequireAttribute(t, attrs, "key", "test_key") + testutil.RequireAttribute(t, attrs, "value_size_bytes", 14) } func TestTracedStoreService_GetGenesisDaHeight_Success(t *testing.T) { @@ -284,7 +284,7 @@ func TestTracedStoreService_GetGenesisDaHeight_Success(t *testing.T) { require.Equal(t, "StoreService.GetGenesisDaHeight", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "genesis_da_height", int64(1000)) + testutil.RequireAttribute(t, attrs, "genesis_da_height", int64(1000)) } func TestTracedStoreService_GetP2PStoreInfo_Success(t *testing.T) { @@ -312,7 +312,7 @@ func TestTracedStoreService_GetP2PStoreInfo_Success(t *testing.T) { require.Equal(t, "StoreService.GetP2PStoreInfo", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "store_count", 2) + testutil.RequireAttribute(t, attrs, "store_count", 2) } // P2PService tests @@ -342,7 +342,7 @@ func TestTracedP2PService_GetPeerInfo_Success(t *testing.T) { require.Equal(t, "P2PService.GetPeerInfo", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "peer_count", 2) + testutil.RequireAttribute(t, attrs, "peer_count", 2) } func TestTracedP2PService_GetPeerInfo_Error(t *testing.T) { @@ -389,8 +389,8 @@ func TestTracedP2PService_GetNetInfo_Success(t *testing.T) { require.Equal(t, "P2PService.GetNetInfo", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "node_id", "node123") - requireAttribute(t, attrs, "listen_address_count", 1) + testutil.RequireAttribute(t, attrs, "node_id", "node123") + testutil.RequireAttribute(t, attrs, "listen_address_count", 1) } // ConfigService tests @@ -418,8 +418,8 @@ func TestTracedConfigService_GetNamespace_Success(t *testing.T) { require.Equal(t, "ConfigService.GetNamespace", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "header_namespace", "0x0001020304050607") - requireAttribute(t, attrs, "data_namespace", "0x08090a0b0c0d0e0f") + testutil.RequireAttribute(t, attrs, "header_namespace", "0x0001020304050607") + testutil.RequireAttribute(t, attrs, "data_namespace", "0x08090a0b0c0d0e0f") } func TestTracedConfigService_GetNamespace_Error(t *testing.T) { @@ -463,7 +463,7 @@ func TestTracedConfigService_GetSignerInfo_Success(t *testing.T) { require.Equal(t, "ConfigService.GetSignerInfo", span.Name()) attrs := span.Attributes() - requireAttribute(t, attrs, "signer_address", "01020304") + testutil.RequireAttribute(t, attrs, "signer_address", "01020304") } func TestTracedConfigService_GetSignerInfo_Error(t *testing.T) { @@ -484,27 +484,3 @@ func TestTracedConfigService_GetSignerInfo_Error(t *testing.T) { span := spans[0] require.Equal(t, codes.Error, span.Status().Code) } - -func requireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - case bool: - require.Equal(t, v, attr.Value.AsBool()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/pkg/telemetry/executor_tracing_test.go b/pkg/telemetry/executor_tracing_test.go index 9a79ba2f6..472ba9852 100644 --- a/pkg/telemetry/executor_tracing_test.go +++ b/pkg/telemetry/executor_tracing_test.go @@ -6,16 +6,15 @@ import ( "testing" "time" + coreexec "github.com/evstack/ev-node/core/execution" + "github.com/evstack/ev-node/pkg/telemetry/testutil" + "github.com/evstack/ev-node/test/mocks" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" - - coreexec "github.com/evstack/ev-node/core/execution" - "github.com/evstack/ev-node/test/mocks" ) // setupTestTracing creates a traced executor with an in-memory span recorder for testing @@ -72,9 +71,9 @@ func TestWithTracingExecutor_InitChain_Success(t *testing.T) { // verify attributes attrs := span.Attributes() require.Len(t, attrs, 3) - requireAttribute(t, attrs, "chain.id", chainID) - requireAttribute(t, attrs, "initial.height", int64(initialHeight)) - requireAttribute(t, attrs, "genesis.time_unix", genesisTime.Unix()) + testutil.RequireAttribute(t, attrs, "chain.id", chainID) + testutil.RequireAttribute(t, attrs, "initial.height", int64(initialHeight)) + testutil.RequireAttribute(t, attrs, "genesis.time_unix", genesisTime.Unix()) } func TestWithTracingExecutor_InitChain_Error(t *testing.T) { @@ -137,7 +136,7 @@ func TestWithTracingExecutor_GetTxs_Success(t *testing.T) { // verify tx.count attribute attrs := span.Attributes() - requireAttribute(t, attrs, "tx.count", len(expectedTxs)) + testutil.RequireAttribute(t, attrs, "tx.count", len(expectedTxs)) } func TestWithTracingExecutor_GetTxs_Error(t *testing.T) { @@ -202,9 +201,9 @@ func TestWithTracingExecutor_ExecuteTxs_Success(t *testing.T) { // verify attributes attrs := span.Attributes() - requireAttribute(t, attrs, "tx.count", len(txs)) - requireAttribute(t, attrs, "block.height", int64(blockHeight)) - requireAttribute(t, attrs, "timestamp", timestamp.Unix()) + testutil.RequireAttribute(t, attrs, "tx.count", len(txs)) + testutil.RequireAttribute(t, attrs, "block.height", int64(blockHeight)) + testutil.RequireAttribute(t, attrs, "timestamp", timestamp.Unix()) } func TestWithTracingExecutor_ExecuteTxs_Error(t *testing.T) { @@ -260,7 +259,7 @@ func TestWithTracingExecutor_SetFinal_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireAttribute(t, attrs, "block.height", int64(blockHeight)) + testutil.RequireAttribute(t, attrs, "block.height", int64(blockHeight)) } func TestWithTracingExecutor_SetFinal_Error(t *testing.T) { @@ -371,26 +370,3 @@ type mockExecutorWithHeight struct { func (m *mockExecutorWithHeight) GetLatestHeight(ctx context.Context) (uint64, error) { return m.height, m.err } - -// requireAttribute is a helper to check span attributes -func requireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/pkg/telemetry/sequencer_tracing_test.go b/pkg/telemetry/sequencer_tracing_test.go index 244024075..bea229f74 100644 --- a/pkg/telemetry/sequencer_tracing_test.go +++ b/pkg/telemetry/sequencer_tracing_test.go @@ -7,9 +7,9 @@ import ( "time" coresequencer "github.com/evstack/ev-node/core/sequencer" + "github.com/evstack/ev-node/pkg/telemetry/testutil" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" sdktrace "go.opentelemetry.io/otel/sdk/trace" "go.opentelemetry.io/otel/sdk/trace/tracetest" @@ -92,8 +92,8 @@ func TestTracedSequencer_SubmitBatchTxs_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireSequencerAttribute(t, attrs, "tx.count", 3) - requireSequencerAttribute(t, attrs, "batch.size_bytes", 9) // "tx1" + "tx2" + "tx3" = 9 bytes + testutil.RequireAttribute(t, attrs, "tx.count", 3) + testutil.RequireAttribute(t, attrs, "batch.size_bytes", 9) // "tx1" + "tx2" + "tx3" = 9 bytes } func TestTracedSequencer_SubmitBatchTxs_Error(t *testing.T) { @@ -150,9 +150,9 @@ func TestTracedSequencer_GetNextBatch_Success(t *testing.T) { require.Equal(t, codes.Unset, span.Status().Code) attrs := span.Attributes() - requireSequencerAttribute(t, attrs, "tx.count", 2) - requireSequencerAttribute(t, attrs, "forced_inclusion.count", 1) - requireSequencerAttribute(t, attrs, "max_bytes", int64(1000)) + testutil.RequireAttribute(t, attrs, "tx.count", 2) + testutil.RequireAttribute(t, attrs, "forced_inclusion.count", 1) + testutil.RequireAttribute(t, attrs, "max_bytes", int64(1000)) } func TestTracedSequencer_GetNextBatch_Error(t *testing.T) { @@ -203,8 +203,8 @@ func TestTracedSequencer_VerifyBatch_Success(t *testing.T) { require.Equal(t, "Sequencer.VerifyBatch", span.Name()) attrs := span.Attributes() - requireSequencerAttribute(t, attrs, "batch_data.count", 2) - requireSequencerAttribute(t, attrs, "verified", true) + testutil.RequireAttribute(t, attrs, "batch_data.count", 2) + testutil.RequireAttribute(t, attrs, "verified", true) } func TestTracedSequencer_VerifyBatch_Failure(t *testing.T) { @@ -231,7 +231,7 @@ func TestTracedSequencer_VerifyBatch_Failure(t *testing.T) { span := spans[0] attrs := span.Attributes() - requireSequencerAttribute(t, attrs, "verified", false) + testutil.RequireAttribute(t, attrs, "verified", false) } func TestTracedSequencer_VerifyBatch_Error(t *testing.T) { @@ -267,27 +267,3 @@ func TestTracedSequencer_DAHeightPassthrough(t *testing.T) { seq.SetDAHeight(200) require.Equal(t, uint64(200), seq.GetDAHeight()) } - -func requireSequencerAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { - t.Helper() - found := false - for _, attr := range attrs { - if string(attr.Key) == key { - found = true - switch v := expected.(type) { - case string: - require.Equal(t, v, attr.Value.AsString()) - case int64: - require.Equal(t, v, attr.Value.AsInt64()) - case int: - require.Equal(t, int64(v), attr.Value.AsInt64()) - case bool: - require.Equal(t, v, attr.Value.AsBool()) - default: - t.Fatalf("unsupported attribute type: %T", expected) - } - break - } - } - require.True(t, found, "attribute %s not found", key) -} diff --git a/pkg/telemetry/testutil/attributes.go b/pkg/telemetry/testutil/attributes.go new file mode 100644 index 000000000..6a6ede4cd --- /dev/null +++ b/pkg/telemetry/testutil/attributes.go @@ -0,0 +1,34 @@ +// Package testutil provides test utilities for OpenTelemetry tracing tests. +package testutil + +import ( + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel/attribute" +) + +// RequireAttribute asserts that an attribute with the given key exists and has the expected value. +func RequireAttribute(t *testing.T, attrs []attribute.KeyValue, key string, expected interface{}) { + t.Helper() + found := false + for _, attr := range attrs { + if string(attr.Key) == key { + found = true + switch v := expected.(type) { + case string: + require.Equal(t, v, attr.Value.AsString()) + case int64: + require.Equal(t, v, attr.Value.AsInt64()) + case int: + require.Equal(t, int64(v), attr.Value.AsInt64()) + case bool: + require.Equal(t, v, attr.Value.AsBool()) + default: + t.Fatalf("unsupported attribute type: %T", expected) + } + break + } + } + require.True(t, found, "attribute %s not found", key) +} From ef202f428ade529e6f0db1cf8602b533d5cc697c Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 09:50:50 +0000 Subject: [PATCH 03/12] chore: adding da retreiver syncing --- .../internal/syncing/da_retriever_tracing.go | 57 ++++++++ .../syncing/da_retriever_tracing_test.go | 123 ++++++++++++++++++ block/internal/syncing/syncer.go | 3 + 3 files changed, 183 insertions(+) create mode 100644 block/internal/syncing/da_retriever_tracing.go create mode 100644 block/internal/syncing/da_retriever_tracing_test.go diff --git a/block/internal/syncing/da_retriever_tracing.go b/block/internal/syncing/da_retriever_tracing.go new file mode 100644 index 000000000..894fc67ba --- /dev/null +++ b/block/internal/syncing/da_retriever_tracing.go @@ -0,0 +1,57 @@ +package syncing + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/evstack/ev-node/block/internal/common" +) + +var _ DARetriever = (*tracedDARetriever)(nil) + +// tracedDARetriever wraps a DARetriever with OpenTelemetry tracing. +type tracedDARetriever struct { + inner DARetriever + tracer trace.Tracer +} + +// WithTracingDARetriever wraps a DARetriever with OpenTelemetry tracing. +func WithTracingDARetriever(inner DARetriever) DARetriever { + return &tracedDARetriever{ + inner: inner, + tracer: otel.Tracer("ev-node/da-retriever"), + } +} + +func (t *tracedDARetriever) RetrieveFromDA(ctx context.Context, daHeight uint64) ([]common.DAHeightEvent, error) { + ctx, span := t.tracer.Start(ctx, "DARetriever.RetrieveFromDA", + trace.WithAttributes( + attribute.Int64("da.height", int64(daHeight)), + ), + ) + defer span.End() + + events, err := t.inner.RetrieveFromDA(ctx, daHeight) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return events, err + } + + span.SetAttributes(attribute.Int("event.count", len(events))) + + // add block heights from events + if len(events) > 0 { + heights := make([]int64, len(events)) + for i, event := range events { + heights[i] = int64(event.Header.Height()) + } + span.SetAttributes(attribute.Int64Slice("block.heights", heights)) + } + + return events, nil +} diff --git a/block/internal/syncing/da_retriever_tracing_test.go b/block/internal/syncing/da_retriever_tracing_test.go new file mode 100644 index 000000000..d83ed99d2 --- /dev/null +++ b/block/internal/syncing/da_retriever_tracing_test.go @@ -0,0 +1,123 @@ +package syncing + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/evstack/ev-node/block/internal/common" + "github.com/evstack/ev-node/pkg/telemetry/testutil" + "github.com/evstack/ev-node/types" +) + +type mockDARetriever struct { + retrieveFromDAFn func(ctx context.Context, daHeight uint64) ([]common.DAHeightEvent, error) +} + +func (m *mockDARetriever) RetrieveFromDA(ctx context.Context, daHeight uint64) ([]common.DAHeightEvent, error) { + if m.retrieveFromDAFn != nil { + return m.retrieveFromDAFn(ctx, daHeight) + } + return nil, nil +} + +func setupDARetrieverTrace(t *testing.T, inner DARetriever) (DARetriever, *tracetest.SpanRecorder) { + t.Helper() + sr := tracetest.NewSpanRecorder() + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) + otel.SetTracerProvider(tp) + return WithTracingDARetriever(inner), sr +} + +func TestTracedDARetriever_RetrieveFromDA_Success(t *testing.T) { + mock := &mockDARetriever{ + retrieveFromDAFn: func(ctx context.Context, daHeight uint64) ([]common.DAHeightEvent, error) { + return []common.DAHeightEvent{ + { + Header: &types.SignedHeader{ + Header: types.Header{ + BaseHeader: types.BaseHeader{Height: 100}, + }, + }, + DaHeight: daHeight, + Source: common.SourceDA, + }, + { + Header: &types.SignedHeader{ + Header: types.Header{ + BaseHeader: types.BaseHeader{Height: 101}, + }, + }, + DaHeight: daHeight, + Source: common.SourceDA, + }, + }, nil + }, + } + retriever, sr := setupDARetrieverTrace(t, mock) + ctx := context.Background() + + events, err := retriever.RetrieveFromDA(ctx, 50) + require.NoError(t, err) + require.Len(t, events, 2) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "DARetriever.RetrieveFromDA", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "da.height", int64(50)) + testutil.RequireAttribute(t, attrs, "event.count", 2) +} + +func TestTracedDARetriever_RetrieveFromDA_NoEvents(t *testing.T) { + mock := &mockDARetriever{ + retrieveFromDAFn: func(ctx context.Context, daHeight uint64) ([]common.DAHeightEvent, error) { + return []common.DAHeightEvent{}, nil + }, + } + retriever, sr := setupDARetrieverTrace(t, mock) + ctx := context.Background() + + events, err := retriever.RetrieveFromDA(ctx, 50) + require.NoError(t, err) + require.Empty(t, events) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "event.count", 0) +} + +func TestTracedDARetriever_RetrieveFromDA_Error(t *testing.T) { + expectedErr := errors.New("DA retrieval failed") + mock := &mockDARetriever{ + retrieveFromDAFn: func(ctx context.Context, daHeight uint64) ([]common.DAHeightEvent, error) { + return nil, expectedErr + }, + } + retriever, sr := setupDARetrieverTrace(t, mock) + ctx := context.Background() + + _, err := retriever.RetrieveFromDA(ctx, 50) + require.Error(t, err) + require.Equal(t, expectedErr, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) + require.Equal(t, expectedErr.Error(), span.Status().Description) +} diff --git a/block/internal/syncing/syncer.go b/block/internal/syncing/syncer.go index a00c2f4fc..6365c548b 100644 --- a/block/internal/syncing/syncer.go +++ b/block/internal/syncing/syncer.go @@ -201,6 +201,9 @@ func (s *Syncer) Start(ctx context.Context) error { // Initialize handlers s.daRetriever = NewDARetriever(s.daClient, s.cache, s.genesis, s.logger) + if s.config.Instrumentation.IsTracingEnabled() { + s.daRetriever = WithTracingDARetriever(s.daRetriever) + } s.fiRetriever = da.NewForcedInclusionRetriever(s.daClient, s.logger, s.config, s.genesis.DAStartHeight, s.genesis.DAEpochForcedInclusion) s.p2pHandler = NewP2PHandler(s.headerStore.Store(), s.dataStore.Store(), s.cache, s.genesis, s.logger) if currentHeight, err := s.store.Height(s.ctx); err != nil { From eeea8b66dae6c70aa245ef92535adc27de591923 Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 10:34:47 +0000 Subject: [PATCH 04/12] chore: bump sonic version to work with 1.25 --- test/docker-e2e/go.mod | 7 ++++--- test/docker-e2e/go.sum | 20 +++++++++----------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/test/docker-e2e/go.mod b/test/docker-e2e/go.mod index 099fdcbb3..e6ea03df7 100644 --- a/test/docker-e2e/go.mod +++ b/test/docker-e2e/go.mod @@ -16,10 +16,11 @@ require ( github.com/StackExchange/wmi v1.2.1 // indirect github.com/bcp-innovations/hyperlane-cosmos v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.20.0 // indirect - github.com/bytedance/sonic v1.13.1 // indirect - github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic v1.14.2 // indirect + github.com/bytedance/sonic/loader v0.4.0 // indirect github.com/celestiaorg/go-square/v3 v3.0.2 // indirect - github.com/cloudwego/base64x v0.1.5 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect github.com/consensys/gnark-crypto v0.18.1 // indirect github.com/containerd/continuity v0.4.5 // indirect github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect diff --git a/test/docker-e2e/go.sum b/test/docker-e2e/go.sum index 000946024..92268cc0b 100644 --- a/test/docker-e2e/go.sum +++ b/test/docker-e2e/go.sum @@ -126,11 +126,12 @@ github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/ github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= github.com/bufbuild/protocompile v0.14.1 h1:iA73zAf/fyljNjQKwYzUHD6AD4R8KMasmwa/FBatYVw= github.com/bufbuild/protocompile v0.14.1/go.mod h1:ppVdAIhbr2H8asPk6k4pY7t9zB1OU5DoEw9xY/FUi1c= -github.com/bytedance/sonic v1.13.1 h1:Jyd5CIvdFnkOWuKXr+wm4Nyk2h0yAFsr8ucJgEasO3g= -github.com/bytedance/sonic v1.13.1/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= -github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= +github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980= +github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o= +github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/celestiaorg/celestia-core v0.39.4 h1:h0WaG8KsP0JyiAVhHipoIgvBP0CYLG/9whUccy1lDlY= github.com/celestiaorg/celestia-core v0.39.4/go.mod h1:t7cSYwLFmpz5RjIBpC3QjpbRoa+RfQ0ULdh+LciKuq8= @@ -162,9 +163,8 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= -github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= @@ -561,12 +561,10 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/klauspost/reedsolomon v1.12.5 h1:4cJuyH926If33BeDgiZpI5OU0pE+wUHZvMSyNGqN73Y= github.com/klauspost/reedsolomon v1.12.5/go.mod h1:LkXRjLYGM8K/iQfujYnaPeDmhZLqkrGUyG9p7zs5L68= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -853,6 +851,7 @@ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1F github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -1233,7 +1232,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.17 h1:KEVeLJkUywCKVsnLIDlD/5gtayKp8VoCkksHCGGfT9Y= nhooyr.io/websocket v1.8.17/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= From 19add9cc2cb97ba4fdd79744fb51007249de0d53 Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 11:23:52 +0000 Subject: [PATCH 05/12] chore: adding tracing for da submitter --- block/components.go | 10 +- .../submitting/da_submitter_tracing.go | 97 +++++++++ .../submitting/da_submitter_tracing_test.go | 190 ++++++++++++++++++ block/internal/submitting/submitter.go | 8 +- 4 files changed, 299 insertions(+), 6 deletions(-) create mode 100644 block/internal/submitting/da_submitter_tracing.go create mode 100644 block/internal/submitting/da_submitter_tracing_test.go diff --git a/block/components.go b/block/components.go index a7a883bb3..17cbe018d 100644 --- a/block/components.go +++ b/block/components.go @@ -157,7 +157,10 @@ func NewSyncComponents( } // Create submitter for sync nodes (no signer, only DA inclusion processing) - daSubmitter := submitting.NewDASubmitter(daClient, config, genesis, blockOpts, metrics, logger) + var daSubmitter submitting.DASubmitterAPI = submitting.NewDASubmitter(daClient, config, genesis, blockOpts, metrics, logger) + if config.Instrumentation.IsTracingEnabled() { + daSubmitter = submitting.WithTracingDASubmitter(daSubmitter) + } submitter := submitting.NewSubmitter( store, exec, @@ -256,7 +259,10 @@ func NewAggregatorComponents( }, nil } - daSubmitter := submitting.NewDASubmitter(daClient, config, genesis, blockOpts, metrics, logger) + var daSubmitter submitting.DASubmitterAPI = submitting.NewDASubmitter(daClient, config, genesis, blockOpts, metrics, logger) + if config.Instrumentation.IsTracingEnabled() { + daSubmitter = submitting.WithTracingDASubmitter(daSubmitter) + } submitter := submitting.NewSubmitter( store, exec, diff --git a/block/internal/submitting/da_submitter_tracing.go b/block/internal/submitting/da_submitter_tracing.go new file mode 100644 index 000000000..e3c531fcf --- /dev/null +++ b/block/internal/submitting/da_submitter_tracing.go @@ -0,0 +1,97 @@ +package submitting + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/evstack/ev-node/block/internal/cache" + "github.com/evstack/ev-node/pkg/genesis" + "github.com/evstack/ev-node/pkg/signer" + "github.com/evstack/ev-node/types" +) + +var _ DASubmitterAPI = (*tracedDASubmitter)(nil) + +// tracedDASubmitter wraps a DASubmitterAPI with OpenTelemetry tracing. +type tracedDASubmitter struct { + inner DASubmitterAPI + tracer trace.Tracer +} + +// WithTracingDASubmitter wraps a DASubmitterAPI with OpenTelemetry tracing. +func WithTracingDASubmitter(inner DASubmitterAPI) DASubmitterAPI { + return &tracedDASubmitter{ + inner: inner, + tracer: otel.Tracer("ev-node/da-submitter"), + } +} + +func (t *tracedDASubmitter) SubmitHeaders(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error { + ctx, span := t.tracer.Start(ctx, "DASubmitter.SubmitHeaders", + trace.WithAttributes( + attribute.Int("header.count", len(headers)), + ), + ) + defer span.End() + + // calculate total size + var totalBytes int + for _, h := range marshalledHeaders { + totalBytes += len(h) + } + span.SetAttributes(attribute.Int("header.total_bytes", totalBytes)) + + // add height range if headers present + if len(headers) > 0 { + span.SetAttributes( + attribute.Int64("header.start_height", int64(headers[0].Height())), + attribute.Int64("header.end_height", int64(headers[len(headers)-1].Height())), + ) + } + + err := t.inner.SubmitHeaders(ctx, headers, marshalledHeaders, cache, signer) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (t *tracedDASubmitter) SubmitData(ctx context.Context, signedDataList []*types.SignedData, marshalledData [][]byte, cache cache.Manager, signer signer.Signer, genesis genesis.Genesis) error { + ctx, span := t.tracer.Start(ctx, "DASubmitter.SubmitData", + trace.WithAttributes( + attribute.Int("data.count", len(signedDataList)), + ), + ) + defer span.End() + + // calculate total size + var totalBytes int + for _, d := range marshalledData { + totalBytes += len(d) + } + span.SetAttributes(attribute.Int("data.total_bytes", totalBytes)) + + // add height range if data present + if len(signedDataList) > 0 { + span.SetAttributes( + attribute.Int64("data.start_height", int64(signedDataList[0].Height())), + attribute.Int64("data.end_height", int64(signedDataList[len(signedDataList)-1].Height())), + ) + } + + err := t.inner.SubmitData(ctx, signedDataList, marshalledData, cache, signer, genesis) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} diff --git a/block/internal/submitting/da_submitter_tracing_test.go b/block/internal/submitting/da_submitter_tracing_test.go new file mode 100644 index 000000000..6edc5c5ec --- /dev/null +++ b/block/internal/submitting/da_submitter_tracing_test.go @@ -0,0 +1,190 @@ +package submitting + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/evstack/ev-node/block/internal/cache" + "github.com/evstack/ev-node/pkg/genesis" + "github.com/evstack/ev-node/pkg/signer" + "github.com/evstack/ev-node/pkg/telemetry/testutil" + "github.com/evstack/ev-node/types" +) + +type mockDASubmitterAPI struct { + submitHeadersFn func(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error + submitDataFn func(ctx context.Context, signedDataList []*types.SignedData, marshalledData [][]byte, cache cache.Manager, signer signer.Signer, genesis genesis.Genesis) error +} + +func (m *mockDASubmitterAPI) SubmitHeaders(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error { + if m.submitHeadersFn != nil { + return m.submitHeadersFn(ctx, headers, marshalledHeaders, cache, signer) + } + return nil +} + +func (m *mockDASubmitterAPI) SubmitData(ctx context.Context, signedDataList []*types.SignedData, marshalledData [][]byte, cache cache.Manager, signer signer.Signer, genesis genesis.Genesis) error { + if m.submitDataFn != nil { + return m.submitDataFn(ctx, signedDataList, marshalledData, cache, signer, genesis) + } + return nil +} + +func setupDASubmitterTrace(t *testing.T, inner DASubmitterAPI) (DASubmitterAPI, *tracetest.SpanRecorder) { + t.Helper() + sr := tracetest.NewSpanRecorder() + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) + otel.SetTracerProvider(tp) + return WithTracingDASubmitter(inner), sr +} + +func TestTracedDASubmitter_SubmitHeaders_Success(t *testing.T) { + mock := &mockDASubmitterAPI{ + submitHeadersFn: func(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error { + return nil + }, + } + submitter, sr := setupDASubmitterTrace(t, mock) + ctx := context.Background() + + headers := []*types.SignedHeader{ + {Header: types.Header{BaseHeader: types.BaseHeader{Height: 100}}}, + {Header: types.Header{BaseHeader: types.BaseHeader{Height: 101}}}, + {Header: types.Header{BaseHeader: types.BaseHeader{Height: 102}}}, + } + marshalledHeaders := [][]byte{ + []byte("header1"), + []byte("header2"), + []byte("header3"), + } + + err := submitter.SubmitHeaders(ctx, headers, marshalledHeaders, nil, nil) + require.NoError(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "DASubmitter.SubmitHeaders", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "header.count", 3) + testutil.RequireAttribute(t, attrs, "header.total_bytes", 21) // 7+7+7 + testutil.RequireAttribute(t, attrs, "header.start_height", int64(100)) + testutil.RequireAttribute(t, attrs, "header.end_height", int64(102)) +} + +func TestTracedDASubmitter_SubmitHeaders_Error(t *testing.T) { + expectedErr := errors.New("DA submission failed") + mock := &mockDASubmitterAPI{ + submitHeadersFn: func(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error { + return expectedErr + }, + } + submitter, sr := setupDASubmitterTrace(t, mock) + ctx := context.Background() + + headers := []*types.SignedHeader{ + {Header: types.Header{BaseHeader: types.BaseHeader{Height: 100}}}, + } + marshalledHeaders := [][]byte{[]byte("header1")} + + err := submitter.SubmitHeaders(ctx, headers, marshalledHeaders, nil, nil) + require.Error(t, err) + require.Equal(t, expectedErr, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) + require.Equal(t, expectedErr.Error(), span.Status().Description) +} + +func TestTracedDASubmitter_SubmitHeaders_Empty(t *testing.T) { + mock := &mockDASubmitterAPI{ + submitHeadersFn: func(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error { + return nil + }, + } + submitter, sr := setupDASubmitterTrace(t, mock) + ctx := context.Background() + + err := submitter.SubmitHeaders(ctx, []*types.SignedHeader{}, [][]byte{}, nil, nil) + require.NoError(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "header.count", 0) + testutil.RequireAttribute(t, attrs, "header.total_bytes", 0) +} + +func TestTracedDASubmitter_SubmitData_Success(t *testing.T) { + mock := &mockDASubmitterAPI{ + submitDataFn: func(ctx context.Context, signedDataList []*types.SignedData, marshalledData [][]byte, cache cache.Manager, signer signer.Signer, genesis genesis.Genesis) error { + return nil + }, + } + submitter, sr := setupDASubmitterTrace(t, mock) + ctx := context.Background() + + signedDataList := []*types.SignedData{ + {Data: types.Data{Metadata: &types.Metadata{Height: 100}}}, + {Data: types.Data{Metadata: &types.Metadata{Height: 101}}}, + } + marshalledData := [][]byte{ + []byte("data1data1"), + []byte("data2data2"), + } + + err := submitter.SubmitData(ctx, signedDataList, marshalledData, nil, nil, genesis.Genesis{}) + require.NoError(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "DASubmitter.SubmitData", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "data.count", 2) + testutil.RequireAttribute(t, attrs, "data.total_bytes", 20) // 10+10 + testutil.RequireAttribute(t, attrs, "data.start_height", int64(100)) + testutil.RequireAttribute(t, attrs, "data.end_height", int64(101)) +} + +func TestTracedDASubmitter_SubmitData_Error(t *testing.T) { + expectedErr := errors.New("data submission failed") + mock := &mockDASubmitterAPI{ + submitDataFn: func(ctx context.Context, signedDataList []*types.SignedData, marshalledData [][]byte, cache cache.Manager, signer signer.Signer, genesis genesis.Genesis) error { + return expectedErr + }, + } + submitter, sr := setupDASubmitterTrace(t, mock) + ctx := context.Background() + + signedDataList := []*types.SignedData{ + {Data: types.Data{Metadata: &types.Metadata{Height: 100}}}, + } + marshalledData := [][]byte{[]byte("data1")} + + err := submitter.SubmitData(ctx, signedDataList, marshalledData, nil, nil, genesis.Genesis{}) + require.Error(t, err) + require.Equal(t, expectedErr, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) + require.Equal(t, expectedErr.Error(), span.Status().Description) +} diff --git a/block/internal/submitting/submitter.go b/block/internal/submitting/submitter.go index e6a2328e9..3e1d96777 100644 --- a/block/internal/submitting/submitter.go +++ b/block/internal/submitting/submitter.go @@ -23,8 +23,8 @@ import ( "github.com/evstack/ev-node/types" ) -// daSubmitterAPI defines minimal methods needed by Submitter for DA submissions. -type daSubmitterAPI interface { +// DASubmitterAPI defines minimal methods needed by Submitter for DA submissions. +type DASubmitterAPI interface { SubmitHeaders(ctx context.Context, headers []*types.SignedHeader, marshalledHeaders [][]byte, cache cache.Manager, signer signer.Signer) error SubmitData(ctx context.Context, signedDataList []*types.SignedData, marshalledData [][]byte, cache cache.Manager, signer signer.Signer, genesis genesis.Genesis) error } @@ -43,7 +43,7 @@ type Submitter struct { metrics *common.Metrics // DA submitter - daSubmitter daSubmitterAPI + daSubmitter DASubmitterAPI // Optional signer (only for aggregator nodes) signer signer.Signer @@ -80,7 +80,7 @@ func NewSubmitter( metrics *common.Metrics, config config.Config, genesis genesis.Genesis, - daSubmitter daSubmitterAPI, + daSubmitter DASubmitterAPI, sequencer coresequencer.Sequencer, // Can be nil for sync nodes signer signer.Signer, // Can be nil for sync nodes logger zerolog.Logger, From 380933b97212a42bb938af266572401c6ac2d30e Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 14:10:10 +0000 Subject: [PATCH 06/12] chore: adding forced inclusion tracing --- block/forced_inclusion_tracing.go | 55 +++++++ block/forced_inclusion_tracing_test.go | 155 ++++++++++++++++++ .../syncing/forced_inclusion_tracing.go | 65 ++++++++ block/internal/syncing/syncer.go | 5 +- block/public.go | 6 +- 5 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 block/forced_inclusion_tracing.go create mode 100644 block/forced_inclusion_tracing_test.go create mode 100644 block/internal/syncing/forced_inclusion_tracing.go diff --git a/block/forced_inclusion_tracing.go b/block/forced_inclusion_tracing.go new file mode 100644 index 000000000..4d518d184 --- /dev/null +++ b/block/forced_inclusion_tracing.go @@ -0,0 +1,55 @@ +package block + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" +) + +var _ ForcedInclusionRetriever = (*tracedForcedInclusionRetriever)(nil) + +type tracedForcedInclusionRetriever struct { + inner ForcedInclusionRetriever + tracer trace.Tracer +} + +// WithTracingForcedInclusionRetriever wraps a ForcedInclusionRetriever with OpenTelemetry tracing. +func WithTracingForcedInclusionRetriever(inner ForcedInclusionRetriever) ForcedInclusionRetriever { + return &tracedForcedInclusionRetriever{ + inner: inner, + tracer: otel.Tracer("ev-node/forced-inclusion"), + } +} + +func (t *tracedForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { + ctx, span := t.tracer.Start(ctx, "ForcedInclusionRetriever.RetrieveForcedIncludedTxs", + trace.WithAttributes( + attribute.Int64("da.height", int64(daHeight)), + ), + ) + defer span.End() + + event, err := t.inner.RetrieveForcedIncludedTxs(ctx, daHeight) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return event, err + } + + if event != nil { + span.SetAttributes( + attribute.Int64("event.start_da_height", int64(event.StartDaHeight)), + attribute.Int64("event.end_da_height", int64(event.EndDaHeight)), + attribute.Int("event.tx_count", len(event.Txs)), + ) + } + + return event, nil +} + +func (t *tracedForcedInclusionRetriever) Stop() { + t.inner.Stop() +} diff --git a/block/forced_inclusion_tracing_test.go b/block/forced_inclusion_tracing_test.go new file mode 100644 index 000000000..259a6a347 --- /dev/null +++ b/block/forced_inclusion_tracing_test.go @@ -0,0 +1,155 @@ +package block + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/evstack/ev-node/pkg/telemetry/testutil" +) + +type mockForcedInclusionRetriever struct { + retrieveFn func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) + stopCalled bool +} + +func (m *mockForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { + if m.retrieveFn != nil { + return m.retrieveFn(ctx, daHeight) + } + return nil, nil +} + +func (m *mockForcedInclusionRetriever) Stop() { + m.stopCalled = true +} + +func setupForcedInclusionTrace(t *testing.T, inner ForcedInclusionRetriever) (ForcedInclusionRetriever, *tracetest.SpanRecorder) { + t.Helper() + sr := tracetest.NewSpanRecorder() + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) + otel.SetTracerProvider(tp) + return WithTracingForcedInclusionRetriever(inner), sr +} + +func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_Success(t *testing.T) { + expectedEvent := &ForcedInclusionEvent{ + Timestamp: time.Now(), + StartDaHeight: 100, + EndDaHeight: 109, + Txs: [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3")}, + } + + mock := &mockForcedInclusionRetriever{ + retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { + return expectedEvent, nil + }, + } + retriever, sr := setupForcedInclusionTrace(t, mock) + ctx := context.Background() + + event, err := retriever.RetrieveForcedIncludedTxs(ctx, 109) + require.NoError(t, err) + require.Equal(t, expectedEvent, event) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "ForcedInclusionRetriever.RetrieveForcedIncludedTxs", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "da.height", int64(109)) + testutil.RequireAttribute(t, attrs, "event.start_da_height", int64(100)) + testutil.RequireAttribute(t, attrs, "event.end_da_height", int64(109)) + testutil.RequireAttribute(t, attrs, "event.tx_count", 3) +} + +func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_Error(t *testing.T) { + expectedErr := errors.New("retrieval failed") + mock := &mockForcedInclusionRetriever{ + retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { + return nil, expectedErr + }, + } + retriever, sr := setupForcedInclusionTrace(t, mock) + ctx := context.Background() + + event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100) + require.Error(t, err) + require.Equal(t, expectedErr, err) + require.Nil(t, event) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) + require.Equal(t, expectedErr.Error(), span.Status().Description) +} + +func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_NilEvent(t *testing.T) { + mock := &mockForcedInclusionRetriever{ + retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { + return nil, nil + }, + } + retriever, sr := setupForcedInclusionTrace(t, mock) + ctx := context.Background() + + event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100) + require.NoError(t, err) + require.Nil(t, event) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Unset, span.Status().Code) + + // only da.height should be present since event is nil + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "da.height", int64(100)) +} + +func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_EmptyTxs(t *testing.T) { + expectedEvent := &ForcedInclusionEvent{ + Timestamp: time.Now(), + StartDaHeight: 100, + EndDaHeight: 100, + Txs: [][]byte{}, + } + + mock := &mockForcedInclusionRetriever{ + retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { + return expectedEvent, nil + }, + } + retriever, sr := setupForcedInclusionTrace(t, mock) + ctx := context.Background() + + event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100) + require.NoError(t, err) + require.Equal(t, expectedEvent, event) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "event.tx_count", 0) +} + +func TestTracedForcedInclusionRetriever_Stop(t *testing.T) { + mock := &mockForcedInclusionRetriever{} + retriever, _ := setupForcedInclusionTrace(t, mock) + + retriever.Stop() + require.True(t, mock.stopCalled) +} diff --git a/block/internal/syncing/forced_inclusion_tracing.go b/block/internal/syncing/forced_inclusion_tracing.go new file mode 100644 index 000000000..816c82a2a --- /dev/null +++ b/block/internal/syncing/forced_inclusion_tracing.go @@ -0,0 +1,65 @@ +package syncing + +import ( + "context" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/evstack/ev-node/block/internal/da" +) + +// forcedInclusionRetriever defines the interface for retrieving forced inclusion +// transactions from DA. This local interface is defined to avoid import cycles +// since block/ imports syncing/. +type forcedInclusionRetriever interface { + RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*da.ForcedInclusionEvent, error) + Stop() +} + +var _ forcedInclusionRetriever = (*tracedForcedInclusionRetriever)(nil) + +type tracedForcedInclusionRetriever struct { + inner forcedInclusionRetriever + tracer trace.Tracer +} + +// withTracingForcedInclusionRetriever wraps a forcedInclusionRetriever with OpenTelemetry tracing. +func withTracingForcedInclusionRetriever(inner forcedInclusionRetriever) forcedInclusionRetriever { + return &tracedForcedInclusionRetriever{ + inner: inner, + tracer: otel.Tracer("ev-node/forced-inclusion"), + } +} + +func (t *tracedForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*da.ForcedInclusionEvent, error) { + ctx, span := t.tracer.Start(ctx, "ForcedInclusionRetriever.RetrieveForcedIncludedTxs", + trace.WithAttributes( + attribute.Int64("da.height", int64(daHeight)), + ), + ) + defer span.End() + + event, err := t.inner.RetrieveForcedIncludedTxs(ctx, daHeight) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return event, err + } + + if event != nil { + span.SetAttributes( + attribute.Int64("event.start_da_height", int64(event.StartDaHeight)), + attribute.Int64("event.end_da_height", int64(event.EndDaHeight)), + attribute.Int("event.tx_count", len(event.Txs)), + ) + } + + return event, nil +} + +func (t *tracedForcedInclusionRetriever) Stop() { + t.inner.Stop() +} diff --git a/block/internal/syncing/syncer.go b/block/internal/syncing/syncer.go index 6365c548b..92d326315 100644 --- a/block/internal/syncing/syncer.go +++ b/block/internal/syncing/syncer.go @@ -104,7 +104,7 @@ type Syncer struct { // Handlers daRetriever DARetriever - fiRetriever *da.ForcedInclusionRetriever + fiRetriever forcedInclusionRetriever p2pHandler p2pHandler // Forced inclusion tracking @@ -205,6 +205,9 @@ func (s *Syncer) Start(ctx context.Context) error { s.daRetriever = WithTracingDARetriever(s.daRetriever) } s.fiRetriever = da.NewForcedInclusionRetriever(s.daClient, s.logger, s.config, s.genesis.DAStartHeight, s.genesis.DAEpochForcedInclusion) + if s.config.Instrumentation.IsTracingEnabled() { + s.fiRetriever = withTracingForcedInclusionRetriever(s.fiRetriever) + } s.p2pHandler = NewP2PHandler(s.headerStore.Store(), s.dataStore.Store(), s.cache, s.genesis, s.logger) if currentHeight, err := s.store.Height(s.ctx); err != nil { s.logger.Error().Err(err).Msg("failed to set initial processed height for p2p handler") diff --git a/block/public.go b/block/public.go index 54bba68c7..0041982b5 100644 --- a/block/public.go +++ b/block/public.go @@ -83,5 +83,9 @@ func NewForcedInclusionRetriever( logger zerolog.Logger, daStartHeight, daEpochSize uint64, ) ForcedInclusionRetriever { - return da.NewForcedInclusionRetriever(client, logger, cfg, daStartHeight, daEpochSize) + base := da.NewForcedInclusionRetriever(client, logger, cfg, daStartHeight, daEpochSize) + if cfg.Instrumentation.IsTracingEnabled() { + return WithTracingForcedInclusionRetriever(base) + } + return base } From 8298a39df83a5c55b6a76c758025e277d578e6e9 Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 14:19:28 +0000 Subject: [PATCH 07/12] chore: handle tracing internally --- .../internal/da/forced_inclusion_retriever.go | 8 +++++-- .../forced_inclusion_tracing.go | 21 +++++++------------ block/internal/syncing/syncer.go | 5 +---- block/public.go | 7 ++----- 4 files changed, 17 insertions(+), 24 deletions(-) rename block/internal/{syncing => da}/forced_inclusion_tracing.go (62%) diff --git a/block/internal/da/forced_inclusion_retriever.go b/block/internal/da/forced_inclusion_retriever.go index 7b07b7d5d..48968e17f 100644 --- a/block/internal/da/forced_inclusion_retriever.go +++ b/block/internal/da/forced_inclusion_retriever.go @@ -40,7 +40,7 @@ func NewForcedInclusionRetriever( logger zerolog.Logger, cfg config.Config, daStartHeight, daEpochSize uint64, -) *ForcedInclusionRetriever { +) ForcedInclusionRetrieverAPI { retrieverLogger := logger.With().Str("component", "forced_inclusion_retriever").Logger() // Create async block retriever for background prefetching @@ -54,13 +54,17 @@ func NewForcedInclusionRetriever( ) asyncFetcher.Start() - return &ForcedInclusionRetriever{ + base := &ForcedInclusionRetriever{ client: client, logger: retrieverLogger, daStartHeight: daStartHeight, daEpochSize: daEpochSize, asyncFetcher: asyncFetcher, } + if cfg.Instrumentation.IsTracingEnabled() { + return withTracingForcedInclusionRetriever(base) + } + return base } // Stop stops the background prefetcher. diff --git a/block/internal/syncing/forced_inclusion_tracing.go b/block/internal/da/forced_inclusion_tracing.go similarity index 62% rename from block/internal/syncing/forced_inclusion_tracing.go rename to block/internal/da/forced_inclusion_tracing.go index 816c82a2a..fbc28a665 100644 --- a/block/internal/syncing/forced_inclusion_tracing.go +++ b/block/internal/da/forced_inclusion_tracing.go @@ -1,4 +1,4 @@ -package syncing +package da import ( "context" @@ -7,34 +7,29 @@ import ( "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" - - "github.com/evstack/ev-node/block/internal/da" ) -// forcedInclusionRetriever defines the interface for retrieving forced inclusion -// transactions from DA. This local interface is defined to avoid import cycles -// since block/ imports syncing/. -type forcedInclusionRetriever interface { - RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*da.ForcedInclusionEvent, error) +// ForcedInclusionRetrieverAPI defines the interface for retrieving forced inclusion transactions from DA. +type ForcedInclusionRetrieverAPI interface { + RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) Stop() } -var _ forcedInclusionRetriever = (*tracedForcedInclusionRetriever)(nil) +var _ ForcedInclusionRetrieverAPI = (*tracedForcedInclusionRetriever)(nil) type tracedForcedInclusionRetriever struct { - inner forcedInclusionRetriever + inner ForcedInclusionRetrieverAPI tracer trace.Tracer } -// withTracingForcedInclusionRetriever wraps a forcedInclusionRetriever with OpenTelemetry tracing. -func withTracingForcedInclusionRetriever(inner forcedInclusionRetriever) forcedInclusionRetriever { +func withTracingForcedInclusionRetriever(inner ForcedInclusionRetrieverAPI) ForcedInclusionRetrieverAPI { return &tracedForcedInclusionRetriever{ inner: inner, tracer: otel.Tracer("ev-node/forced-inclusion"), } } -func (t *tracedForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*da.ForcedInclusionEvent, error) { +func (t *tracedForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { ctx, span := t.tracer.Start(ctx, "ForcedInclusionRetriever.RetrieveForcedIncludedTxs", trace.WithAttributes( attribute.Int64("da.height", int64(daHeight)), diff --git a/block/internal/syncing/syncer.go b/block/internal/syncing/syncer.go index 92d326315..71f032230 100644 --- a/block/internal/syncing/syncer.go +++ b/block/internal/syncing/syncer.go @@ -104,7 +104,7 @@ type Syncer struct { // Handlers daRetriever DARetriever - fiRetriever forcedInclusionRetriever + fiRetriever da.ForcedInclusionRetrieverAPI p2pHandler p2pHandler // Forced inclusion tracking @@ -205,9 +205,6 @@ func (s *Syncer) Start(ctx context.Context) error { s.daRetriever = WithTracingDARetriever(s.daRetriever) } s.fiRetriever = da.NewForcedInclusionRetriever(s.daClient, s.logger, s.config, s.genesis.DAStartHeight, s.genesis.DAEpochForcedInclusion) - if s.config.Instrumentation.IsTracingEnabled() { - s.fiRetriever = withTracingForcedInclusionRetriever(s.fiRetriever) - } s.p2pHandler = NewP2PHandler(s.headerStore.Store(), s.dataStore.Store(), s.cache, s.genesis, s.logger) if currentHeight, err := s.store.Height(s.ctx); err != nil { s.logger.Error().Err(err).Msg("failed to set initial processed height for p2p handler") diff --git a/block/public.go b/block/public.go index 0041982b5..f8586eebc 100644 --- a/block/public.go +++ b/block/public.go @@ -77,15 +77,12 @@ type ForcedInclusionRetriever interface { // NewForcedInclusionRetriever creates a new forced inclusion retriever. // It internally creates and manages an AsyncBlockRetriever for background prefetching. +// Tracing is automatically enabled when configured. func NewForcedInclusionRetriever( client DAClient, cfg config.Config, logger zerolog.Logger, daStartHeight, daEpochSize uint64, ) ForcedInclusionRetriever { - base := da.NewForcedInclusionRetriever(client, logger, cfg, daStartHeight, daEpochSize) - if cfg.Instrumentation.IsTracingEnabled() { - return WithTracingForcedInclusionRetriever(base) - } - return base + return da.NewForcedInclusionRetriever(client, logger, cfg, daStartHeight, daEpochSize) } From 0afb107a9d34988b69817ea67cd44b371ccaa056 Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 14:23:48 +0000 Subject: [PATCH 08/12] chore: removed duplicate tracer --- block/forced_inclusion_tracing.go | 55 --------- block/forced_inclusion_tracing_test.go | 155 ------------------------- 2 files changed, 210 deletions(-) delete mode 100644 block/forced_inclusion_tracing.go delete mode 100644 block/forced_inclusion_tracing_test.go diff --git a/block/forced_inclusion_tracing.go b/block/forced_inclusion_tracing.go deleted file mode 100644 index 4d518d184..000000000 --- a/block/forced_inclusion_tracing.go +++ /dev/null @@ -1,55 +0,0 @@ -package block - -import ( - "context" - - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/attribute" - "go.opentelemetry.io/otel/codes" - "go.opentelemetry.io/otel/trace" -) - -var _ ForcedInclusionRetriever = (*tracedForcedInclusionRetriever)(nil) - -type tracedForcedInclusionRetriever struct { - inner ForcedInclusionRetriever - tracer trace.Tracer -} - -// WithTracingForcedInclusionRetriever wraps a ForcedInclusionRetriever with OpenTelemetry tracing. -func WithTracingForcedInclusionRetriever(inner ForcedInclusionRetriever) ForcedInclusionRetriever { - return &tracedForcedInclusionRetriever{ - inner: inner, - tracer: otel.Tracer("ev-node/forced-inclusion"), - } -} - -func (t *tracedForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { - ctx, span := t.tracer.Start(ctx, "ForcedInclusionRetriever.RetrieveForcedIncludedTxs", - trace.WithAttributes( - attribute.Int64("da.height", int64(daHeight)), - ), - ) - defer span.End() - - event, err := t.inner.RetrieveForcedIncludedTxs(ctx, daHeight) - if err != nil { - span.RecordError(err) - span.SetStatus(codes.Error, err.Error()) - return event, err - } - - if event != nil { - span.SetAttributes( - attribute.Int64("event.start_da_height", int64(event.StartDaHeight)), - attribute.Int64("event.end_da_height", int64(event.EndDaHeight)), - attribute.Int("event.tx_count", len(event.Txs)), - ) - } - - return event, nil -} - -func (t *tracedForcedInclusionRetriever) Stop() { - t.inner.Stop() -} diff --git a/block/forced_inclusion_tracing_test.go b/block/forced_inclusion_tracing_test.go deleted file mode 100644 index 259a6a347..000000000 --- a/block/forced_inclusion_tracing_test.go +++ /dev/null @@ -1,155 +0,0 @@ -package block - -import ( - "context" - "errors" - "testing" - "time" - - "github.com/stretchr/testify/require" - "go.opentelemetry.io/otel" - "go.opentelemetry.io/otel/codes" - sdktrace "go.opentelemetry.io/otel/sdk/trace" - "go.opentelemetry.io/otel/sdk/trace/tracetest" - - "github.com/evstack/ev-node/pkg/telemetry/testutil" -) - -type mockForcedInclusionRetriever struct { - retrieveFn func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) - stopCalled bool -} - -func (m *mockForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { - if m.retrieveFn != nil { - return m.retrieveFn(ctx, daHeight) - } - return nil, nil -} - -func (m *mockForcedInclusionRetriever) Stop() { - m.stopCalled = true -} - -func setupForcedInclusionTrace(t *testing.T, inner ForcedInclusionRetriever) (ForcedInclusionRetriever, *tracetest.SpanRecorder) { - t.Helper() - sr := tracetest.NewSpanRecorder() - tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) - t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) - otel.SetTracerProvider(tp) - return WithTracingForcedInclusionRetriever(inner), sr -} - -func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_Success(t *testing.T) { - expectedEvent := &ForcedInclusionEvent{ - Timestamp: time.Now(), - StartDaHeight: 100, - EndDaHeight: 109, - Txs: [][]byte{[]byte("tx1"), []byte("tx2"), []byte("tx3")}, - } - - mock := &mockForcedInclusionRetriever{ - retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { - return expectedEvent, nil - }, - } - retriever, sr := setupForcedInclusionTrace(t, mock) - ctx := context.Background() - - event, err := retriever.RetrieveForcedIncludedTxs(ctx, 109) - require.NoError(t, err) - require.Equal(t, expectedEvent, event) - - spans := sr.Ended() - require.Len(t, spans, 1) - span := spans[0] - require.Equal(t, "ForcedInclusionRetriever.RetrieveForcedIncludedTxs", span.Name()) - require.Equal(t, codes.Unset, span.Status().Code) - - attrs := span.Attributes() - testutil.RequireAttribute(t, attrs, "da.height", int64(109)) - testutil.RequireAttribute(t, attrs, "event.start_da_height", int64(100)) - testutil.RequireAttribute(t, attrs, "event.end_da_height", int64(109)) - testutil.RequireAttribute(t, attrs, "event.tx_count", 3) -} - -func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_Error(t *testing.T) { - expectedErr := errors.New("retrieval failed") - mock := &mockForcedInclusionRetriever{ - retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { - return nil, expectedErr - }, - } - retriever, sr := setupForcedInclusionTrace(t, mock) - ctx := context.Background() - - event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100) - require.Error(t, err) - require.Equal(t, expectedErr, err) - require.Nil(t, event) - - spans := sr.Ended() - require.Len(t, spans, 1) - span := spans[0] - require.Equal(t, codes.Error, span.Status().Code) - require.Equal(t, expectedErr.Error(), span.Status().Description) -} - -func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_NilEvent(t *testing.T) { - mock := &mockForcedInclusionRetriever{ - retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { - return nil, nil - }, - } - retriever, sr := setupForcedInclusionTrace(t, mock) - ctx := context.Background() - - event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100) - require.NoError(t, err) - require.Nil(t, event) - - spans := sr.Ended() - require.Len(t, spans, 1) - span := spans[0] - require.Equal(t, codes.Unset, span.Status().Code) - - // only da.height should be present since event is nil - attrs := span.Attributes() - testutil.RequireAttribute(t, attrs, "da.height", int64(100)) -} - -func TestTracedForcedInclusionRetriever_RetrieveForcedIncludedTxs_EmptyTxs(t *testing.T) { - expectedEvent := &ForcedInclusionEvent{ - Timestamp: time.Now(), - StartDaHeight: 100, - EndDaHeight: 100, - Txs: [][]byte{}, - } - - mock := &mockForcedInclusionRetriever{ - retrieveFn: func(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { - return expectedEvent, nil - }, - } - retriever, sr := setupForcedInclusionTrace(t, mock) - ctx := context.Background() - - event, err := retriever.RetrieveForcedIncludedTxs(ctx, 100) - require.NoError(t, err) - require.Equal(t, expectedEvent, event) - - spans := sr.Ended() - require.Len(t, spans, 1) - span := spans[0] - - attrs := span.Attributes() - testutil.RequireAttribute(t, attrs, "event.tx_count", 0) -} - -func TestTracedForcedInclusionRetriever_Stop(t *testing.T) { - mock := &mockForcedInclusionRetriever{} - retriever, _ := setupForcedInclusionTrace(t, mock) - - retriever.Stop() - require.True(t, mock.stopCalled) -} From f79103894f1fa697c06a0ce84ef33fbd7deddce3 Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 14:28:59 +0000 Subject: [PATCH 09/12] chore: simplified naming --- .../internal/da/forced_inclusion_retriever.go | 18 ++++++++++++------ block/internal/da/forced_inclusion_tracing.go | 12 +++--------- block/internal/syncing/syncer.go | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/block/internal/da/forced_inclusion_retriever.go b/block/internal/da/forced_inclusion_retriever.go index 48968e17f..9b0ad5529 100644 --- a/block/internal/da/forced_inclusion_retriever.go +++ b/block/internal/da/forced_inclusion_retriever.go @@ -16,8 +16,14 @@ import ( // ErrForceInclusionNotConfigured is returned when the forced inclusion namespace is not configured. var ErrForceInclusionNotConfigured = errors.New("forced inclusion namespace not configured") -// ForcedInclusionRetriever handles retrieval of forced inclusion transactions from DA. -type ForcedInclusionRetriever struct { +// ForcedInclusionRetriever defines the interface for retrieving forced inclusion transactions from DA. +type ForcedInclusionRetriever interface { + RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) + Stop() +} + +// forcedInclusionRetriever handles retrieval of forced inclusion transactions from DA. +type forcedInclusionRetriever struct { client Client logger zerolog.Logger daEpochSize uint64 @@ -40,7 +46,7 @@ func NewForcedInclusionRetriever( logger zerolog.Logger, cfg config.Config, daStartHeight, daEpochSize uint64, -) ForcedInclusionRetrieverAPI { +) ForcedInclusionRetriever { retrieverLogger := logger.With().Str("component", "forced_inclusion_retriever").Logger() // Create async block retriever for background prefetching @@ -54,7 +60,7 @@ func NewForcedInclusionRetriever( ) asyncFetcher.Start() - base := &ForcedInclusionRetriever{ + base := &forcedInclusionRetriever{ client: client, logger: retrieverLogger, daStartHeight: daStartHeight, @@ -68,14 +74,14 @@ func NewForcedInclusionRetriever( } // Stop stops the background prefetcher. -func (r *ForcedInclusionRetriever) Stop() { +func (r *forcedInclusionRetriever) Stop() { r.asyncFetcher.Stop() } // RetrieveForcedIncludedTxs retrieves forced inclusion transactions at the given DA height. // It respects epoch boundaries and only fetches at epoch end. // It tries to get blocks from the async fetcher cache first, then falls back to sync fetching. -func (r *ForcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { +func (r *forcedInclusionRetriever) RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) { // when daStartHeight is not set or no namespace is configured, we retrieve nothing. if !r.client.HasForcedInclusionNamespace() { return nil, ErrForceInclusionNotConfigured diff --git a/block/internal/da/forced_inclusion_tracing.go b/block/internal/da/forced_inclusion_tracing.go index fbc28a665..7e777161f 100644 --- a/block/internal/da/forced_inclusion_tracing.go +++ b/block/internal/da/forced_inclusion_tracing.go @@ -9,20 +9,14 @@ import ( "go.opentelemetry.io/otel/trace" ) -// ForcedInclusionRetrieverAPI defines the interface for retrieving forced inclusion transactions from DA. -type ForcedInclusionRetrieverAPI interface { - RetrieveForcedIncludedTxs(ctx context.Context, daHeight uint64) (*ForcedInclusionEvent, error) - Stop() -} - -var _ ForcedInclusionRetrieverAPI = (*tracedForcedInclusionRetriever)(nil) +var _ ForcedInclusionRetriever = (*tracedForcedInclusionRetriever)(nil) type tracedForcedInclusionRetriever struct { - inner ForcedInclusionRetrieverAPI + inner ForcedInclusionRetriever tracer trace.Tracer } -func withTracingForcedInclusionRetriever(inner ForcedInclusionRetrieverAPI) ForcedInclusionRetrieverAPI { +func withTracingForcedInclusionRetriever(inner ForcedInclusionRetriever) ForcedInclusionRetriever { return &tracedForcedInclusionRetriever{ inner: inner, tracer: otel.Tracer("ev-node/forced-inclusion"), diff --git a/block/internal/syncing/syncer.go b/block/internal/syncing/syncer.go index 71f032230..ede86c9a2 100644 --- a/block/internal/syncing/syncer.go +++ b/block/internal/syncing/syncer.go @@ -104,7 +104,7 @@ type Syncer struct { // Handlers daRetriever DARetriever - fiRetriever da.ForcedInclusionRetrieverAPI + fiRetriever da.ForcedInclusionRetriever p2pHandler p2pHandler // Forced inclusion tracking From a289ef3579ee13622216702448474ecceaece74a Mon Sep 17 00:00:00 2001 From: chatton Date: Mon, 19 Jan 2026 14:49:15 +0000 Subject: [PATCH 10/12] chore: add store tracing --- node/full.go | 3 + pkg/store/tracing.go | 337 +++++++++++++++++++++++++++++++++++ pkg/store/tracing_test.go | 361 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 701 insertions(+) create mode 100644 pkg/store/tracing.go create mode 100644 pkg/store/tracing_test.go diff --git a/node/full.go b/node/full.go index c6086bb5d..874909c45 100644 --- a/node/full.go +++ b/node/full.go @@ -81,6 +81,9 @@ func newFullNode( mainKV := store.NewEvNodeKVStore(database) evstore := store.New(mainKV) + if nodeConfig.Instrumentation.IsTracingEnabled() { + evstore = store.WithTracingStore(evstore) + } headerSyncService, err := initHeaderSyncService(mainKV, nodeConfig, genesis, p2pClient, logger) if err != nil { diff --git a/pkg/store/tracing.go b/pkg/store/tracing.go new file mode 100644 index 000000000..201832a54 --- /dev/null +++ b/pkg/store/tracing.go @@ -0,0 +1,337 @@ +package store + +import ( + "context" + + ds "github.com/ipfs/go-datastore" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/evstack/ev-node/types" +) + +var _ Store = (*tracedStore)(nil) + +type tracedStore struct { + inner Store + tracer trace.Tracer +} + +// WithTracingStore wraps a Store with OpenTelemetry tracing. +func WithTracingStore(inner Store) Store { + return &tracedStore{ + inner: inner, + tracer: otel.Tracer("ev-node/store"), + } +} + +func (t *tracedStore) Height(ctx context.Context) (uint64, error) { + ctx, span := t.tracer.Start(ctx, "Store.Height") + defer span.End() + + height, err := t.inner.Height(ctx) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return height, err + } + + span.SetAttributes(attribute.Int64("height", int64(height))) + return height, nil +} + +func (t *tracedStore) GetBlockData(ctx context.Context, height uint64) (*types.SignedHeader, *types.Data, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetBlockData", + trace.WithAttributes(attribute.Int64("height", int64(height))), + ) + defer span.End() + + header, data, err := t.inner.GetBlockData(ctx, height) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return header, data, err + } + + return header, data, nil +} + +func (t *tracedStore) GetBlockByHash(ctx context.Context, hash []byte) (*types.SignedHeader, *types.Data, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetBlockByHash", + trace.WithAttributes(attribute.String("hash", string(hash))), + ) + defer span.End() + + header, data, err := t.inner.GetBlockByHash(ctx, hash) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return header, data, err + } + + if header != nil { + span.SetAttributes(attribute.Int64("height", int64(header.Height()))) + } + return header, data, nil +} + +func (t *tracedStore) GetSignature(ctx context.Context, height uint64) (*types.Signature, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetSignature", + trace.WithAttributes(attribute.Int64("height", int64(height))), + ) + defer span.End() + + sig, err := t.inner.GetSignature(ctx, height) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return sig, err + } + + return sig, nil +} + +func (t *tracedStore) GetSignatureByHash(ctx context.Context, hash []byte) (*types.Signature, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetSignatureByHash", + trace.WithAttributes(attribute.String("hash", string(hash))), + ) + defer span.End() + + sig, err := t.inner.GetSignatureByHash(ctx, hash) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return sig, err + } + + return sig, nil +} + +func (t *tracedStore) GetHeader(ctx context.Context, height uint64) (*types.SignedHeader, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetHeader", + trace.WithAttributes(attribute.Int64("height", int64(height))), + ) + defer span.End() + + header, err := t.inner.GetHeader(ctx, height) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return header, err + } + + return header, nil +} + +func (t *tracedStore) GetState(ctx context.Context) (types.State, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetState") + defer span.End() + + state, err := t.inner.GetState(ctx) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return state, err + } + + span.SetAttributes(attribute.Int64("state.height", int64(state.LastBlockHeight))) + return state, nil +} + +func (t *tracedStore) GetStateAtHeight(ctx context.Context, height uint64) (types.State, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetStateAtHeight", + trace.WithAttributes(attribute.Int64("height", int64(height))), + ) + defer span.End() + + state, err := t.inner.GetStateAtHeight(ctx, height) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return state, err + } + + return state, nil +} + +func (t *tracedStore) GetMetadata(ctx context.Context, key string) ([]byte, error) { + ctx, span := t.tracer.Start(ctx, "Store.GetMetadata", + trace.WithAttributes(attribute.String("key", key)), + ) + defer span.End() + + data, err := t.inner.GetMetadata(ctx, key) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return data, err + } + + span.SetAttributes(attribute.Int("value.size", len(data))) + return data, nil +} + +func (t *tracedStore) SetMetadata(ctx context.Context, key string, value []byte) error { + ctx, span := t.tracer.Start(ctx, "Store.SetMetadata", + trace.WithAttributes( + attribute.String("key", key), + attribute.Int("value.size", len(value)), + ), + ) + defer span.End() + + err := t.inner.SetMetadata(ctx, key, value) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (t *tracedStore) Rollback(ctx context.Context, height uint64, aggregator bool) error { + ctx, span := t.tracer.Start(ctx, "Store.Rollback", + trace.WithAttributes( + attribute.Int64("height", int64(height)), + attribute.Bool("aggregator", aggregator), + ), + ) + defer span.End() + + err := t.inner.Rollback(ctx, height, aggregator) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (t *tracedStore) Close() error { + return t.inner.Close() +} + +func (t *tracedStore) NewBatch(ctx context.Context) (Batch, error) { + ctx, span := t.tracer.Start(ctx, "Store.NewBatch") + defer span.End() + + batch, err := t.inner.NewBatch(ctx) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return nil, err + } + + return &tracedBatch{ + inner: batch, + tracer: t.tracer, + }, nil +} + +var _ Batch = (*tracedBatch)(nil) + +type tracedBatch struct { + inner Batch + tracer trace.Tracer +} + +func (b *tracedBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { + _, span := b.tracer.Start(context.Background(), "Batch.SaveBlockData", + trace.WithAttributes(attribute.Int64("height", int64(header.Height()))), + ) + defer span.End() + + err := b.inner.SaveBlockData(header, data, signature) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (b *tracedBatch) SetHeight(height uint64) error { + _, span := b.tracer.Start(context.Background(), "Batch.SetHeight", + trace.WithAttributes(attribute.Int64("height", int64(height))), + ) + defer span.End() + + err := b.inner.SetHeight(height) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (b *tracedBatch) UpdateState(state types.State) error { + _, span := b.tracer.Start(context.Background(), "Batch.UpdateState", + trace.WithAttributes(attribute.Int64("state.height", int64(state.LastBlockHeight))), + ) + defer span.End() + + err := b.inner.UpdateState(state) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (b *tracedBatch) Commit() error { + _, span := b.tracer.Start(context.Background(), "Batch.Commit") + defer span.End() + + err := b.inner.Commit() + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (b *tracedBatch) Put(key ds.Key, value []byte) error { + _, span := b.tracer.Start(context.Background(), "Batch.Put", + trace.WithAttributes( + attribute.String("key", key.String()), + attribute.Int("value.size", len(value)), + ), + ) + defer span.End() + + err := b.inner.Put(key, value) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} + +func (b *tracedBatch) Delete(key ds.Key) error { + _, span := b.tracer.Start(context.Background(), "Batch.Delete", + trace.WithAttributes(attribute.String("key", key.String())), + ) + defer span.End() + + err := b.inner.Delete(key) + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + return err + } + + return nil +} diff --git a/pkg/store/tracing_test.go b/pkg/store/tracing_test.go new file mode 100644 index 000000000..a5dca2417 --- /dev/null +++ b/pkg/store/tracing_test.go @@ -0,0 +1,361 @@ +package store + +import ( + "context" + "errors" + "testing" + + ds "github.com/ipfs/go-datastore" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/codes" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace/tracetest" + + "github.com/evstack/ev-node/pkg/telemetry/testutil" + "github.com/evstack/ev-node/types" +) + +type tracingMockStore struct { + heightFn func(ctx context.Context) (uint64, error) + getBlockDataFn func(ctx context.Context, height uint64) (*types.SignedHeader, *types.Data, error) + getBlockByHashFn func(ctx context.Context, hash []byte) (*types.SignedHeader, *types.Data, error) + getSignatureFn func(ctx context.Context, height uint64) (*types.Signature, error) + getSignatureByHash func(ctx context.Context, hash []byte) (*types.Signature, error) + getHeaderFn func(ctx context.Context, height uint64) (*types.SignedHeader, error) + getStateFn func(ctx context.Context) (types.State, error) + getStateAtHeightFn func(ctx context.Context, height uint64) (types.State, error) + getMetadataFn func(ctx context.Context, key string) ([]byte, error) + setMetadataFn func(ctx context.Context, key string, value []byte) error + rollbackFn func(ctx context.Context, height uint64, aggregator bool) error + newBatchFn func(ctx context.Context) (Batch, error) +} + +func (m *tracingMockStore) Height(ctx context.Context) (uint64, error) { + if m.heightFn != nil { + return m.heightFn(ctx) + } + return 0, nil +} + +func (m *tracingMockStore) GetBlockData(ctx context.Context, height uint64) (*types.SignedHeader, *types.Data, error) { + if m.getBlockDataFn != nil { + return m.getBlockDataFn(ctx, height) + } + return nil, nil, nil +} + +func (m *tracingMockStore) GetBlockByHash(ctx context.Context, hash []byte) (*types.SignedHeader, *types.Data, error) { + if m.getBlockByHashFn != nil { + return m.getBlockByHashFn(ctx, hash) + } + return nil, nil, nil +} + +func (m *tracingMockStore) GetSignature(ctx context.Context, height uint64) (*types.Signature, error) { + if m.getSignatureFn != nil { + return m.getSignatureFn(ctx, height) + } + return nil, nil +} + +func (m *tracingMockStore) GetSignatureByHash(ctx context.Context, hash []byte) (*types.Signature, error) { + if m.getSignatureByHash != nil { + return m.getSignatureByHash(ctx, hash) + } + return nil, nil +} + +func (m *tracingMockStore) GetHeader(ctx context.Context, height uint64) (*types.SignedHeader, error) { + if m.getHeaderFn != nil { + return m.getHeaderFn(ctx, height) + } + return nil, nil +} + +func (m *tracingMockStore) GetState(ctx context.Context) (types.State, error) { + if m.getStateFn != nil { + return m.getStateFn(ctx) + } + return types.State{}, nil +} + +func (m *tracingMockStore) GetStateAtHeight(ctx context.Context, height uint64) (types.State, error) { + if m.getStateAtHeightFn != nil { + return m.getStateAtHeightFn(ctx, height) + } + return types.State{}, nil +} + +func (m *tracingMockStore) GetMetadata(ctx context.Context, key string) ([]byte, error) { + if m.getMetadataFn != nil { + return m.getMetadataFn(ctx, key) + } + return nil, nil +} + +func (m *tracingMockStore) SetMetadata(ctx context.Context, key string, value []byte) error { + if m.setMetadataFn != nil { + return m.setMetadataFn(ctx, key, value) + } + return nil +} + +func (m *tracingMockStore) Rollback(ctx context.Context, height uint64, aggregator bool) error { + if m.rollbackFn != nil { + return m.rollbackFn(ctx, height, aggregator) + } + return nil +} + +func (m *tracingMockStore) Close() error { + return nil +} + +func (m *tracingMockStore) NewBatch(ctx context.Context) (Batch, error) { + if m.newBatchFn != nil { + return m.newBatchFn(ctx) + } + return &tracingMockBatch{}, nil +} + +type tracingMockBatch struct { + saveBlockDataFn func(header *types.SignedHeader, data *types.Data, signature *types.Signature) error + setHeightFn func(height uint64) error + updateStateFn func(state types.State) error + commitFn func() error + putFn func(key ds.Key, value []byte) error + deleteFn func(key ds.Key) error +} + +func (b *tracingMockBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { + if b.saveBlockDataFn != nil { + return b.saveBlockDataFn(header, data, signature) + } + return nil +} + +func (b *tracingMockBatch) SetHeight(height uint64) error { + if b.setHeightFn != nil { + return b.setHeightFn(height) + } + return nil +} + +func (b *tracingMockBatch) UpdateState(state types.State) error { + if b.updateStateFn != nil { + return b.updateStateFn(state) + } + return nil +} + +func (b *tracingMockBatch) Commit() error { + if b.commitFn != nil { + return b.commitFn() + } + return nil +} + +func (b *tracingMockBatch) Put(key ds.Key, value []byte) error { + if b.putFn != nil { + return b.putFn(key, value) + } + return nil +} + +func (b *tracingMockBatch) Delete(key ds.Key) error { + if b.deleteFn != nil { + return b.deleteFn(key) + } + return nil +} + +func setupStoreTrace(t *testing.T, inner Store) (Store, *tracetest.SpanRecorder) { + t.Helper() + sr := tracetest.NewSpanRecorder() + tp := sdktrace.NewTracerProvider(sdktrace.WithSpanProcessor(sr)) + t.Cleanup(func() { _ = tp.Shutdown(context.Background()) }) + otel.SetTracerProvider(tp) + return WithTracingStore(inner), sr +} + +func TestTracedStore_Height_Success(t *testing.T) { + mock := &tracingMockStore{ + heightFn: func(ctx context.Context) (uint64, error) { + return 100, nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + height, err := store.Height(ctx) + require.NoError(t, err) + require.Equal(t, uint64(100), height) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Store.Height", span.Name()) + require.Equal(t, codes.Unset, span.Status().Code) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "height", int64(100)) +} + +func TestTracedStore_Height_Error(t *testing.T) { + expectedErr := errors.New("height error") + mock := &tracingMockStore{ + heightFn: func(ctx context.Context) (uint64, error) { + return 0, expectedErr + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + _, err := store.Height(ctx) + require.Error(t, err) + require.Equal(t, expectedErr, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, codes.Error, span.Status().Code) +} + +func TestTracedStore_GetBlockData_Success(t *testing.T) { + expectedHeader := &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{Height: 50}}} + expectedData := &types.Data{} + mock := &tracingMockStore{ + getBlockDataFn: func(ctx context.Context, height uint64) (*types.SignedHeader, *types.Data, error) { + return expectedHeader, expectedData, nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + header, data, err := store.GetBlockData(ctx, 50) + require.NoError(t, err) + require.Equal(t, expectedHeader, header) + require.Equal(t, expectedData, data) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Store.GetBlockData", span.Name()) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "height", int64(50)) +} + +func TestTracedStore_GetState_Success(t *testing.T) { + expectedState := types.State{LastBlockHeight: 200} + mock := &tracingMockStore{ + getStateFn: func(ctx context.Context) (types.State, error) { + return expectedState, nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + state, err := store.GetState(ctx) + require.NoError(t, err) + require.Equal(t, expectedState, state) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Store.GetState", span.Name()) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "state.height", int64(200)) +} + +func TestTracedStore_Rollback_Success(t *testing.T) { + mock := &tracingMockStore{ + rollbackFn: func(ctx context.Context, height uint64, aggregator bool) error { + return nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + err := store.Rollback(ctx, 50, true) + require.NoError(t, err) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Store.Rollback", span.Name()) + + attrs := span.Attributes() + testutil.RequireAttribute(t, attrs, "height", int64(50)) + testutil.RequireAttribute(t, attrs, "aggregator", true) +} + +func TestTracedStore_NewBatch_Success(t *testing.T) { + mock := &tracingMockStore{ + newBatchFn: func(ctx context.Context) (Batch, error) { + return &tracingMockBatch{}, nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + batch, err := store.NewBatch(ctx) + require.NoError(t, err) + require.NotNil(t, batch) + + spans := sr.Ended() + require.Len(t, spans, 1) + span := spans[0] + require.Equal(t, "Store.NewBatch", span.Name()) +} + +func TestTracedBatch_Commit_Success(t *testing.T) { + mock := &tracingMockStore{ + newBatchFn: func(ctx context.Context) (Batch, error) { + return &tracingMockBatch{}, nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + batch, err := store.NewBatch(ctx) + require.NoError(t, err) + + err = batch.Commit() + require.NoError(t, err) + + spans := sr.Ended() + require.Len(t, spans, 2) + require.Equal(t, "Store.NewBatch", spans[0].Name()) + require.Equal(t, "Batch.Commit", spans[1].Name()) +} + +func TestTracedBatch_SaveBlockData_Success(t *testing.T) { + mock := &tracingMockStore{ + newBatchFn: func(ctx context.Context) (Batch, error) { + return &tracingMockBatch{}, nil + }, + } + store, sr := setupStoreTrace(t, mock) + ctx := context.Background() + + batch, err := store.NewBatch(ctx) + require.NoError(t, err) + + header := &types.SignedHeader{Header: types.Header{BaseHeader: types.BaseHeader{Height: 100}}} + data := &types.Data{} + sig := &types.Signature{} + + err = batch.SaveBlockData(header, data, sig) + require.NoError(t, err) + + spans := sr.Ended() + require.Len(t, spans, 2) + require.Equal(t, "Store.NewBatch", spans[0].Name()) + require.Equal(t, "Batch.SaveBlockData", spans[1].Name()) + + attrs := spans[1].Attributes() + testutil.RequireAttribute(t, attrs, "height", int64(100)) +} From 5232812675f455346c5d9ba0ba8d9c6a98e6d4fa Mon Sep 17 00:00:00 2001 From: chatton Date: Tue, 20 Jan 2026 12:43:24 +0000 Subject: [PATCH 11/12] chore: propagating context --- block/internal/cache/bench_test.go | 6 +- block/internal/cache/manager_test.go | 6 +- block/internal/cache/pending_base_test.go | 4 +- block/internal/cache/pending_data_test.go | 14 +- block/internal/cache/pending_headers_test.go | 12 +- block/internal/executing/executor.go | 18 +- .../executing/executor_restart_test.go | 7 +- .../da_submitter_integration_test.go | 17 +- .../internal/submitting/da_submitter_test.go | 36 ++-- block/internal/submitting/submitter_test.go | 30 +-- block/internal/syncing/syncer.go | 14 +- block/internal/syncing/syncer_test.go | 10 +- pkg/store/batch.go | 36 ++-- pkg/store/store_test.go | 137 +++++++------- pkg/store/tracing.go | 36 ++-- pkg/store/tracing_test.go | 40 ++-- pkg/store/types.go | 12 +- test/mocks/batch.go | 175 +++++++++--------- 18 files changed, 306 insertions(+), 304 deletions(-) diff --git a/block/internal/cache/bench_test.go b/block/internal/cache/bench_test.go index aef81866f..cb37b3563 100644 --- a/block/internal/cache/bench_test.go +++ b/block/internal/cache/bench_test.go @@ -40,13 +40,13 @@ func benchSetupStore(b *testing.B, n int, txsPer int, chainID string) store.Stor if err != nil { b.Fatal(err) } - if err := batch.SaveBlockData(h, d, &types.Signature{}); err != nil { + if err := batch.SaveBlockData(ctx, h, d, &types.Signature{}); err != nil { b.Fatal(err) } - if err := batch.SetHeight(uint64(i)); err != nil { + if err := batch.SetHeight(ctx, uint64(i)); err != nil { b.Fatal(err) } - if err := batch.Commit(); err != nil { + if err := batch.Commit(ctx); err != nil { b.Fatal(err) } } diff --git a/block/internal/cache/manager_test.go b/block/internal/cache/manager_test.go index 328a1de7d..cc36106cf 100644 --- a/block/internal/cache/manager_test.go +++ b/block/internal/cache/manager_test.go @@ -169,11 +169,11 @@ func TestPendingHeadersAndData_Flow(t *testing.T) { }{{h1, d1}, {h2, d2}, {h3, d3}} { batch, err := st.NewBatch(ctx) require.NoError(t, err) - err = batch.SaveBlockData(pair.h, pair.d, &types.Signature{}) + err = batch.SaveBlockData(ctx, pair.h, pair.d, &types.Signature{}) require.NoError(t, err) - err = batch.SetHeight(uint64(i + 1)) + err = batch.SetHeight(ctx, uint64(i+1)) require.NoError(t, err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(t, err) } diff --git a/block/internal/cache/pending_base_test.go b/block/internal/cache/pending_base_test.go index eb9734a03..ff6f73fab 100644 --- a/block/internal/cache/pending_base_test.go +++ b/block/internal/cache/pending_base_test.go @@ -60,8 +60,8 @@ func TestPendingBase_PersistLastSubmitted(t *testing.T) { // store height 3 to make numPending meaningful batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(3)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SetHeight(ctx, 3)) + require.NoError(t, batch.Commit(ctx)) assert.Equal(t, uint64(3), ph.NumPendingHeaders()) // set last submitted higher and ensure metadata is written diff --git a/block/internal/cache/pending_data_test.go b/block/internal/cache/pending_data_test.go index 75679a24f..2dd56cb63 100644 --- a/block/internal/cache/pending_data_test.go +++ b/block/internal/cache/pending_data_test.go @@ -29,9 +29,9 @@ func TestPendingData_BasicFlow(t *testing.T) { }{{h1, d1}, {h2, d2}, {h3, d3}} { batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(p.h, p.d, &types.Signature{})) - require.NoError(t, batch.SetHeight(uint64(i+1))) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, p.h, p.d, &types.Signature{})) + require.NoError(t, batch.SetHeight(ctx, uint64(i+1))) + require.NoError(t, batch.Commit(ctx)) } pendingData, err := NewPendingData(store, zerolog.Nop()) @@ -74,8 +74,8 @@ func TestPendingData_InitFromMetadata(t *testing.T) { // store height is 3 batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(3)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SetHeight(ctx, 3)) + require.NoError(t, batch.Commit(ctx)) pendingData, err := NewPendingData(store, zerolog.Nop()) require.NoError(t, err) @@ -90,8 +90,8 @@ func TestPendingData_GetPending_PropagatesFetchError(t *testing.T) { // Set height to 1 but do not save any block data batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(1)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SetHeight(ctx, 1)) + require.NoError(t, batch.Commit(ctx)) pendingData, err := NewPendingData(store, zerolog.Nop()) require.NoError(t, err) diff --git a/block/internal/cache/pending_headers_test.go b/block/internal/cache/pending_headers_test.go index 25c029700..40ab66117 100644 --- a/block/internal/cache/pending_headers_test.go +++ b/block/internal/cache/pending_headers_test.go @@ -29,9 +29,9 @@ func TestPendingHeaders_BasicFlow(t *testing.T) { }{{h1, d1}, {h2, d2}, {h3, d3}} { batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(p.h, p.d, &types.Signature{})) - require.NoError(t, batch.SetHeight(uint64(i+1))) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, p.h, p.d, &types.Signature{})) + require.NoError(t, batch.SetHeight(ctx, uint64(i+1))) + require.NoError(t, batch.Commit(ctx)) } pendingHeaders, err := NewPendingHeaders(store, zerolog.Nop()) @@ -72,9 +72,9 @@ func TestPendingHeaders_EmptyWhenUpToDate(t *testing.T) { h, d := types.GetRandomBlock(1, 1, "ph-up") batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(h, d, &types.Signature{})) - require.NoError(t, batch.SetHeight(1)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, h, d, &types.Signature{})) + require.NoError(t, batch.SetHeight(ctx, 1)) + require.NoError(t, batch.Commit(ctx)) pendingHeaders, err := NewPendingHeaders(store, zerolog.Nop()) require.NoError(t, err) diff --git a/block/internal/executing/executor.go b/block/internal/executing/executor.go index 86a3d0eb0..c9a553e46 100644 --- a/block/internal/executing/executor.go +++ b/block/internal/executing/executor.go @@ -224,13 +224,13 @@ func (e *Executor) initializeState() error { if err != nil { return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SetHeight(state.LastBlockHeight); err != nil { + if err := batch.SetHeight(e.ctx, state.LastBlockHeight); err != nil { return fmt.Errorf("failed to set store height: %w", err) } - if err := batch.UpdateState(state); err != nil { + if err := batch.UpdateState(e.ctx, state); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(); err != nil { + if err := batch.Commit(e.ctx); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } @@ -389,10 +389,10 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to create batch for early save: %w", err) } - if err = batch.SaveBlockData(header, data, &types.Signature{}); err != nil { + if err = batch.SaveBlockData(ctx, header, data, &types.Signature{}); err != nil { return fmt.Errorf("failed to save block data: %w", err) } - if err = batch.Commit(); err != nil { + if err = batch.Commit(ctx); err != nil { return fmt.Errorf("failed to commit early save batch: %w", err) } } @@ -433,19 +433,19 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SaveBlockData(header, data, &signature); err != nil { + if err := batch.SaveBlockData(ctx, header, data, &signature); err != nil { return fmt.Errorf("failed to save block: %w", err) } - if err := batch.SetHeight(newHeight); err != nil { + if err := batch.SetHeight(ctx, newHeight); err != nil { return fmt.Errorf("failed to update store height: %w", err) } - if err := batch.UpdateState(newState); err != nil { + if err := batch.UpdateState(ctx, newState); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(); err != nil { + if err := batch.Commit(ctx); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } diff --git a/block/internal/executing/executor_restart_test.go b/block/internal/executing/executor_restart_test.go index 0df53d8e9..6ec5a5c8f 100644 --- a/block/internal/executing/executor_restart_test.go +++ b/block/internal/executing/executor_restart_test.go @@ -156,11 +156,12 @@ func TestExecutor_RestartUsesPendingHeader(t *testing.T) { pendingHeader.DataHash = pendingData.DACommitment() // Save pending block data (this is what would happen during a crash) - batch, err := memStore.NewBatch(context.Background()) + ctx := context.Background() + batch, err := memStore.NewBatch(ctx) require.NoError(t, err) - err = batch.SaveBlockData(pendingHeader, pendingData, &types.Signature{}) + err = batch.SaveBlockData(ctx, pendingHeader, pendingData, &types.Signature{}) require.NoError(t, err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(t, err) // Stop first executor (simulating crash/restart) diff --git a/block/internal/submitting/da_submitter_integration_test.go b/block/internal/submitting/da_submitter_integration_test.go index 0c23f7d08..f9f026469 100644 --- a/block/internal/submitting/da_submitter_integration_test.go +++ b/block/internal/submitting/da_submitter_integration_test.go @@ -71,18 +71,19 @@ func TestDASubmitter_SubmitHeadersAndData_MarksInclusionAndUpdatesLastSubmitted( sig2t := types.Signature(sig2) // Save block 1 - batch1, err := st.NewBatch(context.Background()) + ctx := context.Background() + batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(hdr1, data1, &sig1t)) - require.NoError(t, batch1.SetHeight(1)) - require.NoError(t, batch1.Commit()) + require.NoError(t, batch1.SaveBlockData(ctx, hdr1, data1, &sig1t)) + require.NoError(t, batch1.SetHeight(ctx, 1)) + require.NoError(t, batch1.Commit(ctx)) // Save block 2 - batch2, err := st.NewBatch(context.Background()) + batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(hdr2, data2, &sig2t)) - require.NoError(t, batch2.SetHeight(2)) - require.NoError(t, batch2.Commit()) + require.NoError(t, batch2.SaveBlockData(ctx, hdr2, data2, &sig2t)) + require.NoError(t, batch2.SetHeight(ctx, 2)) + require.NoError(t, batch2.Commit(ctx)) // Mock DA client client := mocks.NewMockClient(t) diff --git a/block/internal/submitting/da_submitter_test.go b/block/internal/submitting/da_submitter_test.go index 476011fe8..66598f302 100644 --- a/block/internal/submitting/da_submitter_test.go +++ b/block/internal/submitting/da_submitter_test.go @@ -201,16 +201,16 @@ func TestDASubmitter_SubmitHeaders_Success(t *testing.T) { // Save block 1 batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(header1, data1, &sig1)) - require.NoError(t, batch1.SetHeight(1)) - require.NoError(t, batch1.Commit()) + require.NoError(t, batch1.SaveBlockData(ctx, header1, data1, &sig1)) + require.NoError(t, batch1.SetHeight(ctx, 1)) + require.NoError(t, batch1.Commit(ctx)) // Save block 2 batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(header2, data2, &sig2)) - require.NoError(t, batch2.SetHeight(2)) - require.NoError(t, batch2.Commit()) + require.NoError(t, batch2.SaveBlockData(ctx, header2, data2, &sig2)) + require.NoError(t, batch2.SetHeight(ctx, 2)) + require.NoError(t, batch2.Commit(ctx)) // Get headers from cache and submit headers, marshalledHeaders, err := cm.GetPendingHeaders(ctx) @@ -318,16 +318,16 @@ func TestDASubmitter_SubmitData_Success(t *testing.T) { // Save block 1 batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(header1, data1, &sig1)) - require.NoError(t, batch1.SetHeight(1)) - require.NoError(t, batch1.Commit()) + require.NoError(t, batch1.SaveBlockData(ctx, header1, data1, &sig1)) + require.NoError(t, batch1.SetHeight(ctx, 1)) + require.NoError(t, batch1.Commit(ctx)) // Save block 2 batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(header2, data2, &sig2)) - require.NoError(t, batch2.SetHeight(2)) - require.NoError(t, batch2.Commit()) + require.NoError(t, batch2.SaveBlockData(ctx, header2, data2, &sig2)) + require.NoError(t, batch2.SetHeight(ctx, 2)) + require.NoError(t, batch2.Commit(ctx)) // Get data from cache and submit signedDataList, marshalledData, err := cm.GetPendingData(ctx) @@ -378,9 +378,9 @@ func TestDASubmitter_SubmitData_SkipsEmptyData(t *testing.T) { sig := types.Signature([]byte("sig")) batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(header, emptyData, &sig)) - require.NoError(t, batch.SetHeight(1)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, header, emptyData, &sig)) + require.NoError(t, batch.SetHeight(ctx, 1)) + require.NoError(t, batch.Commit(ctx)) // Get data from cache and submit - should succeed but skip empty data // Get data from cache and submit @@ -439,9 +439,9 @@ func TestDASubmitter_SubmitData_NilSigner(t *testing.T) { sig := types.Signature([]byte("sig")) batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(header, data, &sig)) - require.NoError(t, batch.SetHeight(1)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, header, data, &sig)) + require.NoError(t, batch.SetHeight(ctx, 1)) + require.NoError(t, batch.Commit(ctx)) // Get data from cache and submit with nil signer - should fail signedDataList, marshalledData, err := cm.GetPendingData(ctx) diff --git a/block/internal/submitting/submitter_test.go b/block/internal/submitting/submitter_test.go index 07703be94..7f35535c8 100644 --- a/block/internal/submitting/submitter_test.go +++ b/block/internal/submitting/submitter_test.go @@ -111,8 +111,8 @@ func TestSubmitter_IsHeightDAIncluded(t *testing.T) { cm, st := newTestCacheAndStore(t) batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(5)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SetHeight(ctx, 5)) + require.NoError(t, batch.Commit(ctx)) s := &Submitter{store: st, cache: cm, logger: zerolog.Nop()} s.ctx = ctx @@ -265,16 +265,16 @@ func TestSubmitter_processDAInclusionLoop_advances(t *testing.T) { // Save block 1 batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(h1, d1, &sig)) - require.NoError(t, batch1.SetHeight(1)) - require.NoError(t, batch1.Commit()) + require.NoError(t, batch1.SaveBlockData(ctx, h1, d1, &sig)) + require.NoError(t, batch1.SetHeight(ctx, 1)) + require.NoError(t, batch1.Commit(ctx)) // Save block 2 batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(h2, d2, &sig)) - require.NoError(t, batch2.SetHeight(2)) - require.NoError(t, batch2.Commit()) + require.NoError(t, batch2.SaveBlockData(ctx, h2, d2, &sig)) + require.NoError(t, batch2.SetHeight(ctx, 2)) + require.NoError(t, batch2.Commit(ctx)) cm.SetHeaderDAIncluded(h1.Hash().String(), 100, 1) cm.SetDataDAIncluded(d1.DACommitment().String(), 100, 1) @@ -380,10 +380,10 @@ func TestSubmitter_daSubmissionLoop(t *testing.T) { // Store the blocks batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(h1, d1, &types.Signature{})) - require.NoError(t, batch.SaveBlockData(h2, d2, &types.Signature{})) - require.NoError(t, batch.SetHeight(2)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, h1, d1, &types.Signature{})) + require.NoError(t, batch.SaveBlockData(ctx, h2, d2, &types.Signature{})) + require.NoError(t, batch.SetHeight(ctx, 2)) + require.NoError(t, batch.Commit(ctx)) // Start and wait for calls require.NoError(t, s.Start(ctx)) @@ -481,9 +481,9 @@ func TestSubmitter_CacheClearedOnHeightInclusion(t *testing.T) { for _, block := range blocks { batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(block.header, block.data, &sig)) - require.NoError(t, batch.SetHeight(block.height)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SaveBlockData(ctx, block.header, block.data, &sig)) + require.NoError(t, batch.SetHeight(ctx, block.height)) + require.NoError(t, batch.Commit(ctx)) } // Set up cache with headers and data seen for all heights diff --git a/block/internal/syncing/syncer.go b/block/internal/syncing/syncer.go index ede86c9a2..3a75e05b0 100644 --- a/block/internal/syncing/syncer.go +++ b/block/internal/syncing/syncer.go @@ -320,13 +320,13 @@ func (s *Syncer) initializeState() error { if err != nil { return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SetHeight(state.LastBlockHeight); err != nil { + if err := batch.SetHeight(s.ctx, state.LastBlockHeight); err != nil { return fmt.Errorf("failed to set store height: %w", err) } - if err := batch.UpdateState(state); err != nil { + if err := batch.UpdateState(s.ctx, state); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(); err != nil { + if err := batch.Commit(s.ctx); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } s.SetLastState(state) @@ -694,19 +694,19 @@ func (s *Syncer) TrySyncNextBlock(ctx context.Context, event *common.DAHeightEve return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SaveBlockData(header, data, &header.Signature); err != nil { + if err := batch.SaveBlockData(ctx, header, data, &header.Signature); err != nil { return fmt.Errorf("failed to save block: %w", err) } - if err := batch.SetHeight(nextHeight); err != nil { + if err := batch.SetHeight(ctx, nextHeight); err != nil { return fmt.Errorf("failed to update height: %w", err) } - if err := batch.UpdateState(newState); err != nil { + if err := batch.UpdateState(ctx, newState); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(); err != nil { + if err := batch.Commit(ctx); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } diff --git a/block/internal/syncing/syncer_test.go b/block/internal/syncing/syncer_test.go index 2ba71a78f..48d3ceefb 100644 --- a/block/internal/syncing/syncer_test.go +++ b/block/internal/syncing/syncer_test.go @@ -288,8 +288,8 @@ func TestSyncer_processPendingEvents(t *testing.T) { // current height 1 batch, err := st.NewBatch(t.Context()) require.NoError(t, err) - require.NoError(t, batch.SetHeight(1)) - require.NoError(t, batch.Commit()) + require.NoError(t, batch.SetHeight(t.Context(), 1)) + require.NoError(t, batch.Commit(t.Context())) s := &Syncer{ store: st, @@ -614,9 +614,9 @@ func TestSyncer_InitializeState_CallsReplayer(t *testing.T) { // Mock batch operations mockBatch := new(testmocks.MockBatch) - mockBatch.On("SetHeight", storeHeight).Return(nil) - mockBatch.On("UpdateState", mock.Anything).Return(nil) - mockBatch.On("Commit").Return(nil) + mockBatch.On("SetHeight", mock.Anything, storeHeight).Return(nil) + mockBatch.On("UpdateState", mock.Anything, mock.Anything).Return(nil) + mockBatch.On("Commit", mock.Anything).Return(nil) mockStore.EXPECT().NewBatch(mock.Anything).Return(mockBatch, nil) syncer := &Syncer{ diff --git a/pkg/store/batch.go b/pkg/store/batch.go index 405119c61..788819783 100644 --- a/pkg/store/batch.go +++ b/pkg/store/batch.go @@ -15,7 +15,6 @@ import ( type DefaultBatch struct { store *DefaultStore batch ds.Batch - ctx context.Context } // NewBatch creates a new batch for atomic operations @@ -28,13 +27,12 @@ func (s *DefaultStore) NewBatch(ctx context.Context) (Batch, error) { return &DefaultBatch{ store: s, batch: batch, - ctx: ctx, }, nil } // SetHeight sets the height in the batch -func (b *DefaultBatch) SetHeight(height uint64) error { - currentHeight, err := b.store.Height(b.ctx) +func (b *DefaultBatch) SetHeight(ctx context.Context, height uint64) error { + currentHeight, err := b.store.Height(ctx) if err != nil { return err } @@ -43,11 +41,11 @@ func (b *DefaultBatch) SetHeight(height uint64) error { } heightBytes := encodeHeight(height) - return b.batch.Put(b.ctx, ds.NewKey(getHeightKey()), heightBytes) + return b.batch.Put(ctx, ds.NewKey(getHeightKey()), heightBytes) } // SaveBlockData saves block data to the batch -func (b *DefaultBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { +func (b *DefaultBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { height := header.Height() signatureHash := *signature @@ -60,19 +58,19 @@ func (b *DefaultBatch) SaveBlockData(header *types.SignedHeader, data *types.Dat return fmt.Errorf("failed to marshal Data to binary: %w", err) } - if err := b.batch.Put(b.ctx, ds.NewKey(getHeaderKey(height)), headerBlob); err != nil { + if err := b.batch.Put(ctx, ds.NewKey(getHeaderKey(height)), headerBlob); err != nil { return fmt.Errorf("failed to put header blob in batch: %w", err) } - if err := b.batch.Put(b.ctx, ds.NewKey(getDataKey(height)), dataBlob); err != nil { + if err := b.batch.Put(ctx, ds.NewKey(getDataKey(height)), dataBlob); err != nil { return fmt.Errorf("failed to put data blob in batch: %w", err) } - if err := b.batch.Put(b.ctx, ds.NewKey(getSignatureKey(height)), signatureHash[:]); err != nil { + if err := b.batch.Put(ctx, ds.NewKey(getSignatureKey(height)), signatureHash[:]); err != nil { return fmt.Errorf("failed to put signature blob in batch: %w", err) } headerHash := sha256.Sum256(headerBlob) heightBytes := encodeHeight(height) - if err := b.batch.Put(b.ctx, ds.NewKey(getIndexKey(headerHash[:])), heightBytes); err != nil { + if err := b.batch.Put(ctx, ds.NewKey(getIndexKey(headerHash[:])), heightBytes); err != nil { return fmt.Errorf("failed to put index key in batch: %w", err) } @@ -80,8 +78,8 @@ func (b *DefaultBatch) SaveBlockData(header *types.SignedHeader, data *types.Dat } // UpdateState updates the state in the batch -func (b *DefaultBatch) UpdateState(state types.State) error { - // Save the state at the height specified in the state itself +func (b *DefaultBatch) UpdateState(ctx context.Context, state types.State) error { + // save the state at the height specified in the state itself height := state.LastBlockHeight pbState, err := state.ToProto() @@ -93,23 +91,23 @@ func (b *DefaultBatch) UpdateState(state types.State) error { return fmt.Errorf("failed to marshal state to protobuf: %w", err) } - return b.batch.Put(b.ctx, ds.NewKey(getStateAtHeightKey(height)), data) + return b.batch.Put(ctx, ds.NewKey(getStateAtHeightKey(height)), data) } // Commit commits all batched operations atomically -func (b *DefaultBatch) Commit() error { - if err := b.batch.Commit(b.ctx); err != nil { +func (b *DefaultBatch) Commit(ctx context.Context) error { + if err := b.batch.Commit(ctx); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } return nil } // Delete adds a delete operation to the batch -func (b *DefaultBatch) Delete(key ds.Key) error { - return b.batch.Delete(b.ctx, key) +func (b *DefaultBatch) Delete(ctx context.Context, key ds.Key) error { + return b.batch.Delete(ctx, key) } // Put adds a put operation to the batch -func (b *DefaultBatch) Put(key ds.Key, value []byte) error { - return b.batch.Put(b.ctx, key, value) +func (b *DefaultBatch) Put(ctx context.Context, key ds.Key, value []byte) error { + return b.batch.Put(ctx, key, value) } diff --git a/pkg/store/store_test.go b/pkg/store/store_test.go index 8636a0ad0..c751e99e4 100644 --- a/pkg/store/store_test.go +++ b/pkg/store/store_test.go @@ -122,19 +122,20 @@ func TestStoreHeight(t *testing.T) { assert.NoError(err) assert.Equal(uint64(0), height) + ctx := t.Context() for i, header := range c.headers { data := c.data[i] - batch, err := bstore.NewBatch(t.Context()) + batch, err := bstore.NewBatch(ctx) require.NoError(t, err) - err = batch.SaveBlockData(header, data, &types.Signature{}) + err = batch.SaveBlockData(ctx, header, data, &types.Signature{}) require.NoError(t, err) - err = batch.SetHeight(header.Height()) + err = batch.SetHeight(ctx, header.Height()) require.NoError(t, err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(t, err) } - height, err = bstore.Height(t.Context()) + height, err = bstore.Height(ctx) assert.NoError(err) assert.Equal(c.expected, height) }) @@ -175,28 +176,29 @@ func TestStoreLoad(t *testing.T) { require := require.New(t) bstore := New(kv) + ctx := t.Context() for i, header := range c.headers { data := c.data[i] signature := &header.Signature - batch, err := bstore.NewBatch(t.Context()) + batch, err := bstore.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, signature) + err = batch.SaveBlockData(ctx, header, data, signature) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } for i, expectedHeader := range c.headers { expectedData := c.data[i] - header, data, err := bstore.GetBlockData(t.Context(), expectedHeader.Height()) + header, data, err := bstore.GetBlockData(ctx, expectedHeader.Height()) assert.NoError(err) assert.NotNil(header) assert.NotNil(data) assert.Equal(expectedHeader, header) assert.Equal(expectedData, data) - signature, err := bstore.GetSignature(t.Context(), expectedHeader.Height()) + signature, err := bstore.GetSignature(ctx, expectedHeader.Height()) assert.NoError(err) assert.NotNil(signature) } @@ -218,15 +220,16 @@ func TestRestart(t *testing.T) { s1 := New(kv) expectedHeight := uint64(10) - batch, err := s1.NewBatch(t.Context()) + ctx := t.Context() + batch, err := s1.NewBatch(ctx) require.NoError(err) - err = batch.SetHeight(expectedHeight) + err = batch.SetHeight(ctx, expectedHeight) require.NoError(err) - err = batch.UpdateState(types.State{ + err = batch.UpdateState(ctx, types.State{ LastBlockHeight: expectedHeight, }) assert.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) assert.NoError(err) err = s1.Close() @@ -238,7 +241,7 @@ func TestRestart(t *testing.T) { s2 := New(kv) assert.NoError(err) - state2, err := s2.GetState(t.Context()) + state2, err := s2.GetState(ctx) assert.NoError(err) err = s2.Close() @@ -257,9 +260,10 @@ func TestSetHeightError(t *testing.T) { mockBatchingDs := &mockBatchingDatastore{Batching: mockDs, putError: mockErr} s := New(mockBatchingDs) - batch, err := s.NewBatch(t.Context()) + ctx := t.Context() + batch, err := s.NewBatch(ctx) require.NoError(err) - err = batch.SetHeight(10) + err = batch.SetHeight(ctx, 10) require.Error(err) require.Contains(err.Error(), mockErr.Error()) } @@ -434,20 +438,21 @@ func TestSaveBlockDataErrors(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() s := New(tc.mock) + ctx := t.Context() - batch, err := s.NewBatch(t.Context()) + batch, err := s.NewBatch(ctx) if tc.expectSub == "failed to create a new batch" { require.ErrorContains(t, err, tc.expectSub) return } require.NoError(t, err) - err = batch.SaveBlockData(header, data, signature) + err = batch.SaveBlockData(ctx, header, data, signature) if tc.expectSub == "failed to put header blob" { require.ErrorContains(t, err, tc.expectSub) return } require.NoError(t, err) - err = batch.Commit() + err = batch.Commit(ctx) require.ErrorContains(t, err, tc.expectSub) }) } @@ -529,9 +534,10 @@ func TestUpdateStateError(t *testing.T) { mockBatchingDsPut := &mockBatchingDatastore{Batching: mockDsPut, putError: mockErrPut} sPut := New(mockBatchingDsPut) - batch, err := sPut.NewBatch(t.Context()) + ctx := t.Context() + batch, err := sPut.NewBatch(ctx) require.NoError(err) - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.Error(err) require.Contains(err.Error(), mockErrPut.Error()) } @@ -557,17 +563,18 @@ func TestGetStateError(t *testing.T) { // Put some data that will cause unmarshal error height := uint64(1) - batch, err := sUnmarshal.NewBatch(t.Context()) + ctx := t.Context() + batch, err := sUnmarshal.NewBatch(ctx) require.NoError(err) - err = batch.SetHeight(height) + err = batch.SetHeight(ctx, height) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) - err = mockBatchingDsUnmarshal.Put(t.Context(), ds.NewKey(getStateAtHeightKey(height)), []byte("invalid state proto")) + err = mockBatchingDsUnmarshal.Put(ctx, ds.NewKey(getStateAtHeightKey(height)), []byte("invalid state proto")) require.NoError(err) - _, err = sUnmarshal.GetState(t.Context()) + _, err = sUnmarshal.GetState(ctx) require.Error(err) require.Contains(err.Error(), "failed to unmarshal state from protobuf") } @@ -676,9 +683,9 @@ func TestRollback(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -689,9 +696,9 @@ func TestRollback(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -750,11 +757,11 @@ func TestRollbackToSameHeight(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(height) + err = batch.SetHeight(ctx, height) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) // Execute rollback to same height @@ -787,11 +794,11 @@ func TestRollbackToHigherHeight(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(currentHeight) + err = batch.SetHeight(ctx, currentHeight) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) // Execute rollback to higher height @@ -863,9 +870,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -876,9 +883,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -910,9 +917,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -923,9 +930,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -961,9 +968,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -974,9 +981,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -1012,9 +1019,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -1025,9 +1032,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -1067,9 +1074,9 @@ func TestRollbackDAIncludedHeightNotSet(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -1080,9 +1087,9 @@ func TestRollbackDAIncludedHeightNotSet(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -1115,9 +1122,9 @@ func TestRollbackDAIncludedHeightInvalidLength(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(h) + err = batch.SetHeight(ctx, h) require.NoError(err) // Create and update state for this height @@ -1128,9 +1135,9 @@ func TestRollbackDAIncludedHeightInvalidLength(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) } @@ -1165,9 +1172,9 @@ func TestRollbackDAIncludedHeightGetMetadataError(t *testing.T) { sig := &header.Signature batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(err) - err = batch.SetHeight(uint64(2)) + err = batch.SetHeight(ctx, uint64(2)) require.NoError(err) // Create and update state for this height @@ -1178,9 +1185,9 @@ func TestRollbackDAIncludedHeightGetMetadataError(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(state) + err = batch.UpdateState(ctx, state) require.NoError(err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(err) // Configure mock to return error when getting DA included height metadata diff --git a/pkg/store/tracing.go b/pkg/store/tracing.go index 201832a54..204cfe756 100644 --- a/pkg/store/tracing.go +++ b/pkg/store/tracing.go @@ -239,13 +239,13 @@ type tracedBatch struct { tracer trace.Tracer } -func (b *tracedBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { - _, span := b.tracer.Start(context.Background(), "Batch.SaveBlockData", +func (b *tracedBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { + ctx, span := b.tracer.Start(ctx, "Batch.SaveBlockData", trace.WithAttributes(attribute.Int64("height", int64(header.Height()))), ) defer span.End() - err := b.inner.SaveBlockData(header, data, signature) + err := b.inner.SaveBlockData(ctx, header, data, signature) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -255,13 +255,13 @@ func (b *tracedBatch) SaveBlockData(header *types.SignedHeader, data *types.Data return nil } -func (b *tracedBatch) SetHeight(height uint64) error { - _, span := b.tracer.Start(context.Background(), "Batch.SetHeight", +func (b *tracedBatch) SetHeight(ctx context.Context, height uint64) error { + ctx, span := b.tracer.Start(ctx, "Batch.SetHeight", trace.WithAttributes(attribute.Int64("height", int64(height))), ) defer span.End() - err := b.inner.SetHeight(height) + err := b.inner.SetHeight(ctx, height) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -271,13 +271,13 @@ func (b *tracedBatch) SetHeight(height uint64) error { return nil } -func (b *tracedBatch) UpdateState(state types.State) error { - _, span := b.tracer.Start(context.Background(), "Batch.UpdateState", +func (b *tracedBatch) UpdateState(ctx context.Context, state types.State) error { + ctx, span := b.tracer.Start(ctx, "Batch.UpdateState", trace.WithAttributes(attribute.Int64("state.height", int64(state.LastBlockHeight))), ) defer span.End() - err := b.inner.UpdateState(state) + err := b.inner.UpdateState(ctx, state) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -287,11 +287,11 @@ func (b *tracedBatch) UpdateState(state types.State) error { return nil } -func (b *tracedBatch) Commit() error { - _, span := b.tracer.Start(context.Background(), "Batch.Commit") +func (b *tracedBatch) Commit(ctx context.Context) error { + ctx, span := b.tracer.Start(ctx, "Batch.Commit") defer span.End() - err := b.inner.Commit() + err := b.inner.Commit(ctx) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -301,8 +301,8 @@ func (b *tracedBatch) Commit() error { return nil } -func (b *tracedBatch) Put(key ds.Key, value []byte) error { - _, span := b.tracer.Start(context.Background(), "Batch.Put", +func (b *tracedBatch) Put(ctx context.Context, key ds.Key, value []byte) error { + ctx, span := b.tracer.Start(ctx, "Batch.Put", trace.WithAttributes( attribute.String("key", key.String()), attribute.Int("value.size", len(value)), @@ -310,7 +310,7 @@ func (b *tracedBatch) Put(key ds.Key, value []byte) error { ) defer span.End() - err := b.inner.Put(key, value) + err := b.inner.Put(ctx, key, value) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -320,13 +320,13 @@ func (b *tracedBatch) Put(key ds.Key, value []byte) error { return nil } -func (b *tracedBatch) Delete(key ds.Key) error { - _, span := b.tracer.Start(context.Background(), "Batch.Delete", +func (b *tracedBatch) Delete(ctx context.Context, key ds.Key) error { + ctx, span := b.tracer.Start(ctx, "Batch.Delete", trace.WithAttributes(attribute.String("key", key.String())), ) defer span.End() - err := b.inner.Delete(key) + err := b.inner.Delete(ctx, key) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) diff --git a/pkg/store/tracing_test.go b/pkg/store/tracing_test.go index a5dca2417..d5d92af1f 100644 --- a/pkg/store/tracing_test.go +++ b/pkg/store/tracing_test.go @@ -120,52 +120,52 @@ func (m *tracingMockStore) NewBatch(ctx context.Context) (Batch, error) { } type tracingMockBatch struct { - saveBlockDataFn func(header *types.SignedHeader, data *types.Data, signature *types.Signature) error - setHeightFn func(height uint64) error - updateStateFn func(state types.State) error - commitFn func() error - putFn func(key ds.Key, value []byte) error - deleteFn func(key ds.Key) error + saveBlockDataFn func(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error + setHeightFn func(ctx context.Context, height uint64) error + updateStateFn func(ctx context.Context, state types.State) error + commitFn func(ctx context.Context) error + putFn func(ctx context.Context, key ds.Key, value []byte) error + deleteFn func(ctx context.Context, key ds.Key) error } -func (b *tracingMockBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { +func (b *tracingMockBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { if b.saveBlockDataFn != nil { - return b.saveBlockDataFn(header, data, signature) + return b.saveBlockDataFn(ctx, header, data, signature) } return nil } -func (b *tracingMockBatch) SetHeight(height uint64) error { +func (b *tracingMockBatch) SetHeight(ctx context.Context, height uint64) error { if b.setHeightFn != nil { - return b.setHeightFn(height) + return b.setHeightFn(ctx, height) } return nil } -func (b *tracingMockBatch) UpdateState(state types.State) error { +func (b *tracingMockBatch) UpdateState(ctx context.Context, state types.State) error { if b.updateStateFn != nil { - return b.updateStateFn(state) + return b.updateStateFn(ctx, state) } return nil } -func (b *tracingMockBatch) Commit() error { +func (b *tracingMockBatch) Commit(ctx context.Context) error { if b.commitFn != nil { - return b.commitFn() + return b.commitFn(ctx) } return nil } -func (b *tracingMockBatch) Put(key ds.Key, value []byte) error { +func (b *tracingMockBatch) Put(ctx context.Context, key ds.Key, value []byte) error { if b.putFn != nil { - return b.putFn(key, value) + return b.putFn(ctx, key, value) } return nil } -func (b *tracingMockBatch) Delete(key ds.Key) error { +func (b *tracingMockBatch) Delete(ctx context.Context, key ds.Key) error { if b.deleteFn != nil { - return b.deleteFn(key) + return b.deleteFn(ctx, key) } return nil } @@ -323,7 +323,7 @@ func TestTracedBatch_Commit_Success(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(t, err) - err = batch.Commit() + err = batch.Commit(ctx) require.NoError(t, err) spans := sr.Ended() @@ -348,7 +348,7 @@ func TestTracedBatch_SaveBlockData_Success(t *testing.T) { data := &types.Data{} sig := &types.Signature{} - err = batch.SaveBlockData(header, data, sig) + err = batch.SaveBlockData(ctx, header, data, sig) require.NoError(t, err) spans := sr.Ended() diff --git a/pkg/store/types.go b/pkg/store/types.go index bf1cb6ced..f5c3e0629 100644 --- a/pkg/store/types.go +++ b/pkg/store/types.go @@ -11,22 +11,22 @@ import ( // Batch provides atomic operations for the store type Batch interface { // SaveBlockData atomically saves the block header, data, and signature - SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error + SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error // SetHeight sets the height in the batch - SetHeight(height uint64) error + SetHeight(ctx context.Context, height uint64) error // UpdateState updates the state in the batch - UpdateState(state types.State) error + UpdateState(ctx context.Context, state types.State) error // Commit commits all batch operations atomically - Commit() error + Commit(ctx context.Context) error // Put adds a put operation to the batch (used internally for rollback) - Put(key ds.Key, value []byte) error + Put(ctx context.Context, key ds.Key, value []byte) error // Delete adds a delete operation to the batch (used internally for rollback) - Delete(key ds.Key) error + Delete(ctx context.Context, key ds.Key) error } // Store is minimal interface for storing and retrieving blocks, commits and state. diff --git a/test/mocks/batch.go b/test/mocks/batch.go index 90025855b..7301ffefb 100644 --- a/test/mocks/batch.go +++ b/test/mocks/batch.go @@ -5,6 +5,8 @@ package mocks import ( + "context" + "github.com/evstack/ev-node/types" "github.com/ipfs/go-datastore" mock "github.com/stretchr/testify/mock" @@ -38,16 +40,16 @@ func (_m *MockBatch) EXPECT() *MockBatch_Expecter { } // Commit provides a mock function for the type MockBatch -func (_mock *MockBatch) Commit() error { - ret := _mock.Called() +func (_mock *MockBatch) Commit(ctx context.Context) error { + ret := _mock.Called(ctx) if len(ret) == 0 { panic("no return value specified for Commit") } var r0 error - if returnFunc, ok := ret.Get(0).(func() error); ok { - r0 = returnFunc() + if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = returnFunc(ctx) } else { r0 = ret.Error(0) } @@ -60,13 +62,14 @@ type MockBatch_Commit_Call struct { } // Commit is a helper method to define mock.On call -func (_e *MockBatch_Expecter) Commit() *MockBatch_Commit_Call { - return &MockBatch_Commit_Call{Call: _e.mock.On("Commit")} +// - ctx context.Context +func (_e *MockBatch_Expecter) Commit(ctx interface{}) *MockBatch_Commit_Call { + return &MockBatch_Commit_Call{Call: _e.mock.On("Commit", ctx)} } -func (_c *MockBatch_Commit_Call) Run(run func()) *MockBatch_Commit_Call { +func (_c *MockBatch_Commit_Call) Run(run func(ctx context.Context)) *MockBatch_Commit_Call { _c.Call.Run(func(args mock.Arguments) { - run() + run(args[0].(context.Context)) }) return _c } @@ -76,22 +79,22 @@ func (_c *MockBatch_Commit_Call) Return(err error) *MockBatch_Commit_Call { return _c } -func (_c *MockBatch_Commit_Call) RunAndReturn(run func() error) *MockBatch_Commit_Call { +func (_c *MockBatch_Commit_Call) RunAndReturn(run func(context.Context) error) *MockBatch_Commit_Call { _c.Call.Return(run) return _c } // Delete provides a mock function for the type MockBatch -func (_mock *MockBatch) Delete(key datastore.Key) error { - ret := _mock.Called(key) +func (_mock *MockBatch) Delete(ctx context.Context, key datastore.Key) error { + ret := _mock.Called(ctx, key) if len(ret) == 0 { panic("no return value specified for Delete") } var r0 error - if returnFunc, ok := ret.Get(0).(func(datastore.Key) error); ok { - r0 = returnFunc(key) + if returnFunc, ok := ret.Get(0).(func(context.Context, datastore.Key) error); ok { + r0 = returnFunc(ctx, key) } else { r0 = ret.Error(0) } @@ -104,20 +107,19 @@ type MockBatch_Delete_Call struct { } // Delete is a helper method to define mock.On call +// - ctx context.Context // - key datastore.Key -func (_e *MockBatch_Expecter) Delete(key interface{}) *MockBatch_Delete_Call { - return &MockBatch_Delete_Call{Call: _e.mock.On("Delete", key)} +func (_e *MockBatch_Expecter) Delete(ctx interface{}, key interface{}) *MockBatch_Delete_Call { + return &MockBatch_Delete_Call{Call: _e.mock.On("Delete", ctx, key)} } -func (_c *MockBatch_Delete_Call) Run(run func(key datastore.Key)) *MockBatch_Delete_Call { +func (_c *MockBatch_Delete_Call) Run(run func(ctx context.Context, key datastore.Key)) *MockBatch_Delete_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 datastore.Key - if args[0] != nil { - arg0 = args[0].(datastore.Key) + var arg1 datastore.Key + if args[1] != nil { + arg1 = args[1].(datastore.Key) } - run( - arg0, - ) + run(args[0].(context.Context), arg1) }) return _c } @@ -127,22 +129,22 @@ func (_c *MockBatch_Delete_Call) Return(err error) *MockBatch_Delete_Call { return _c } -func (_c *MockBatch_Delete_Call) RunAndReturn(run func(key datastore.Key) error) *MockBatch_Delete_Call { +func (_c *MockBatch_Delete_Call) RunAndReturn(run func(context.Context, datastore.Key) error) *MockBatch_Delete_Call { _c.Call.Return(run) return _c } // Put provides a mock function for the type MockBatch -func (_mock *MockBatch) Put(key datastore.Key, value []byte) error { - ret := _mock.Called(key, value) +func (_mock *MockBatch) Put(ctx context.Context, key datastore.Key, value []byte) error { + ret := _mock.Called(ctx, key, value) if len(ret) == 0 { panic("no return value specified for Put") } var r0 error - if returnFunc, ok := ret.Get(0).(func(datastore.Key, []byte) error); ok { - r0 = returnFunc(key, value) + if returnFunc, ok := ret.Get(0).(func(context.Context, datastore.Key, []byte) error); ok { + r0 = returnFunc(ctx, key, value) } else { r0 = ret.Error(0) } @@ -155,26 +157,24 @@ type MockBatch_Put_Call struct { } // Put is a helper method to define mock.On call +// - ctx context.Context // - key datastore.Key // - value []byte -func (_e *MockBatch_Expecter) Put(key interface{}, value interface{}) *MockBatch_Put_Call { - return &MockBatch_Put_Call{Call: _e.mock.On("Put", key, value)} +func (_e *MockBatch_Expecter) Put(ctx interface{}, key interface{}, value interface{}) *MockBatch_Put_Call { + return &MockBatch_Put_Call{Call: _e.mock.On("Put", ctx, key, value)} } -func (_c *MockBatch_Put_Call) Run(run func(key datastore.Key, value []byte)) *MockBatch_Put_Call { +func (_c *MockBatch_Put_Call) Run(run func(ctx context.Context, key datastore.Key, value []byte)) *MockBatch_Put_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 datastore.Key - if args[0] != nil { - arg0 = args[0].(datastore.Key) - } - var arg1 []byte + var arg1 datastore.Key if args[1] != nil { - arg1 = args[1].([]byte) + arg1 = args[1].(datastore.Key) + } + var arg2 []byte + if args[2] != nil { + arg2 = args[2].([]byte) } - run( - arg0, - arg1, - ) + run(args[0].(context.Context), arg1, arg2) }) return _c } @@ -184,22 +184,22 @@ func (_c *MockBatch_Put_Call) Return(err error) *MockBatch_Put_Call { return _c } -func (_c *MockBatch_Put_Call) RunAndReturn(run func(key datastore.Key, value []byte) error) *MockBatch_Put_Call { +func (_c *MockBatch_Put_Call) RunAndReturn(run func(context.Context, datastore.Key, []byte) error) *MockBatch_Put_Call { _c.Call.Return(run) return _c } // SaveBlockData provides a mock function for the type MockBatch -func (_mock *MockBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { - ret := _mock.Called(header, data, signature) +func (_mock *MockBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { + ret := _mock.Called(ctx, header, data, signature) if len(ret) == 0 { panic("no return value specified for SaveBlockData") } var r0 error - if returnFunc, ok := ret.Get(0).(func(*types.SignedHeader, *types.Data, *types.Signature) error); ok { - r0 = returnFunc(header, data, signature) + if returnFunc, ok := ret.Get(0).(func(context.Context, *types.SignedHeader, *types.Data, *types.Signature) error); ok { + r0 = returnFunc(ctx, header, data, signature) } else { r0 = ret.Error(0) } @@ -212,32 +212,29 @@ type MockBatch_SaveBlockData_Call struct { } // SaveBlockData is a helper method to define mock.On call +// - ctx context.Context // - header *types.SignedHeader // - data *types.Data // - signature *types.Signature -func (_e *MockBatch_Expecter) SaveBlockData(header interface{}, data interface{}, signature interface{}) *MockBatch_SaveBlockData_Call { - return &MockBatch_SaveBlockData_Call{Call: _e.mock.On("SaveBlockData", header, data, signature)} +func (_e *MockBatch_Expecter) SaveBlockData(ctx interface{}, header interface{}, data interface{}, signature interface{}) *MockBatch_SaveBlockData_Call { + return &MockBatch_SaveBlockData_Call{Call: _e.mock.On("SaveBlockData", ctx, header, data, signature)} } -func (_c *MockBatch_SaveBlockData_Call) Run(run func(header *types.SignedHeader, data *types.Data, signature *types.Signature)) *MockBatch_SaveBlockData_Call { +func (_c *MockBatch_SaveBlockData_Call) Run(run func(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature)) *MockBatch_SaveBlockData_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 *types.SignedHeader - if args[0] != nil { - arg0 = args[0].(*types.SignedHeader) - } - var arg1 *types.Data + var arg1 *types.SignedHeader if args[1] != nil { - arg1 = args[1].(*types.Data) + arg1 = args[1].(*types.SignedHeader) } - var arg2 *types.Signature + var arg2 *types.Data if args[2] != nil { - arg2 = args[2].(*types.Signature) + arg2 = args[2].(*types.Data) + } + var arg3 *types.Signature + if args[3] != nil { + arg3 = args[3].(*types.Signature) } - run( - arg0, - arg1, - arg2, - ) + run(args[0].(context.Context), arg1, arg2, arg3) }) return _c } @@ -247,22 +244,22 @@ func (_c *MockBatch_SaveBlockData_Call) Return(err error) *MockBatch_SaveBlockDa return _c } -func (_c *MockBatch_SaveBlockData_Call) RunAndReturn(run func(header *types.SignedHeader, data *types.Data, signature *types.Signature) error) *MockBatch_SaveBlockData_Call { +func (_c *MockBatch_SaveBlockData_Call) RunAndReturn(run func(context.Context, *types.SignedHeader, *types.Data, *types.Signature) error) *MockBatch_SaveBlockData_Call { _c.Call.Return(run) return _c } // SetHeight provides a mock function for the type MockBatch -func (_mock *MockBatch) SetHeight(height uint64) error { - ret := _mock.Called(height) +func (_mock *MockBatch) SetHeight(ctx context.Context, height uint64) error { + ret := _mock.Called(ctx, height) if len(ret) == 0 { panic("no return value specified for SetHeight") } var r0 error - if returnFunc, ok := ret.Get(0).(func(uint64) error); ok { - r0 = returnFunc(height) + if returnFunc, ok := ret.Get(0).(func(context.Context, uint64) error); ok { + r0 = returnFunc(ctx, height) } else { r0 = ret.Error(0) } @@ -275,20 +272,19 @@ type MockBatch_SetHeight_Call struct { } // SetHeight is a helper method to define mock.On call +// - ctx context.Context // - height uint64 -func (_e *MockBatch_Expecter) SetHeight(height interface{}) *MockBatch_SetHeight_Call { - return &MockBatch_SetHeight_Call{Call: _e.mock.On("SetHeight", height)} +func (_e *MockBatch_Expecter) SetHeight(ctx interface{}, height interface{}) *MockBatch_SetHeight_Call { + return &MockBatch_SetHeight_Call{Call: _e.mock.On("SetHeight", ctx, height)} } -func (_c *MockBatch_SetHeight_Call) Run(run func(height uint64)) *MockBatch_SetHeight_Call { +func (_c *MockBatch_SetHeight_Call) Run(run func(ctx context.Context, height uint64)) *MockBatch_SetHeight_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 uint64 - if args[0] != nil { - arg0 = args[0].(uint64) + var arg1 uint64 + if args[1] != nil { + arg1 = args[1].(uint64) } - run( - arg0, - ) + run(args[0].(context.Context), arg1) }) return _c } @@ -298,22 +294,22 @@ func (_c *MockBatch_SetHeight_Call) Return(err error) *MockBatch_SetHeight_Call return _c } -func (_c *MockBatch_SetHeight_Call) RunAndReturn(run func(height uint64) error) *MockBatch_SetHeight_Call { +func (_c *MockBatch_SetHeight_Call) RunAndReturn(run func(context.Context, uint64) error) *MockBatch_SetHeight_Call { _c.Call.Return(run) return _c } // UpdateState provides a mock function for the type MockBatch -func (_mock *MockBatch) UpdateState(state types.State) error { - ret := _mock.Called(state) +func (_mock *MockBatch) UpdateState(ctx context.Context, state types.State) error { + ret := _mock.Called(ctx, state) if len(ret) == 0 { panic("no return value specified for UpdateState") } var r0 error - if returnFunc, ok := ret.Get(0).(func(types.State) error); ok { - r0 = returnFunc(state) + if returnFunc, ok := ret.Get(0).(func(context.Context, types.State) error); ok { + r0 = returnFunc(ctx, state) } else { r0 = ret.Error(0) } @@ -326,20 +322,19 @@ type MockBatch_UpdateState_Call struct { } // UpdateState is a helper method to define mock.On call +// - ctx context.Context // - state types.State -func (_e *MockBatch_Expecter) UpdateState(state interface{}) *MockBatch_UpdateState_Call { - return &MockBatch_UpdateState_Call{Call: _e.mock.On("UpdateState", state)} +func (_e *MockBatch_Expecter) UpdateState(ctx interface{}, state interface{}) *MockBatch_UpdateState_Call { + return &MockBatch_UpdateState_Call{Call: _e.mock.On("UpdateState", ctx, state)} } -func (_c *MockBatch_UpdateState_Call) Run(run func(state types.State)) *MockBatch_UpdateState_Call { +func (_c *MockBatch_UpdateState_Call) Run(run func(ctx context.Context, state types.State)) *MockBatch_UpdateState_Call { _c.Call.Run(func(args mock.Arguments) { - var arg0 types.State - if args[0] != nil { - arg0 = args[0].(types.State) + var arg1 types.State + if args[1] != nil { + arg1 = args[1].(types.State) } - run( - arg0, - ) + run(args[0].(context.Context), arg1) }) return _c } @@ -349,7 +344,7 @@ func (_c *MockBatch_UpdateState_Call) Return(err error) *MockBatch_UpdateState_C return _c } -func (_c *MockBatch_UpdateState_Call) RunAndReturn(run func(state types.State) error) *MockBatch_UpdateState_Call { +func (_c *MockBatch_UpdateState_Call) RunAndReturn(run func(context.Context, types.State) error) *MockBatch_UpdateState_Call { _c.Call.Return(run) return _c } From 34bf89747560ae8ef18d9e7d833970b6cd0b010d Mon Sep 17 00:00:00 2001 From: chatton Date: Tue, 20 Jan 2026 13:04:29 +0000 Subject: [PATCH 12/12] chore: reverted ctx change --- block/internal/cache/bench_test.go | 6 +- block/internal/cache/manager_test.go | 6 +- block/internal/cache/pending_base_test.go | 4 +- block/internal/cache/pending_data_test.go | 14 +- block/internal/cache/pending_headers_test.go | 12 +- block/internal/executing/executor.go | 18 +- .../executing/executor_restart_test.go | 7 +- .../da_submitter_integration_test.go | 17 +- .../internal/submitting/da_submitter_test.go | 36 ++-- block/internal/submitting/submitter_test.go | 30 +-- block/internal/syncing/syncer.go | 14 +- block/internal/syncing/syncer_test.go | 10 +- pkg/store/batch.go | 36 ++-- pkg/store/store_test.go | 137 +++++++------- pkg/store/tracing.go | 43 +++-- pkg/store/tracing_test.go | 40 ++-- pkg/store/types.go | 12 +- test/mocks/batch.go | 175 +++++++++--------- 18 files changed, 309 insertions(+), 308 deletions(-) diff --git a/block/internal/cache/bench_test.go b/block/internal/cache/bench_test.go index cb37b3563..aef81866f 100644 --- a/block/internal/cache/bench_test.go +++ b/block/internal/cache/bench_test.go @@ -40,13 +40,13 @@ func benchSetupStore(b *testing.B, n int, txsPer int, chainID string) store.Stor if err != nil { b.Fatal(err) } - if err := batch.SaveBlockData(ctx, h, d, &types.Signature{}); err != nil { + if err := batch.SaveBlockData(h, d, &types.Signature{}); err != nil { b.Fatal(err) } - if err := batch.SetHeight(ctx, uint64(i)); err != nil { + if err := batch.SetHeight(uint64(i)); err != nil { b.Fatal(err) } - if err := batch.Commit(ctx); err != nil { + if err := batch.Commit(); err != nil { b.Fatal(err) } } diff --git a/block/internal/cache/manager_test.go b/block/internal/cache/manager_test.go index cc36106cf..328a1de7d 100644 --- a/block/internal/cache/manager_test.go +++ b/block/internal/cache/manager_test.go @@ -169,11 +169,11 @@ func TestPendingHeadersAndData_Flow(t *testing.T) { }{{h1, d1}, {h2, d2}, {h3, d3}} { batch, err := st.NewBatch(ctx) require.NoError(t, err) - err = batch.SaveBlockData(ctx, pair.h, pair.d, &types.Signature{}) + err = batch.SaveBlockData(pair.h, pair.d, &types.Signature{}) require.NoError(t, err) - err = batch.SetHeight(ctx, uint64(i+1)) + err = batch.SetHeight(uint64(i + 1)) require.NoError(t, err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(t, err) } diff --git a/block/internal/cache/pending_base_test.go b/block/internal/cache/pending_base_test.go index ff6f73fab..eb9734a03 100644 --- a/block/internal/cache/pending_base_test.go +++ b/block/internal/cache/pending_base_test.go @@ -60,8 +60,8 @@ func TestPendingBase_PersistLastSubmitted(t *testing.T) { // store height 3 to make numPending meaningful batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(ctx, 3)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SetHeight(3)) + require.NoError(t, batch.Commit()) assert.Equal(t, uint64(3), ph.NumPendingHeaders()) // set last submitted higher and ensure metadata is written diff --git a/block/internal/cache/pending_data_test.go b/block/internal/cache/pending_data_test.go index 2dd56cb63..75679a24f 100644 --- a/block/internal/cache/pending_data_test.go +++ b/block/internal/cache/pending_data_test.go @@ -29,9 +29,9 @@ func TestPendingData_BasicFlow(t *testing.T) { }{{h1, d1}, {h2, d2}, {h3, d3}} { batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, p.h, p.d, &types.Signature{})) - require.NoError(t, batch.SetHeight(ctx, uint64(i+1))) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(p.h, p.d, &types.Signature{})) + require.NoError(t, batch.SetHeight(uint64(i+1))) + require.NoError(t, batch.Commit()) } pendingData, err := NewPendingData(store, zerolog.Nop()) @@ -74,8 +74,8 @@ func TestPendingData_InitFromMetadata(t *testing.T) { // store height is 3 batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(ctx, 3)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SetHeight(3)) + require.NoError(t, batch.Commit()) pendingData, err := NewPendingData(store, zerolog.Nop()) require.NoError(t, err) @@ -90,8 +90,8 @@ func TestPendingData_GetPending_PropagatesFetchError(t *testing.T) { // Set height to 1 but do not save any block data batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(ctx, 1)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SetHeight(1)) + require.NoError(t, batch.Commit()) pendingData, err := NewPendingData(store, zerolog.Nop()) require.NoError(t, err) diff --git a/block/internal/cache/pending_headers_test.go b/block/internal/cache/pending_headers_test.go index 40ab66117..25c029700 100644 --- a/block/internal/cache/pending_headers_test.go +++ b/block/internal/cache/pending_headers_test.go @@ -29,9 +29,9 @@ func TestPendingHeaders_BasicFlow(t *testing.T) { }{{h1, d1}, {h2, d2}, {h3, d3}} { batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, p.h, p.d, &types.Signature{})) - require.NoError(t, batch.SetHeight(ctx, uint64(i+1))) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(p.h, p.d, &types.Signature{})) + require.NoError(t, batch.SetHeight(uint64(i+1))) + require.NoError(t, batch.Commit()) } pendingHeaders, err := NewPendingHeaders(store, zerolog.Nop()) @@ -72,9 +72,9 @@ func TestPendingHeaders_EmptyWhenUpToDate(t *testing.T) { h, d := types.GetRandomBlock(1, 1, "ph-up") batch, err := store.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, h, d, &types.Signature{})) - require.NoError(t, batch.SetHeight(ctx, 1)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(h, d, &types.Signature{})) + require.NoError(t, batch.SetHeight(1)) + require.NoError(t, batch.Commit()) pendingHeaders, err := NewPendingHeaders(store, zerolog.Nop()) require.NoError(t, err) diff --git a/block/internal/executing/executor.go b/block/internal/executing/executor.go index c9a553e46..86a3d0eb0 100644 --- a/block/internal/executing/executor.go +++ b/block/internal/executing/executor.go @@ -224,13 +224,13 @@ func (e *Executor) initializeState() error { if err != nil { return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SetHeight(e.ctx, state.LastBlockHeight); err != nil { + if err := batch.SetHeight(state.LastBlockHeight); err != nil { return fmt.Errorf("failed to set store height: %w", err) } - if err := batch.UpdateState(e.ctx, state); err != nil { + if err := batch.UpdateState(state); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(e.ctx); err != nil { + if err := batch.Commit(); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } @@ -389,10 +389,10 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to create batch for early save: %w", err) } - if err = batch.SaveBlockData(ctx, header, data, &types.Signature{}); err != nil { + if err = batch.SaveBlockData(header, data, &types.Signature{}); err != nil { return fmt.Errorf("failed to save block data: %w", err) } - if err = batch.Commit(ctx); err != nil { + if err = batch.Commit(); err != nil { return fmt.Errorf("failed to commit early save batch: %w", err) } } @@ -433,19 +433,19 @@ func (e *Executor) ProduceBlock(ctx context.Context) error { return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SaveBlockData(ctx, header, data, &signature); err != nil { + if err := batch.SaveBlockData(header, data, &signature); err != nil { return fmt.Errorf("failed to save block: %w", err) } - if err := batch.SetHeight(ctx, newHeight); err != nil { + if err := batch.SetHeight(newHeight); err != nil { return fmt.Errorf("failed to update store height: %w", err) } - if err := batch.UpdateState(ctx, newState); err != nil { + if err := batch.UpdateState(newState); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(ctx); err != nil { + if err := batch.Commit(); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } diff --git a/block/internal/executing/executor_restart_test.go b/block/internal/executing/executor_restart_test.go index 6ec5a5c8f..0df53d8e9 100644 --- a/block/internal/executing/executor_restart_test.go +++ b/block/internal/executing/executor_restart_test.go @@ -156,12 +156,11 @@ func TestExecutor_RestartUsesPendingHeader(t *testing.T) { pendingHeader.DataHash = pendingData.DACommitment() // Save pending block data (this is what would happen during a crash) - ctx := context.Background() - batch, err := memStore.NewBatch(ctx) + batch, err := memStore.NewBatch(context.Background()) require.NoError(t, err) - err = batch.SaveBlockData(ctx, pendingHeader, pendingData, &types.Signature{}) + err = batch.SaveBlockData(pendingHeader, pendingData, &types.Signature{}) require.NoError(t, err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(t, err) // Stop first executor (simulating crash/restart) diff --git a/block/internal/submitting/da_submitter_integration_test.go b/block/internal/submitting/da_submitter_integration_test.go index f9f026469..0c23f7d08 100644 --- a/block/internal/submitting/da_submitter_integration_test.go +++ b/block/internal/submitting/da_submitter_integration_test.go @@ -71,19 +71,18 @@ func TestDASubmitter_SubmitHeadersAndData_MarksInclusionAndUpdatesLastSubmitted( sig2t := types.Signature(sig2) // Save block 1 - ctx := context.Background() - batch1, err := st.NewBatch(ctx) + batch1, err := st.NewBatch(context.Background()) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(ctx, hdr1, data1, &sig1t)) - require.NoError(t, batch1.SetHeight(ctx, 1)) - require.NoError(t, batch1.Commit(ctx)) + require.NoError(t, batch1.SaveBlockData(hdr1, data1, &sig1t)) + require.NoError(t, batch1.SetHeight(1)) + require.NoError(t, batch1.Commit()) // Save block 2 - batch2, err := st.NewBatch(ctx) + batch2, err := st.NewBatch(context.Background()) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(ctx, hdr2, data2, &sig2t)) - require.NoError(t, batch2.SetHeight(ctx, 2)) - require.NoError(t, batch2.Commit(ctx)) + require.NoError(t, batch2.SaveBlockData(hdr2, data2, &sig2t)) + require.NoError(t, batch2.SetHeight(2)) + require.NoError(t, batch2.Commit()) // Mock DA client client := mocks.NewMockClient(t) diff --git a/block/internal/submitting/da_submitter_test.go b/block/internal/submitting/da_submitter_test.go index 66598f302..476011fe8 100644 --- a/block/internal/submitting/da_submitter_test.go +++ b/block/internal/submitting/da_submitter_test.go @@ -201,16 +201,16 @@ func TestDASubmitter_SubmitHeaders_Success(t *testing.T) { // Save block 1 batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(ctx, header1, data1, &sig1)) - require.NoError(t, batch1.SetHeight(ctx, 1)) - require.NoError(t, batch1.Commit(ctx)) + require.NoError(t, batch1.SaveBlockData(header1, data1, &sig1)) + require.NoError(t, batch1.SetHeight(1)) + require.NoError(t, batch1.Commit()) // Save block 2 batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(ctx, header2, data2, &sig2)) - require.NoError(t, batch2.SetHeight(ctx, 2)) - require.NoError(t, batch2.Commit(ctx)) + require.NoError(t, batch2.SaveBlockData(header2, data2, &sig2)) + require.NoError(t, batch2.SetHeight(2)) + require.NoError(t, batch2.Commit()) // Get headers from cache and submit headers, marshalledHeaders, err := cm.GetPendingHeaders(ctx) @@ -318,16 +318,16 @@ func TestDASubmitter_SubmitData_Success(t *testing.T) { // Save block 1 batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(ctx, header1, data1, &sig1)) - require.NoError(t, batch1.SetHeight(ctx, 1)) - require.NoError(t, batch1.Commit(ctx)) + require.NoError(t, batch1.SaveBlockData(header1, data1, &sig1)) + require.NoError(t, batch1.SetHeight(1)) + require.NoError(t, batch1.Commit()) // Save block 2 batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(ctx, header2, data2, &sig2)) - require.NoError(t, batch2.SetHeight(ctx, 2)) - require.NoError(t, batch2.Commit(ctx)) + require.NoError(t, batch2.SaveBlockData(header2, data2, &sig2)) + require.NoError(t, batch2.SetHeight(2)) + require.NoError(t, batch2.Commit()) // Get data from cache and submit signedDataList, marshalledData, err := cm.GetPendingData(ctx) @@ -378,9 +378,9 @@ func TestDASubmitter_SubmitData_SkipsEmptyData(t *testing.T) { sig := types.Signature([]byte("sig")) batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, header, emptyData, &sig)) - require.NoError(t, batch.SetHeight(ctx, 1)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(header, emptyData, &sig)) + require.NoError(t, batch.SetHeight(1)) + require.NoError(t, batch.Commit()) // Get data from cache and submit - should succeed but skip empty data // Get data from cache and submit @@ -439,9 +439,9 @@ func TestDASubmitter_SubmitData_NilSigner(t *testing.T) { sig := types.Signature([]byte("sig")) batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, header, data, &sig)) - require.NoError(t, batch.SetHeight(ctx, 1)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(header, data, &sig)) + require.NoError(t, batch.SetHeight(1)) + require.NoError(t, batch.Commit()) // Get data from cache and submit with nil signer - should fail signedDataList, marshalledData, err := cm.GetPendingData(ctx) diff --git a/block/internal/submitting/submitter_test.go b/block/internal/submitting/submitter_test.go index 7f35535c8..07703be94 100644 --- a/block/internal/submitting/submitter_test.go +++ b/block/internal/submitting/submitter_test.go @@ -111,8 +111,8 @@ func TestSubmitter_IsHeightDAIncluded(t *testing.T) { cm, st := newTestCacheAndStore(t) batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SetHeight(ctx, 5)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SetHeight(5)) + require.NoError(t, batch.Commit()) s := &Submitter{store: st, cache: cm, logger: zerolog.Nop()} s.ctx = ctx @@ -265,16 +265,16 @@ func TestSubmitter_processDAInclusionLoop_advances(t *testing.T) { // Save block 1 batch1, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch1.SaveBlockData(ctx, h1, d1, &sig)) - require.NoError(t, batch1.SetHeight(ctx, 1)) - require.NoError(t, batch1.Commit(ctx)) + require.NoError(t, batch1.SaveBlockData(h1, d1, &sig)) + require.NoError(t, batch1.SetHeight(1)) + require.NoError(t, batch1.Commit()) // Save block 2 batch2, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch2.SaveBlockData(ctx, h2, d2, &sig)) - require.NoError(t, batch2.SetHeight(ctx, 2)) - require.NoError(t, batch2.Commit(ctx)) + require.NoError(t, batch2.SaveBlockData(h2, d2, &sig)) + require.NoError(t, batch2.SetHeight(2)) + require.NoError(t, batch2.Commit()) cm.SetHeaderDAIncluded(h1.Hash().String(), 100, 1) cm.SetDataDAIncluded(d1.DACommitment().String(), 100, 1) @@ -380,10 +380,10 @@ func TestSubmitter_daSubmissionLoop(t *testing.T) { // Store the blocks batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, h1, d1, &types.Signature{})) - require.NoError(t, batch.SaveBlockData(ctx, h2, d2, &types.Signature{})) - require.NoError(t, batch.SetHeight(ctx, 2)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(h1, d1, &types.Signature{})) + require.NoError(t, batch.SaveBlockData(h2, d2, &types.Signature{})) + require.NoError(t, batch.SetHeight(2)) + require.NoError(t, batch.Commit()) // Start and wait for calls require.NoError(t, s.Start(ctx)) @@ -481,9 +481,9 @@ func TestSubmitter_CacheClearedOnHeightInclusion(t *testing.T) { for _, block := range blocks { batch, err := st.NewBatch(ctx) require.NoError(t, err) - require.NoError(t, batch.SaveBlockData(ctx, block.header, block.data, &sig)) - require.NoError(t, batch.SetHeight(ctx, block.height)) - require.NoError(t, batch.Commit(ctx)) + require.NoError(t, batch.SaveBlockData(block.header, block.data, &sig)) + require.NoError(t, batch.SetHeight(block.height)) + require.NoError(t, batch.Commit()) } // Set up cache with headers and data seen for all heights diff --git a/block/internal/syncing/syncer.go b/block/internal/syncing/syncer.go index 3a75e05b0..ede86c9a2 100644 --- a/block/internal/syncing/syncer.go +++ b/block/internal/syncing/syncer.go @@ -320,13 +320,13 @@ func (s *Syncer) initializeState() error { if err != nil { return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SetHeight(s.ctx, state.LastBlockHeight); err != nil { + if err := batch.SetHeight(state.LastBlockHeight); err != nil { return fmt.Errorf("failed to set store height: %w", err) } - if err := batch.UpdateState(s.ctx, state); err != nil { + if err := batch.UpdateState(state); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(s.ctx); err != nil { + if err := batch.Commit(); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } s.SetLastState(state) @@ -694,19 +694,19 @@ func (s *Syncer) TrySyncNextBlock(ctx context.Context, event *common.DAHeightEve return fmt.Errorf("failed to create batch: %w", err) } - if err := batch.SaveBlockData(ctx, header, data, &header.Signature); err != nil { + if err := batch.SaveBlockData(header, data, &header.Signature); err != nil { return fmt.Errorf("failed to save block: %w", err) } - if err := batch.SetHeight(ctx, nextHeight); err != nil { + if err := batch.SetHeight(nextHeight); err != nil { return fmt.Errorf("failed to update height: %w", err) } - if err := batch.UpdateState(ctx, newState); err != nil { + if err := batch.UpdateState(newState); err != nil { return fmt.Errorf("failed to update state: %w", err) } - if err := batch.Commit(ctx); err != nil { + if err := batch.Commit(); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } diff --git a/block/internal/syncing/syncer_test.go b/block/internal/syncing/syncer_test.go index 48d3ceefb..2ba71a78f 100644 --- a/block/internal/syncing/syncer_test.go +++ b/block/internal/syncing/syncer_test.go @@ -288,8 +288,8 @@ func TestSyncer_processPendingEvents(t *testing.T) { // current height 1 batch, err := st.NewBatch(t.Context()) require.NoError(t, err) - require.NoError(t, batch.SetHeight(t.Context(), 1)) - require.NoError(t, batch.Commit(t.Context())) + require.NoError(t, batch.SetHeight(1)) + require.NoError(t, batch.Commit()) s := &Syncer{ store: st, @@ -614,9 +614,9 @@ func TestSyncer_InitializeState_CallsReplayer(t *testing.T) { // Mock batch operations mockBatch := new(testmocks.MockBatch) - mockBatch.On("SetHeight", mock.Anything, storeHeight).Return(nil) - mockBatch.On("UpdateState", mock.Anything, mock.Anything).Return(nil) - mockBatch.On("Commit", mock.Anything).Return(nil) + mockBatch.On("SetHeight", storeHeight).Return(nil) + mockBatch.On("UpdateState", mock.Anything).Return(nil) + mockBatch.On("Commit").Return(nil) mockStore.EXPECT().NewBatch(mock.Anything).Return(mockBatch, nil) syncer := &Syncer{ diff --git a/pkg/store/batch.go b/pkg/store/batch.go index 788819783..405119c61 100644 --- a/pkg/store/batch.go +++ b/pkg/store/batch.go @@ -15,6 +15,7 @@ import ( type DefaultBatch struct { store *DefaultStore batch ds.Batch + ctx context.Context } // NewBatch creates a new batch for atomic operations @@ -27,12 +28,13 @@ func (s *DefaultStore) NewBatch(ctx context.Context) (Batch, error) { return &DefaultBatch{ store: s, batch: batch, + ctx: ctx, }, nil } // SetHeight sets the height in the batch -func (b *DefaultBatch) SetHeight(ctx context.Context, height uint64) error { - currentHeight, err := b.store.Height(ctx) +func (b *DefaultBatch) SetHeight(height uint64) error { + currentHeight, err := b.store.Height(b.ctx) if err != nil { return err } @@ -41,11 +43,11 @@ func (b *DefaultBatch) SetHeight(ctx context.Context, height uint64) error { } heightBytes := encodeHeight(height) - return b.batch.Put(ctx, ds.NewKey(getHeightKey()), heightBytes) + return b.batch.Put(b.ctx, ds.NewKey(getHeightKey()), heightBytes) } // SaveBlockData saves block data to the batch -func (b *DefaultBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { +func (b *DefaultBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { height := header.Height() signatureHash := *signature @@ -58,19 +60,19 @@ func (b *DefaultBatch) SaveBlockData(ctx context.Context, header *types.SignedHe return fmt.Errorf("failed to marshal Data to binary: %w", err) } - if err := b.batch.Put(ctx, ds.NewKey(getHeaderKey(height)), headerBlob); err != nil { + if err := b.batch.Put(b.ctx, ds.NewKey(getHeaderKey(height)), headerBlob); err != nil { return fmt.Errorf("failed to put header blob in batch: %w", err) } - if err := b.batch.Put(ctx, ds.NewKey(getDataKey(height)), dataBlob); err != nil { + if err := b.batch.Put(b.ctx, ds.NewKey(getDataKey(height)), dataBlob); err != nil { return fmt.Errorf("failed to put data blob in batch: %w", err) } - if err := b.batch.Put(ctx, ds.NewKey(getSignatureKey(height)), signatureHash[:]); err != nil { + if err := b.batch.Put(b.ctx, ds.NewKey(getSignatureKey(height)), signatureHash[:]); err != nil { return fmt.Errorf("failed to put signature blob in batch: %w", err) } headerHash := sha256.Sum256(headerBlob) heightBytes := encodeHeight(height) - if err := b.batch.Put(ctx, ds.NewKey(getIndexKey(headerHash[:])), heightBytes); err != nil { + if err := b.batch.Put(b.ctx, ds.NewKey(getIndexKey(headerHash[:])), heightBytes); err != nil { return fmt.Errorf("failed to put index key in batch: %w", err) } @@ -78,8 +80,8 @@ func (b *DefaultBatch) SaveBlockData(ctx context.Context, header *types.SignedHe } // UpdateState updates the state in the batch -func (b *DefaultBatch) UpdateState(ctx context.Context, state types.State) error { - // save the state at the height specified in the state itself +func (b *DefaultBatch) UpdateState(state types.State) error { + // Save the state at the height specified in the state itself height := state.LastBlockHeight pbState, err := state.ToProto() @@ -91,23 +93,23 @@ func (b *DefaultBatch) UpdateState(ctx context.Context, state types.State) error return fmt.Errorf("failed to marshal state to protobuf: %w", err) } - return b.batch.Put(ctx, ds.NewKey(getStateAtHeightKey(height)), data) + return b.batch.Put(b.ctx, ds.NewKey(getStateAtHeightKey(height)), data) } // Commit commits all batched operations atomically -func (b *DefaultBatch) Commit(ctx context.Context) error { - if err := b.batch.Commit(ctx); err != nil { +func (b *DefaultBatch) Commit() error { + if err := b.batch.Commit(b.ctx); err != nil { return fmt.Errorf("failed to commit batch: %w", err) } return nil } // Delete adds a delete operation to the batch -func (b *DefaultBatch) Delete(ctx context.Context, key ds.Key) error { - return b.batch.Delete(ctx, key) +func (b *DefaultBatch) Delete(key ds.Key) error { + return b.batch.Delete(b.ctx, key) } // Put adds a put operation to the batch -func (b *DefaultBatch) Put(ctx context.Context, key ds.Key, value []byte) error { - return b.batch.Put(ctx, key, value) +func (b *DefaultBatch) Put(key ds.Key, value []byte) error { + return b.batch.Put(b.ctx, key, value) } diff --git a/pkg/store/store_test.go b/pkg/store/store_test.go index c751e99e4..8636a0ad0 100644 --- a/pkg/store/store_test.go +++ b/pkg/store/store_test.go @@ -122,20 +122,19 @@ func TestStoreHeight(t *testing.T) { assert.NoError(err) assert.Equal(uint64(0), height) - ctx := t.Context() for i, header := range c.headers { data := c.data[i] - batch, err := bstore.NewBatch(ctx) + batch, err := bstore.NewBatch(t.Context()) require.NoError(t, err) - err = batch.SaveBlockData(ctx, header, data, &types.Signature{}) + err = batch.SaveBlockData(header, data, &types.Signature{}) require.NoError(t, err) - err = batch.SetHeight(ctx, header.Height()) + err = batch.SetHeight(header.Height()) require.NoError(t, err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(t, err) } - height, err = bstore.Height(ctx) + height, err = bstore.Height(t.Context()) assert.NoError(err) assert.Equal(c.expected, height) }) @@ -176,29 +175,28 @@ func TestStoreLoad(t *testing.T) { require := require.New(t) bstore := New(kv) - ctx := t.Context() for i, header := range c.headers { data := c.data[i] signature := &header.Signature - batch, err := bstore.NewBatch(ctx) + batch, err := bstore.NewBatch(t.Context()) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, signature) + err = batch.SaveBlockData(header, data, signature) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } for i, expectedHeader := range c.headers { expectedData := c.data[i] - header, data, err := bstore.GetBlockData(ctx, expectedHeader.Height()) + header, data, err := bstore.GetBlockData(t.Context(), expectedHeader.Height()) assert.NoError(err) assert.NotNil(header) assert.NotNil(data) assert.Equal(expectedHeader, header) assert.Equal(expectedData, data) - signature, err := bstore.GetSignature(ctx, expectedHeader.Height()) + signature, err := bstore.GetSignature(t.Context(), expectedHeader.Height()) assert.NoError(err) assert.NotNil(signature) } @@ -220,16 +218,15 @@ func TestRestart(t *testing.T) { s1 := New(kv) expectedHeight := uint64(10) - ctx := t.Context() - batch, err := s1.NewBatch(ctx) + batch, err := s1.NewBatch(t.Context()) require.NoError(err) - err = batch.SetHeight(ctx, expectedHeight) + err = batch.SetHeight(expectedHeight) require.NoError(err) - err = batch.UpdateState(ctx, types.State{ + err = batch.UpdateState(types.State{ LastBlockHeight: expectedHeight, }) assert.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() assert.NoError(err) err = s1.Close() @@ -241,7 +238,7 @@ func TestRestart(t *testing.T) { s2 := New(kv) assert.NoError(err) - state2, err := s2.GetState(ctx) + state2, err := s2.GetState(t.Context()) assert.NoError(err) err = s2.Close() @@ -260,10 +257,9 @@ func TestSetHeightError(t *testing.T) { mockBatchingDs := &mockBatchingDatastore{Batching: mockDs, putError: mockErr} s := New(mockBatchingDs) - ctx := t.Context() - batch, err := s.NewBatch(ctx) + batch, err := s.NewBatch(t.Context()) require.NoError(err) - err = batch.SetHeight(ctx, 10) + err = batch.SetHeight(10) require.Error(err) require.Contains(err.Error(), mockErr.Error()) } @@ -438,21 +434,20 @@ func TestSaveBlockDataErrors(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() s := New(tc.mock) - ctx := t.Context() - batch, err := s.NewBatch(ctx) + batch, err := s.NewBatch(t.Context()) if tc.expectSub == "failed to create a new batch" { require.ErrorContains(t, err, tc.expectSub) return } require.NoError(t, err) - err = batch.SaveBlockData(ctx, header, data, signature) + err = batch.SaveBlockData(header, data, signature) if tc.expectSub == "failed to put header blob" { require.ErrorContains(t, err, tc.expectSub) return } require.NoError(t, err) - err = batch.Commit(ctx) + err = batch.Commit() require.ErrorContains(t, err, tc.expectSub) }) } @@ -534,10 +529,9 @@ func TestUpdateStateError(t *testing.T) { mockBatchingDsPut := &mockBatchingDatastore{Batching: mockDsPut, putError: mockErrPut} sPut := New(mockBatchingDsPut) - ctx := t.Context() - batch, err := sPut.NewBatch(ctx) + batch, err := sPut.NewBatch(t.Context()) require.NoError(err) - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.Error(err) require.Contains(err.Error(), mockErrPut.Error()) } @@ -563,18 +557,17 @@ func TestGetStateError(t *testing.T) { // Put some data that will cause unmarshal error height := uint64(1) - ctx := t.Context() - batch, err := sUnmarshal.NewBatch(ctx) + batch, err := sUnmarshal.NewBatch(t.Context()) require.NoError(err) - err = batch.SetHeight(ctx, height) + err = batch.SetHeight(height) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) - err = mockBatchingDsUnmarshal.Put(ctx, ds.NewKey(getStateAtHeightKey(height)), []byte("invalid state proto")) + err = mockBatchingDsUnmarshal.Put(t.Context(), ds.NewKey(getStateAtHeightKey(height)), []byte("invalid state proto")) require.NoError(err) - _, err = sUnmarshal.GetState(ctx) + _, err = sUnmarshal.GetState(t.Context()) require.Error(err) require.Contains(err.Error(), "failed to unmarshal state from protobuf") } @@ -683,9 +676,9 @@ func TestRollback(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -696,9 +689,9 @@ func TestRollback(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -757,11 +750,11 @@ func TestRollbackToSameHeight(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, height) + err = batch.SetHeight(height) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) // Execute rollback to same height @@ -794,11 +787,11 @@ func TestRollbackToHigherHeight(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, currentHeight) + err = batch.SetHeight(currentHeight) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) // Execute rollback to higher height @@ -870,9 +863,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -883,9 +876,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -917,9 +910,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -930,9 +923,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -968,9 +961,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -981,9 +974,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -1019,9 +1012,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -1032,9 +1025,9 @@ func TestRollbackDAIncludedHeightValidation(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -1074,9 +1067,9 @@ func TestRollbackDAIncludedHeightNotSet(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -1087,9 +1080,9 @@ func TestRollbackDAIncludedHeightNotSet(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -1122,9 +1115,9 @@ func TestRollbackDAIncludedHeightInvalidLength(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, h) + err = batch.SetHeight(h) require.NoError(err) // Create and update state for this height @@ -1135,9 +1128,9 @@ func TestRollbackDAIncludedHeightInvalidLength(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) } @@ -1172,9 +1165,9 @@ func TestRollbackDAIncludedHeightGetMetadataError(t *testing.T) { sig := &header.Signature batch, err := store.NewBatch(ctx) require.NoError(err) - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(err) - err = batch.SetHeight(ctx, uint64(2)) + err = batch.SetHeight(uint64(2)) require.NoError(err) // Create and update state for this height @@ -1185,9 +1178,9 @@ func TestRollbackDAIncludedHeightGetMetadataError(t *testing.T) { LastBlockTime: header.Time(), AppHash: header.AppHash, } - err = batch.UpdateState(ctx, state) + err = batch.UpdateState(state) require.NoError(err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(err) // Configure mock to return error when getting DA included height metadata diff --git a/pkg/store/tracing.go b/pkg/store/tracing.go index 204cfe756..a63ffb782 100644 --- a/pkg/store/tracing.go +++ b/pkg/store/tracing.go @@ -2,6 +2,7 @@ package store import ( "context" + "encoding/hex" ds "github.com/ipfs/go-datastore" "go.opentelemetry.io/otel" @@ -60,7 +61,7 @@ func (t *tracedStore) GetBlockData(ctx context.Context, height uint64) (*types.S func (t *tracedStore) GetBlockByHash(ctx context.Context, hash []byte) (*types.SignedHeader, *types.Data, error) { ctx, span := t.tracer.Start(ctx, "Store.GetBlockByHash", - trace.WithAttributes(attribute.String("hash", string(hash))), + trace.WithAttributes(attribute.String("hash", hex.EncodeToString(hash))), ) defer span.End() @@ -95,7 +96,7 @@ func (t *tracedStore) GetSignature(ctx context.Context, height uint64) (*types.S func (t *tracedStore) GetSignatureByHash(ctx context.Context, hash []byte) (*types.Signature, error) { ctx, span := t.tracer.Start(ctx, "Store.GetSignatureByHash", - trace.WithAttributes(attribute.String("hash", string(hash))), + trace.WithAttributes(attribute.String("hash", hex.EncodeToString(hash))), ) defer span.End() @@ -229,6 +230,7 @@ func (t *tracedStore) NewBatch(ctx context.Context) (Batch, error) { return &tracedBatch{ inner: batch, tracer: t.tracer, + ctx: ctx, }, nil } @@ -237,15 +239,16 @@ var _ Batch = (*tracedBatch)(nil) type tracedBatch struct { inner Batch tracer trace.Tracer + ctx context.Context } -func (b *tracedBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { - ctx, span := b.tracer.Start(ctx, "Batch.SaveBlockData", +func (b *tracedBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { + _, span := b.tracer.Start(b.ctx, "Batch.SaveBlockData", trace.WithAttributes(attribute.Int64("height", int64(header.Height()))), ) defer span.End() - err := b.inner.SaveBlockData(ctx, header, data, signature) + err := b.inner.SaveBlockData(header, data, signature) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -255,13 +258,13 @@ func (b *tracedBatch) SaveBlockData(ctx context.Context, header *types.SignedHea return nil } -func (b *tracedBatch) SetHeight(ctx context.Context, height uint64) error { - ctx, span := b.tracer.Start(ctx, "Batch.SetHeight", +func (b *tracedBatch) SetHeight(height uint64) error { + _, span := b.tracer.Start(b.ctx, "Batch.SetHeight", trace.WithAttributes(attribute.Int64("height", int64(height))), ) defer span.End() - err := b.inner.SetHeight(ctx, height) + err := b.inner.SetHeight(height) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -271,13 +274,13 @@ func (b *tracedBatch) SetHeight(ctx context.Context, height uint64) error { return nil } -func (b *tracedBatch) UpdateState(ctx context.Context, state types.State) error { - ctx, span := b.tracer.Start(ctx, "Batch.UpdateState", +func (b *tracedBatch) UpdateState(state types.State) error { + _, span := b.tracer.Start(b.ctx, "Batch.UpdateState", trace.WithAttributes(attribute.Int64("state.height", int64(state.LastBlockHeight))), ) defer span.End() - err := b.inner.UpdateState(ctx, state) + err := b.inner.UpdateState(state) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -287,11 +290,11 @@ func (b *tracedBatch) UpdateState(ctx context.Context, state types.State) error return nil } -func (b *tracedBatch) Commit(ctx context.Context) error { - ctx, span := b.tracer.Start(ctx, "Batch.Commit") +func (b *tracedBatch) Commit() error { + _, span := b.tracer.Start(b.ctx, "Batch.Commit") defer span.End() - err := b.inner.Commit(ctx) + err := b.inner.Commit() if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -301,8 +304,8 @@ func (b *tracedBatch) Commit(ctx context.Context) error { return nil } -func (b *tracedBatch) Put(ctx context.Context, key ds.Key, value []byte) error { - ctx, span := b.tracer.Start(ctx, "Batch.Put", +func (b *tracedBatch) Put(key ds.Key, value []byte) error { + _, span := b.tracer.Start(b.ctx, "Batch.Put", trace.WithAttributes( attribute.String("key", key.String()), attribute.Int("value.size", len(value)), @@ -310,7 +313,7 @@ func (b *tracedBatch) Put(ctx context.Context, key ds.Key, value []byte) error { ) defer span.End() - err := b.inner.Put(ctx, key, value) + err := b.inner.Put(key, value) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) @@ -320,13 +323,13 @@ func (b *tracedBatch) Put(ctx context.Context, key ds.Key, value []byte) error { return nil } -func (b *tracedBatch) Delete(ctx context.Context, key ds.Key) error { - ctx, span := b.tracer.Start(ctx, "Batch.Delete", +func (b *tracedBatch) Delete(key ds.Key) error { + _, span := b.tracer.Start(b.ctx, "Batch.Delete", trace.WithAttributes(attribute.String("key", key.String())), ) defer span.End() - err := b.inner.Delete(ctx, key) + err := b.inner.Delete(key) if err != nil { span.RecordError(err) span.SetStatus(codes.Error, err.Error()) diff --git a/pkg/store/tracing_test.go b/pkg/store/tracing_test.go index d5d92af1f..a5dca2417 100644 --- a/pkg/store/tracing_test.go +++ b/pkg/store/tracing_test.go @@ -120,52 +120,52 @@ func (m *tracingMockStore) NewBatch(ctx context.Context) (Batch, error) { } type tracingMockBatch struct { - saveBlockDataFn func(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error - setHeightFn func(ctx context.Context, height uint64) error - updateStateFn func(ctx context.Context, state types.State) error - commitFn func(ctx context.Context) error - putFn func(ctx context.Context, key ds.Key, value []byte) error - deleteFn func(ctx context.Context, key ds.Key) error + saveBlockDataFn func(header *types.SignedHeader, data *types.Data, signature *types.Signature) error + setHeightFn func(height uint64) error + updateStateFn func(state types.State) error + commitFn func() error + putFn func(key ds.Key, value []byte) error + deleteFn func(key ds.Key) error } -func (b *tracingMockBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { +func (b *tracingMockBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { if b.saveBlockDataFn != nil { - return b.saveBlockDataFn(ctx, header, data, signature) + return b.saveBlockDataFn(header, data, signature) } return nil } -func (b *tracingMockBatch) SetHeight(ctx context.Context, height uint64) error { +func (b *tracingMockBatch) SetHeight(height uint64) error { if b.setHeightFn != nil { - return b.setHeightFn(ctx, height) + return b.setHeightFn(height) } return nil } -func (b *tracingMockBatch) UpdateState(ctx context.Context, state types.State) error { +func (b *tracingMockBatch) UpdateState(state types.State) error { if b.updateStateFn != nil { - return b.updateStateFn(ctx, state) + return b.updateStateFn(state) } return nil } -func (b *tracingMockBatch) Commit(ctx context.Context) error { +func (b *tracingMockBatch) Commit() error { if b.commitFn != nil { - return b.commitFn(ctx) + return b.commitFn() } return nil } -func (b *tracingMockBatch) Put(ctx context.Context, key ds.Key, value []byte) error { +func (b *tracingMockBatch) Put(key ds.Key, value []byte) error { if b.putFn != nil { - return b.putFn(ctx, key, value) + return b.putFn(key, value) } return nil } -func (b *tracingMockBatch) Delete(ctx context.Context, key ds.Key) error { +func (b *tracingMockBatch) Delete(key ds.Key) error { if b.deleteFn != nil { - return b.deleteFn(ctx, key) + return b.deleteFn(key) } return nil } @@ -323,7 +323,7 @@ func TestTracedBatch_Commit_Success(t *testing.T) { batch, err := store.NewBatch(ctx) require.NoError(t, err) - err = batch.Commit(ctx) + err = batch.Commit() require.NoError(t, err) spans := sr.Ended() @@ -348,7 +348,7 @@ func TestTracedBatch_SaveBlockData_Success(t *testing.T) { data := &types.Data{} sig := &types.Signature{} - err = batch.SaveBlockData(ctx, header, data, sig) + err = batch.SaveBlockData(header, data, sig) require.NoError(t, err) spans := sr.Ended() diff --git a/pkg/store/types.go b/pkg/store/types.go index f5c3e0629..bf1cb6ced 100644 --- a/pkg/store/types.go +++ b/pkg/store/types.go @@ -11,22 +11,22 @@ import ( // Batch provides atomic operations for the store type Batch interface { // SaveBlockData atomically saves the block header, data, and signature - SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error + SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error // SetHeight sets the height in the batch - SetHeight(ctx context.Context, height uint64) error + SetHeight(height uint64) error // UpdateState updates the state in the batch - UpdateState(ctx context.Context, state types.State) error + UpdateState(state types.State) error // Commit commits all batch operations atomically - Commit(ctx context.Context) error + Commit() error // Put adds a put operation to the batch (used internally for rollback) - Put(ctx context.Context, key ds.Key, value []byte) error + Put(key ds.Key, value []byte) error // Delete adds a delete operation to the batch (used internally for rollback) - Delete(ctx context.Context, key ds.Key) error + Delete(key ds.Key) error } // Store is minimal interface for storing and retrieving blocks, commits and state. diff --git a/test/mocks/batch.go b/test/mocks/batch.go index 7301ffefb..90025855b 100644 --- a/test/mocks/batch.go +++ b/test/mocks/batch.go @@ -5,8 +5,6 @@ package mocks import ( - "context" - "github.com/evstack/ev-node/types" "github.com/ipfs/go-datastore" mock "github.com/stretchr/testify/mock" @@ -40,16 +38,16 @@ func (_m *MockBatch) EXPECT() *MockBatch_Expecter { } // Commit provides a mock function for the type MockBatch -func (_mock *MockBatch) Commit(ctx context.Context) error { - ret := _mock.Called(ctx) +func (_mock *MockBatch) Commit() error { + ret := _mock.Called() if len(ret) == 0 { panic("no return value specified for Commit") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context) error); ok { - r0 = returnFunc(ctx) + if returnFunc, ok := ret.Get(0).(func() error); ok { + r0 = returnFunc() } else { r0 = ret.Error(0) } @@ -62,14 +60,13 @@ type MockBatch_Commit_Call struct { } // Commit is a helper method to define mock.On call -// - ctx context.Context -func (_e *MockBatch_Expecter) Commit(ctx interface{}) *MockBatch_Commit_Call { - return &MockBatch_Commit_Call{Call: _e.mock.On("Commit", ctx)} +func (_e *MockBatch_Expecter) Commit() *MockBatch_Commit_Call { + return &MockBatch_Commit_Call{Call: _e.mock.On("Commit")} } -func (_c *MockBatch_Commit_Call) Run(run func(ctx context.Context)) *MockBatch_Commit_Call { +func (_c *MockBatch_Commit_Call) Run(run func()) *MockBatch_Commit_Call { _c.Call.Run(func(args mock.Arguments) { - run(args[0].(context.Context)) + run() }) return _c } @@ -79,22 +76,22 @@ func (_c *MockBatch_Commit_Call) Return(err error) *MockBatch_Commit_Call { return _c } -func (_c *MockBatch_Commit_Call) RunAndReturn(run func(context.Context) error) *MockBatch_Commit_Call { +func (_c *MockBatch_Commit_Call) RunAndReturn(run func() error) *MockBatch_Commit_Call { _c.Call.Return(run) return _c } // Delete provides a mock function for the type MockBatch -func (_mock *MockBatch) Delete(ctx context.Context, key datastore.Key) error { - ret := _mock.Called(ctx, key) +func (_mock *MockBatch) Delete(key datastore.Key) error { + ret := _mock.Called(key) if len(ret) == 0 { panic("no return value specified for Delete") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, datastore.Key) error); ok { - r0 = returnFunc(ctx, key) + if returnFunc, ok := ret.Get(0).(func(datastore.Key) error); ok { + r0 = returnFunc(key) } else { r0 = ret.Error(0) } @@ -107,19 +104,20 @@ type MockBatch_Delete_Call struct { } // Delete is a helper method to define mock.On call -// - ctx context.Context // - key datastore.Key -func (_e *MockBatch_Expecter) Delete(ctx interface{}, key interface{}) *MockBatch_Delete_Call { - return &MockBatch_Delete_Call{Call: _e.mock.On("Delete", ctx, key)} +func (_e *MockBatch_Expecter) Delete(key interface{}) *MockBatch_Delete_Call { + return &MockBatch_Delete_Call{Call: _e.mock.On("Delete", key)} } -func (_c *MockBatch_Delete_Call) Run(run func(ctx context.Context, key datastore.Key)) *MockBatch_Delete_Call { +func (_c *MockBatch_Delete_Call) Run(run func(key datastore.Key)) *MockBatch_Delete_Call { _c.Call.Run(func(args mock.Arguments) { - var arg1 datastore.Key - if args[1] != nil { - arg1 = args[1].(datastore.Key) + var arg0 datastore.Key + if args[0] != nil { + arg0 = args[0].(datastore.Key) } - run(args[0].(context.Context), arg1) + run( + arg0, + ) }) return _c } @@ -129,22 +127,22 @@ func (_c *MockBatch_Delete_Call) Return(err error) *MockBatch_Delete_Call { return _c } -func (_c *MockBatch_Delete_Call) RunAndReturn(run func(context.Context, datastore.Key) error) *MockBatch_Delete_Call { +func (_c *MockBatch_Delete_Call) RunAndReturn(run func(key datastore.Key) error) *MockBatch_Delete_Call { _c.Call.Return(run) return _c } // Put provides a mock function for the type MockBatch -func (_mock *MockBatch) Put(ctx context.Context, key datastore.Key, value []byte) error { - ret := _mock.Called(ctx, key, value) +func (_mock *MockBatch) Put(key datastore.Key, value []byte) error { + ret := _mock.Called(key, value) if len(ret) == 0 { panic("no return value specified for Put") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, datastore.Key, []byte) error); ok { - r0 = returnFunc(ctx, key, value) + if returnFunc, ok := ret.Get(0).(func(datastore.Key, []byte) error); ok { + r0 = returnFunc(key, value) } else { r0 = ret.Error(0) } @@ -157,24 +155,26 @@ type MockBatch_Put_Call struct { } // Put is a helper method to define mock.On call -// - ctx context.Context // - key datastore.Key // - value []byte -func (_e *MockBatch_Expecter) Put(ctx interface{}, key interface{}, value interface{}) *MockBatch_Put_Call { - return &MockBatch_Put_Call{Call: _e.mock.On("Put", ctx, key, value)} +func (_e *MockBatch_Expecter) Put(key interface{}, value interface{}) *MockBatch_Put_Call { + return &MockBatch_Put_Call{Call: _e.mock.On("Put", key, value)} } -func (_c *MockBatch_Put_Call) Run(run func(ctx context.Context, key datastore.Key, value []byte)) *MockBatch_Put_Call { +func (_c *MockBatch_Put_Call) Run(run func(key datastore.Key, value []byte)) *MockBatch_Put_Call { _c.Call.Run(func(args mock.Arguments) { - var arg1 datastore.Key - if args[1] != nil { - arg1 = args[1].(datastore.Key) + var arg0 datastore.Key + if args[0] != nil { + arg0 = args[0].(datastore.Key) } - var arg2 []byte - if args[2] != nil { - arg2 = args[2].([]byte) + var arg1 []byte + if args[1] != nil { + arg1 = args[1].([]byte) } - run(args[0].(context.Context), arg1, arg2) + run( + arg0, + arg1, + ) }) return _c } @@ -184,22 +184,22 @@ func (_c *MockBatch_Put_Call) Return(err error) *MockBatch_Put_Call { return _c } -func (_c *MockBatch_Put_Call) RunAndReturn(run func(context.Context, datastore.Key, []byte) error) *MockBatch_Put_Call { +func (_c *MockBatch_Put_Call) RunAndReturn(run func(key datastore.Key, value []byte) error) *MockBatch_Put_Call { _c.Call.Return(run) return _c } // SaveBlockData provides a mock function for the type MockBatch -func (_mock *MockBatch) SaveBlockData(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature) error { - ret := _mock.Called(ctx, header, data, signature) +func (_mock *MockBatch) SaveBlockData(header *types.SignedHeader, data *types.Data, signature *types.Signature) error { + ret := _mock.Called(header, data, signature) if len(ret) == 0 { panic("no return value specified for SaveBlockData") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, *types.SignedHeader, *types.Data, *types.Signature) error); ok { - r0 = returnFunc(ctx, header, data, signature) + if returnFunc, ok := ret.Get(0).(func(*types.SignedHeader, *types.Data, *types.Signature) error); ok { + r0 = returnFunc(header, data, signature) } else { r0 = ret.Error(0) } @@ -212,29 +212,32 @@ type MockBatch_SaveBlockData_Call struct { } // SaveBlockData is a helper method to define mock.On call -// - ctx context.Context // - header *types.SignedHeader // - data *types.Data // - signature *types.Signature -func (_e *MockBatch_Expecter) SaveBlockData(ctx interface{}, header interface{}, data interface{}, signature interface{}) *MockBatch_SaveBlockData_Call { - return &MockBatch_SaveBlockData_Call{Call: _e.mock.On("SaveBlockData", ctx, header, data, signature)} +func (_e *MockBatch_Expecter) SaveBlockData(header interface{}, data interface{}, signature interface{}) *MockBatch_SaveBlockData_Call { + return &MockBatch_SaveBlockData_Call{Call: _e.mock.On("SaveBlockData", header, data, signature)} } -func (_c *MockBatch_SaveBlockData_Call) Run(run func(ctx context.Context, header *types.SignedHeader, data *types.Data, signature *types.Signature)) *MockBatch_SaveBlockData_Call { +func (_c *MockBatch_SaveBlockData_Call) Run(run func(header *types.SignedHeader, data *types.Data, signature *types.Signature)) *MockBatch_SaveBlockData_Call { _c.Call.Run(func(args mock.Arguments) { - var arg1 *types.SignedHeader + var arg0 *types.SignedHeader + if args[0] != nil { + arg0 = args[0].(*types.SignedHeader) + } + var arg1 *types.Data if args[1] != nil { - arg1 = args[1].(*types.SignedHeader) + arg1 = args[1].(*types.Data) } - var arg2 *types.Data + var arg2 *types.Signature if args[2] != nil { - arg2 = args[2].(*types.Data) - } - var arg3 *types.Signature - if args[3] != nil { - arg3 = args[3].(*types.Signature) + arg2 = args[2].(*types.Signature) } - run(args[0].(context.Context), arg1, arg2, arg3) + run( + arg0, + arg1, + arg2, + ) }) return _c } @@ -244,22 +247,22 @@ func (_c *MockBatch_SaveBlockData_Call) Return(err error) *MockBatch_SaveBlockDa return _c } -func (_c *MockBatch_SaveBlockData_Call) RunAndReturn(run func(context.Context, *types.SignedHeader, *types.Data, *types.Signature) error) *MockBatch_SaveBlockData_Call { +func (_c *MockBatch_SaveBlockData_Call) RunAndReturn(run func(header *types.SignedHeader, data *types.Data, signature *types.Signature) error) *MockBatch_SaveBlockData_Call { _c.Call.Return(run) return _c } // SetHeight provides a mock function for the type MockBatch -func (_mock *MockBatch) SetHeight(ctx context.Context, height uint64) error { - ret := _mock.Called(ctx, height) +func (_mock *MockBatch) SetHeight(height uint64) error { + ret := _mock.Called(height) if len(ret) == 0 { panic("no return value specified for SetHeight") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, uint64) error); ok { - r0 = returnFunc(ctx, height) + if returnFunc, ok := ret.Get(0).(func(uint64) error); ok { + r0 = returnFunc(height) } else { r0 = ret.Error(0) } @@ -272,19 +275,20 @@ type MockBatch_SetHeight_Call struct { } // SetHeight is a helper method to define mock.On call -// - ctx context.Context // - height uint64 -func (_e *MockBatch_Expecter) SetHeight(ctx interface{}, height interface{}) *MockBatch_SetHeight_Call { - return &MockBatch_SetHeight_Call{Call: _e.mock.On("SetHeight", ctx, height)} +func (_e *MockBatch_Expecter) SetHeight(height interface{}) *MockBatch_SetHeight_Call { + return &MockBatch_SetHeight_Call{Call: _e.mock.On("SetHeight", height)} } -func (_c *MockBatch_SetHeight_Call) Run(run func(ctx context.Context, height uint64)) *MockBatch_SetHeight_Call { +func (_c *MockBatch_SetHeight_Call) Run(run func(height uint64)) *MockBatch_SetHeight_Call { _c.Call.Run(func(args mock.Arguments) { - var arg1 uint64 - if args[1] != nil { - arg1 = args[1].(uint64) + var arg0 uint64 + if args[0] != nil { + arg0 = args[0].(uint64) } - run(args[0].(context.Context), arg1) + run( + arg0, + ) }) return _c } @@ -294,22 +298,22 @@ func (_c *MockBatch_SetHeight_Call) Return(err error) *MockBatch_SetHeight_Call return _c } -func (_c *MockBatch_SetHeight_Call) RunAndReturn(run func(context.Context, uint64) error) *MockBatch_SetHeight_Call { +func (_c *MockBatch_SetHeight_Call) RunAndReturn(run func(height uint64) error) *MockBatch_SetHeight_Call { _c.Call.Return(run) return _c } // UpdateState provides a mock function for the type MockBatch -func (_mock *MockBatch) UpdateState(ctx context.Context, state types.State) error { - ret := _mock.Called(ctx, state) +func (_mock *MockBatch) UpdateState(state types.State) error { + ret := _mock.Called(state) if len(ret) == 0 { panic("no return value specified for UpdateState") } var r0 error - if returnFunc, ok := ret.Get(0).(func(context.Context, types.State) error); ok { - r0 = returnFunc(ctx, state) + if returnFunc, ok := ret.Get(0).(func(types.State) error); ok { + r0 = returnFunc(state) } else { r0 = ret.Error(0) } @@ -322,19 +326,20 @@ type MockBatch_UpdateState_Call struct { } // UpdateState is a helper method to define mock.On call -// - ctx context.Context // - state types.State -func (_e *MockBatch_Expecter) UpdateState(ctx interface{}, state interface{}) *MockBatch_UpdateState_Call { - return &MockBatch_UpdateState_Call{Call: _e.mock.On("UpdateState", ctx, state)} +func (_e *MockBatch_Expecter) UpdateState(state interface{}) *MockBatch_UpdateState_Call { + return &MockBatch_UpdateState_Call{Call: _e.mock.On("UpdateState", state)} } -func (_c *MockBatch_UpdateState_Call) Run(run func(ctx context.Context, state types.State)) *MockBatch_UpdateState_Call { +func (_c *MockBatch_UpdateState_Call) Run(run func(state types.State)) *MockBatch_UpdateState_Call { _c.Call.Run(func(args mock.Arguments) { - var arg1 types.State - if args[1] != nil { - arg1 = args[1].(types.State) + var arg0 types.State + if args[0] != nil { + arg0 = args[0].(types.State) } - run(args[0].(context.Context), arg1) + run( + arg0, + ) }) return _c } @@ -344,7 +349,7 @@ func (_c *MockBatch_UpdateState_Call) Return(err error) *MockBatch_UpdateState_C return _c } -func (_c *MockBatch_UpdateState_Call) RunAndReturn(run func(context.Context, types.State) error) *MockBatch_UpdateState_Call { +func (_c *MockBatch_UpdateState_Call) RunAndReturn(run func(state types.State) error) *MockBatch_UpdateState_Call { _c.Call.Return(run) return _c }