Read the paired FinalizeSettle with its own parser in BeginSettle - #123
Open
mayanksharma-eth wants to merge 1 commit into
Open
Read the paired FinalizeSettle with its own parser in BeginSettle#123mayanksharma-eth wants to merge 1 commit into
mayanksharma-eth wants to merge 1 commit into
Conversation
BeginSettle re-derived the finalize's account layout by hand, walking the introspected metas from `FINALIZE_FIXED_ACCOUNTS + 1` in steps of two, while the builder wrote that same layout as a meta literal and the parser read it back as a slice pattern. Three statements of one fact, none of them linked, so a fixed account added or removed on one side had to be caught by review. Missing the introspection copy is silent: BeginSettle would start reading source buffers where it expects destinations, and validate each order's push against the wrong account. Collect the introspected account addresses into a slice and hand them to `FinalizeSettleInput::parse`, the parser the finalize already applies to itself. `push_destinations`, `finalize_pushes` and the now-unused `finalize_push_amounts` go away with it. Tie the remaining counts to the wire format rather than restating it: both settle builders emit their fixed metas from a `fixed_accounts` helper returning `[AccountMeta; N]`, so a fixed account added without updating N is now a compile error instead of a review item. This closes a blind spot three tests documented: BeginSettle now rejects a finalize whose accounts and data disagree, before pulling any funds, rather than leaving it to the finalize. The transaction failed either way, so the on-chain outcome is unchanged; the failing instruction index moves from 1 to 0, which those tests assert on. Costs 3-4% more compute on the settle path (at most +870 CU on a 23.5k CU settlement) for the collected address slice; bench-report.json updated. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
FinalizeSettle's account layout — "three fixed accounts, then[source_buffer, destination]per push" — was stated in three unlinked places:From<FinalizeSettle> for Instruction)FinalizeSettleInput::parse_bodypush_destinations:Change the fixed accounts on one side and the others still compile. Missing the third copy is the dangerous one and it fails silently:
BeginSettlewould start reading source buffers where it expects destinations, so the check that each order is paid to its ownbuy_token_accountwould compare the wrong account.This is the change anyone adding an account to the settle instructions has to get right — a solver signer for solver authentication, most obviously.
The change
BeginSettlenow reads its counterpart throughFinalizeSettleInput::parse, the same parser the finalize applies to itself. The parser is already generic over the account type; the only reason the program couldn't use it is that introspection hands out metas one index at a time rather than as a slice, sointrospected_accountscollects the addresses and bridges that gap.Deleted:
push_destinations,finalize_pushes, andfinalize_push_amounts(its only non-test caller wasfinalize_pushes).Both settle builders now emit their fixed metas from a
fixed_accountshelper returning[AccountMeta; N], so the count is load-bearing — adding a fixed account without updatingNis a compile error rather than something review has to catch.FINALIZE_FIXED_ACCOUNTSis crate-private now; nothing outside the interface needs it.Behaviour change
BeginSettlenow rejects a finalize whose accounts and data disagree, before pulling any funds, instead of leaving it to the finalize.The transaction failed either way, so the on-chain outcome is unchanged — but the failing instruction index moves from 1 to 0. Three tests asserted on that index, and their comments described the old blind spot explicitly:
It can see it now. Those tests and comments are updated; no test was weakened to accommodate the change.
Cost
3–4% more compute on the settle path, for the collected address slice:
settles_a_single_ordersettles_multiple_orderspushes_a_single_orderfinalizes_with_no_pushesWorst case +870 CU on a 23.5k CU settlement.
bench-report.jsonis regenerated.If that trade isn't wanted, the cheaper fallback keeps most of the benefit: leave the two read paths separate but derive both from the single
fixed_accountsarray, so the count still can't drift even though the offsets stay duplicated. Happy to switch if you'd prefer that.Testing
just test— 298 tests, all passingjust lint(clippy--deny=warnings),just fmt-check,just doc-dev— cleanjust bench— regenerated, drift check passesThe property test guarding the two routes into the parser is kept and retargeted:
introspected_parse_matches_account_parsenow asserts that parsing the introspected metas and parsing the runtimeAccountViews recover the builder's pushes identically. On the interface side,parser_recovers_builder_pushesreplaces the amounts-only cross-check with a full round-trip over all four push fields (source buffer, destination, bump, amount), since the metas and the instruction data are written by separate paths in the builder.Context: this came out of reading the settle path while scoping solver authentication, which
DESIGN.mdspecifies but the program doesn't implement yet —BeginSettle/FinalizeSettlecurrently take no signer. That's a separate PR; this one is just the layout dedup that it depends on. Happy to open an issue for the rest of what I noticed if that's useful.