Skip to content

Commit c4d0c4e

Browse files
committed
Don't define preferCandidate in terms of Ord
We want to restrict the Praos VRF tiebreakers based on slot distance. Naively adaopting the `Ord` instance will however make the chain order non-transitive. As a solution, we allow to customize the logic of `preferCandidate`, while still keeping a total chain order.
1 parent 214db42 commit c4d0c4e

File tree

18 files changed

+314
-128
lines changed

18 files changed

+314
-128
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Non-Breaking
2+
3+
- Adapted to introduction of new `ChainOrder` type class.

ouroboros-consensus-protocol/src/ouroboros-consensus-protocol/Ouroboros/Consensus/Protocol/Praos/Common.hs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
{-# LANGUAGE DeriveAnyClass #-}
33
{-# LANGUAGE DeriveGeneric #-}
44
{-# LANGUAGE DerivingStrategies #-}
5+
{-# LANGUAGE DerivingVia #-}
56
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+
{-# LANGUAGE StandaloneDeriving #-}
68
{-# LANGUAGE TypeFamilies #-}
9+
{-# LANGUAGE UndecidableInstances #-}
710

811
-- | Various things common to iterations of the Praos protocol.
912
module Ouroboros.Consensus.Protocol.Praos.Common (
@@ -41,15 +44,7 @@ newtype MaxMajorProtVer = MaxMajorProtVer
4144
deriving (Eq, Show, Generic)
4245
deriving newtype NoThunks
4346

44-
-- | View of the ledger tip for chain selection.
45-
--
46-
-- We order between chains as follows:
47-
--
48-
-- 1. By chain length, with longer chains always preferred.
49-
-- 2. If the tip of each chain was issued by the same agent, then we prefer
50-
-- the chain whose tip has the highest ocert issue number.
51-
-- 3. By a VRF value from the chain tip, with lower values preferred. See
52-
-- @pTieBreakVRFValue@ for which one is used.
47+
-- | View of the tip of a header fragment for chain selection.
5348
data PraosChainSelectView c = PraosChainSelectView
5449
{ csvChainLength :: BlockNo,
5550
csvSlotNo :: SlotNo,
@@ -59,6 +54,13 @@ data PraosChainSelectView c = PraosChainSelectView
5954
}
6055
deriving (Show, Eq, Generic, NoThunks)
6156

57+
-- | We order between chains as follows:
58+
--
59+
-- 1. By chain length, with longer chains always preferred.
60+
-- 2. If the tip of each chain was issued by the same agent, then we prefer
61+
-- the chain whose tip has the highest ocert issue number.
62+
-- 3. By a VRF value from the chain tip, with lower values preferred. See
63+
-- @pTieBreakVRFValue@ for which one is used.
6264
instance Crypto c => Ord (PraosChainSelectView c) where
6365
compare =
6466
mconcat
@@ -80,6 +82,9 @@ instance Crypto c => Ord (PraosChainSelectView c) where
8082
| otherwise =
8183
EQ
8284

85+
deriving via SimpleChainOrder (PraosChainSelectView c)
86+
instance Crypto c => ChainOrder (PraosChainSelectView c)
87+
8388
data PraosCanBeLeader c = PraosCanBeLeader
8489
{ -- | Certificate delegating rights from the stake pool cold key (or
8590
-- genesis stakeholder delegate cold key) to the online KES key.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Breaking
2+
3+
- Introduced new `ChainOrder` (with `preferCandidate`) class for `SelectView`s,
4+
and add necessary instances. Adapted `preferAnchoredCandidate` to use
5+
`preferCandidate` instead of relying on `preferAnchoredFragment`.

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Block/SupportsProtocol.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,13 @@ class ( GetHeader blk
3434
=> BlockConfig blk
3535
-> Header blk -> SelectView (BlockProtocol blk)
3636
selectView _ = blockNo
37+
38+
projectChainOrderConfig ::
39+
BlockConfig blk
40+
-> ChainOrderConfig (SelectView (BlockProtocol blk))
41+
42+
default projectChainOrderConfig ::
43+
ChainOrderConfig (SelectView (BlockProtocol blk)) ~ ()
44+
=> BlockConfig blk
45+
-> ChainOrderConfig (SelectView (BlockProtocol blk))
46+
projectChainOrderConfig _ = ()

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/AcrossEras.hs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
module Ouroboros.Consensus.HardFork.Combinator.AcrossEras (
2222
-- * Value for /each/ era
2323
PerEraBlockConfig (..)
24+
, PerEraChainOrderConfig (..)
2425
, PerEraCodecConfig (..)
2526
, PerEraConsensusConfig (..)
2627
, PerEraLedgerConfig (..)
@@ -97,13 +98,14 @@ import Ouroboros.Consensus.Util.Condense (Condense (..))
9798
Value for /each/ era
9899
-------------------------------------------------------------------------------}
99100

100-
newtype PerEraBlockConfig xs = PerEraBlockConfig { getPerEraBlockConfig :: NP BlockConfig xs }
101-
newtype PerEraCodecConfig xs = PerEraCodecConfig { getPerEraCodecConfig :: NP CodecConfig xs }
102-
newtype PerEraConsensusConfig xs = PerEraConsensusConfig { getPerEraConsensusConfig :: NP WrapPartialConsensusConfig xs }
103-
newtype PerEraLedgerConfig xs = PerEraLedgerConfig { getPerEraLedgerConfig :: NP WrapPartialLedgerConfig xs }
104-
newtype PerEraStorageConfig xs = PerEraStorageConfig { getPerEraStorageConfig :: NP StorageConfig xs }
101+
newtype PerEraBlockConfig xs = PerEraBlockConfig { getPerEraBlockConfig :: NP BlockConfig xs }
102+
newtype PerEraChainOrderConfig xs = PerEraChainOrderConfig { getPerEraChainOrderConfig :: NP WrapChainOrderConfig xs }
103+
newtype PerEraCodecConfig xs = PerEraCodecConfig { getPerEraCodecConfig :: NP CodecConfig xs }
104+
newtype PerEraConsensusConfig xs = PerEraConsensusConfig { getPerEraConsensusConfig :: NP WrapPartialConsensusConfig xs }
105+
newtype PerEraLedgerConfig xs = PerEraLedgerConfig { getPerEraLedgerConfig :: NP WrapPartialLedgerConfig xs }
106+
newtype PerEraStorageConfig xs = PerEraStorageConfig { getPerEraStorageConfig :: NP StorageConfig xs }
105107

106-
newtype PerEraProtocolParams xs = PerEraProtocolParams { getPerEraProtocolParams :: NP ProtocolParams xs }
108+
newtype PerEraProtocolParams xs = PerEraProtocolParams { getPerEraProtocolParams :: NP ProtocolParams xs }
107109

108110
{-------------------------------------------------------------------------------
109111
Values for /some/ eras

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/Protocol.hs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,26 @@ newtype HardForkSelectView xs = HardForkSelectView {
7171

7272
instance CanHardFork xs => Ord (HardForkSelectView xs) where
7373
compare (HardForkSelectView l) (HardForkSelectView r) =
74-
acrossEraSelection
75-
hardForkChainSel
76-
(mapWithBlockNo getOneEraSelectView l)
77-
(mapWithBlockNo getOneEraSelectView r)
74+
acrossEraSelection
75+
AcrossEraCompare
76+
(hpure Proxy)
77+
hardForkChainSel
78+
(mapWithBlockNo getOneEraSelectView l)
79+
(mapWithBlockNo getOneEraSelectView r)
80+
81+
instance CanHardFork xs => ChainOrder (HardForkSelectView xs) where
82+
type ChainOrderConfig (HardForkSelectView xs) = PerEraChainOrderConfig xs
83+
84+
preferCandidate
85+
(PerEraChainOrderConfig cfg)
86+
(HardForkSelectView ours)
87+
(HardForkSelectView cand) =
88+
acrossEraSelection
89+
AcrossEraPreferCandidate
90+
cfg
91+
hardForkChainSel
92+
(mapWithBlockNo getOneEraSelectView ours)
93+
(mapWithBlockNo getOneEraSelectView cand)
7894

7995
mkHardForkSelectView ::
8096
BlockNo
@@ -133,6 +149,12 @@ instance CanHardFork xs => BlockSupportsProtocol (HardForkBlock xs) where
133149
where
134150
cfgs = getPerEraBlockConfig hardForkBlockConfigPerEra
135151

152+
projectChainOrderConfig =
153+
PerEraChainOrderConfig
154+
. hcmap proxySingle (WrapChainOrderConfig . projectChainOrderConfig)
155+
. getPerEraBlockConfig
156+
. hardForkBlockConfigPerEra
157+
136158
{-------------------------------------------------------------------------------
137159
Ticking the chain dependent state
138160
-------------------------------------------------------------------------------}

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/Protocol/ChainSel.hs

Lines changed: 90 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111

1212
-- | Infrastructure for doing chain selection across eras
1313
module Ouroboros.Consensus.HardFork.Combinator.Protocol.ChainSel (
14-
AcrossEraSelection (..)
14+
AcrossEraMode (..)
15+
, AcrossEraSelection (..)
1516
, WithBlockNo (..)
1617
, acrossEraSelection
1718
, mapWithBlockNo
@@ -41,7 +42,12 @@ data AcrossEraSelection :: Type -> Type -> Type where
4142

4243
-- | Two eras using the same 'SelectView'. In this case, we can just compare
4344
-- chains even across eras, as the chain ordering is fully captured by
44-
-- 'SelectView' and its 'Ord' instance.
45+
-- 'SelectView' and its 'ChainOrder' instance.
46+
--
47+
-- We use the 'ChainOrderConfig' of the 'SelectView' in the newer era (with
48+
-- the intuition that newer eras are generally "preferred") when invoking
49+
-- 'compareChains'. However, this choice is arbitrary; we could also make it
50+
-- configurable here.
4551
CompareSameSelectView ::
4652
SelectView (BlockProtocol x) ~ SelectView (BlockProtocol y)
4753
=> AcrossEraSelection x y
@@ -50,55 +56,99 @@ data AcrossEraSelection :: Type -> Type -> Type where
5056
Compare two eras
5157
-------------------------------------------------------------------------------}
5258

59+
60+
-- | GADT indicating whether we are lifting 'compare' or 'preferCandidate' to
61+
-- the HFC, together with the type of configuration we need for that and the
62+
-- result type.
63+
data AcrossEraMode cfg a where
64+
AcrossEraCompare :: AcrossEraMode Proxy Ordering
65+
AcrossEraPreferCandidate :: AcrossEraMode WrapChainOrderConfig Bool
66+
67+
applyAcrossEraMode ::
68+
ChainOrder sv
69+
=> cfg blk
70+
-> (WrapChainOrderConfig blk -> ChainOrderConfig sv)
71+
-> AcrossEraMode cfg a
72+
-> sv -> sv -> a
73+
applyAcrossEraMode cfg f = \case
74+
AcrossEraCompare -> compare
75+
AcrossEraPreferCandidate -> preferCandidate (f cfg)
76+
77+
data FlipArgs = KeepArgs | FlipArgs
78+
5379
acrossEras ::
54-
forall blk blk'. SingleEraBlock blk
55-
=> WithBlockNo WrapSelectView blk
80+
forall blk blk' cfg a. SingleEraBlock blk
81+
=> FlipArgs
82+
-> AcrossEraMode cfg a
83+
-> cfg blk'
84+
-- ^ The configuration corresponding to the later block/era, also see
85+
-- 'CompareSameSelectView'.
86+
-> WithBlockNo WrapSelectView blk
5687
-> WithBlockNo WrapSelectView blk'
5788
-> AcrossEraSelection blk blk'
58-
-> Ordering
59-
acrossEras (WithBlockNo bnoL (WrapSelectView l))
60-
(WithBlockNo bnoR (WrapSelectView r)) = \case
61-
CompareBlockNo -> compare bnoL bnoR
62-
CompareSameSelectView -> compare l r
89+
-> a
90+
acrossEras flipArgs mode cfg
91+
(WithBlockNo bnoL (WrapSelectView l))
92+
(WithBlockNo bnoR (WrapSelectView r)) = \case
93+
CompareBlockNo -> maybeFlip cmp bnoL bnoR
94+
where
95+
cmp = applyAcrossEraMode cfg (const ()) mode
96+
CompareSameSelectView -> maybeFlip cmp l r
97+
where
98+
cmp = applyAcrossEraMode cfg (unwrapChainOrderConfig) mode
99+
where
100+
maybeFlip :: (b -> b -> a) -> b -> b -> a
101+
maybeFlip = case flipArgs of
102+
KeepArgs -> id
103+
FlipArgs -> flip
63104

64105
acrossEraSelection ::
106+
forall xs cfg a.
65107
All SingleEraBlock xs
66-
=> Tails AcrossEraSelection xs
108+
=> AcrossEraMode cfg a
109+
-> NP cfg xs
110+
-> Tails AcrossEraSelection xs
67111
-> WithBlockNo (NS WrapSelectView) xs
68112
-> WithBlockNo (NS WrapSelectView) xs
69-
-> Ordering
70-
acrossEraSelection = \ffs l r ->
71-
goLeft ffs (distribBlockNo l, distribBlockNo r)
113+
-> a
114+
acrossEraSelection mode = \cfg ffs l r ->
115+
goBoth cfg ffs (distribBlockNo l, distribBlockNo r)
72116
where
73-
goLeft ::
74-
All SingleEraBlock xs
75-
=> Tails AcrossEraSelection xs
76-
-> ( NS (WithBlockNo WrapSelectView) xs
77-
, NS (WithBlockNo WrapSelectView) xs
117+
goBoth ::
118+
All SingleEraBlock xs'
119+
=> NP cfg xs'
120+
-> Tails AcrossEraSelection xs'
121+
-> ( NS (WithBlockNo WrapSelectView) xs'
122+
, NS (WithBlockNo WrapSelectView) xs'
78123
)
79-
-> Ordering
80-
goLeft TNil = \(a, _) -> case a of {}
81-
goLeft (TCons fs ffs') = \case
82-
(Z a, Z b) -> compare (dropBlockNo a) (dropBlockNo b)
83-
(Z a, S b) -> goRight a fs b
84-
(S a, Z b) -> invert $ goRight b fs a
85-
(S a, S b) -> goLeft ffs' (a, b)
86-
87-
goRight ::
88-
forall x xs. (SingleEraBlock x, All SingleEraBlock xs)
89-
=> WithBlockNo WrapSelectView x
90-
-> NP (AcrossEraSelection x) xs
91-
-> NS (WithBlockNo WrapSelectView) xs
92-
-> Ordering
93-
goRight a = go
124+
-> a
125+
goBoth _ TNil = \(a, _) -> case a of {}
126+
goBoth (cfg :* cfgs) (TCons fs ffs') = \case
127+
(Z a, Z b) -> cmp (dropBlockNo a) (dropBlockNo b)
128+
where
129+
cmp = applyAcrossEraMode cfg unwrapChainOrderConfig mode
130+
(Z a, S b) -> goOne KeepArgs a cfgs fs b
131+
(S a, Z b) -> goOne FlipArgs b cfgs fs a
132+
(S a, S b) -> goBoth cfgs ffs' (a, b)
133+
134+
goOne ::
135+
forall x xs'. (SingleEraBlock x, All SingleEraBlock xs')
136+
=> FlipArgs
137+
-> WithBlockNo WrapSelectView x
138+
-> NP cfg xs'
139+
-> NP (AcrossEraSelection x) xs'
140+
-> NS (WithBlockNo WrapSelectView) xs'
141+
-> a
142+
goOne flipArgs a = go
94143
where
95-
go :: forall xs'. All SingleEraBlock xs'
96-
=> NP (AcrossEraSelection x) xs'
97-
-> NS (WithBlockNo WrapSelectView) xs'
98-
-> Ordering
99-
go Nil b = case b of {}
100-
go (f :* _) (Z b) = acrossEras a b f
101-
go (_ :* fs) (S b) = go fs b
144+
go :: forall xs''. All SingleEraBlock xs''
145+
=> NP cfg xs''
146+
-> NP (AcrossEraSelection x) xs''
147+
-> NS (WithBlockNo WrapSelectView) xs''
148+
-> a
149+
go _ Nil b = case b of {}
150+
go (cfg :* _ ) (f :* _) (Z b) = acrossEras flipArgs mode cfg a b f
151+
go (_ :* cfgs) (_ :* fs) (S b) = go cfgs fs b
102152

103153
{-------------------------------------------------------------------------------
104154
WithBlockNo
@@ -115,12 +165,3 @@ mapWithBlockNo f (WithBlockNo bno fx) = WithBlockNo bno (f fx)
115165

116166
distribBlockNo :: SListI xs => WithBlockNo (NS f) xs -> NS (WithBlockNo f) xs
117167
distribBlockNo (WithBlockNo b ns) = hmap (WithBlockNo b) ns
118-
119-
{-------------------------------------------------------------------------------
120-
Auxiliary
121-
-------------------------------------------------------------------------------}
122-
123-
invert :: Ordering -> Ordering
124-
invert LT = GT
125-
invert GT = LT
126-
invert EQ = EQ

ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/Dual.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ instance Bridge m a => BlockSupportsProtocol (DualBlock m a) where
288288
validateView cfg = validateView (dualBlockConfigMain cfg) . dualHeaderMain
289289
selectView cfg = selectView (dualBlockConfigMain cfg) . dualHeaderMain
290290

291+
projectChainOrderConfig = projectChainOrderConfig . dualBlockConfigMain
292+
291293
{-------------------------------------------------------------------------------
292294
Ledger errors
293295
-------------------------------------------------------------------------------}

0 commit comments

Comments
 (0)