Skip to content

feat(blockchain): accrue Reward Points to non-outlier validator submissions - #64

Merged
flo2517 merged 1 commit into
mainfrom
feat/issue-29-validator-rewards
Aug 6, 2026
Merged

feat(blockchain): accrue Reward Points to non-outlier validator submissions#64
flo2517 merged 1 commit into
mainfrom
feat/issue-29-validator-rewards

Conversation

@flo2517

@flo2517 flo2517 commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

Sixth slice of #29 / ADR-011 §5, completing the incentives bullet after the registry (#43), gated origins (#46), aggregation (#47), committee assignment (#48) and disputes (#49).

When a round closes, every submission that survived outlier trimming earns PointsPerAcceptedSubmission Reward Points; the trimmed high and low earn nothing for that round. Honest reporting is therefore the paying strategy, and a validator that is consistently an outlier — whether colluding or merely malfunctioning — is paid nothing, without needing a separate detection mechanism.

Trimming had to become attributable

The old trimmed_mean sorted bare scores and lost track of who submitted what, so it couldn't say who to pay. It's now trimmed(), returning the surviving submissions alongside their mean.

It sorts by (score_bps, validator) rather than score alone — with tied scores, that makes the choice of which entry gets trimmed total-ordered and therefore identical on every node, which a score-only sort did not guarantee. That's a consensus-correctness point, not a style one.

Decoupling

pallet-rewards gains accrue_points(), a non-extrinsic entry point, so it stays the only writer of RewardBalances and keeps its own overflow checking regardless of caller. Validators claim through the existing signed claim_reward path — balances are keyed by account, so providers and validators share it without a second store. The new ValidatorRewards trait keeps the two pallets decoupled, wired by the runtime's ValidatorRewardsBridge, matching the existing ReputationUpdater / ProviderInspector pattern.

RoundClosed now reports a rewarded count, so an observer can see how many submissions actually counted without replaying the trim.

Known gap (documented at the accrual site, not left implicit)

Crediting is one-way: an upheld dispute does not claw back points already accrued for that round. Clawback belongs with slashing economics, which ADR-011 explicitly leaves out of scope.

Tests (3 new, 36 in this pallet, 84 workspace-wide)

Only survivors of trimming are paid and both outliers get zero · RoundClosed's rewarded count matches the actual payouts · all-tied scores trim deterministically by validator id, paying exactly the middle three.

Verification

$ cargo fmt --all -- --check
$ SKIP_WASM_BUILD=1 cargo clippy --workspace --exclude openinfra-node --all-targets -- -D warnings
$ SKIP_WASM_BUILD=1 cargo test --workspace --exclude openinfra-node

What remains in #29

Nothing blockchain-side except slashing and VRF committee entropy, both explicitly deferred by ADR-011. The open work is the dashboard validator views and the independent validator executable that drives SolveChallenge — the latter is a new component boundary (own mTLS identity, chain signer, deployment), so it's flagged for a human decision rather than assumed.

🤖 Generated with Claude Code

…ssions

Sixth slice of #29/ADR-011 §5, completing the incentives bullet after
the registry (#43), gated origins (#46), aggregation (#47), committee
assignment (#48) and disputes (#49).

When a round closes, every submission that survived outlier trimming
earns PointsPerAcceptedSubmission Reward Points; the trimmed high and
low earn nothing for that round. Honest reporting is therefore the
paying strategy, and a validator that is consistently an outlier --
whether colluding or merely malfunctioning -- is paid nothing without
needing a separate detection mechanism.

Trimming had to become attributable to do this: the old trimmed_mean
sorted bare scores and lost track of who submitted what. It is now
trimmed(), returning the surviving submissions alongside their mean,
sorted by (score_bps, validator) rather than score alone -- with tied
scores that makes the choice of which entry gets trimmed
total-ordered and therefore identical on every node, which a
score-only sort did not guarantee.

pallet-rewards gains accrue_points(), a non-extrinsic entry point, so
it stays the only writer of RewardBalances and keeps its own overflow
checking regardless of caller. Validators claim through the existing
signed claim_reward path -- balances are keyed by account, so
providers and validators share it without a second store. The new
ValidatorRewards trait keeps the two pallets decoupled, wired by the
runtime's ValidatorRewardsBridge, matching the existing
ReputationUpdater/ProviderInspector pattern.

RoundClosed now reports a 'rewarded' count, so an observer can see how many
submissions actually counted without replaying the trim.

Known gap, documented at the accrual site rather than left implicit:
crediting is one-way -- an upheld dispute does not claw back points
already accrued for that round. Clawback belongs with slashing
economics, which ADR-011 explicitly leaves out of scope.

Adds 3 tests (36 in this pallet, 84 workspace-wide): only survivors of
trimming are paid and both outliers get zero; RoundClosed's rewarded
count matches the payouts; all-tied scores trim deterministically by
validator id, paying exactly the middle three.

Verified (blockchain/): cargo fmt --all -- --check; SKIP_WASM_BUILD=1
cargo clippy --workspace --exclude openinfra-node --all-targets --
-D warnings; SKIP_WASM_BUILD=1 cargo test --workspace --exclude
openinfra-node.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@flo2517
flo2517 merged commit 2a7987c into main Aug 6, 2026
4 checks passed
@flo2517
flo2517 deleted the feat/issue-29-validator-rewards branch August 6, 2026 11:33
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