Skip to content

Commit 8e1097a

Browse files
committed
docs(staking): add test coverage section, sync with contract
Move PR test summary into STAKING.md. Document missing errors, freeze behavior, and Redistribution penalty integration. Remove PR_TESTS doc.
1 parent 4345fdb commit 8e1097a

2 files changed

Lines changed: 52 additions & 154 deletions

File tree

docs/PR_TESTS_new_staking.md

Lines changed: 0 additions & 150 deletions
This file was deleted.

docs/STAKING.md

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,14 @@ First stake for an address (no existing committed stake with balance).
7979

8080
- Pulls `amount` of BZZ via `transferFrom`
8181
- Requires `amount >= MIN_STAKE * 2**height`
82+
- Reverts `AlreadyStaked()` if the address already has committed stake with balance
83+
- Reverts `StakingHeightTooLarge()` if `height > MAX_STAKING_HEIGHT`
84+
- Returns `effectiveFromRound` (same value as in `DepositCreated`)
8285
- Emits `DepositCreated`
8386

8487
#### addTokens(amount)
8588

86-
Adds BZZ to an existing stake (queued).
89+
Adds BZZ to an existing stake (queued). Reverts `InvalidAmount()` if `amount == 0`.
8790

8891
#### changeOverlay(setNonce)
8992

@@ -115,10 +118,13 @@ When contract is **paused**: returns active balance plus amounts from queued `Cr
115118

116119
#### freezeDeposit(owner, time)
117120

121+
Requires `REDISTRIBUTOR_ROLE` and `whenNotPaused`.
122+
118123
- Extends `freezeUntilBlock` to at least `block.number + time` (monotonic — never shortened)
119-
- Calls `_applyReadyUpdates` first: a **matured** withdrawal at queue head on an **unfrozen** account can pay out in the same tx before the new freeze applies
124+
- If the account has no stake and no queue: records account-level freeze only and emits `AccountFreezeExtended`
125+
- Otherwise calls `_applyReadyUpdates` first: a **matured** withdrawal at queue head on an **unfrozen** account can pay out in the same tx before the new freeze applies
120126
- While frozen: `nodeEffectiveStake` is 0; further due withdrawals are blocked
121-
- Emits `StakeFrozen` when committed stake exists; otherwise `AccountFreezeExtended` for account-only freeze
127+
- Emits `StakeFrozen` when committed stake remains after applying ready updates
122128

123129
### Admin functions
124130

@@ -239,23 +245,31 @@ stakeRegistry.freezeDeposit(node, durationBlocks);
239245

240246
`Redistribution` reads `overlayOfAddress`, `heightOfAddress`, and `nodeEffectiveStake` (and lookahead variants for eligibility). Commit requires `_stake != 0`. Stake density in winner selection uses the stake recorded at commit time.
241247

248+
On claim, `Redistribution` calls `freezeDeposit` on non-revealers and on revealers whose hash/depth disagrees with the selected truth (subject to `penaltyRandomFactor`). Freeze duration scales with `ROUND_LENGTH` and truth depth.
249+
242250
Price oracle affects **postage** economics only, not stake effective balance.
243251

244-
## Errors (selected)
252+
## Errors
245253

246254
```solidity
247255
error BelowMinimumStake(uint256 have, uint256 need);
248256
error NotStaked();
249257
error AlreadyStaked();
258+
error InvalidAmount();
250259
error FrozenWithdrawal();
251260
error UpdateQueueFull(uint256 queuedCount, uint256 limit);
252261
error QueueClosed();
253262
error OnlyRedistributor();
254263
error InvalidWithdrawalAmount(WithdrawalAmountIssue reason);
255264
error OverlayUnchanged();
256265
error HeightDecreaseNotAllowed();
266+
error StakingHeightTooLarge(uint8 height, uint8 maxHeight);
267+
error InvalidWaitConfiguration(uint64 waitBase, uint64 waitOverlayChange, uint64 waitWithdrawal);
268+
error TransferFailed();
257269
```
258270

271+
Penalties are **freeze-only**; there is no slash path on `StakeRegistry`.
272+
259273
## Security and integration notes
260274

261275
1. **Preview vs storage**: View functions include matured queue state; Bee must use the same semantics as `commit`/`reveal` verification.
@@ -267,3 +281,37 @@ error HeightDecreaseNotAllowed();
267281

268282
- **Token**: ERC20 BZZ (`bzzToken`)
269283
- **Redistribution**: Commit/reveal game; holds `REDISTRIBUTOR_ROLE` for penalties
284+
285+
## Test coverage
286+
287+
Hardhat suite: **177 tests** (~33s). Staking-specific: **51 tests** in `test/Staking.test.ts`. Run `npx hardhat compile && npm test`.
288+
289+
Property tests: `src/echidna/EchidnaStakingHarness.sol` (see `echidna/README.md`).
290+
291+
### Unit tests (`test/Staking.test.ts`)
292+
293+
| Area | What is tested |
294+
|------|----------------|
295+
| **Deposit & queue** | Deploy wait params; `createDeposit` delay and activation; inactive until delay; top-up / height / overlay scheduling; lookahead previews; queue full; queue closed after `exit()`; redeposit after exit |
296+
| **Validation** | Below minimum at deposit; `AlreadyStaked()`; `InvalidAmount()` on `addTokens(0)`; height decrease rejected; height increase below new minimum; `MAX_STAKING_HEIGHT`; invalid constructor wait config |
297+
| **Withdraw & exit** | Partial withdraw + `applyUpdates` payout; full exit; invalid amounts; below-minimum remainder; withdraw/exit while active in current round (Redistribution commit) |
298+
| **Freeze** | Effective stake = 0 while frozen; non-withdrawal updates still apply; queued withdrawal blocked until unfreeze; `FrozenWithdrawal()` atomic revert on `applyUpdates`; freeze monotonic; freeze survives exit and `migrateStake`; `OnlyRedistributor()`; `AccountFreezeExtended` on unstaked account; freeze while paused reverts |
299+
| **Pause & migrate** | Staking blocked when paused; `migrateStake` only when paused (includes queued `CreateDeposit` / `AddTokens`); unpause restores flow |
300+
| **Enqueue API** | `callStatic` return matches event effective round; overlay unchanged revert; height unchanged no-op; non-decreasing rounds when stacking `addTokens`; atomic `applyUpdates` when withdrawal blocked mid-batch |
301+
| **FIFO & mixed delays** | Different wait configs (top-up → withdraw → overlay); same-round ordering; uniform waits (top-up → withdraw → height in one round) |
302+
| **Oracle independence** | Price oracle change does not change effective stake |
303+
304+
### Integration tests (`test/Redistribution.test.ts`)
305+
306+
| Area | What is tested |
307+
|------|----------------|
308+
| **Eligibility** | Unstaked / recently staked cannot commit; height-based minimum at commit; effective stake in reveals and winner selection |
309+
| **Queued stake** | Multi-node fixture with `createDeposit` and `addTokens`; effective stake after top-ups |
310+
| **Exit & lookahead** | Next-round stake state during claim-phase eligibility after queued `exit()` |
311+
| **Freeze from game** | Non-revealer frozen on claim (`nodeEffectiveStake == 0`); `StakeFrozen` event parsed from claim tx logs (overlay + duration) |
312+
| **Winner flow** | Single reveal, both reveal, postage payout failure retry — unchanged game logic with new stake API |
313+
314+
### Not tested in Hardhat (by design)
315+
316+
- Slash penalties (removed; freeze-only protocol)
317+
- Mainnet / testnet deployed bytecode (local fixture only)

0 commit comments

Comments
 (0)