99{-# LANGUAGE TupleSections #-}
1010{-# LANGUAGE TypeApplications #-}
1111{-# LANGUAGE TypeFamilies #-}
12+ {-# LANGUAGE TypeOperators #-}
1213
1314module Cardano.Api.Experimental.Tx.Internal.BodyContent.New
1415 ( TxCertificates (.. )
@@ -112,6 +113,7 @@ import Cardano.Api.Plutus.Internal.Script
112113 , ScriptInAnyLang (.. )
113114 , ScriptLanguage (.. )
114115 , fromAllegraTimelock
116+ , toAllegraTimelock
115117 )
116118import Cardano.Api.Plutus.Internal.Script qualified as OldScript
117119import Cardano.Api.Plutus.Internal.ScriptData qualified as Api
@@ -138,6 +140,7 @@ import Cardano.Api.Value.Internal
138140 )
139141
140142import Cardano.Binary qualified as CBOR
143+ import Cardano.Ledger.Allegra.Scripts (Timelock )
141144import Cardano.Ledger.Alonzo.Scripts qualified as L
142145import Cardano.Ledger.Alonzo.Tx qualified as L
143146import Cardano.Ledger.Alonzo.TxBody qualified as L
@@ -149,8 +152,9 @@ import Cardano.Ledger.Plutus.Language (PlutusBinary (..), plutusLanguage)
149152import Cardano.Ledger.Plutus.Language qualified as Plutus
150153
151154import Control.Monad
152- import Data.Aeson (ToJSON (.. ), (.=) )
155+ import Data.Aeson (FromJSON ( .. ), ToJSON (.. ), (.:) , (.:? ) , (.=) )
153156import Data.Aeson qualified as Aeson
157+ import Data.Aeson.Types (Parser )
154158import Data.ByteString.Base16 qualified as Base16
155159import Data.ByteString.Short qualified as SBS
156160import Data.Functor
@@ -475,6 +479,122 @@ deriving instance (Show (TxOut era))
475479
476480deriving instance (Eq (TxOut era ))
477481
482+ -- | Pre-Alonzo eras have no datums or reference scripts, so parsing
483+ -- only needs address and value.
484+ instance FromJSON (TxOut L. ShelleyEra ) where
485+ parseJSON = Aeson. withObject " TxOut" $ fmap TxOut . txOutBaseParseJson
486+
487+ instance FromJSON (TxOut L. AllegraEra ) where
488+ parseJSON = Aeson. withObject " TxOut" $ fmap TxOut . txOutBaseParseJson
489+
490+ instance FromJSON (TxOut L. MaryEra ) where
491+ parseJSON = Aeson. withObject " TxOut" $ fmap TxOut . txOutBaseParseJson
492+
493+ -- | Alonzo supports datum hashes but not inline datums or reference scripts.
494+ instance FromJSON (TxOut L. AlonzoEra ) where
495+ parseJSON = Aeson. withObject " TxOut" $ \ o -> do
496+ baseTxOut <- txOutBaseParseJson o
497+ mDatumHash <- o .:? " datumhash"
498+ pure . TxOut $ case mDatumHash of
499+ Nothing -> baseTxOut
500+ Just dh -> baseTxOut & L. dataHashTxOutL .~ SJust dh
501+
502+ -- | Babbage and later eras support inline datums and reference scripts.
503+ instance FromJSON (TxOut L. BabbageEra ) where
504+ parseJSON = Aeson. withObject " TxOut" babbageOnwardsTxOutParseJson
505+
506+ instance FromJSON (TxOut L. ConwayEra ) where
507+ parseJSON = Aeson. withObject " TxOut" babbageOnwardsTxOutParseJson
508+
509+ -- | Parse the base fields (address and value) shared by all eras.
510+ txOutBaseParseJson :: L. EraTxOut era => Aeson. Object -> Parser (L. TxOut era )
511+ txOutBaseParseJson o = do
512+ addr <- addrFromJson =<< o .: " address"
513+ apiVal <- parseJSON =<< o .: " value"
514+ let mv = toMaryValue apiVal
515+ val <- case cast mv of
516+ Just v -> pure v
517+ Nothing -> case cast (L. coin mv) of
518+ Just v -> pure v
519+ Nothing -> fail " Unsupported value type for era"
520+ pure $ L. mkBasicTxOut addr val
521+
522+ -- | Parse a ledger 'L.Addr' from JSON. Reverse of 'addrToJson'.
523+ addrFromJson :: Aeson. Value -> Parser L. Addr
524+ addrFromJson = Aeson. withText " Address" $ \ txt ->
525+ case deserialiseAddress AsAddressAny txt of
526+ Nothing -> fail " Invalid address"
527+ Just addrAny -> pure $ case addrAny of
528+ AddressByron (ByronAddress addr) -> L. AddrBootstrap (L. BootstrapAddress addr)
529+ AddressShelley (ShelleyAddress nw pc scr) -> L. Addr nw pc scr
530+
531+ -- | Parse a Babbage+ TxOut with datum and reference script support.
532+ babbageOnwardsTxOutParseJson
533+ :: forall era
534+ . ( L. BabbageEraTxOut era
535+ , L. NativeScript era ~ Timelock era
536+ )
537+ => Aeson. Object -> Parser (TxOut era )
538+ babbageOnwardsTxOutParseJson o = do
539+ baseTxOut <- txOutBaseParseJson o
540+ -- Parse datum fields
541+ mDatumHash <- o .:? " datumhash"
542+ mInlineDatumRaw <- o .:? " inlineDatumRaw"
543+ mInlineDatumHash <- o .:? " inlineDatumhash"
544+ -- Parse reference script
545+ mRefScript <- o .:? " referenceScript"
546+ -- Determine datum
547+ datum <- case mInlineDatumRaw of
548+ Just rawHex -> do
549+ expectedHash <- case mInlineDatumHash of
550+ Nothing -> fail " inlineDatumRaw present without inlineDatumhash"
551+ Just h -> pure h
552+ rawBytes <- case Base16. decode (Text. encodeUtf8 rawHex) of
553+ Left err -> fail $ " Error decoding inlineDatumRaw hex: " <> show err
554+ Right bs -> pure bs
555+ binaryData <- case L. makeBinaryData (SBS. toShort rawBytes) of
556+ Left err -> fail $ " Error decoding inlineDatumRaw CBOR: " <> err
557+ Right bd -> pure bd
558+ when (L. hashBinaryData binaryData /= expectedHash) $
559+ fail " Inline datum hash does not match inlineDatumRaw"
560+ pure $ L. Datum binaryData
561+ Nothing -> case mDatumHash of
562+ Just dh -> pure $ L. DatumHash dh
563+ Nothing -> pure L. NoDatum
564+ -- Determine reference script
565+ refScript <- case mRefScript of
566+ Nothing -> pure SNothing
567+ Just script -> SJust <$> scriptInAnyLangToLedgerScript script
568+ -- Construct TxOut
569+ pure . TxOut $
570+ baseTxOut
571+ & L. datumTxOutL .~ datum
572+ & L. referenceScriptTxOutL .~ refScript
573+
574+ -- | Convert a 'ScriptInAnyLang' to a ledger 'L.Script'. Reverse of 'ledgerScriptToScriptInAnyLang'.
575+ scriptInAnyLangToLedgerScript
576+ :: forall era
577+ . ( L. AlonzoEraScript era
578+ , L. NativeScript era ~ Timelock era
579+ )
580+ => ScriptInAnyLang -> Parser (L. Script era )
581+ scriptInAnyLangToLedgerScript (ScriptInAnyLang lang script) =
582+ case (lang, script) of
583+ (SimpleScriptLanguage , OldScript. SimpleScript ss) ->
584+ pure $ L. fromNativeScript (toAllegraTimelock ss)
585+ (PlutusScriptLanguage PlutusScriptV1 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
586+ L. fromPlutusScript
587+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV1 )
588+ (PlutusScriptLanguage PlutusScriptV2 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
589+ L. fromPlutusScript
590+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV2 )
591+ (PlutusScriptLanguage PlutusScriptV3 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
592+ L. fromPlutusScript
593+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV3 )
594+ (PlutusScriptLanguage PlutusScriptV4 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
595+ L. fromPlutusScript
596+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV4 )
597+
478598data Datum ctx era where
479599 TxOutDatumHash
480600 :: L. DataHash
0 commit comments