Skip to content

tokens: AddToken/DeleteToken events are published inside the open transaction, so subscribers observe tokens that are never committed #2183

Description

@adecaro

Summary

AddToken/DeleteToken events are published synchronously inside the still-open database transaction, before the caller who owns that transaction has committed or rolled it back. Subscribers can therefore observe a token that is later rolled back and never actually persisted.

Where

  • token/services/tokens/storage.go:220-226 (AppendToken) — t.Notify(ctx, AddToken, ...) runs immediately after t.Tx.StoreToken(...), in the same call.
  • token/services/tokens/storage.go:159-163 (DeleteToken) — same pattern for DeleteToken.
  • token/services/tokens/tokens.go:97-161 (AppendValid) — never commits; it calls AppendToken/DeleteTokens on the transaction and returns. The transaction is committed by the caller (the finality listener), after AppendValid has already returned and the events have already gone out.

Impact

The in-repo subscriber is the interactive certifier (token/services/ttx/certifier/interactive/client.go:122 subscribes, :248 casts, OnReceive enqueues the token ID for asynchronous certification). If the surrounding transaction is rolled back after AppendValid returns an error further up the call stack, the certifier has already been notified about a token that will never exist in the store, producing spurious certification attempts and retries. events.Publisher is also a public extension point (any application-level subscriber sees the same premature event).

Reproduction

tx, _ := tokens.NewTransaction(pub, &tokendb.Transaction{TokenStoreTransaction: mockTx}, tmsID)
err := tx.AppendToken(ctx, tta)
require.NoError(t, err)
require.Equal(t, 1, pub.PublishCallCount(), "AddToken should have been published")

// caller now decides to roll back
require.NoError(t, tx.Rollback())

// BUG: publish call count is unchanged — the event already escaped and cannot be retracted
assert.Equal(t, 1, pub.PublishCallCount())
=== RUN   TestTransaction_AppendToken_NotifiesBeforeRollback
--- PASS: TestTransaction_AppendToken_NotifiesBeforeRollback (0.00s)
PASS

Severity

High — an event bus observing not-yet-committed, possibly-never-committed state is a correctness hazard for every current and future subscriber.

Part of #2112.

Metadata

Metadata

Assignees

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions