Skip to content

perf(svm): reuse constant V5 transfer delegates - #1573

Open
Reinis-FRP wants to merge 4 commits into
reinis/svm-cu-benchmarksfrom
reinis/svm-constant-transfer-delegates
Open

Reinis-FRP wants to merge 4 commits into
reinis/svm-cu-benchmarksfrom
reinis/svm-constant-transfer-delegates

Conversation

@Reinis-FRP

@Reinis-FRP Reinis-FRP commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

V5 deposits and fills search for fixed transfer-delegate PDAs on every transfer. Select each delegate's constant address, seed and bump as one internal enum variant, retain the supplied-authority check, and sign the SPL CPI without repeating the search. Keep the V5-specific helper and its mapping/wrong-authority tests together in v5/transfer.rs.

Restacked onto the current #1568. This now optimizes every fill, including self-transfers; it preserves #1564's shared SPL delegate validation and does not restore the old InPlace branch. Public IDLs and account ordering are unchanged. Fixes #1569.

Measured execution CU medians against the same updated base:

Flow Before After Delta
v5-deposit 73,516 67,469 -6,047
v5-external-fill 84,564 78,518 -6,046
v5-inplace-fill 91,841 85,795 -6,046

Legacy flows and per-fixture approval/buffer costs are unchanged. These are consumed-CU measurements on the documented local runtime, not production budgets.

Validation after restacking: 18 Rust tests, all 25 CU fixtures, and 11 targeted deposit/fill validator tests passed, including allowance, wrong authority, PermanentDelegate, frozen accounts and rollback. Guarded SBF builds passed; the validator-test Spoke binary matches the measured binary byte for byte. Formatting and diff checks passed. Full verified-build results are tracked separately in CI.

Targets #1568 (reinis/svm-cu-benchmarks), independently of #1575.

Per-fixture execution CU and provenance

Before snapshot and after snapshot use Node 22.22.0, Agave 4.1.2, cargo-build-sbf 4.1.0 and platform-tools 1.44/1.52/1.54 for legacy/Spoke/Gateway. Fixture, runtime, public IDL, legacy and Gateway hashes match. Approval and buffer costs are unchanged per fixture. Source revisions, tracked source-diff hashes and binary hashes are recorded in the snapshots.

Flow Fixture Before After Delta
legacy-deposit 0 42,207 42,207 +0
legacy-fill 0 41,021 41,021 +0
legacy-deposit 1 34,707 34,707 +0
legacy-fill 1 44,021 44,021 +0
legacy-deposit 2 34,707 34,707 +0
legacy-fill 2 39,521 39,521 +0
legacy-deposit 3 34,707 34,707 +0
legacy-fill 3 39,521 39,521 +0
legacy-deposit 4 34,707 34,707 +0
legacy-fill 4 51,521 51,521 +0
v5-deposit 0 79,516 73,469 -6,047
v5-external-fill 0 84,564 78,518 -6,046
v5-inplace-fill 0 97,841 91,795 -6,046
v5-deposit 1 73,516 67,469 -6,047
v5-external-fill 1 80,064 74,018 -6,046
v5-inplace-fill 1 91,841 85,795 -6,046
v5-deposit 2 73,516 67,469 -6,047
v5-external-fill 2 83,064 77,018 -6,046
v5-inplace-fill 2 91,841 85,795 -6,046
v5-deposit 3 82,516 76,469 -6,047
v5-external-fill 3 93,564 87,518 -6,046
v5-inplace-fill 3 111,341 105,295 -6,046
v5-deposit 4 67,516 61,469 -6,047
v5-external-fill 4 86,064 80,018 -6,046
v5-inplace-fill 4 87,341 81,295 -6,046

Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

@Reinis-FRP
Reinis-FRP requested a review from droplet-rl October 1, 2026 15:09

@droplet-rl droplet-rl 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. The change is small and correct, and it is safe to land as is. My comments below are all non-blocking.

Correctness

  • V5TransferDelegate::pda() returns constants that hardcoded_v5_authorities_match_canonical_pdas and the golden-fixture test already pin. The enum means callers can't mix one delegate's seed with another's bump, and no caller-supplied bump reaches the signer seeds.
  • If crate::ID ever drifted from the constants, this still fails closed. The loaders' find_v5_account(.., &V5_*_DELEGATE, ..) checks would still match the constant, but the runtime's invoke_signed derivation would no longer match it, so the CPI would be rejected for a missing signature. The only change is that the error would no longer be InvalidDelegatePda.
  • The authority equality check in transfer_from duplicates the loaders' find_v5_account lookup. It costs almost nothing and keeps the helper safe on its own, so keeping it is fine.
  • Moving the helper out of utils has no other in-repo callers. The svm_spoke::utils::transfer_from on master already has a different signature that earlier commits in this stack changed, so this PR adds no new break for external code.
  • No IDL or account-order changes. In-place delivery is untouched.

What I verified locally

  • cargo test -p svm-spoke --lib: 18/18 pass on fc151516.
  • Mutation check: removing the delegate != authority check makes wrong_transfer_authority_is_rejected_before_cpi fail, so that test really guards the check.
  • Provenance: the trackedDiffSha256 in baseline.json (57c5f9f5…) is exactly the sha256 of the trimmed git diff 2676e12c fc151516 -- programs Cargo.toml Cargo.lock. So head plus that diff matches the committed relocation.
  • before-constant-delegates.json matches the base branch's baseline.json on every measurement and every binary/IDL/runtime hash. Only head and node differ, which supports the claim that the before run reproduced the base.
  • README numbers match baseline.json: execution medians are 67,483 / 78,614 / 77,369, and the total-median external−in-place gap is 7,245. The normalized gap is 72,614 − 71,369 = 1,245, sample 3 gives 87,614 − 96,869 = −9,255, and the normalized totals are 74,122 / 84,943.

I did not re-run the SBF build or the Gateway conformance and CU benchmark suites.

The nits are inline: a test gap for a wholesale Deposit↔Fill swap, stale local paths in the README, a suggestion to keep the per-PR history out of the README, and a stray line break in the spec.

Comment thread programs/svm-spoke/src/v5/transfer.rs Outdated
use super::*;

#[test]
fn transfer_delegates_match_canonical_pdas() {

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.

Nit: this test checks that each variant's address, seed and bump agree with each other, but not which delegate each variant maps to. If the two arms of pda() were swapped wholesale (Deposit => (V5_FILL_DELEGATE, V5_FILL_DELEGATE_SEED, V5_FILL_DELEGATE_BUMP) and vice versa), both unit tests would still pass. The rejection test skips the selected variant's own address, so it misses the swap too. Only the Gateway conformance suite would catch it.

The seed/bump/address check also mostly repeats v5::tests::hardcoded_v5_authorities_match_canonical_pdas. Pinning the mapping would be cheaper and catch more:

assert_eq!(V5TransferDelegate::Deposit.pda(), (V5_DEPOSIT_DELEGATE, V5_DEPOSIT_DELEGATE_SEED, V5_DEPOSIT_DELEGATE_BUMP));
assert_eq!(V5TransferDelegate::Fill.pda(), (V5_FILL_DELEGATE, V5_FILL_DELEGATE_SEED, V5_FILL_DELEGATE_BUMP));

The derivation check stays covered in v5/tests.rs. Separately, fn pda(self) is a bit more idiomatic than &self on a Copy enum.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 6a9d019: the test now pins each variant to its exact address/seed/bump tuple, and pda takes self. I also verified that swapping both mapping arms makes the test fail. All 18 Rust tests pass with the correct mappings; canonical derivation stays covered in v5/tests.rs.


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

Comment thread test/svm-gateway/README.md Outdated
| v5-inplace-fill | 3 | 96,869 | 96,869 | 0 |
| v5-inplace-fill | 4 | 74,369 | 74,369 | 0 |

Raw receipts and build/validator logs are retained locally in `target/cu-1569-before`, `target/cu-1569-after`,

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.

These target/cu-1569-* directories only exist on the machine that ran the benchmark, so readers of the checked-in README can't use them. I'd drop this sentence or move it to the PR description. Also, this line and the next are about 143 columns, past the 120 printWidth used everywhere else in this file's new prose. Prettier keeps it because proseWrap is preserve.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Removed the machine-local paths and the overlong prose in 6a9d019. The README now links to the PR for the historical comparison.


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

Comment thread test/svm-gateway/README.md Outdated
deposit** and **6,046 CU per external fill**. Runtime signer derivation for the token CPI remains. In-place fills
skip this helper and their CU is unchanged.

The [before snapshot](cu/before-constant-delegates.json) measures benchmark base

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.

Non-blocking, about upkeep. This section, its 25-row table and the 612-line cu/before-constant-delegates.json record one PR's history. The before snapshot matches the base branch's baseline.json except for head and node, so git history already holds the same measurements. Once baseline.json is regenerated for the next change, this section goes stale, and it's unclear whether it should be kept up to date.

Consider keeping just a one-line pointer to #1569/#1573 here, putting the per-fixture table in the PR or issue, and dropping the extra JSON. Fine to keep if the in-repo history is intentional.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in 6a9d019. The README keeps a short issue/PR pointer, the per-fixture table is now in the PR description with immutable before/after snapshot links, and the duplicate before JSON is removed.


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

Comment thread programs/svm-spoke/V5_ADAPTER_SPEC.md Outdated
bump as one internal combination. It checks the supplied authority against that address and signs the token CPI with
its canonical seeds, without searching for the PDA again. Both combinations are tested against derivation under the
configured Spoke program ID;
program-ID changes must update those constants together. No caller-supplied bump is accepted.

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.

Nit: there's a hard line break mid-sentence after program ID;. Please re-wrap this paragraph to 120 columns like the rest of the spec.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Rewrapped the paragraph to 120 columns in 6a9d019.


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

@droplet-rl

Copy link
Copy Markdown
Contributor

🔎 View trace

@Reinis-FRP

Copy link
Copy Markdown
Contributor Author

@droplet-rl , please recheck after addressing your comments

@droplet-rl

Copy link
Copy Markdown
Contributor

Rechecked at 6a9d019d. All four comments are addressed and I have nothing blocking.

  • Delegate mapping test: transfer_delegate_variants_select_their_expected_pdas now pins each variant to its own address, seed and bump. I swapped the Deposit/Fill arms in pda() to try it, and the test now fails, where before the change it passed. The derivation check itself is still covered by v5::tests::hardcoded_v5_authorities_match_canonical_pdas. pda(self) is fine.
  • Benchmark history: before-constant-delegates.json and the per-PR README section are gone, and the README now just links to perf(svm): reuse constant V5 transfer delegates #1573 and perf(svm): reuse constant V5 transfer delegate addresses and bumps #1569. Nothing else references the removed file or the target/cu-1569-* directories.
  • Spec line break: fixed.
  • Tests: cargo test -p svm-spoke --lib passes 18/18 at the tip.

Two optional notes:

  • V5_ADAPTER_SPEC.md:136 is now 122 columns. A few other lines in that file already run past 120, so leave it if you like.
  • baseline.json records the source as 2676e12c plus the diff to fc151516. This commit touches programs/ again (spec, tests, pda(self)), so the next benchmark run will record a different source diff hash. I don't expect self vs &self to change the Spoke binary, but I couldn't confirm it because cargo-build-sbf isn't installed here.

I made no commits.

@droplet-rl

Copy link
Copy Markdown
Contributor

🔎 View trace

@Reinis-FRP

Copy link
Copy Markdown
Contributor Author

Addressed the follow-up notes:

  • Rewrapped the spec paragraph in 8c95584; its longest line is now 119 columns. Prettier and all commit hooks passed under Node 22.18.0.
  • The guarded test-feature SBF build after the 6a9d019d code changes is byte-identical to the measured baseline. I rechecked the artifact against baseline.json: both have SHA-256 5432f53a8a0c5e5a0375d27218a2cd4ea3d849bc58e3ecb23ad0db807ed18616.

The snapshot retains the source metadata of the actual benchmark run (2676e12c plus the recorded diff to fc151516). A future benchmark will record its own source hash; the later code changes preserve the measured binary, and this latest commit only wraps Markdown.


Sent from Reinis Martinsons's Codex CLI Agent using gpt-6-astra 🤖

Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
Signed-off-by: Reinis Martinsons <reinis@umaproject.org>
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-constant-transfer-delegates branch from 6390423 to c9dc5a2 Compare October 5, 2026 12:04
@Reinis-FRP
Reinis-FRP force-pushed the reinis/svm-cu-benchmarks branch from 3d3178f to a537c44 Compare October 5, 2026 12:04

@fusmanii fusmanii 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.

nice!!

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.

3 participants