Skip to content

fix(ttx): merge appendPayload instead of overwriting TokenRequest/Transient#1914

Merged
adecaro merged 2 commits into
mainfrom
fix-ttx-append-payload
Jul 15, 2026
Merged

fix(ttx): merge appendPayload instead of overwriting TokenRequest/Transient#1914
adecaro merged 2 commits into
mainfrom
fix-ttx-append-payload

Conversation

@adecaro

@adecaro adecaro commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #1913.

Transaction.appendPayload (token/services/ttx/transaction.go) previously merged
a received Payload into the current transaction by wholesale overwrite:

func (t *Transaction) appendPayload(payload *Payload) error {
	// TODO: change this
	t.TokenRequest = payload.TokenRequest
	t.Transient = payload.Transient
	return nil
	// ~25 lines of dead, commented-out merge code
}

This is the single call site used by collectRemote (collectactions.go), invoked
once per action when CollectActionsView collects a transfer action from a remote
counterparty.

Problem

  • TokenRequest overwrite: replaced the entire token request instead of merging
    in only new content. Safe today only because the collect-actions responder
    happens to round-trip the initiator's full transaction state before replying — an
    unchecked assumption. A protocol change, a partial/stale responder reply, or
    multiple concurrent/interleaved remote actions could silently drop previously
    collected actions with no error.
  • Transient overwrite: unconditionally replaced the initiator's transient map
    with the (typically near-empty) remote one, silently discarding any transient data
    set locally. Currently latent (no in-tree writer populates Transaction.Transient
    yet) but a real bug waiting for its first caller.
  • Dead code: the commented-out block referenced payload.Request.Issues,
    TokenInfos, ValidationRecords, and t.Set(...) — none of which exist on the
    current Payload/token.Request model. It predates a request-type refactor and
    could not be reinstated as-is.
  • No test coverage for appendPayload, or for collecting more than one action.

Fix

Replaced the overwrite with an explicit, checked merge:

  • TokenRequest: if the transaction has no actions yet, adopt the incoming
    request unconditionally (today's normal first-write path, unchanged behavior).
    Otherwise, verify the incoming request's actions are a superset-extension of the
    existing ones (same type + raw bytes, same order, for every existing action) before
    adopting it. A violation now returns an explicit error instead of silently
    corrupting/losing state.
  • Transient: entries from the incoming payload are merged in only for keys not
    already present locally — existing values win, so nothing set on the initiator can
    be clobbered by a remote round-trip.
  • Deleted the stale, unusable commented-out code.
  • Added white-box unit tests (transaction_internal_test.go) covering: first-write
    adoption, superset-extension success, shorter/divergent-prefix violations (both
    error), transient existing-wins merge, transient merge into a nil map, and a
    combined appendPayload merge.
  • Documented the merge semantics in docs/services/ttx.md's Collect Actions Flow
    section.

No behavior change for existing single-action integration call sites
(integration/token/dvp, integration/token/fungible swap) — they only ever
exercise the first-write path.

Test plan

  • go test ./token/services/ttx/... — new unit tests pass
  • make unit-tests-race — no races, ttx package green
  • make checks — license/fmt/vet/staticcheck clean
  • make lint-auto-fix — 0 issues

🤖 Generated with Claude Code

@adecaro adecaro added this to the Q3/26 milestone Jul 15, 2026
@adecaro adecaro added bug Something isn't working testing hardening ttx labels Jul 15, 2026
@adecaro adecaro self-assigned this Jul 15, 2026
@adecaro adecaro force-pushed the fix-ttx-append-payload branch 4 times, most recently from 13f83f8 to 9697d0c Compare July 15, 2026 06:48
adecaro added a commit that referenced this pull request Jul 15, 2026
…velopment

Following the appendPayload fix workflow (issue #1913 -> PR #1914), document the
exact metadata expected on issues and PRs so future contributors/agents don't have
to rediscover it: assignee, labels, milestone, the Panurus project, and — for
issues only — the Issue Type field, which has no gh CLI flag and requires a
GraphQL mutation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
@adecaro adecaro force-pushed the fix-ttx-append-payload branch from 9697d0c to 62d8dbf Compare July 15, 2026 06:48
adecaro added a commit that referenced this pull request Jul 15, 2026
…velopment

Following the appendPayload fix workflow (issue #1913 -> PR #1914), document the
exact metadata expected on issues and PRs so future contributors/agents don't have
to rediscover it: assignee, labels, milestone, the Panurus project, and — for
issues only — the Issue Type field, which has no gh CLI flag and requires a
GraphQL mutation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
@adecaro adecaro force-pushed the fix-ttx-append-payload branch from 62d8dbf to ae1d9aa Compare July 15, 2026 06:49
adecaro added 2 commits July 15, 2026 11:15
Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
mergeTransient previously let the existing value silently win when a
key was present on both sides. Detect divergence instead and fail the
merge, since a differing value for the same key indicates a protocol
inconsistency rather than a benign duplicate.

Signed-off-by: Angelo De Caro <adc@zurich.ibm.com>
@adecaro adecaro force-pushed the fix-ttx-append-payload branch from ff078ed to 9d13be4 Compare July 15, 2026 08:15

@AkramBitar AkramBitar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM.

I added to comments. Please have a look.

Comment thread token/services/ttx/transaction.go
Comment thread token/services/ttx/transaction.go

@AkramBitar AkramBitar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@adecaro adecaro merged commit 96f771c into main Jul 15, 2026
95 checks passed
@adecaro adecaro deleted the fix-ttx-append-payload branch July 15, 2026 09:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working hardening testing ttx

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ttx: Transaction.appendPayload overwrites TokenRequest/Transient instead of merging

3 participants