solana-sdk: mirror ValidatorClientRewards as a zero-copy struct - #410
Conversation
bgm-malbeclabs
left a comment
There was a problem hiding this comment.
Checked the change against the onchain source rather than only reading the diff.
What checks out: the new ValidatorClientRewards mirror in crates/solana-sdk/src/shred_subscription/state.rs is field for field identical to the program struct in doublezero-shred-subscription, including _padding_0, _padding_1, _gap: StorageGap<2>, and #[repr(C, align(8))]. Size works out to 176, so 184 - DISCRIMINATOR_LEN holds and the offsets match the deleted VCR_*_OFFSET constants exactly. discriminator_slice() returns the same 8 bytes the old borsh::to_vec produced, so the synthetic fork account stays byte identical. cargo clippy --all-targets -- -Dclippy::all is clean on the three affected packages, unit tests pass including the exact match show summary assertion, and the \ line continuations in the new format! reproduce the old two space indent. No references to the deleted API remain.
No correctness bug found. One note below, not a blocker.
8915b37 to
cc65a22
Compare
…eclabs/doublezero-offchain#410) Closes #4183 ## Summary #### solana-sdk - Add `ValidatorClientRewards` as a `Pod` mirror of the onchain struct, with a `PrecomputedDiscriminator` impl, a `Default` impl matching the program's, a `checked_short_description` accessor, and a compile-time assertion pinning the account at 184 bytes - Delete the five `VCR_*_OFFSET` constants, `VCR_SHORT_DESCRIPTION_LEN`, `VCR_ACCOUNT_DATA_LEN`, the standalone discriminator constant, `parse_validator_client_rewards`, and `ValidatorClientRewardsInfo`. None of them shipped, so the changelog bullet that introduced them is rewritten rather than paired with a removal entry - Fold the new mirror into the existing mirrored-layout section alongside `ShredRewardToken` and `ValidatorPublisherRewards` rather than opening a second one #### solana-cli - Read the account through the mirror in `show`, `claim`, and `init-holding`. All three decode the whole struct, so they require at least the 184 bytes the program allocates - `claim` takes its post-transaction count from `SolanaConnection::try_fetch_zero_copy_data_with_commitment`, which warns on a missing or undecodable account where the previous code printed `(unavailable)` silently #### solana-fork - Build the synthetic account from `ValidatorClientRewards::default()` plus field assignment instead of copying bytes to hand-written offsets, and size the rent exemption from the built buffer. The parse-back round trip and its three `ensure!` checks go with them, being tautological against a struct the same function builds - Rename `--synthetic-vcr-manager` to `--synthetic-validator-client-rewards-manager`, along with the fork test script and the local-validator workflow #### repo - Record the conventions this change was written under in `CLAUDE.md`: the `_key` suffix for pubkey bindings, and which helper to reach for when reading a zero-copy account ## Testing - Unit tests across the three crates, including three cases for `checked_short_description` and an exact-match assertion pinning the rendered `show` summary byte for byte, which is what guards the format string's line continuations against a dropped escape - The `local-validator` workflow drives `show`, `init-holding`, and `claim` against a synthetic account baked into the fork at genesis, covering the renamed flag and the mirror's field offsets end to end
Closes malbeclabs/doublezero#4183
Summary
solana-sdk
ValidatorClientRewardsas aPodmirror of the onchain struct, with aPrecomputedDiscriminatorimpl, aDefaultimpl matching the program's, achecked_short_descriptionaccessor, and a compile-time assertion pinning the account at 184 bytesVCR_*_OFFSETconstants,VCR_SHORT_DESCRIPTION_LEN,VCR_ACCOUNT_DATA_LEN, the standalone discriminator constant,parse_validator_client_rewards, andValidatorClientRewardsInfo. None of them shipped, so the changelog bullet that introduced them is rewritten rather than paired with a removal entryShredRewardTokenandValidatorPublisherRewardsrather than opening a second onesolana-cli
show,claim, andinit-holding. All three decode the whole struct, so they require at least the 184 bytes the program allocatesclaimtakes its post-transaction count fromSolanaConnection::try_fetch_zero_copy_data_with_commitment, which warns on a missing or undecodable account where the previous code printed(unavailable)silentlysolana-fork
ValidatorClientRewards::default()plus field assignment instead of copying bytes to hand-written offsets, and size the rent exemption from the built buffer. The parse-back round trip and its threeensure!checks go with them, being tautological against a struct the same function builds--synthetic-vcr-managerto--synthetic-validator-client-rewards-manager, along with the fork test script and the local-validator workflowSYNTHETIC_VCR_MANAGERtoSYNTHETIC_VALIDATOR_CLIENT_REWARDS_MANAGER. A value left under the old name is ignored rather than rejected, so the fork boots with no synthetic account and the fork test fails later at the firstvalidator-client-rewards show. The flag has only ever existed under## [Unreleased], so no released version is affected, but anyone trackingmainwith the old name in a local.envor an infra job needs to move itrepo
CLAUDE.md: the_keysuffix for pubkey bindings, and which helper to reach for when reading a zero-copy accountTesting
checked_short_descriptionand an exact-match assertion pinning the renderedshowsummary byte for byte, which is what guards the format string's line continuations against a dropped escapelocal-validatorworkflow drivesshow,init-holding, andclaimagainst a synthetic account baked into the fork at genesis, covering the renamed flag and the mirror's field offsets end to end