Skip to content

Merge main into protocol-next - #252

Merged
tamirms merged 9 commits into
protocol-nextfrom
sync-main-into-protocol-next
Oct 20, 2026
Merged

tamirms merged 9 commits into
protocol-nextfrom
sync-main-into-protocol-next

Conversation

@tamirms

@tamirms tamirms commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

Syncs protocol-next with main before the Protocol 30 release. It brings in #230, #241, #246, #247, #248, #249, #250 and #251.

There was one conflict, in internal/ingest/core_lcm_test.go. #241 made filters.NewFilters take the network passphrase, and #237 moved the test code it changed into ingestCoreLCMFile, so the helper now passes coreTestNetworkPassphrase.

Please merge with a merge commit, not squash, so main's commits stay in protocol-next's history and the release merge back into main stays clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MBWddBqppX2FsWGbuaJ82g

karthikiyer56 and others added 9 commits October 9, 2026 10:55
)

* internal/ingest/filters: match asset rules on ledger entry changes

The asset ingestion filter matched rules against a fixed list of operation
body fields. Any transaction that moved a whitelisted asset through a field
outside that list was not ingested. The documented behaviour is that all
transactions related to a whitelisted asset are retained.

Cases that did not match before this change:

- path payments where the whitelisted asset appears only in the Path array
- claim and clawback of a claimable balance denominated in the asset
- liquidity pool deposit and withdraw for a pool containing the asset
- SetTrustLineFlags and AllowTrust on the asset
- RevokeSponsorship of a trustline for the asset
- Stellar Asset Contract transfers, mints, burns and clawbacks of the asset

The filter now checks two sources and keeps the transaction on the first
match: operation bodies, then ledger entry changes (trustline, offer, claimable
balance, liquidity pool and Stellar Asset Contract balance entries). Operation
bodies stay first because they are the only source for failed transactions.

The asset filter now takes the network passphrase so it can derive the
Stellar Asset Contract id for each whitelisted asset and match contract
balance entries by that id.

The admin API now validates each asset whitelist entry as a canonical
CODE:ISSUER string and stores the canonical form. A malformed entry returns
400 instead of being stored and never matching.

Operators running with an asset filter can re-run historical range
ingestion to back-fill transactions skipped by earlier versions.

Non-goals: CreateAccount and AccountMerge move only lumens and are not
matched even when native is whitelisted. Stellar Asset Contract events are
not read; contract activity is matched through the ledger entries it changes.

Closes #11, closes #112. Addresses the asset half of #35.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* internal/ingest/filters: avoid shadowed err in AllowTrust test

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* internal/ingest/filters: read asset matches from raw meta and SAC ids

- Match contract data entries on the Stellar Asset Contract id alone.
  This covers the native lumens contract and removes per-entry hashing.
- Read ledger entry changes straight from the transaction meta, V0 to V4.
  GetChanges is no longer called, so the filter has no error path and no sort.
- Store whitelist entries in canonical form when the filter loads them.
- Share one whitelist parser between the filter and the admin API.
- Add non-matching assertions for path payment, SetTrustLineFlags,
  RevokeSponsorship and claimable balance clawback.
- Add tests for the native contract, a non-canonical stored entry,
  a malformed stored entry, and every meta version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* internal/ingest/filters: compare whitelisted assets by a map key

The filter built the canonical string of every asset in every transaction
to look it up in the whitelist. That encodes the issuer to strkey and
allocates. The whitelist is now a map keyed by the 12-byte code and the
issuer key. A lookup drops from about 175 ns and 3 allocations to about
7 ns and 0 allocations.

- The key follows the canonical string rules: a 4-byte and a 12-byte code
  for the same letters give the same key.
- A stored entry that does not parse still counts toward the whitelist
  size, so the filter stays enabled, as before.
- TestAssetKeyAgreesWithCanonicalString checks that the key and the
  canonical string agree on equality.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* internal/ingest/filters: address Copilot review comments

- Return meta change groups in the documented order: the changes before
  the operations, then each operation, then the changes after.
- Test AllowTrust inside a fee-bump. The issuer comes from the inner
  transaction source, not the fee source.
- Move the shared test constants to testutils_test.go.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* internal/ingest/filters: address review comments on asset matching

Matching:
- Match InvokeHostFunction on the Stellar Asset Contract address and on a
  CreateContract from-asset preimage. A failed call has no operation meta,
  so the operation body is the only source. A native transfer between two
  G accounts changes only account entries, which are not read.
- Match the Soroban footprint of the envelope. ExtendFootprintTTL and a
  pre-protocol-23 RestoreFootprint write only TTL entries to the meta.
- Return an error on an unknown meta version instead of dropping silently.

Admin API:
- Keep a stored whitelist entry that does not parse, so a client can read
  the config, flip "enabled", and write it back. Reject only new entries.
  List every rejected entry in the response.
- Return after rendering a database error in both update handlers.

Perf:
- Match fields in place instead of collecting []xdr.Asset per entry.
  Dropping a credit/credit offer that crosses three offers goes from
  34 allocations and 1500 ns to 0 allocations and 256 ns.
- matchMeta returns on the first match and allocates nothing.
- Keep SAC ids in a set.

Docs and tests:
- CHANGELOG: claim, clawback, deposit and withdraw are kept only on
  success. Range reingest is safe only when the filter config has not
  changed since the range was first ingested.
- Table-driven claim/clawback and SAC balance tests. Shared created()
  helper. Integration test uses MustCreateClaimableBalance.
- Benchmark for the drop path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Brings in #230, #241, #246, #247, #248, #249, #250 and #251 from main.

internal/ingest/core_lcm_test.go conflicted. #241 made filters.NewFilters
take the network passphrase, and #237 moved the test code it changed into
ingestCoreLCMFile, so the helper now passes coreTestNetworkPassphrase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MBWddBqppX2FsWGbuaJ82g
@tamirms
tamirms merged commit 17dc53d into protocol-next Oct 20, 2026
16 checks passed
@tamirms
tamirms deleted the sync-main-into-protocol-next branch October 20, 2026 19:50
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