feat: support ACP-236 auto-renewed staking commands#28
Conversation
owenwahlgren
left a comment
There was a problem hiding this comment.
Review: ACP-236 auto-renewed staking commands
Thanks for this — the Go itself is high quality: clean DI seams (the validator*Fn function vars), config structs, %w error wrapping, no secret logging, and three solid layers of tests (command validation, pchain unit, build-tagged devnetcompat serialization). My concerns are mostly architectural and merge-readiness, not code style. Requesting changes on the three high-severity items below.
🔴 High / Blockers
1. reward-auto is invalid by design — remove it.
RewardAutoRenewedValidatorTx is issued automatically by the consensus engine at the end of each validation cycle (same model as the classic RewardValidatorTx). It has no inputs/credentials and cannot be issued by a user wallet via the mempool/API — now or after devnet "supports" the type. The PR description frames the unsupported transaction type rejection as a temporary devnet gap ("cannot be accepted until devnet AvalancheGo supports that tx path"), but it's a permanent architectural constraint. That framing should be corrected so nobody later tries to "fix" it by waiting on devnet.
Please remove the whole path:
validatorRewardAutoCmd+ itsinit()registration (cmd/validator.go)valRewardAutoTxID/valRewardAutoTimevars and flag defsparseRewardAutoTimestamp+TestParseRewardAutoTimestamp(cmd/helpers_test.go)RewardAutoRenewedValidator/RewardAutoRenewedValidatorConfig/issueRewardAutoRenewedValidatorTxand thevalidatorRewardAutoRenewedValidatorFnseam (pkg/pchain/pchain.go,cmd/validator.go)- the reward-auto reset lines in
resetValidatorAutoCommandState
That leaves add-auto-renewed and set-auto-config, which are genuinely user-issuable — a cleaner, defensible surface.
2. Pinning main to an unreleased avalanchego devnet branch.
avalanchego v1.14.3-0.20260602193739-919446e8501f (plus matching graft/coreth, graft/evm, libevm) is a sae-devnet-5 pseudo-version. Merging to main couples the entire CLI — every existing command — to a pre-release SDK branch and makes a clean tagged release impossible. This should stay on a feature branch (or behind a build tag) until ACP-236 lands in an actual avalanchego release. At minimum, the PR should explicitly call out that main would knowingly track a devnet branch.
3. Fee under-funding risk in add-auto-renewed on Fuji/Mainnet.
issueAddAutoRenewedValidatorTx (pkg/pchain/pchain.go) builds an AddPermissionlessValidatorTx purely to drive UTXO selection + stake outputs, then repackages BaseTx/StakeOuts into a larger AddAutoRenewedValidatorTx (extra fields: ValidatorAuthority, AutoCompoundRewardShares, Period, ValidatorNodeID, DelegationShares). Under Etna dynamic fees the burned amount was sized for the smaller tx's complexity, but the node charges on the actual (bigger) tx bytes. It passed on devnet almost certainly because devnet gas price is at the floor; on Fuji/Mainnet under load this can be rejected as underfunded. Please verify the fee delta is covered (buffer, or confirm the builder over-selects) and validate on Fuji before this is trusted with mainnet funds.
🟡 Medium
4. SetAutoRenewedValidatorConfig is structurally fragile. It borrows NewDisableL1ValidatorTx to mint the auth, copies authTx.DisableAuth into SetAutoRenewedValidatorConfigTx.Auth, and newPWalletWithOwner hand-builds a backend with an owners map keyed by txID → authority. This works only because the disable-L1 builder resolves the auth owner from the backend's owners map. If avalanchego changes how that builder sources the disable owner (e.g. reads chain state), this breaks silently. Reasonable workaround for missing wallet helpers, but please add a comment explaining why NewDisableL1ValidatorTx is borrowed and a TODO to swap to a native builder once the SDK exposes one.
5. GetAutoRenewedValidatorAuthority fetches all current validators, then linear-scans. The request sends empty args, so on Mainnet that's a multi-thousand-entry JSON payload to find one txID. The hand-rolled JSON structs are justified (the typed client likely doesn't expose validatorAuthority yet), but consider a nodeIDs/subnetID filter if the API supports narrowing.
6. Silent period truncation at the library layer. uint64(cfg.Period / time.Second) truncates sub-second input. The cmd layer guards this (parseAutoRenewPeriod), but pkg/pchain is importable as a library, so a direct caller gets silent truncation. Consider validating Period % time.Second == 0 inside the pchain functions too.
🟢 Low / Nits
--stakehelp says "(min 2000)" onadd-auto-renewed, but the command enforcesnetConfig.MinValidatorStake(1 on Local/Fuji). Mirrors the existingaddflag, so pre-existing, but misleading on testnets.parseRewardAutoTimestamp'stimestamp > uint64(1<<63-1)is correct (Go binds<<tighter than-), butmath.MaxInt64would read more clearly. (Moot if #1 removes this function.)
✅ Strengths
- Test coverage is excellent — table-driven command validation, injected-function pchain unit tests, and
//go:build devnetcompattests that actually runSyntacticVerify+ round-trip serialize. Right way to test tx construction without a live node. - Clean DRY refactor:
feeToSharesnow delegates tofractionToShareswith no behavior change, covered byTestFractionToShares. - Follows project conventions: input validation before tx building,
%wwrapping, no panics, no key/secret logging, DI matching the existinggetNetworkConfig/loadPChainWalletpattern.
Verdict: The code is well-built and well-tested; the blockers are release-integrity issues, not code quality. #1 (a tx users can't issue) and #2 (devnet-branch dependency) should be resolved before merge to main, and #3 needs a real Fuji validation. Medium items are fine as follow-ups with TODO comments.
|
Hey @anishnar — full pass done. Functionally solid; main asks before merge:
Happy to pair on the rebase. 🙏 |
…faces (#30) * refactor: replace func-typed injection seams in pkg/pchain with interfaces The issue*Tx helpers took the wallet call as a function-typed parameter so unit tests could stub it. With multi-parameter builders the inline function types grew longer than the helper bodies (and the pattern was recently copied into an even larger 10-parameter signature in #28). Replace each seam with a single-method issuer interface mirroring the corresponding avalanchego wallet/chain/p method, so w.PWallet() satisfies them directly and tests stub a small struct instead of a closure. No behavior change: exported API, error messages, and wrapping are identical. Also gofmt's five files that were unformatted on main (same whitespace fixes as #29). * fix: upgrade Go to 1.25.11 and golang.org/x deps to resolve stdlib vulnerabilities govulncheck fails CI on main: GO-2026-5037 (crypto/x509), GO-2026-4971 (net), GO-2026-4918 (net/http + golang.org/x/net) are reachable from keystore key generation, wallet dialing, and network ID lookup. All are fixed in go1.25.10/1.25.11 and x/net v0.53.0. - go.mod: go 1.25.9 -> 1.25.11; x/net v0.47.0 -> v0.53.0 (pulls x/crypto, x/sys, x/term, x/text et al. forward) - ci.yml: bump setup-go and GOTOOLCHAIN pins to 1.25.11 govulncheck now reports 0 reachable vulnerabilities. --------- Co-authored-by: Martin Eckardt <m.eckardt@outlook.com>
…faces The issue*Tx helpers took the wallet call as a function-typed parameter so unit tests could stub it. With multi-parameter builders the inline function types grew longer than the helper bodies (and the pattern was recently copied into an even larger 10-parameter signature in #28). Replace each seam with a single-method issuer interface mirroring the corresponding avalanchego wallet/chain/p method, so w.PWallet() satisfies them directly and tests stub a small struct instead of a closure. No behavior change: exported API, error messages, and wrapping are identical. Also gofmt's five files that were unformatted on main (same whitespace fixes as #29).
Add `validator add-auto-renewed` and `validator set-auto-config`, built on the interface-based issuer seams from the pkg/pchain refactor. Addressing review feedback on #28: - replace the func-typed injection seams for the auto-renewed issuers with small single-method interfaces (autoRenewedValidatorTxIssuer, setAutoRenewedValidatorConfigTxIssuer), matching the rest of pkg/pchain; the native pwallet IssueAddAutoRenewedValidatorTx / IssueSetAutoRenewed- ValidatorConfigTx methods satisfy them - drop the validator*Fn package globals in cmd/validator.go; the commands call the production pchain functions directly and coverage lives in the pchain layer + e2e Help/MissingArgs cases - narrow GetAutoRenewedValidatorAuthority via the getCurrentValidators nodeIDs filter (new optional --node-id) instead of fetching the full set - build the owner-authorized wallet once via wallet.NewWalletFromKeychain- WithOwner, removing the redundant P-Chain state fetch in set-auto-config - bound the auto-renewal period by network MaxStakeDuration - use reward.PercentDenominator instead of a literal 1_000_000 The avalanchego dependency still tracks the unreleased helicon-devnet pseudo-version because the ACP-236 tx types and wallet builders are not yet in a tagged release; this PR should wait for upstream release readiness. Co-authored-by: anishnar <anish@avalabs.org>
636ecf1 to
7bb028b
Compare
Add `validator add-auto-renewed` and `validator set-auto-config`, built on the interface-based issuer seams from the pkg/pchain refactor. Addressing review feedback on #28: - replace the func-typed injection seams for the auto-renewed issuers with small single-method interfaces (autoRenewedValidatorTxIssuer, setAutoRenewedValidatorConfigTxIssuer), matching the rest of pkg/pchain; the native pwallet IssueAddAutoRenewedValidatorTx / IssueSetAutoRenewed- ValidatorConfigTx methods satisfy them - drop the validator*Fn package globals in cmd/validator.go; the commands call the production pchain functions directly and coverage lives in the pchain layer + e2e Help/MissingArgs cases - narrow GetAutoRenewedValidatorAuthority via the getCurrentValidators nodeIDs filter (new optional --node-id) instead of fetching the full set - build the owner-authorized wallet once via wallet.NewWalletFromKeychain- WithOwner, removing the redundant P-Chain state fetch in set-auto-config - bound the auto-renewal period by network MaxStakeDuration - use reward.PercentDenominator instead of a literal 1_000_000 The avalanchego dependency still tracks the unreleased helicon-devnet pseudo-version because the ACP-236 tx types and wallet builders are not yet in a tagged release; this PR should wait for upstream release readiness. Co-authored-by: anishnar <anish@avalabs.org>
7bb028b to
74c7ece
Compare
Add `validator add-auto-renewed` and `validator set-auto-config`, built on the interface-based issuer seams from the pkg/pchain refactor. Addressing review feedback on #28: - replace the func-typed injection seams for the auto-renewed issuers with small single-method interfaces (autoRenewedValidatorTxIssuer, setAutoRenewedValidatorConfigTxIssuer), matching the rest of pkg/pchain; the native pwallet IssueAddAutoRenewedValidatorTx / IssueSetAutoRenewed- ValidatorConfigTx methods satisfy them - drop the validator*Fn package globals in cmd/validator.go; the commands call the production pchain functions directly and coverage lives in the pchain layer + e2e Help/MissingArgs cases - narrow GetAutoRenewedValidatorAuthority via the getCurrentValidators nodeIDs filter (new optional --node-id) instead of fetching the full set - build the owner-authorized wallet once via wallet.NewWalletFromKeychain- WithOwner, removing the redundant P-Chain state fetch in set-auto-config - bound the auto-renewal period by network MaxStakeDuration - use reward.PercentDenominator instead of a literal 1_000_000 The avalanchego dependency still tracks the unreleased helicon-devnet pseudo-version because the ACP-236 tx types and wallet builders are not yet in a tagged release; this PR should wait for upstream release readiness. Co-authored-by: anishnar <anish@avalabs.org>
74c7ece to
0f2f75e
Compare
|
Pushed a pass addressing the review feedback (rebased onto Review checklist
Note: the owner-backed wallet is still required for |
Summary
platform validator add-auto-renewedfor ACP-236 auto-renewed validator creationplatform validator set-auto-configfor next-cycle config updates and graceful cycle-end exitreward-autocommand becauseRewardAutoRenewedValidatorTxis consensus-issued onlyvalidatorAuthorityfromplatform.getCurrentValidatorsforSetAutoRenewedValidatorConfigTxsigningAddAutoRenewedValidatorTxandSetAutoRenewedValidatorConfigTxhelicon-devnetpseudo-versionv1.14.3-0.20260603151011-1339ef45dc6cNotes
RewardAutoRenewedValidatorTxis issued by the consensus engine at cycle end, analogous to classic reward validator handling. It is not a user-facing CLI command and is not expected to succeed through manualissueTx.add-auto-renewednow calls the nativeNewAddAutoRenewedValidatorTxbuilder instead of sizing fees throughAddPermissionlessValidatorTx, avoiding underfunding risk under dynamic fees.set-auto-configloads the active validator'svalidatorAuthorityowner from current validators and signs the owner-auth path with that state.helicon-devnet, so it should wait for upstream release readiness before merge.Closes #20
Devnet E2E
76https://api.avax-dev.networkadd-auto-renewedaccepted:2eZGX56X7v67fHBvaLo7pn6N8sqHH5BwXNziCqymaVWDVo9NPMset-auto-configaccepted:2StJDneDaDZLD3vDntzqPRX5C2698tDCGsZ2RperLaFdoSJavbadd-auto-renewedaccepted:2jcpUhCU8yJBg7PKWVB5BstVBrcNbi81D3SKsouMHXTZPgkPNtset-auto-configaccepted:9XjDvMtUpsvwsSiet66CARZXcziC1es7ih81vQfV49EkcWpELweight: 2000000000000,period: 172800,autoCompoundRewardShares: 400000, and the expectedvalidatorAuthorityTests
go test ./cmd ./pkg/pchaingo test -tags devnetcompat ./pkg/pchaingo test ./...git diff --checkGOTOOLCHAIN=go1.25.9+auto go run golang.org/x/vuln/cmd/govulncheck@v1.2.0 ./...go run . validator add-auto-renewed --helpgo run . validator set-auto-config --help