Skip to content

sdk: serviceability go executor CreateUser/DeleteUser#3774

Open
elitegreg wants to merge 3 commits into
mainfrom
gm/sdk-user-crud-reconcile
Open

sdk: serviceability go executor CreateUser/DeleteUser#3774
elitegreg wants to merge 3 commits into
mainfrom
gm/sdk-user-crud-reconcile

Conversation

@elitegreg
Copy link
Copy Markdown
Contributor

@elitegreg elitegreg commented May 27, 2026

Summary

Adds the Solana-side primitives the device-stress orchestrator (#3746) needs as a library-only change — no new binary.

  • CreateUser (variant 36) and DeleteUser (variant 42) executor methods on smartcontract/sdk/go/serviceability, with account-list construction mirroring the Rust SDK; both wait for post-confirmation visible state so callers get a meaningful t_activate to record.
  • PDA helpers GetUserPDA, GetAccessPassPDA, GetTunnelIdsPDA, GetDzPrefixBlockPDA — seeds mirrored from smartcontract/programs/doublezero-serviceability/src/pda.rs.
  • Rust fixture generator extended to emit user_create_args.{bin,json} and user_delete_args.{bin,json}; Go tests load them as the cross-language wire-format contract.

Part 1 of #3746. Closes #3770.

PlanReconcile / ReconcilePlan were originally scoped here but are orchestrator policy, not an SDK primitive, so they now land alongside the orchestrator binary in part 2 of #3746.

Testing Verification

  • Golden-byte parity: Go buildCreateUserInstruction / buildDeleteUserInstruction produce the same borsh body as the Rust-generated fixtures (user_create_args.bin, user_delete_args.bin).
  • Account ordering verified against the Rust SDK's expected AccountMeta lists (smartcontract/sdk/rs/src/commands/user/create.rs:209 and delete.rs:363).
  • New PDA helpers cross-checked against an independent recomputation of the seed bytes; GetTunnelIdsPDA / GetDzPrefixBlockPDA verified to use 8-byte little-endian index.
  • make go-build go-lint go-test all green.

@elitegreg elitegreg changed the title sdk: serviceability go executor CreateUser/DeleteUser + reconcile planner sdk: serviceability go executor CreateUser/DeleteUser May 27, 2026
elitegreg added 3 commits May 27, 2026 15:13
… planner

Adds the Solana-side primitives the device-stress orchestrator (#3746) needs:

- CreateUser / DeleteUser methods on the Go serviceability executor (variants
  36 / 42), with account-list construction mirroring the Rust SDK and a
  post-confirmation visibility wait so callers can record t_activate against
  the user PDA.
- PDA helpers: GetUserPDA, GetAccessPassPDA, GetTunnelIdsPDA,
  GetDzPrefixBlockPDA — seed bytes mirrored from
  smartcontract/programs/doublezero-serviceability/src/pda.rs.
- Pure PlanReconcile function and ReconcilePlan type for sweep delta planning,
  deterministic via ClientIp-ascending sort.
- Rust fixture generator extended to emit user_create_args.{bin,json} and
  user_delete_args.{bin,json}; Go tests load them as the cross-language wire
  format contract.

Part 1 of #3746 — library-only, no new binary.

Closes #3770.
PlanReconcile is orchestrator policy ("how many users do we want") rather
than an SDK primitive ("how do I submit a CreateUser/DeleteUser"). Move it
out of the serviceability SDK and land it alongside the device-stress
orchestrator binary in part 2 of #3746.
}
user.PubKey = userPubkey

// The Rust SDK currently passes dz_prefix_count=1 / multicast_publisher_count=1
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.

We should probably also mention that this function does not currently unsubscribe from multicast groups (which I believe the Rust SDK does).

assert.True(t, tenantSlot.IsWritable)
}

func TestBuildCreateUserInstruction_RejectsZeroDzPrefix(t *testing.T) {
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.

Should this be named TestCreateUser_RejectsZeroDzPrefix?

// CreateUser submits a CreateUser instruction (variant 36) and waits for the user
// PDA to become visible on-chain. Returns the signature and derived user PDA so the
// caller can correlate (e.g., record t_activate against this user).
func (e *Executor) CreateUser(ctx context.Context, args UserCreateArgs) (solana.Signature, solana.PublicKey, error) {
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.

Add tests for CreateUser() and DeleteUser()?

Comment thread CHANGELOG.md
- Build a `CliContext` once at binary startup from `--env`, the per-field global overrides (`--url`, `--ws`, `--solana-url`, `--program-id`, `--geo-program-id`, `--keypair`, `--sock-file`), and the persisted `~/.config/doublezero/cli/config.yml` (overridable via `DOUBLEZERO_CONFIG_FILE`), per RFC-20 (§CliContext). Precedence (highest wins): CLI flag > persisted config > env-derived default. When `--env` is not set and the persisted config has a serviceability program ID, the environment is derived from that program ID via `Environment::from_program_id`; otherwise the binary falls back to `Environment::default()`. The legacy `DZClient` is now constructed from the fully resolved `CliContext` URL, WebSocket, and program-ID values directly, so verbs that migrate to read `CliContext` see the same backend as the legacy bridge. Keypair resolution is intentionally left to `DZClient::new`'s internal `load_keypair` precedence (CLI `--keypair` flag > `DOUBLEZERO_KEYPAIR` env var > stdin > persisted config) so the `DOUBLEZERO_KEYPAIR` env var continues to override the persisted keypair path, as relied on by the e2e contributor-auth negative-authz suite. File reads happen only in the binary; module crates remain forbidden from touching the filesystem (RFC-20 §67).
- Centralize top-level error rendering through `doublezero_cli_core::error::render_eyre`. Replaces three ad-hoc `eprintln!("Error: {e}")` sites in `client/doublezero/src/main.rs` (env-parse failure, env-config resolution failure, top-level command failure) with a single helper that prints `Error: <head>` followed by the full chain of causes on stderr.
- SDK (Go)
- Add `CreateUser` (instruction variant 36) and `DeleteUser` (variant 42) to the serviceability executor. Account ordering mirrors the Rust SDK at `smartcontract/sdk/rs/src/commands/user/{create,delete}.rs`; the borsh-encoded payload matches Rust's `UserCreateArgs` / `UserDeleteArgs` exactly. Both methods wait for the user PDA to become visible (or disappear) on-chain after finalization so callers can record a meaningful `t_activate` against the operation. `UserCreateArgs` bundles the borsh-encoded fields with `DevicePubkey` / optional `TenantPubkey` for account derivation. Introduces `GetUserPDA`, `GetAccessPassPDA`, `GetTunnelIdsPDA`, `GetDzPrefixBlockPDA` helpers in `pda.go`. Cross-language wire format is locked down by new Rust-generated `user_create_args.{bin,json}` and `user_delete_args.{bin,json}` fixtures that the Go tests load via the existing fixture pipeline ([#3770](https://github.com/malbeclabs/doublezero/issues/3770)).
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.

TL;DR:

Add CreateUser / DeleteUser to the serviceability executor with cross-language wire-format fixtures and four new PDA helpers (GetUserPDA, GetAccessPassPDA, GetTunnelIdsPDA, GetDzPrefixBlockPDA)

func (e *Executor) buildDeleteUserInstruction(userPubkey solana.PublicKey, user User, dzPrefixCount, multicastPublisherCount uint8) (solana.Instruction, error) {
data := []byte{instructionDeleteUser, dzPrefixCount, multicastPublisherCount}

accessPassPDA, _, err := GetAccessPassPDA(e.programID, user.ClientIp, user.Owner)
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.

The onchain process_delete_user() and the Rust SDK both also handle an UNSPECIFIED access pass, but that's missing here. Probably not a problem for device stress test, but could bite someone in the future

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.

stress: serviceability SDK user CRUD + reconcile-to-target (part 1 of #3746)

2 participants