Dec 10, 4-5 PM (34)
Dec 10, 5-6 PM (24)
Dec 10, 6-7 PM (19)
Dec 10, 7-8 PM (18)
Dec 10, 8-9 PM (24)
Dec 10, 9-10 PM (25)
Dec 10, 10-11 PM (49)
Dec 10, 11-12 AM (18)
Dec 11, 12-1 AM (5)
Dec 11, 1-2 AM (9)
Dec 11, 2-3 AM (12)
Dec 11, 3-4 AM (11)
Dec 11, 4-5 AM (10)
Dec 11, 5-6 AM (4)
Dec 11, 6-7 AM (30)
Dec 11, 7-8 AM (54)
Dec 11, 8-9 AM (40)
Dec 11, 9-10 AM (28)
Dec 11, 10-11 AM (41)
Dec 11, 11-12 PM (36)
Dec 11, 12-1 PM (49)
Dec 11, 1-2 PM (79)
Dec 11, 2-3 PM (56)
Dec 11, 3-4 PM (37)
Dec 11, 4-5 PM (34)
Dec 11, 5-6 PM (7)
Dec 11, 6-7 PM (13)
Dec 11, 7-8 PM (34)
Dec 11, 8-9 PM (19)
Dec 11, 9-10 PM (25)
Dec 11, 10-11 PM (32)
Dec 11, 11-12 AM (27)
Dec 12, 12-1 AM (3)
Dec 12, 1-2 AM (10)
Dec 12, 2-3 AM (11)
Dec 12, 3-4 AM (2)
Dec 12, 4-5 AM (11)
Dec 12, 5-6 AM (7)
Dec 12, 6-7 AM (7)
Dec 12, 7-8 AM (32)
Dec 12, 8-9 AM (86)
Dec 12, 9-10 AM (36)
Dec 12, 10-11 AM (70)
Dec 12, 11-12 PM (48)
Dec 12, 12-1 PM (73)
Dec 12, 1-2 PM (59)
Dec 12, 2-3 PM (41)
Dec 12, 3-4 PM (26)
Dec 12, 4-5 PM (32)
Dec 12, 5-6 PM (19)
Dec 12, 6-7 PM (12)
Dec 12, 7-8 PM (14)
Dec 12, 8-9 PM (7)
Dec 12, 9-10 PM (16)
Dec 12, 10-11 PM (26)
Dec 12, 11-12 AM (14)
Dec 13, 12-1 AM (0)
Dec 13, 1-2 AM (1)
Dec 13, 2-3 AM (0)
Dec 13, 3-4 AM (13)
Dec 13, 4-5 AM (0)
Dec 13, 5-6 AM (1)
Dec 13, 6-7 AM (1)
Dec 13, 7-8 AM (9)
Dec 13, 8-9 AM (1)
Dec 13, 9-10 AM (7)
Dec 13, 10-11 AM (4)
Dec 13, 11-12 PM (5)
Dec 13, 12-1 PM (8)
Dec 13, 1-2 PM (0)
Dec 13, 2-3 PM (4)
Dec 13, 3-4 PM (3)
Dec 13, 4-5 PM (1)
Dec 13, 5-6 PM (6)
Dec 13, 6-7 PM (11)
Dec 13, 7-8 PM (4)
Dec 13, 8-9 PM (8)
Dec 13, 9-10 PM (14)
Dec 13, 10-11 PM (39)
Dec 13, 11-12 AM (34)
Dec 14, 12-1 AM (2)
Dec 14, 1-2 AM (1)
Dec 14, 2-3 AM (0)
Dec 14, 3-4 AM (1)
Dec 14, 4-5 AM (4)
Dec 14, 5-6 AM (0)
Dec 14, 6-7 AM (0)
Dec 14, 7-8 AM (0)
Dec 14, 8-9 AM (8)
Dec 14, 9-10 AM (5)
Dec 14, 10-11 AM (33)
Dec 14, 11-12 PM (5)
Dec 14, 12-1 PM (8)
Dec 14, 1-2 PM (1)
Dec 14, 2-3 PM (3)
Dec 14, 3-4 PM (3)
Dec 14, 4-5 PM (2)
Dec 14, 5-6 PM (10)
Dec 14, 6-7 PM (13)
Dec 14, 7-8 PM (23)
Dec 14, 8-9 PM (11)
Dec 14, 9-10 PM (30)
Dec 14, 10-11 PM (32)
Dec 14, 11-12 AM (26)
Dec 15, 12-1 AM (5)
Dec 15, 1-2 AM (2)
Dec 15, 2-3 AM (8)
Dec 15, 3-4 AM (7)
Dec 15, 4-5 AM (3)
Dec 15, 5-6 AM (4)
Dec 15, 6-7 AM (2)
Dec 15, 7-8 AM (75)
Dec 15, 8-9 AM (36)
Dec 15, 9-10 AM (43)
Dec 15, 10-11 AM (29)
Dec 15, 11-12 PM (42)
Dec 15, 12-1 PM (47)
Dec 15, 1-2 PM (54)
Dec 15, 2-3 PM (37)
Dec 15, 3-4 PM (34)
Dec 15, 4-5 PM (31)
Dec 15, 5-6 PM (21)
Dec 15, 6-7 PM (55)
Dec 15, 7-8 PM (11)
Dec 15, 8-9 PM (27)
Dec 15, 9-10 PM (14)
Dec 15, 10-11 PM (26)
Dec 15, 11-12 AM (37)
Dec 16, 12-1 AM (17)
Dec 16, 1-2 AM (2)
Dec 16, 2-3 AM (6)
Dec 16, 3-4 AM (3)
Dec 16, 4-5 AM (2)
Dec 16, 5-6 AM (7)
Dec 16, 6-7 AM (13)
Dec 16, 7-8 AM (25)
Dec 16, 8-9 AM (36)
Dec 16, 9-10 AM (49)
Dec 16, 10-11 AM (53)
Dec 16, 11-12 PM (45)
Dec 16, 12-1 PM (71)
Dec 16, 1-2 PM (49)
Dec 16, 2-3 PM (36)
Dec 16, 3-4 PM (25)
Dec 16, 4-5 PM (33)
Dec 16, 5-6 PM (30)
Dec 16, 6-7 PM (19)
Dec 16, 7-8 PM (20)
Dec 16, 8-9 PM (41)
Dec 16, 9-10 PM (16)
Dec 16, 10-11 PM (63)
Dec 16, 11-12 AM (25)
Dec 17, 12-1 AM (5)
Dec 17, 1-2 AM (5)
Dec 17, 2-3 AM (6)
Dec 17, 3-4 AM (1)
Dec 17, 4-5 AM (4)
Dec 17, 5-6 AM (5)
Dec 17, 6-7 AM (13)
Dec 17, 7-8 AM (13)
Dec 17, 8-9 AM (26)
Dec 17, 9-10 AM (22)
Dec 17, 10-11 AM (50)
Dec 17, 11-12 PM (38)
Dec 17, 12-1 PM (39)
Dec 17, 1-2 PM (43)
Dec 17, 2-3 PM (32)
Dec 17, 3-4 PM (22)
Dec 17, 4-5 PM (6)
3,598 commits this week Dec 10, 2025 - Dec 17, 2025
Add pure Peras Voting rules
This PR implements the Peras voting rules in a pure fashion. That is,
given a PerasVotingView (which needs to be constructed in an impure
context), they determine purely if a node is allowed to vote or not in a
given round w.r.t. the voting rules from the CIP-0140. See:

https://github.com/cardano-foundation/CIPs/blob/master/CIP-0140/README.md#rules-for-voting-in-a-round

(Note that this is orthogonal from the committee selection: both
conditions need to be satisfied before a node is able to cast a vote in
a give round.)

Notably, our implementation needs to cover the somewhat special case of
deciding whether to start voting at genesis. The problem arises due to
the lack of a latestCertSeen to enable the "voting path" of the rules.
While the CIP uses a specially-defined cert0, our implementation encodes
this case via nullable certificate behind a WithOrigin.

In addition, this PR implements conformance tests against a simpler model
implemented directly over Bools. In the future, this model could be
reified from the Agda spec. Notably, we need to remark that, because VR-1A
is evaluated first, then:

  * VR-1A => !VR-2A

So, this case can't be covered by the test suite in a consistent way.
To hopefully convince you of this, we can show that VR-1A~True and
VR-2A~True leads to a contradiction. Using the following definitions:

  * r: current round (>=0)
  * C: round of the latest certificate seen (>=0)
  * R: Peras ignorance rounds (>0)

We have:

  * VR-1A := r + 1 == C
  * VR-2A := C + R <= r

If we replace C from VR-1A into VR-2A, we get:

  * r + 1 + R <= r

Since R>0:

  * 1 + R > 1

Therefore, we have:

  * r + 1 + R > r + 1 > r

And we conclude that:

  * C + R > r

Thus, !VR-2A, which is a contradiction.

With this in mind, the currently selected generation sizes and frequencies
lead to the following coverage of our testing property:

ouroboros-consensus
  Peras
    Peras voting rules
      isPerasVotingAllowed: OK (0.45s)
        +++ OK, passed 100000 tests.

        Actual result (100000 in total):
        33.708% VoteReason(VR-2A and VR-2B)
        32.242% NoVoteReason(VR-1A or VR-2B)
        30.538% NoVoteReason(VR-1A or VR-2A)
         1.871% VoteReason(VR-1A and VR-1B)
         1.641% NoVoteReason(VR-1B or VR-2A)

        Should vote according to model (100000 in total):
        64.421% False
        35.579% True

        VR-(1A|1B|2A|2B) (100000 in total):
        19.213% (False,True,True,True)
        18.595% (False,True,True,False)
        14.495% (False,False,True,True)
        13.647% (False,False,True,False)
         7.992% (False,True,False,False)
         7.835% (False,False,False,False)
         7.365% (False,False,False,True)
         7.346% (False,True,False,True)
         1.017% (True,True,False,False)
         0.860% (True,False,False,False)
         0.854% (True,True,False,True)
         0.781% (True,False,False,True)

        VR-1A (100000 in total):
        96.488% False
         3.512% True

        VR-1B (100000 in total):
        55.017% True
        44.983% False

        VR-2A (100000 in total):
        65.950% True
        34.050% False

        VR-2B (100000 in total):
        50.054% True
        49.946% False

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
Add explainable boolean predicate evaluator
This commits adds a simple boolean predicate evaluator DSL that allows
computing boolean predicates while returning evidence for the outcome.

This is needed by the Peras voting rules to be able to trace why we did
or did not enter/exit a cooldown period with good granularity.

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>