99{-# LANGUAGE TupleSections #-}
1010{-# LANGUAGE TypeApplications #-}
1111{-# LANGUAGE TypeFamilies #-}
12+ {-# LANGUAGE TypeOperators #-}
1213
1314module Cardano.Api.Experimental.Tx.Internal.BodyContent.New
1415 ( TxCertificates (.. )
@@ -108,6 +109,7 @@ import Cardano.Api.Plutus.Internal.Script
108109 , ScriptInAnyLang (.. )
109110 , ScriptLanguage (.. )
110111 , fromAllegraTimelock
112+ , toAllegraTimelock
111113 )
112114import Cardano.Api.Plutus.Internal.Script qualified as OldScript
113115import Cardano.Api.Plutus.Internal.ScriptData qualified as Api
@@ -132,6 +134,7 @@ import Cardano.Api.Value.Internal
132134 )
133135
134136import Cardano.Binary qualified as CBOR
137+ import Cardano.Ledger.Allegra.Scripts (Timelock )
135138import Cardano.Ledger.Alonzo.Scripts qualified as L
136139import Cardano.Ledger.Alonzo.Tx qualified as L
137140import Cardano.Ledger.Alonzo.TxBody qualified as L
@@ -142,9 +145,11 @@ import Cardano.Ledger.Plutus.Language (PlutusBinary (..), plutusLanguage)
142145import Cardano.Ledger.Plutus.Language qualified as Plutus
143146
144147import Control.Monad
145- import Data.Aeson (ToJSON (.. ), (.=) )
148+ import Data.Aeson (FromJSON ( .. ), ToJSON (.. ), (.:) , (.:? ) , (.=) )
146149import Data.Aeson qualified as Aeson
150+ import Data.Aeson.Types (Parser )
147151import Data.ByteString.Base16 qualified as Base16
152+ import Data.ByteString.Short qualified as SBS
148153import Data.Functor
149154import Data.List qualified as List
150155import Data.Map.Ordered.Strict (OMap )
@@ -489,6 +494,127 @@ deriving instance (Show (TxOut era))
489494
490495deriving instance (Eq (TxOut era ))
491496
497+ -- | Pre-Alonzo eras have no datums or reference scripts, so parsing
498+ -- only needs address and value.
499+ instance FromJSON (TxOut L. ShelleyEra ) where
500+ parseJSON = Aeson. withObject " TxOut" $ fmap TxOut . txOutBaseParseJson
501+
502+ instance FromJSON (TxOut L. AllegraEra ) where
503+ parseJSON = Aeson. withObject " TxOut" $ fmap TxOut . txOutBaseParseJson
504+
505+ instance FromJSON (TxOut L. MaryEra ) where
506+ parseJSON = Aeson. withObject " TxOut" $ fmap TxOut . txOutBaseParseJson
507+
508+ -- | Alonzo supports datum hashes but not inline datums or reference scripts.
509+ instance FromJSON (TxOut L. AlonzoEra ) where
510+ parseJSON = Aeson. withObject " TxOut" $ \ o -> do
511+ baseTxOut <- txOutBaseParseJson o
512+ mDatumHash <- o .:? " datumhash"
513+ pure . TxOut $ case mDatumHash of
514+ Nothing -> baseTxOut
515+ Just dh -> baseTxOut & L. dataHashTxOutL .~ SJust dh
516+
517+ -- | Babbage and later eras support inline datums and reference scripts.
518+ instance FromJSON (TxOut L. BabbageEra ) where
519+ parseJSON = Aeson. withObject " TxOut" babbageOnwardsTxOutParseJson
520+
521+ instance FromJSON (TxOut L. ConwayEra ) where
522+ parseJSON = Aeson. withObject " TxOut" babbageOnwardsTxOutParseJson
523+
524+ -- | Parse the base fields (address and value) shared by all eras.
525+ txOutBaseParseJson :: L. EraTxOut era => Aeson. Object -> Parser (L. TxOut era )
526+ txOutBaseParseJson o = do
527+ addr <- addrFromJson =<< o .: " address"
528+ apiVal <- parseJSON =<< o .: " value"
529+ let mv = toMaryValue apiVal
530+ val <- case cast mv of
531+ Just v -> pure v
532+ Nothing -> case cast (L. coin mv) of
533+ Just v -> pure v
534+ Nothing -> fail " txOutBaseParseJson: value is unsupported for this era"
535+ pure $ L. mkBasicTxOut addr val
536+
537+ -- | Parse a ledger 'L.Addr' from JSON. Reverse of 'addrToJson'.
538+ addrFromJson :: Aeson. Value -> Parser L. Addr
539+ addrFromJson = Aeson. withText " Address" $ \ txt ->
540+ case deserialiseAddress AsAddressAny txt of
541+ Nothing -> fail $ " addrFromJson: invalid address: " <> show txt
542+ Just addrAny -> pure $ case addrAny of
543+ AddressByron (ByronAddress addr) -> L. AddrBootstrap (L. BootstrapAddress addr)
544+ AddressShelley (ShelleyAddress nw pc scr) -> L. Addr nw pc scr
545+
546+ -- | Parse a Babbage+ TxOut with datum and reference script support.
547+ babbageOnwardsTxOutParseJson
548+ :: forall era
549+ . ( L. BabbageEraTxOut era
550+ , L. NativeScript era ~ Timelock era
551+ )
552+ => Aeson. Object -> Parser (TxOut era )
553+ babbageOnwardsTxOutParseJson o = do
554+ baseTxOut <- txOutBaseParseJson o
555+ -- Parse datum fields
556+ mDatumHash <- o .:? " datumhash"
557+ mInlineDatumRaw <- o .:? " inlineDatumRaw"
558+ mInlineDatumHash <- o .:? " inlineDatumhash"
559+ -- Parse reference script
560+ mRefScript <- o .:? " referenceScript"
561+ -- Determine datum
562+ datum <- case mInlineDatumRaw of
563+ Just rawHex -> do
564+ expectedHash <- case mInlineDatumHash of
565+ Nothing -> fail " babbageOnwardsTxOutParseJson: inlineDatumRaw present without inlineDatumhash"
566+ Just h -> pure h
567+ rawBytes <- case Base16. decode (Text. encodeUtf8 rawHex) of
568+ Left err -> fail $ " babbageOnwardsTxOutParseJson: failed to hex-decode inlineDatumRaw: " <> show err
569+ Right bs -> pure bs
570+ binaryData <- case L. makeBinaryData (SBS. toShort rawBytes) of
571+ Left err -> fail $ " babbageOnwardsTxOutParseJson: failed to CBOR-decode inlineDatumRaw: " <> err
572+ Right bd -> pure bd
573+ when (L. hashBinaryData binaryData /= expectedHash) $
574+ fail $
575+ " babbageOnwardsTxOutParseJson: inline datum hash mismatch: "
576+ <> " expected "
577+ <> show expectedHash
578+ <> " , got "
579+ <> show (L. hashBinaryData binaryData)
580+ pure $ L. Datum binaryData
581+ Nothing -> case mDatumHash of
582+ Just dh -> pure $ L. DatumHash dh
583+ Nothing -> pure L. NoDatum
584+ -- Determine reference script
585+ refScript <- case mRefScript of
586+ Nothing -> pure SNothing
587+ Just script -> SJust <$> scriptInAnyLangToLedgerScript script
588+ -- Construct TxOut
589+ pure . TxOut $
590+ baseTxOut
591+ & L. datumTxOutL .~ datum
592+ & L. referenceScriptTxOutL .~ refScript
593+
594+ -- | Convert a 'ScriptInAnyLang' to a ledger 'L.Script'. Reverse of 'ledgerScriptToScriptInAnyLang'.
595+ scriptInAnyLangToLedgerScript
596+ :: forall era
597+ . ( L. AlonzoEraScript era
598+ , L. NativeScript era ~ Timelock era
599+ )
600+ => ScriptInAnyLang -> Parser (L. Script era )
601+ scriptInAnyLangToLedgerScript (ScriptInAnyLang lang script) =
602+ case (lang, script) of
603+ (SimpleScriptLanguage , OldScript. SimpleScript ss) ->
604+ pure $ Ledger. fromNativeScript (toAllegraTimelock ss)
605+ (PlutusScriptLanguage PlutusScriptV1 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
606+ L. fromPlutusScript
607+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV1 )
608+ (PlutusScriptLanguage PlutusScriptV2 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
609+ L. fromPlutusScript
610+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV2 )
611+ (PlutusScriptLanguage PlutusScriptV3 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
612+ L. fromPlutusScript
613+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV3 )
614+ (PlutusScriptLanguage PlutusScriptV4 , OldScript. PlutusScript _ (PlutusScriptSerialised sbs)) ->
615+ L. fromPlutusScript
616+ <$> L. mkPlutusScript (Plutus. Plutus (PlutusBinary sbs) :: Plutus. Plutus 'Plutus.PlutusV4 )
617+
492618data Datum ctx era where
493619 TxOutDatumHash
494620 :: L. DataHash
0 commit comments