Skip to content

Commit ab63048

Browse files
authored
Merge pull request #960 from IntersectMBO/SerialiseAsCBOR-for-TxOut
Add `SerialiseAsCBOR` instance for `TxOut`
2 parents a583064 + 453d6b9 commit ab63048

File tree

2 files changed

+56
-3
lines changed
  • cardano-api
    • src/Cardano/Api/Tx/Internal
    • test/cardano-api-test/Test/Cardano/Api

2 files changed

+56
-3
lines changed

cardano-api/src/Cardano/Api/Tx/Internal/Output.hs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
{-# LANGUAGE FlexibleContexts #-}
66
{-# LANGUAGE FlexibleInstances #-}
77
{-# LANGUAGE GADTs #-}
8+
{-# LANGUAGE InstanceSigs #-}
89
{-# LANGUAGE LambdaCase #-}
910
{-# LANGUAGE RankNTypes #-}
1011
{-# LANGUAGE ScopedTypeVariables #-}
1112
{-# LANGUAGE StandaloneDeriving #-}
1213
{-# LANGUAGE TypeApplications #-}
1314
{-# LANGUAGE TypeFamilies #-}
1415
{-# LANGUAGE TypeOperators #-}
16+
{-# LANGUAGE UndecidableInstances #-}
1517

1618
module Cardano.Api.Tx.Internal.Output
1719
( -- * Transaction outputs
@@ -63,7 +65,7 @@ import Cardano.Api.Era.Internal.Eon.Convert
6365
import Cardano.Api.Era.Internal.Eon.ConwayEraOnwards
6466
import Cardano.Api.Era.Internal.Eon.ShelleyBasedEra
6567
import Cardano.Api.Error (Error (..), displayError)
66-
import Cardano.Api.Hash
68+
import Cardano.Api.HasTypeProxy qualified as HTP
6769
import Cardano.Api.Ledger.Internal.Reexport qualified as Ledger
6870
import Cardano.Api.Monad.Error
6971
import Cardano.Api.Parser.Text qualified as P
@@ -82,7 +84,6 @@ import Cardano.Ledger.Alonzo.Core qualified as L
8284
import Cardano.Ledger.Api qualified as L
8385
import Cardano.Ledger.BaseTypes (StrictMaybe (..))
8486
import Cardano.Ledger.Coin qualified as L
85-
import Cardano.Ledger.Core ()
8687
import Cardano.Ledger.Core qualified as Core
8788
import Cardano.Ledger.Core qualified as Ledger
8889
import Cardano.Ledger.Plutus.Data qualified as Plutus
@@ -100,6 +101,7 @@ import Data.Sequence.Strict qualified as Seq
100101
import Data.Text (Text)
101102
import Data.Text.Encoding qualified as Text
102103
import Data.Type.Equality
104+
import Data.Typeable (Typeable)
103105
import Data.Word
104106
import GHC.Exts (IsList (..))
105107
import GHC.Stack
@@ -122,6 +124,21 @@ data TxOut ctx era
122124
(TxOutDatum ctx era)
123125
(ReferenceScript era)
124126

127+
instance (Typeable ctx, IsShelleyBasedEra era) => HTP.HasTypeProxy (TxOut ctx era) where
128+
data AsType (TxOut ctx era) = AsTxOut (AsType era)
129+
proxyToAsType :: HTP.Proxy (TxOut ctx era) -> AsType (TxOut ctx era)
130+
proxyToAsType _ = AsTxOut (HTP.asType @era)
131+
132+
-- | We do not provide a 'ToCBOR' instance for 'TxOut' because 'TxOut's can contain
133+
-- supplemental datums and the ledger's CBOR representation does not support this.
134+
-- For this reason, if we were to serialise a 'TxOut' with a supplemental datum,
135+
-- we would lose information and the roundtrip property would not hold.
136+
instance (Typeable ctx, IsShelleyBasedEra era) => FromCBOR (TxOut ctx era) where
137+
fromCBOR :: Ledger.Decoder s (TxOut ctx era)
138+
fromCBOR =
139+
shelleyBasedEraConstraints (shelleyBasedEra @era) $
140+
pure (fromShelleyTxOut shelleyBasedEra) <*> L.fromEraCBOR @(ShelleyLedgerEra era)
141+
125142
deriving instance Eq (TxOut ctx era)
126143

127144
deriving instance Show (TxOut ctx era)

cardano-api/test/cardano-api-test/Test/Cardano/Api/CBOR.hs

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{-# LANGUAGE FlexibleContexts #-}
2+
{-# LANGUAGE FlexibleInstances #-}
23
{-# LANGUAGE GADTs #-}
3-
{-# LANGUAGE RankNTypes #-}
4+
{-# LANGUAGE ScopedTypeVariables #-}
45
{-# LANGUAGE TypeApplications #-}
56
-- TODO remove when serialiseTxLedgerCddl is removed
67
{-# OPTIONS_GHC -Wno-deprecations #-}
@@ -11,6 +12,7 @@ module Test.Cardano.Api.CBOR
1112
where
1213

1314
import Cardano.Api
15+
import Cardano.Api.Ledger qualified as Ledger
1416

1517
import Cardano.Binary qualified as CBOR
1618

@@ -109,6 +111,39 @@ prop_roundtrip_tx_CBOR = H.property $ do
109111
x <- H.forAll $ genTx era
110112
shelleyBasedEraConstraints era $ H.trippingCbor (proxyToAsType Proxy) x
111113

114+
-- | The CBOR representation for 'TxOut' does not store supplemental datums.
115+
-- This means we cannot provide a lossless serialisation instance for which
116+
-- a roundtrip property would hold.
117+
--
118+
-- Therefore, we only provide a deserialisation instance. The serialisation
119+
-- implementation is included for testing purposes only.
120+
--
121+
-- For the roundtrip test, we hash any supplemental datum before serialisation
122+
-- to ensure the property holds.
123+
prop_roundtrip_tx_out_CBOR :: Property
124+
prop_roundtrip_tx_out_CBOR = H.property $ do
125+
AnyShelleyBasedEra era <- H.noteShowM . H.forAll $ Gen.element [minBound .. maxBound]
126+
x <- H.forAll $ genTx era
127+
txOut <- H.forAll $ Gen.element $ txOuts $ getTxBodyContent $ getTxBody x
128+
let fixedTxOut = hashDatum txOut
129+
shelleyBasedEraConstraints era $
130+
H.tripping fixedTxOut lossyEncodingForTesting CBOR.decodeFull'
131+
where
132+
hashDatum :: TxOut CtxTx era -> TxOut CtxTx era
133+
hashDatum txOut@(TxOut aie val datum rs) =
134+
case datum of
135+
(TxOutSupplementalDatum aeo d) ->
136+
TxOut aie val (TxOutDatumHash aeo (hashScriptDataBytes d)) rs
137+
_ -> txOut
138+
139+
lossyEncodingForTesting :: IsShelleyBasedEra era => TxOut CtxTx era -> ByteString
140+
lossyEncodingForTesting txOut = LBS.toStrict $ CBOR.serialize $ toCBOR' txOut
141+
where
142+
toCBOR' :: forall ctx era. IsShelleyBasedEra era => TxOut ctx era -> CBOR.Encoding
143+
toCBOR' txOut' =
144+
shelleyBasedEraConstraints (shelleyBasedEra @era) $
145+
Ledger.toEraCBOR @(ShelleyLedgerEra era) (toShelleyTxOutAny shelleyBasedEra txOut')
146+
112147
prop_roundtrip_witness_CBOR :: Property
113148
prop_roundtrip_witness_CBOR = H.property $ do
114149
AnyShelleyBasedEra era <- H.noteShowM . H.forAll $ Gen.element [minBound .. maxBound]
@@ -520,6 +555,7 @@ tests =
520555
, testProperty "roundtrip ScriptData CBOR" prop_roundtrip_ScriptData_CBOR
521556
, testProperty "roundtrip TxWitness Cddl" prop_roundtrip_TxWitness_Cddl
522557
, testProperty "roundtrip tx CBOR" prop_roundtrip_tx_CBOR
558+
, testProperty "roundtrip tx out CBOR" prop_roundtrip_tx_out_CBOR
523559
, testProperty
524560
"roundtrip GovernancePoll CBOR"
525561
prop_roundtrip_GovernancePoll_CBOR

0 commit comments

Comments
 (0)