ingest: one ledger walk — ExtractLedgerTxParts + EventsFromTxParts/FeesFromTxParts (supersedes the extractor bundles) - #5966
Conversation
ExtractFees(lcm xdr.LedgerCloseMetaView, passphrase) returns one ledger's fee observations (LedgerFees) split into classic per-op fees and Soroban inclusion fees — the buckets stellar-rpc's getFeeStats windows consume — from one TxProcessing walk plus by-hash envelope pairing, decoding nothing beyond FeeCharged, op count/type, and the Soroban resource-fee extension. The classification replicates stellar-rpc v1's FeeWindows.IngestFees bug-for-bug (soroban gate is the op type alone; soroban-shaped txs without SorobanTransactionMetaExtV1 are skipped, not classic; fee-bump reads inner ops with the outer FeeCharged; uint64 subtraction wraps when the charged resource fee exceeds FeeCharged). A verbatim test-only port of that walk is the primary oracle; the parsed LedgerTransaction fee helpers are the secondary oracle, cross-asserted where semantics align and pinned where they deliberately diverge. Part 1 of stellar/stellar-rpc#881. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a zero-copy, view-based fee extractor matching stellar-rpc v1 fee classification.
Changes:
- Introduces
LedgerFeesandExtractFees. - Adds comprehensive synthetic, real-ledger, multiphase, and benchmark coverage.
- Extends transaction fixtures with fee-processing data.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
ingest/extract.go |
Implements fee extraction and classification. |
ingest/extract_fees_test.go |
Adds fee matrix and oracle tests. |
ingest/extract_fees_multiphase_test.go |
Tests mixed transaction-set phases. |
ingest/extract_real_ledger_test.go |
Tests real-ledger equivalence. |
ingest/extract_bench_test.go |
Benchmarks parsed and view paths. |
ingest/transaction_view_test.go |
Enhances shared transaction fixtures. |
ingest/CHANGELOG.md |
Documents the experimental API. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code-review follow-ups: - Replicate the parsed reader's pre-protocol-10 meta guard (badMetaVersionErr) so ExtractFees rejects the same outdated-stellar-core ledgers v1 does; adds xdr.LedgerCloseMetaView.ProtocolVersion and FeeProcessing on the TxProcessing projection to support it. - Hash the ENTIRE TxSet like v1's reader (no early break, hasher built on the first envelope): an invalid passphrase or malformed envelope anywhere in a non-empty TxSet errors exactly when it does on the parsed path, while an empty TxSet still skips passphrase validation. - Resolve each envelope's fee shape (op count + sole-op type) in the same pairing pass instead of computing the unused isSoroban flag and re-parsing the envelope afterwards. - Tests: reuse the production feeBucket enum, drop the duplicated wrap test, consolidate the LCM builders (shared header/TxProcessing/result helpers, explicit setLedgerVersion override), strip test-function docstrings, and add guard/passphrase parity tests. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The parsed LedgerTransaction fee helpers computed overlapping quantities via independent routes (balance deltas, the protocol inclusion-fee equation), and a block of tests cross-asserted or pinned every agreement and disagreement. Confusing in practice, and the helpers have no production callers — the contract that matters is the one the remaining tests state directly: ExtractFees returns exactly what stellar-rpc v1's IngestFees returns, verified against the verbatim ported walk on every fixture and on a real pubnet ledger (where each transaction's fee is also re-derived from the raw parsed fields). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fee stats only ever ingest ledgers at the tip (protocol 23+), so the parsed reader's stale-meta check for pre-2019 ledgers can never fire here. Removing it also removes the ProtocolVersion view helper and the FeeProcessing projection field it required — xdr/ledger_close_meta_view.go and ingest/ledger_close_meta_view_nav.go revert to untouched. The deliberate non-goal is noted in the ExtractFees godoc. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ExtractFees itself is the port of v1's walk; carrying a second copy of that walk in the test file to diff against added 100 lines without adding trust. The tests now assert v1's documented rules directly: hand-computed expected buckets for every fixture in the matrix, and a per-transaction re-derivation from the parsed fields on the real pubnet ledger. The benchmark measures the view path alone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Wiring this into rpc v2 surfaces an API-shape problem worth fixing while the extractor family is still experimental. rpc v2 has three consumers of per-ledger extraction, and each wants a different subset of the same three products. Hot ingestion wants events, txhashes, and fees. Cold backfill wants events and txhashes, and since it is the bulk path, it should never pay for fees it will not serve. The fee-window warm-up replay (stellar/stellar-rpc#888) wants fees alone. But the API sells fixed bundles rather than products: The intuition for the fix: walking the ledger and reading products off it are different jobs, and only the walk is worth sharing. Let one function own the walk and return per-transaction handles, and make each product a plain function of those handles: // The only function in the package that walks TxProcessing.
func ExtractLedgerTxParts(lcm xdr.LedgerCloseMetaView) ([]LedgerTxParts, error)
type LedgerTxParts struct {
Hash, InnerHash [32]byte // filled during the walk; everything pairs by hash
FeeBump bool
Result xdr.TransactionResultPairView // lazy handle into the buffer
Meta xdr.TransactionMetaView // lazy handle into the buffer
}
// Products. No walking, just reads off the handles.
func EventsFromTxParts(parts []LedgerTxParts) ([]TxEvents, error)
func FeesFromTxParts(parts []LedgerTxParts) (LedgerFees, error)Each consumer composes exactly what it needs, and "one walk per ledger" becomes a property of the API instead of a promise each bundle's doc comment has to make: // hot: one walk, both products
parts, err := ingest.ExtractLedgerTxParts(lcm)
events, err := ingest.EventsFromTxParts(parts)
fees, err := ingest.FeesFromTxParts(parts)
// cold: one walk, fees never computed
parts, err := ingest.ExtractLedgerTxParts(lcm)
events, err := ingest.EventsFromTxParts(parts)
// fee-only replay (stellar/stellar-rpc#888): same two calls, fees instead of events
parts, err := ingest.ExtractLedgerTxParts(lcm)
fees, err := ingest.FeesFromTxParts(parts)There is a catch: that signature is only possible if we stop copying v1 exactly. v1 answers two questions by reading the envelope: is this transaction soroban, and how many operations does it have. Envelopes live in the TxSet, and the only way to match a TxSet envelope to its result is to hash every envelope with the network passphrase. That matching is the entire reason But on any ledger the current protocol can produce, both answers are already sitting in TxProcessing. A transaction is soroban exactly when its meta carries The one real difference is In exchange, the pairing pass, the per-envelope hashing, and the passphrase leave ingestion entirely. Fees become a plain fold over the walk output like every other product, and hot and cold are both exactly one walk per ledger with nothing extra. For this PR that means the classification arithmetic and the meta reading carry over ( |
|
@tamirms - responding here to segments of your comment here Re:
this wasnt lost on me fwiw. the bigger consequence is what this does to the tickets.
Re:
ngl this is a refreshing take, and one i shouldve arrived at myself. i was treating v1's mechanism as the contract and porting its misgivings over bug-for-bug just so i could say "parity". |
…as functions Per tamirms' review on #5966 (spec: stellar/stellar-rpc#912): walking the ledger and reading products off it are different jobs, and only the walk is worth sharing. ExtractLedgerTxParts owns the single TxProcessing walk and returns per-tx handles; EventsFromTxParts and FeesFromTxParts are plain functions of those handles. The ExtractTxHashes / ExtractLedgerEvents / ExtractFees bundles delete, along with the fee path's envelope pairing, whole-TxSet hashing, and passphrase parameter. Fee classification now reads TxProcessing alone: soroban iff SorobanMeta present, opCount = per-operation result count. Matches v1's IngestFees on every ledger a correctly functioning core produces; deliberate deltas: txINTERNAL_ERROR skipped, resource fee above FeeCharged errors instead of wrapping. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Suppressed comments (1)
ingest/extract.go:234
- The signed addition can underflow back to a non-negative value: for example, two
math.MinInt64components produce0, bypass this check, and yield an inclusion fee even though the resource-fee sum is negative. Reject negative components before adding them so corrupt values cannot wrap through the validation; a regression case for this boundary would also prevent recurrence.
// int64 addition first: two huge fees wrap negative and hit the error
// below rather than summing wide.
resourceFee := nonRefundable + refundable
if resourceFee < 0 {
return 0, feeBucketNone, fmt.Errorf("ingest: tx %x: resource fee charged cannot be negative", txParts.Hash)
…Parts A sum-only negativity check can be wrapped through: two huge negative components sum back to non-negative (two MinInt64s make exactly 0) and a negative component can hide behind a larger positive one. Check each component first; with both non-negative, a negative sum can only be int64 overflow, reported as such. Surfaced by Copilot's re-review (its one suppressed comment). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
The reshape lands exactly as proposed: the classification, the error edges, and the test matrix all read right to me, and That is not quite true. Core re-runs transaction validation at apply time ( I think the behavior should stay exactly as is. Skipping these is the same principled rule as the rest of the classification (count a transaction when its record carries what the observation needs), the dropped samples say nothing a fee bidder should act on, and the soroban bucket is untouched by all of this: core writes the fee ext before validation runs ( Suggested wording for the comparison paragraph on // The output matches stellar-rpc v1's FeeWindows.IngestFees on organic
// current-protocol traffic, but the definition differs: v1 classifies from
// the ENVELOPE (a single operation of a soroban type; opCount = envelope
// operations), this classifies from TxProcessing alone as described above.
// The behavioral deltas are confined to classic transactions whose
// operations never ran, so their results carry no per-operation list: v1
// counts them from the envelope, this skips them. Only two things put such
// a transaction in a ledger: a core malfunction (txINTERNAL_ERROR), or an
// account invalidating its own pending transaction with an operation it
// signed inside another account's transaction in the same ledger (account
// merge, signer removal, sequence bump). Pre-protocol-20 tx sets also
// allowed several transactions per account, so old ledgers contain such
// failures organically; fee stats only ever ingests tip ledgers. v1 also
// lets a resource fee above FeeCharged wrap around uint64 where this
// errors.and for the // - A transaction with no per-operation result list is skipped. That
// covers txINTERNAL_ERROR (core malfunction) and transactions
// invalidated by an earlier transaction in the same ledger before their
// operations ran (possible when an account signs an operation against
// itself inside another account's transaction).The same sentence recurs in the One item from auditing the view usage, separate from the parity note. // today: three prefix walks over the same V4 meta
tev.TransactionEvents = collectRaws(metaV4View.MustEvents().MustIter())
opsView := metaV4View.MustOperations()
// ...
*diag = collectRaws(metaV4View.MustDiagnosticEvents().MustIter())
// one locate pass
f := mustView(metaV4View.Fields())
tev.TransactionEvents = collectRaws(f.Events.MustIter())
// ... f.Operations ...
*diag = collectRaws(f.DiagnosticEvents.MustIter())The V3 arm has the same shape one level down when both event sets are wanted: |
… walker Per tamirms' review: the no-per-op-results family is not just txINTERNAL_ERROR. An account can invalidate its own pending transaction via an operation it signed inside another account's transaction (account merge, signer removal, sequence bump), and pre-protocol-20 tx sets allowed several transactions per account, so old ledgers carry such never-ran failures organically. The docs now say so in plain English with a worked example; classification behavior is unchanged. Adds the no_account_skipped matrix cell (the skip is a family rule, not a txINTERNAL_ERROR special case) and the LedgerTxParts disclosure that FeeBump derives from the result code — a never-ran fee-bump reads as a plain transaction with no inner hash. metaEventRaws' V4 arm now locates all fields in one Fields() pass instead of per-field prefix walks: DiagnosticEvents is the last field, so its accessor re-walked the whole meta interior once more per transaction on the read path (~10% on a synthetic V4 walker, growing with op count; the pubnet fixture is protocol 22/all-V3 where the arm never runs). V3 uses the locate pass only when both event sets are wanted; with one set the single accessor sizes strictly less. mustFields is the local Must twin that Fields() lacks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Taken in full — thanks for the catch, and for the pre-protocol-20 history note. All the doc sites now describe the whole never-ran family instead of "txINTERNAL_ERROR only": the Behavior unchanged everywhere. The PR description, stellar/stellar-rpc#912, and stellar/stellar-rpc#881 got the same widening, and the #883 framing everywhere is now "a divergence means go look at that ledger", not "core had an incident" full stop. (fa830cc) |
|
Both optional additions taken as well:
|
Done, with measurements — and one honest wrinkle: the pubnet fixture can't show this fix at all.
|
… updates (#1185) Yes — the previous response still rendered the Markdown. You want the **literal Markdown source**, with no HTML tags at all. ```markdown Bumps the minor-and-patch group with 7 updates in the `/` directory: | Package | From | To | | --- | --- | --- | | [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.43.0` | `1.43.5` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.32.31` | `1.32.36` | | [github.com/aws/aws-sdk-go-v2/service/ses](https://github.com/aws/aws-sdk-go-v2) | `1.37.0` | `1.37.5` | | [github.com/aws/aws-sdk-go-v2/service/sns](https://github.com/aws/aws-sdk-go-v2) | `1.42.0` | `1.42.5` | | [github.com/stellar/go-stellar-sdk](https://github.com/stellar/go-stellar-sdk) | `0.6.0` | `0.7.2` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.54.0` | `0.55.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.57.0` | `0.58.0` | Updates `github.com/aws/aws-sdk-go-v2` from 1.43.0 to 1.43.5 ### Commits - [`a14f5f1`](aws/aws-sdk-go-v2@a14f5f1) Release 2026-08-10 - [`339f0b6`](aws/aws-sdk-go-v2@339f0b6) Regenerated Clients - [`0978e3d`](aws/aws-sdk-go-v2@0978e3d) Update API model - [`3cc614d`](aws/aws-sdk-go-v2@3cc614d) Fix codegen mp ([#3508](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3508)) - [`1434b18`](aws/aws-sdk-go-v2@1434b18) generate response snapshots for json ([#3507](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3507)) - [`ad58f77`](aws/aws-sdk-go-v2@ad58f77) Checkout smithy-go on PRs at the commit pointed out by SMITHY_GO_CODEGEN_VERS... - [`c002860`](aws/aws-sdk-go-v2@c002860) feat: move close-body, logger, and service-metadata work out of the middlewar... - [`f152336`](aws/aws-sdk-go-v2@f152336) Release 2026-08-07 - [`37d88d7`](aws/aws-sdk-go-v2@37d88d7) Regenerated Clients - [`c54b278`](aws/aws-sdk-go-v2@c54b278) Update endpoints model - Additional commits viewable in [compare view](aws/aws-sdk-go-v2@v1.43.0...v1.43.5) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.32.31 to 1.32.36 ### Commits - [`a14f5f1`](aws/aws-sdk-go-v2@a14f5f1) Release 2026-08-10 - [`339f0b6`](aws/aws-sdk-go-v2@339f0b6) Regenerated Clients - [`0978e3d`](aws/aws-sdk-go-v2@0978e3d) Update API model - [`3cc614d`](aws/aws-sdk-go-v2@3cc614d) Fix codegen mp ([#3508](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3508)) - [`1434b18`](aws/aws-sdk-go-v2@1434b18) generate response snapshots for json ([#3507](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3507)) - [`ad58f77`](aws/aws-sdk-go-v2@ad58f77) Checkout smithy-go on PRs at the commit pointed out by SMITHY_GO_CODEGEN_VERS... - [`c002860`](aws/aws-sdk-go-v2@c002860) feat: move close-body, logger, and service-metadata work out of the middlewar... - [`f152336`](aws/aws-sdk-go-v2@f152336) Release 2026-08-07 - [`37d88d7`](aws/aws-sdk-go-v2@37d88d7) Regenerated Clients - [`c54b278`](aws/aws-sdk-go-v2@c54b278) Update endpoints model - Additional commits viewable in [compare view](aws/aws-sdk-go-v2@config/v1.32.31...config/v1.32.36) Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.30 to 1.19.35 ### Commits - [`a14f5f1`](aws/aws-sdk-go-v2@a14f5f1) Release 2026-08-10 - [`339f0b6`](aws/aws-sdk-go-v2@339f0b6) Regenerated Clients - [`0978e3d`](aws/aws-sdk-go-v2@0978e3d) Update API model - [`3cc614d`](aws/aws-sdk-go-v2@3cc614d) Fix codegen mp ([#3508](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3508)) - [`1434b18`](aws/aws-sdk-go-v2@1434b18) generate response snapshots for json ([#3507](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3507)) - [`ad58f77`](aws/aws-sdk-go-v2@ad58f77) Checkout smithy-go on PRs at the commit pointed out by SMITHY_GO_CODEGEN_VERS... - [`c002860`](aws/aws-sdk-go-v2@c002860) feat: move close-body, logger, and service-metadata work out of the middlewar... - [`f152336`](aws/aws-sdk-go-v2@f152336) Release 2026-08-07 - [`37d88d7`](aws/aws-sdk-go-v2@37d88d7) Regenerated Clients - [`c54b278`](aws/aws-sdk-go-v2@c54b278) Update endpoints model - Additional commits viewable in [compare view](aws/aws-sdk-go-v2@credentials/v1.19.30...credentials/v1.19.35) Updates `github.com/aws/aws-sdk-go-v2/service/ses` from 1.37.0 to 1.37.5 ### Commits - [`b4784c1`](aws/aws-sdk-go-v2@b4784c1) Release 2026-07-01 - [`97c0201`](aws/aws-sdk-go-v2@97c0201) Regenerated Clients - [`6687238`](aws/aws-sdk-go-v2@6687238) Update endpoints model - [`995297f`](aws/aws-sdk-go-v2@995297f) Update API model - [`c26cfc6`](aws/aws-sdk-go-v2@c26cfc6) Fix bump smithy-go to cover multiple issues ([#3461](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3461)) - [`fae66b8`](aws/aws-sdk-go-v2@fae66b8) Set transfer manager error as the first error seen, preventing race condition... - [`cf0eabd`](aws/aws-sdk-go-v2@cf0eabd) Release 2026-06-30 - [`ad0c091`](aws/aws-sdk-go-v2@ad0c091) Regenerated Clients - [`196e961`](aws/aws-sdk-go-v2@196e961) Update endpoints model - [`1529ead`](aws/aws-sdk-go-v2@1529ead) Update API model - Additional commits viewable in [compare view](aws/aws-sdk-go-v2@v1.37.0...service/pi/v1.37.5) Updates `github.com/aws/aws-sdk-go-v2/service/sns` from 1.42.0 to 1.42.5 ### Commits - [`dcbed91`](aws/aws-sdk-go-v2@dcbed91) Release 2026-01-09 - [`08120e8`](aws/aws-sdk-go-v2@08120e8) Regenerated Clients - [`1d7a925`](aws/aws-sdk-go-v2@1d7a925) Update endpoints model - [`482067d`](aws/aws-sdk-go-v2@482067d) Update API model - [`4662404`](aws/aws-sdk-go-v2@4662404) remove example ([#3282](https://redirect.github.com/aws/aws-sdk-go-v2/issues/3282)) - [`c28a6f4`](aws/aws-sdk-go-v2@c28a6f4) Release 2026-01-07 - [`2fa7a72`](aws/aws-sdk-go-v2@2fa7a72) Regenerated Clients - [`077cbaa`](aws/aws-sdk-go-v2@077cbaa) Update endpoints model - [`3282dbc`](aws/aws-sdk-go-v2@3282dbc) Update API model - [`3daa74a`](aws/aws-sdk-go-v2@3daa74a) Release 2026-01-06 - Additional commits viewable in [compare view](aws/aws-sdk-go-v2@v1.42.0...service/amp/v1.42.5) Updates `github.com/stellar/go-stellar-sdk` from 0.6.0 to 0.7.2 ### Release Notes Source: [github.com/stellar/go-stellar-sdk releases](https://github.com/stellar/go-stellar-sdk/releases) #### v0.7.2 ##### What's Changed - xdr: compare assets by their XDR encoding, and use Equals for asset equality by [@karthikiyer56](https://github.com/karthikiyer56) in [stellar/go-stellar-sdk#5974](https://redirect.github.com/stellar/go-stellar-sdk/pull/5974) - txnbuild: consolidate liquidity pool ordering guards; xdr and ingest cleanups by [@karthikiyer56](https://github.com/karthikiyer56) in [stellar/go-stellar-sdk#5978](https://redirect.github.com/stellar/go-stellar-sdk/pull/5978) - strkey: enforce SEP-23 payload lengths in Decode and DecodeAny by [@karthikiyer56](https://github.com/karthikiyer56) in [stellar/go-stellar-sdk#5977](https://redirect.github.com/stellar/go-stellar-sdk/pull/5977) **Full Changelog:** [v0.7.1...v0.7.2](stellar/go-stellar-sdk@v0.7.1...v0.7.2) #### v0.7.1 ##### What's Changed - stellartoml: validate domain in GetStellarToml for parity with sibling by [@karthikiyer56](https://github.com/karthikiyer56) in [stellar/go-stellar-sdk#5970](https://redirect.github.com/stellar/go-stellar-sdk/pull/5970) - ingest: one ledger walk — ExtractLedgerTxParts + EventsFromTxParts/FeesFromTxParts (supersedes the extractor bundles) by [@karthikiyer56](https://github.com/karthikiyer56) in [stellar/go-stellar-sdk#5966](https://redirect.github.com/stellar/go-stellar-sdk/pull/5966) **Full Changelog:** [v0.7.0...v0.7.1](stellar/go-stellar-sdk@v0.7.0...v0.7.1) #### v0.7.0 ##### What's Changed - ingest,network,xdr: zero-copy XDR view extractors for full-history ingestion by [@chowbao](https://github.com/chowbao) in [stellar/go-stellar-sdk#5949](https://redirect.github.com/stellar/go-stellar-sdk/pull/5949) - xdr,xdrgen,ingest: extend xdr views (decoded discriminants, typed opaque, count validation, Fields) and rebuild the view extractors on them by [@tamirms](https://github.com/tamirms) in [stellar/go-stellar-sdk#5951](https://redirect.github.com/stellar/go-stellar-sdk/pull/5951) - protocols/rpc: add UseUpgradedAuth flag to SimulateTransactionRequest by [@Ryang-21](https://github.com/Ryang-21) in [stellar/go-stellar-sdk#5948](https://redirect.github.com/stellar/go-stellar-sdk/pull/5948) - protocols/rpc: Add LedgerCloseTime to GetHealthResponse by [@felixl256](https://github.com/felixl256) in [stellar/go-stellar-sdk#5958](https://redirect.github.com/stellar/go-stellar-sdk/pull/5958) - ingest/loadtest: expand loadtest functionality to handle multiple ledger bundles by [@cjonas9](https://github.com/cjonas9) in [stellar/go-stellar-sdk#5959](https://redirect.github.com/stellar/go-stellar-sdk/pull/5959) - ingest: expose fee-bump inner hashes on the view extractors by [@tamirms](https://github.com/tamirms) in [stellar/go-stellar-sdk#5964](https://redirect.github.com/stellar/go-stellar-sdk/pull/5964) - ingest/ledgerbackend: Update captive-core-pubnet.cfg: swap SP with Obsrvr by [@drebelsky](https://github.com/drebelsky) in [stellar/go-stellar-sdk#5963](https://redirect.github.com/stellar/go-stellar-sdk/pull/5963) - Protocol 28 (CAP-0085) by [@sisuresh](https://github.com/sisuresh) in [stellar/go-stellar-sdk#5965](https://redirect.github.com/stellar/go-stellar-sdk/pull/5965) ##### New Contributors - [@Ryang-21](https://github.com/Ryang-21) made their first contribution in [stellar/go-stellar-sdk#5948](https://redirect.github.com/stellar/go-stellar-sdk/pull/5948) - [@felixl256](https://github.com/felixl256) made their first contribution in [stellar/go-stellar-sdk#5958](https://redirect.github.com/stellar/go-stellar-sdk/pull/5958) **Full Changelog:** [v0.6.0...v0.7.0](stellar/go-stellar-sdk@v0.6.0...v0.7.0) #### v0.6.1 ##### What's Changed - Backports for Horizon 27.0.1 (release-0.6.1) by [@karthikiyer56](https://github.com/karthikiyer56) in [stellar/go-stellar-sdk#5979](https://redirect.github.com/stellar/go-stellar-sdk/pull/5979) **Full Changelog:** [v0.6.0...v0.6.1](stellar/go-stellar-sdk@v0.6.0...v0.6.1) ### Changelog Source: [github.com/stellar/go-stellar-sdk changelog](https://github.com/stellar/go-stellar-sdk/blob/main/CHANGELOG.md) # Changelog This repository adheres to [Go module Versioning](https://go.dev/doc/modules/version-numbers). This monorepo contains a number of SDKs: - `horizonclient` ([changelog](https://github.com/stellar/go-stellar-sdk/blob/main/clients/horizonclient/CHANGELOG.md)) - `txnbuild` ([changelog](https://github.com/stellar/go-stellar-sdk/blob/main/txnbuild/CHANGELOG.md)) - `rpcclient` ([changelog](https://github.com/stellar/go-stellar-sdk/blob/main/clients/rpcclient/CHANGELOG.md)) - `corelient` ([changelog](https://github.com/stellar/go-stellar-sdk/blob/main/clients/stellarcore/CHANGELOG.md)) Official project releases may be found here: [https://github.com/stellar/go-stellar-sdk/releases](https://github.com/stellar/go-stellar-sdk/releases) ## Pending ### New Features - protocols/rpc: Add `LatestLedgerCloseTime` and `OldestLedgerCloseTime` to `GetHealthResponse`, exposing the latest and oldest ledgers' close times (unix seconds) on the `getHealth` response ([#5958](https://redirect.github.com/stellar/go-stellar-sdk/pull/5958)) ### Breaking Changes - strkey: `Decode` and `DecodeAny` now validate the payload length against the version byte per [SEP-23](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0023.md). Fixed-length keys (account ID, seed, muxed account, contract, liquidity pool, claimable balance, hashTx, hashX) must decode to their exact canonical size, and signed payloads must carry a declared payload length of 1–64 bytes matched by their zero padding. Inputs with a valid checksum but a wrong-length payload — previously accepted by `Decode`, `DecodeAny`, and every `IsValid*` helper — are now rejected ([#5977](https://redirect.github.com/stellar/go-stellar-sdk/pull/5977)). - `NewSignedPayload` rejects empty payloads, matching CAP-40 (the protocol fails such signers with `SET_OPTIONS_BAD_SIGNER`/`txMALFORMED`) and keeping `SignedPayload.Encode` output decodable. - Length re-checks that `Decode` now subsumes were removed from `xdr.AccountId.SetAddress`, `xdr.MuxedAccount.SetAddress`, `xdr.SignerKey.SetAddress`, `xdr.ClaimableBalanceId.DecodeFromStrkey`, `strkey.DecodeMuxedAccount`, `strkey.MuxedAccount.SetAccountID`, and txnbuild contract-address parsing. For wrong-length inputs, the xdr and txnbuild callers now return strkey's `invalid payload length` error instead of their own; the strkey muxed-account helpers keep their generic `invalid muxed account` / `invalid ed25519 public key` errors; `xdr.MuxedAccount.SetAddress` is unchanged (it rejects on encoded string length before decoding). - `keypair.ParseAddress` wraps every decode failure with `ErrInvalidKey`, so `errors.Is(err, ErrInvalidKey)` keeps matching wrong-length keys (and now also matches checksum/encoding failures, which previously returned the bare strkey error). - `DecodeSignedPayload` delegates structure validation to `Decode`; structurally invalid inputs now uniformly error with `invalid signed payload` (previously `signed payload too short: ...` or `invalid signed payload padding`). - xdr: `Asset.LessThan` now orders assets the way the protocol does — by the raw 32-byte issuer key — instead of by base32 strkey text, and `xdr.NewPoolId` requires strictly `a < b`, rejecting reversed and identical pairs ([#5974](https://redirect.github.com/stellar/go-stellar-sdk/pull/5974)) - txnbuild: liquidity pool operations reject asset pairs that are not strictly ordered; see the [txnbuild changelog](https://github.com/stellar/go-stellar-sdk/blob/main/txnbuild/CHANGELOG.md) ([#5974](https://redirect.github.com/stellar/go-stellar-sdk/pull/5974)) ### Bug Fixes - processors/token_transfer: trustline revocation now compares liquidity pool assets by value instead of pointer identity, fixing wrong-leg selection when burning pool shares ([#5974](https://redirect.github.com/stellar/go-stellar-sdk/pull/5974)) ## [0.7.0] ### New Features - xdr: Protocol 28 support (CAP-0083, CAP-0085). XDR regenerated from [stellar-xdr@9c9c1459](stellar/stellar-xdr@9c9c145), the commit stellar-core 28.0.0 pins; both CAPs are ungated upstream so `XDR_FEATURES` is now empty. ### Commits - [`b46a463`](stellar/go-stellar-sdk@b46a463) strkey: enforce SEP-23 payload lengths in Decode and DecodeAny ([#5977](https://redirect.github.com/stellar/go-stellar-sdk/issues/5977)) - [`f8cd5df`](stellar/go-stellar-sdk@f8cd5df) txnbuild: consolidate liquidity pool ordering guards; xdr and ingest cleanups... - [`d2f530f`](stellar/go-stellar-sdk@d2f530f) xdr: compare assets by their XDR encoding, and use Equals for asset equality ... - [`3114a80`](stellar/go-stellar-sdk@3114a80) ingest: one ledger walk — ExtractLedgerTxParts + EventsFromTxParts/FeesFromTx... - [`3c3872f`](stellar/go-stellar-sdk@3c3872f) stellartoml: validate domain in GetStellarToml for parity with sibling ([#5970](https://redirect.github.com/stellar/go-stellar-sdk/issues/5970)) - [`2b16db0`](stellar/go-stellar-sdk@2b16db0) Protocol 28 Support ([#5969](https://redirect.github.com/stellar/go-stellar-sdk/issues/5969)) - [`e5d0cb9`](stellar/go-stellar-sdk@e5d0cb9) Merge main into protocol-next ahead of the Protocol 28 GA merge - [`149b994`](stellar/go-stellar-sdk@149b994) Finalize Protocol 28: pin stellar-xdr @ `9c9c1459` ([#5968](https://redirect.github.com/stellar/go-stellar-sdk/issues/5968)) - [`82df764`](stellar/go-stellar-sdk@82df764) Protocol 28 (CAP-0085) XDR regeneration ([#5965](https://redirect.github.com/stellar/go-stellar-sdk/issues/5965)) - [`8dd9cad`](stellar/go-stellar-sdk@8dd9cad) Update captive-core-pubnet.cfg: swap SP with Obsrvr ([#5963](https://redirect.github.com/stellar/go-stellar-sdk/issues/5963)) - Additional commits viewable in [compare view](stellar/go-stellar-sdk@v0.6.0...v0.7.2) Updates `golang.org/x/crypto` from 0.54.0 to 0.55.0 ### Commits - [`f44d03d`](golang/crypto@f44d03d) go.mod: update golang.org/x dependencies - [`5ed4944`](golang/crypto@5ed4944) crypto/internal/poly1305: provide optimised assembly for riscv64 - [`b07833c`](golang/crypto@b07833c) ssh: return window credit for discarded extended data - [`d701c51`](golang/crypto@d701c51) acme: fix nil pointer dereference in pebble test error reporting - [`999d053`](golang/crypto@999d053) ssh: fix parsing of GSSAPI payloads offering multiple mechanisms - [`90f76b8`](golang/crypto@90f76b8) ssh: reject certificate signature keys before recursing - [`b53964a`](golang/crypto@b53964a) ssh: permit empty but non-nil HostKeyAlgorithms, KeyExchanges, Ciphers, MACs - [`626e40f`](golang/crypto@626e40f) ssh: drain stderr on forwarded TCP and Unix channels - [`31914c6`](golang/crypto@31914c6) x509roots/fallback: update bundle - [`f2135b8`](golang/crypto@f2135b8) all: clean up minor issues found by staticcheck - Additional commits viewable in [compare view](golang/crypto@v0.54.0...v0.55.0) Updates `golang.org/x/net` from 0.57.0 to 0.58.0 ### Commits - [`acc78e0`](golang/net@acc78e0) go.mod: update golang.org/x dependencies - [`90d10f0`](golang/net@90d10f0) internal/http3: delete invalid Content-Length if declared in server handler - [`08abf4d`](golang/net@08abf4d) internal/http3: infer headers when Content-Encoding is set but is empty - [`8d10596`](golang/net@8d10596) http2: avoid deadlocks in wrapped ClientConn state callback - [`99c3b0a`](golang/net@99c3b0a) http2/hpack: build the table lookup maps lazily, only for encoders - [`5a920b1`](golang/net@5a920b1) http3: rework registration to allow using a fake network - [`7fd2842`](golang/net@7fd2842) quic: return an error from Accept after PacketConn reader exits - [`825111d`](golang/net@825111d) quic: avoid busy-loop when keep-alive is blocked by congestion control - [`a02ddfa`](golang/net@a02ddfa) http/httpproxy: prioritize lowercase proxy environment variables - [`574e5eb`](golang/net@574e5eb) quic: halt conn goroutines on close when listener exits early - Additional commits viewable in [compare view](golang/net@v0.57.0...v0.58.0) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. --- You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove all of the ignore conditions of the specified dependency ```
* changelog: cut the Pending section as 0.7.2, and record 0.7.1 v0.7.2 was tagged at b46a463 so that stellar-rpc, Horizon and Galexie v28.0.0 all pin a released SDK tag for Protocol 28; Horizon had been pinning a pseudo-version to get #5974. Everything the Pending section listed ships in it, with two corrections: * the `protocols/rpc` GetHealthResponse close-time fields (#5958) moved to [0.7.0] — that commit is an ancestor of the v0.7.0 tag, so it has been released since 2026-08-03. * added a [0.7.1] section for #5966 and #5970, which were tagged on 2026-08-04 without a changelog entry, so 0.7.2 does not absorb them. The ingest/ and txnbuild/ sub-changelogs still carry Pending sections whose contents span several released versions; untangling those is left alone here. * ingest: ApplyLedgerMetadata closes the datastore and ledger backend and propagates the PrepareRange error, so an early return no longer strands a goroutine per worker and a failed prepare no longer passes silently. * go.mod: the go-xdr bump to dc590f1, which fixes decoder bound handling for a variable-length field whose length prefix ends the input. 0.6.1 recorded this bump under Updates; 0.7.2 carries the same one. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
TL;DR
Reshape of the experimental view-extractor family per tamirms' review, spec'd in stellar/stellar-rpc#912: one function walks the ledger; every product is a plain function of the walk's output.
ExtractTxHashes— hashes, one walk•
ExtractLedgerEvents— hashes + events, one walk•
ExtractFees(lcm, passphrase)— fees, ANOTHER walk + whole-TxSet SHA-256 pairing passExtractLedgerTxParts(lcmView)— the ONLY TxProcessing walk, returns per-tx handles•
EventsFromTxParts(txParts)— events product, no walking•
FeesFromTxParts(txParts)— fees product, no walking, no passphrase• rpc v2's hot path walks twice to get all three products
• each consumer composes exactly the subset it needs
Why the old functions are DELETED, not kept alongside
ExtractTxHashestxParts[i].Hashoff the walkExtractLedgerEvents+LedgerTransactionEventsEventsFromTxParts+TxEvents(hash lives ontxParts)ExtractFees(lcm, passphrase)+LedgerFees.LedgerSequence/CloseTimeFeesFromTxParts(no passphrase; seq/close-time are the caller's)Experimental:, still under CHANGELOG Pending. Deleting now is free; one release later it's a deprecation cycle + major-version break.ExtractFeeswould keep a SECOND fee definition (envelope gate, uint64 wrap) — same ledger, different buckets, permanent "which one is right?" trap.The API
• one walk, all three products
• fees never computed
• events never computed, no passphrase to plumb
Fee classification — TxProcessing only
The envelope leaves fee ingestion entirely: soroban-ness and op count are read from the result + meta, so the TxSet pairing pass, the per-envelope hashing, and the passphrase parameter all delete.
SorobanMeta,Ext.V == 1FeeCharged − (TotalNonRefundable + TotalRefundable resource fee charged)→ Soroban bucketSorobanMetapresent, ext absentSorobanMetaabsentFeeCharged / opCount→ classic bucket• opCount = number of per-op results (outer result for
txSUCCESS/txFAILED, INNER result pair's for fee-bumps)txINTERNAL_ERROR) or emptyFeeChargedfrom the OUTER result, opCount from the INNER resultFeeCharged/ negative resource-fee component (or int64-overflowing sum) / resource fee >FeeChargedIngestFeestxINTERNAL_ERROR), or an account invalidating its own pending tx with an operation it signed inside another account's tx (merge / signer removal / sequence bump — the godoc has the worked example); pre-P20 tx sets also produced these organically• the dropped fee says nothing about what a fee bidder should pay
• a stellar-rpc#883 getFeeStats divergence therefore means "go look at that ledger", not automatically "core had an incident"
FeeChargedis an error (v1 wrapped the uint64)Parity contract: identical output to v1 on organic current-protocol traffic — deltas confined to never-ran transactions — verified empirically on the recorded pubnet ledger (envelope-derived and results/meta-derived definitions agree per-tx on op count, soroban gate, and ext presence across all 249 txs, incl. 41 failed and 113 fee-bumps).
How it's verified
extract_fees_test.go)• results-derived op counts: failed multi-op, fee-bump inner counts,
results_count_is_authoritative(result count ≠ envelope count)• the no-op-list family skipped:
txINTERNAL_ERROR, self-invalidatedtxNO_ACCOUNT, fee-bump-inner-internal-error• soroban gate flips pinned: SorobanMeta present w/ classic op → soroban; soroban op w/o SorobanMeta → classic
• error cells: negative fees, int64 overflow, resource fee > FeeCharged
TestFeesFromTxParts_TxSetNotConsultedextract_real_ledger_test.go)58752000(249 txs)• parts: hash/inner-hash/fee-bump/result-bytes/meta-bytes vs the parsed reader
• events: wire-identical vs
GetTransactionEvents• fees: re-derived with the OLD envelope-based oracle — deliberately kept as the standing cross-definition check
metaEventRaws)Fields()locate pass instead of per-field prefix walks (DiagnosticEventsis the LAST field — its accessor re-walked the whole meta per tx on the read path)• ~10% on a synthetic V4 walker (10 ops × 3 events), growing with op count; the pubnet fixture is protocol 22 / all-V3, where measurements show parity
• V3 arm uses the locate pass only when both event sets are wanted
• walk + events: 0.42 ms / 137 allocs
• walk + fees: 0.42 ms / 34 allocs (old
ExtractFeesbundle: 0.58 ms / 67 allocs)• walk + all products: 0.58 ms / 154 allocs — the hot composition now costs what the fee bundle alone used to
• parsed path: ~5.7 ms / ~95k allocs