CLI: governance improvements#458
Open
Bridgerz wants to merge 5 commits intoupgrade-test-infrastructurefrom
Open
CLI: governance improvements#458Bridgerz wants to merge 5 commits intoupgrade-test-infrastructurefrom
Bridgerz wants to merge 5 commits intoupgrade-test-infrastructurefrom
Conversation
`scrape_proposals` was reading `child_object.contents` from every `list_dynamic_fields` entry, but `child_object` is only populated for DynamicObject kinds (i.e. `ObjectBag`). Proposals live in a regular `Bag` (DynamicField kind, value stored inline), so the proposal type and BCS have to be read from the top-level `DynamicField.value_type` and `DynamicField.value` fields instead. Previously this surfaced as `unable to parse type ""` on any fullnode that strictly followed the field-mask contract, blocking every CLI governance command. Also make the treasury scrape skip (with a warning) any dynamic field whose child object comes back without a type, instead of hard-failing init — treasury caps aren't needed for the governance flows so partial state is fine there.
Two small UX fixes on the proposal commands: - Replace the `(y/N)` confirm prompt with `Press enter to X, or Ctrl+C to cancel`. Most of the time you want to proceed; typing `y` for every governance action is just noise. Ctrl+C still bails. - After `proposal create ...` executes successfully, print the new proposal's ID so it can be copy/pasted into the follow-up `vote` and `execute` commands without digging through the tx digest. `execute_or_simulate` now returns `Option<ExecuteTransactionResponse>` (Some on a real execute, None on dry-run or when no keypair is configured) so the create handlers can pull the `ProposalCreatedEvent` out of effects to get the proposal ID.
`proposal view <id>` now displays vote progress alongside the basic
details so you can tell at a glance whether a proposal is ready to
execute:
Creator: <creator_address>
Votes: N voter(s) — <voted_weight> of total weight <total> — QUORUM REACHED
(or <voted>/<threshold> weight, <remaining> more needed)
Threshold: <bps> (<percent>%)
Voters: <addr>...
Metadata: <k>: <v>
Scoped deliberately to the CLI side — no new fields on
`onchain::types::Proposal` and no watcher changes, so the validator's
in-memory state stays unchanged. The CLI pays a single extra
`list_dynamic_fields` call (only on `proposal view`, not `proposal list`)
to pull the full `Proposal<T>` envelope and read votes + threshold.
New `HashiClient::fetch_current_committee` exposes the cached committee
(with weights) for the quorum arithmetic; new `fetch_proposal_details`
returns a `ProposalDetails` struct with votes, quorum threshold,
creator, and metadata.
`hashi proposal vote <id> --execute` (or `-e`) now casts the vote and, if that vote pushes the proposal over its quorum threshold, chains an immediate `proposal::<module>::execute` call in the same CLI invocation. Saves the extra round-trip of running `proposal execute <id>` manually once the last validator votes. Semantics: - If quorum is not reached after the vote, print an info message and skip the execute step. No failure. - Upgrade proposals are rejected — they need the dedicated upgrade flow (execute + publish + finalize in one PTB), which a generic `<module>::execute` call can't express. - Dry-run and no-keypair paths skip the execute naturally since `execute_or_simulate` returns `None` in those cases. Uses the existing `HashiClient::build_execute_proposal_transaction` helper. The quorum check re-fetches live vote state via `fetch_proposal_details` (added in the previous commit) so it reflects the vote we just submitted.
Writes each validator's `operator_private_key` to
`<data-dir>/validators/validator_<N>.pem` (0600) at `hashi-localnet
start` time, and adds a `Validator Keys:` line to the connection
details output.
Without this, localnet-resident operator keys only live in-memory on
the running validator processes, so there's no way to point the main
`hashi` CLI at a committee-member key for `proposal create` / `vote` /
`execute`. Dumping them makes the whole governance flow drivable end
to end against localnet with:
hashi proposal \
-c .hashi/localnet/hashi-cli.toml \
--keypair .hashi/localnet/validators/validator_<N>.pem \
<subcommand>
Factors the existing funded-key dump through a shared `write_pem_to_disk`
helper so both writes share the restrictive-permissions logic.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A batch of CLI UX/ergonomics improvements for the governance (proposal) flow,
plus one correctness fix for on-chain state scraping and a small localnet
helper to make this stuff actually drivable against a local dev network.
Stacked on top of #456 — set base back to
mainonce that lands.Commits
scraper was reading
child_object.contentsfor every dynamic field,but that's only populated for
ObjectBag(DynamicObject) kinds. Forplain
Bagentries (proposals), the real payload is on theDynamicFielditself viavalue_type+value. Without this,every governance CLI command errored with
unable to parse type ""once the proposals bag had any entries.
(y/N)→Press enter to X, or Ctrl+C to cancel. Afterproposal create ...,print the new
Proposal ID: 0x…so it can be copy/pasted into thefollow-up
vote/view/executecommands.proposal view. One extralist_dynamic_fieldscall pulls the fullProposal<T>envelope anddisplays creator, voter addresses, voted-vs-threshold weight, and
whether quorum is reached. Scoped to the CLI — no new fields on
onchain::types::Proposalor changes to the watcher.--executeflag onproposal vote. Casts the vote and, ifthat vote pushes the proposal over quorum, chains an immediate
proposal::<module>::executein the same invocation. Skips silently(with an info message) when quorum isn't reached. Upgrade proposals
are rejected — they need the dedicated upgrade flow.
validator's
operator_private_keyto<data-dir>/validators/validator_<N>.pem(0600) athashi-localnet starttime. Without this, the keys only live in-memory on therunning validator processes, so there's no way to point the CLI at a
committee-member key for
proposal create/vote/executeonlocalnet.
Test plan
Tested end-to-end against a 4-validator localnet:
proposal create update-config bitcoin_confirmation_threshold u64:3prints the new proposal ID and uses the press-enter prompt.
proposal view <id>showsCreator,Votes: N voter(s) — <w> of total weight <total>,Threshold: <bps> (<pct>%), and the voter list.proposal vote <id> --executefrom the second validator prints"Quorum not reached yet (…); skipping --execute."
proposal vote <id> --executefrom the third validator prints"Quorum reached (…); executing…" and submits both the vote and the
execute transactions.
bitcoin_confirmation_thresholdupdated to3afterthe auto-execute.