Skip to content

Feature(pos): add validator stake delegation with delegator rewards and undelegation cooldown - #54

Open
machieke wants to merge 7 commits into
F1R3FLY-io:devfrom
machieke:feature/validator-stake-delegation
Open

Feature(pos): add validator stake delegation with delegator rewards and undelegation cooldown#54
machieke wants to merge 7 commits into
F1R3FLY-io:devfrom
machieke:feature/validator-stake-delegation

Conversation

@machieke

Copy link
Copy Markdown
Collaborator

Summary

This PR extends PoS to support delegation from external accounts to existing validators, and hardens the full delegation lifecycle:

  • Add stake delegation state and flows
  • Include delegated stake in effective validator stake used by consensus reads
  • Add on-chain delegator reward accrual + claim
  • Change undelegation to a 2-step cooldown flow (undelegate request, then completeUndelegate)
  • Update PoS tests and operator/wallet-facing docs

What changed

PoS contract (PoS.rhox)

  • Added delegation tracking (delegations, delegatedTotals) and effective-bond computation.
  • Added delegator rewards tracking (delegatorRewards) and claim path:
     - claimDelegatorRewards
     - getDelegatorRewards
  • Added pending undelegation tracking (pendingUndelegations) and cooldown completion path:
     - undelegate now creates pending withdrawal (no immediate principal transfer)
     - completeUndelegate returns principal after unlock
     - getPendingUndelegations
  • Updated epoch reward accounting to split validator/delegator rewards and treat pending liabilities correctly.
  • Preserved delegation-related state through slash/update paths.

Runtime integration (casper/src/rust/rholang/runtime.rs)

  • Runtime stake/bond queries now use effective bonds so delegated stake is reflected in on-chain stake reads.

Tests (PoSTest.rho)

  • Added/updated tests for:
     - delegation success
     - undelegation cooldown + completion
     - delegator rewards claim
     - withdraw restrictions with active delegations

Docs

  • Added full delegation design/integration doc:
     - docs/casper/POS_STAKE_DELEGATION.md
  • Updated docs/casper/README.md with delegation documentation linkage/refresh.

Behavioral impact

  • undelegate is now request-only; principal is claimed later via completeUndelegate.
  • Delegators can now receive/claim rewards on-chain.
  • Validators with active delegations cannot follow incompatible withdraw paths until delegated stake is cleared.

Validation

  • PoS contract test suite executed via:
     - cargo test -p casper --test mod pos_spec -- --nocapture
  • Docker image build succeeds for the Rust node.

Notes / follow-ups

  • Cooldown duration currently tracks epoch length (no separate configurable undelegation parameter yet).
  • Only one pending undelegation per (delegator, validator) pair at a time.
  • Delegator rewards are tracked per delegator total.

dylon added a commit that referenced this pull request Jun 15, 2026
… compose

The integration tests have been running against a STALE binary cached
on the self-hosted runner, not the binary built from this PR. Forensic
chain established from artifact `~/Downloads/logs_68905250139.zip`:

- Source on this branch (and merge ref `de70010`): `node v0.4.13`,
  `multi_parent_casper_impl.rs` is an 8-line shim, the slashing surface
  lives under `casper_engine/{snapshot,dispatch,finalization_runner,
  block_admission,…}.rs`.
- Build job DID compile our code (build log: `Compiling casper v0.1.0
  (/build/casper)`, finishes in 3m28s, `Compiling node v0.4.13`).
- Runtime binary reports `F1r3node Rust 0.4.2` and emits `tracing`
  events with `filename:"casper/src/rust/multi_parent_casper_impl.rs",
  line_number:1767` and log strings (`"Received DeployData..."`) that
  exist NOWHERE in our git history. No `casper_engine/*.rs` filename
  references at all.

Root cause:

  `system-integration/integration-tests/docker-compose.rust.yml:21`:
      image: ${F1R3FLY_RUST_IMAGE:-f1r3flyindustries/f1r3fly-rust-node:latest}

  Our CI tags the freshly-built image as
  `f1r3flyindustries/f1r3fly-rust:{amd64,latest}` (note: no `-node`
  suffix). It sets `DEFAULT_IMAGE` for `pytest`, but compose reads
  `F1R3FLY_RUST_IMAGE` — not `DEFAULT_IMAGE`. With `F1R3FLY_RUST_IMAGE`
  unset, compose falls back to its default
  `f1r3flyindustries/f1r3fly-rust-node:latest` — a DIFFERENT image
  repository name. That tag had been pinned on the self-hosted runner
  by an earlier process; compose silently used it for every PR build.

Fix:

- `Clean self-hosted runner state`: delete any stale
  `f1r3fly-rust-node:latest` so the compose default cannot resolve to
  cached state.
- `Import Docker Image`: after loading and tagging
  `f1r3flyindustries/f1r3fly-rust:{arch,latest}`, also tag the same
  image as `f1r3flyindustries/f1r3fly-rust-node:latest` so the compose
  default points at THIS build.

This unblocks the slashing PR by making the integration tests actually
exercise the code they're meant to test. All the "regressions" we've
been debugging (heartbeat-shard, cross-validator stalls, LFB freezing
at #54) were against a pre-`f0b2934` binary and may not reflect any
real defect in this PR's changes — we'll know once the next run
produces a clean baseline.

Verification after push:
- `gh run download <new_id> --name integration-logs-amd64`
- `grep 'F1r3node Rust' integration-tests.log` should show `0.4.13`.
- `grep -oE '"filename":"casper/src/rust/[^"]+\.rs"' integration-tests.log
  | sort -u` should include `casper_engine/*` paths.
@9Neechan

9Neechan commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Delegating stake permanently forks the network — validate.rs never got updated to match runtime.rs's new effective-bonds semantics

Tested this branch (built as 9neechan/rust-node:pr54-stake-delegation from feature/validator-stake-delegation @ 7a7f666b) against a 3-genesis-validator chain (1000 REV self-bond each) plus a funded non-validator account. Submitted a single delegate deploy (500 REV) and the network went from producing blocks normally to a permanent 3-way fork/halt within one block.

Timeline (namespace exp-deleg-v3-validator1-20260806-094541):

09:53:41.064 validator1: delegate deploy submitted (500 REV, self-delegation to validator1)
09:53:41.867 validator1: Creating block #23 — deploy selected, executed
09:53:43.037 validator1: Block #23 (5627f784cd...) created and added — this is the block
             that executes the delegate deploy
09:53:44.843 validator2: Creating block #24 (10853d2353...), built on top of #23
09:53:45.127 validator3: Creating block #24 (e57f0197e2...), built on top of #23
09:53:45.835 validator2: WARN "Bonds in proof of stake contract do not match block's
             bond cache." (validate.rs:1053)
             WARN "Recording invalid block e57f0197e2... for InvalidBondsCache."
             (multi_parent_casper_impl.rs:1031)

validator3 rejects in lockstep (same message, ~30ms apart). From here every subsequent block, from any proposer, is rejected — first as InvalidBondsCache, then cascading as NeglectedInvalidBlock for descendants. validator2/validator3 stop proposing entirely (~09:54:22). validator1 keeps emitting Propose finished: Success for its own new blocks every ~12–25s for the next 40+ minutes, but its own synchrony logs show why that's meaningless:

09:54:47.066 validator1: WARN "Seen 0 senders with weight 0 out of total 2000
             (0.00 out of 0.33 needed)"
             WARN "Synchrony constraint satisfied via finalized-block baseline
             (primary 0.00 < 0.33, finalized 1.00 >= 0.33)"

It's proposing into a void — no peer will ever accept a descendant of the block it already flagged invalid. No panic, no crash in any node's error log — this is a silent, permanent consensus fork.

Root cause (best guess, not 100% confirmed by reading validate.rs directly — this diff doesn't touch it)

"Bonds in proof of stake contract do not match block's bond cache" describes exactly two bond sources disagreeing:

  1. The block's cached bonds, baked in at creation time by the proposer. Per this PR's runtime.rs change, that's now getEffectiveBonds (1500 = 1000 self-bond + 500 delegated) — confirmed independently via the testbed's own observer read of FinalizedBonds, which read 1500 right after block 23.
  2. The independent re-derivation validate.rs performs when a peer validates an incoming block — per this PR's own docs (POS_STAKE_DELEGATION.md), getBonds is intentionally kept at raw self-bond semantics (1000) for backward compatibility.

I checked: this diff does not touch casper/src/rust/validate.rs or multi_parent_casper_impl.rs at all. Only PoS.rhox, runtime.rs, PoSTest.rho, and docs were changed. So it looks like the block-production path (runtime.rs) was updated to bake effective bonds into new blocks, but the block-validation path that every peer runs to independently re-check "bonds in proof of stake contract" against the block's cache was never updated to agree — it's still comparing against raw self-bonds. Since that mismatch is structural (any active delegation causes it), every block will be rejected for as long as a delegation exists, not just once.

Keeping getBonds at legacy semantics for RPC/backward-compat is a reasonable design choice on its own — the bug looks like it's specifically that validate.rs's bonds-cache check wasn't updated to use the same effective-bonds source runtime.rs now uses, so the two internal paths disagree on which figure is authoritative.

To reproduce: 3 genesis validators (1000 REV self-bond each), one funded non-validator account, submit a delegate deploy (self-delegation reproduces it too, no second validator needed as target) — watch peers immediately reject the next block as InvalidBondsCache, then everything after it as NeglectedInvalidBlock, and finalization freeze.

Full logs (all 3 validators + bootstrap + observer, plus the harness's own read of FinalizedBonds) available if useful — happy to share.

@9Neechan

9Neechan commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

@machieke Please take a look

@machieke
machieke force-pushed the feature/validator-stake-delegation branch from 7a7f666 to ee63bcd Compare August 16, 2026 02:51
@machieke

Copy link
Copy Markdown
Collaborator Author

Rebased feature/validator-stake-delegation on the latest dev.

I also addressed and validated 9Neechan’s specific concern about delegation causing peers to reject blocks with InvalidBondsCache.

The proposer and validation paths now agree on effective stake semantics for the consensus bond cache: runtime bond-cache derivation uses getEffectiveBonds, while getBonds remains raw self-bond semantics for compatibility.

Validation performed against the exact reported live scenario:

  • Clean 3-validator shard from fresh Docker volumes.
  • Three genesis validators with 1000 REV self-bond each.
  • Funded non-validator account submitted one delegate deploy for 500 REV to validator1.
  • Deploy finalized successfully.
  • Finalized PoS state after the deploy:
    • validator1 getBonds: 1000
    • validator1 getEffectiveBonds: 1500
    • validator1 delegated total: 500
  • Finality continued advancing after delegation: LFB moved from 24 at submit time to 55.
  • All validator/read-only nodes agreed on LFB.
  • No InvalidBondsCache.
  • No “Bonds in proof of stake contract do not match block's bond cache”.
  • No NeglectedInvalidBlock.
  • No invalid-block recording or consensus halt.

@machieke
machieke force-pushed the feature/validator-stake-delegation branch from ee63bcd to d5183a3 Compare August 18, 2026 09:47

@jeffrey-l-turner jeffrey-l-turner left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multi-Agent Code Review

Verdict: 🔍 Needs Review (100.0000% agreement)

Reviewed by:

Provider Model Verdict Confidence
anthropic claude-fable-5 Abstain 0
bedrock us.amazon.nova-pro-v1:0 Needs Review 0.9
openai gpt-5.6-sol Needs Review 0.94
openrouter moonshotai/kimi-k3 Needs Review 0.68
xai grok-4.5 Abstain 0

Summary

The delegation lifecycle is broadly implemented and tested, but the current design introduces significant epoch-accounting and consensus-snapshot concerns. Delegations affect both reward allocation and runtime stake reads immediately, enabling reward sniping and potentially changing consensus weights mid-epoch. The effective-bond cap also needs enforcement across every self-bond mutation path, and the unbounded per-delegator reward scan creates a recurring liveness risk. These issues should be resolved or explicitly validated against the protocol's epoch and consensus invariants before merge.

This PR implements a substantial and mostly coherent delegation lifecycle, including effective stake integration, cooldown withdrawals, reward claiming, and slashing updates. However, the economic/security surface is large: pending undelegations are slashable during cooldown, cooldown is hardwired to epoch length, and reward distribution uses truncation-heavy integer math plus expensive full-map folds each epoch. I would not merge without protocol-level review of slashing semantics for pending withdrawals, explicit undelegation cooldown configuration, and performance hardening for reward/delegation accounting.

The code introduces significant changes to support delegation in the PoS contract. While the overall approach seems sound, there are some potential security and logic issues that need to be addressed, along with minor style improvements and documentation suggestions. A thorough review and testing are recommended before merging.


Issues Found

🔴 Critical: Pending undelegation makes stake instantly slashable and may be griefable (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 652)

The contract removes delegated stake from effective bonds immediately at request time, while leaving principal locked in PoS vault as pending undelegations. If a slash happens during cooldown, both active delegation and pending undelegation are confiscated (slash transfers valBond + valDelegated + slashedPending). This is economically consistent but creates a hard griefing path: any invalidBlocks entry can burn pending withdrawals right before users claim them. In this diff, tests even assert that completeUndelegate cannot claim slashed pending stake, which confirms funds can be permanently seized during cooldown. Given slash authority may be broad in some environments, this materially increases loss surface for delegators.

Suggestion:

Add explicit governance/authorization controls around slash-triggered confiscation of pending undelegations (or delay when pending becomes slashable), and document threat model. Consider separate escrow accounting with delayed confiscation eligibility or dispute window.
🟠 Major: rewardsInfo now performs multiple full-map folds on every call (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 308)

rewardsInfo computes effectiveBonds fold, withdrawers fold, committedRewards fold, delegatorRewards fold, and pendingUndelegations nested fold. These are O(total state) and called by getCurrentEpochRewards, which itself folds all bonds. On large validator/delegator sets, closeBlock and read paths become expensive.

Suggestion:

Maintain running aggregates in state (totalBonds, totalCommittedRewards, totalDelegatorRewards, totalPendingUndelegations) updated incrementally on each mutating operation.
🟠 Major: Unbounded delegation maps create recurring closeBlock work (Reported by: openai)

File: casper/src/main/resources/PoS.rhox (line 315)

Delegation accepts any positive amount and imposes no minimum delegation or limit on delegators per validator. Epoch processing then scans the complete nested delegations map to calculate rewards, and rewardsInfo also scans effective bonds and every pending undelegation. An attacker can create many one-unit delegations, paying the creation cost once while imposing repeated system-contract work on every subsequent closeBlock. This creates state-growth and consensus-liveness risk.

Suggestion:

Set a meaningful minimum delegation and bounded delegator count, or use an accumulated reward-per-share index so epoch processing is O(number of validators) and each delegator settles rewards only when delegating, undelegating, or claiming.
🟠 Major: Potential integer overflow in delegation amount check (Reported by: bedrock)

File: casper/src/main/resources/PoS.rhox (line 355)

The code checks if the delegation amount would exceed the validator's maximum effective bond by comparing sums of integers. In languages like Rholang, integer overflow can occur if the sum exceeds the maximum representable integer value. This could lead to unexpected behavior or security vulnerabilities. Consider using arbitrary precision arithmetic or adding explicit overflow checks.

Suggestion:

Use arbitrary precision arithmetic or add explicit overflow checks to prevent integer overflow.
🟠 Major: computeDelegatorRewardDeltas scales O(delegations x validators) (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 360)

Reward distribution iterates every delegator and every validator mapping to compute shares each epoch. With many delegators this becomes quadratic-ish and can blow up block processing cost.

Suggestion:

Store per-validator delegator lists or aggregate share indexes to compute rewards per validator with direct lookups. Consider checkpoint/lazy-claim accounting patterns.
🟠 Major: Reward accounting is vulnerable to truncation drift and silent loss (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 459)

Validator/delegator split uses integer division in multiple places (reward * selfBond / effectiveBond and validatorReward * delegatedAmount / effectiveBond). Over many delegators and epochs, truncation can leave dust unallocated in vault while total liabilities grow. Because getCurrentEpochRewards subtracts totalDelegatorRewards and totalPendingUndelegations but not rounding dust explicitly, accounting may drift and either underpay future rewards or leave unclaimable residue.

Suggestion:

Add explicit dust bucket in state (e.g. rewardRemainder) or deterministic largest-remainder allocation. Add property tests comparing vault balance vs sum of all claimable liabilities.
🟠 Major: Incomplete handling of slashed pending undelegations (Reported by: bedrock)

File: casper/src/main/resources/PoS.rhox (line 465)

When a validator is slashed, the code removes the validator from the active set and adjusts the bonds, but it does not explicitly handle pending undelegations. This could lead to orphaned pending undelegation entries that are no longer associated with an active validator.

Suggestion:

Ensure that pending undelegations are properly cleaned up when a validator is slashed.
🟠 Major: Delegator identity derived from deployerId may collide across auth contexts (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 574)

delegate/undelegate/claim use getUser!(deployerId) and vaultAddressOps!(fromDeployerId) for payout. If deployerId mapping to public key/vault is not strictly one-to-one across all auth modes, reward/principal claims could be misrouted or blocked. This is especially important for contract-controlled deployers or wrapper wallets.

Suggestion:

Document identity assumptions and add negative tests for alternate auth paths. If supported, separate delegation owner key from payout destination.
🟠 Major: Delegation can capture a full epoch of rewards immediately (Reported by: openai)

File: casper/src/main/resources/PoS.rhox (line 576)

The delegate operation updates delegations and delegatedTotals immediately, while closeBlock calculates the entire epoch's rewards from the state present at close time. A delegator can therefore delegate immediately before closeBlock and receive a proportional share of fees accumulated before their stake was present. Similarly, undelegating immediately before closeBlock forfeits rewards for stake that was present for most of the epoch. This enables epoch-boundary reward sniping and unfairly dilutes long-lived delegators and validators.

Suggestion:

Activate delegation and undelegation changes only at epoch boundaries, or maintain epoch stake snapshots/reward-per-share indices with per-delegation reward debt so rewards are based on the duration for which stake was active.
🟠 Major: Maximum effective bond cap is enforced only when delegating (Reported by: openai)

File: casper/src/main/resources/PoS.rhox (line 590)

delegate checks selfBond + delegatedTotal + amount against maximumBond, but the invariant also has to be enforced by every path that can increase a validator's self-bond. The existing bond/update processing shown elsewhere is not updated here to account for delegatedTotals. If a validator can increase or replace its self-bond after receiving delegation, it can push its effective bond above maximumBond despite the new check.

Suggestion:

Centralize the effective-bond invariant and call it from delegate, bond, rebond, and bond-update processing. Add a test that delegates up to the cap and then attempts to increase the validator's self-bond.
🟠 Major: Maximum-bond check uses stale pre-update components and can reject/accept incorrectly (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 601)

delegate computes checks from selfBond and delegatedTotal before deposit and state mutation, but all checks are against current values only. In concurrent execution, other delegations could land between read and commit and push validator over maximumBond. runMVar serializes state mutation, but the checks occur inside same serialized block with stale snapshots from that block only; that part is okay. The subtle issue is that cap logic uses selfBond > maximumBond and delegatedTotal > maximumBond - selfBond style checks, which allows weird behavior when selfBond already equals maximumBond but delegatedTotal nonzero is impossible by construction. More importantly, repeated delegations from same delegator are allowed but pending undelegations are restricted to one per pair, creating asymmetry that can trap users.

Suggestion:

Either allow multiple pending undelegation entries per pair (list/queue) or enforce one active delegation lifecycle per pair in delegate too. Add invariants tests around concurrent multi-delegation and repeated delegate/undelegate cycles.
🟠 Major: Cooldown tied directly to epochLength is inflexible and can be very short/long (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 700)

undelegate sets unlockBlock = blockNumber + $$epochLength$$. The PR notes acknowledge there is no separate configurable undelegation parameter. This creates protocol risk because undelegation safety depends on epoch cadence changes and can misalign with quarantineLength and validator withdrawal timing. It also means any governance change to epoch length implicitly changes undelegation risk windows.

Suggestion:

Introduce explicit undelegationCooldownBlocks parameter (or reuse quarantineLength deliberately) and wire it through config + tests + docs.
🟠 Major: Consensus-visible stake changes immediately during an epoch (Reported by: openai)

File: casper/src/rust/rholang/runtime.rs (line 1375)

Runtime bond reads now return getEffectiveBonds, and delegate/undelegate mutate delegatedTotals immediately rather than through the epoch transition. If this runtime query supplies consensus or fork-choice weights, a transaction can alter voting weight in the middle of an epoch even though active validator selection is only refreshed by closeBlock. Mid-epoch stake changes can invalidate assumptions that validator weights are fixed for a consensus epoch and may cause different protocol components to use inconsistent stake snapshots.

Suggestion:

Confirm that all consensus consumers use a single epoch snapshot. Prefer maintaining current-epoch and next-epoch effective bond maps, applying delegation changes at closeBlock, and having runtime consensus reads return only the immutable current-epoch map.
🟡 Minor: Inconsistent indentation (Reported by: bedrock)

File: casper/src/main/resources/PoS.rhox (line 170)

The code has inconsistent indentation, which can make it harder to read and maintain. Ensure consistent indentation throughout the file.

Suggestion:

Apply consistent indentation (e.g., 4 spaces) throughout the file.
🟡 Minor: Large nested helper contracts reduce readability and auditability (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 300)

New helpers (removeDelegationsForValidator, stripPendingUndelegationsForValidator, computeDelegatorRewardDeltas) are deeply nested with repeated map updates and inline branching. This is hard to audit and easy to regress.

Suggestion:

Refactor into smaller pure map utilities with unit-style tests per helper and invariant assertions.
🟡 Minor: Reward weighting truncates delegated stake in minimumBond-sized steps (Reported by: openai)

File: casper/src/main/resources/PoS.rhox (line 397)

getCurrentEpochRewards computes validator weight with bonds / minimumBond and activeBonds / minimumBond. Delegation amounts are unrestricted, so delegated stake below the next minimumBond boundary can increase consensus weight without increasing reward weight at all, while crossing the boundary causes a discontinuous jump. The later validator/delegator split uses exact effectiveBond values, producing inconsistent stake weighting between reward allocation and reward ownership.

Suggestion:

Use an overflow-safe multiply-then-divide implementation with exact effective bonds, such as pool * bond / activeBonds using arbitrary precision or checked arithmetic, rather than dividing each bond by minimumBond first.
🟡 Minor: completeUndelegate transfers before state cleanup (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 728)

The method transfers funds first and only then deletes pending undelegation. If transfer succeeds but subsequent state update fails unexpectedly, user could potentially double-claim. Rholang execution model may make this atomic in practice, but the code ordering increases risk and complicates reasoning.

Suggestion:

If platform semantics allow, mark claim as consumed before external transfer or add reentrancy guard / claim lock pattern.
🟡 Minor: Slash now depends on stale invalidBlocks mapping hash-to-validator without binding block context (Reported by: openrouter)

File: casper/src/main/resources/PoS.rhox (line 926)

Slash uses invalidBlocks.get(blockHash) and then slashes validator globally, including delegations and pending undelegations. If hash collisions or stale mappings exist, this can punish the wrong validator set. The diff doesn’t add additional validation of epoch/context for the invalid block.

Suggestion:

Bind invalidBlocks entries to (validator, blockHeight/epoch) and enforce slashing only within a bounded evidence window.
🟡 Minor: Runtime now reads effective bonds but method name still generic (Reported by: openrouter)

File: casper/src/rust/rholang/runtime.rs (line 1375)

Runtime stake query switched from getBonds to getEffectiveBonds, but any downstream consumers expecting raw self-bonds may silently change behavior. This is likely intended but should be flagged in API/changelog.

Suggestion:

Add explicit runtime API naming or versioned method to distinguish self-bond vs effective-bond consumers.
🟡 Minor: Critical economic parameters need explicit operator guidance (Reported by: openrouter)

File: docs/casper/POS_STAKE_DELEGATION.md (line 1)

Docs mention delegation lifecycle but should explicitly call out: cooldown source (epochLength), one pending undelegation per pair, slash exposure during cooldown, reward rounding behavior, and max effective bond checks.

Suggestion:

Add an operator-facing risk table and migration notes for validators/delegators.
⚪ Suggestion: Add comments for complex logic (Reported by: bedrock)

File: casper/src/main/resources/PoS.rhox (line 170)

The code contains complex logic for handling delegations, undelegations, and rewards. Adding comments to explain the purpose and behavior of each function and contract would improve readability and maintainability.

Suggestion:

Add descriptive comments to explain the purpose and behavior of each function and contract.
---
View individual reviewer assessments

anthropic (claude-fable-5)

ABSTAIN: invalid_request_error: Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.

bedrock (us.amazon.nova-pro-v1:0)

The code introduces significant changes to support delegation in the PoS contract. While the overall approach seems sound, there are some potential security and logic issues that need to be addressed, along with minor style improvements and documentation suggestions. A thorough review and testing are recommended before merging.

openai (gpt-5.6-sol)

The delegation lifecycle is broadly implemented and tested, but the current design introduces significant epoch-accounting and consensus-snapshot concerns. Delegations affect both reward allocation and runtime stake reads immediately, enabling reward sniping and potentially changing consensus weights mid-epoch. The effective-bond cap also needs enforcement across every self-bond mutation path, and the unbounded per-delegator reward scan creates a recurring liveness risk. These issues should be resolved or explicitly validated against the protocol's epoch and consensus invariants before merge.

openrouter (moonshotai/kimi-k3)

This PR implements a substantial and mostly coherent delegation lifecycle, including effective stake integration, cooldown withdrawals, reward claiming, and slashing updates. However, the economic/security surface is large: pending undelegations are slashable during cooldown, cooldown is hardwired to epoch length, and reward distribution uses truncation-heavy integer math plus expensive full-map folds each epoch. I would not merge without protocol-level review of slashing semantics for pending withdrawals, explicit undelegation cooldown configuration, and performance hardening for reward/delegation accounting.

xai (grok-4.5)

ABSTAIN: request timed out after 115s (model: grok-4.5, tools: false)


Generated by Multi-Agent Review System

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.

3 participants