serviceability-instruction: globalstate/globalconfig/allowlist/index/migrate builders (RFC-26 R9) - #4058
Conversation
…RFC-26 R0) (#4049) ## Summary RFC-26 **R0**: scaffold the pure, RPC-free instruction-builder crate `doublezero-serviceability-instruction` — SPL-style builders that return a single unsigned `Instruction` per serviceability instruction, no signing/sending. - `common::build` (no-permission trailing `[payer, system]`) + `common::build_with_permission` (the deferred, activate-in-one-place Permission append) + `compute_budget_prelude` (1.4M CU / 256 KiB). - Four exemplar builders establishing the pattern every later PR copies: `create_device`, `delete_device` (legacy/atomic), `create_link`, `create_subscribe_user`. - Deps limited to `doublezero-serviceability` + `solana-program` + `solana-system-interface` + `solana-compute-budget-interface` — no RPC tree. The `suspend_device` exemplar from the RFC was replaced with `delete_device` (`SuspendDevice` is a deprecated variant); the RFC and #4015 were updated, and the "length-detected family" classification was corrected (only `CreateUser` is length-detected). ## Why Instruction assembly is currently coupled to signing+sending inside `commands/*::execute()`; there is no reusable `build_xxx(args) -> Instruction`. See [rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md). ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for each exemplar, including the `delete_device` legacy vs atomic layouts and the `create_subscribe_user` optional-feed placement. Closes #4015. Part of RFC-26. --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars ← **this PR** 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
6a548bf to
b4d8e02
Compare
e0235a0 to
9240816
Compare
b4d8e02 to
0afe42f
Compare
9240816 to
9e23736
Compare
## Summary RFC-26 **R1** (stacked on the previous phase's branch). Complete the device domain on top of the R0 exemplars: update_device (max(old,new) dz_prefix block), set_device_health, and interface create/delete/update (Vpnv4 topology PDAs, segment-routing reconcile). All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4016. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device ← **this PR** 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
## Summary RFC-26 **R2** (stacked on the previous phase's branch). accept_link, update_link (LinkUpdateAuthority preamble + conditional tunnel_net / tunnel-resource / topology-union sections), delete_link (topology reference-count accounts), set_link_health. All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4017. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link ← **this PR** 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
## Summary RFC-26 **R3** (stacked on the previous phase's branch). create_user (length-detected -> build, no permission), update_user, delete_user, request_ban_user, check_user_access_pass, and set_user_bgp_status (metrics-publisher check, no authorize -> build). All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4018. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user ← **this PR** 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
0afe42f to
74ade12
Compare
9e23736 to
b919cea
Compare
74ade12 to
f31652d
Compare
b919cea to
4618cc4
Compare
f31652d to
f7d915c
Compare
4618cc4 to
155cbc7
Compare
…FC-26 R4) (#4053) ## Summary RFC-26 **R4** (stacked on the previous phase's branch). Three account_index-seeded CRUD domains (create/update/suspend/resume/delete). Exchange create/update carry globalconfig; set_device_exchange carries the device; create_contributor carries the owner. All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4019. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor ← **this PR** 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
…26 R5) (#4054) ## Summary RFC-26 **R5** (stacked on the previous phase's branch). create/update (conditional multicast_group_block)/suspend/reactivate/delete, update_multicast_group_roles, and the four pub/sub allowlist add/remove builders. All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4020. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists ← **this PR** 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
…4055) ## Summary RFC-26 **R6** (stacked on the previous phase's branch). Tenant CRUD + add/remove administrator + update_payment_status (globalstate writable on create, read-only elsewhere), and permission create/update/suspend/resume/delete (target PDA derived from args.user_payer). All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4021. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission ← **this PR** 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
## Summary RFC-26 **R7** (stacked on the previous phase's branch). Topology create/delete plus batched clear_topology / assign_topology_node_segments (single-chunk + *_batched; CLEAR_BATCH_SIZE=16 / BACKFILL_BATCH_SIZE=4 moved into the crate). Feed create/update/delete. All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4022. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed ← **this PR** 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
f7d915c to
ad00567
Compare
155cbc7 to
d934652
Compare
#4057) ## Summary RFC-26 **R8** (stacked on the previous phase's branch). Access-pass set (conditional tenant pair)/close/check-status/set-feeds, and resource-extension allocate/create/deallocate/close (resource PDA + associated account derived from the data-bearing args.resource_type). All builders route through `authorize()` -> `build_with_permission` unless noted; each carries a verbatim account-layout doc-comment copied from its processor. ## Testing Verification - Unit tests assert the exact `AccountMeta` list (incl. trailing `[payer, system]`) and the borsh tag byte for every builder, covering the conditional/variable-account paths. Closes #4023. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each is based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource ← **this PR** 10. #4058 — R9 globalstate/config/allowlist/index/migrate R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.
…dex/migrate builders (RFC-26 R9) init_global_state and migrate never call authorize() -> build; the setters, set_global_config (config PDA + all 8 resource pools), foundation/QA allowlist toggles, and index create/delete route through authorize() -> build_with_permission. After this PR every buildable serviceability variant has a builder. Refs #4024, RFC-26.
…sion builder process_set_version reads program_config FIRST (it writes the updated min_compatible_version there), then globalstate. The builder emitted only [globalstate], so the instruction could never execute. Add program_config as the leading (writable) account, matching the processor and init_global_state.
d934652 to
bde6207
Compare
…globalstate builders
ben-dz
left a comment
There was a problem hiding this comment.
lgtm — builders faithfully mirror the processors (all 13 tag bytes, account orders, writability/signer flags, PDA derivations, and the build vs build_with_permission split verified), byte-parity with the existing SDK commands holds, and tests pin the full AccountMeta vector + tag byte per builder. One trivial nit; one informational note about a pre-existing SDK SetVersionCommand bug (the new set_min_version builder is correct where the SDK is stale) worth a separate follow-up.
- Informational (pre-existing, not this PR):
smartcontract/sdk/rs/src/commands/globalstate/setversion.rssends only[globalstate], but the processor reads[program_config, globalstate, payer, system]since #3977, so the SDK'sSetVersionCommandfails onchain today. The newset_min_versionbuilder is correct — an intentional, documented divergence from SDK byte-parity. Worth a separate follow-up to fix the SDK command.
The processor reads [program_config, globalstate, payer, system] since #3977, but the SDK command sent only [globalstate], so it failed onchain. Prepend program_config to match the processor's account order.
Good catch — rather than defer this, I fixed it here in 5259c95: |
LGTM, no need for a new branch. |
…4059) ## Summary RFC-26 **RF**: golden fixtures for the instruction builders + a CI drift guard. - Extend the serviceability fixture generator to depend on `doublezero-serviceability-instruction` and emit, per instruction, `ix_<name>.bin` (wire bytes = tag + borsh) and `ix_<name>.json` (`{ variant, data_hex, accounts: [{pubkey, is_signer, is_writable}] }`) from fixed, deterministic inputs. - Representative set covering the trickiest layouts: `create_device` (variable dz_prefix), `delete_device` (atomic close), `create_link`, `create_subscribe_user` (optional feed), `create_user` (length-detected), `clear_topology` / `assign_topology_node_segments` (batched), `set_global_config` (config PDA + all 8 pools). - `make generate-fixtures-check` (regenerate + fail on drift, scoped to the emitted `.bin`/`.json`) and a `fixtures-check` job in the `rust` workflow. These fixtures capture byte-for-byte the current SDK trailing convention (they will drive Go/Python/TS parity in a later phase). ## Testing Verification - `make generate-fixtures-check` passes (regenerated output is byte-identical to the committed fixtures); a hand-edited fixture makes it fail as expected. Closes #4026. Part of RFC-26 ([rfcs/rfc26-rust-instruction-builder-library.md](rfcs/rfc26-rust-instruction-builder-library.md)). --- ### PR stack (RFC-26 builder library) Stacked PRs, merge in order (each based on the previous one's branch): 1. #4049 — R0 scaffold + exemplars 2. #4050 — R1 device 3. #4051 — R2 link 4. #4052 — R3 user 5. #4053 — R4 location/exchange/contributor 6. #4054 — R5 multicastgroup + allowlists 7. #4055 — R6 tenant + permission 8. #4056 — R7 topology + feed 9. #4057 — R8 accesspass + resource 10. #4058 — R9 globalstate/config/allowlist/index/migrate 11. this PR — RF fixtures + CI guard R10 (commands/* migration + program-test) is the final PR and will carry the global changelog entry.
Summary
RFC-26 R9 (stacked on the previous phase's branch). init_global_state and migrate (no authorize -> build); setters, set_global_config (config PDA + all 8 resource pools), foundation/QA allowlist toggles, index create/delete. After this every buildable variant has a builder.
All builders route through
authorize()->build_with_permissionunless noted; each carries a verbatim account-layout doc-comment copied from its processor.Testing Verification
AccountMetalist (incl. trailing[payer, system]) and the borsh tag byte for every builder, covering the conditional/variable-account paths.Closes #4024. Part of RFC-26 (rfcs/rfc26-rust-instruction-builder-library.md).
PR stack (RFC-26 builder library)
Stacked PRs, merge in order (each is based on the previous one's branch):
R10 (commands/* migration + program-test) and RF (fixtures) follow as separate PRs.