Skip to content

IIP-59: cover block reward + snapshot-based voter list#73

Open
envestcc wants to merge 1 commit into
iotexproject:masterfrom
envestcc:iip-59-block-epoch-amendment
Open

IIP-59: cover block reward + snapshot-based voter list#73
envestcc wants to merge 1 commit into
iotexproject:masterfrom
envestcc:iip-59-block-epoch-amendment

Conversation

@envestcc

@envestcc envestcc commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

Amends IIP-59 so protocol-native distribution matches the two reward streams Hermes previously distributed — block reward + epoch reward — preserving voter net income at Hermes-era levels for the same commission rate. Foundation Bonus is explicitly out of scope (already zero on mainnet).

This is a spec-only change (touches iip-59.md). No code in this PR.

What changed

Specification (§3 split into 3.1–3.4)

  • §3.1 Block reward accumulation. GrantBlockReward routes to a per-delegate pending pool when CommissionRate > 0. O(1) per block: one state read + one state write. No per-voter work at block time.
  • §3.2 Epoch reward split. GrantEpochReward drains each delegate's pending pool and folds it into the single per-epoch voter distribution call. Total voter income = (epochReward + Σ blockReward) × (1 − commission).
  • §3.3 Orphan drain. A trailing pass at the end of GrantEpochReward drains pending pools for delegates that produced blocks but exited top-N mid-epoch. Bounded by candidate count (~100).
  • §3.4 Snapshot semantics. Both commission rate and per-voter weight list are frozen at the previous epoch's PutPollResult; distribution reads the frozen snapshot, not the live staking view. Voters get a ~1.5-epoch reaction window on rate changes.

Skeletons (§7)

  • §7.3 GrantBlockReward — pending pool path + legacy path.
  • §7.4 GrantEpochReward — fold pending, distributeToVoters reading the snapshot, orphan drain.
  • §7.5 Voter weight snapshot — per-candidate blob (21-byte key, sorted-by-voter proto), incremental write with byte-equality skip, DelState when a candidate's voter list empties.
  • §7.6 GetActiveBucketsByCandidate marked as no longer needed — the reward path reads only the snapshot.

Supporting sections

  • Migration: Hermes-era rates map 1:1 (no upward adjustment needed to compensate for block reward).
  • Rationale: added "Why fold block reward instead of distributing per block" — O(1) per block, one deterministic epoch distribution, identical voter economics.
  • Performance: sub-ms per-block overhead; per-epoch overhead unchanged from the epoch-only design.
  • Backwards Compatibility: new pending pool + snapshot state, both persisted (restart-safe).
  • Security Considerations: pending pool drained exactly once per epoch; snapshot determinism invariant (sorted encoding → byte-identical blobs) called out.
  • Test Cases: three new cases (add iip-5 #9 block reward folding, iip 6: exclude delegate nodes with lower than expected version #10 non-top-N pending drain, add iip 7 #11 snapshot reaction window).

Test plan

  • Editorial review of §3.1–3.4 and §7.3–7.6 for internal consistency
  • Confirm the Foundation Bonus exclusion is acceptable to reviewers (proposal explicitly defers a future proposal for that stream)
  • Cross-check the 1:1 rate mapping claim in §5 against Hermes v1 fee schedule
  • Verify §3.3 orphan drain bound (~100 candidates) matches current mainnet candidate count

🤖 Generated with Claude Code

Amend IIP-59 so protocol-native distribution matches the two reward
streams Hermes previously distributed (block + epoch), preserving
voter net income at Hermes-era levels for the same commission rate.
Foundation Bonus is explicitly out of scope (already zero on mainnet).

Specification changes
- Simple Summary / Abstract rewritten for dual stream.
- New §3.1: block reward accumulates into a per-delegate pending pool
  during the epoch (O(1) per block, no per-voter work).
- New §3.2: GrantEpochReward drains the pending pool and folds it
  into the single per-epoch voter distribution.
- New §3.3: trailing drain for delegates that produced blocks but
  exited top-N before epoch end — bounded by candidate count (~100).
- New §3.4: both commission rate and per-voter weight list are
  frozen at the previous epoch's PutPollResult; distribution reads
  from the frozen snapshot, not the live staking view. Gives voters
  a ~1.5-epoch reaction window on rate changes.

Skeletons and supporting sections
- §7.3 GrantBlockReward: route to pending pool when feature flag on
  and CommissionRate > 0; legacy credit path otherwise.
- §7.4 GrantEpochReward: fold pending, call distributeToVoters
  reading the snapshot, then run orphan drain.
- §7.5 Voter weight snapshot: per-candidate blob (21-byte key,
  sorted-by-voter proto), incremental write with byte-equality skip,
  DelState when voter list empties. Called from PutPollResult.
- §7.6 GetActiveBucketsByCandidate marked as no longer needed —
  reward path reads only the snapshot.

Rationale / Migration / Perf / Backwards Compatibility / Security /
Test Cases updated for the dual-stream design: rate mapping from
Hermes era is 1:1, per-block overhead is sub-millisecond, the
snapshot determinism invariant (sorted encoding → byte-identical
blobs) is called out, and three new test cases cover block-reward
folding, non-top-N pending drain, and the snapshot reaction window.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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.

1 participant