diff --git a/alonzo/impl/cardano-ledger-alonzo.cabal b/alonzo/impl/cardano-ledger-alonzo.cabal index eca3512219..d524df90ea 100644 --- a/alonzo/impl/cardano-ledger-alonzo.cabal +++ b/alonzo/impl/cardano-ledger-alonzo.cabal @@ -38,7 +38,8 @@ library deepseq, nothunks, shelley-spec-ledger, - small-steps + small-steps, + text hs-source-dirs: src ghc-options: @@ -58,6 +59,7 @@ library test Test.Cardano.Ledger.Alonzo.Serialisation.Generators build-depends: base >=4.14 && <4.15, + base16-bytestring, cardano-crypto-class, cardano-ledger-alonzo, cardano-ledger-shelley-ma-test, @@ -87,6 +89,7 @@ test-suite tests build-depends: base >=4.14 && <4.15, bytestring, + base16-bytestring, cardano-binary, cardano-ledger-alonzo, cardano-ledger-shelley-ma-test, diff --git a/alonzo/impl/src/Cardano/Ledger/Alonzo/Data.hs b/alonzo/impl/src/Cardano/Ledger/Alonzo/Data.hs index ad643c0b8b..b7ceb7f4a9 100644 --- a/alonzo/impl/src/Cardano/Ledger/Alonzo/Data.hs +++ b/alonzo/impl/src/Cardano/Ledger/Alonzo/Data.hs @@ -1,37 +1,82 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE DerivingVia #-} {-# LANGUAGE EmptyDataDeriving #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TypeFamilies #-} module Cardano.Ledger.Alonzo.Data - ( Data (..), + ( PlutusData (..), + -- Figure 2 (partial list) + Data (Data, ..), EraIndependentData, DataHash (..), hashData, ) where -import Cardano.Binary (FromCBOR (..), ToCBOR (..)) +import Cardano.Binary (FromCBOR (..), ToCBOR (..), decodeInt, encodeInt) import qualified Cardano.Crypto.Hash as Hash import Cardano.Ledger.Crypto (HASH) import qualified Cardano.Ledger.Crypto as CC import Cardano.Ledger.Era (Crypto, Era) import Control.DeepSeq (NFData) import Data.Coders +import Data.MemoBytes (Mem, MemoBytes (..), memoBytes) import GHC.Generics (Generic) import NoThunks.Class (NoThunks) import Shelley.Spec.Ledger.Hashing (HashAnnotated (..)) --- | TODO this should be isomorphic to the plutus (alonzo version) type -data Data era = NotReallyData - deriving (Eq, Ord, Generic, Show) +-- ===================================================================== +-- PlutusData is a placeholder for the type that Plutus expects as data. + +data PlutusData = NotReallyData + deriving (Eq, Show, Ord, Generic) + +instance NoThunks PlutusData + +-- | TODO appropriate serialisation for the Real Plutus Data +instance ToCBOR (PlutusData) where + toCBOR _ = encodeInt 0 + +instance FromCBOR (PlutusData) where + fromCBOR = do + i <- decodeInt + case i of + 0 -> pure NotReallyData + _ -> fail "oh no" + +instance FromCBOR (Annotator PlutusData) where + fromCBOR = pure <$> fromCBOR + +-- ============================================================================ +-- the newtype Data is a wrapper around the type that Plutus expects as data. +-- The newtype will memoize the serialized bytes. The strategy is to replace +-- PlutusData with the correct type + +newtype Data era = DataConstr (MemoBytes (PlutusData)) + deriving (Eq, Ord, Generic, ToCBOR, Show) + +deriving via + (Mem PlutusData) + instance + (Era era) => + FromCBOR (Annotator (Data era)) instance NoThunks (Data era) +pattern Data :: PlutusData -> Data era +pattern Data p <- + DataConstr (Memo p _) + where + Data p = DataConstr (memoBytes (To p)) + +-- ============================================================================= + data EraIndependentData newtype DataHash crypto @@ -53,15 +98,3 @@ hashData = DataHash . hashAnnotated -------------------------------------------------------------------------------- -- Serialisation -------------------------------------------------------------------------------- - --- | TODO appropriate serialisation -instance Era era => ToCBOR (Data era) where - toCBOR = encode . encodeData - where - encodeData NotReallyData = Sum NotReallyData 0 - -instance Era era => FromCBOR (Data era) where - fromCBOR = decode $ Summands "Data" decodeData - where - decodeData 0 = SumD NotReallyData - decodeData n = Invalid n diff --git a/alonzo/impl/src/Cardano/Ledger/Alonzo/PParams.hs b/alonzo/impl/src/Cardano/Ledger/Alonzo/PParams.hs index 0370239ac9..7aeb89a279 100644 --- a/alonzo/impl/src/Cardano/Ledger/Alonzo/PParams.hs +++ b/alonzo/impl/src/Cardano/Ledger/Alonzo/PParams.hs @@ -4,9 +4,12 @@ {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeApplications #-} @@ -24,6 +27,9 @@ module Cardano.Ledger.Alonzo.PParams PParamsUpdate, emptyPParamsUpdate, updatePParams, + EraIndependentPP, + HashTuple (..), + hashLanguagePP, ) where @@ -34,6 +40,7 @@ import Cardano.Binary encodeListLen, encodeMapLen, encodeWord, + serialize', ) import qualified Cardano.Crypto.Hash as Hash import Cardano.Ledger.Alonzo.Scripts (CostModel, ExUnits (..), Language, Prices (..)) @@ -42,12 +49,16 @@ import qualified Cardano.Ledger.Crypto as CC import Cardano.Ledger.Era import Control.DeepSeq (NFData) import Control.Monad (unless) +import Control.SetAlgebra (eval, (◁)) +import Data.ByteString (ByteString) import Data.Foldable (fold) import Data.Functor.Identity (Identity) import Data.List (nub) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Maybe (fromMaybe, mapMaybe) +import Data.Proxy +import Data.Set (singleton) import GHC.Generics (Generic) import NoThunks.Class (NoThunks (..)) import Numeric.Natural (Natural) @@ -75,6 +86,7 @@ import Shelley.Spec.Ledger.Serialization ) import Shelley.Spec.Ledger.Slot (EpochNo (..)) +-- ================================================================ -- TODO -- make type families for PParams and PParamsUpdate -- what is the encodeListLen ?? @@ -401,6 +413,9 @@ updatePParams pp ppup = fromMaybe' :: a -> StrictMaybe a -> a fromMaybe' x = fromMaybe x . strictMaybeToMaybe +-- =================================================== +-- Figure 1: "Definitions Used in Protocol Parameters" + data EraIndependentPP -- Hash of a subset of Protocol Parameters relevant to Plutus script evaluation @@ -413,3 +428,39 @@ newtype PPHash crypto deriving newtype instance CC.Crypto crypto => FromCBOR (PPHash crypto) deriving newtype instance CC.Crypto crypto => ToCBOR (PPHash crypto) + +hashLanguagePP :: forall era. Era era => PParams era -> Language -> PPHash (Crypto era) +hashLanguagePP pp lang = PPHash (hash2P (Proxy @EraIndependentPP) (Proxy @era) [HashItem restrictedmap]) + where + restrictedmap :: Map.Map Language CostModel + restrictedmap = eval (singleton lang ◁ (_costmdls pp)) + +-- =============================================================== +-- Hashing Tuples (upto to width 4) in a uniform way + +data HashItem where + HashItem :: ToCBOR a => a -> HashItem + +serializeHashTuple :: [HashItem] -> ByteString +serializeHashTuple xs = mconcat (map f xs) + where + f (HashItem x) = serialize' x + +-- Hash with 2 Proxys. This function is only used internally +-- The type families need alot of help keeping things straight, +-- the Proxys (and the type signature) provide that help. + +hash2P :: forall t e. Era e => Proxy t -> Proxy e -> [HashItem] -> Hash.Hash (CC.HASH (Crypto e)) t +hash2P _ _ x = Hash.castHash (Hash.hashWith serializeHashTuple x) + +class Era e => HashTuple e t where + hashTuple :: Proxy e -> t -> Hash.Hash (CC.HASH (Crypto e)) t + +instance (Era e, ToCBOR a, ToCBOR b) => HashTuple e (a, b) where + hashTuple p (x, y) = hash2P Proxy p [HashItem x, HashItem y] + +instance (Era e, ToCBOR a, ToCBOR b, ToCBOR c) => HashTuple e (a, b, c) where + hashTuple p (x, y, z) = hash2P Proxy p [HashItem x, HashItem y, HashItem z] + +instance (Era e, ToCBOR a, ToCBOR b, ToCBOR c, ToCBOR d) => HashTuple e (a, b, c, d) where + hashTuple p (w, x, y, z) = hash2P Proxy p [HashItem w, HashItem x, HashItem y, HashItem z] diff --git a/alonzo/impl/src/Cardano/Ledger/Alonzo/Scripts.hs b/alonzo/impl/src/Cardano/Ledger/Alonzo/Scripts.hs index 4f4bc4f482..635a153412 100644 --- a/alonzo/impl/src/Cardano/Ledger/Alonzo/Scripts.hs +++ b/alonzo/impl/src/Cardano/Ledger/Alonzo/Scripts.hs @@ -13,7 +13,7 @@ module Cardano.Ledger.Alonzo.Scripts Script (..), ExUnits (..), CostModel, - Language, + Language (..), Prices (..), ) where @@ -75,7 +75,10 @@ instance Semigroup ExUnits where instance Monoid ExUnits where mempty = ExUnits 0 0 --- Script language +-- Non-Native Script language. +-- This is an open type. We will add values of this type +-- for each Non-Native scripting language as they are added. + newtype Language = Language ByteString deriving (Eq, Generic, Show, Ord) @@ -160,3 +163,7 @@ instance decodeScript 0 = Ann (SumD NativeScript) <*! From decodeScript 1 = Ann (SumD PlutusScript) decodeScript n = Invalid n + +-- ================================================= +-- Languages +-- ================================================= diff --git a/alonzo/impl/src/Cardano/Ledger/Alonzo/Tx.hs b/alonzo/impl/src/Cardano/Ledger/Alonzo/Tx.hs index 6f9c5b9990..e006d8fdc7 100644 --- a/alonzo/impl/src/Cardano/Ledger/Alonzo/Tx.hs +++ b/alonzo/impl/src/Cardano/Ledger/Alonzo/Tx.hs @@ -1,53 +1,136 @@ +{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingStrategies #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} +-- | This module exports implementations of many of the functions outlined in the Alonzo specification. +-- The link to source of the specification +-- https://github.com/input-output-hk/cardano-ledger-specs/tree/master/alonzo/formal-spec +-- The most recent version of the document can be found here: +-- https://hydra.iohk.io/job/Cardano/cardano-ledger-specs/specs.alonzo-ledger/latest/download-by-type/doc-pdf/alonzo-changes +-- The functions can be found in Figures in that document, and sections of this code refer to those figures. module Cardano.Ledger.Alonzo.Tx - ( IsValidating (..), + ( Indexable (..), + -- Figure 1 + CostModel, + PPHash, + hashLanguagePP, + -- Figure 2 + ScriptData, + ScriptDataHash, + Data, + DataHash, + IsValidating (..), + hashData, + language, + plutusLanguage, + timelockLanguage, + nonNativeLanguages, + hashScriptData, + getCoin, + -- Figure 3 Tx (Tx, body, wits, isValidating, auxiliaryData), + TxBody (..), + -- Figure 4 ScriptPurpose (..), - Indexable (..), + -- Figure 5 + txinputs_fee, + getValidatorHash, + txscriptfee, + txbody, + minfee, + isNonNativeScriptAddress, + feesOK, + -- Figure 6 txrdmrs, rdptr, getMapFromValue, indexedRdmrs, + -- Figure 7 + valContext, + runPLCScript, + -- Figure 8 + getData, + collectNNScriptInputs, + evalScripts, + -- Figure 12 + scriptsNeeded, + checkScriptData, + hashSD, ) where import Cardano.Binary (FromCBOR (..), ToCBOR (..)) -import Cardano.Ledger.Alonzo.Data (Data) -import qualified Cardano.Ledger.Alonzo.Scripts as AlonzoScript (Tag (..)) -import Cardano.Ledger.Alonzo.TxBody (AlonzoBody, TxBody (..), TxIn) -import Cardano.Ledger.Alonzo.TxWitness (RdmrPtr (..), TxWitness (..)) +import Cardano.Ledger.Alonzo.Data (Data, DataHash, hashData) +import Cardano.Ledger.Alonzo.PParams (PPHash, PParams, PParams' (..), hashLanguagePP) +import Cardano.Ledger.Alonzo.Scripts (CostModel, ExUnits (..), Language (..), Prices (..)) +import qualified Cardano.Ledger.Alonzo.Scripts as AlonzoScript (Script (..), Tag (..)) +import Cardano.Ledger.Alonzo.TxBody + ( AlonzoBody, + TxBody (..), + TxOut (..), + ) +import Cardano.Ledger.Alonzo.TxWitness + ( RdmrPtr (..), + ScriptData (..), + ScriptDataHash (..), + TxWitness (..), + hashSD, + ) import Cardano.Ledger.Compactible import qualified Cardano.Ledger.Core as Core import Cardano.Ledger.Era (Crypto, Era) -import Cardano.Ledger.Mary.Value (AssetName, PolicyID, Value (..)) -import Cardano.Ledger.Val (DecodeMint, DecodeNonNegative, Val) +import Cardano.Ledger.Mary.Value (AssetName, PolicyID (..), Value (..)) +import Cardano.Ledger.Shelley.Constraints +import Cardano.Ledger.Val (DecodeMint, DecodeNonNegative, Val (coin, (<+>), (<×>))) +import Control.SetAlgebra (eval, (◁)) +import qualified Data.ByteString.Short as SBS (length) import Data.Coders +import Data.List (foldl') import qualified Data.Map as Map +import Data.Maybe (isJust, maybeToList) import Data.MemoBytes (Mem, MemoBytes (Memo), memoBytes) import Data.Sequence.Strict (StrictSeq) import qualified Data.Sequence.Strict as StrictSeq +import Data.Set (Set) import qualified Data.Set as Set + ( elemAt, + empty, + findIndex, + insert, + map, + null, + union, + ) +import Data.Text.Encoding (encodeUtf8) import Data.Typeable (Typeable) import Data.Word (Word64) import GHC.Generics (Generic) +import GHC.Records (HasField (..)) import NoThunks.Class (NoThunks) -import Shelley.Spec.Ledger.Address (RewardAcnt) +import Shelley.Spec.Ledger.Address (Addr (..), RewardAcnt, getRwdCred) import Shelley.Spec.Ledger.BaseTypes (StrictMaybe, maybeToStrictMaybe, strictMaybeToMaybe) -import Shelley.Spec.Ledger.Delegation.Certificates (DCert) -import Shelley.Spec.Ledger.TxBody (Wdrl (..), unWdrl) +import Shelley.Spec.Ledger.Coin (Coin (..)) +import Shelley.Spec.Ledger.Credential (Credential (ScriptHashObj)) +import Shelley.Spec.Ledger.Delegation.Certificates (DCert (..)) +import Shelley.Spec.Ledger.Scripts (ScriptHash) +import Shelley.Spec.Ledger.Tx (ValidateScript (isNativeScript)) +import Shelley.Spec.Ledger.TxBody (DelegCert (..), Delegation (..), TxIn (..), Wdrl (..), unWdrl) +import Shelley.Spec.Ledger.UTxO (UTxO (..), balance) -- =================================================== @@ -204,21 +287,135 @@ deriving via ) => FromCBOR (Annotator (Tx era)) --- =========================================== +-- ========================================================= +-- Figure 2: Definitions for Transactions + +-- For now, the only Non-Native Scriting language is Plutus +-- We might add new languages in the futures. + +nonNativeLanguages :: Set Language +nonNativeLanguages = Set.insert plutusLanguage Set.empty + +plutusLanguage :: Language +plutusLanguage = Language (encodeUtf8 "Plutus") + +timelockLanguage :: Language +timelockLanguage = Language (encodeUtf8 "Timelock") + +language :: AlonzoScript.Script era -> Language +language (AlonzoScript.NativeScript _) = timelockLanguage +language (AlonzoScript.PlutusScript) = plutusLanguage + +getCoin :: UsesValue era => TxOut era -> Coin +getCoin (TxOut _ v _) = coin v + +hashScriptData :: + Era era => + PParams era -> + Set Language -> + Map.Map RdmrPtr (Data era) -> + Maybe (ScriptDataHash era) +hashScriptData pp langs rdmrs = + if Map.null rdmrs && Set.null langs + then Nothing + else + let _newset = Set.map (hashLanguagePP pp) langs + in Nothing -- hash(rdmrs,newset) + +-- TODO fix this A SriptDataHash is a hash of a Virtual triple. See hashSD in TxWitness +-- This is something competely different + +-- =============================================================== +-- From the specification, Figure 5 "Functions related to fees" +-- =============================================================== + +isNonNativeScriptAddress :: + forall era. + ValidateScript era => + Tx era -> + Addr (Crypto era) -> + Bool +isNonNativeScriptAddress (TxConstr (Memo (TxRaw {_wits = w}) _)) addr = + case getValidatorHash addr of + Nothing -> False + Just hash -> + case Map.lookup hash (txscripts w) of + Nothing -> False + Just scr -> not (isNativeScript @era scr) + +feesOK :: + forall era. + ( UsesValue era, + AlonzoBody era, + UsesTxOut era, + ValidateScript era + ) => + PParams era -> + Tx era -> + UTxO era -> + Bool +feesOK pp tx (UTxO m) = + (bal >= txfee txb) + && (all (\txout -> not (isNonNativeScriptAddress tx (getField @"address" txout))) utxoFees) + && (minfee pp tx <= txfee txb) + where + txb = txbody tx + fees = inputs_fee txb + utxoFees = eval (fees ◁ m) -- compute the domain restriction to those inputs where fees are paid + bal = coin (balance @era (UTxO utxoFees)) + +-- | The keys of all the inputs of the TxBody (both the inputs for fees, and the normal inputs). +txins :: AlonzoBody era => TxBody era -> Set (TxIn (Crypto era)) +txins (TxBody {inputs = is, inputs_fee = fs}) = Set.union is fs + +txscriptfee :: Prices -> ExUnits -> Coin +txscriptfee (Prices pr_mem pr_steps) (ExUnits mem steps) = + (mem <×> pr_mem) <+> (steps <×> pr_steps) + +-- | txsize computes the length of the serialised bytes +txsize :: Tx era -> Integer +txsize (TxConstr (Memo _ bytes)) = fromIntegral (SBS.length bytes) + +minfee :: AlonzoBody era => PParams era -> Tx era -> Coin +minfee pp tx = + ((txsize tx) <×> (a pp)) + <+> (b pp) + <+> (txscriptfee (_prices pp) (exunits (txbody tx))) + where + a protparam = Coin (fromIntegral (_minfeeA protparam)) + b protparam = Coin (fromIntegral (_minfeeB protparam)) + +-- The specification uses "validatorHash" to extract ScriptHash from +-- an Addr. But not every Addr has a ScriptHash. In particular KeyHashObj +-- do not. So we use getValidatorHash which returns a Maybe type. + +getValidatorHash :: Addr crypto -> Maybe (ScriptHash crypto) +getValidatorHash (Addr _network (ScriptHashObj hash) _ref) = Just hash +getValidatorHash _ = Nothing + +txbody :: Tx era -> TxBody era +txbody (TxConstr (Memo (TxRaw {_body = b}) _)) = b + +txinputs_fee :: AlonzoBody era => TxBody era -> Set (TxIn (Crypto era)) +txinputs_fee (TxBody {inputs_fee = ifee}) = ifee + +-- =============================================================== -- Operations on scripts from specification -- Figure 6:Indexing script and data objects +-- =============================================================== data ScriptPurpose crypto = Minting !(PolicyID crypto) | Spending !(TxIn crypto) | Rewarding !(RewardAcnt crypto) -- Not sure if this is the right type. | Certifying !(DCert crypto) + deriving (Eq) class Indexable elem container where indexOf :: elem -> container -> Word64 atIndex :: Word64 -> container -> elem -instance Ord k => Indexable k (Set.Set k) where +instance Ord k => Indexable k (Set k) where indexOf n set = fromIntegral $ Set.findIndex n set atIndex i set = Set.elemAt (fromIntegral i) set @@ -239,10 +436,10 @@ rdptr :: TxBody era -> ScriptPurpose (Crypto era) -> RdmrPtr -rdptr txbody (Minting pid) = RdmrPtr AlonzoScript.Mint (indexOf pid (getMapFromValue (mint txbody))) -rdptr txbody (Spending txin) = RdmrPtr AlonzoScript.Spend (indexOf txin (inputs txbody)) -rdptr txbody (Rewarding racnt) = RdmrPtr AlonzoScript.Rewrd (indexOf racnt (unWdrl (wdrls txbody))) -rdptr txbody (Certifying d) = RdmrPtr AlonzoScript.Cert (indexOf d (certs txbody)) +rdptr txb (Minting pid) = RdmrPtr AlonzoScript.Mint (indexOf pid (getMapFromValue (mint txb))) +rdptr txb (Spending txin) = RdmrPtr AlonzoScript.Spend (indexOf txin (inputs txb)) +rdptr txb (Rewarding racnt) = RdmrPtr AlonzoScript.Rewrd (indexOf racnt (unWdrl (wdrls txb))) +rdptr txb (Certifying d) = RdmrPtr AlonzoScript.Cert (indexOf d (certs txb)) getMapFromValue :: Value crypto -> Map.Map (PolicyID crypto) (Map.Map AssetName Integer) getMapFromValue (Value _ m) = m @@ -250,7 +447,7 @@ getMapFromValue (Value _ m) = m txrdmrs :: (Era era, ToCBOR (Core.Script era)) => TxWitness era -> - Map.Map RdmrPtr (Data era) + Map.Map RdmrPtr (Data era, ExUnits) txrdmrs (TxWitness {witsRdmr = m}) = m indexedRdmrs :: @@ -261,7 +458,173 @@ indexedRdmrs :: ) => Tx era -> ScriptPurpose (Crypto era) -> - Maybe (Data era) -indexedRdmrs tx sp = Map.lookup policyid (txrdmrs . wits $ tx) + Maybe (Data era, ExUnits) +indexedRdmrs tx sp = Map.lookup policyid (txrdmrs . txwits $ tx) where policyid = rdptr (body tx) sp + +-- =============================================================== +-- From the specification, Figure 7 "Script Validation, cont." +-- =============================================================== + +-- | valContext collects info from the Tx and the UTxO and translates it into +-- a 'Data', which the Plutus language knows how to interpret. +valContext :: UTxO era -> Tx era -> ScriptPurpose (Crypto era) -> [Data era] +valContext _utxo _tx _sp = [] + +--TODO FIX THIS, when defined will always return singleton list +-- see also: collectNNScriptInputs where it is called + +-- TODO Specification says CostMod, not CostModel +runPLCScript :: + CostModel -> + AlonzoScript.Script era -> + [Data era] -> + ExUnits -> + (IsValidating, ExUnits) +runPLCScript _cost _script _data _exunits = (IsValidating True, ExUnits 0 0) -- TODO FIX THIS + +-- =============================================================== +-- From the specification, Figure 8 "Scripts and their Arguments" +-- =============================================================== + +getData :: + forall era. + ( ToCBOR (Core.AuxiliaryData era), + ToCBOR (Core.Script era), + UsesTxOut era, + HasField "datahash" (Core.TxOut era) (Maybe (DataHash (Crypto era))) + ) => + Tx era -> + UTxO era -> + ScriptPurpose (Crypto era) -> + [Data era] +getData tx (UTxO m) sp = case sp of + Minting _policyid -> [] + Rewarding _rewaccnt -> [] + Certifying _dcert -> [] + Spending txin -> + -- Only the Spending ScriptPurpose contains Data + case Map.lookup txin m of + Nothing -> [] + Just txout -> + case getField @"datahash" txout of + Nothing -> [] + Just hash -> + case Map.lookup hash (witsData (wits tx)) of + Nothing -> [] + Just d -> [d] + +collectNNScriptInputs :: + ( UsesTxOut era, + ToCBOR (Core.Script era), + ToCBOR (CompactForm (Core.Value era)), + ToCBOR (Core.AuxiliaryData era), + Core.Script era ~ AlonzoScript.Script era, + HasField "datahash" (Core.TxOut era) (Maybe (DataHash (Crypto era))) + ) => + PParams era -> + Tx era -> + UTxO era -> + [(AlonzoScript.Script era, [Data era], ExUnits, CostModel)] +collectNNScriptInputs _pp tx utxo = + [ (script, (d : (valContext utxo tx sp ++ getData tx utxo sp)), eu, cost) + | (sp, scripthash) <- scriptsNeeded utxo tx, -- TODO, IN specification ORDER IS WRONG + (d, eu) <- maybeToList (indexedRdmrs tx sp), + script <- maybeToList (Map.lookup scripthash (txscripts (txwits tx))), + cost <- maybeToList (Map.lookup (language script) (_costmdls _pp)) + ] + +evalScripts :: (AlonzoScript.Script era, [Data era], ExUnits, CostModel) -> Bool +evalScripts (AlonzoScript.NativeScript _timelock, _, _, _) = True +evalScripts (AlonzoScript.PlutusScript, ds, units, cost) = b + where + (IsValidating b, _exunits) = runPLCScript cost AlonzoScript.PlutusScript ds units + +-- =================================================================== +-- From Specification, Figure 12 "UTXOW helper functions" + +-- THE SPEC CALLS FOR A SET, BUT THAT NEEDS A BUNCH OF ORD INSTANCES (DCert) +scriptsNeeded :: + forall era. + ( UsesTxOut era, + AlonzoBody era + ) => + UTxO era -> + Tx era -> + [(ScriptPurpose (Crypto era), ScriptHash (Crypto era))] +scriptsNeeded (UTxO utxomap) tx = spend ++ reward ++ cert ++ minted + where + txb = txbody tx + !spend = foldl' accum [] (txins txb) + where + accum !ans !i = + case Map.lookup i utxomap of + Nothing -> ans + Just txout -> + case getValidatorHash (getField @"address" txout) of + Nothing -> ans + Just hash -> (Spending i, hash) : ans + + !reward = foldl' accum [] (Map.keys m2) + where + (Wdrl m2) = wdrls txb + accum !ans !accnt = case getRwdCred accnt of -- TODO IS THIS RIGHT? + (ScriptHashObj hash) -> (Rewarding accnt, hash) : ans + _ -> ans + + !cert = foldl addOnlyCwitness [] (certs txb) + + !minted = map (\(pid@(PolicyID hash)) -> (Minting pid, hash)) (Map.keys m3) + where + m3 = getMapFromValue (mint txb) + +-- We only find certificate witnesses in Delegating and Deregistration DCerts +-- that have ScriptHashObj credentials. +addOnlyCwitness :: + [(ScriptPurpose crypto, ScriptHash crypto)] -> + DCert crypto -> + [(ScriptPurpose crypto, ScriptHash crypto)] +addOnlyCwitness !ans !(DCertDeleg (c@(DeRegKey (ScriptHashObj hk)))) = + (Certifying $ DCertDeleg c, hk) : ans +addOnlyCwitness !ans !(DCertDeleg (c@(Delegate (Delegation (ScriptHashObj hk) _dpool)))) = + (Certifying $ DCertDeleg c, hk) : ans +addOnlyCwitness !ans _ = ans + +checkScriptData :: + forall era. + ( ToCBOR (Core.AuxiliaryData era), + ValidateScript era, + ToCBOR (CompactForm (Core.Value era)), + UsesTxOut era, + HasField "datahash" (Core.TxOut era) (Maybe (DataHash (Crypto era))) + ) => + Tx era -> + UTxO era -> + (ScriptPurpose (Crypto era), ScriptHash (Crypto era)) -> + Bool +checkScriptData tx utxo (sp, _h) = any ok scripts + where + scripts = txscripts (txwits tx) + isSpending (Spending _) = True + isSpending _ = False + ok s = + (isNativeScript @era s) + || ( isJust (indexedRdmrs tx sp) + && (not (isSpending sp) || not (null (getData tx utxo sp))) + ) + +-- The function hasSD, specified in Figure 12 +-- hashSD :: TxWitness era -> Maybe (ScriptDataHash (Crypto era)) +-- is defined in Cardano.Ledger.Alonzo.TxWitness + +-- languages:: TxWitness era -> Set Language -- TODO + +txscripts :: + (Era era, ToCBOR (Core.Script era)) => + TxWitness era -> + Map.Map (ScriptHash (Crypto era)) (Core.Script era) +txscripts x = witsScript x + +txwits :: (Era era, ToCBOR (Core.AuxiliaryData era)) => Tx era -> TxWitness era +txwits x = wits x diff --git a/alonzo/impl/src/Cardano/Ledger/Alonzo/TxBody.hs b/alonzo/impl/src/Cardano/Ledger/Alonzo/TxBody.hs index 5f7725b7f9..c8c517e019 100644 --- a/alonzo/impl/src/Cardano/Ledger/Alonzo/TxBody.hs +++ b/alonzo/impl/src/Cardano/Ledger/Alonzo/TxBody.hs @@ -18,11 +18,11 @@ module Cardano.Ledger.Alonzo.TxBody ( IsFee (..), - TxIn (..), TxOut (TxOut, TxOutCompact), TxBody ( TxBody, inputs, + inputs_fee, outputs, certs, wdrls, @@ -36,8 +36,6 @@ module Cardano.Ledger.Alonzo.TxBody scriptHash ), AlonzoBody, - txins, - txinputs_fee, ) where @@ -67,9 +65,7 @@ import Data.MemoBytes (Mem, MemoBytes (..), memoBytes) import Data.Sequence.Strict (StrictSeq) import qualified Data.Sequence.Strict as StrictSeq import Data.Set (Set) -import qualified Data.Set as Set import Data.Typeable (Typeable) -import Data.Word (Word64) import GHC.Generics (Generic) import GHC.Stack (HasCallStack) import NoThunks.Class (InspectHeapNamed (..), NoThunks) @@ -80,7 +76,7 @@ import Shelley.Spec.Ledger.CompactAddr (CompactAddr, compactAddr, decompactAddr) import Shelley.Spec.Ledger.Delegation.Certificates (DCert) import Shelley.Spec.Ledger.Hashing import Shelley.Spec.Ledger.PParams (Update) -import Shelley.Spec.Ledger.TxBody (TxId, Wdrl (Wdrl), unWdrl) +import Shelley.Spec.Ledger.TxBody (TxIn (..), Wdrl (Wdrl), unWdrl) import Prelude hiding (lookup) -- | Tag indicating whether an input should be used to pay transaction fees. @@ -98,26 +94,6 @@ newtype IsFee = IsFee Bool FromCBOR ) --- | Input of a UTxO. This references the transaction being spent from and the --- index of the output being spent, as well as a tag indicating whether this --- input should be used as a fee. -data TxIn crypto - = TxInCompact - {-# UNPACK #-} !(TxId crypto) - {-# UNPACK #-} !Word64 - !IsFee - deriving (Generic) - -deriving instance Ord (TxIn crypto) - -deriving instance Eq (TxIn crypto) - -deriving instance Show (TxIn crypto) - -instance CC.Crypto crypto => NFData (TxIn crypto) - -instance NoThunks (TxIn crypto) - data TxOut era = TxOutCompact {-# UNPACK #-} !(CompactAddr (Crypto era)) @@ -166,6 +142,7 @@ pattern TxOut addr vl dh <- data TxBodyRaw era = TxBodyRaw { _inputs :: !(Set (TxIn (Crypto era))), + _inputs_fee :: !(Set (TxIn (Crypto era))), _outputs :: !(StrictSeq (TxOut era)), _certs :: !(StrictSeq (DCert (Crypto era))), _wdrls :: !(Wdrl (Crypto era)), @@ -243,6 +220,7 @@ type AlonzoBody era = pattern TxBody :: AlonzoBody era => Set (TxIn (Crypto era)) -> + Set (TxIn (Crypto era)) -> StrictSeq (TxOut era) -> StrictSeq (DCert (Crypto era)) -> Wdrl (Crypto era) -> @@ -257,6 +235,7 @@ pattern TxBody :: TxBody era pattern TxBody { inputs, + inputs_fee, outputs, certs, wdrls, @@ -273,6 +252,7 @@ pattern TxBody ( Memo TxBodyRaw { _inputs = inputs, + _inputs_fee = inputs_fee, _outputs = outputs, _certs = certs, _wdrls = wdrls, @@ -290,6 +270,7 @@ pattern TxBody where TxBody inputs' + inputs_fee' outputs' certs' wdrls' @@ -306,6 +287,7 @@ pattern TxBody ( encodeTxBodyRaw $ TxBodyRaw inputs' + inputs_fee' outputs' certs' wdrls' @@ -328,17 +310,6 @@ instance Era era => HashAnnotated (TxBody era) era where -- Serialisation -------------------------------------------------------------------------------- -instance CC.Crypto crypto => ToCBOR (TxIn crypto) where - toCBOR (TxInCompact txId idx isFee) = - encode $ - Rec TxInCompact - !> To txId - !> To idx - !> To isFee - -instance CC.Crypto crypto => FromCBOR (TxIn crypto) where - fromCBOR = decode $ RecD TxInCompact - TxBodyRaw i o c w f (ValidityInterval b t) u mh mi e s + ( \i ifee o f t c w u mh b mi e s -> + TxBodyRaw i ifee o c w f (ValidityInterval b t) u mh mi e s ) !> Key 0 (E encodeFoldable _inputs) + !> Key 13 (E encodeFoldable _inputs_fee) !> Key 1 (E encodeFoldable _outputs) !> Key 2 (To _txfee) !> encodeKeyedStrictMaybe 3 top @@ -434,6 +407,7 @@ instance initial :: TxBodyRaw era initial = TxBodyRaw + mempty mempty StrictSeq.empty StrictSeq.empty @@ -450,6 +424,10 @@ instance field (\x tx -> tx {_inputs = x}) (D (decodeSet fromCBOR)) + bodyFields 13 = + field + (\x tx -> tx {_inputs_fee = x}) + (D (decodeSet fromCBOR)) bodyFields 1 = field (\x tx -> tx {_outputs = x}) @@ -496,17 +474,3 @@ instance FromCBOR (Annotator (TxBodyRaw era)) where fromCBOR = pure <$> fromCBOR - --- ============================================================ --- From the specification, Figure 5 "Functions related to fees" - -txins :: AlonzoBody era => TxBody era -> Set (TxId (Crypto era), Word64) -txins (TxBody {inputs = is}) = Set.foldl' accum Set.empty is - where - accum ans (TxInCompact idx index _) = Set.insert (idx, index) ans - -txinputs_fee :: AlonzoBody era => TxBody era -> Set (TxId (Crypto era), Word64) -txinputs_fee (TxBody {inputs = is}) = Set.foldl' accum Set.empty is - where - accum ans (TxInCompact idx index (IsFee True)) = Set.insert (idx, index) ans - accum ans _ = ans diff --git a/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWitness.hs b/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWitness.hs index 6aebe996cc..9d4671b042 100644 --- a/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWitness.hs +++ b/alonzo/impl/src/Cardano/Ledger/Alonzo/TxWitness.hs @@ -7,6 +7,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -19,19 +20,22 @@ module Cardano.Ledger.Alonzo.TxWitness ( RdmrPtr (..), TxWitness (TxWitness, witsVKey, witsBoot, witsScript, witsData, witsRdmr), + ScriptData (ScriptData), EraIndependentScriptData, ScriptDataHash (..), + hashSD, ) where import Cardano.Binary (FromCBOR (..), ToCBOR (..)) import qualified Cardano.Crypto.Hash as Hash import Cardano.Ledger.Alonzo.Data (Data, DataHash) -import Cardano.Ledger.Alonzo.Scripts (Tag) +import Cardano.Ledger.Alonzo.Scripts (ExUnits (..), Tag) import qualified Cardano.Ledger.Core as Core import Cardano.Ledger.Crypto (HASH) import qualified Cardano.Ledger.Crypto as CC import Cardano.Ledger.Era (Era (Crypto)) +import Control.Applicative (liftA2) import Control.DeepSeq (NFData) import Data.Coders import Data.Map.Strict (Map) @@ -45,11 +49,18 @@ import GHC.Generics import GHC.Records import NoThunks.Class (NoThunks) import Shelley.Spec.Ledger.Address.Bootstrap (BootstrapWitness) +import Shelley.Spec.Ledger.Hashing (HashAnnotated (..)) import Shelley.Spec.Ledger.Keys import Shelley.Spec.Ledger.Scripts (ScriptHash) -import Shelley.Spec.Ledger.Serialization (mapFromCBOR, mapToCBOR) +import Shelley.Spec.Ledger.Serialization + ( decodeMapTraverse, + mapFromCBOR, + mapToCBOR, + ) import Shelley.Spec.Ledger.TxBody (WitVKey) +-- ========================================== + data RdmrPtr = RdmrPtr !Tag @@ -97,7 +108,7 @@ pattern TxWitness :: Set (BootstrapWitness (Crypto era)) -> Map (ScriptHash (Crypto era)) (Core.Script era) -> Map (DataHash (Crypto era)) (Data era) -> - Map RdmrPtr (Data era) -> + Map RdmrPtr (Data era, ExUnits) -> TxWitness era pattern TxWitness { witsVKey, @@ -176,7 +187,7 @@ instance . unScriptDataConstr $ _witsScriptData m -instance HasField "witsRdmrs" (TxWitness era) (Map RdmrPtr (Data era)) where +instance HasField "witsRdmrs" (TxWitness era) (Map RdmrPtr (Data era, ExUnits)) where getField (TxWitnessConstr (Memo m _)) = _scriptDataRdmrs . memotype @@ -190,7 +201,7 @@ instance HasField "witsRdmrs" (TxWitness era) (Map RdmrPtr (Data era)) where data ScriptDataRaw era = ScriptDataRaw { _scriptDataScripts :: Map (ScriptHash (Crypto era)) (Core.Script era), _scriptDataData :: Map (DataHash (Crypto era)) (Data era), - _scriptDataRdmrs :: Map RdmrPtr (Data era) + _scriptDataRdmrs :: Map RdmrPtr (Data era, ExUnits) } deriving (Generic) @@ -228,7 +239,7 @@ pattern ScriptData :: (Era era, ToCBOR (Core.Script era)) => Map (ScriptHash (Crypto era)) (Core.Script era) -> Map (DataHash (Crypto era)) (Data era) -> - Map RdmrPtr (Data era) -> + Map RdmrPtr (Data era, ExUnits) -> ScriptData era pattern ScriptData s d r <- ScriptDataConstr (Memo (ScriptDataRaw s d r) _) @@ -297,15 +308,31 @@ instance decode $ Ann (RecD ScriptDataRaw) <*! D (sequence <$> mapFromCBOR) - <*! Ann (D mapFromCBOR) - <*! Ann (D mapFromCBOR) + <*! D (sequence <$> mapFromCBOR) + <*! D (splitMapFromCBOR fromCBOR fromCBOR fromCBOR) + +-- ScriptData includes a field with type: (Map RdmrPtr (Data era, ExUnits)) +-- We only have a (ToCBOR (Annotator (Data era))) instance, so we need a special +-- way to decode a Map where one half of its range has only a (FromCBOR (Annotator _)) +-- instance. We have to be careful since the map is encodedwith 'mapToCBOR' and the +-- decoder needs to be consistent with that encoding. + +splitMapFromCBOR :: + Ord dom => + Decoder s dom -> + Decoder s (Annotator rngLeft) -> + Decoder s rngRight -> + Decoder s (Annotator (Map dom (rngLeft, rngRight))) +splitMapFromCBOR a b c = decodeMapTraverse (pure <$> a) (liftPair <$> decodePair b c) + where + liftPair :: (Annotator a, b) -> Annotator (a, b) + liftPair (x, y) = liftA2 (,) x (pure y) deriving via (Mem (ScriptDataRaw era)) instance ( Era era, FromCBOR (Annotator (Core.Script era)), - FromCBOR (Data era), Typeable (Core.Script era) ) => FromCBOR (Annotator (ScriptData era)) @@ -317,7 +344,7 @@ encodeWitnessRaw :: Set (BootstrapWitness (Crypto era)) -> Map (ScriptHash (Crypto era)) (Core.Script era) -> Map (DataHash (Crypto era)) (Data era) -> - Map RdmrPtr (Data era) -> + Map RdmrPtr (Data era, ExUnits) -> Encode ('Closed 'Dense) (TxWitnessRaw era) encodeWitnessRaw a b s d r = Rec TxWitnessRaw @@ -349,3 +376,31 @@ deriving via ToCBOR (Core.Script era) ) => FromCBOR (Annotator (TxWitness era)) + +-- ======================================================= +-- To compute a ScriptDataHash from a TxWitness we have to hash +-- the virtual triple: +-- ( Map (ScriptHash c) script, +-- Map (DataHash c) (Data era), +-- Map RdmrPtr (Data era,ExUnits) ) +-- See function hashSD in Figure 12 "UTXOW helper functions" +-- This virtual triple is stored inside of the TxWitness as a concrete value +-- of type (ScriptData era). It is a newtype around a MemoBytes, so hashing +-- it is just hashing the memoized bytestring inside the MemoBytes. +-- Furtunately, applying toCBOR to (ScriptData era) is just that memoized +-- bytstring, so we can make a HashAnnotated instance for (ScriptData era) +-- to compute the hash, since (hashAnnotated x) is (hash (toCBOR x)) + +instance (Era era, Typeable era) => HashAnnotated (ScriptData era) era where + type HashIndex (ScriptData era) = EraIndependentScriptData + +-- Note that 'triple' has type ScriptData in 'hashSD' below. + +hashSD :: + (Era era, ToCBOR (Core.Script era)) => + TxWitness era -> + Maybe (ScriptDataHash (Crypto era)) +hashSD (w@(TxWitnessConstr (Memo (TxWitnessRaw _ _ triple) _))) = + if (Map.null (witsScript w) && Map.null (witsData w) && Map.null (witsRdmr w)) + then Nothing + else Just (ScriptDataHash (hashAnnotated triple)) diff --git a/alonzo/impl/test/lib/Test/Cardano/Ledger/Alonzo/Serialisation/Generators.hs b/alonzo/impl/test/lib/Test/Cardano/Ledger/Alonzo/Serialisation/Generators.hs index 9e270c7fae..123b9f9e85 100644 --- a/alonzo/impl/test/lib/Test/Cardano/Ledger/Alonzo/Serialisation/Generators.hs +++ b/alonzo/impl/test/lib/Test/Cardano/Ledger/Alonzo/Serialisation/Generators.hs @@ -13,14 +13,13 @@ module Test.Cardano.Ledger.Alonzo.Serialisation.Generators where import Cardano.Ledger.Alonzo (AlonzoEra) -import Cardano.Ledger.Alonzo.Data (Data (..), DataHash (..)) +import Cardano.Ledger.Alonzo.Data (Data (..), DataHash (..), PlutusData (..)) import Cardano.Ledger.Alonzo.PParams (PPHash (..)) import Cardano.Ledger.Alonzo.Scripts (ExUnits (..), Script (..), Tag (..)) import Cardano.Ledger.Alonzo.Tx import Cardano.Ledger.Alonzo.TxBody ( IsFee (..), TxBody (TxBody), - TxIn (..), TxOut (..), ) import Cardano.Ledger.Alonzo.TxWitness @@ -35,7 +34,7 @@ import Test.Shelley.Spec.Ledger.Serialisation.EraIndepGenerators () -- TODO correct arbitrary generator for Data instance Arbitrary (Data era) where - arbitrary = pure NotReallyData + arbitrary = pure (Data NotReallyData) instance Arbitrary Tag where arbitrary = elements [Spend, Mint, Cert, Rewrd] @@ -63,6 +62,17 @@ instance <*> arbitrary <*> arbitrary +instance + ( Era era, + UsesValue era, + Mock (Crypto era), + Arbitrary (Core.Script era), + UsesScript era + ) => + Arbitrary (ScriptData era) + where + arbitrary = ScriptData <$> arbitrary <*> arbitrary <*> arbitrary + deriving newtype instance CC.Crypto c => Arbitrary (ScriptDataHash c) deriving newtype instance CC.Crypto c => Arbitrary (DataHash c) @@ -71,17 +81,6 @@ deriving newtype instance CC.Crypto c => Arbitrary (PPHash c) deriving newtype instance Arbitrary IsFee -instance - ( CC.Crypto c - ) => - Arbitrary (TxIn c) - where - arbitrary = - TxInCompact - <$> arbitrary - <*> arbitrary - <*> arbitrary - instance ( Era era, UsesValue era, @@ -111,6 +110,7 @@ instance <*> arbitrary <*> arbitrary <*> arbitrary + <*> arbitrary <*> genMintValues @c <*> arbitrary <*> arbitrary diff --git a/alonzo/impl/test/test/Test/Cardano/Ledger/Alonzo/Serialisation/Tripping.hs b/alonzo/impl/test/test/Test/Cardano/Ledger/Alonzo/Serialisation/Tripping.hs index 5b972da91e..9ab690196a 100644 --- a/alonzo/impl/test/test/Test/Cardano/Ledger/Alonzo/Serialisation/Tripping.hs +++ b/alonzo/impl/test/test/Test/Cardano/Ledger/Alonzo/Serialisation/Tripping.hs @@ -5,9 +5,11 @@ module Test.Cardano.Ledger.Alonzo.Serialisation.Tripping where import Cardano.Binary import Cardano.Ledger.Alonzo +import Cardano.Ledger.Alonzo.Data import Cardano.Ledger.Alonzo.Tx (Tx) import Cardano.Ledger.Alonzo.TxBody (TxBody) -import Cardano.Ledger.Alonzo.TxWitness (TxWitness) +import Cardano.Ledger.Alonzo.TxWitness +import qualified Data.ByteString.Base16.Lazy as Base16 import qualified Data.ByteString.Lazy.Char8 as BSL import Test.Cardano.Ledger.Alonzo.Serialisation.Generators () import Test.Cardano.Ledger.ShelleyMA.Serialisation.Coders @@ -32,7 +34,13 @@ trippingAnn x = case roundTripAnn x of False Left stuff -> counterexample - ("Failed to decode: " <> show stuff) + ( concat + [ "Failed to decode: ", + show stuff, + "\nbytes: ", + show (Base16.encode (serialize x)) + ] + ) False tests :: TestTree @@ -41,6 +49,10 @@ tests = "Alonzo CBOR round-trip" [ testProperty "alonzo/TxWitness" $ trippingAnn @(TxWitness (AlonzoEra C_Crypto)), + testProperty "alonzo/Data" $ + trippingAnn @(Data (AlonzoEra C_Crypto)), + testProperty "alonzo/ScriptDataRaw" $ + trippingAnn @(ScriptData (AlonzoEra C_Crypto)), testProperty "alonzo/TxBody" $ trippingAnn @(TxBody (AlonzoEra C_Crypto)), testProperty "alonzo/Tx" $ diff --git a/semantics/executable-spec/src/Data/Coders.hs b/semantics/executable-spec/src/Data/Coders.hs index 2c55ed8b3e..668ac806e4 100644 --- a/semantics/executable-spec/src/Data/Coders.hs +++ b/semantics/executable-spec/src/Data/Coders.hs @@ -45,6 +45,7 @@ module Data.Coders decode, runE, -- Used in testing decodeList, + decodePair, decodeSeq, decodeStrictSeq, decodeSet, @@ -54,6 +55,7 @@ module Data.Coders unusedRequiredKeys, duplicateKey, wrapCBORArray, + encodePair, encodeFoldable, decodeCollectionWithLen, decodeCollection, @@ -147,6 +149,19 @@ decodeNullMaybe decoder = do pure Nothing _ -> Just <$> decoder + +decodePair :: Decoder s a -> Decoder s b -> Decoder s (a,b) +decodePair first second = decodeRecordNamed "pair" (const 2) $ do + a <- first + b <- second + pure (a,b) + +encodePair :: (a -> Encoding) -> (b -> Encoding) -> (a,b) -> Encoding +encodePair encodeFirst encodeSecond (x,y) = encodeListLen 2 + <> encodeFirst x + <> encodeSecond y + + invalidKey :: Word -> Decoder s a invalidKey k = cborError $ DecoderErrorCustom "not a valid key:" (Text.pack $ show k) diff --git a/semantics/small-steps-test/small-steps-test.cabal b/semantics/small-steps-test/small-steps-test.cabal index fcb7f9deb3..e8e2c27552 100644 --- a/semantics/small-steps-test/small-steps-test.cabal +++ b/semantics/small-steps-test/small-steps-test.cabal @@ -33,6 +33,7 @@ library , microlens-th , mtl , nothunks + , prettyprinter , transformers >= 0.5 , QuickCheck -- IOHK deps @@ -55,6 +56,7 @@ test-suite doctests default-language: Haskell2010 build-depends: base , doctest + , prettyprinter build-tool-depends: doctest-discover:doctest-discover ghc-options: -Wall diff --git a/shelley/chain-and-ledger/executable-spec/src/Shelley/Spec/Ledger/Tx.hs b/shelley/chain-and-ledger/executable-spec/src/Shelley/Spec/Ledger/Tx.hs index a3599df1fc..c1ea729551 100644 --- a/shelley/chain-and-ledger/executable-spec/src/Shelley/Spec/Ledger/Tx.hs +++ b/shelley/chain-and-ledger/executable-spec/src/Shelley/Spec/Ledger/Tx.hs @@ -382,6 +382,8 @@ class where validateScript :: Core.Script era -> Tx era -> Bool hashScript :: Core.Script era -> ScriptHash (Crypto era) + isNativeScript :: Core.Script era -> Bool + isNativeScript _ = True -- | Script evaluator for native multi-signature scheme. 'vhks' is the set of -- key hashes that signed the transaction to be validated.