Skip to content

Add TransactionLifecycleID#396

Open
dimriou wants to merge 12 commits intodevelopfrom
transaction_tracing
Open

Add TransactionLifecycleID#396
dimriou wants to merge 12 commits intodevelopfrom
transaction_tracing

Conversation

@dimriou
Copy link
Contributor

@dimriou dimriou commented Mar 23, 2026

This PR adds TransactionLifecycleID to txmv2 and enables it both for v1 and v2 for the dual broadcast relayer.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

✅ API Diff Results - github.com/smartcontractkit/chainlink-evm

✅ Compatible Changes (2)

pkg/txm/types.(*Transaction) (1)
  • GetTransactionLifecycleID — ➕ Added
pkg/txm/types.TxMeta (1)
  • TransactionLifecycleID — ➕ Added

📄 View full apidiff report

@dimriou dimriou force-pushed the transaction_tracing branch from c890fb8 to 16775bb Compare March 23, 2026 17:26
return nil
}

func generateTracingIDForOCR2(configDigest ocrtypes.ConfigDigest, epoch uint32, round uint8) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could make sense to add a quick test for this function, just to be 100% sure

if meta != nil && meta.TracingID != nil {
return *meta.TracingID
}
return ""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe unknown is clearer? It will differentiate it from the error case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think unknown is consistent. I'd rather have it empty, or probably not log the field at all if it's nil in a future PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, I do think it would be nice to differentiate between the unknown/unset situation and the error situation though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, but we shouldn't relay an error in an unrelated log field just because the underlying method happened to fail. That was the intention of the error log above. Each time you'll make the call you'll see the empty field but you'll also see the failed to get meta of the transaction message. If you feel there is a better way to handle this, happy to discuss.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not completely following this. Let's say we grep the logs for broadcasted transactions by querying e.g. <logs> |= "Broadcasted attempt". We will then see a number of broadcasts with their tracing ID attached. Then we can do a new search with <logs> | tracingID="<tracingID>".

However, if tracing ID is empty, then we don't know what happened: is it actually not set or were we unable to retrieve Meta? We won't see the failed to get meta of the transaction log line because we've already scoped the query to Broadcasted attempt.

Anyway, I'm fine leaving it like this since there's a very low chance GetMeta() returns an error, this would only happen on malformed json.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if you filter specifically with <logs> |= "Broadcasted attempt" you won't see it, but if you filter for the TXM logs, which is usually what we do, you'll be able to see a bunch of errors explaining the issue. If you're expecting a tracingID value and it's not there it's somewhat easy to infer what happened. Overall, I see the point, it's just I don't want to pollute the implementation with a custom handling for such an edge case, while we can retrieve the information by another log

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also this might be eventually split up to individual fields i.e. configDigest, epoch, round

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. Ok let's leave it like this

return &m, nil
}

func (t *Transaction) GetTracingID(lggr logger.SugaredLogger) string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunate that we have to pass the logger but I also don't see any better option

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I struggled to find a good way to do this, but v1's GetMeta already does that so I just went with it.

Comment on lines +191 to +194
select {
case triggerCh <- struct{}{}:
default:
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't fully oversee the consequences of this change, can you explain this a bit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this is the default way in go to not block the caller and just fill the channel and exit. Before we would (mistakenly) block after the buffer was filled. The old way may impact throughput on some rare cases, hence the change.

@dimriou dimriou force-pushed the transaction_tracing branch from 912d9bf to d9b80dd Compare March 24, 2026 13:33
@dimriou dimriou marked this pull request as ready for review March 24, 2026 14:01
Copilot AI review requested due to automatic review settings March 24, 2026 14:01
@dimriou dimriou requested review from a team as code owners March 24, 2026 14:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a transaction tracing ID to support end-to-end correlation from OCR transmit through TXM lifecycle and dual-broadcast relaying.

Changes:

  • Introduces TracingID in TX meta and helpers to extract/log it across TXM components.
  • Updates TX string formatting/tests to avoid double-escaped meta fields in structured logs.
  • Generates and attaches an OCR2-derived tracing ID in the dual contract transmitter; bumps chainlink-framework/chains dependency.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/txm/types/transaction.go Adds TracingID to TxMeta, improves Transaction.String(), and introduces GetTracingID.
pkg/txm/types/transaction_test.go Extends tests to validate string/meta formatting and JSON-encoding behavior in logs.
pkg/txm/txm.go Logs tracing IDs on create/broadcast/confirm; makes trigger signaling non-blocking.
pkg/txm/clientwrappers/dualbroadcast/meta_client.go Includes tracing ID in dual-broadcast interception log.
pkg/transmitter/dual_contract_transmitter.go Generates OCR2 tracing IDs, attaches them to tx meta, and logs transmit/create events.
pkg/transmitter/dual_contract_transmitter_test.go Adds unit test for OCR2 tracing ID generation.
go.mod Bumps chainlink-framework/chains version.
go.sum Updates checksums for the bumped dependency.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

}

func generateTracingIDForOCR2(reportTimestamp ocrtypes.ReportTimestamp) string {
return fmt.Sprintf("0x%s:%d:%d", reportTimestamp.ConfigDigest.Hex(), reportTimestamp.Epoch, reportTimestamp.Round)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the config digest captures the aggregator address right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not directly, but it's encapsulated.

augustbleeds
augustbleeds previously approved these changes Mar 24, 2026
Copy link
Contributor

@augustbleeds augustbleeds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what's the main debugging path you'll use for this -- figuring out what happened to transmissions where primary succeeded but secondary failed?

@dimriou
Copy link
Contributor Author

dimriou commented Mar 24, 2026

what's the main debugging path you'll use for this -- figuring out what happened to transmissions where primary succeeded but secondary failed?

Two things:

  • being able to quickly debug logs for a given transmission end to end (both primary & secondary).
  • understanding the parts of the code that are considered "checkpoints" for transmission and what information we can expose so we can create metrics/fire alerts in a following PR.

@dimriou dimriou changed the title Add tracing ID Add TransactionLifecycleID Mar 26, 2026
@dimriou dimriou requested a review from jmank88 March 26, 2026 16:18
@dimriou dimriou requested a review from augustbleeds March 26, 2026 17:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants