Skip to content

feat: dispute and resolve-dispute handling (ADR-013 slice 5) - #86

Merged
flo2517 merged 1 commit into
mainfrom
feat/issue-78-dispute-handling
Aug 7, 2026
Merged

feat: dispute and resolve-dispute handling (ADR-013 slice 5)#86
flo2517 merged 1 commit into
mainfrom
feat/issue-78-dispute-handling

Conversation

@flo2517

@flo2517 flo2517 commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Last slice of ADR-013/#78: pallet-network-validator's dispute_round and resolve_dispute, split across two binaries by necessity, not choice — dispute_round is directly signed by the disputing validator's own account, but resolve_dispute is SuspensionOrigin-gated (EnsureRoot in this runtime), so only the Control Plane's own bridge/sudo account can call it.

Closes #78 — all five ADR-013 slices are now implemented.

internal/blockchainbridge/networkvalidatorregistrar.go

  • Registrar.DisputeRound (call_index 7): directly signed, fixed-width encoding matching submit_evidence/close_round's established shape. Deliberately a manual CLI action, never triggered by the challenge loop itself.
  • Registrar.SubmitSudo + Registrar.ResolveDispute (call_index 8): SubmitSudo is SubmitDirect's sibling for SuspensionOrigin/EnsureRoot-gated calls, mirroring the exact sudo-wrapping EnsureActive/EnsureLeaseActive already use inline.
  • ParseScoreDimension (String's inverse) for the two new CLI subcommands.

cmd/networkvalidator

New dispute <provider-hex> <round> <dimension> subcommand, directly signed like the rest of this binary's calls.

cmd/controlplane-admin

New resolve-dispute <provider-hex> <round> <dimension> <uphold|reject> subcommand — restructured run() to dispatch on the subcommand before connecting to any credential store, since this is the first subcommand needing chain access instead of Postgres.

Known, explicitly out-of-scope gap

The pallet's dispute_round also authorizes the scored provider to dispute, not only a committee validator — real on-chain but unreachable by any tool in this MVP, since a provider has no independent chain-signing path (AGENTS.md: the Provider Agent never talks to the chain directly). A future Control-Plane-proxied dispute_round_for, mirroring register_provider_for's already-accepted delegation pattern, is the natural fix — not attempted here.

Tests

encodeDisputeRoundCall/encodeResolveDisputeCall byte-layout tests matching the established pattern, including an explicit check that uphold/reject differ by exactly one trailing byte.

Verified

gofmt -l .; go build ./...; go vet ./...; go test ./... -count=1 — full workspace green. Both new CLI paths smoke-tested against the live local dev chain: argument validation rejects malformed input correctly, and a well-formed call reaches the RPC submission layer and fails with the same already-documented "unreachable" wasm trap as every other pallet-network-validator extrinsic in this sandbox (the deployed chain's wasm predates this pallet — not a new issue).

#29

Stays open — its full acceptance criteria include dashboard validator views (challenge outcomes, score history), tracked in #76, plus two gaps ADR-011 itself already flagged as deferred (prohibited-related-worker detection beyond self-scoring, and slashing economics). Will comment on #29 with the precise remaining breakdown.

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

Last slice of ADR-013/#78: pallet-network-validator's dispute_round and
resolve_dispute, split across two binaries by necessity, not choice --
dispute_round is directly signed by the disputing validator's own
account, but resolve_dispute is SuspensionOrigin-gated (EnsureRoot in
this runtime), so only the Control Plane's own bridge/sudo account can
call it.

internal/blockchainbridge/networkvalidatorregistrar.go:

- Registrar.DisputeRound (call_index 7): directly signed, fixed-width
  encoding matching submit_evidence/close_round's established shape.
  Deliberately a manual CLI action, never triggered by the challenge
  loop itself -- ADR-013 §9's own reasoning: a validator disputing
  algorithmically on every disagreement would just move the trust
  problem, not solve it.
- Registrar.SubmitSudo + Registrar.ResolveDispute (call_index 8):
  SubmitSudo is SubmitDirect's sibling for SuspensionOrigin/EnsureRoot
  -gated calls, mirroring the exact sudo-wrapping EnsureActive/
  EnsureLeaseActive already use inline, just factored out. uphold
  encodes as a single SCALE bool byte (0/1).
- ParseScoreDimension (String's inverse) for the two new CLI
  subcommands' dimension argument.

cmd/networkvalidator: new `dispute <provider-hex> <round> <dimension>`
subcommand, directly signed by the validator's own key like
register/status/request-exit/withdraw/run.

cmd/controlplane-admin: new `resolve-dispute <provider-hex> <round>
<dimension> <uphold|reject>` subcommand -- restructured run() to
dispatch on the subcommand before connecting to any credential store,
since this is the first subcommand that needs chain access instead of
Postgres (user/API-key commands never touch the chain; resolve-dispute
never touches Postgres).

Known, explicitly out-of-scope gap (documented in ADR-013's
implementation note and in DisputeRound's doc comment): the pallet's
dispute_round also authorizes the scored provider to dispute, not only
a committee validator -- that path is real on-chain but not reachable
by any tool in this MVP, since a provider has no independent
chain-signing path (AGENTS.md: the Provider Agent never talks to the
chain directly). A future Control-Plane-proxied dispute_round_for,
mirroring register_provider_for's already-accepted delegation pattern,
is the natural way to close that gap.

Tests: encodeDisputeRoundCall/encodeResolveDisputeCall byte-layout
tests matching the established encodeSubmitEvidenceCall/
encodeCloseRoundCall pattern, including an explicit check that
uphold/reject differ by exactly one trailing byte.

Verified (control-plane/): gofmt -l .; go build ./...; go vet ./...;
go test ./... -count=1 -- full workspace green. Both new CLI paths
smoke-tested against the live local dev chain: argument validation
rejects malformed input correctly, and a well-formed call reaches the
RPC submission layer and fails with the same already-documented
"unreachable" wasm trap as every other pallet-network-validator
extrinsic in this sandbox (the deployed chain's wasm predates this
pallet -- see PR #79's description for the original finding via
state_getMetadata; not a new issue, not a regression).

Closes #78 (all five ADR-013 slices now implemented). #29 remains open
only for whatever broader scope beyond the daemon itself it still
represents -- worth a fresh look now that the daemon is complete.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@flo2517
flo2517 merged commit b83a7b7 into main Aug 7, 2026
4 checks passed
@flo2517
flo2517 deleted the feat/issue-78-dispute-handling branch August 7, 2026 10:47
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.

Network Validator daemon: endpoint discovery, allowlist push, challenge loop, disputes (remainder of #29 / ADR-013)

2 participants