diff --git a/cabal.project b/cabal.project index 40d3b04b9dc..e46e385ca6e 100644 --- a/cabal.project +++ b/cabal.project @@ -269,6 +269,9 @@ package cardano-wallet-network-layer package cardano-wallet-primitive tests: True +package cardano-wallet-read + tests: True + package cardano-wallet-text-class tests: True diff --git a/lib/read/cardano-wallet-read.cabal b/lib/read/cardano-wallet-read.cabal index 7e86c75fe11..e013af7f43d 100644 --- a/lib/read/cardano-wallet-read.cabal +++ b/lib/read/cardano-wallet-read.cabal @@ -43,6 +43,8 @@ flag release library import: opts-lib, language exposed-modules: + Cardano.Read.Ledger + Cardano.Read.Ledger.Tx.TxId Cardano.Wallet.Read Cardano.Wallet.Read.Block Cardano.Wallet.Read.Block.BHeader @@ -90,6 +92,7 @@ library Cardano.Wallet.Read.Tx.Outputs Cardano.Wallet.Read.Tx.ReferenceInputs Cardano.Wallet.Read.Tx.ScriptValidity + Cardano.Wallet.Read.Tx.TxId Cardano.Wallet.Read.Tx.Validity Cardano.Wallet.Read.Tx.Withdrawals Cardano.Wallet.Read.Tx.Witnesses @@ -147,6 +150,7 @@ test-suite test other-modules: Cardano.Wallet.Read.EraValueSpec Cardano.Wallet.Read.Tx.CBORSpec + Cardano.Wallet.Read.Tx.TxIdSpec Spec SpecHook diff --git a/lib/read/lib/Cardano/Read/Ledger.hs b/lib/read/lib/Cardano/Read/Ledger.hs new file mode 100644 index 00000000000..66af8306b74 --- /dev/null +++ b/lib/read/lib/Cardano/Read/Ledger.hs @@ -0,0 +1,25 @@ +{- | +Copyright: © 2024 Cardano Foundation +License: Apache-2.0 + +The module hierarchy "Cardano.Read.Ledger" contains data types +that are used for reading from the Cardano mainnet ledger. +Specifically, these data types are represented as +era-indexed unions of types from the Haskell ledger implementations +that are used in `cardano-node`. + +"Cardano.Read.Ledger" is meant to + +* Provide an era-indexed interface over the Byron and Shelley-style + ledger implementations. +* Improve the useability of type classes in the Shelley-style ledger + implementation with explicitly notated instances + and specialization to single eras. + +In contrast, the module hierarchy "Cardano.Wallet.Read" +is meant to provide a semantic view of the ledger, +such that the implementation of this view is built on +and mostly compatible with "Cardano.Read.Ledger". + +-} +module Cardano.Read.Ledger where diff --git a/lib/read/lib/Cardano/Read/Ledger/Tx/TxId.hs b/lib/read/lib/Cardano/Read/Ledger/Tx/TxId.hs new file mode 100644 index 00000000000..e2a0bdbd211 --- /dev/null +++ b/lib/read/lib/Cardano/Read/Ledger/Tx/TxId.hs @@ -0,0 +1,84 @@ +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeFamilies #-} + +-- | +-- Copyright: © 2024 Cardano Foundation +-- License: Apache-2.0 +-- +module Cardano.Read.Ledger.Tx.TxId + ( TxIdType + , TxId (..) + , getEraTxId + ) + where + +import Prelude + +import Cardano.Chain.UTxO + ( taTx + ) +import Cardano.Crypto.Hashing + ( serializeCborHash + ) +import Cardano.Ledger.Core + ( bodyTxL + , txIdTxBody + ) +import Cardano.Ledger.Crypto + ( StandardCrypto + ) +import Cardano.Wallet.Read + ( Tx + ) +import Cardano.Wallet.Read.Eras + ( Allegra + , Alonzo + , Babbage + , Byron + , Conway + , Era (..) + , IsEra (..) + , Mary + , Shelley + ) +import Cardano.Wallet.Read.Tx.Eras + ( onTx + ) +import Control.Lens + ( (^.) + ) + +import qualified Cardano.Chain.UTxO as BY +import qualified Cardano.Ledger.Core as SH.Core +import qualified Cardano.Ledger.TxIn as SH.TxIn + +type family TxIdType era where + TxIdType Byron = BY.TxId + TxIdType Shelley = SH.TxIn.TxId StandardCrypto + TxIdType Allegra = SH.TxIn.TxId StandardCrypto + TxIdType Mary = SH.TxIn.TxId StandardCrypto + TxIdType Alonzo = SH.TxIn.TxId StandardCrypto + TxIdType Babbage = SH.TxIn.TxId StandardCrypto + TxIdType Conway = SH.TxIn.TxId StandardCrypto + +newtype TxId era = TxId {unTxId :: TxIdType era} + +{-# INLINEABLE getEraTxId #-} +getEraTxId :: forall era. IsEra era => Tx era -> TxId era +getEraTxId = case theEra :: Era era of + Byron -> TxId . onTx byronTxId + Shelley -> TxId . onTx shelleyTxId + Allegra -> TxId . onTx shelleyTxId + Mary -> TxId . onTx shelleyTxId + Alonzo -> TxId . onTx shelleyTxId + Babbage -> TxId . onTx shelleyTxId + Conway -> TxId . onTx shelleyTxId + +byronTxId :: BY.ATxAux a -> BY.TxId +byronTxId = serializeCborHash . taTx + +shelleyTxId + :: SH.Core.EraTx era + => SH.Core.Tx era + -> SH.TxIn.TxId (SH.Core.EraCrypto era) +shelleyTxId tx = txIdTxBody (tx ^. bodyTxL) diff --git a/lib/read/lib/Cardano/Wallet/Read/Block/Gen/Build.hs b/lib/read/lib/Cardano/Wallet/Read/Block/Gen/Build.hs index 16dacb0bd37..2c3b819efd7 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Block/Gen/Build.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Block/Gen/Build.hs @@ -73,11 +73,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) ) -import Cardano.Wallet.Read.Tx.Hash - ( getEraTxHash +import Cardano.Wallet.Read.Tx.TxId + ( TxId + , getTxId + , txIdFromHash ) import Control.Category ( (.) @@ -118,6 +119,9 @@ import Data.Kind import Data.List.NonEmpty ( NonEmpty (..) ) +import Data.Maybe + ( fromJust + ) import Data.Monoid ( Endo (..) ) @@ -136,6 +140,7 @@ import Test.QuickCheck.Random ( mkQCGen ) +import qualified Cardano.Wallet.Read.Hash as Hash import qualified Data.ByteString.Char8 as B8 -- | DSL for building a tx @@ -267,8 +272,8 @@ interpretChainF m genAddress blockNo ml = do -> BlockParameters era -> ChainM m a updateCurrentBlock k newTx bp = - let txid' = getEraTxHash newTx - in interpretChainF (k $ TxId txid') genAddress blockNo + let txid' = getTxId newTx + in interpretChainF (k txid') genAddress blockNo $ Just $ CurrentBlockParameters $ over txsL (newTx :) bp @@ -389,11 +394,13 @@ exampleChainF = do -- Generate an invalid (not an hash) txid from a char txid :: Char -> TxId -txid = TxId . B8.pack . replicate 32 +txid = txIdFromHash . fromJust . Hash.hashFromBytes . B8.pack . replicate 32 -- Generate a random invalid txid genTxId :: Gen TxId -genTxId = TxId . B8.pack <$> replicateM 32 (choose ('a', 'z')) +genTxId = + txIdFromHash . fromJust . Hash.hashFromBytes . B8.pack + <$> replicateM 32 (choose ('a', 'z')) -- an infinite list of example blocks computed out of repeating the 'exampleChainF' exampleBlocks :: [ConsensusBlock] diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Allegra.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Allegra.hs index 0405cd60dfc..4b7277a0e72 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Allegra.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Allegra.hs @@ -38,10 +38,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + ) import Data.List.NonEmpty ( NonEmpty ) diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Alonzo.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Alonzo.hs index e3e0e2dc275..5fd3c6a9591 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Alonzo.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Alonzo.hs @@ -62,10 +62,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + ) import Data.Foldable ( toList ) diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Babbage.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Babbage.hs index eb9d81ed207..5b47bf57aad 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Babbage.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Babbage.hs @@ -79,10 +79,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + ) import Data.Foldable ( toList ) diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Byron.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Byron.hs index f1262ea601f..2f39be0dc56 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Byron.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Byron.hs @@ -33,10 +33,13 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + , hashFromTxId + ) import Data.ByteString ( ByteString ) @@ -50,6 +53,7 @@ import GHC.Stack import qualified Cardano.Chain.Common as Byron import qualified Cardano.Chain.UTxO as Byron import qualified Cardano.Crypto.Signing as Byron +import qualified Cardano.Wallet.Read.Hash as Hash mkByronTx :: HasCallStack @@ -64,8 +68,8 @@ mkByronTx TxParameters{txInputs, txOutputs} = outputs = txOutputs <&> mkByronTxOut mkByronInput :: (Index, TxId) -> TxIn -mkByronInput (Index idx, TxId h) = - TxInUtxo (hashUnsafe h) +mkByronInput (Index idx, txid) = + TxInUtxo (unsafeHashFromTxId txid) $ fromIntegral idx mkByronTxOut :: HasCallStack => (Address, Lovelace) -> Byron.TxOut @@ -88,10 +92,11 @@ mkByronAddrFromXPub addr = (Byron.VerKeyASD $ Byron.VerificationKey $ XPub addr $ ChainCode mempty) $ Byron.AddrAttributes Nothing Byron.NetworkMainOrStage -hashUnsafe :: ByteString -> Hash a -hashUnsafe x = case hashFromBytes x of - Nothing -> error "hashUnsafe: failed to hash" - Just h -> h +unsafeHashFromTxId :: TxId -> Hash a +unsafeHashFromTxId txid = + case hashFromBytes (Hash.hashToBytes $ hashFromTxId txid) of + Nothing -> error "hashUnsafe: failed to hash" + Just h -> h exampleByronTx :: ATxAux () exampleByronTx = mkByronTx exampleTxParameters diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Conway.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Conway.hs index 704f0f81054..8410b2a0387 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Conway.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Conway.hs @@ -89,10 +89,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + ) import Data.Foldable ( toList ) diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Mary.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Mary.hs index ac10d0bfa3b..e346348d47c 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Mary.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Mary.hs @@ -50,10 +50,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + ) import Data.Foldable ( toList ) diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Shelley.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Shelley.hs index 1799b4bc9e8..f28848e8cc7 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Shelley.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/Shelley.hs @@ -50,9 +50,6 @@ import Cardano.Ledger.Credential import Cardano.Ledger.Keys ( KeyHash (..) ) -import Cardano.Ledger.SafeHash - ( unsafeMakeSafeHash - ) import Cardano.Ledger.Shelley.API.Types ( ShelleyTx (ShelleyTx) , ShelleyTxAuxData @@ -78,10 +75,12 @@ import Cardano.Wallet.Read.Tx.Gen.TxParameters ( Address (..) , Index (..) , Lovelace (..) - , TxId (..) , TxParameters (..) , exampleTxParameters ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + ) import Data.ByteString ( ByteString ) @@ -107,7 +106,6 @@ import GHC.Stack ) import qualified Cardano.Ledger.Core as L -import qualified Cardano.Ledger.TxIn as L import qualified Data.ByteString.Short as B import qualified Data.Set as Set @@ -187,9 +185,9 @@ mkShelleyInput => Index -> TxId -> Set (TxIn StandardCrypto) -mkShelleyInput (Index idx) (TxId h) = +mkShelleyInput (Index idx) txid = Set.singleton - $ mkTxInPartial (L.TxId $ unsafeMakeSafeHash $ UnsafeHash $ B.toShort h) + $ mkTxInPartial txid $ fromIntegral idx exampleShelleyTx :: ShelleyTx (ShelleyEra StandardCrypto) diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/TxParameters.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/TxParameters.hs index 80759a5cb76..3ded632942b 100644 --- a/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/TxParameters.hs +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/Gen/TxParameters.hs @@ -1,6 +1,5 @@ module Cardano.Wallet.Read.Tx.Gen.TxParameters ( TxParameters (..) - , TxId (..) , Lovelace (..) , Address (..) , Index (..) @@ -10,21 +9,26 @@ where import Prelude +import Cardano.Wallet.Read.Tx.TxId + ( TxId + , txIdFromHash + ) import Data.ByteString ( ByteString ) import Data.List.NonEmpty ( NonEmpty (..) ) +import Data.Maybe + ( fromJust + ) import Numeric.Natural ( Natural ) +import qualified Cardano.Wallet.Read.Hash as Hash import qualified Data.ByteString.Char8 as B8 -newtype TxId = TxId ByteString - deriving (Eq, Show) - newtype Lovelace = Lovelace Integer deriving (Eq, Show) @@ -52,4 +56,4 @@ exampleTxParameters = } txIdx :: Char -> TxId -txIdx x = TxId $ B8.pack $ replicate 32 (x :: Char) +txIdx = txIdFromHash . fromJust . Hash.hashFromBytes . B8.pack . replicate 32 diff --git a/lib/read/lib/Cardano/Wallet/Read/Tx/TxId.hs b/lib/read/lib/Cardano/Wallet/Read/Tx/TxId.hs new file mode 100644 index 00000000000..96356b81f66 --- /dev/null +++ b/lib/read/lib/Cardano/Wallet/Read/Tx/TxId.hs @@ -0,0 +1,90 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE ViewPatterns #-} + +-- | +-- Copyright: © 2024 Cardano Foundation +-- License: Apache-2.0 +-- +-- 'TxId' — unique identifier for a 'TxBody'. +-- + +module Cardano.Wallet.Read.Tx.TxId + ( TxId + , pattern TxId + , txIdFromHash + , hashFromTxId + , getTxId + + -- * Internal + , fromLedgerTxId + ) + where + +import Prelude + +import Cardano.Ledger.Hashes + ( EraIndependentTxBody + ) +import Cardano.Ledger.Shelley.API.ByronTranslation + ( translateTxIdByronToShelley + ) +import Cardano.Wallet.Read + ( Tx + ) +import Cardano.Wallet.Read.Eras + ( Era (..) + , IsEra (..) + , Shelley + ) +import Cardano.Wallet.Read.Hash + ( Blake2b_256 + , Hash + ) + +import qualified Cardano.Ledger.SafeHash as SafeHash +import qualified Cardano.Ledger.TxIn as SH.TxIn +import qualified Cardano.Read.Ledger.Tx.TxId as L + +-- | A 'TxId' is a unique identifier for a transaction body. +-- It is obtained by hashing. +-- +-- 'TxId' is an __era-independent__ concept: +-- The inputs of any transaction can refer to 'TxId's from previous eras. +-- Hence, 'TxId' does not have an @era@ type index. +-- +-- Note: We use a type synonym here because we want zero-cost +-- coercion between @Set TxId@ and @Set L.TxIdType Shelley@. +-- Unfortunately, 'Set' expects a nominal role. +-- (See the design literature on 'Data.Coercible'.) +type TxId = L.TxIdType Shelley + +{-# COMPLETE TxId #-} +pattern TxId :: Hash Blake2b_256 EraIndependentTxBody -> TxId +pattern TxId x <- (hashFromTxId -> x) + where TxId x = txIdFromHash x + +txIdFromHash + :: Hash Blake2b_256 EraIndependentTxBody -> TxId +txIdFromHash = SH.TxIn.TxId . SafeHash.unsafeMakeSafeHash + +hashFromTxId + :: TxId -> Hash Blake2b_256 EraIndependentTxBody +hashFromTxId (SH.TxIn.TxId h) = SafeHash.extractHash h + +{-# INLINEABLE getTxId #-} +-- | Extract the 'TxId' of a transaction. +getTxId :: forall era. IsEra era => Tx era -> TxId +getTxId = fromLedgerTxId . L.getEraTxId + +{-# INLINEABLE fromLedgerTxId #-} +fromLedgerTxId :: forall era. IsEra era => L.TxId era -> TxId +fromLedgerTxId = case theEra :: Era era of + Byron -> translateTxIdByronToShelley . L.unTxId + Shelley -> L.unTxId + Allegra -> L.unTxId + Mary -> L.unTxId + Alonzo -> L.unTxId + Babbage -> L.unTxId + Conway -> L.unTxId diff --git a/lib/read/test/Cardano/Wallet/Read/Tx/TxIdSpec.hs b/lib/read/test/Cardano/Wallet/Read/Tx/TxIdSpec.hs new file mode 100644 index 00000000000..01e6942ff15 --- /dev/null +++ b/lib/read/test/Cardano/Wallet/Read/Tx/TxIdSpec.hs @@ -0,0 +1,253 @@ +{-# LANGUAGE GADTs #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeOperators #-} + +module Cardano.Wallet.Read.Tx.TxIdSpec + ( spec + ) where + +import Prelude + +import Cardano.Wallet.Read.Eras + ( IsEra + , K (..) + , (:.:) (Comp) + ) +import Cardano.Wallet.Read.Hash + ( hashFromBytesAsHex + ) +import Cardano.Wallet.Read.Tx + ( Tx + ) +import Cardano.Wallet.Read.Tx.CBOR + ( deserializeTx + ) +import Cardano.Wallet.Read.Tx.TxId + ( TxId + , getTxId + , txIdFromHash + ) +import Data.ByteArray.Encoding + ( Base (..) + , convertFromBase + ) +import Data.ByteString + ( ByteString + ) +import Data.ByteString.Lazy + ( fromStrict + ) +import Data.Maybe + ( fromMaybe + ) +import Test.Hspec + ( Spec + , describe + , it + ) +import Test.QuickCheck + ( Property + , (===) + ) + +import qualified Cardano.Wallet.Read as Read +import qualified Data.ByteString.Lazy as BL + +spec :: Spec +spec = + describe "TxId golden tests" $ do + it "byron tx" $ do + prop_matches_TxId byronTx byronTxId + it "shelley tx" $ + prop_matches_TxId shelleyTx shelleyTxId + it "allegra tx" $ + prop_matches_TxId allegraTx allegraTxId + it "mary tx" $ + prop_matches_TxId maryTx maryTxId + it "alonzo tx" $ + prop_matches_TxId alonzoTx alonzoTxId + it "babbage tx" $ + prop_matches_TxId babbageTx babbageTxId + +prop_matches_TxId :: forall era. IsEra era => Tx era -> TxId -> Property +prop_matches_TxId tx txId = getTxId tx === txId + +byronTxId :: TxId +byronTxId = unsafeTxIdFromHex + "376293e16cf87c377dce58ea6efd256276f86454fc13d390f673db789dcd7104" + +-- This transaction was assembled from the CBOR using +-- https://github.com/IntersectMBO/cardano-ledger +-- /blob/0a098670a7cf0d084c4087d4140f704f82784977 +-- /eras/byron/ledger/impl/test/golden/cbor/utxo/Tx +-- /eras/byron/ledger/impl/test/golden/cbor/utxo/TxWitness +byronTx :: Tx Read.Byron +byronTx = unsafeParseEraTxFromHex + "82839f8200d81858258258204ba839c420b3d2bd439530f891cae9\ + \a5d4c4d812044630dac72e8e0962feeecc182fff9f8282d8185821\ + \83581caa5372095aaa680d19d4ca496983a145709c3be18b0d4c83\ + \cb7bdc5ea0001a32dc988e182fffa0818200D81858858258404B6D\ + \7977346C4453453553346653483665744E6F756958657A4379456A\ + \4B63337447346A61306B466A4F38717A616932365A4D5055454A66\ + \457931356F78356B5840688AAD857BC7FF30FC6862DA1BE281F420\ + \C65271B76AB19782FF40E2955AF88819C38E5C79138F28073ABAE1\ + \52C882258B4420A0C1C9FDD26C98812697FC3E00\ + \" + +shelleyTxId :: TxId +shelleyTxId = unsafeTxIdFromHex + "ca011f22d07b97ee17f6f2e2ef568b9521791608169425e92993c8c6e5541d79" + +shelleyTx :: Tx Read.Shelley +shelleyTx = unsafeParseEraTxFromHex + "83a400828258200000000000000000000000000000000000000000\ + \000000000000000000000000008258200000000000000000000000\ + \000000000000000000000000000000000000000000010183825839\ + \010202020202020202020202020202020202020202020202020202\ + \020202020202020202020202020202020202020202020202020202\ + \0202021a005b8d8082583901030303030303030303030303030303\ + \030303030303030303030303030303030303030303030303030303\ + \03030303030303030303030303031a005b8d808258390104040404\ + \040404040404040404040404040404040404040404040404040404\ + \040404040404040404040404040404040404040404040404041a00\ + \7801e0021a0002102003191e46a10282845820130ae82201d7072e\ + \6fbfc0a1884fb54636554d14945b799125cf7ce38d477f51584058\ + \35ff78c6fc5e4466a179ca659fa85c99b8a3fba083f3f3f42ba360\ + \d479c64ef169914b52ade49b19a7208fd63a6e67a19c406b482660\ + \8fdc5307025506c307582001010101010101010101010101010101\ + \0101010101010101010101010101010144a1024100845820010000\ + \000000000000000000000000000000000000000000000000000000\ + \00005840e8e769ecd0f3c538f0a5a574a1c881775f086d6f4c845b\ + \81be9b78955728bffa7efa54297c6a5d73337bd6280205b1759c13\ + \f79d4c93f29871fc51b78aeba80e58200000000000000000000000\ + \00000000000000000000000000000000000000000044a1024100f6" + +allegraTxId :: TxId +allegraTxId = unsafeTxIdFromHex + "ca011f22d07b97ee17f6f2e2ef568b9521791608169425e92993c8c6e5541d79" + +allegraTx :: Tx Read.Allegra +allegraTx = unsafeParseEraTxFromHex + "83a400828258200000000000000000000000000000000000000000\ + \000000000000000000000000008258200000000000000000000000\ + \000000000000000000000000000000000000000000010183825839\ + \010202020202020202020202020202020202020202020202020202\ + \020202020202020202020202020202020202020202020202020202\ + \0202021a005b8d8082583901030303030303030303030303030303\ + \030303030303030303030303030303030303030303030303030303\ + \03030303030303030303030303031a005b8d808258390104040404\ + \040404040404040404040404040404040404040404040404040404\ + \040404040404040404040404040404040404040404040404041a00\ + \7801e0021a0002102003191e46a10282845820130ae82201d7072e\ + \6fbfc0a1884fb54636554d14945b799125cf7ce38d477f51584058\ + \35ff78c6fc5e4466a179ca659fa85c99b8a3fba083f3f3f42ba360\ + \d479c64ef169914b52ade49b19a7208fd63a6e67a19c406b482660\ + \8fdc5307025506c307582001010101010101010101010101010101\ + \0101010101010101010101010101010144a1024100845820010000\ + \000000000000000000000000000000000000000000000000000000\ + \00005840e8e769ecd0f3c538f0a5a574a1c881775f086d6f4c845b\ + \81be9b78955728bffa7efa54297c6a5d73337bd6280205b1759c13\ + \f79d4c93f29871fc51b78aeba80e58200000000000000000000000\ + \00000000000000000000000000000000000000000044a1024100f6" + +maryTxId :: TxId +maryTxId = unsafeTxIdFromHex + "ca011f22d07b97ee17f6f2e2ef568b9521791608169425e92993c8c6e5541d79" + +maryTx :: Tx Read.Mary +maryTx = unsafeParseEraTxFromHex + "83a400828258200000000000000000000000000000000000000000\ + \000000000000000000000000008258200000000000000000000000\ + \000000000000000000000000000000000000000000010183825839\ + \010202020202020202020202020202020202020202020202020202\ + \020202020202020202020202020202020202020202020202020202\ + \0202021a005b8d8082583901030303030303030303030303030303\ + \030303030303030303030303030303030303030303030303030303\ + \03030303030303030303030303031a005b8d808258390104040404\ + \040404040404040404040404040404040404040404040404040404\ + \040404040404040404040404040404040404040404040404041a00\ + \7801e0021a0002102003191e46a10282845820130ae82201d7072e\ + \6fbfc0a1884fb54636554d14945b799125cf7ce38d477f51584058\ + \35ff78c6fc5e4466a179ca659fa85c99b8a3fba083f3f3f42ba360\ + \d479c64ef169914b52ade49b19a7208fd63a6e67a19c406b482660\ + \8fdc5307025506c307582001010101010101010101010101010101\ + \0101010101010101010101010101010144a1024100845820010000\ + \000000000000000000000000000000000000000000000000000000\ + \00005840e8e769ecd0f3c538f0a5a574a1c881775f086d6f4c845b\ + \81be9b78955728bffa7efa54297c6a5d73337bd6280205b1759c13\ + \f79d4c93f29871fc51b78aeba80e58200000000000000000000000\ + \00000000000000000000000000000000000000000044a1024100f6" + +alonzoTxId :: TxId +alonzoTxId = unsafeTxIdFromHex + "ca011f22d07b97ee17f6f2e2ef568b9521791608169425e92993c8c6e5541d79" + +alonzoTx :: Tx Read.Alonzo +alonzoTx = unsafeParseEraTxFromHex + "84a400828258200000000000000000000000000000000000000000\ + \000000000000000000000000008258200000000000000000000000\ + \000000000000000000000000000000000000000000010183825839\ + \010202020202020202020202020202020202020202020202020202\ + \020202020202020202020202020202020202020202020202020202\ + \0202021a005b8d8082583901030303030303030303030303030303\ + \030303030303030303030303030303030303030303030303030303\ + \03030303030303030303030303031a005b8d808258390104040404\ + \040404040404040404040404040404040404040404040404040404\ + \040404040404040404040404040404040404040404040404041a00\ + \7801e0021a0002102003191e46a10282845820130ae82201d7072e\ + \6fbfc0a1884fb54636554d14945b799125cf7ce38d477f51584058\ + \35ff78c6fc5e4466a179ca659fa85c99b8a3fba083f3f3f42ba360\ + \d479c64ef169914b52ade49b19a7208fd63a6e67a19c406b482660\ + \8fdc5307025506c307582001010101010101010101010101010101\ + \0101010101010101010101010101010144a1024100845820010000\ + \000000000000000000000000000000000000000000000000000000\ + \00005840e8e769ecd0f3c538f0a5a574a1c881775f086d6f4c845b\ + \81be9b78955728bffa7efa54297c6a5d73337bd6280205b1759c13\ + \f79d4c93f29871fc51b78aeba80e58200000000000000000000000\ + \00000000000000000000000000000000000000000044a1024100f5\ + \f6" + +babbageTxId :: TxId +babbageTxId = unsafeTxIdFromHex + "1b81e33957d8b96e17a142ae06004213d25dc6abed0fc3949a16f3a96eb2a093" + +babbageTx :: Tx Read.Babbage +babbageTx = unsafeParseEraTxFromHex + "84a400818258200000000000000000000000000000000000000000\ + \000000000000000000000000000182a20058390101010101010101\ + \010101010101010101010101010101010101010101010101010101\ + \01010101010101010101010101010101010101010101011a001e84\ + \80a200583901020202020202020202020202020202020202020202\ + \020202020202020202020202020202020202020202020202020202\ + \0202020202020202011a0078175c021a0001faa403191e46a10281\ + \845820010000000000000000000000000000000000000000000000\ + \000000000000000058407154db81463825f150bb3b9b0824caf151\ + \3716f73498afe61d917a5621912a2b3df252bea14683a9ee56710d\ + \483a53a5aa35247e0d2b80e6300f7bdec763a20458200000000000\ + \000000000000000000000000000000000000000000000000000000\ + \44a1024100f5f6" + +-- | Parse a hex-encoded transaction into a particular era. +unsafeParseEraTxFromHex + :: forall era. IsEra era + => ByteString + -> Tx era +unsafeParseEraTxFromHex bytes = + either (error . show) id + . unComp + $ deserializeTx + (K (unsafeReadBase16 bytes) :: K BL.ByteString era) + where + unComp :: (f :.: g) era -> f (g era) + unComp (Comp fg) = fg + +unsafeTxIdFromHex :: ByteString -> TxId +unsafeTxIdFromHex = + txIdFromHash + . fromMaybe (error "unsafeTxIdFromHex: invalid hex length") + . hashFromBytesAsHex + +unsafeReadBase16 :: ByteString -> BL.ByteString +unsafeReadBase16 = either reportError fromStrict . convertFromBase Base16 + where + reportError = error "unsafeReadBase16: input not in Base16"