Skip to content

Commit 30d1e20

Browse files
authored
Merge pull request #427 from input-output-hk/shd/comments-on-epoch-timing
Added comments about epoch timing
2 parents 122a2fc + 79f3e94 commit 30d1e20

File tree

2 files changed

+94
-10
lines changed

2 files changed

+94
-10
lines changed

docs/epoch-timing.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Rewards and pools timing.
2+
3+
The following notes is the result of discussions with ledger team
4+
and of Haskell code analysis.
5+
6+
## Anikett Deshpande's comment on the epoch boundary transition:
7+
8+
In summary, at the epoch boundary:
9+
10+
* `TICK` is called, calls
11+
* `NEWEPOCH` which forces the existing rewards pulser to complete and distributes the rewards and then calls
12+
* `EPOCH`, which calls
13+
* `SNAP` to rotate the snapshots: now new -> mark, mark -> set , and set -> go
14+
* `SNAP` returns to
15+
* `EPOCH`, which returns to
16+
* `NEWEPOCH`, which returns to
17+
* `TICK`, which calls
18+
* `RUPD`, which in turn sets off the new rewards pulser using the newly rotated go snapshot (after stability window ~1.5 days), and returns to
19+
* `TICK`
20+
21+
In short:
22+
23+
* `TICK` calls `NEWEPOCH`
24+
* `NEWEPOCH` forces pulser and distributes rewards from the go snapshot (we are about to deallocate) which was rotated and marked as go at the previous boundary and was originally snapshotted as mark 2 epoch boundaries before that.
25+
* `SNAP` rotates the snapshots and takes a new one for mark
26+
* `RUPD` sets of the new pulser with the newly rotated go snapshot, which was marked as set in the previous epoch and was used for leader schedule processing.
27+
28+
I hope this answers the question much better than before. :blush: (edited)
29+
30+
## Rewards distribution timing
31+
32+
As we can conclude from that and from the Haskell node, the sequence of events is the following:
33+
34+
* Rewards calculated during epoch E, are calculated based on Go (E-3), and applied to Ledger state:
35+
36+
```"NEWEPOCH" rule: es' <- ... updateRewards es eNo ru'```
37+
38+
```updateRewards: let !(!es', filtered) = applyRUpdFiltered ru' es```
39+
40+
```applyRUpdFiltered:
41+
ls' =
42+
ls
43+
& lsUTxOStateL . utxosFeesL %~ (`addDeltaCoin` deltaF ru)
44+
& lsCertStateL . certDStateL . dsUnifiedL .~ (rewards dState UM.∪+ registeredAggregated)```
45+
46+
* Current ledger state is converted then into new Mark.
47+
48+
```
49+
es' <- case ru of
50+
SJust (Complete ru') -> updateRewards es eNo ru'
51+
es'' <- trans @(EraRule "MIR" era) $ TRC ((), es', ())
52+
es''' <- trans @(EraRule "EPOCH" era) $ TRC ((), es'', eNo)
53+
let adaPots = totalAdaPotsES es'''
54+
...
55+
let pd' = ssStakeMarkPoolDistr (esSnapshots es)
56+
```
57+
58+
* However, new Mark Pool distribution field does not include rewards.
59+
* Rewards for epoch E first appear in snapshot in epoch E+3 (as mark in EpochState).
60+
* Rewards for epoch E first used for leader scheduling in epoch E+4 (when it becomes set).
61+
62+
Conclusion: rewards, earned by block validation during epoch 209 (TODO: double-check,
63+
add code) and evaluated in epoch 210 (based on epoch 207 stake distribution: 'go' for 210),
64+
appear in snapshot in epoch 211 (as mark), and first used in epoch 212 for scheduling.
65+
66+
## Pool retirement timing
67+
68+
Each epoch boundary has a set of rules, concerning pool retirement. So, if we have epoch E-1 to E transition:
69+
70+
* Rule "EPOCH", called for epoch (E-1) => E transition, which calls "SNAP" and then "POOLREAP":
71+
* Rule "SNAP" rotates epochs (so, we have Set snapshot for E-1)
72+
* Rule "POOLREAP" removes all pools, retiring in epoch E (so, all pools,
73+
retiring in E, are not there from the start of the epoch)
74+
75+
So, next iteration of "EPOCH" rule (E=>E+1 transition) would make
76+
Set snapshot of epoch E (first shapshot without pools).
77+
78+
One more iteration (E+1=>E+2) makes shapshot Mark without pools.
79+
80+
Conclusion: if pool retires in epoch E, it disappears from current Mark
81+
(VRF active stake) in the beginning of epoch E+2.

docs/ledger-verification.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ But the reaction to the errors can be different.
1414
We have basically two variants of behaviour if the error in application/verification occurred:
1515

1616
1. Print an error message. This happens when an incorrect block apply happened, which probably
17-
broke the state. We can only hope that further blockchain rollbacks will correct it.
17+
broke the state. Here we can no automatic response here.
1818

1919
2. Send a ValidationStatus::NoGo message via corresponding channel (each module has a validation
2020
outcome channel). These channels are listened by Consensus module, and if at least one of them
@@ -28,8 +28,8 @@ We have basically two variants of behaviour if the error in application/verifica
2828
* Mithril/other trusted source --- blocks, already accepted by the blockchain.
2929
If everything is ok, then the block is applied, internal structures updated, and next block
3030
is processed.
31-
If something is not correct, then the whole blockchain is broken, and outside intervention
32-
is required.
31+
If something is not correct, then either the whole blockchain is broken, or our code is
32+
incorrect (inconsistent with bugs in Haskell node); outside intervention is required.
3333

3434
* Mempool or consensus blocks --- proposals for the blockchain. If the block/transaction is not
3535
successfully verified, then it could be refused.
@@ -60,17 +60,20 @@ We have basically two variants of behaviour if the error in application/verifica
6060

6161
## Block number and rollbacks
6262

63-
1. `BlockInfo` keeps track of the current block number. Blocks are numbered sequentially. So if the
63+
Rollback happens in one for the following situation:
64+
65+
* `BlockInfo` keeps track of the current block number. Blocks are numbered sequentially. So if the
6466
number equal to the previous one (or smaller than it), then Rollback takes place (all info from
6567
blocks with this or greater number should be deleted, and new attempt to apply block is done).
6668
In another words, applying of block N may be possible only if module state is actual for block N-1.
6769

68-
2. So, if the block applied unsuccessfully (and internal structures are broken), the situation
69-
can be corrected by rolling back to last correct block and applying different (correct) block
70-
after it.
70+
* Explicit `BlockStatus::RolledBack` message, which remvoes tip of the blockchain, and specifies
71+
last good state.
7172

72-
However, after unsucessful application and before successful rollback the state of the node is
73-
incorrect.
73+
If the block(s) are applied unsuccessfully (and internal structures are broken), the situation
74+
can be corrected by rolling back to last correct block and applying different (correct) block
75+
after it. However, after unsucessful application and before successful rollback the state of the
76+
node is incorrect.
7477

7578
## Mulit-module ledger specifics
7679

@@ -84,4 +87,4 @@ Ledger is split into several modules, so it gives additional challenges to the v
8487
but also block data (hash, etc), and skip all replies that do not correspond to current verification.
8588

8689
Instead, it should wait either for all 'Go' (from all modules), or for at least one 'NoGo',
87-
and do not wait for further messages.
90+
and should not wait for further messages.

0 commit comments

Comments
 (0)