Skip to content

feat!: add subnet add-validator + align P-Chain command names with avalanchego tx types (v2.0.0)#33

Open
owenwahlgren wants to merge 4 commits into
mainfrom
feature/subnet-add-validator
Open

feat!: add subnet add-validator + align P-Chain command names with avalanchego tx types (v2.0.0)#33
owenwahlgren wants to merge 4 commits into
mainfrom
feature/subnet-add-validator

Conversation

@owenwahlgren

@owenwahlgren owenwahlgren commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

What

Two related changes, shipping as a breaking v2.0.0 release:

1. New command: subnet add-validator

Adds a validator to a permissioned subnet via AddSubnetValidatorTx — the gap where the CLI could add primary-network validators and L1 validators but not permissioned-subnet validators.

platform subnet add-validator \
  --subnet-id <SubnetID> --node-id NodeID-... --weight 100 \
  [--start now] [--duration 336h] --key-name <subnet-owner-key>
  • Node must already validate the primary network; validation period must fit within its primary-network window and meet the network's min stake duration (checked locally).
  • Subnet owner authorizes via subnet auth (wallet loaded tracking the subnet).
  • --weight is sampling weight, not a stake amount.

2. Align command names with avalanchego tx types (BREAKING)

Every P-Chain command now maps 1:1 to the avalanchego transaction it issues. Old names are removed (no aliases) and now error with unknown command (exit 1):

Old (removed) New
validator add validator add-permissionless
validator delegate validator add-permissionless-delegator
subnet convert-l1 subnet convert-to-l1
l1 set-weight l1 set-validator-weight
l1 add-balance l1 increase-validator-balance

Command groups (root + validator/subnet/l1/chain/transfer/keys/wallet/node) now reject unknown subcommands via requireSubcommand instead of silently printing help + exiting 0.

Changes

  • pkg/pchain: AddSubnetValidator + AddSubnetValidatorConfig + addSubnetValidatorTxIssuer seam (matches the existing interface-based issue*Tx pattern).
  • cmd/: command renames; requireSubcommand guard; tx-type annotations in Short help; --start no-op note (post-Durango).
  • Tests: TestIssueAddSubnetValidatorTx (unit); e2e TestCLIRemovedOldNamesRejected, TestCLIUnknownSubcommandRejected, TestCLISubnetAddValidatorMissingArgs.
  • Docs: docs/usage.md, docs/pchain-operations.md (migration table). Companion builders-hub docs PR: docs(platform-cli): align with v2.0.0 command renames + add subnet add-validator builders-hub#4304.

Backward compatibility

Breaking. Old command names are removed and must be migrated (see table). Cut as a major version bump (v2.0.0).

Testing

  • go build, go vet ./..., staticcheck ./..., gofmt — clean
  • go test ./pkg/... — pass
  • go test -tags=clie2e ./e2e/... -run "Help|Params|MissingArgs|RemovedOldNames|UnknownSubcommand" — pass (matches the CI smoke filter)

Adds a validator to a permissioned subnet via AddSubnetValidatorTx, the
classic path for managing validators on a permissioned subnet (distinct
from `l1 register-validator`, which targets converted L1s).

- pkg/pchain: AddSubnetValidator + issuer seam/config, mirroring the
  existing interface-based issue*Tx helpers
- cmd/subnet: `subnet add-validator` with --subnet-id/--node-id/--weight
  /--start/--duration; subnet owner authorizes via subnet auth, so the
  wallet is loaded tracking the subnet
- tests: pchain unit test + e2e help/missing-args coverage
- docs: usage.md and pchain-operations.md
Rename commands so each maps 1:1 to the avalanchego transaction it issues,
keeping the previous names as deprecated aliases (a warning is printed when
an alias is used). pchain function names already matched the tx types.

Renames (old name kept as alias):
- validator add            -> validator add-permissionless
- validator delegate       -> validator add-permissionless-delegator
- subnet convert-l1        -> subnet convert-to-l1
- l1 set-weight            -> l1 set-validator-weight
- l1 add-balance           -> l1 increase-validator-balance

Also annotates each command's Short help with its tx type, adds a shared
warnIfDeprecatedAlias helper, an e2e alias-deprecation test, and updates
docs (usage.md, pchain-operations.md) and CLAUDE.md.
@owenwahlgren owenwahlgren changed the title feat: add subnet add-validator command (AddSubnetValidatorTx) feat: add subnet add-validator + align P-Chain command names with avalanchego tx types Jun 16, 2026
BREAKING CHANGE: the previous command names are removed entirely (no
aliases). Old names now error with "unknown command". Migrate:
- validator add        -> validator add-permissionless
- validator delegate   -> validator add-permissionless-delegator
- subnet convert-l1    -> subnet convert-to-l1
- l1 set-weight        -> l1 set-validator-weight
- l1 add-balance       -> l1 increase-validator-balance

- drop Aliases + warnIfDeprecatedAlias helper
- command groups (validator/subnet/l1) now reject unknown subcommands
  via requireSubcommand so removed names fail loudly (exit 1)
- e2e: assert old names are rejected; docs reframed as v2.0.0 migration
@owenwahlgren

Copy link
Copy Markdown
Collaborator Author

@federiconardelli7 heads-up — this PR changed materially since your approval, so it needs another look before merge.

What changed: we turned the command rename into a hard cutover (breaking). The deprecated aliases are gone — the old names (validator add, validator delegate, subnet convert-l1, l1 set-weight, l1 add-balance) no longer work and now error with unknown command (exit 1). The command groups got a requireSubcommand RunE so removed names fail loudly instead of silently printing help.

Impact: this is now a v2.0.0 / breaking release rather than the backward-compatible v1.1.0 you approved.

New commits since your review:

  • 2404412 align command names with avalanchego tx types
  • 3e9ae74 remove deprecated aliases (hard cutover)

CI is green (build, vet, staticcheck, unit, offline e2e). Companion docs PR: ava-labs/builders-hub#4304.

Mind taking another pass? 🙏

@federiconardelli7 federiconardelli7 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed the full PR — all changed files plus cmd/chain.go, go.mod, and the CI workflows. The new subnet add-validator and the rename are well done: the rename is applied consistently (I swept the repo for leftover old names and found none), pkg/pchain follows the existing issuer-seam pattern with a thorough unit test, and requireSubcommand correctly turns a removed name into exit 1 + unknown command (verified against root.go's SilenceErrors/SilenceUsage and Execute()).

Things to look at — left inline, summarized here:

1. (should-fix) The removed-names guard does not run in CI. ci.yml runs go test -tags=clie2e ... -run 'Help|Params|MissingArgs', which TestCLIRemovedOldNamesRejected does not match — so it is compiled (staticcheck) but never executed. Extending that -run filter (e.g. ...|RemovedOldNames) would make the guard real. (Inline on the test.)

2. (minor) chain was left out of the conventions applied everywhere else. chain create's Short is still 'Create a new chain' — the only canonical command with no (CreateChainTx) annotation — and chainCmd did not get requireSubcommand, so platform chain <typo> still silently prints help and exits 0, the footgun requireSubcommand fixes for the other groups. transfer/keys/wallet/node are in the same boat. Out of scope for the rename, but worth a follow-up. (cmd/chain.go is unchanged, so no inline comment.)

3. (minor) Small items on the new command's validation and its test — inline on cmd/subnet.go and e2e/cli_test.go.

Process note: since old names are removed outright, make sure the release is cut as a major version bump.

Net: implementation looks solid; the main actionable item is wiring the removed-names guard into CI so the cutover stays protected.

Comment thread cmd/subnet.go
return fmt.Errorf("invalid node ID: %w", err)
}

start, end, err := parseTimeRange(subnetValStartTime, subnetValDuration)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Question on --start: avalanchego is pinned at v1.14.1 (post-Durango), where I believe the P-chain ignores the tx Start for subnet/permissionless validators — they begin at acceptance. If that is right, parseTimeRange returning now+30s and feeding it into the tx has no on-chain effect, so --start is effectively a no-op and might deserve a note in the flag help. Not specific to this PR (the existing add-permissionless commands do the same) — just flagging it since this new flag advertises a start time.

Comment thread cmd/subnet.go Outdated
if err != nil {
return err
}
if !end.After(start) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Lighter validation than the sibling add-permissionless / add-permissionless-delegator, which also check end.Sub(start) >= netConfig.MinStakeDuration and return a friendly local error. Here only end > start is enforced, so a too-short or out-of-window period only fails at the network. Optional, but a local duration check would give a clearer message.

Comment thread e2e/cli_test.go

// TestCLIRemovedOldNamesRejected verifies the v2.0.0 hard cutover: the old
// command names were removed (no aliases) and are now rejected as unknown.
func TestCLIRemovedOldNamesRejected(t *testing.T) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Good guard for the cutover. One catch though: CI's smoke step runs go test -tags=clie2e ... -run 'Help|Params|MissingArgs' (.github/workflows/ci.yml), and this test name matches none of those patterns, so it is compiled via staticcheck but never actually executed in CI. Adding a matching pattern (e.g. ...|RemovedOldNames) to that -run filter would make sure the guard runs. Side note: the test command in the PR description references DeprecatedAlias, which does not exist.

Comment thread e2e/cli_test.go
t.Error("expected error when missing required args")
}

if !strings.Contains(stderr, "subnet-id") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Minor: because this subnet-id check uses t.Logf, the test only really asserts err != nil — a wrong or empty error message would not fail it. t.Errorf would make it a real assertion. This matches the existing convention in the other ...MissingArgs tests in this file, so feel free to treat it as optional (or a small file-wide cleanup).

…r polish

Addresses @federiconardelli7's re-review of #33:

- ci.yml: add RemovedOldNames + UnknownSubcommand to the clie2e -run filter
  so the cutover guards actually execute in CI (were compiled-only before)
- apply requireSubcommand to root + chain/transfer/keys/wallet/node so an
  unknown subcommand errors (exit 1) instead of silently printing help;
  annotate `chain create` Short with (CreateChainTx)
- subnet add-validator: add local min-duration check mirroring
  add-permissionless (friendly error instead of network-only failure)
- note on --start that post-Durango networks ignore it (validator starts at
  acceptance) on subnet add-validator + the two validator commands; verified
  against avalanchego v1.14.1 staker_tx_verification.go
- e2e: real t.Errorf assertion in TestCLISubnetAddValidatorMissingArgs;
  new TestCLIUnknownSubcommandRejected covering the newly-guarded groups
@owenwahlgren owenwahlgren changed the title feat: add subnet add-validator + align P-Chain command names with avalanchego tx types feat!: add subnet add-validator + align P-Chain command names with avalanchego tx types (v2.0.0) Jun 18, 2026
@owenwahlgren

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough re-review @federiconardelli7 — all addressed in 1342d93:

1. (should-fix) Removed-names guard not running in CI ✅ — extended the clie2e smoke filter in ci.yml to Help|Params|MissingArgs|RemovedOldNames|UnknownSubcommand, so TestCLIRemovedOldNamesRejected (and the new test below) now actually execute, not just compile.

2. (minor) Footgun + chain left out of conventions ✅ — applied requireSubcommand to root + chain/transfer/keys/wallet/node (not just the three rename groups), so platform <typo> / platform chain <typo> now exit 1 with unknown command instead of silently printing help + exiting 0. Annotated chain create's Short with (CreateChainTx). Added TestCLIUnknownSubcommandRejected covering the newly-guarded groups.

3a. (minor) --start may be a no-op ✅ — confirmed against staker_tx_verification.go in the pinned avalanchego v1.14.1: post-Durango startTime := currentTimestamp (tx Start only used pre-Durango). Added a note to --start help on subnet add-validator and the two validator commands (since they share the behavior).

3b. (minor) Lighter validation than siblings ✅ — subnet add-validator now does a local end.Sub(start) < netConfig.MinStakeDuration check (the same executor enforces MinStakeDuration/MaxStakeDuration for subnet validators), giving a friendly local error instead of a network-only failure.

4. (minor) t.Logft.Errorf ✅ — TestCLISubnetAddValidatorMissingArgs now makes a real assertion on the error message.

Process note (major bump) ✅ — will cut as v2.0.0; PR title/description updated to reflect the breaking hard cutover (and fixed the stale DeprecatedAlias reference + test command you spotted).

I scoped the broader requireSubcommand/annotation sweep (item 2) to this PR since it directly closes the footgun you flagged — happy to split it out if you'd prefer. Mind taking another pass?

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.

2 participants