From 8a0cfbac7f34f4c4e38cbc257a777b4ea93182ca Mon Sep 17 00:00:00 2001 From: Dimitris Date: Mon, 23 Mar 2026 16:19:50 +0200 Subject: [PATCH 01/11] Add tracing ID --- go.mod | 2 +- go.sum | 4 +-- pkg/transmitter/dual_contract_transmitter.go | 26 ++++++++++++++++---- pkg/txm/txm.go | 8 ++++-- pkg/txm/types/transaction.go | 16 ++++++++++++ 5 files changed, 46 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index c2f6ce055c..908fc7b2a5 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.1.12-0.20260227110503-42b236799872 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8ae8cf67c1 github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563 - github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317132927-e8bc2c7b01f1 + github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317142002-710d5a74a87a github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 diff --git a/go.sum b/go.sum index 5cc9b7785d..4533c65597 100644 --- a/go.sum +++ b/go.sum @@ -660,8 +660,8 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8ae8cf67c1/go.mod h1:oyfOm4k0uqmgZIfxk1elI/59B02shbbJQiiUdPdbMgI= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563 h1:ACpDbAxG4fa4sA83dbtYcrnlpE/y7thNIZfHxTv2ZLs= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563/go.mod h1:jP5mrOLFEYZZkl7EiCHRRIMSSHCQsYypm1OZSus//iI= -github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317132927-e8bc2c7b01f1 h1:aUdjMnHpriMkEwsgeqQ/ZuNBjrWw6c46HG57TuPPEbE= -github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317132927-e8bc2c7b01f1/go.mod h1:kGprqyjsz6qFNVszOQoHc24wfvCjyipNZFste/3zcbs= +github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317142002-710d5a74a87a h1:sb0VC87+IGYF0C5GTv3seovKuszi9/DjuvMs0SaVBco= +github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317142002-710d5a74a87a/go.mod h1:kGprqyjsz6qFNVszOQoHc24wfvCjyipNZFste/3zcbs= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a h1:pr0VFI7AWlDVJBEkcvzXWd97V8w8QMNjRdfPVa/IQLk= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a/go.mod h1:jo+cUqNcHwN8IF7SInQNXDZ8qzBsyMpnLdYbDswviFc= github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 h1:T/eCDsUI8EJT4n5zSP4w1mz4RHH+ap8qieA17QYfBhk= diff --git a/pkg/transmitter/dual_contract_transmitter.go b/pkg/transmitter/dual_contract_transmitter.go index 1fa292757b..20130d17a5 100644 --- a/pkg/transmitter/dual_contract_transmitter.go +++ b/pkg/transmitter/dual_contract_transmitter.go @@ -3,7 +3,6 @@ package transmitter import ( "context" "database/sql" - "encoding/hex" stderrors "errors" "fmt" "strings" @@ -20,6 +19,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-evm/pkg/keys" "github.com/smartcontractkit/chainlink-evm/pkg/logpoller" + "github.com/smartcontractkit/chainlink-evm/pkg/txmgr" ) // TODO: Remove when new dual transmitter contracts are merged @@ -120,7 +120,12 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty oc.lggr.Warnw("failed to generate tx metadata for report", "err", err) } - oc.lggr.Debugw("Transmitting report", "report", hex.EncodeToString(report), "rawReportCtx", rawReportCtx, "contractAddress", oc.contractAddress, "txMeta", txMeta) + if txMeta == nil { + txMeta = &txmgr.TxMeta{} + } + tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp.ConfigDigest, reportCtx.ReportTimestamp.Epoch, reportCtx.ReportTimestamp.Round, oc.contractAddress) + txMeta.TracingID = &tracingID + oc.lggr.Infow("Transmitting report", "configDigest", reportCtx.ReportTimestamp.ConfigDigest, "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) // Primary transmission payload, err := oc.contractABI.Pack("transmit", rawReportCtx, []byte(report), rs, ss, vs) @@ -129,8 +134,11 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty } transactionErr := errors.Wrap(oc.transmitter.CreateEthTransaction(ctx, oc.contractAddress, payload, txMeta), "failed to send primary Eth transaction") - - oc.lggr.Debugw("Created primary transaction", "error", transactionErr) + if transactionErr != nil { + oc.lggr.Errorw("Failed to create primary Eth transaction", "error", transactionErr, "tracingID", tracingID) + } else { + oc.lggr.Debugw("Created primary transaction", "tracingID", tracingID) + } // Secondary transmission secondaryPayload, err := oc.dualTransmissionABI.Pack("transmitSecondary", rawReportCtx, []byte(report), rs, ss, vs) @@ -139,7 +147,11 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty } err = errors.Wrap(oc.transmitter.CreateSecondaryEthTransaction(ctx, secondaryPayload, txMeta), "failed to send secondary Eth transaction") - oc.lggr.Debugw("Created secondary transaction", "error", err) + if err != nil { + oc.lggr.Errorw("Failed to create secondary Eth transaction", "error", err, "tracingID", tracingID) + } else { + oc.lggr.Debugw("Created secondary transaction", "tracingID", tracingID) + } return stderrors.Join(transactionErr, err) } @@ -242,6 +254,10 @@ func (oc *dualContractTransmitter) lockSecondary(ctx context.Context) error { return nil } +func generateTracingIDForOCR2(configDigest ocrtypes.ConfigDigest, epoch uint32, round uint8, address gethcommon.Address) string { + return fmt.Sprintf("%x:%d:%d:%s", configDigest, epoch, round, address.String()) +} + func (oc *dualContractTransmitter) Start(ctx context.Context) error { return oc.lockTransmitters(ctx) } diff --git a/pkg/txm/txm.go b/pkg/txm/txm.go index 8ce3da1087..98e5eb2676 100644 --- a/pkg/txm/txm.go +++ b/pkg/txm/txm.go @@ -188,7 +188,10 @@ func (t *Txm) Trigger(address common.Address) { if !exists { return } - triggerCh <- struct{}{} + select { + case triggerCh <- struct{}{}: + default: + } }) { t.lggr.Error("Txm unstarted") } @@ -333,7 +336,7 @@ func (t *Txm) sendTransactionWithError(ctx context.Context, tx *types.Transactio } start := time.Now() txErr := t.client.SendTransaction(ctx, tx, attempt) - t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "duration", time.Since(start), "txErr: ", txErr) + t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "tracingID", tx.GetTracingID(t.lggr), "duration", time.Since(start), "txErr: ", txErr) if txErr != nil && t.errorHandler != nil { if err = t.errorHandler.HandleError(ctx, tx, txErr, t.txStore, t.SetNonce, false); err != nil { return @@ -440,6 +443,7 @@ func (t *Txm) extractMetrics(ctx context.Context, txs []*types.Transaction) []ui if tx.InitialBroadcastAt != nil { t.Metrics.RecordTimeUntilTxConfirmed(ctx, float64(time.Since(*tx.InitialBroadcastAt))) } + t.lggr.Infow("Confirmed transaction", "txID", tx.ID, "tracingID", tx.GetTracingID(t.lggr)) } return confirmedTxIDs } diff --git a/pkg/txm/types/transaction.go b/pkg/txm/types/transaction.go index f33bcbef1b..ae8e21f2c2 100644 --- a/pkg/txm/types/transaction.go +++ b/pkg/txm/types/transaction.go @@ -13,6 +13,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/chainlink-common/pkg/sqlutil" clnull "github.com/smartcontractkit/chainlink-common/pkg/utils/null" @@ -106,6 +107,18 @@ func (t *Transaction) GetMeta() (*TxMeta, error) { return &m, nil } +func (t *Transaction) GetTracingID(lggr logger.SugaredLogger) string { + meta, err := t.GetMeta() + if err != nil { + lggr.Errorw("failed to get meta of the transaction", "err", err) + return "" + } + if meta != nil && meta.TracingID != nil { + return *meta.TracingID + } + return "" +} + type Attempt struct { ID uint64 TxID uint64 @@ -184,6 +197,9 @@ type TxMeta struct { // Dual Broadcast DualBroadcast *bool `json:"DualBroadcast,omitempty"` DualBroadcastParams *string `json:"DualBroadcastParams,omitempty"` + + // TracingID is used for tracing the entire lifecycle of a transaction from OCR Transmit to confirmation on-chain. + TracingID *string `json:"TracingID,omitempty"` } type QueueingTxStrategy struct { From b5f7f015c5ce88ee8665cfd31bcd1f5faeb40d3e Mon Sep 17 00:00:00 2001 From: Dimitris Date: Mon, 23 Mar 2026 17:58:23 +0200 Subject: [PATCH 02/11] Update tracingID --- pkg/transmitter/dual_contract_transmitter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/transmitter/dual_contract_transmitter.go b/pkg/transmitter/dual_contract_transmitter.go index 20130d17a5..d199a624ac 100644 --- a/pkg/transmitter/dual_contract_transmitter.go +++ b/pkg/transmitter/dual_contract_transmitter.go @@ -123,7 +123,7 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty if txMeta == nil { txMeta = &txmgr.TxMeta{} } - tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp.ConfigDigest, reportCtx.ReportTimestamp.Epoch, reportCtx.ReportTimestamp.Round, oc.contractAddress) + tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp.ConfigDigest, reportCtx.ReportTimestamp.Epoch, reportCtx.ReportTimestamp.Round) txMeta.TracingID = &tracingID oc.lggr.Infow("Transmitting report", "configDigest", reportCtx.ReportTimestamp.ConfigDigest, "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) @@ -254,8 +254,8 @@ func (oc *dualContractTransmitter) lockSecondary(ctx context.Context) error { return nil } -func generateTracingIDForOCR2(configDigest ocrtypes.ConfigDigest, epoch uint32, round uint8, address gethcommon.Address) string { - return fmt.Sprintf("%x:%d:%d:%s", configDigest, epoch, round, address.String()) +func generateTracingIDForOCR2(configDigest ocrtypes.ConfigDigest, epoch uint32, round uint8) string { + return fmt.Sprintf("0x%s:%d:%d", configDigest.Hex(), epoch, round) } func (oc *dualContractTransmitter) Start(ctx context.Context) error { From 16775bb2c3da843f63489b5f9e42e0ac157a0b70 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Mon, 23 Mar 2026 19:20:55 +0200 Subject: [PATCH 03/11] Improve logs --- .../clientwrappers/dualbroadcast/meta_client.go | 2 +- pkg/txm/txm.go | 2 +- pkg/txm/types/transaction.go | 17 +++++++++++++++-- pkg/txm/types/transaction_test.go | 13 +++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/pkg/txm/clientwrappers/dualbroadcast/meta_client.go b/pkg/txm/clientwrappers/dualbroadcast/meta_client.go index 9423973063..fdb2653c3c 100644 --- a/pkg/txm/clientwrappers/dualbroadcast/meta_client.go +++ b/pkg/txm/clientwrappers/dualbroadcast/meta_client.go @@ -595,6 +595,6 @@ func (a *MetaClient) SendOperation(ctx context.Context, tx *types.Transaction, a return fmt.Errorf("failed to update signed attempt for txID: %v, err: %w", tx.ID, err) } a.lggr.Infow("Intercepted attempt for tx", "txID", tx.ID, "hash", signedTx.Hash(), "toAddress", meta.ToAddress, "gasLimit", meta.GasLimit, - "TipCap", tip, "FeeCap", meta.MaxFeePerGas) + "TipCap", tip, "FeeCap", meta.MaxFeePerGas, "tracingID", tx.GetTracingID(a.lggr)) return a.c.SendTransaction(ctx, signedTx) } diff --git a/pkg/txm/txm.go b/pkg/txm/txm.go index 98e5eb2676..5b507fcb65 100644 --- a/pkg/txm/txm.go +++ b/pkg/txm/txm.go @@ -336,7 +336,7 @@ func (t *Txm) sendTransactionWithError(ctx context.Context, tx *types.Transactio } start := time.Now() txErr := t.client.SendTransaction(ctx, tx, attempt) - t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "tracingID", tx.GetTracingID(t.lggr), "duration", time.Since(start), "txErr: ", txErr) + t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "tracingID", tx.GetTracingID(t.lggr), "duration", time.Since(start), "txErr", txErr) if txErr != nil && t.errorHandler != nil { if err = t.errorHandler.HandleError(ctx, tx, txErr, t.txStore, t.SetNonce, false); err != nil { return diff --git a/pkg/txm/types/transaction.go b/pkg/txm/types/transaction.go index ae8e21f2c2..a5e718b822 100644 --- a/pkg/txm/types/transaction.go +++ b/pkg/txm/types/transaction.go @@ -54,10 +54,23 @@ type Transaction struct { func (t *Transaction) String() string { return fmt.Sprintf(`{txID:%d, IdempotencyKey:%v, ChainID:%v, Nonce:%s, FromAddress:%v, ToAddress:%v, Value:%v, `+ `Data:%s, SpecifiedGasLimit:%d, CreatedAt:%v, InitialBroadcastAt:%v, LastBroadcastAt:%v, State:%v, IsPurgeable:%v, AttemptCount:%d, `+ - `Meta:%v, Subject:%v}`, + `Meta:%s, Subject:%v}`, t.ID, stringOrNull(t.IdempotencyKey), t.ChainID, stringOrNull(t.Nonce), t.FromAddress, t.ToAddress, t.Value, base64.StdEncoding.EncodeToString(t.Data), t.SpecifiedGasLimit, t.CreatedAt, stringOrNull(t.InitialBroadcastAt), stringOrNull(t.LastBroadcastAt), - t.State, t.IsPurgeable, t.AttemptCount, t.Meta, t.Subject) + t.State, t.IsPurgeable, t.AttemptCount, t.metaString(), t.Subject) +} + +func (t *Transaction) metaString() string { + if t.Meta == nil { + return "null" + } + + meta, err := t.GetMeta() + if err == nil && meta != nil { + return fmt.Sprintf("%+v", *meta) + } + + return t.Meta.String() } func stringOrNull[T any](t *T) string { diff --git a/pkg/txm/types/transaction_test.go b/pkg/txm/types/transaction_test.go index 2e11100702..afb0ab7ea5 100644 --- a/pkg/txm/types/transaction_test.go +++ b/pkg/txm/types/transaction_test.go @@ -104,6 +104,19 @@ func TestTransaction_GetMeta(t *testing.T) { require.NoError(t, err) assert.Equal(t, tt.expected, got) + str := tx.String() + if tt.meta == nil { + assert.Contains(t, str, "Meta:null") + } else { + assert.Contains(t, str, "Meta:{") + assert.NotContains(t, str, `\"`) + + // Simulate structured log JSON encoding where tx is emitted as a string field. + encoded, encErr := json.Marshal(map[string]string{"tx": str}) + require.NoError(t, encErr) + assert.Contains(t, string(encoded), "FailOnRevert") + assert.NotContains(t, string(encoded), `\\\"FailOnRevert\\\"`) + } }) } } From e8777809923cf2c839f397ca9375d23dcd4bc0a1 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 14:55:40 +0200 Subject: [PATCH 04/11] Update framework dep --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 908fc7b2a5..ea4e195a1f 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.1.12-0.20260227110503-42b236799872 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8ae8cf67c1 github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563 - github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317142002-710d5a74a87a + github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260324111548-bcbe08e42d6f github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 diff --git a/go.sum b/go.sum index 4533c65597..5ac1e9ac5e 100644 --- a/go.sum +++ b/go.sum @@ -660,8 +660,8 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8ae8cf67c1/go.mod h1:oyfOm4k0uqmgZIfxk1elI/59B02shbbJQiiUdPdbMgI= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563 h1:ACpDbAxG4fa4sA83dbtYcrnlpE/y7thNIZfHxTv2ZLs= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563/go.mod h1:jP5mrOLFEYZZkl7EiCHRRIMSSHCQsYypm1OZSus//iI= -github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317142002-710d5a74a87a h1:sb0VC87+IGYF0C5GTv3seovKuszi9/DjuvMs0SaVBco= -github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260317142002-710d5a74a87a/go.mod h1:kGprqyjsz6qFNVszOQoHc24wfvCjyipNZFste/3zcbs= +github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260324111548-bcbe08e42d6f h1:YvEnhV5qHMBFCFgRcJFgW4YTbdO53lBOe+0ay2qrs1o= +github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260324111548-bcbe08e42d6f/go.mod h1:kGprqyjsz6qFNVszOQoHc24wfvCjyipNZFste/3zcbs= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a h1:pr0VFI7AWlDVJBEkcvzXWd97V8w8QMNjRdfPVa/IQLk= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a/go.mod h1:jo+cUqNcHwN8IF7SInQNXDZ8qzBsyMpnLdYbDswviFc= github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 h1:T/eCDsUI8EJT4n5zSP4w1mz4RHH+ap8qieA17QYfBhk= From d9b80dd7beff9c95f3a30ce020b1e885acc9a17c Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 15:30:43 +0200 Subject: [PATCH 05/11] Add tracingID to transaction creation --- pkg/txm/txm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/txm/txm.go b/pkg/txm/txm.go index 5b507fcb65..39279b8d14 100644 --- a/pkg/txm/txm.go +++ b/pkg/txm/txm.go @@ -177,7 +177,7 @@ func (t *Txm) HealthReport() map[string]error { func (t *Txm) CreateTransaction(ctx context.Context, txRequest *types.TxRequest) (tx *types.Transaction, err error) { tx, err = t.txStore.CreateTransaction(ctx, txRequest) if err == nil { - t.lggr.Infow("Created transaction", "tx", tx) + t.lggr.Infow("Created transaction", "txID", tx.ID, "tx", tx, "tracingID", tx.GetTracingID(t.lggr)) } return } From 32a7fdb9e3170381817fd58d6f56178babdfdd24 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 15:47:05 +0200 Subject: [PATCH 06/11] Update tracing id signature --- pkg/transmitter/dual_contract_transmitter.go | 6 +++--- pkg/transmitter/dual_contract_transmitter_test.go | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/pkg/transmitter/dual_contract_transmitter.go b/pkg/transmitter/dual_contract_transmitter.go index d199a624ac..f186c0bb55 100644 --- a/pkg/transmitter/dual_contract_transmitter.go +++ b/pkg/transmitter/dual_contract_transmitter.go @@ -123,7 +123,7 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty if txMeta == nil { txMeta = &txmgr.TxMeta{} } - tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp.ConfigDigest, reportCtx.ReportTimestamp.Epoch, reportCtx.ReportTimestamp.Round) + tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp) txMeta.TracingID = &tracingID oc.lggr.Infow("Transmitting report", "configDigest", reportCtx.ReportTimestamp.ConfigDigest, "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) @@ -254,8 +254,8 @@ func (oc *dualContractTransmitter) lockSecondary(ctx context.Context) error { return nil } -func generateTracingIDForOCR2(configDigest ocrtypes.ConfigDigest, epoch uint32, round uint8) string { - return fmt.Sprintf("0x%s:%d:%d", configDigest.Hex(), epoch, round) +func generateTracingIDForOCR2(reportTimestamp ocrtypes.ReportTimestamp) string { + return fmt.Sprintf("0x%s:%d:%d", reportTimestamp.ConfigDigest.Hex(), reportTimestamp.Epoch, reportTimestamp.Round) } func (oc *dualContractTransmitter) Start(ctx context.Context) error { diff --git a/pkg/transmitter/dual_contract_transmitter_test.go b/pkg/transmitter/dual_contract_transmitter_test.go index 0743485197..dbed72162f 100644 --- a/pkg/transmitter/dual_contract_transmitter_test.go +++ b/pkg/transmitter/dual_contract_transmitter_test.go @@ -147,6 +147,20 @@ func Test_dualContractTransmitter_Transmit_SignaturesAreTransmitted(t *testing.T require.Equal(t, transmitter.lastSecondaryPayload, withSignaturesPayloadSecondary, "secondary payload not equal") } +func Test_generateTracingIDForOCR2(t *testing.T) { + t.Parallel() + + digestBytes, err := hex.DecodeString("000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776") + require.NoError(t, err) + + reportTimestamp := types.ReportTimestamp{Epoch: 42, Round: 7} + copy(reportTimestamp.ConfigDigest[:], digestBytes) + + tracingID := generateTracingIDForOCR2(reportTimestamp) + + assert.Equal(t, "0x000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776:42:7", tracingID) +} + func createDualContractTransmitter(ctx context.Context, t *testing.T, transmitter Transmitter, ops ...OCRTransmitterOption) *dualContractTransmitter { contractABI, err := abi.JSON(strings.NewReader(ocr2aggregator.OCR2AggregatorMetaData.ABI)) require.NoError(t, err) From 4272a55ea44813927790eb9ab8fb288b301a2a8c Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 16:05:59 +0200 Subject: [PATCH 07/11] Revert meta change --- pkg/txm/types/transaction.go | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/pkg/txm/types/transaction.go b/pkg/txm/types/transaction.go index a5e718b822..ae8e21f2c2 100644 --- a/pkg/txm/types/transaction.go +++ b/pkg/txm/types/transaction.go @@ -54,23 +54,10 @@ type Transaction struct { func (t *Transaction) String() string { return fmt.Sprintf(`{txID:%d, IdempotencyKey:%v, ChainID:%v, Nonce:%s, FromAddress:%v, ToAddress:%v, Value:%v, `+ `Data:%s, SpecifiedGasLimit:%d, CreatedAt:%v, InitialBroadcastAt:%v, LastBroadcastAt:%v, State:%v, IsPurgeable:%v, AttemptCount:%d, `+ - `Meta:%s, Subject:%v}`, + `Meta:%v, Subject:%v}`, t.ID, stringOrNull(t.IdempotencyKey), t.ChainID, stringOrNull(t.Nonce), t.FromAddress, t.ToAddress, t.Value, base64.StdEncoding.EncodeToString(t.Data), t.SpecifiedGasLimit, t.CreatedAt, stringOrNull(t.InitialBroadcastAt), stringOrNull(t.LastBroadcastAt), - t.State, t.IsPurgeable, t.AttemptCount, t.metaString(), t.Subject) -} - -func (t *Transaction) metaString() string { - if t.Meta == nil { - return "null" - } - - meta, err := t.GetMeta() - if err == nil && meta != nil { - return fmt.Sprintf("%+v", *meta) - } - - return t.Meta.String() + t.State, t.IsPurgeable, t.AttemptCount, t.Meta, t.Subject) } func stringOrNull[T any](t *T) string { From 23622b1a97e0bd4ad8b55aa3fa1ddf71db150b9c Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 16:12:05 +0200 Subject: [PATCH 08/11] Update transmit log --- pkg/transmitter/dual_contract_transmitter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/transmitter/dual_contract_transmitter.go b/pkg/transmitter/dual_contract_transmitter.go index f186c0bb55..25a5ce2933 100644 --- a/pkg/transmitter/dual_contract_transmitter.go +++ b/pkg/transmitter/dual_contract_transmitter.go @@ -125,7 +125,7 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty } tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp) txMeta.TracingID = &tracingID - oc.lggr.Infow("Transmitting report", "configDigest", reportCtx.ReportTimestamp.ConfigDigest, "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) + oc.lggr.Infow("Transmitting report", "configDigest", reportCtx.ReportTimestamp.ConfigDigest.Hex(), "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) // Primary transmission payload, err := oc.contractABI.Pack("transmit", rawReportCtx, []byte(report), rs, ss, vs) From 49a6329339b7ad822ef8eee5d7234b9d55cea24d Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 16:12:10 +0200 Subject: [PATCH 09/11] Update transmit log --- pkg/transmitter/dual_contract_transmitter.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/transmitter/dual_contract_transmitter.go b/pkg/transmitter/dual_contract_transmitter.go index 25a5ce2933..5269311e57 100644 --- a/pkg/transmitter/dual_contract_transmitter.go +++ b/pkg/transmitter/dual_contract_transmitter.go @@ -125,7 +125,7 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty } tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp) txMeta.TracingID = &tracingID - oc.lggr.Infow("Transmitting report", "configDigest", reportCtx.ReportTimestamp.ConfigDigest.Hex(), "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) + oc.lggr.Infow("Transmitting report", "configDigest", "0x"+reportCtx.ReportTimestamp.ConfigDigest.Hex(), "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) // Primary transmission payload, err := oc.contractABI.Pack("transmit", rawReportCtx, []byte(report), rs, ss, vs) From 5f4997240a420eec9de98b389cc4ef077eb50d1d Mon Sep 17 00:00:00 2001 From: Dimitris Date: Tue, 24 Mar 2026 16:22:53 +0200 Subject: [PATCH 10/11] Remove test for reverted change --- pkg/txm/types/transaction_test.go | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/pkg/txm/types/transaction_test.go b/pkg/txm/types/transaction_test.go index afb0ab7ea5..2e11100702 100644 --- a/pkg/txm/types/transaction_test.go +++ b/pkg/txm/types/transaction_test.go @@ -104,19 +104,6 @@ func TestTransaction_GetMeta(t *testing.T) { require.NoError(t, err) assert.Equal(t, tt.expected, got) - str := tx.String() - if tt.meta == nil { - assert.Contains(t, str, "Meta:null") - } else { - assert.Contains(t, str, "Meta:{") - assert.NotContains(t, str, `\"`) - - // Simulate structured log JSON encoding where tx is emitted as a string field. - encoded, encErr := json.Marshal(map[string]string{"tx": str}) - require.NoError(t, encErr) - assert.Contains(t, string(encoded), "FailOnRevert") - assert.NotContains(t, string(encoded), `\\\"FailOnRevert\\\"`) - } }) } } From 9810ef8f23046956daf7e7b03fbda98bf979d8c1 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Thu, 26 Mar 2026 16:57:30 +0200 Subject: [PATCH 11/11] Rename tracingID to transactionLifecycleID --- go.mod | 2 +- go.sum | 4 ++-- pkg/transmitter/dual_contract_transmitter.go | 18 ++++++++++-------- .../dual_contract_transmitter_test.go | 4 ++-- .../dualbroadcast/meta_client.go | 2 +- pkg/txm/txm.go | 6 +++--- pkg/txm/types/transaction.go | 10 +++++----- 7 files changed, 24 insertions(+), 22 deletions(-) diff --git a/go.mod b/go.mod index ea4e195a1f..34cb1e63f2 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/smartcontractkit/chainlink-data-streams v0.1.12-0.20260227110503-42b236799872 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8ae8cf67c1 github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563 - github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260324111548-bcbe08e42d6f + github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260326122810-b657beadfb57 github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 github.com/smartcontractkit/chainlink-protos/cre/go v0.0.0-20260226130359-963f935e0396 diff --git a/go.sum b/go.sum index 5ac1e9ac5e..80cd983c73 100644 --- a/go.sum +++ b/go.sum @@ -660,8 +660,8 @@ github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8 github.com/smartcontractkit/chainlink-evm/gethwrappers v0.0.0-20251022073203-7d8ae8cf67c1/go.mod h1:oyfOm4k0uqmgZIfxk1elI/59B02shbbJQiiUdPdbMgI= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563 h1:ACpDbAxG4fa4sA83dbtYcrnlpE/y7thNIZfHxTv2ZLs= github.com/smartcontractkit/chainlink-framework/capabilities v0.0.0-20250818175541-3389ac08a563/go.mod h1:jP5mrOLFEYZZkl7EiCHRRIMSSHCQsYypm1OZSus//iI= -github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260324111548-bcbe08e42d6f h1:YvEnhV5qHMBFCFgRcJFgW4YTbdO53lBOe+0ay2qrs1o= -github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260324111548-bcbe08e42d6f/go.mod h1:kGprqyjsz6qFNVszOQoHc24wfvCjyipNZFste/3zcbs= +github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260326122810-b657beadfb57 h1:sCrr1Oy/JZstf/Oi2cRuU4mDN1BRUKfXP2CKByCMADg= +github.com/smartcontractkit/chainlink-framework/chains v0.0.0-20260326122810-b657beadfb57/go.mod h1:kGprqyjsz6qFNVszOQoHc24wfvCjyipNZFste/3zcbs= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a h1:pr0VFI7AWlDVJBEkcvzXWd97V8w8QMNjRdfPVa/IQLk= github.com/smartcontractkit/chainlink-framework/metrics v0.0.0-20251020150604-8ab84f7bad1a/go.mod h1:jo+cUqNcHwN8IF7SInQNXDZ8qzBsyMpnLdYbDswviFc= github.com/smartcontractkit/chainlink-framework/multinode v0.0.0-20251021173435-e86785845942 h1:T/eCDsUI8EJT4n5zSP4w1mz4RHH+ap8qieA17QYfBhk= diff --git a/pkg/transmitter/dual_contract_transmitter.go b/pkg/transmitter/dual_contract_transmitter.go index 5269311e57..9e68ebf29d 100644 --- a/pkg/transmitter/dual_contract_transmitter.go +++ b/pkg/transmitter/dual_contract_transmitter.go @@ -123,9 +123,11 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty if txMeta == nil { txMeta = &txmgr.TxMeta{} } - tracingID := generateTracingIDForOCR2(reportCtx.ReportTimestamp) - txMeta.TracingID = &tracingID - oc.lggr.Infow("Transmitting report", "configDigest", "0x"+reportCtx.ReportTimestamp.ConfigDigest.Hex(), "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", oc.contractAddress, "txMeta", txMeta, "tracingID", tracingID) + transactionLifecycleID := generateTransactionLifecycleIDForOCR2(reportCtx.ReportTimestamp) + txMeta.TransactionLifecycleID = &transactionLifecycleID + oc.lggr.Infow("Transmitting report", "configDigest", "0x"+reportCtx.ReportTimestamp.ConfigDigest.Hex(), "epoch", reportCtx.ReportTimestamp.Epoch, "round", reportCtx.ReportTimestamp.Round, "contractAddress", + oc.contractAddress, "txMeta", txMeta, "transactionLifecycleID", transactionLifecycleID) + // Primary transmission payload, err := oc.contractABI.Pack("transmit", rawReportCtx, []byte(report), rs, ss, vs) @@ -135,9 +137,9 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty transactionErr := errors.Wrap(oc.transmitter.CreateEthTransaction(ctx, oc.contractAddress, payload, txMeta), "failed to send primary Eth transaction") if transactionErr != nil { - oc.lggr.Errorw("Failed to create primary Eth transaction", "error", transactionErr, "tracingID", tracingID) + oc.lggr.Errorw("Failed to create primary Eth transaction", "error", transactionErr, "transactionLifecycleID", transactionLifecycleID) } else { - oc.lggr.Debugw("Created primary transaction", "tracingID", tracingID) + oc.lggr.Debugw("Created primary transaction", "transactionLifecycleID", transactionLifecycleID) } // Secondary transmission @@ -148,9 +150,9 @@ func (oc *dualContractTransmitter) Transmit(ctx context.Context, reportCtx ocrty err = errors.Wrap(oc.transmitter.CreateSecondaryEthTransaction(ctx, secondaryPayload, txMeta), "failed to send secondary Eth transaction") if err != nil { - oc.lggr.Errorw("Failed to create secondary Eth transaction", "error", err, "tracingID", tracingID) + oc.lggr.Errorw("Failed to create secondary Eth transaction", "error", err, "transactionLifecycleID", transactionLifecycleID) } else { - oc.lggr.Debugw("Created secondary transaction", "tracingID", tracingID) + oc.lggr.Debugw("Created secondary transaction", "transactionLifecycleID", transactionLifecycleID) } return stderrors.Join(transactionErr, err) } @@ -254,7 +256,7 @@ func (oc *dualContractTransmitter) lockSecondary(ctx context.Context) error { return nil } -func generateTracingIDForOCR2(reportTimestamp ocrtypes.ReportTimestamp) string { +func generateTransactionLifecycleIDForOCR2(reportTimestamp ocrtypes.ReportTimestamp) string { return fmt.Sprintf("0x%s:%d:%d", reportTimestamp.ConfigDigest.Hex(), reportTimestamp.Epoch, reportTimestamp.Round) } diff --git a/pkg/transmitter/dual_contract_transmitter_test.go b/pkg/transmitter/dual_contract_transmitter_test.go index dbed72162f..80c1b43150 100644 --- a/pkg/transmitter/dual_contract_transmitter_test.go +++ b/pkg/transmitter/dual_contract_transmitter_test.go @@ -156,9 +156,9 @@ func Test_generateTracingIDForOCR2(t *testing.T) { reportTimestamp := types.ReportTimestamp{Epoch: 42, Round: 7} copy(reportTimestamp.ConfigDigest[:], digestBytes) - tracingID := generateTracingIDForOCR2(reportTimestamp) + transactionLifecycleID := generateTransactionLifecycleIDForOCR2(reportTimestamp) - assert.Equal(t, "0x000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776:42:7", tracingID) + assert.Equal(t, "0x000130da6b9315bd59af6b0a3f5463c0d0a39e92eaa34cbcbdbace7b3bfcc776:42:7", transactionLifecycleID) } func createDualContractTransmitter(ctx context.Context, t *testing.T, transmitter Transmitter, ops ...OCRTransmitterOption) *dualContractTransmitter { diff --git a/pkg/txm/clientwrappers/dualbroadcast/meta_client.go b/pkg/txm/clientwrappers/dualbroadcast/meta_client.go index fdb2653c3c..2b7a989f8e 100644 --- a/pkg/txm/clientwrappers/dualbroadcast/meta_client.go +++ b/pkg/txm/clientwrappers/dualbroadcast/meta_client.go @@ -595,6 +595,6 @@ func (a *MetaClient) SendOperation(ctx context.Context, tx *types.Transaction, a return fmt.Errorf("failed to update signed attempt for txID: %v, err: %w", tx.ID, err) } a.lggr.Infow("Intercepted attempt for tx", "txID", tx.ID, "hash", signedTx.Hash(), "toAddress", meta.ToAddress, "gasLimit", meta.GasLimit, - "TipCap", tip, "FeeCap", meta.MaxFeePerGas, "tracingID", tx.GetTracingID(a.lggr)) + "TipCap", tip, "FeeCap", meta.MaxFeePerGas, "transactionLifecycleID", tx.GetTransactionLifecycleID(a.lggr)) return a.c.SendTransaction(ctx, signedTx) } diff --git a/pkg/txm/txm.go b/pkg/txm/txm.go index 39279b8d14..454f58ddaf 100644 --- a/pkg/txm/txm.go +++ b/pkg/txm/txm.go @@ -177,7 +177,7 @@ func (t *Txm) HealthReport() map[string]error { func (t *Txm) CreateTransaction(ctx context.Context, txRequest *types.TxRequest) (tx *types.Transaction, err error) { tx, err = t.txStore.CreateTransaction(ctx, txRequest) if err == nil { - t.lggr.Infow("Created transaction", "txID", tx.ID, "tx", tx, "tracingID", tx.GetTracingID(t.lggr)) + t.lggr.Infow("Created transaction", "txID", tx.ID, "tx", tx, "transactionLifecycleID", tx.GetTransactionLifecycleID(t.lggr)) } return } @@ -336,7 +336,7 @@ func (t *Txm) sendTransactionWithError(ctx context.Context, tx *types.Transactio } start := time.Now() txErr := t.client.SendTransaction(ctx, tx, attempt) - t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "tracingID", tx.GetTracingID(t.lggr), "duration", time.Since(start), "txErr", txErr) + t.lggr.Infow("Broadcasted attempt", "tx", tx, "attempt", attempt, "transactionLifecycleID", tx.GetTransactionLifecycleID(t.lggr), "duration", time.Since(start), "txErr", txErr) if txErr != nil && t.errorHandler != nil { if err = t.errorHandler.HandleError(ctx, tx, txErr, t.txStore, t.SetNonce, false); err != nil { return @@ -443,7 +443,7 @@ func (t *Txm) extractMetrics(ctx context.Context, txs []*types.Transaction) []ui if tx.InitialBroadcastAt != nil { t.Metrics.RecordTimeUntilTxConfirmed(ctx, float64(time.Since(*tx.InitialBroadcastAt))) } - t.lggr.Infow("Confirmed transaction", "txID", tx.ID, "tracingID", tx.GetTracingID(t.lggr)) + t.lggr.Infow("Confirmed transaction", "txID", tx.ID, "transactionLifecycleID", tx.GetTransactionLifecycleID(t.lggr)) } return confirmedTxIDs } diff --git a/pkg/txm/types/transaction.go b/pkg/txm/types/transaction.go index ae8e21f2c2..d3efe366fc 100644 --- a/pkg/txm/types/transaction.go +++ b/pkg/txm/types/transaction.go @@ -107,14 +107,14 @@ func (t *Transaction) GetMeta() (*TxMeta, error) { return &m, nil } -func (t *Transaction) GetTracingID(lggr logger.SugaredLogger) string { +func (t *Transaction) GetTransactionLifecycleID(lggr logger.SugaredLogger) string { meta, err := t.GetMeta() if err != nil { lggr.Errorw("failed to get meta of the transaction", "err", err) return "" } - if meta != nil && meta.TracingID != nil { - return *meta.TracingID + if meta != nil && meta.TransactionLifecycleID != nil { + return *meta.TransactionLifecycleID } return "" } @@ -198,8 +198,8 @@ type TxMeta struct { DualBroadcast *bool `json:"DualBroadcast,omitempty"` DualBroadcastParams *string `json:"DualBroadcastParams,omitempty"` - // TracingID is used for tracing the entire lifecycle of a transaction from OCR Transmit to confirmation on-chain. - TracingID *string `json:"TracingID,omitempty"` + // TransactionLifecycleID is used for tracing the entire lifecycle of a transaction from OCR Transmit to confirmation on-chain. + TransactionLifecycleID *string `json:"TransactionLifecycleID,omitempty"` } type QueueingTxStrategy struct {