Skip to content

feat(rewarding): add DelegateDistributed batched log encoder (IIP-59 PR 4.7)#4923

Draft
envestcc wants to merge 1 commit into
iip-59/pr4.6-autodeposit-bridgefrom
iip-59/pr4.7-delegatedistributed-log
Draft

feat(rewarding): add DelegateDistributed batched log encoder (IIP-59 PR 4.7)#4923
envestcc wants to merge 1 commit into
iip-59/pr4.6-autodeposit-bridgefrom
iip-59/pr4.7-delegatedistributed-log

Conversation

@envestcc

Copy link
Copy Markdown
Member

Summary

Adds the encoder primitives for IIP-59 §3.2's per-delegate batched DelegateDistributed receipt log. distributeToVoters (PR 3', upcoming) will call Pack once per delegate at epoch close and wrap the returned (topics, data) into an *action.Log.

Batching keeps total epoch-last-block logs bounded by |topDelegates| + |orphanDrain| (~200) regardless of voter count, avoiding receipt-trie bloat under load (top 100 delegates × ~2k voters each).

Structural sibling of PR 4.5 (`delegateprofile`) and PR 4.6 (`autodeposit`) — same minimal-ABI package shape, same discipline about where the seam lands.

What lands

New package `action/protocol/rewarding/distributedlog`

  • `Pack(EventArgs) → (action.Topics, []byte, error)` produces the exact wire layout an EVM-emitted `DelegateDistributed` event would produce, so `eth_getLogs` clients (and PR Try again #45's off-chain verifier) decode with stock `ethers.js` / `web3.py` against the same ABI.
  • `EventArgs` mirrors §3.2's Solidity signature field-for-field: `Epoch(indexed)` + `Delegate(indexed)` + `RewardAddr` + `TotalCommission` + `TotalVoterPool` + `SnapshotHash` + `Voters[]` + `Amounts[]` + `Routings[]`.
  • `Routings[]` reuses `autodeposit.Route` verbatim — PR 4.6's enum is the single source of truth for the credit/compound wire encoding.
  • `SnapshotHash(voters, weights)` computes the `bytes32` digest of a delegate's frozen voter list, domain-separated (`iip59.delegatedistributed.snapshot.v1`) so verifiers can pin the exact bytes and it cannot collide with same-layout hashes from other contexts.

Semantics

Hard-error on caller-side wiring bugs:

Condition Error
nil `Delegate` / `RewardAddr` / any `Voters[i]` `ErrNilAddress`
nil `TotalCommission` / `TotalVoterPool` / any `Amounts[i]` `ErrNilBigInt`
`len(Voters) != len(Amounts) != len(Routings)` `ErrParallelArrayLengthMismatch`

The encoder is not a consensus path, so there is no per-item to degrade — loud failure surfaces PR 3' bugs. Empty voter list is NOT an error: a delegate with zero voters still emits a log for observability, per §3.2's "one log per delegate".

Explicit non-goals

  • *No `action.Log` construction. Block context (height, action hash, protocol address) belongs to PR 3'. Mirrors PR 4.5 / 4.6 seams.
  • No event dispatch / subscription. Encoder, not bus.
  • No proto message. The event is ABI-encoded so decode-time behaviour is identical to a Solidity-emitted event.
  • No orphan-drain log variant. §3.3's orphan drain emits the same shape; one encoder covers both call sites.
  • No `foundationBonus` field. §3.5 leaves foundation bonus out of scope.

Test plan

  • `go build ./...` clean
  • `gofmt -l` clean on touched files
  • `go vet ./action/protocol/rewarding/distributedlog/...` clean
  • `go test ./action/protocol/rewarding/distributedlog/... -count=1 -v` — 15 tests, all green
  • Regression `go test ./action/protocol/rewarding/... ./action/...` — 1104 tests across 31 packages, all green

Test coverage: happy path, zero voters, both parallel-length mismatch variants, nil-field variants (delegate/reward/commission/pool/per-voter address/per-voter amount), selector pin (golden 32 bytes so external verifiers can hard-code the same signature hash), full byte-level round-trip via the same ABI, and `SnapshotHash` determinism + empty-list constant + truncation semantics.

Stack

Branched on top of `iip-59/pr4.6-autodeposit-bridge` (#4922) because this PR imports `autodeposit.Route` at the Go-module level. Once PR 4.6 merges to `master`, the base of this PR can be flipped to `master` without conflict.

🤖 Generated with Claude Code

…PR 4.7)

Introduces the encoder primitives for IIP-59 §3.2's per-delegate batched
receipt log. distributeToVoters (PR 3', upcoming) calls Pack once per
delegate at epoch close and wraps the returned (topics, data) into an
*action.Log; the batching keeps total epoch-last-block logs bounded by
|topDelegates| + |orphanDrain| (~200) regardless of voter count, avoiding
receipt-trie bloat under load (top 100 delegates × ~2k voters each).

New package action/protocol/rewarding/distributedlog:

- Pack(EventArgs) → (action.Topics, []byte, error) encodes the event in
  the exact wire layout an EVM-emitted DelegateDistributed would produce,
  so eth_getLogs clients (and PR #45's off-chain verifier) can decode with
  stock ethers.js / web3.py against the same ABI.

- EventArgs mirrors §3.2's Solidity signature field-for-field:
  Epoch(indexed) + Delegate(indexed) + RewardAddr + TotalCommission +
  TotalVoterPool + SnapshotHash + Voters[] + Amounts[] + Routings[].

- Routings[] reuses autodeposit.Route verbatim — PR 4.6's enum is the
  single source of truth for the credit/compound wire encoding.

- SnapshotHash(voters, weights) computes the bytes32 digest of a
  delegate's frozen voter list, domain-separated
  ("iip59.delegatedistributed.snapshot.v1") so verifiers can pin the
  exact bytes and the value cannot collide with same-layout hashes from
  other contexts. Empty-list case is well-defined and asserted.

- Hard-errors on caller-side wiring bugs (nil address, nil *big.Int,
  parallel-array length mismatch) — the encoder is not a consensus path
  so there is no per-item to degrade; loud failure surfaces PR 3' bugs.
  Empty voter list is NOT an error: a delegate with zero voters still
  emits a log for observability, per §3.2's "one log per delegate".

- Structurally mirrors PR 4.5's delegateprofile.Bridge and PR 4.6's
  autodeposit.Bridge minimal-ABI package shape. Deliberately does NOT
  construct *action.Log; block context (height, action hash, protocol
  address) belongs to PR 3', keeping this package unit-testable in
  isolation.

15 tests cover: happy path, zero voters, both parallel-length mismatches,
nil-field variants (delegate/reward/commission/pool/per-voter),
selector-pin (golden 32 bytes for verifier stability), full byte-level
round-trip via the same ABI, and SnapshotHash determinism / empty-list
constant / truncation semantics.
@sonarqubecloud

Copy link
Copy Markdown

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