Skip to content

Commit c47305f

Browse files
committed
L1: Add Maybe LeiosCert on DijkstraBody (Dijkstra-only)
Introduce 'Maybe LeiosCert' (mirrored after the existing 'Maybe PerasCert') as an optional field on the Dijkstra-era 'BlockBody'. Other eras are unaffected — this is the entire Leios-related ledger changes from leios-prototype at the time of the remake.
1 parent 6030591 commit c47305f

9 files changed

Lines changed: 218 additions & 38 deletions

File tree

eras/dijkstra/impl/cardano-ledger-dijkstra.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ test-suite tests
236236
other-modules:
237237
Paths_cardano_ledger_dijkstra
238238
Test.Cardano.Ledger.Dijkstra.Binary.CddlSpec
239+
Test.Cardano.Ledger.Dijkstra.BlockBodySpec
239240
Test.Cardano.Ledger.Dijkstra.GoldenSpec
240241

241242
default-language: Haskell2010
@@ -255,8 +256,9 @@ test-suite tests
255256
base,
256257
cardano-ledger-alonzo,
257258
cardano-ledger-babbage:testlib,
258-
cardano-ledger-binary:testlib,
259+
cardano-ledger-binary:{cardano-ledger-binary, testlib},
259260
cardano-ledger-conway:{cardano-ledger-conway, testlib},
260261
cardano-ledger-core:{cardano-ledger-core, testlib},
261262
cardano-ledger-dijkstra:{cardano-ledger-dijkstra, cddl, testlib},
262263
cardano-ledger-shelley:testlib,
264+
cardano-strict-containers,

eras/dijkstra/impl/cddl/data/dijkstra.cddl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@
55
; the same
66
; 2) every transaction_index must be strictly smaller than the length of
77
; transaction_bodies
8+
;
9+
; The 'leios_cert' and 'peras_cert' slots are always present on the wire and
10+
; encode as CBOR null when absent. Two slots in a row can't be wire-disambiguated
11+
; as trailing-optionals (the prototype's [4-or-5-item-body] pattern), so the body
12+
; has a fixed item count and the slots themselves are nullable.
813
block =
914
[ header
1015
, transaction_bodies : [* transaction_body]
1116
, transaction_witness_sets : [* transaction_witness_set]
1217
, auxiliary_data_set : {* transaction_index => auxiliary_data}
1318
, invalid_transactions : [* transaction_index]
19+
, leios_cert : leios_cert/ nil
20+
, peras_cert : peras_cert/ nil
1421
]
1522

1623

@@ -805,3 +812,13 @@ auxiliary_data_map =
805812

806813
transaction_index = uint .size 2
807814

815+
; Placeholder Leios certificate. The real cert payload will be filled in
816+
; once the Leios design lands; for now it serialises as an empty CBOR list
817+
; so the on-wire token is unambiguous against the surrounding 'null /
818+
; leios_cert' choice.
819+
leios_cert = []
820+
821+
; Placeholder Peras certificate. Same shape as 'leios_cert' (empty CBOR
822+
; list) until the Peras design fixes the payload.
823+
peras_cert = []
824+

eras/dijkstra/impl/cddl/lib/Cardano/Ledger/Dijkstra/HuddleSpec.hs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,11 @@ instance HuddleRule "block" DijkstraEra where
863863
| the same
864864
| 2) every transaction_index must be strictly smaller than the length of
865865
| transaction_bodies
866+
|
867+
|The 'leios_cert' and 'peras_cert' slots are always present on the wire and
868+
|encode as CBOR null when absent. Two slots in a row can't be wire-disambiguated
869+
|as trailing-optionals (the prototype's [4-or-5-item-body] pattern), so the body
870+
|has a fixed item count and the slots themselves are nullable.
866871
|]
867872
$ pname
868873
=.= arr
@@ -876,8 +881,28 @@ instance HuddleRule "block" DijkstraEra where
876881
==> huddleRule @"auxiliary_data" p
877882
]
878883
, "invalid_transactions" ==> arr [0 <+ a (huddleRule @"transaction_index" p)]
884+
, "leios_cert" ==> huddleRule @"leios_cert" p / VNil
885+
, "peras_cert" ==> huddleRule @"peras_cert" p / VNil
879886
]
880887

888+
instance HuddleRule "leios_cert" DijkstraEra where
889+
huddleRuleNamed pname _p =
890+
comment
891+
[str|Placeholder Leios certificate. The real cert payload will be filled in
892+
|once the Leios design lands; for now it serialises as an empty CBOR list
893+
|so the on-wire token is unambiguous against the surrounding 'null /
894+
|leios_cert' choice.
895+
|]
896+
$ pname =.= arr []
897+
898+
instance HuddleRule "peras_cert" DijkstraEra where
899+
huddleRuleNamed pname _p =
900+
comment
901+
[str|Placeholder Peras certificate. Same shape as 'leios_cert' (empty CBOR
902+
|list) until the Peras design fixes the payload.
903+
|]
904+
$ pname =.= arr []
905+
881906
instance HuddleRule "auxiliary_scripts" DijkstraEra where
882907
huddleRuleNamed = auxiliaryScriptsRule
883908

eras/dijkstra/impl/src/Cardano/Ledger/Dijkstra/BlockBody/Internal.hs

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,21 @@ module Cardano.Ledger.Dijkstra.BlockBody.Internal (
3030
alignedValidFlags,
3131
mkBasicBlockBodyDijkstra,
3232
DijkstraEraBlockBody (..),
33+
LeiosCert,
3334
) where
3435

3536
import qualified Cardano.Crypto.Hash as Hash
3637
import Cardano.Ledger.Alonzo.Tx (AlonzoEraTx (..), IsValid (..))
37-
import Cardano.Ledger.BaseTypes (PerasCert)
38+
import Cardano.Ledger.BaseTypes (LeiosCert, PerasCert)
3839
import Cardano.Ledger.Binary (
3940
Annotator (..),
4041
DecCBOR (..),
4142
EncCBORGroup (..),
42-
decodeListLen,
43+
decodeNullStrictMaybe,
4344
encCBOR,
4445
encodeFoldableEncoder,
4546
encodeFoldableMapEncoder,
47+
encodeNullStrictMaybe,
4648
encodePreEncoded,
4749
serialize,
4850
withSlice,
@@ -53,7 +55,6 @@ import Cardano.Ledger.Dijkstra.Tx ()
5355
import Cardano.Ledger.Shelley.BlockBody (auxDataSeqDecoder)
5456
import Control.DeepSeq (NFData)
5557
import Control.Monad (unless)
56-
import Data.Bifunctor (Bifunctor (..))
5758
import Data.ByteString (ByteString)
5859
import Data.ByteString.Builder (Builder, shortByteString, toLazyByteString)
5960
import qualified Data.ByteString.Lazy as BSL
@@ -84,6 +85,9 @@ import NoThunks.Class (AllowThunksIn (..), NoThunks)
8485

8586
data DijkstraBlockBody era = DijkstraBlockBodyInternal
8687
{ dbbTxs :: !(StrictSeq (Tx TopTx era))
88+
, dbbLeiosCert :: !(StrictMaybe LeiosCert)
89+
-- ^ Optional Leios certificate. When 'SJust', the EB closure carrying
90+
-- the actual transactions must be looked up via the LeiosDB.
8791
, dbbPerasCert :: !(StrictMaybe PerasCert)
8892
-- ^ Optional Peras certificate
8993
, dbbHash :: Hash.Hash HASH EraIndependentBlockBody
@@ -98,81 +102,112 @@ data DijkstraBlockBody era = DijkstraBlockBodyInternal
98102
, dbbTxsIsValidBytes :: BSL.ByteString
99103
-- ^ Bytes representing a set of integers. These are the indices of
100104
-- transactions with 'isValid' == False.
105+
, dbbLeiosCertBytes :: Maybe BSL.ByteString
106+
-- ^ Bytes encoding the optional Leios certificate
101107
, dbbPerasCertBytes :: Maybe BSL.ByteString
102108
-- ^ Bytes encoding the optional Peras certificate
103109
}
104110
deriving (Generic)
105111

106-
instance (NFData (Tx TopTx era), NFData PerasCert) => NFData (DijkstraBlockBody era)
112+
instance
113+
(NFData (Tx TopTx era), NFData LeiosCert, NFData PerasCert) =>
114+
NFData (DijkstraBlockBody era)
107115

108116
instance EraBlockBody DijkstraEra where
109117
type BlockBody DijkstraEra = DijkstraBlockBody DijkstraEra
110118
mkBasicBlockBody = mkBasicBlockBodyDijkstra
111-
txSeqBlockBodyL = lens dbbTxs (\bb p -> bb {dbbTxs = p})
119+
120+
-- Rebuild via the bidirectional pattern so the cached body/wits/aux/isvalid
121+
-- bytes and the body hash are recomputed for the new tx sequence. Mirrors
122+
-- the Alonzo idiom 'lens abbTxs (\_ s -> AlonzoBlockBody s)'.
123+
txSeqBlockBodyL =
124+
lens dbbTxs (\bb txs -> DijkstraBlockBody txs (dbbLeiosCert bb) (dbbPerasCert bb))
112125
hashBlockBody = dbbHash
113-
numSegComponents = 5
126+
numSegComponents = 6
114127

115128
mkBasicBlockBodyDijkstra ::
116129
( SafeToHash (TxWits era)
117130
, BlockBody era ~ DijkstraBlockBody era
118131
, AlonzoEraTx era
119132
) =>
120133
BlockBody era
121-
mkBasicBlockBodyDijkstra = DijkstraBlockBody mempty SNothing
134+
mkBasicBlockBodyDijkstra = DijkstraBlockBody mempty SNothing SNothing
122135
{-# INLINEABLE mkBasicBlockBodyDijkstra #-}
123136

124137
-- | Dijkstra-specific extensions to 'EraBlockBody'
125138
class EraBlockBody era => DijkstraEraBlockBody era where
139+
leiosCertBlockBodyL :: Lens' (BlockBody era) (StrictMaybe LeiosCert)
140+
-- ^ Lens to access the optional Leios certificate in the block body
141+
126142
perasCertBlockBodyL :: Lens' (BlockBody era) (StrictMaybe PerasCert)
127143
-- ^ Lens to access the optional Peras certificate in the block body
128144

129145
instance DijkstraEraBlockBody DijkstraEra where
130-
perasCertBlockBodyL = lens dbbPerasCert (\bb c -> bb {dbbPerasCert = c})
146+
leiosCertBlockBodyL =
147+
lens dbbLeiosCert (\bb c -> DijkstraBlockBody (dbbTxs bb) c (dbbPerasCert bb))
148+
perasCertBlockBodyL =
149+
lens dbbPerasCert (\bb c -> DijkstraBlockBody (dbbTxs bb) (dbbLeiosCert bb) c)
131150

132151
pattern DijkstraBlockBody ::
133152
forall era.
134153
( AlonzoEraTx era
135154
, SafeToHash (TxWits era)
136155
) =>
137156
StrictSeq (Tx TopTx era) ->
157+
StrictMaybe LeiosCert ->
138158
StrictMaybe PerasCert ->
139159
DijkstraBlockBody era
140-
pattern DijkstraBlockBody xs mbPerasCert <-
141-
DijkstraBlockBodyInternal xs mbPerasCert _ _ _ _ _ _
160+
pattern DijkstraBlockBody xs mbLeiosCert mbPerasCert <-
161+
DijkstraBlockBodyInternal xs mbLeiosCert mbPerasCert _ _ _ _ _ _ _
142162
where
143-
DijkstraBlockBody txns mbPerasCert =
163+
DijkstraBlockBody txns mbLeiosCert mbPerasCert =
144164
let version = eraProtVerLow @era
145165
serializeFoldablePreEncoded x =
146166
serialize version $
147167
encodeFoldableEncoder encodePreEncoded x
148168
metaChunk index m = encodeIndexed <$> strictMaybeToMaybe m
149169
where
150170
encodeIndexed metadata = encCBOR index <> encodePreEncoded metadata
151-
txSeqBodies =
152-
serializeFoldablePreEncoded $ originalBytes . view bodyTxL <$> txns
153-
txSeqWits =
154-
serializeFoldablePreEncoded $ originalBytes . view witsTxL <$> txns
155-
txSeqAuxDatas =
156-
serialize version . encodeFoldableMapEncoder metaChunk $
157-
fmap originalBytes . view auxDataTxL <$> txns
158-
txSeqIsValids =
159-
serialize version $ encCBOR $ nonValidatingIndices txns
171+
-- NOTE: When the block contains a LeiosCert, any transactions must
172+
-- not be encoded or contribute to the block body hash.
173+
(txSeqBodies, txSeqWits, txSeqAuxDatas, txSeqIsValids) =
174+
case mbLeiosCert of
175+
SJust _ ->
176+
( serializeFoldablePreEncoded (mempty :: StrictSeq ByteString)
177+
, serializeFoldablePreEncoded (mempty :: StrictSeq ByteString)
178+
, serialize
179+
version
180+
(encodeFoldableMapEncoder metaChunk (mempty :: StrictSeq (StrictMaybe ByteString)))
181+
, serialize version (encCBOR ([] :: [Int]))
182+
)
183+
SNothing ->
184+
( serializeFoldablePreEncoded $ originalBytes . view bodyTxL <$> txns
185+
, serializeFoldablePreEncoded $ originalBytes . view witsTxL <$> txns
186+
, serialize version . encodeFoldableMapEncoder metaChunk $
187+
fmap originalBytes . view auxDataTxL <$> txns
188+
, serialize version $ encCBOR $ nonValidatingIndices txns
189+
)
190+
mbLeiosCertBytes =
191+
Just (serialize version (encodeNullStrictMaybe encCBOR mbLeiosCert))
160192
mbPerasCertBytes =
161-
fmap (serialize version) (strictMaybeToMaybe mbPerasCert)
193+
Just (serialize version (encodeNullStrictMaybe encCBOR mbPerasCert))
162194
in DijkstraBlockBodyInternal
163195
{ dbbTxs = txns
196+
, dbbLeiosCert = mbLeiosCert
164197
, dbbPerasCert = mbPerasCert
165198
, dbbHash =
166199
hashDijkstraSegWits
167200
txSeqBodies
168201
txSeqWits
169202
txSeqAuxDatas
170203
txSeqIsValids
204+
mbLeiosCertBytes
171205
mbPerasCertBytes
172206
, dbbTxsBodyBytes = txSeqBodies
173207
, dbbTxsWitsBytes = txSeqWits
174208
, dbbTxsAuxDataBytes = txSeqAuxDatas
175209
, dbbTxsIsValidBytes = txSeqIsValids
210+
, dbbLeiosCertBytes = mbLeiosCertBytes
176211
, dbbPerasCertBytes = mbPerasCertBytes
177212
}
178213

@@ -185,6 +220,7 @@ deriving via
185220
, "dbbTxsWitsBytes"
186221
, "dbbTxsAuxDataBytes"
187222
, "dbbTxsIsValidBytes"
223+
, "dbbLeiosCertBytes"
188224
, "dbbPerasCertBytes"
189225
]
190226
(DijkstraBlockBody era)
@@ -207,8 +243,13 @@ instance Era era => EncCBORGroup (DijkstraBlockBody era) where
207243
<> dbbTxsWitsBytes blockBody
208244
<> dbbTxsAuxDataBytes blockBody
209245
<> dbbTxsIsValidBytes blockBody
246+
<> fromMaybe BSL.empty (dbbLeiosCertBytes blockBody)
210247
<> fromMaybe BSL.empty (dbbPerasCertBytes blockBody)
211-
listLen _ = 5
248+
249+
-- The cert byte fields always encode something (a CBOR null when
250+
-- absent, the cert value when present), so the body always has 6
251+
-- elements.
252+
listLen _ = 6
212253

213254
hashDijkstraSegWits ::
214255
BSL.ByteString ->
@@ -220,14 +261,17 @@ hashDijkstraSegWits ::
220261
BSL.ByteString ->
221262
-- | Bytes for transaction isValid flags
222263
Maybe BSL.ByteString ->
264+
-- | Bytes for optional Leios certificate
265+
Maybe BSL.ByteString ->
223266
-- | Bytes for optional Peras certificate
224267
Hash HASH EraIndependentBlockBody
225-
hashDijkstraSegWits txSeqBodies txSeqWits txAuxData txSeqIsValids mbPerasCert =
268+
hashDijkstraSegWits txSeqBodies txSeqWits txAuxData txSeqIsValids mbLeiosCert mbPerasCert =
226269
coerce . hashLazy . toLazyByteString $
227270
hashPart txSeqBodies
228271
<> hashPart txSeqWits
229272
<> hashPart txAuxData
230273
<> hashPart txSeqIsValids
274+
<> maybe mempty hashPart mbLeiosCert
231275
<> maybe mempty hashPart mbPerasCert
232276
where
233277
hashLazy :: BSL.ByteString -> Hash HASH ByteString
@@ -246,9 +290,12 @@ instance
246290
) =>
247291
DecCBOR (Annotator (DijkstraBlockBody era))
248292
where
293+
-- The body is encoded as 6 group elements (via 'encCBORGroup'), so
294+
-- the outer 'decodeRecordNamed \"Block\" (const 7)' in
295+
-- 'Cardano.Ledger.Block' already consumed the list-length header.
296+
-- We just sequence the 6 items here (no inner 'decodeListLen'),
297+
-- matching the Alonzo/Conway-style body decoder.
249298
decCBOR = do
250-
len <- decodeListLen
251-
252299
(bodies, bodiesAnn) <- withSlice decCBOR
253300
(wits, witsAnn) <- withSlice decCBOR
254301
let bodiesLength = length bodies
@@ -279,27 +326,35 @@ instance
279326
StrictSeq.forceToStrict $
280327
Seq.zipWith4 dijkstraSegwitTx bodies wits validFlags auxData
281328

282-
(mbPerasCert, mbPerasCertAnn) <-
283-
case len of
284-
4 -> return (pure SNothing, pure Nothing)
285-
5 -> bimap (pure . SJust) (fmap Just) <$> withSlice decCBOR
286-
_ -> fail $ "unexpected body length: " <> show len
329+
-- Each cert slot is always present as 'encodeNullStrictMaybe'
330+
-- (CBOR null when absent, value when present).
331+
(leios, leiosSliceAnn) <-
332+
withSlice (decodeNullStrictMaybe (decCBOR @LeiosCert))
333+
(peras, perasSliceAnn) <-
334+
withSlice (decodeNullStrictMaybe (decCBOR @PerasCert))
335+
let mbLeiosCert = pure leios
336+
mbLeiosCertAnn = Just <$> leiosSliceAnn
337+
mbPerasCert = pure peras
338+
mbPerasCertAnn = Just <$> perasSliceAnn
287339

288340
pure $
289341
DijkstraBlockBodyInternal
290342
<$> txns
343+
<*> mbLeiosCert
291344
<*> mbPerasCert
292345
<*> ( hashDijkstraSegWits
293346
<$> bodiesAnn
294347
<*> witsAnn
295348
<*> auxDataAnn
296349
<*> isValAnn
350+
<*> mbLeiosCertAnn
297351
<*> mbPerasCertAnn
298352
)
299353
<*> bodiesAnn
300354
<*> witsAnn
301355
<*> auxDataAnn
302356
<*> isValAnn
357+
<*> mbLeiosCertAnn
303358
<*> mbPerasCertAnn
304359

305360
--------------------------------------------------------------------------------

eras/dijkstra/impl/test/Main.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Test.Cardano.Ledger.Dijkstra.Binary.Annotator ()
1313
import qualified Test.Cardano.Ledger.Dijkstra.Binary.CddlSpec as Cddl
1414
import qualified Test.Cardano.Ledger.Dijkstra.Binary.Golden as Golden
1515
import Test.Cardano.Ledger.Dijkstra.Binary.RoundTrip ()
16+
import qualified Test.Cardano.Ledger.Dijkstra.BlockBodySpec as BlockBodySpec
1617
import qualified Test.Cardano.Ledger.Dijkstra.GoldenSpec as GoldenSpec
1718
import qualified Test.Cardano.Ledger.Dijkstra.Imp as Imp
1819
import Test.Cardano.Ledger.Dijkstra.ImpTest ()
@@ -26,6 +27,7 @@ main =
2627
describe "RoundTrip" $ do
2728
roundTripConwayCommonSpec @DijkstraEra
2829
Cddl.spec
30+
BlockBodySpec.spec
2931
GoldenSpec.spec
3032
roundTripJsonShelleyEraSpec @DijkstraEra
3133
describe "Imp" $ do

0 commit comments

Comments
 (0)