Skip to content

Commit 46bad6b

Browse files
authored
Test EB inclusion (#1971)
Closes input-output-hk/ouroboros-leios#836
2 parents 04eb003 + b763aa0 commit 46bad6b

5 files changed

Lines changed: 307 additions & 44 deletions

File tree

ouroboros-consensus-cardano/ouroboros-consensus-cardano.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,17 +473,20 @@ test-suite cardano-test
473473
containers,
474474
contra-tracer,
475475
filepath,
476+
io-sim,
476477
microlens,
477478
ouroboros-consensus:{ouroboros-consensus, unstable-consensus-testlib, unstable-mempool-test-utils},
478479
ouroboros-consensus-cardano:{ouroboros-consensus-cardano, unstable-cardano-testlib},
479480
ouroboros-consensus-diffusion:unstable-diffusion-testlib,
480481
ouroboros-consensus-protocol,
481482
ouroboros-network-api,
483+
ouroboros-network-mock,
482484
ouroboros-network-protocols:{ouroboros-network-protocols, testlib},
483485
pretty-simple,
484486
sop-core,
485487
sop-extras,
486488
strict-sop-core,
489+
strict-stm,
487490
tasty,
488491
tasty-hunit,
489492
tasty-quickcheck,

ouroboros-consensus-cardano/test/cardano-test/Test/ThreadNet/Leios.hs

Lines changed: 161 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{-# LANGUAGE NamedFieldPuns #-}
55
{-# LANGUAGE NumericUnderscores #-}
66
{-# LANGUAGE OverloadedRecordDot #-}
7+
{-# LANGUAGE PatternSynonyms #-}
78
{-# LANGUAGE RankNTypes #-}
89
{-# LANGUAGE ScopedTypeVariables #-}
910
{-# LANGUAGE TypeApplications #-}
@@ -32,19 +33,24 @@ import Cardano.Ledger.Api
3233
import Cardano.Ledger.Api.Transition (mkLatestTransitionConfig)
3334
import Cardano.Ledger.Api.Tx.In (TxIn (..))
3435
import Cardano.Ledger.BaseTypes (ProtVer (..), TxIx (..), knownNonZeroBounded)
36+
import qualified Cardano.Ledger.Block as SL
3537
import Cardano.Protocol.Crypto (StandardCrypto)
3638
import Cardano.Protocol.TPraos.OCert (KESPeriod (..))
3739
import Cardano.Slotting.Time (SlotLength, slotLengthFromSec)
38-
import Control.Monad (replicateM)
40+
import qualified Control.Concurrent.Class.MonadSTM.Strict.TVar as StrictTVar
41+
import Control.Monad (foldM, replicateM)
42+
import Control.Monad.IOSim (runSimOrThrow)
3943
import Data.Foldable (toList)
4044
import Data.Function ((&))
45+
import Data.Functor.Identity (runIdentity)
4146
import Data.Map (Map)
4247
import qualified Data.Map.Strict as Map
4348
import Data.Maybe (isNothing, mapMaybe)
4449
import Data.Proxy (Proxy (..))
4550
import Data.Sequence.Strict ((|>))
4651
import qualified Data.Set as Set
4752
import Data.Word (Word64)
53+
import LeiosDemoDb (LeiosDbHandle, newLeiosDBInMemoryWith)
4854
import LeiosDemoTypes (LeiosPoint (..), TraceLeiosKernel (..), hashLeiosEb)
4955
import Lens.Micro (each, (%~), (^.), (^..))
5056
import Ouroboros.Consensus.Block (SlotNo (..))
@@ -55,13 +61,29 @@ import Ouroboros.Consensus.Cardano
5561
, ProtocolParamsShelleyBased (..)
5662
, ShelleyGenesis (..)
5763
)
64+
import Ouroboros.Consensus.Cardano.Block (pattern BlockConway)
5865
import Ouroboros.Consensus.Cardano.Node (CardanoProtocolParams (..), protocolInfoCardano)
59-
import Ouroboros.Consensus.Config (SecurityParam (..))
66+
import Ouroboros.Consensus.Config (SecurityParam (..), TopLevelConfig)
67+
import Ouroboros.Consensus.Ledger.Abstract
68+
( ComputeLedgerEvents (OmitLedgerEvents)
69+
, LedgerCfg
70+
, tickThenReapply
71+
)
72+
import Ouroboros.Consensus.Ledger.Basics (EmptyMK, LedgerState, ValuesMK)
73+
import Ouroboros.Consensus.Ledger.Extended
74+
( ExtLedgerCfg (..)
75+
, ExtLedgerState (..)
76+
, ledgerState
77+
)
6078
import Ouroboros.Consensus.Ledger.SupportsMempool (extractTxs)
79+
import Ouroboros.Consensus.Ledger.Tables.Utils (applyDiffs, forgetLedgerTables)
6180
import Ouroboros.Consensus.Mempool (TraceEventMempool (..))
62-
import Ouroboros.Consensus.Node.ProtocolInfo (NumCoreNodes (..))
81+
import Ouroboros.Consensus.Node.ProtocolInfo (NumCoreNodes (..), ProtocolInfo (..))
6382
import Ouroboros.Consensus.NodeId (CoreNodeId (..))
83+
import Ouroboros.Consensus.Shelley.Ledger.Block (shelleyBlockRaw)
6484
import Ouroboros.Consensus.Shelley.Ledger.SupportsProtocol ()
85+
import Ouroboros.Consensus.Storage.LedgerDB.Forker (ResolveLeiosBlock (..))
86+
import qualified Ouroboros.Network.Mock.Chain as Chain
6587
import Test.Cardano.Ledger.Alonzo.Examples.Consensus (exampleAlonzoGenesis)
6688
import Test.Cardano.Ledger.Conway.Examples.Consensus (exampleConwayGenesis)
6789
import Test.Consensus.Cardano.ProtocolInfo (Era (Conway), hardForkInto)
@@ -72,6 +94,7 @@ import Test.QuickCheck
7294
, counterexample
7395
, tabulate
7496
, withMaxSuccess
97+
, (.&&.)
7598
, (===)
7699
)
77100
import Test.Tasty (TestTree, testGroup)
@@ -97,7 +120,8 @@ import Test.ThreadNet.Infra.Shelley
97120
, signTx
98121
)
99122
import Test.ThreadNet.Network
100-
( NodeOutput (..)
123+
( LeiosState (..)
124+
, NodeOutput (..)
101125
, TestNodeInitialization (..)
102126
, _FromLeios
103127
, _FromMempool
@@ -118,6 +142,8 @@ tests =
118142
"Leios ThreadNet"
119143
[ testProperty "EB production" $
120144
withMaxSuccess 1 prop_leios_blocksProduced
145+
, testProperty "EB certificate inclusion" $
146+
withMaxSuccess 1 prop_leios_ebCertificateInclusion
121147
]
122148

123149
prop_leios_blocksProduced :: Seed -> Property
@@ -144,6 +170,7 @@ prop_leios_blocksProduced seed =
144170
& counterexample ("mempool total rejected: " <> show (length mempoolRejectedTxs))
145171
& tabulate "Praos blocks forged" [show $ length forgedBlocks]
146172
& tabulate "Leios blocks forged" [show $ length forgedEBs]
173+
& tabulate "Certifying blocks" [show $ length certifyingBlocks]
147174
& tabulate "Effective throughput" [show throughput]
148175
where
149176
traces = testOutput.allTraces
@@ -174,58 +201,148 @@ prop_leios_blocksProduced seed =
174201
TraceMempoolRejectedTx tx _ _ -> Just tx
175202
_ -> Nothing
176203

204+
nodeChains = nodeOutputFinalChain <$> testOutput.testOutputNodes
205+
206+
certifyingBlocks =
207+
[ blk
208+
| blk@(BlockConway shelleyBlk) <- concatMap Chain.toOldestFirst nodeChains
209+
, SL.blockCertifiesEb (shelleyBlockRaw shelleyBlk)
210+
]
211+
177212
throughput = fromIntegral (sum includedTxCounts) / fromRational numSlots :: Double
178213

179-
testOutput =
214+
(testOutput, _protocolInfo) =
180215
runThreadNet seed (NumSlots $ ceiling numSlots) (NumCoreNodes $ fromIntegral numNodes)
181216

182217
numNodes = 3 :: Integer
183218

184219
numSlots = 200
185220

221+
-- | Verify that independently replaying the chain with EB resolution produces
222+
-- the same ledger state as the one computed by the ChainDB during the
223+
-- simulation. This proves that EB transactions from certified EBs are
224+
-- actually applied to the ledger.
225+
prop_leios_ebCertificateInclusion :: Seed -> Property
226+
prop_leios_ebCertificateInclusion seed =
227+
( not (null certifyingBlocks)
228+
& counterexample "no certifying blocks — test is vacuous"
229+
)
230+
.&&. (foldedLedger === expectedLedger)
231+
where
232+
(testOutput, ProtocolInfo{pInfoConfig, pInfoInitLedger}) =
233+
runThreadNet seed (NumSlots numSlots) (NumCoreNodes $ fromIntegral numNodes)
234+
235+
-- Pick any node — all nodes should converge to the same chain.
236+
someNode = snd . Map.findMin $ testOutput.testOutputNodes
237+
238+
nodeChains = nodeOutputFinalChain <$> testOutput.testOutputNodes
239+
240+
certifyingBlocks =
241+
[ blk
242+
| blk@(BlockConway shelleyBlk) <- concatMap Chain.toOldestFirst nodeChains
243+
, SL.blockCertifiesEb (shelleyBlockRaw shelleyBlk)
244+
]
245+
246+
-- nodeOutputFinalLedger has EmptyMK (tables stripped by ChainDB on
247+
-- extraction), so we strip the fold result's tables for comparison.
248+
expectedLedger = nodeOutputFinalLedger someNode
249+
250+
foldedLedger = replayNodeChain pInfoConfig pInfoInitLedger someNode
251+
252+
numNodes = 3 :: Integer
253+
numSlots = 200 :: Word64
254+
255+
-- | Replay a node's chain with Leios block resolution and return the
256+
-- resulting ledger state.
257+
replayNodeChain ::
258+
TopLevelConfig (CardanoBlock StandardCrypto) ->
259+
ExtLedgerState (CardanoBlock StandardCrypto) ValuesMK ->
260+
NodeOutput (CardanoBlock StandardCrypto) ->
261+
LedgerState (CardanoBlock StandardCrypto) EmptyMK
262+
replayNodeChain topConfig initLedger node = runSimOrThrow $ do
263+
let db = runIdentity . lsLeiosDb . nodeLeiosState $ node
264+
stateVar <- StrictTVar.newTVarIO db
265+
leiosDb <- newLeiosDBInMemoryWith stateVar
266+
let chain = Chain.toOldestFirst . nodeOutputFinalChain $ node
267+
cfg = ExtLedgerCfg topConfig
268+
foldedState <- foldWithResolution leiosDb cfg chain initLedger
269+
pure $ forgetLedgerTables . ledgerState $ foldedState
270+
271+
-- | Fold a chain of blocks over an initial ledger state, resolving Leios
272+
-- blocks (filling in EB transaction closures for certifying blocks) before
273+
-- each application.
274+
--
275+
-- We use 'tickThenReapply' because the blocks have already been validated
276+
-- by the ChainDB. We use 'applyDiffs' instead of 'applyDiffForKeys'
277+
-- because we need to accumulate the full UTxO — 'applyDiffForKeys' only
278+
-- retains entries referenced by the current block, which is designed for
279+
-- the LedgerDB's backing store architecture.
280+
foldWithResolution ::
281+
Monad m =>
282+
LeiosDbHandle m ->
283+
LedgerCfg (ExtLedgerState (CardanoBlock StandardCrypto)) ->
284+
[CardanoBlock StandardCrypto] ->
285+
ExtLedgerState (CardanoBlock StandardCrypto) ValuesMK ->
286+
m (ExtLedgerState (CardanoBlock StandardCrypto) ValuesMK)
287+
foldWithResolution leiosDb cfg blks initState =
288+
foldM step initState blks
289+
where
290+
step state blk = do
291+
blk' <- resolveLeiosBlock leiosDb state blk
292+
pure $ applyDiffs state $ tickThenReapply OmitLedgerEvents cfg blk' state
293+
186294
-- * Running the thread net
187295

188-
runThreadNet :: Seed -> NumSlots -> NumCoreNodes -> TestOutput (CardanoBlock StandardCrypto)
296+
runThreadNet ::
297+
Seed ->
298+
NumSlots ->
299+
NumCoreNodes ->
300+
(TestOutput (CardanoBlock StandardCrypto), ProtocolInfo (CardanoBlock StandardCrypto))
189301
runThreadNet initSeed numSlots numCoreNodes =
190-
runTestNetwork
191-
testConfig
192-
testConfigB
193-
TestConfigMB
194-
{ nodeInfo = \(CoreNodeId nid) ->
195-
let (protocolInfo, blockForging) =
196-
protocolInfoCardano
197-
CardanoProtocolParams
198-
{ byronProtocolParams =
199-
ProtocolParamsByron
200-
{ byronGenesis
201-
, byronPbftSignatureThreshold = Nothing
202-
, byronProtocolVersion = Byron.ProtocolVersion 0 0 0
203-
, byronSoftwareVersion = theProposedSoftwareVersion
204-
, byronLeaderCredentials = Nothing
205-
}
206-
, shelleyBasedProtocolParams =
207-
ProtocolParamsShelleyBased
208-
{ shelleyBasedInitialNonce = NeutralNonce
209-
, shelleyBasedLeaderCredentials =
210-
-- NOTE: Needed to hard-fork into shelley. After
211-
-- that, with d=0, it's stake based leaders.
212-
pure . mkLeaderCredentials $ coreNodes !! fromIntegral nid
213-
}
214-
, cardanoHardForkTriggers = hardForkInto Conway
215-
, cardanoLedgerTransitionConfig =
216-
-- TODO: provide better alonzo/conway genesis
217-
mkLatestTransitionConfig shelleyGenesis exampleAlonzoGenesis exampleConwayGenesis
218-
, cardanoCheckpoints = mempty
219-
, cardanoProtocolVersion = conwayProtVer
220-
}
221-
in TestNodeInitialization
222-
{ tniProtocolInfo = protocolInfo
223-
, tniCrucialTxs = []
224-
, tniBlockForging = blockForging
225-
}
226-
, mkRekeyM = Nothing
227-
}
302+
( runTestNetwork
303+
testConfig
304+
testConfigB
305+
TestConfigMB
306+
{ nodeInfo = \(CoreNodeId nid) ->
307+
let (protocolInfo, blockForging) = protocolInfoCardano (cardanoProtocolParams nid)
308+
in TestNodeInitialization
309+
{ tniProtocolInfo = protocolInfo
310+
, tniCrucialTxs = []
311+
, tniBlockForging = blockForging
312+
}
313+
, mkRekeyM = Nothing
314+
}
315+
, protocolInfo0
316+
)
228317
where
318+
protocolInfo0 = fst $ protocolInfoCardano @StandardCrypto @IO (cardanoProtocolParams (0 :: Word64))
319+
320+
cardanoProtocolParams nid =
321+
CardanoProtocolParams
322+
{ byronProtocolParams =
323+
ProtocolParamsByron
324+
{ byronGenesis
325+
, byronPbftSignatureThreshold = Nothing
326+
, byronProtocolVersion = Byron.ProtocolVersion 0 0 0
327+
, byronSoftwareVersion = theProposedSoftwareVersion
328+
, byronLeaderCredentials = Nothing
329+
}
330+
, shelleyBasedProtocolParams =
331+
ProtocolParamsShelleyBased
332+
{ shelleyBasedInitialNonce = NeutralNonce
333+
, shelleyBasedLeaderCredentials =
334+
-- NOTE: Needed to hard-fork into shelley. After
335+
-- that, with d=0, it's stake based leaders.
336+
pure . mkLeaderCredentials $ coreNodes !! fromIntegral nid
337+
}
338+
, cardanoHardForkTriggers = hardForkInto Conway
339+
, cardanoLedgerTransitionConfig =
340+
-- TODO: provide better alonzo/conway genesis
341+
mkLatestTransitionConfig shelleyGenesis exampleAlonzoGenesis exampleConwayGenesis
342+
, cardanoCheckpoints = mempty
343+
, cardanoProtocolVersion = conwayProtVer
344+
}
345+
229346
conwayProtVer = ProtVer (eraProtVerLow @ConwayEra) 0
230347

231348
NumCoreNodes n = numCoreNodes

ouroboros-consensus-diffusion/ouroboros-consensus-diffusion.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ test-suite mock-test
204204
Test.Consensus.Ledger.Mock.LedgerTables
205205
Test.ThreadNet.BFT
206206
Test.ThreadNet.LeaderSchedule
207+
Test.ThreadNet.LedgerStateConsistency
207208
Test.ThreadNet.PBFT
208209
Test.ThreadNet.Praos
209210

ouroboros-consensus-diffusion/test/mock-test/Main.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import qualified Test.Consensus.Ledger.Mock.LedgerTables (tests)
55
import Test.Tasty
66
import qualified Test.ThreadNet.BFT (tests)
77
import qualified Test.ThreadNet.LeaderSchedule (tests)
8+
import qualified Test.ThreadNet.LedgerStateConsistency (tests)
89
import qualified Test.ThreadNet.PBFT (tests)
910
import qualified Test.ThreadNet.Praos (tests)
1011
import Test.Util.TestEnv
@@ -22,6 +23,7 @@ tests =
2223
[ Test.Consensus.Ledger.Mock.tests
2324
, Test.Consensus.Ledger.Mock.LedgerTables.tests
2425
, Test.ThreadNet.BFT.tests
26+
, Test.ThreadNet.LedgerStateConsistency.tests
2527
, Test.ThreadNet.LeaderSchedule.tests
2628
, Test.ThreadNet.PBFT.tests
2729
, Test.ThreadNet.Praos.tests

0 commit comments

Comments
 (0)