Skip to content

Commit

Permalink
[ADP-3198] Compute primitive block via erafun (#4192)
Browse files Browse the repository at this point in the history
- [x] add block to primitive block era fun
- [x] encode `fromCardanoBlock` as an era dependent application

Benchmark note:

To prove that the change is not damaging performance too much I added a
benchmark covering the translation from ledger block to primitive block
that compares the previous version against the new one. Because the old
version is not more accessible at the end of the PR, this is the link to
the commit to checkout and re-run it
4a3c328
via `cabal bench -O2 cardano-wallet:benchmark:era-fun`

ADP-3198 ADP-3217
  • Loading branch information
paolino authored Nov 14, 2023
2 parents 7d23323 + 12f133a commit 48a1ebc
Show file tree
Hide file tree
Showing 24 changed files with 1,113 additions and 992 deletions.
1 change: 1 addition & 0 deletions lib/read/cardano-wallet-read.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ library
Cardano.Wallet.Read.Block.Gen.Shelley
Cardano.Wallet.Read.Block.HeaderHash
Cardano.Wallet.Read.Block.SlotNo
Cardano.Wallet.Read.Block.Txs
Cardano.Wallet.Read.Eras
Cardano.Wallet.Read.Eras.EraFun
Cardano.Wallet.Read.Eras.EraValue
Expand Down
82 changes: 11 additions & 71 deletions lib/read/lib/Cardano/Wallet/Read/Block.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- |
Expand All @@ -16,7 +13,6 @@ module Cardano.Wallet.Read.Block
( ConsensusBlock
, Block (..)
, fromConsensusBlock
, getTxs
, toConsensusBlock
) where

Expand All @@ -34,55 +30,30 @@ import Cardano.Api
import Cardano.Ledger.Api
( StandardCrypto
)
import Cardano.Ledger.Binary
( EncCBOR
)
import Cardano.Wallet.Read.Eras
( (:.:) (..)
, EraFun (..)
( EraFun (..)
, EraValue
, K (..)
, allegra
, alonzo
, applyEraFun
, babbage
, byron
, conway
, inject
, mary
, sequenceEraValue
, shelley
)
import Cardano.Wallet.Read.Tx
( Tx (..)
, TxT
)
import Data.Foldable
( toList
)
import Ouroboros.Consensus.Protocol.Praos
( Praos
)
import Ouroboros.Consensus.Protocol.TPraos
( TPraos
)
import Ouroboros.Consensus.Shelley.Protocol.Abstract
( ShelleyProtocolHeader
)

import qualified Cardano.Chain.Block as Byron
import qualified Cardano.Chain.UTxO as Byron
import qualified Cardano.Ledger.Api as Ledger
import qualified Cardano.Ledger.Era as Shelley
import qualified Cardano.Ledger.Shelley.API as Shelley
import qualified Ouroboros.Consensus.Byron.Ledger as Byron
import qualified Ouroboros.Consensus.Byron.Ledger as O
import qualified Ouroboros.Consensus.Cardano.Block as O
import qualified Ouroboros.Consensus.Shelley.Ledger as O

{-------------------------------------------------------------------------------
Block type
-------------------------------------------------------------------------------}
-- | Type synonym for 'CardanoBlock' with cryptography as used on mainnet.
type ConsensusBlock = O.CardanoBlock O.StandardCrypto

Expand All @@ -107,35 +78,6 @@ newtype Block era = Block {unBlock :: BlockT era}
deriving instance Show (BlockT era) => Show (Block era)
deriving instance Eq (BlockT era) => Eq (Block era)

-- | Get sequence of transactions in the block.
txsFromBlockE :: EraFun Block ([] :.: Tx)
txsFromBlockE =
EraFun
{ byronFun = getTxs' getTxsFromBlockByron
, shelleyFun = getTxs' getTxsFromBlockShelleyAndOn
, maryFun = getTxs' getTxsFromBlockShelleyAndOn
, allegraFun = getTxs' getTxsFromBlockShelleyAndOn
, alonzoFun = getTxs' getTxsFromBlockShelleyAndOn
, babbageFun = getTxs' getTxsFromBlockShelleyAndOn
, conwayFun = getTxs' getTxsFromBlockShelleyAndOn
}
where
getTxs' f (Block block) = Comp $ Tx <$> f block

getTxsFromBlockByron :: O.ByronBlock -> [TxT ByronEra]
getTxsFromBlockByron block =
case Byron.byronBlockRaw block of
Byron.ABOBBlock b ->
map (() <$) . Byron.unTxPayload . Byron.blockTxPayload $ b
Byron.ABOBBoundary _ -> []

getTxsFromBlockShelleyAndOn
:: (Shelley.EraSegWits era, EncCBOR (ShelleyProtocolHeader proto))
=> O.ShelleyBlock proto era
-> [Ledger.Tx era]
getTxsFromBlockShelleyAndOn (O.ShelleyBlock (Shelley.Block _ txs) _) =
toList (Shelley.fromTxSeq txs)

-- | Convert block as received from cardano-node
-- via Haskell library of mini-protocol.
fromConsensusBlock :: ConsensusBlock -> EraValue Block
Expand All @@ -148,16 +90,14 @@ fromConsensusBlock = \case
O.BlockBabbage block -> inject babbage $ Block block
O.BlockConway block -> inject conway $ Block block

getTxs :: O.CardanoBlock StandardCrypto -> [EraValue Tx]
getTxs = sequenceEraValue . applyEraFun txsFromBlockE . fromConsensusBlock

toConsensusBlock :: EraFun Block (K ConsensusBlock)
toConsensusBlock = EraFun
{ byronFun = K . O.BlockByron . unBlock
, shelleyFun = K . O.BlockShelley . unBlock
, allegraFun = K . O.BlockAllegra . unBlock
, maryFun = K . O.BlockMary . unBlock
, alonzoFun = K . O.BlockAlonzo . unBlock
, babbageFun = K . O.BlockBabbage . unBlock
, conwayFun = K . O.BlockConway . unBlock
}
toConsensusBlock =
EraFun
{ byronFun = K . O.BlockByron . unBlock
, shelleyFun = K . O.BlockShelley . unBlock
, allegraFun = K . O.BlockAllegra . unBlock
, maryFun = K . O.BlockMary . unBlock
, alonzoFun = K . O.BlockAlonzo . unBlock
, babbageFun = K . O.BlockBabbage . unBlock
, conwayFun = K . O.BlockConway . unBlock
}
4 changes: 4 additions & 0 deletions lib/read/lib/Cardano/Wallet/Read/Block/HeaderHash.hs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import qualified Ouroboros.Consensus.Shelley.Ledger.Block as O
import qualified Ouroboros.Consensus.Shelley.Protocol.Abstract as Shelley
import qualified Ouroboros.Network.Block as O

-- | Era-specific header hash type from the ledger
type family HeaderHashT era where
HeaderHashT ByronEra = ByronHash
HeaderHashT ShelleyEra = ShelleyHash StandardCrypto
Expand All @@ -70,6 +71,7 @@ type family HeaderHashT era where
HeaderHashT BabbageEra = ShelleyHash StandardCrypto
HeaderHashT ConwayEra = ShelleyHash StandardCrypto

-- | Era-specific header hash type from the ledger
newtype HeaderHash era = HeaderHash (HeaderHashT era)

getEraHeaderHash :: EraFun Block HeaderHash
Expand All @@ -96,6 +98,7 @@ getHeaderHashShelley
getHeaderHashShelley
(O.ShelleyBlock (Shelley.Block header _) _) = Shelley.pHeaderHash header

-- | Era-specific previous header hash type from the ledger
type family PrevHeaderHashT era where
PrevHeaderHashT ByronEra = O.ChainHash ByronBlock
PrevHeaderHashT ShelleyEra = PrevHash StandardCrypto
Expand All @@ -105,6 +108,7 @@ type family PrevHeaderHashT era where
PrevHeaderHashT BabbageEra = PrevHash StandardCrypto
PrevHeaderHashT ConwayEra = PrevHash StandardCrypto

-- | Era-specific previous header hash type from the ledger
newtype PrevHeaderHash era = PrevHeaderHash (PrevHeaderHashT era)

getPrevHeaderHashShelley
Expand Down
70 changes: 70 additions & 0 deletions lib/read/lib/Cardano/Wallet/Read/Block/Txs.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

module Cardano.Wallet.Read.Block.Txs
( getEraTransactions
) where

import Prelude

import Cardano.Api
( ByronEra
)
import Cardano.Ledger.Binary
( EncCBOR
)
import Cardano.Wallet.Read.Block
( Block (..)
)
import Cardano.Wallet.Read.Eras
( (:.:) (..)
, EraFun (..)
)
import Cardano.Wallet.Read.Tx
( Tx (..)
, TxT
)
import Data.Foldable
( toList
)
import Ouroboros.Consensus.Shelley.Protocol.Abstract
( ShelleyProtocolHeader
)

import qualified Cardano.Chain.Block as Byron
import qualified Cardano.Chain.UTxO as Byron
import qualified Cardano.Ledger.Api as Ledger
import qualified Cardano.Ledger.Era as Shelley
import qualified Cardano.Ledger.Shelley.API as Shelley
import qualified Ouroboros.Consensus.Byron.Ledger as Byron
import qualified Ouroboros.Consensus.Byron.Ledger as O
import qualified Ouroboros.Consensus.Shelley.Ledger as O

-- | Get the list of transactions in the block.
getEraTransactions :: EraFun Block ([] :.: Tx)
getEraTransactions =
EraFun
{ byronFun = getTxs' getTxsFromBlockByron
, shelleyFun = getTxs' getTxsFromBlockShelleyAndOn
, maryFun = getTxs' getTxsFromBlockShelleyAndOn
, allegraFun = getTxs' getTxsFromBlockShelleyAndOn
, alonzoFun = getTxs' getTxsFromBlockShelleyAndOn
, babbageFun = getTxs' getTxsFromBlockShelleyAndOn
, conwayFun = getTxs' getTxsFromBlockShelleyAndOn
}
where
getTxs' f (Block block) = Comp $ Tx <$> f block

getTxsFromBlockByron :: O.ByronBlock -> [TxT ByronEra]
getTxsFromBlockByron block =
case Byron.byronBlockRaw block of
Byron.ABOBBlock b -> Byron.unTxPayload . Byron.blockTxPayload $ b
Byron.ABOBBoundary _ -> []

getTxsFromBlockShelleyAndOn
:: (Shelley.EraSegWits era, EncCBOR (ShelleyProtocolHeader proto))
=> O.ShelleyBlock proto era
-> [Ledger.Tx era]
getTxsFromBlockShelleyAndOn (O.ShelleyBlock (Shelley.Block _ txs) _) =
toList (Shelley.fromTxSeq txs)
61 changes: 58 additions & 3 deletions lib/read/lib/Cardano/Wallet/Read/Eras/EraFun.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
-- A datatype that represents a vector of functions covering all known eras
-- The functions are supposed to map values from and to the same era.
--
-- We removed the cached encoding at the price of 'toEraFun' and 'fromEraFun'
-- We removed the cached encoding at the price of 'MkEraFun' and 'fromEraFun'
-- during all compositions, we are not 100% it's not relevant for performance
-- If the computed functions after record compositions are the same then we can
-- avoid that layer
--
-- Note composition is anyway expansive, do not recompose,
-- Note:
-- composition is anyway expansive, do not recompose,
-- just cache and reuse the compositions
module Cardano.Wallet.Read.Eras.EraFun
( -- * Types.
Expand All @@ -40,6 +41,7 @@ module Cardano.Wallet.Read.Eras.EraFun
-- * Composition.
, (*.**)
, (*&&&*)
, (*****)

-- * Application.
, applyEraFun
Expand All @@ -60,6 +62,9 @@ module Cardano.Wallet.Read.Eras.EraFun
, babbageVal
, conwayVal
)
, liftK
, mapOnEraFun
, CollectTuple (..)
)
where

Expand Down Expand Up @@ -197,14 +202,16 @@ composeEraFunWith
-> EraFunI f (w h)
composeEraFunWith q = zipWith_NP (\(Fn f') (Fn g') -> Fn $ q f' g')

infixr 9 *&&&*
infixr 8 *&&&*

-- | Compose 2 EraFunI as parallel application using '(:*:)'.
(*&&&*) :: EraFun f g -> EraFun f h -> EraFun f (g :*: h)
f *&&&* g = MkEraFun $ zipWith_NP r (fromEraFun f) (fromEraFun g)
where
r (Fn f') (Fn g') = Fn $ \x -> f' x :*: g' x

-- | A type of EraFun with a constant output functor
-- wrapped up to be an applicative.
newtype EraFunK src ft = EraFunK {fromEraFunK :: EraFun src (K ft)}

instance Functor (EraFunK src) where
Expand Down Expand Up @@ -284,3 +291,51 @@ runAllEraValue (AllEraValue v) = collapse_NP $ zipWith_NP q prisms (fromEraFun v
where
q :: MkEraValue f era -> (K () -.-> f) era -> K (EraValue f) era
q p (Fn f) = K $ inject p $ f (K ())

-- | Lift an internal K of the output functor of the EraFun
liftK :: Functor g => EraFun f (g :.: K a) -> EraFun f (K (g a))
liftK f = MkEraFun $ map_NP q $ fromEraFun f
where
q (Fn h) = Fn $ deComp . h
deComp :: Functor g => (g :.: K a) era -> K (g a) era
deComp (Comp l) = K $ unK <$> l

-- | Map on the output of an EraFun with an era independent function
mapOnEraFun
:: forall f g h
. (forall a. g a -> h a)
-> EraFun f g
-> EraFun f h
mapOnEraFun f e = MkEraFun $ map_NP g $ fromEraFun e
where
g :: (-.->) f g a -> (-.->) f h a
g (Fn f') = Fn $ f . f'

infixr 9 *****

-- | Compose 2 EraFun as parallel application.
(*****) :: EraFun f g -> EraFun h k -> EraFun (f :*: h) (g :*: k)
f ***** g = MkEraFun $ zipWith_NP r (fromEraFun f) (fromEraFun g)
where
r (Fn f') (Fn g') = Fn $ \(x :*: y) -> f' x :*: g' y

-- | A type family that computes the tuple type from a product type of Ks.
type family TupleFromProduct f where
TupleFromProduct (K f :*: K g) = (f, g)
TupleFromProduct (K f :*: g) = (f, TupleFromProduct g)

class CollectTuple f where
-- | Collect a tuple from a product of Ks.
collectTuple :: f x -> K (TupleFromProduct f) x

instance CollectTuple (K f :*: K g) where
collectTuple (K f :*: K g) = K (f, g)

instance
{-# OVERLAPS #-}
(CollectTuple g, TupleFromProduct (K f :*: g) ~ (f, TupleFromProduct g))
=> CollectTuple (K f :*: g)
where
collectTuple (K f :*: g) =
let K g' = collectTuple g
in K (f, g')
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ data ParsedTxCBOR = ParsedTxCBOR
parser :: EraFun Tx (K ParsedTxCBOR)
parser = fromEraFunK
$ ParsedTxCBOR
<$> EraFunK (Feature.certificates <<< getEraCertificates)
<$> EraFunK (Feature.primitiveCertificates <<< getEraCertificates)
<*> EraFunK (Feature.mint <<<
getEraMint *&&&* getEraWitnesses *&&&* getEraReferenceInputs)
<*> EraFunK (Feature.getValidity <<< getEraValidity)
Expand Down
Loading

0 comments on commit 48a1ebc

Please sign in to comment.