Skip to content

Commit 84d6b71

Browse files
authored
Merge pull request #5122 from IntersectMBO/lehins/memoize-block-body-hash
Memoize block body hash computation
2 parents 4235e19 + 110505f commit 84d6b71

File tree

4 files changed

+125
-89
lines changed

4 files changed

+125
-89
lines changed

eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxSeq/Internal.hs

Lines changed: 64 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
module Cardano.Ledger.Alonzo.TxSeq.Internal (
2626
AlonzoTxSeq (.., AlonzoTxSeq),
2727
hashAlonzoTxSeq,
28+
hashAlonzoSegWits,
2829
alignedValidFlags,
2930
) where
3031

@@ -47,7 +48,7 @@ import Cardano.Ledger.Core
4748
import Cardano.Ledger.Shelley.BlockChain (auxDataSeqDecoder)
4849
import Control.Monad (unless)
4950
import Data.ByteString (ByteString)
50-
import Data.ByteString.Builder (shortByteString, toLazyByteString)
51+
import Data.ByteString.Builder (Builder, shortByteString, toLazyByteString)
5152
import qualified Data.ByteString.Lazy as BSL
5253
import Data.Coerce (coerce)
5354
import Data.Maybe.Strict (maybeToStrictMaybe, strictMaybeToMaybe)
@@ -72,12 +73,14 @@ import NoThunks.Class (AllowThunksIn (..), NoThunks)
7273

7374
data AlonzoTxSeq era = AlonzoTxSeqRaw
7475
{ txSeqTxns :: !(StrictSeq (Tx era))
76+
, txSeqHash :: Hash.Hash HASH EraIndependentBlockBody
77+
-- ^ Memoized hash to avoid recomputation. Lazy on purpose.
7578
, txSeqBodyBytes :: BSL.ByteString
76-
-- ^ Bytes encoding @Seq ('AlonzoTxBody' era)@
79+
-- ^ Bytes encoding @Seq ('TxBody' era)@
7780
, txSeqWitsBytes :: BSL.ByteString
78-
-- ^ Bytes encoding @Seq ('TxWitness' era)@
81+
-- ^ Bytes encoding @Seq ('TxWits' era)@
7982
, txSeqMetadataBytes :: BSL.ByteString
80-
-- ^ Bytes encoding a @Map Int ('AuxiliaryData')@. Missing indices have
83+
-- ^ Bytes encoding a @'TxAuxData')@. Missing indices have
8184
-- 'SNothing' for metadata
8285
, txSeqIsValidBytes :: BSL.ByteString
8386
-- ^ Bytes representing a set of integers. These are the indices of
@@ -100,7 +103,7 @@ pattern AlonzoTxSeq ::
100103
StrictSeq (Tx era) ->
101104
AlonzoTxSeq era
102105
pattern AlonzoTxSeq xs <-
103-
AlonzoTxSeqRaw xs _ _ _ _
106+
AlonzoTxSeqRaw xs _ _ _ _ _
104107
where
105108
AlonzoTxSeq txns =
106109
let version = eraProtVerLow @era
@@ -110,24 +113,30 @@ pattern AlonzoTxSeq xs <-
110113
metaChunk index m = encodeIndexed <$> strictMaybeToMaybe m
111114
where
112115
encodeIndexed metadata = encCBOR index <> encodePreEncoded metadata
116+
txSeqBodies =
117+
serializeFoldablePreEncoded $ originalBytes . view bodyTxL <$> txns
118+
txSeqWits =
119+
serializeFoldablePreEncoded $ originalBytes . view witsTxL <$> txns
120+
txSeqAuxDatas =
121+
serialize version . encodeFoldableMapEncoder metaChunk $
122+
fmap originalBytes . view auxDataTxL <$> txns
123+
txSeqIsValids =
124+
serialize version $ encCBOR $ nonValidatingIndices txns
113125
in AlonzoTxSeqRaw
114126
{ txSeqTxns = txns
115-
, txSeqBodyBytes =
116-
serializeFoldablePreEncoded $ originalBytes . view bodyTxL <$> txns
117-
, txSeqWitsBytes =
118-
serializeFoldablePreEncoded $ originalBytes . view witsTxL <$> txns
119-
, txSeqMetadataBytes =
120-
serialize version . encodeFoldableMapEncoder metaChunk $
121-
fmap originalBytes . view auxDataTxL <$> txns
122-
, txSeqIsValidBytes =
123-
serialize version $ encCBOR $ nonValidatingIndices txns
127+
, txSeqHash = hashAlonzoSegWits txSeqBodies txSeqWits txSeqAuxDatas txSeqIsValids
128+
, txSeqBodyBytes = txSeqBodies
129+
, txSeqWitsBytes = txSeqWits
130+
, txSeqMetadataBytes = txSeqAuxDatas
131+
, txSeqIsValidBytes = txSeqIsValids
124132
}
125133

126134
{-# COMPLETE AlonzoTxSeq #-}
127135

128136
deriving via
129137
AllowThunksIn
130-
'[ "txSeqBodyBytes"
138+
'[ "txSeqHash"
139+
, "txSeqBodyBytes"
131140
, "txSeqWitsBytes"
132141
, "txSeqMetadataBytes"
133142
, "txSeqIsValidBytes"
@@ -145,7 +154,7 @@ deriving stock instance Eq (Tx era) => Eq (AlonzoTxSeq era)
145154
--------------------------------------------------------------------------------
146155

147156
instance Era era => EncCBORGroup (AlonzoTxSeq era) where
148-
encCBORGroup (AlonzoTxSeqRaw _ bodyBytes witsBytes metadataBytes invalidBytes) =
157+
encCBORGroup (AlonzoTxSeqRaw _ _ bodyBytes witsBytes metadataBytes invalidBytes) =
149158
encodePreEncoded $
150159
BSL.toStrict $
151160
bodyBytes <> witsBytes <> metadataBytes <> invalidBytes
@@ -162,21 +171,32 @@ hashAlonzoTxSeq ::
162171
forall era.
163172
AlonzoTxSeq era ->
164173
Hash HASH EraIndependentBlockBody
165-
hashAlonzoTxSeq (AlonzoTxSeqRaw _ bodies ws md vs) =
166-
coerce $
167-
hashStrict $
168-
BSL.toStrict $
169-
toLazyByteString $
170-
mconcat
171-
[ hashPart bodies
172-
, hashPart ws
173-
, hashPart md
174-
, hashPart vs
175-
]
174+
hashAlonzoTxSeq = txSeqHash
175+
176+
hashAlonzoSegWits ::
177+
BSL.ByteString ->
178+
-- | Bytes for transaction bodies
179+
BSL.ByteString ->
180+
-- | Bytes for transaction witnesses
181+
BSL.ByteString ->
182+
-- | Bytes for transaction auxiliary datas
183+
BSL.ByteString ->
184+
-- | Bytes for transaction isValid flags
185+
Hash HASH EraIndependentBlockBody
186+
hashAlonzoSegWits txSeqBodies txSeqWits txAuxData txSeqIsValids =
187+
coerce . hashLazy . toLazyByteString $
188+
hashPart txSeqBodies
189+
<> hashPart txSeqWits
190+
<> hashPart txAuxData
191+
<> hashPart txSeqIsValids
176192
where
177-
hashStrict :: ByteString -> Hash HASH ByteString
178-
hashStrict = Hash.hashWith id
179-
hashPart = shortByteString . Hash.hashToBytesShort . hashStrict . BSL.toStrict
193+
hashLazy :: BSL.ByteString -> Hash HASH ByteString
194+
hashLazy = Hash.hashWith id . BSL.toStrict
195+
{-# INLINE hashLazy #-}
196+
hashPart :: BSL.ByteString -> Builder
197+
hashPart = shortByteString . Hash.hashToBytesShort . hashLazy
198+
{-# INLINE hashPart #-}
199+
{-# INLINE hashAlonzoSegWits #-}
180200

181201
instance
182202
( AlonzoEraTx era
@@ -198,24 +218,19 @@ instance
198218

199219
(isValIdxs, isValAnn) <- withSlice decCBOR
200220
let validFlags = alignedValidFlags bodiesLength isValIdxs
201-
unless
202-
(bodiesLength == witsLength)
203-
( fail $
204-
"different number of transaction bodies ("
205-
<> show bodiesLength
206-
<> ") and witness sets ("
207-
<> show witsLength
208-
<> ")"
209-
)
210-
unless
211-
(all inRange isValIdxs)
212-
( fail
213-
( "Some IsValid index is not in the range: 0 .. "
214-
++ show (bodiesLength - 1)
215-
++ ", "
216-
++ show isValIdxs
217-
)
218-
)
221+
unless (bodiesLength == witsLength) $
222+
fail $
223+
"different number of transaction bodies ("
224+
<> show bodiesLength
225+
<> ") and witness sets ("
226+
<> show witsLength
227+
<> ")"
228+
unless (all inRange isValIdxs) $
229+
fail $
230+
"Some IsValid index is not in the range: 0 .. "
231+
++ show (bodiesLength - 1)
232+
++ ", "
233+
++ show isValIdxs
219234

220235
let txns =
221236
sequenceA $
@@ -224,6 +239,7 @@ instance
224239
pure $
225240
AlonzoTxSeqRaw
226241
<$> txns
242+
<*> (hashAlonzoSegWits <$> bodiesAnn <*> witsAnn <*> auxDataAnn <*> isValAnn)
227243
<*> bodiesAnn
228244
<*> witsAnn
229245
<*> auxDataAnn

eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Binary/Annotator.hs

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,24 +62,20 @@ instance
6262
auxData <- auxDataSeqDecoder @(TxAuxData era) bodiesLength auxDataMap
6363
Annotated isValidIdxs isValidBytes <- decodeAnnotated decCBOR
6464
let validFlags = alignedValidFlags bodiesLength isValidIdxs
65-
unless
66-
(bodiesLength == witsLength)
67-
( fail $
68-
"different number of transaction bodies ("
69-
<> show bodiesLength
70-
<> ") and witness sets ("
71-
<> show witsLength
72-
<> ")"
73-
)
74-
unless
75-
(all inRange isValidIdxs)
76-
( fail
77-
( "Some IsValid index is not in the range: 0 .. "
78-
++ show (bodiesLength - 1)
79-
++ ", "
80-
++ show isValidIdxs
81-
)
82-
)
65+
unless (bodiesLength == witsLength) $
66+
fail $
67+
"different number of transaction bodies ("
68+
<> show bodiesLength
69+
<> ") and witness sets ("
70+
<> show witsLength
71+
<> ")"
72+
unless (all inRange isValidIdxs) $
73+
fail $
74+
"Some IsValid index is not in the range: 0 .. "
75+
++ show (bodiesLength - 1)
76+
++ ", "
77+
++ show isValidIdxs
78+
8379
let mkTx body wit isValid aData =
8480
mkBasicTx body
8581
& witsTxL .~ wit
@@ -91,6 +87,7 @@ instance
9187
pure $
9288
AlonzoTxSeqRaw
9389
txs
90+
(hashAlonzoSegWits bodiesBytes witsBytes auxDataBytes isValidBytes)
9491
bodiesBytes
9592
witsBytes
9693
auxDataBytes

eras/shelley/impl/src/Cardano/Ledger/Shelley/BlockChain.hs

Lines changed: 44 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -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 (..))
5556
import Cardano.Ledger.Slot (SlotNo (..))
5657
import Control.Monad (unless)
5758
import Data.ByteString (ByteString)
59+
import Data.ByteString.Builder (Builder, shortByteString, toLazyByteString)
5860
import qualified Data.ByteString.Lazy as BSL
5961
import Data.Coerce (coerce)
6062
import Data.IntMap (IntMap)
@@ -72,23 +74,29 @@ import NoThunks.Class (AllowThunksIn (..), NoThunks (..))
7274

7375
data 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

8289
instance 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

8996
deriving 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
133141
pattern 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

158169
txSeqTxns :: ShelleyTxSeq era -> StrictSeq (ShelleyTx era)
159-
txSeqTxns (TxSeq' ts _ _ _) = ts
170+
txSeqTxns (TxSeq' ts _ _ _ _) = ts
160171

161172
instance
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
178189
bbHash :: 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

191212
auxDataSeqDecoder ::
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

251273
slotToNonce :: SlotNo -> Nonce
252274
slotToNonce (SlotNo s) = mkNonceFromNumber s

eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Binary/Annotator.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ instance
6666
let txs =
6767
StrictSeq.forceToStrict $
6868
Seq.zipWith3 ShelleyTx bodies wits (maybeToStrictMaybe <$> auxData)
69-
pure $ TxSeq' txs bodiesBytes witsBytes auxDataBytes
69+
hash = hashShelleySegWits bodiesBytes witsBytes auxDataBytes
70+
pure $ TxSeq' txs hash bodiesBytes witsBytes auxDataBytes
7071

7172
deriving newtype instance DecCBOR (TxBody ShelleyEra)
7273

0 commit comments

Comments
 (0)