-
Notifications
You must be signed in to change notification settings - Fork 20
/
A.hs
589 lines (472 loc) · 20.3 KB
/
A.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE EmptyDataDeriving #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module Test.Consensus.HardFork.Combinator.A (
BlockA (..)
, ProtocolA
, blockForgingA
, safeFromTipA
, stabilityWindowA
-- * Additional types
, PartialLedgerConfigA (..)
, TxPayloadA (..)
-- * Type family instances
, BlockConfig (..)
, CodecConfig (..)
, ConsensusConfig (..)
, GenTx (..)
, Header (..)
, LedgerState (..)
, NestedCtxt_ (..)
, StorageConfig (..)
, TxId (..)
) where
import Cardano.Slotting.EpochInfo
import Codec.Serialise
import Control.Monad (guard)
import Control.Monad.Except (runExcept)
import qualified Data.Binary as B
import Data.ByteString as Strict
import qualified Data.ByteString.Lazy as Lazy
import qualified Data.ByteString.Short as SBS
import Data.Functor.Identity (Identity)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Void
import Data.Word
import GHC.Generics (Generic)
import NoThunks.Class (NoThunks, OnlyCheckWhnfNamed (..))
import Ouroboros.Consensus.Block
import Ouroboros.Consensus.BlockchainTime
import Ouroboros.Consensus.Config
import Ouroboros.Consensus.Config.SupportsNode
import Ouroboros.Consensus.Forecast
import Ouroboros.Consensus.HardFork.Combinator
import Ouroboros.Consensus.HardFork.Combinator.Condense
import Ouroboros.Consensus.HardFork.Combinator.Serialisation.Common
import Ouroboros.Consensus.HardFork.History (Bound (..),
EraParams (..))
import qualified Ouroboros.Consensus.HardFork.History as History
import Ouroboros.Consensus.HeaderValidation
import Ouroboros.Consensus.Ledger.Abstract
import Ouroboros.Consensus.Ledger.CommonProtocolParams
import Ouroboros.Consensus.Ledger.Inspect
import Ouroboros.Consensus.Ledger.Query
import Ouroboros.Consensus.Ledger.SupportsMempool
import Ouroboros.Consensus.Ledger.SupportsPeerSelection
import Ouroboros.Consensus.Ledger.SupportsProtocol
import Ouroboros.Consensus.Node.InitStorage
import Ouroboros.Consensus.Node.NetworkProtocolVersion
import Ouroboros.Consensus.Node.Run
import Ouroboros.Consensus.Node.Serialisation
import Ouroboros.Consensus.Protocol.Abstract
import Ouroboros.Consensus.Storage.ImmutableDB (simpleChunkInfo)
import Ouroboros.Consensus.Storage.Serialisation
import Ouroboros.Consensus.Util (repeatedlyM, (..:), (.:))
import Ouroboros.Consensus.Util.Condense
import Ouroboros.Consensus.Util.Orphans ()
import Ouroboros.Network.Block (Serialised, unwrapCBORinCBOR,
wrapCBORinCBOR)
import Ouroboros.Network.Magic
import Test.Cardano.Slotting.Numeric ()
import Test.Util.Time (dawnOfTime)
{-------------------------------------------------------------------------------
BlockA
-------------------------------------------------------------------------------}
data ProtocolA
data instance ConsensusConfig ProtocolA = CfgA {
cfgA_k :: SecurityParam
, cfgA_leadInSlots :: Set SlotNo
}
deriving NoThunks via OnlyCheckWhnfNamed "CfgA" (ConsensusConfig ProtocolA)
instance ConsensusProtocol ProtocolA where
type ChainDepState ProtocolA = ()
type LedgerView ProtocolA = ()
type IsLeader ProtocolA = ()
type CanBeLeader ProtocolA = ()
type ValidateView ProtocolA = ()
type ValidationErr ProtocolA = Void
checkIsLeader CfgA{..} () slot _ =
if slot `Set.member` cfgA_leadInSlots
then Just ()
else Nothing
protocolSecurityParam = cfgA_k
tickChainDepState _ _ _ _ = TickedTrivial
updateChainDepState _ _ _ _ = return ()
reupdateChainDepState _ _ _ _ = ()
data BlockA = BlkA {
blkA_header :: Header BlockA
, blkA_body :: [GenTx BlockA]
}
deriving stock (Show, Eq, Generic)
deriving anyclass (Serialise)
deriving NoThunks via OnlyCheckWhnfNamed "BlkA" BlockA
data instance Header BlockA = HdrA {
hdrA_fields :: HeaderFields BlockA
, hdrA_prev :: ChainHash BlockA
}
deriving stock (Show, Eq, Generic)
deriving anyclass (Serialise)
deriving NoThunks via OnlyCheckWhnfNamed "HdrA" (Header BlockA)
instance GetHeader BlockA where
getHeader = blkA_header
blockMatchesHeader = \_ _ -> True -- We are not interested in integrity here
headerIsEBB = const Nothing
data instance BlockConfig BlockA = BCfgA
deriving (Generic, NoThunks)
type instance BlockProtocol BlockA = ProtocolA
type instance HeaderHash BlockA = Strict.ByteString
data instance CodecConfig BlockA = CCfgA
deriving (Generic, NoThunks)
data instance StorageConfig BlockA = SCfgA
deriving (Generic, NoThunks)
instance ConfigSupportsNode BlockA where
getSystemStart _ = SystemStart dawnOfTime
getNetworkMagic _ = NetworkMagic 0
instance StandardHash BlockA
instance HasHeader BlockA where
getHeaderFields = getBlockHeaderFields
instance HasHeader (Header BlockA) where
getHeaderFields = castHeaderFields . hdrA_fields
instance GetPrevHash BlockA where
headerPrevHash = hdrA_prev
instance HasAnnTip BlockA where
instance BasicEnvelopeValidation BlockA where
-- Use defaults
instance ValidateEnvelope BlockA where
data instance LedgerState BlockA = LgrA {
lgrA_tip :: Point BlockA
-- | The 'SlotNo' of the block containing the 'InitiateAtoB' transaction
, lgrA_transition :: Maybe SlotNo
}
deriving (Show, Eq, Generic, Serialise)
deriving NoThunks via OnlyCheckWhnfNamed "LgrA" (LedgerState BlockA)
-- | Ticking has no state on the A ledger state
newtype instance Ticked (LedgerState BlockA) = TickedLedgerStateA {
getTickedLedgerStateA :: LedgerState BlockA
}
deriving NoThunks via OnlyCheckWhnfNamed "TickedLgrA" (Ticked (LedgerState BlockA))
data PartialLedgerConfigA = LCfgA {
lcfgA_k :: SecurityParam
, lcfgA_systemStart :: SystemStart
, lcfgA_forgeTxs :: Map SlotNo [GenTx BlockA]
}
deriving NoThunks via OnlyCheckWhnfNamed "LCfgA" PartialLedgerConfigA
type instance LedgerCfg (LedgerState BlockA) =
(EpochInfo Identity, PartialLedgerConfigA)
instance GetTip (LedgerState BlockA) where
getTip = castPoint . lgrA_tip
instance GetTip (Ticked (LedgerState BlockA)) where
getTip = castPoint . getTip . getTickedLedgerStateA
instance IsLedger (LedgerState BlockA) where
type LedgerErr (LedgerState BlockA) = Void
type AuxLedgerEvent (LedgerState BlockA) =
VoidLedgerEvent (LedgerState BlockA)
applyChainTickLedgerResult _ _ = pureLedgerResult . TickedLedgerStateA
instance ApplyBlock (LedgerState BlockA) BlockA where
applyBlockLedgerResult cfg blk =
fmap (pureLedgerResult . setTip)
. repeatedlyM
(fmap fst .: applyTx cfg DoNotIntervene (blockSlot blk))
(blkA_body blk)
where
setTip :: TickedLedgerState BlockA -> LedgerState BlockA
setTip (TickedLedgerStateA st) = st { lgrA_tip = blockPoint blk }
reapplyBlockLedgerResult =
dontExpectError ..: applyBlockLedgerResult
where
dontExpectError :: Except a b -> b
dontExpectError mb = case runExcept mb of
Left _ -> error "reapplyBlockLedgerResult: unexpected error"
Right b -> b
instance UpdateLedger BlockA
instance CommonProtocolParams BlockA where
maxHeaderSize _ = maxBound
maxTxSize _ = maxBound
instance BlockSupportsProtocol BlockA where
validateView _ _ = ()
instance LedgerSupportsProtocol BlockA where
protocolLedgerView _ _ = ()
ledgerViewForecastAt _ = trivialForecast
instance HasPartialConsensusConfig ProtocolA
instance HasPartialLedgerConfig BlockA where
type PartialLedgerConfig BlockA = PartialLedgerConfigA
completeLedgerConfig _ ei pcfg = (History.toPureEpochInfo ei, pcfg)
data TxPayloadA = InitiateAtoB
deriving (Show, Eq, Generic, NoThunks, Serialise)
type instance CannotForge BlockA = Void
type instance ForgeStateInfo BlockA = ()
type instance ForgeStateUpdateError BlockA = Void
forgeBlockA ::
TopLevelConfig BlockA
-> BlockNo
-> SlotNo
-> TickedLedgerState BlockA
-> [GenTx BlockA]
-> IsLeader (BlockProtocol BlockA)
-> BlockA
forgeBlockA tlc bno sno (TickedLedgerStateA st) _txs _ = BlkA {
blkA_header = HdrA {
hdrA_fields = HeaderFields {
headerFieldHash = Lazy.toStrict . B.encode $ unSlotNo sno
, headerFieldSlot = sno
, headerFieldBlockNo = bno
}
, hdrA_prev = ledgerTipHash st
}
, blkA_body = Map.findWithDefault [] sno (lcfgA_forgeTxs ledgerConfig)
}
where
ledgerConfig :: PartialLedgerConfig BlockA
ledgerConfig = snd $ configLedger tlc
blockForgingA :: Monad m => BlockForging m BlockA
blockForgingA = BlockForging {
forgeLabel = "BlockA"
, canBeLeader = ()
, updateForgeState = \_ _ _ -> return $ ForgeStateUpdated ()
, checkCanForge = \_ _ _ _ _ -> return ()
, forgeBlock = \cfg bno slot st txs proof -> return $
forgeBlockA cfg bno slot st (fmap txForgetValidated txs) proof
}
-- | See 'Ouroboros.Consensus.HardFork.History.EraParams.safeFromTip'
safeFromTipA :: SecurityParam -> Word64
safeFromTipA (SecurityParam k) = k
-- | This mock ledger assumes that every node is honest and online, every slot
-- has a single leader, and ever message arrives before the next slot. So a run
-- of @k@ slots is guaranteed to extend the chain by @k@ blocks.
stabilityWindowA :: SecurityParam -> Word64
stabilityWindowA (SecurityParam k) = k
data instance GenTx BlockA = TxA {
txA_id :: TxId (GenTx BlockA)
, txA_payload :: TxPayloadA
}
deriving (Show, Eq, Generic, Serialise)
deriving NoThunks via OnlyCheckWhnfNamed "TxA" (GenTx BlockA)
newtype instance Validated (GenTx BlockA) = ValidatedGenTxA { forgetValidatedGenTxA :: GenTx BlockA }
deriving stock (Show)
deriving newtype (Generic, Eq)
deriving anyclass (NoThunks)
type instance ApplyTxErr BlockA = Void
instance LedgerSupportsMempool BlockA where
applyTx _ _wti sno tx@(TxA _ payload) (TickedLedgerStateA st) =
case payload of
InitiateAtoB -> do
return (TickedLedgerStateA $ st { lgrA_transition = Just sno }, ValidatedGenTxA tx)
reapplyTx cfg slot = fmap fst .: (applyTx cfg DoNotIntervene slot . forgetValidatedGenTxA)
txsMaxBytes _ = maxBound
txInBlockSize _ = 0
txForgetValidated = forgetValidatedGenTxA
newtype instance TxId (GenTx BlockA) = TxIdA Int
deriving stock (Show, Eq, Ord, Generic)
deriving newtype (NoThunks, Serialise)
instance HasTxId (GenTx BlockA) where
txId = txA_id
instance ConvertRawTxId (GenTx BlockA) where
toRawTxIdHash = SBS.toShort . Lazy.toStrict . serialise
instance ShowQuery (BlockQuery BlockA) where
showResult qry = case qry of {}
data instance BlockQuery BlockA result
deriving (Show)
instance BlockSupportsLedgerQuery BlockA where
answerBlockQuery _ qry = case qry of {}
instance SameDepIndex (BlockQuery BlockA) where
sameDepIndex qry _qry' = case qry of {}
instance ConvertRawHash BlockA where
toRawHash _ = id
fromRawHash _ = id
hashSize _ = 8 -- We use the SlotNo as the hash, which is Word64
data instance NestedCtxt_ BlockA f a where
CtxtA :: NestedCtxt_ BlockA f (f BlockA)
deriving instance Show (NestedCtxt_ BlockA f a)
instance SameDepIndex (NestedCtxt_ BlockA f)
instance TrivialDependency (NestedCtxt_ BlockA f) where
type TrivialIndex (NestedCtxt_ BlockA f) = f BlockA
hasSingleIndex CtxtA CtxtA = Refl
indexIsTrivial = CtxtA
instance EncodeDisk BlockA (Header BlockA)
instance DecodeDisk BlockA (Lazy.ByteString -> Header BlockA) where
decodeDisk _ = const <$> decode
instance EncodeDiskDepIx (NestedCtxt Header) BlockA
instance EncodeDiskDep (NestedCtxt Header) BlockA
instance DecodeDiskDepIx (NestedCtxt Header) BlockA
instance DecodeDiskDep (NestedCtxt Header) BlockA
instance HasNestedContent Header BlockA where
-- Use defaults
instance ReconstructNestedCtxt Header BlockA
-- Use defaults
instance LedgerSupportsPeerSelection BlockA where
getPeers = const []
data UpdateA =
ProposalSubmitted
| ProposalStable
deriving (Show, Eq)
instance Condense UpdateA where
condense = show
instance InspectLedger BlockA where
type LedgerWarning BlockA = Void
type LedgerUpdate BlockA = UpdateA
inspectLedger cfg before after =
case (getConfirmationDepth before, getConfirmationDepth after) of
(Nothing, Just _) ->
return $ LedgerUpdate ProposalSubmitted
(Just (_, d), Just (_, d')) -> do
guard $ d < k && d' >= k
return $ LedgerUpdate ProposalStable
_otherwise ->
[]
where
k = stabilityWindowA (lcfgA_k (snd (configLedger cfg)))
getConfirmationDepth :: LedgerState BlockA -> Maybe (SlotNo, Word64)
getConfirmationDepth st = do
confirmedInSlot <- lgrA_transition st
return $ case ledgerTipSlot st of
Origin -> error "impossible"
NotOrigin s -> if s < confirmedInSlot
then error "impossible"
else ( confirmedInSlot
, History.countSlots s confirmedInSlot
)
instance NodeInitStorage BlockA where
nodeCheckIntegrity _ _ = True
-- Pick some chunk size
nodeImmutableDbChunkInfo _ = simpleChunkInfo 10
instance BlockSupportsMetrics BlockA where
isSelfIssued = isSelfIssuedConstUnknown
deriving via SelectViewDiffusionPipelining BlockA
instance BlockSupportsDiffusionPipelining BlockA
instance SingleEraBlock BlockA where
singleEraInfo _ = SingleEraInfo "A"
singleEraTransition cfg EraParams{..} eraStart st = do
(confirmedInSlot, confirmationDepth) <- getConfirmationDepth st
-- The ledger must report the scheduled transition to the next era as soon
-- as the block containing this transaction is immutable (that is, at
-- least @k@ blocks have come after) -- this happens elsewhere in the
-- corresponding 'SingleEraBlock' instance. It must not report it sooner
-- than that because the consensus layer requires that conversions about
-- time (when successful) must not be subject to rollback.
guard $ confirmationDepth >= stabilityWindowA (lcfgA_k cfg)
-- Consensus /also/ insists that as long as the transition to the next era
-- is not yet known (ie not yet determined by an immutable block), there
-- is a safe zone that extends past the tip of the ledger in which we
-- guarantee the next era will not begin. This means that we must have an
-- additional @safeFromTipA k@ blocks /after/ reporting the transition and
-- /before/ the start of the next era.
--
-- Thus, we schedule the next era to begin with the first upcoming epoch
-- that starts /after/ we're guaranteed to see both the aforementioned @k@
-- additional blocks and also a further @safeFromTipA k@ slots after the
-- last of those.
let -- The last slot that must be in the current era
firstPossibleLastSlotThisEra =
History.addSlots
(stabilityWindowA k + safeFromTipA k)
confirmedInSlot
-- The 'EpochNo' corresponding to 'firstPossibleLastSlotThisEra'
lastEpochThisEra = slotToEpoch firstPossibleLastSlotThisEra
-- The first epoch that may be in the next era
-- (recall: eras are epoch-aligned)
firstEpochNextEra = succ lastEpochThisEra
return firstEpochNextEra
where
k = lcfgA_k cfg
-- Slot conversion (valid for slots in this era only)
slotToEpoch :: SlotNo -> EpochNo
slotToEpoch s =
History.addEpochs
(History.countSlots s (boundSlot eraStart) `div` unEpochSize eraEpochSize)
(boundEpoch eraStart)
instance HasTxs BlockA where
extractTxs = blkA_body
{-------------------------------------------------------------------------------
Condense
-------------------------------------------------------------------------------}
instance CondenseConstraints BlockA
instance Condense BlockA where condense = show
instance Condense (Header BlockA) where condense = show
instance Condense (GenTx BlockA) where condense = show
instance Condense (TxId (GenTx BlockA)) where condense = show
{-------------------------------------------------------------------------------
Top-level serialisation constraints
-------------------------------------------------------------------------------}
instance HasBinaryBlockInfo BlockA where
-- Standard cborg generic serialisation is:
--
-- > [number of fields in the product]
-- > [tag of the constructor]
-- > field1
-- > ..
-- > fieldN
getBinaryBlockInfo BlkA{..} = BinaryBlockInfo {
headerOffset = 2
, headerSize = fromIntegral $ Lazy.length (serialise blkA_header)
}
instance SerialiseConstraintsHFC BlockA
instance SerialiseDiskConstraints BlockA
instance SerialiseNodeToClientConstraints BlockA
instance SerialiseNodeToNodeConstraints BlockA where
estimateBlockSize = const 0
{-------------------------------------------------------------------------------
SerialiseDiskConstraints
-------------------------------------------------------------------------------}
deriving instance Serialise (AnnTip BlockA)
instance EncodeDisk BlockA (LedgerState BlockA)
instance DecodeDisk BlockA (LedgerState BlockA)
instance EncodeDisk BlockA BlockA
instance DecodeDisk BlockA (Lazy.ByteString -> BlockA) where
decodeDisk _ = const <$> decode
instance EncodeDisk BlockA (AnnTip BlockA)
instance DecodeDisk BlockA (AnnTip BlockA)
instance EncodeDisk BlockA ()
instance DecodeDisk BlockA ()
instance HasNetworkProtocolVersion BlockA
{-------------------------------------------------------------------------------
SerialiseNodeToNode
-------------------------------------------------------------------------------}
instance SerialiseNodeToNode BlockA BlockA
instance SerialiseNodeToNode BlockA Strict.ByteString
instance SerialiseNodeToNode BlockA (Serialised BlockA)
instance SerialiseNodeToNode BlockA (SerialisedHeader BlockA)
instance SerialiseNodeToNode BlockA (GenTx BlockA)
instance SerialiseNodeToNode BlockA (GenTxId BlockA)
-- Must be compatible with @(SerialisedHeader BlockA)@, which uses
-- the @Serialise (SerialisedHeader BlockA)@ instance below
instance SerialiseNodeToNode BlockA (Header BlockA) where
encodeNodeToNode _ _ = wrapCBORinCBOR encode
decodeNodeToNode _ _ = unwrapCBORinCBOR (const <$> decode)
instance Serialise (SerialisedHeader BlockA) where
encode = encodeTrivialSerialisedHeader
decode = decodeTrivialSerialisedHeader
{-------------------------------------------------------------------------------
SerialiseNodeToClient
-------------------------------------------------------------------------------}
instance SerialiseNodeToClient BlockA BlockA
instance SerialiseNodeToClient BlockA (Serialised BlockA)
instance SerialiseNodeToClient BlockA (GenTx BlockA)
instance SerialiseNodeToClient BlockA (GenTxId BlockA)
instance SerialiseNodeToClient BlockA SlotNo
instance SerialiseNodeToClient BlockA Void where
encodeNodeToClient _ _ = absurd
decodeNodeToClient _ _ = fail "no ApplyTxErr to be decoded"
instance SerialiseNodeToClient BlockA (SomeSecond BlockQuery BlockA) where
encodeNodeToClient _ _ = \case {}
decodeNodeToClient _ _ = fail "there are no queries to be decoded"
instance SerialiseResult BlockA (BlockQuery BlockA) where
encodeResult _ _ = \case {}
decodeResult _ _ = \case {}