@@ -21,6 +21,7 @@ module Cardano.Ledger.Shelley.BlockChain (
2121 auxDataSeqDecoder ,
2222 txSeqTxns ,
2323 bbHash ,
24+ hashShelleySegWits ,
2425 bBodySize ,
2526 slotToNonce ,
2627 --
@@ -55,6 +56,7 @@ import Cardano.Ledger.Shelley.Tx (ShelleyTx (..))
5556import Cardano.Ledger.Slot (SlotNo (.. ))
5657import Control.Monad (unless )
5758import Data.ByteString (ByteString )
59+ import Data.ByteString.Builder (Builder , shortByteString , toLazyByteString )
5860import qualified Data.ByteString.Lazy as BSL
5961import Data.Coerce (coerce )
6062import Data.IntMap (IntMap )
@@ -72,23 +74,29 @@ import NoThunks.Class (AllowThunksIn (..), NoThunks (..))
7274
7375data ShelleyTxSeq era = TxSeq'
7476 { txSeqTxns' :: ! (StrictSeq (ShelleyTx era ))
77+ , txSeqHash :: Hash. Hash HASH EraIndependentBlockBody
78+ -- ^ Memoized hash to avoid recomputation. Lazy on purpose.
7579 , txSeqBodyBytes :: BSL. ByteString
80+ -- ^ Bytes encoding @Seq ('TxBody' era)@
7681 , txSeqWitsBytes :: BSL. ByteString
82+ -- ^ Bytes encoding @Seq ('TxWits' era)@
7783 , txSeqMetadataBytes :: BSL. ByteString
78- -- bytes representing a (Map index metadata). Missing indices have SNothing for metadata
84+ -- ^ Bytes encoding a @Seq ('TxAuxData' era)@. Missing indices have
85+ -- 'SNothing' for metadata
7986 }
8087 deriving (Generic )
8188
8289instance EraSegWits ShelleyEra where
8390 type TxSeq ShelleyEra = ShelleyTxSeq ShelleyEra
8491 fromTxSeq = txSeqTxns
8592 toTxSeq = ShelleyTxSeq
86- hashTxSeq = bbHash
93+ hashTxSeq = txSeqHash
8794 numSegComponents = 3
8895
8996deriving via
9097 AllowThunksIn
91- '[ " txSeqBodyBytes"
98+ '[ " txSeqHash"
99+ , " txSeqBodyBytes"
92100 , " txSeqWitsBytes"
93101 , " txSeqMetadataBytes"
94102 ]
@@ -131,7 +139,7 @@ pattern ShelleyTxSeq ::
131139 StrictSeq (Tx era) ->
132140 ShelleyTxSeq era
133141pattern ShelleyTxSeq xs <-
134- TxSeq' xs _ _ _
142+ TxSeq' xs _ _ _ _
135143 where
136144 ShelleyTxSeq txns =
137145 let version = eraProtVerLow @ era
@@ -141,29 +149,32 @@ pattern ShelleyTxSeq xs <-
141149 metaChunk index m = encodePair <$> strictMaybeToMaybe m
142150 where
143151 encodePair metadata = encCBOR index <> encodePreEncoded metadata
152+ txSeqBodies = serializeFoldable $ coreBodyBytes @ era <$> txns
153+ txSeqWits = serializeFoldable $ coreWitnessBytes @ era <$> txns
154+ txSeqAuxDatas =
155+ serialize version . encodeFoldableMapEncoder metaChunk $ coreAuxDataBytes @ era <$> txns
144156 in TxSeq'
145157 { txSeqTxns' = txns
158+ , txSeqHash = hashShelleySegWits txSeqBodies txSeqWits txSeqAuxDatas
146159 , -- bytes encoding "Seq (TxBody era)"
147- txSeqBodyBytes = serializeFoldable $ coreBodyBytes @ era <$> txns
160+ txSeqBodyBytes = txSeqBodies
148161 , -- bytes encoding "Seq (TxWits era)"
149- txSeqWitsBytes = serializeFoldable $ coreWitnessBytes @ era <$> txns
162+ txSeqWitsBytes = txSeqWits
150163 , -- bytes encoding a "Map Int TxAuxData"
151- txSeqMetadataBytes =
152- serialize version . encodeFoldableMapEncoder metaChunk $
153- coreAuxDataBytes @ era <$> txns
164+ txSeqMetadataBytes = txSeqAuxDatas
154165 }
155166
156167{-# COMPLETE ShelleyTxSeq #-}
157168
158169txSeqTxns :: ShelleyTxSeq era -> StrictSeq (ShelleyTx era )
159- txSeqTxns (TxSeq' ts _ _ _) = ts
170+ txSeqTxns (TxSeq' ts _ _ _ _ ) = ts
160171
161172instance
162173 forall era .
163174 Era era =>
164175 EncCBORGroup (ShelleyTxSeq era )
165176 where
166- encCBORGroup (TxSeq' _ bodyBytes witsBytes metadataBytes) =
177+ encCBORGroup (TxSeq' _ _ bodyBytes witsBytes metadataBytes) =
167178 encodePreEncoded $
168179 BSL. toStrict $
169180 bodyBytes <> witsBytes <> metadataBytes
@@ -176,17 +187,27 @@ instance
176187
177188-- | Hash a given block body
178189bbHash :: ShelleyTxSeq era -> Hash HASH EraIndependentBlockBody
179- bbHash (TxSeq' _ bodies wits md) =
180- coerce $
181- hashStrict
182- ( hashPart bodies
183- <> hashPart wits
184- <> hashPart md
185- )
190+ bbHash = txSeqHash
191+
192+ hashShelleySegWits ::
193+ BSL. ByteString ->
194+ -- | Bytes for transaction bodies
195+ BSL. ByteString ->
196+ -- | Bytes for transaction witnesses
197+ BSL. ByteString ->
198+ -- | Bytes for transaction auxiliary datas
199+ Hash HASH EraIndependentBlockBody
200+ hashShelleySegWits bodies wits md =
201+ coerce . hashLazy . toLazyByteString $
202+ hashPart bodies <> hashPart wits <> hashPart md
186203 where
187- hashStrict :: ByteString -> Hash HASH ByteString
188- hashStrict = Hash. hashWith id
189- hashPart = Hash. hashToBytes . hashStrict . BSL. toStrict
204+ hashLazy :: BSL. ByteString -> Hash HASH ByteString
205+ hashLazy = Hash. hashWith id . BSL. toStrict
206+ {-# INLINE hashLazy #-}
207+ hashPart :: BSL. ByteString -> Builder
208+ hashPart = shortByteString . Hash. hashToBytesShort . hashLazy
209+ {-# INLINE hashPart #-}
210+ {-# INLINE hashShelleySegWits #-}
190211
191212auxDataSeqDecoder ::
192213 Int -> IntMap a -> Decoder s (Seq (Maybe a ))
@@ -246,7 +267,8 @@ instance
246267 sequenceA $
247268 StrictSeq. forceToStrict $
248269 Seq. zipWith3 segWitAnnTx bodies wits metadata
249- pure $ TxSeq' <$> txns <*> bodiesAnn <*> witsAnn <*> metadataAnn
270+ hashAnn = hashShelleySegWits <$> bodiesAnn <*> witsAnn <*> metadataAnn
271+ pure $ TxSeq' <$> txns <*> hashAnn <*> bodiesAnn <*> witsAnn <*> metadataAnn
250272
251273slotToNonce :: SlotNo -> Nonce
252274slotToNonce (SlotNo s) = mkNonceFromNumber s
0 commit comments