Skip to content

Commit 6651e6e

Browse files
committed
WIP Linear Leios
1 parent b93a795 commit 6651e6e

File tree

13 files changed

+558
-81
lines changed

13 files changed

+558
-81
lines changed

leios-trace-hs/src/LeiosConfig.hs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ instance Default CleanupPolicies where
8585
allCleanupPolicies :: CleanupPolicies
8686
allCleanupPolicies = CleanupPolicies $ Set.fromList [minBound .. maxBound]
8787

88-
data LeiosVariant = Short | Full
88+
data LeiosVariant = Short | Full | Linear
8989
deriving (Show, Eq, Generic)
9090

9191
data Config = Config
@@ -97,6 +97,8 @@ data Config = Config
9797
, simulateTransactions :: Bool
9898
, leiosStageLengthSlots :: Word
9999
, leiosStageActiveVotingSlots :: Word
100+
, linearVoteStageLengthSlots :: Word
101+
, linearDiffuseStageLengthSlots :: Word
100102
, leiosVoteSendRecvStages :: Bool
101103
, leiosVariant :: LeiosVariant
102104
, leiosLateIbInclusion :: Bool
@@ -141,6 +143,7 @@ data Config = Config
141143
, voteGenerationProbability :: Double
142144
, voteGenerationCpuTimeMsConstant :: DurationMs
143145
, voteGenerationCpuTimeMsPerIb :: DurationMs
146+
, voteGenerationCpuTimeMsPerTx :: DurationMs
144147
, voteValidationCpuTimeMs :: DurationMs
145148
, voteThreshold :: Word
146149
, voteBundleSizeBytesConstant :: SizeBytes
@@ -170,6 +173,8 @@ instance Default Config where
170173
, simulateTransactions = True
171174
, leiosStageLengthSlots = 20
172175
, leiosStageActiveVotingSlots = 1
176+
, linearVoteStageLengthSlots = 5
177+
, linearDiffuseStageLengthSlots = 5
173178
, leiosVoteSendRecvStages = False
174179
, leiosVariant = Short
175180
, leiosLateIbInclusion = True
@@ -214,6 +219,7 @@ instance Default Config where
214219
, voteGenerationProbability = 500.0
215220
, voteGenerationCpuTimeMsConstant = 0.164
216221
, voteGenerationCpuTimeMsPerIb = 0.0
222+
, voteGenerationCpuTimeMsPerTx = 0.0
217223
, voteValidationCpuTimeMs = 0.816
218224
, voteThreshold = 300
219225
, voteBundleSizeBytesConstant = 0
@@ -291,6 +297,7 @@ configToKVsWith getter cfg =
291297
, get @"voteGenerationProbability" getter cfg
292298
, get @"voteGenerationCpuTimeMsConstant" getter cfg
293299
, get @"voteGenerationCpuTimeMsPerIb" getter cfg
300+
, get @"voteGenerationCpuTimeMsPerTx" getter cfg
294301
, get @"voteValidationCpuTimeMs" getter cfg
295302
, get @"voteThreshold" getter cfg
296303
, get @"voteBundleSizeBytesConstant" getter cfg
@@ -339,6 +346,8 @@ instance FromJSON Config where
339346
leiosStageLengthSlots <- parseFieldOrDefault @Config @"leiosStageLengthSlots" obj
340347
leiosStageActiveVotingSlots <- parseFieldOrDefault @Config @"leiosStageActiveVotingSlots" obj
341348
leiosVoteSendRecvStages <- parseFieldOrDefault @Config @"leiosVoteSendRecvStages" obj
349+
linearVoteStageLengthSlots <- parseFieldOrDefault @Config @"linearVoteStageLengthSlots" obj
350+
linearDiffuseStageLengthSlots <- parseFieldOrDefault @Config @"linearDiffuseStageLengthSlots" obj
342351
txGenerationDistribution <- parseFieldOrDefault @Config @"txGenerationDistribution" obj
343352
txSizeBytesDistribution <- parseFieldOrDefault @Config @"txSizeBytesDistribution" obj
344353
txValidationCpuTimeMs <- parseFieldOrDefault @Config @"txValidationCpuTimeMs" obj
@@ -378,6 +387,7 @@ instance FromJSON Config where
378387
voteGenerationProbability <- parseFieldOrDefault @Config @"voteGenerationProbability" obj
379388
voteGenerationCpuTimeMsConstant <- parseFieldOrDefault @Config @"voteGenerationCpuTimeMsConstant" obj
380389
voteGenerationCpuTimeMsPerIb <- parseFieldOrDefault @Config @"voteGenerationCpuTimeMsPerIb" obj
390+
voteGenerationCpuTimeMsPerTx <- parseFieldOrDefault @Config @"voteGenerationCpuTimeMsPerTx" obj
381391
voteValidationCpuTimeMs <- parseFieldOrDefault @Config @"voteValidationCpuTimeMs" obj
382392
voteThreshold <- parseFieldOrDefault @Config @"voteThreshold" obj
383393
voteBundleSizeBytesConstant <- parseFieldOrDefault @Config @"voteBundleSizeBytesConstant" obj

simulation/docs/SimulatorModel.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,3 +347,76 @@ These variables maintain tasks blocked on some missing input.
347347
- `taskQueue`.
348348
A queue of tasks scheduled for the CPU, labeled according to what they model (eg "validate an RB").
349349
Tasks are only removed when they are executed.
350+
351+
# Alternative Design: Linear Leios
352+
353+
## Motivation
354+
355+
In Linear Leios, every time any party issues an RB, they also issue an EB.
356+
In effect, the RB now consists of three parts: a header, a first body (the standard RB body), and a second body (the EB) that extends the first.
357+
The EB includes txs (either by value or by reference)---there are no IBs.
358+
When a (child) RB extends some (parent) RB, it will sometimes include a certificate that demonstrates a quorum of stake has already validated the parent RB's second body.
359+
If the child RB includes that certificate, then its first body extends the parent's second body.
360+
If the child RB excludes that certificate, then its first body instead extends the parent's first body---the parent's second body is irrevocably lost (except maybe on other chains extending that same parent RB).
361+
362+
This variant is _linear_ because the txs that end up on some chain are never unordered: they are ordered in the mempools and they are ordered in the roughly-alternating chain of RBs and EBs.
363+
This is in crucial contrast to Short Leios and its extensions: there, txs included (directly or indirectly) via EBs are concurrent until an RB serializes them (at the latest possible moment).
364+
As a result, this variant is immediately compatible with today's ledger interface, as is---possibly other than reward calculations etc.
365+
366+
## A Terse Interpretation of the Linear Leois Specification
367+
368+
- A node should fetch EBs according to a FreshestFirst policy.
369+
- A node should validate an EB as soon as possible while both of the following are true.
370+
- It has validated the EB's parent RB.
371+
The node cannot validate the EB before validating its parent RB.
372+
- The EB's parent RB is the tip of the best RB header chain it has validated, excluding RB header's whose body turned out to be invalid.
373+
Any RB that extends the EB's parent RB either excludes the EB or else certifies its validity---in either case, the node no longer needs to validate the EB.
374+
Note that this conjunct is non-monotonic, due to a relevant RB header being later disqualified when its RB body is recognized as invalid.
375+
TODO for now, it's merely what the chain has selected, regardless of any better headers.
376+
- A node should vote for an EB as soon as possible within the following interval.
377+
- The interval ends `L_vote` slots after the onset of the EB's slot (aka `linear-vote-stage-length-slots`).
378+
- The interval begins either when the node validates the EB or three \Delta_hdr of the onset of the EB's slot, whichever happens last (ie combined the two times via `max`).
379+
A node must not vote for an EB if it receives evidence of the issuer's equivocation within three \Delta_hdr of the EB's slot onset.
380+
- A node should include an EB certificate, if any, in a new RB that extends the EB's parent if the new RB is at least `L_vote + L_diff` slots younger than the EB's parent (aka `linear-vote-stage-length-slots + linear-diffuse-stage-length-slots`).
381+
It is important to clarify that an RB remains valid if it excludes the certificate even when those constraints are satisfied.
382+
- A node should diffuse an EB even before it knows whether it's invalid (ie it should enable "EB diffusion pipelining").
383+
TODO would it even be worth going one step further: _streaming_ the EB, ie diffusing its prefix before its suffix has been received (and therefore before the EB could have been parsed)?
384+
- A node should diffuse RB headers (at most two per election proof) even if it they're not on the best header chain, since such a header might still evidence equivocation.
385+
We anticipate this happening separately (and redundantly) of ChainSync and BlockFetch.
386+
TODO it can also abort validation of the EB, right?
387+
TODO should it exclude the certificate of an equivocated EB if it issues the next RB?
388+
389+
A node should acquire EBs from election opportunities even if they're on competing forks.
390+
That way, if the node switches chains, it will already have the necessary EBs.
391+
It won't have validated them, but that's fine, since it will only need to apply them when a certificate forces them to, so they can skip the validation checks.
392+
393+
In order for the node to receive EBs for RB chains it doesn't necessarily have the RB headers for, RB headers will also be propogated via Relay in addition to and completely independently from their diffusion via ChainSync and BlockFetch.
394+
This is because the RB header announces not just the RB body but also the EB (via the new `EBannounced` field in the RB header from the Linear Leios spec).
395+
396+
## Implementation Notes
397+
398+
The Linear Leios specification indicates that an RB header names the EB that occurs on the chain before the RB, if any, the EB that occurs after the RB, if any, and an EB names the RB that it follows.
399+
However, in the absence of an attacker, the simulator can simply use the existing data types for Linear Leios even though the RB header type excludes those two fields.
400+
401+
- The simulator does not currently have extensible data types for Praos headers, so it would not be trivial to add these header fields.
402+
- In the absence of an attacker within the simulation, the new RB header fields aren't _necessary_.
403+
- Despite being unnecessary, having the RB header announce its second body EB would plausibly decrease the average latency of the EBs.
404+
But that decrease should be _very_ minor; with the current overly-coarse multiplexer logic (see [Issue 453](https://github.com/input-output-hk/ouroboros-leios/issues/453)), the EB's `RelayHeader` will arrive immediately after the ChainSync header (which are small), except perhaps during severe congestion.
405+
406+
The Linear Leios simulator adds the following new variables, some of which also require some new threads.
407+
408+
- `relayLinearEBState`.
409+
As a shortcut, the first Linear Leios simulator will instantiate `Relay` with `RelayHeader InputBlockId` and `InputBlock`.
410+
This is because the IB specified in Short Leios has just a few small fields more than the EB specified in Linear Leios.
411+
- `waitingForLedgerStateAndLinearEbVar` and `ledgerStateAndLinearEbVar`.
412+
An RB that contains an EB cert can be adopted before that EB has been validated.
413+
TODO but it still needs to be applied, which is much cheaper but not free
414+
- `waitingForTipVar`.
415+
The Linear EB should be validated the first time its arrived and its parent RB is the tip of the node's selection.
416+
- `linearEbsToVoteVar`.
417+
Once a Linear EB has been validated, it should be voted for.
418+
A new custom thread monitors this variable in addition to the clock, so that it can avoid issugin a vote too early or too late.
419+
420+
TODO block diffusion pipelining for both RBs and EBs
421+
422+
TODO RBs are so far only distributed by ChainSync and BlockFetch

simulation/src/LeiosProtocol/Common.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,9 @@ inputBlockInvariant ib = ib.header.id == ib.body.id
147147
instance HasField "id" InputBlock InputBlockId where
148148
getField = (.id) . (.header)
149149

150+
instance HasField "slot" InputBlock SlotNo where
151+
getField = (.slot) . (.header)
152+
150153
data EndorseBlockId = EndorseBlockId
151154
{ node :: !NodeId
152155
, num :: !Int

simulation/src/LeiosProtocol/Short.hs

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ data LeiosDelays = LeiosDelays
6363
, endorseBlockGeneration :: !(EndorseBlock -> DiffTime)
6464
, endorseBlockValidation :: !(EndorseBlock -> DiffTime)
6565
, voteMsgGeneration :: !(VoteMsg -> [EndorseBlock] -> DiffTime)
66+
, linearVoteMsgGeneration :: !(VoteMsg -> [InputBlock] -> DiffTime)
6667
, voteMsgValidation :: !(VoteMsg -> DiffTime)
6768
, certificateGeneration :: !(Certificate -> DiffTime)
6869
, certificateValidation :: !(Certificate -> DiffTime)
@@ -122,6 +123,8 @@ data LeiosConfig = forall p. IsPipeline p => LeiosConfig
122123
, votesForCertificate :: Int
123124
, sizes :: SizesConfig
124125
, delays :: LeiosDelays
126+
, linearVoteStageLengthSlots :: Int
127+
, linearDiffuseStageLengthSlots :: Int
125128
, ibDiffusion :: RelayDiffusionConfig
126129
, ebDiffusion :: RelayDiffusionConfig
127130
, voteDiffusion :: RelayDiffusionConfig
@@ -153,11 +156,13 @@ convertConfig disk =
153156
, headerDiffusionTime = realToFrac $ durationMsToDiffTime disk.leiosHeaderDiffusionTimeMs
154157
, lateIbInclusion = disk.leiosLateIbInclusion
155158
, pipelinesToReferenceFromEB =
156-
if disk.leiosVariant == Full
157-
then
158-
ceiling ((3 * disk.praosChainQuality) / fromIntegral sliceLength) - 2
159-
else 0
159+
case disk.leiosVariant of
160+
Full -> ceiling ((3 * disk.praosChainQuality) / fromIntegral sliceLength) - 2
161+
Short -> 0
162+
Linear -> 0
160163
, activeVotingStageLength = fromIntegral disk.leiosStageActiveVotingSlots
164+
, linearVoteStageLengthSlots = fromIntegral disk.linearVoteStageLengthSlots
165+
, linearDiffuseStageLengthSlots = fromIntegral disk.linearDiffuseStageLengthSlots
161166
, votingFrequencyPerStage = disk.voteGenerationProbability
162167
, votesForCertificate = fromIntegral disk.voteThreshold
163168
, sizes
@@ -267,13 +272,25 @@ convertConfig disk =
267272
+ disk.voteGenerationCpuTimeMsPerIb `forEach` eb.inputBlocks
268273
| eb <- ebs
269274
]
275+
, linearVoteMsgGeneration = \vm ibs ->
276+
assert (1 == length vm.endorseBlocks) $
277+
assert (vm.endorseBlocks == map (convertLinearId . (.id)) ibs) $
278+
assert (0 == disk.voteGenerationCpuTimeMsPerTx) $ -- TODO
279+
durationMsToDiffTime $
280+
disk.voteGenerationCpuTimeMsConstant `forEach` ibs
270281
, voteMsgValidation = \vm ->
271282
durationMsToDiffTime $
272283
disk.voteValidationCpuTimeMs `forEach` vm.endorseBlocks
273284
, certificateGeneration = const $ error "certificateGeneration delay included in RB generation"
274285
, certificateValidation = const $ error "certificateValidation delay included in RB validation"
275286
}
276287

288+
convertLinearId :: InputBlockId -> EndorseBlockId
289+
convertLinearId (InputBlockId x y) = EndorseBlockId x y
290+
291+
unconvertLinearId :: EndorseBlockId -> InputBlockId
292+
unconvertLinearId (EndorseBlockId x y) = InputBlockId x y
293+
277294
delaysAndSizesAsFull :: LeiosConfig -> LeiosConfig
278295
delaysAndSizesAsFull cfg@LeiosConfig{pipeline, voteSendStage} =
279296
-- Fields spelled out to more likely trigger an error and review when type changes.
@@ -293,6 +310,8 @@ delaysAndSizesAsFull cfg@LeiosConfig{pipeline, voteSendStage} =
293310
, lateIbInclusion = cfg.lateIbInclusion
294311
, pipelinesToReferenceFromEB = cfg.pipelinesToReferenceFromEB
295312
, activeVotingStageLength = cfg.activeVotingStageLength
313+
, linearVoteStageLengthSlots = cfg.linearVoteStageLengthSlots
314+
, linearDiffuseStageLengthSlots = cfg.linearDiffuseStageLengthSlots
296315
, votingFrequencyPerStage = cfg.votingFrequencyPerStage
297316
, voteSendStage = voteSendStage
298317
, votesForCertificate = cfg.votesForCertificate
@@ -310,6 +329,15 @@ delaysAndSizesAsFull cfg@LeiosConfig{pipeline, voteSendStage} =
310329
| id' <- fullVT.endorseBlocks
311330
, let EndorseBlock{..} = fullEB
312331
]
332+
fullLinearEBsVotedFor =
333+
[ InputBlock {
334+
body = fullIB.body
335+
, header =
336+
let InputBlockHeader{..} = fullIB.header
337+
in InputBlockHeader {id = unconvertLinearId id', ..}
338+
}
339+
| id' <- fullVT.endorseBlocks
340+
]
313341
fullRB = mockFullRankingBlock cfg
314342
fullCert = mockFullCertificate cfg
315343
praos =
@@ -347,6 +375,12 @@ delaysAndSizesAsFull cfg@LeiosConfig{pipeline, voteSendStage} =
347375
cfg.delays.voteMsgGeneration
348376
fullVT
349377
fullEBsVotedFor
378+
, linearVoteMsgGeneration =
379+
const $
380+
const @DiffTime $
381+
cfg.delays.linearVoteMsgGeneration
382+
fullVT
383+
fullLinearEBsVotedFor
350384
, voteMsgValidation = const @DiffTime $ cfg.delays.voteMsgValidation fullVT
351385
, certificateGeneration = const @DiffTime $ cfg.delays.certificateGeneration fullCert
352386
, certificateValidation = const @DiffTime $ cfg.delays.certificateValidation fullCert
@@ -661,7 +695,8 @@ mockFullCertificate cfg = mockCertificate cfg cfg.votesForCertificate
661695
-- Buffers views, divided to avoid reading unneeded buffers.
662696

663697
data NewRankingBlockData = NewRankingBlockData
664-
{ certifiedEBforRBAt :: SlotNo -> Maybe (EndorseBlockId, Certificate)
698+
{ prevChain :: Chain RankingBlock
699+
, mbEbCert :: Maybe (EndorseBlockId, Certificate)
665700
, txsPayload :: Bytes
666701
}
667702

@@ -758,13 +793,14 @@ endorseBlocksToReference ::
758793
EndorseBlocksSnapshot ->
759794
(PipelineNo -> UTCTime -> Bool) ->
760795
[(PipelineNo, [EndorseBlock])]
761-
endorseBlocksToReference LeiosConfig{variant = Short} _ _ _ = []
762-
endorseBlocksToReference cfg@LeiosConfig{variant = Full} pl EndorseBlocksSnapshot{..} checkDeliveryTime =
763-
assert
764-
( all (\(p, ebs) -> all (\eb -> p == endorseBlockPipeline cfg eb) ebs && succ (succ p) <= pl) result
765-
&& (\ps -> sort ps == ps) (map fst result)
766-
)
767-
result
796+
endorseBlocksToReference cfg pl EndorseBlocksSnapshot{..} checkDeliveryTime
797+
| Full <- cfg.variant =
798+
assert
799+
( all (\(p, ebs) -> all (\eb -> p == endorseBlockPipeline cfg eb) ebs && succ (succ p) <= pl) result
800+
&& (\ps -> sort ps == ps) (map fst result)
801+
)
802+
result
803+
| otherwise = []
768804
where
769805
result =
770806
[ (p, [eb | (eb, _, _) <- es])

simulation/src/LeiosProtocol/Short/DataSimP2P.hs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,11 @@ accumLeiosSimState _cfg now (LeiosEventNode (LabelNode nid (LeiosNodeEvent event
153153
{ ebDiffusionLatency = accumDiffusionLatency' now nid event x.id x ebDiffusionLatency
154154
, ..
155155
}
156+
EventLinearEB x ->
157+
LeiosSimState
158+
{ ibDiffusionLatency = accumDiffusionLatency' now nid event x.id x.header ibDiffusionLatency
159+
, ..
160+
}
156161
EventVote x ->
157162
LeiosSimState
158163
{ voteDiffusionLatency = accumDiffusionLatency' now nid event x.id x voteDiffusionLatency

0 commit comments

Comments
 (0)