diff --git a/plutarch-ledger-api/plutarch-ledger-api.cabal b/plutarch-ledger-api/plutarch-ledger-api.cabal index 879619b4e..2d0f13190 100644 --- a/plutarch-ledger-api/plutarch-ledger-api.cabal +++ b/plutarch-ledger-api/plutarch-ledger-api.cabal @@ -66,6 +66,7 @@ library other-modules: Plutarch.LedgerApi.V1.Address + Plutarch.LedgerApi.V1.Contexts Plutarch.LedgerApi.V1.Credential Plutarch.LedgerApi.V1.Crypto Plutarch.LedgerApi.V1.DCert @@ -74,6 +75,7 @@ library Plutarch.LedgerApi.V1.Tx Plutarch.LedgerApi.V2.Tx Plutarch.LedgerApi.V3.Contexts + Plutarch.LedgerApi.V3.Tx build-depends: , bytestring @@ -91,5 +93,8 @@ test-suite tests import: test-lang type: exitcode-stdio-1.0 main-is: Main.hs - build-depends: plutarch-orphanage + build-depends: + , plutarch-orphanage + , prettyprinter + hs-source-dirs: test diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V1.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V1.hs index 1b1250b5e..88d8a09e8 100644 --- a/plutarch-ledger-api/src/Plutarch/LedgerApi/V1.hs +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V1.hs @@ -2,7 +2,7 @@ module Plutarch.LedgerApi.V1 ( -- * Contexts - PScriptPurpose (..), + Contexts.PScriptPurpose (..), PScriptContext (..), -- * Certificates @@ -57,6 +57,7 @@ import Plutarch.DataRepr ( import Plutarch.LedgerApi.AssocMap qualified as AssocMap import Plutarch.LedgerApi.Interval qualified as Interval import Plutarch.LedgerApi.V1.Address qualified as Address +import Plutarch.LedgerApi.V1.Contexts qualified as Contexts import Plutarch.LedgerApi.V1.Credential qualified as Credential import Plutarch.LedgerApi.V1.Crypto qualified as Crypto import Plutarch.LedgerApi.V1.DCert qualified as DCert @@ -162,46 +163,6 @@ deriving via -- | @since 3.1.1 instance PTryFrom PData (PAsData PTxInInfo) --- | @since 3.1.1 -data PScriptPurpose (s :: S) - = PMinting (Term s (PDataRecord '["_0" ':= Value.PCurrencySymbol])) - | PSpending (Term s (PDataRecord '["_0" ':= Tx.PTxOutRef])) - | PRewarding (Term s (PDataRecord '["_0" ':= Credential.PStakingCredential])) - | PCertifying (Term s (PDataRecord '["_0" ':= DCert.PDCert])) - deriving stock - ( -- | @since 3.1.1 - Generic - ) - deriving anyclass - ( -- | @since 3.1.1 - PlutusType - , -- | @since 3.1.1 - PIsData - , -- | @since 3.1.1 - PEq - , -- | @since 3.1.1 - PShow - , -- | @since 3.1.1 - PTryFrom PData - ) - --- | @since 3.1.1 -instance DerivePlutusType PScriptPurpose where - type DPTStrat _ = PlutusTypeData - --- | @since 3.1.1 -instance PUnsafeLiftDecl PScriptPurpose where - type PLifted PScriptPurpose = Plutus.ScriptPurpose - --- | @since 3.1.1 -deriving via - (DerivePConstantViaData Plutus.ScriptPurpose PScriptPurpose) - instance - PConstantDecl Plutus.ScriptPurpose - --- | @since 3.1.1 -instance PTryFrom PData (PAsData PScriptPurpose) - -- | @since 3.1.1 newtype PTxInfo (s :: S) = PTxInfo @@ -216,7 +177,7 @@ newtype PTxInfo (s :: S) , "wdrl" ':= AssocMap.PMap 'AssocMap.Unsorted Credential.PStakingCredential PInteger -- Staking withdrawals , "validRange" ':= Interval.PInterval Time.PPosixTime , "signatories" ':= PBuiltinList (PAsData Crypto.PPubKeyHash) - , "redeemers" ':= AssocMap.PMap 'AssocMap.Unsorted PScriptPurpose Scripts.PRedeemer + , "redeemers" ':= AssocMap.PMap 'AssocMap.Unsorted Contexts.PScriptPurpose Scripts.PRedeemer , "data" ':= AssocMap.PMap 'AssocMap.Unsorted Scripts.PDatumHash Scripts.PDatum , "id" ':= Tx.PTxId -- hash of the pending transaction ] @@ -260,7 +221,7 @@ instance PTryFrom PData (PAsData PTxInfo) -- | @since 3.1.1 newtype PScriptContext (s :: S) - = PScriptContext (Term s (PDataRecord '["txInfo" ':= PTxInfo, "purpose" ':= PScriptPurpose])) + = PScriptContext (Term s (PDataRecord '["txInfo" ':= PTxInfo, "purpose" ':= Contexts.PScriptPurpose])) deriving stock ( -- | @since 3.1.1 Generic diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Contexts.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Contexts.hs new file mode 100644 index 000000000..250facf0a --- /dev/null +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Contexts.hs @@ -0,0 +1,59 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Plutarch.LedgerApi.V1.Contexts ( + PScriptPurpose (..), +) where + +import Plutarch.DataRepr ( + DerivePConstantViaData (DerivePConstantViaData), + ) +import Plutarch.LedgerApi.V1.Credential qualified as Credential +import Plutarch.LedgerApi.V1.DCert qualified as DCert +import Plutarch.LedgerApi.V1.Tx qualified as Tx +import Plutarch.LedgerApi.Value qualified as Value +import Plutarch.Lift ( + PConstantDecl, + PUnsafeLiftDecl (PLifted), + ) +import Plutarch.Prelude +import PlutusLedgerApi.V1 qualified as Plutus + +-- | @since 3.1.1 +data PScriptPurpose (s :: S) + = PMinting (Term s (PDataRecord '["_0" ':= Value.PCurrencySymbol])) + | PSpending (Term s (PDataRecord '["_0" ':= Tx.PTxOutRef])) + | PRewarding (Term s (PDataRecord '["_0" ':= Credential.PStakingCredential])) + | PCertifying (Term s (PDataRecord '["_0" ':= DCert.PDCert])) + deriving stock + ( -- | @since 3.1.1 + Generic + ) + deriving anyclass + ( -- | @since 3.1.1 + PlutusType + , -- | @since 3.1.1 + PIsData + , -- | @since 3.1.1 + PEq + , -- | @since 3.1.1 + PShow + , -- | @since 3.1.1 + PTryFrom PData + ) + +-- | @since 3.1.1 +instance DerivePlutusType PScriptPurpose where + type DPTStrat _ = PlutusTypeData + +-- | @since 3.1.1 +instance PUnsafeLiftDecl PScriptPurpose where + type PLifted PScriptPurpose = Plutus.ScriptPurpose + +-- | @since 3.1.1 +deriving via + (DerivePConstantViaData Plutus.ScriptPurpose PScriptPurpose) + instance + PConstantDecl Plutus.ScriptPurpose + +-- | @since 3.1.1 +instance PTryFrom PData (PAsData PScriptPurpose) diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Tx.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Tx.hs index 1a464f8ab..31f361664 100644 --- a/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Tx.hs +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V1/Tx.hs @@ -6,7 +6,6 @@ module Plutarch.LedgerApi.V1.Tx ( PTxOutRef (..), ) where -import Plutarch.Builtin (PDataNewtype (PDataNewtype)) import Plutarch.DataRepr ( DerivePConstantViaData (DerivePConstantViaData), PDataFields, @@ -25,7 +24,7 @@ import PlutusLedgerApi.V1 qualified as Plutus @since 3.1.0 -} -newtype PTxId (s :: S) = PTxId (Term s (PDataNewtype PByteString)) +newtype PTxId (s :: S) = PTxId (Term s (PDataRecord '["_0" ':= PByteString])) deriving stock ( -- | @since 2.0.0 Generic @@ -47,7 +46,7 @@ newtype PTxId (s :: S) = PTxId (Term s (PDataNewtype PByteString)) -- | @since 3.1.0 instance DerivePlutusType PTxId where - type DPTStrat _ = PlutusTypeNewtype + type DPTStrat _ = PlutusTypeData -- | @since 2.0.0 instance PUnsafeLiftDecl PTxId where @@ -69,7 +68,7 @@ instance PTryFrom PData PTxId where (plengthBS # unwrapped #== 32) (f ()) (ptraceInfoError "ptryFrom(PTxId): must be 32 bytes long") - pure (punsafeCoerce opq, pcon . PTxId . pcon . PDataNewtype . pdata $ unwrapped) + pure (punsafeCoerce opq, pcon . PTxId $ pdcons # pdata unwrapped # pdnil) -- | @since 3.1.0 instance PTryFrom PData (PAsData PTxId) where @@ -81,7 +80,7 @@ instance PTryFrom PData (PAsData PTxId) where (plengthBS # unwrapped #== 32) (f ()) (ptraceInfoError "ptryFrom(PTxId): must be 32 bytes long") - pure (punsafeCoerce opq, pcon . PTxId . pcon . PDataNewtype . pdata $ unwrapped) + pure (punsafeCoerce opq, pcon . PTxId $ pdcons # pdata unwrapped # pdnil) {- | Reference to a transaction output, with an index referencing which exact output we mean. diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V2.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V2.hs index 90d376596..bc285301b 100644 --- a/plutarch-ledger-api/src/Plutarch/LedgerApi/V2.hs +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V2.hs @@ -2,7 +2,7 @@ module Plutarch.LedgerApi.V2 ( -- * Contexts - V1.PScriptPurpose (..), + Contexts.PScriptPurpose (..), PScriptContext (..), -- * Certificates @@ -51,9 +51,14 @@ module Plutarch.LedgerApi.V2 ( AssocMap.Commutativity (..), ) where +import Plutarch.DataRepr ( + DerivePConstantViaData (DerivePConstantViaData), + PDataFields, + ) import Plutarch.LedgerApi.AssocMap qualified as AssocMap import Plutarch.LedgerApi.Interval qualified as Interval import Plutarch.LedgerApi.V1.Address qualified as Address +import Plutarch.LedgerApi.V1.Contexts qualified as Contexts import Plutarch.LedgerApi.V1.Credential qualified as Credential import Plutarch.LedgerApi.V1.Crypto qualified as Crypto import Plutarch.LedgerApi.V1.DCert qualified as DCert @@ -62,14 +67,6 @@ import Plutarch.LedgerApi.V1.Time qualified as Time import Plutarch.LedgerApi.V1.Tx qualified as V1Tx import Plutarch.LedgerApi.V2.Tx qualified as V2Tx import Plutarch.LedgerApi.Value qualified as Value - --- TODO: Cleaner factoring - -import Plutarch.DataRepr ( - DerivePConstantViaData (DerivePConstantViaData), - PDataFields, - ) -import Plutarch.LedgerApi.V1 qualified as V1 import Plutarch.Lift ( PConstantDecl, PUnsafeLiftDecl (PLifted), @@ -139,7 +136,7 @@ newtype PTxInfo (s :: S) , "wdrl" ':= AssocMap.PMap 'AssocMap.Unsorted Credential.PStakingCredential PInteger -- Staking withdrawals , "validRange" ':= Interval.PInterval Time.PPosixTime , "signatories" ':= PBuiltinList (PAsData Crypto.PPubKeyHash) - , "redeemers" ':= AssocMap.PMap 'AssocMap.Unsorted V1.PScriptPurpose Scripts.PRedeemer + , "redeemers" ':= AssocMap.PMap 'AssocMap.Unsorted Contexts.PScriptPurpose Scripts.PRedeemer , "data" ':= AssocMap.PMap 'AssocMap.Unsorted Scripts.PDatumHash Scripts.PDatum , "id" ':= V1Tx.PTxId -- hash of the pending transaction ] @@ -183,7 +180,7 @@ instance PTryFrom PData (PAsData PTxInfo) -- | @since 3.1.1 newtype PScriptContext (s :: S) - = PScriptContext (Term s (PDataRecord '["txInfo" ':= PTxInfo, "purpose" ':= V1.PScriptPurpose])) + = PScriptContext (Term s (PDataRecord '["txInfo" ':= PTxInfo, "purpose" ':= Contexts.PScriptPurpose])) deriving stock ( -- | @since 3.1.1 Generic diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V3.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V3.hs index a2b5f417d..282d81873 100644 --- a/plutarch-ledger-api/src/Plutarch/LedgerApi/V3.hs +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V3.hs @@ -20,9 +20,9 @@ module Plutarch.LedgerApi.V3 ( -- * Tx -- ** Types - V1Tx.PTxOutRef (..), + V3Tx.PTxOutRef (..), V2Tx.PTxOut (..), - V1Tx.PTxId (..), + V3Tx.PTxId (..), Contexts.PTxInInfo (..), V2Tx.POutputDatum (..), @@ -133,9 +133,9 @@ import Plutarch.LedgerApi.V1.Credential qualified as Credential import Plutarch.LedgerApi.V1.Crypto qualified as Crypto import Plutarch.LedgerApi.V1.Scripts qualified as Scripts import Plutarch.LedgerApi.V1.Time qualified as Time -import Plutarch.LedgerApi.V1.Tx qualified as V1Tx import Plutarch.LedgerApi.V2.Tx qualified as V2Tx import Plutarch.LedgerApi.V3.Contexts qualified as Contexts +import Plutarch.LedgerApi.V3.Tx qualified as V3Tx import Plutarch.LedgerApi.Value qualified as Value import Plutarch.Prelude import Plutarch.Script (Script (unScript)) @@ -213,7 +213,7 @@ pgetContinuingOutputs :: s ( PBuiltinList Contexts.PTxInInfo :--> PBuiltinList V2Tx.PTxOut - :--> V1Tx.PTxOutRef + :--> V3Tx.PTxOutRef :--> PBuiltinList V2Tx.PTxOut ) pgetContinuingOutputs = phoistAcyclic $ @@ -257,7 +257,7 @@ pfindOwnInput :: Term s ( PBuiltinList Contexts.PTxInInfo - :--> V1Tx.PTxOutRef + :--> V3Tx.PTxOutRef :--> PMaybe Contexts.PTxInInfo ) pfindOwnInput = phoistAcyclic $ @@ -266,7 +266,7 @@ pfindOwnInput = phoistAcyclic $ where matches :: forall (s' :: S). - Term s' (V1Tx.PTxOutRef :--> Contexts.PTxInInfo :--> PBool) + Term s' (V3Tx.PTxOutRef :--> Contexts.PTxInInfo :--> PBool) matches = phoistAcyclic $ plam $ \outref txininfo -> outref #== pfield @"outRef" # txininfo diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Contexts.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Contexts.hs index 7db23ef34..84da50f25 100644 --- a/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Contexts.hs +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Contexts.hs @@ -45,7 +45,7 @@ import Plutarch.DataRepr ( import Plutarch.LedgerApi.AssocMap qualified as AssocMap import Plutarch.LedgerApi.Interval qualified as Interval import Plutarch.LedgerApi.Utils (PMaybeData, PRationalData) -import Plutarch.LedgerApi.V1.Credential (PCredential, PStakingCredential) +import Plutarch.LedgerApi.V1.Credential (PCredential) import Plutarch.LedgerApi.V1.Crypto (PPubKeyHash) import Plutarch.LedgerApi.V1.Scripts ( PDatum, @@ -54,8 +54,8 @@ import Plutarch.LedgerApi.V1.Scripts ( PScriptHash, ) import Plutarch.LedgerApi.V1.Time (PPosixTime) -import Plutarch.LedgerApi.V1.Tx (PTxId, PTxOutRef) import Plutarch.LedgerApi.V2.Tx (PTxOut) +import Plutarch.LedgerApi.V3.Tx (PTxId, PTxOutRef) import Plutarch.LedgerApi.Value qualified as Value import Plutarch.Lift ( DerivePConstantViaBuiltin (DerivePConstantViaBuiltin), @@ -254,7 +254,7 @@ instance PTryFrom PData (PAsData PDelegatee) -- | @since 3.1.0 data PTxCert (s :: S) - = PTxCertRegStaking (Term s (PDataRecord '["_0" ':= PCredential, "_1" ':= PMaybeData Value.PCurrencySymbol])) + = PTxCertRegStaking (Term s (PDataRecord '["_0" ':= PCredential, "_1" ':= PMaybeData Value.PLovelace])) | PTxCertUnRegStaking (Term s (PDataRecord '["_0" ':= PCredential, "_1" ':= PMaybeData Value.PLovelace])) | PTxCertDelegStaking (Term s (PDataRecord '["_0" ':= PCredential, "_1" ':= PDelegatee])) | PTxCertRegDeleg (Term s (PDataRecord '["_0" ':= PCredential, "_1" ':= PDelegatee, "_2" ':= Value.PLovelace])) @@ -827,10 +827,10 @@ newtype PTxInfo (s :: S) '[ "inputs" ':= PBuiltinList (PAsData PTxInInfo) , "referenceInputs" ':= PBuiltinList (PAsData PTxInInfo) , "outputs" ':= PBuiltinList (PAsData PTxOut) - , "fee" ':= Value.PValue 'AssocMap.Sorted 'Value.Positive - , "mint" ':= Value.PValue 'AssocMap.Sorted 'Value.NonZero -- value minted by transaction + , "fee" ':= Value.PLovelace + , "mint" ':= Value.PValue 'AssocMap.Sorted 'Value.NoGuarantees -- value minted by transaction , "txCerts" ':= PBuiltinList (PAsData PTxCert) - , "wdrl" ':= AssocMap.PMap 'AssocMap.Unsorted PStakingCredential PInteger -- Staking withdrawals + , "wdrl" ':= AssocMap.PMap 'AssocMap.Unsorted PCredential Value.PLovelace -- Staking withdrawals , "validRange" ':= Interval.PInterval PPosixTime , "signatories" ':= PBuiltinList (PAsData PPubKeyHash) , "redeemers" ':= AssocMap.PMap 'AssocMap.Unsorted PScriptPurpose PRedeemer diff --git a/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Tx.hs b/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Tx.hs new file mode 100644 index 000000000..8180c5490 --- /dev/null +++ b/plutarch-ledger-api/src/Plutarch/LedgerApi/V3/Tx.hs @@ -0,0 +1,134 @@ +{-# OPTIONS_GHC -Wno-orphans #-} + +module Plutarch.LedgerApi.V3.Tx ( + PTxId (..), + PTxOutRef (..), +) where + +import Plutarch.Builtin (PDataNewtype (PDataNewtype)) +import Plutarch.DataRepr ( + DerivePConstantViaData (DerivePConstantViaData), + PDataFields, + ) +import Plutarch.LedgerApi.Utils (Mret) +import Plutarch.Lift ( + PConstantDecl, + PUnsafeLiftDecl (PLifted), + ) +import Plutarch.Prelude +import Plutarch.TryFrom (PTryFrom (PTryFromExcess, ptryFrom')) +import Plutarch.Unsafe (punsafeCoerce) +import PlutusLedgerApi.V3 qualified as Plutus + +{- | Hashed with @BLAKE2b-256@. + +@since 3.1.0 +-} +newtype PTxId (s :: S) = PTxId (Term s (PDataNewtype PByteString)) + deriving stock + ( -- | @since 2.0.0 + Generic + ) + deriving anyclass + ( -- | @since 2.0.0 + PlutusType + , -- | @since 2.0.0 + PIsData + , -- | @since 2.0.0 + PEq + , -- | @since 2.0.0 + PPartialOrd + , -- | @since 2.0.0 + POrd + , -- | @since 2.0.0 + PShow + ) + +-- | @since 3.1.0 +instance DerivePlutusType PTxId where + type DPTStrat _ = PlutusTypeNewtype + +-- | @since 2.0.0 +instance PUnsafeLiftDecl PTxId where + type PLifted PTxId = Plutus.TxId + +-- | @since 3.1.0 +deriving via + (DerivePConstantViaData Plutus.TxId PTxId) + instance + PConstantDecl Plutus.TxId + +-- | @since 3.1.0 +instance PTryFrom PData PTxId where + type PTryFromExcess PData PTxId = Mret PTxId + ptryFrom' opq = runTermCont $ do + unwrapped <- tcont . plet $ ptryFrom @(PAsData PByteString) opq snd + tcont $ \f -> + pif + (plengthBS # unwrapped #== 32) + (f ()) + (ptraceInfoError "ptryFrom(PTxId): must be 32 bytes long") + pure (punsafeCoerce opq, pcon . PTxId . pcon . PDataNewtype . pdata $ unwrapped) + +-- | @since 3.1.0 +instance PTryFrom PData (PAsData PTxId) where + type PTryFromExcess PData (PAsData PTxId) = Mret PTxId + ptryFrom' opq = runTermCont $ do + unwrapped <- tcont . plet $ ptryFrom @(PAsData PByteString) opq snd + tcont $ \f -> + pif + (plengthBS # unwrapped #== 32) + (f ()) + (ptraceInfoError "ptryFrom(PTxId): must be 32 bytes long") + pure (punsafeCoerce opq, pcon . PTxId . pcon . PDataNewtype . pdata $ unwrapped) + +-- | @since 3.1.0 +newtype PTxOutRef (s :: S) + = PTxOutRef + ( Term + s + ( PDataRecord + '[ "id" ':= PTxId + , "idx" ':= PInteger + ] + ) + ) + deriving stock + ( -- | @since 3.1.0 + Generic + ) + deriving anyclass + ( -- | @since 3.1.0 + PlutusType + , -- | @since 3.1.0 + PIsData + , -- | @since 3.1.0 + PDataFields + , -- | @since 3.1.0 + PEq + , -- | @since 3.1.0 + PPartialOrd + , -- | @since 3.1.0 + POrd + , -- | @since 3.1.0 + PTryFrom PData + , -- | @since 3.1.0 + PShow + ) + +-- | @since 3.1.0 +instance DerivePlutusType PTxOutRef where + type DPTStrat _ = PlutusTypeData + +-- | @since 3.1.0 +instance PUnsafeLiftDecl PTxOutRef where + type PLifted PTxOutRef = Plutus.TxOutRef + +-- | @since 3.1.0 +deriving via + (DerivePConstantViaData Plutus.TxOutRef PTxOutRef) + instance + PConstantDecl Plutus.TxOutRef + +-- | @since 3.1.0 +instance PTryFrom PData (PAsData PTxOutRef) diff --git a/plutarch-ledger-api/test/Main.hs b/plutarch-ledger-api/test/Main.hs index 5fe174415..9481fc006 100644 --- a/plutarch-ledger-api/test/Main.hs +++ b/plutarch-ledger-api/test/Main.hs @@ -12,17 +12,23 @@ import Plutarch.LedgerApi.V3 qualified as PlutarchV3 import Plutarch.Lift (PUnsafeLiftDecl (PLifted)) import Plutarch.Prelude ( PAsData, + PData, PIsData, + PTryFrom, S, pconstant, pdata, pfromData, plift, + ptryFrom, ) +import PlutusLedgerApi.V1 qualified as PlutusLA import PlutusLedgerApi.V1.Orphans () import PlutusLedgerApi.V2.Orphans () import PlutusLedgerApi.V3 qualified as PlutusV3 import PlutusLedgerApi.V3.Orphans () +import Prettyprinter (Pretty (pretty), layoutCompact) +import Prettyprinter.Render.String (renderString) import Test.QuickCheck ( Arbitrary (arbitrary, shrink), forAllShrinkShow, @@ -119,6 +125,29 @@ main = do , pIsDataLaws @PlutarchV2.POutputDatum , adjustOption fewerTests $ pIsDataLaws @PlutarchV2.PScriptContext ] + , adjustOption slightlyFewerTests $ + testGroup + "PTryFrom" + [ ptryFromLaws @PlutarchV2.PAddress + , ptryFromLaws @PlutarchV2.PCredential + , ptryFromLaws @PlutarchV2.PStakingCredential + , ptryFromLaws @PlutarchV2.PPubKeyHash + , ptryFromLaws @PlutarchV2.PPosixTime + , ptryFromLaws @(PlutarchV2.PExtended PlutarchV2.PPosixTime) + , ptryFromLaws @(PlutarchV2.PLowerBound PlutarchV2.PPosixTime) + , ptryFromLaws @(PlutarchV2.PUpperBound PlutarchV2.PPosixTime) + , ptryFromLaws @(PlutarchV2.PInterval PlutarchV2.PPosixTime) + , ptryFromLaws @PlutarchV2.PScriptHash + , ptryFromLaws @PlutarchV2.PDatum + , ptryFromLaws @PlutarchV2.PRedeemer + , ptryFromLaws @PlutarchV2.PDatumHash + , ptryFromLaws @PlutarchV2.PRedeemerHash + , ptryFromLaws @PlutarchV2.PCurrencySymbol + , ptryFromLaws @PlutarchV2.PTokenName + , ptryFromLaws @PlutarchV2.PLovelace + -- , ptryFromLaws @(PlutarchLA.PValue PlutarchLA.Unsorted PlutarchLA.NonZero) + -- Need PAsData handler because Plutarch is special + ] ] , testGroup "V3" @@ -176,6 +205,34 @@ main = do , pIsDataLaws @PlutarchV3.PTxOut , pIsDataLaws @PlutarchV3.POutputDatum ] + , adjustOption slightlyFewerTests $ + testGroup + "PTryFrom" + [ ptryFromLaws @PlutarchV3.PColdCommitteeCredential + , ptryFromLaws @PlutarchV3.PHotCommitteeCredential + , ptryFromLaws @PlutarchV3.PDRepCredential + , ptryFromLaws @PlutarchV3.PDRep + , ptryFromLaws @PlutarchV3.PDelegatee + , ptryFromLaws @PlutarchV3.PTxCert + , ptryFromLaws @PlutarchV3.PVoter + , ptryFromLaws @PlutarchV3.PVote + , ptryFromLaws @PlutarchV3.PGovernanceActionId + , ptryFromLaws @PlutarchV3.PCommittee + , ptryFromLaws @PlutarchV3.PConstitution + , ptryFromLaws @PlutarchV3.PProtocolVersion + , ptryFromLaws @PlutarchV3.PChangedParameters + , ptryFromLaws @PlutarchV3.PGovernanceAction + , ptryFromLaws @PlutarchV3.PProposalProcedure + , ptryFromLaws @PlutarchV3.PScriptPurpose + , ptryFromLaws @PlutarchV3.PScriptInfo + , ptryFromLaws @PlutarchV3.PTxInInfo + , adjustOption fewerTests $ ptryFromLaws @PlutarchV3.PTxInfo + , adjustOption fewerTests $ ptryFromLaws @PlutarchV3.PScriptContext + , ptryFromLaws @PlutarchV3.PTxId + , ptryFromLaws @PlutarchV3.PTxOutRef + , ptryFromLaws @PlutarchV3.PTxOut + , ptryFromLaws @PlutarchV3.POutputDatum + ] ] ] where @@ -186,6 +243,9 @@ main = do -- TODO: Fix those. fewerTests :: QuickCheckTests -> QuickCheckTests fewerTests = const 250 + -- PTryFrom tests run slow too + slightlyFewerTests :: QuickCheckTests -> QuickCheckTests + slightlyFewerTests = (`quot` 2) -- Properties @@ -250,3 +310,30 @@ pIsDataLaws = plift (pfromData . punsafeCoerce @_ @_ @(PAsData a) . pconstant . PlutusV3.toData $ x) === x coerceName :: String coerceName = "plift . pfromData . punsafeCoerce @(PAsData " <> groupName <> ") . pconstant . toData = id" + +ptryFromLaws :: + forall (a :: S -> Type). + ( Arbitrary (PLifted a) + , Show (PLifted a) + , PUnsafeLiftDecl a + , Eq (PLifted a) + , Typeable a + , PTryFrom PData a + , PlutusLA.ToData (PLifted a) + , Pretty (PLifted a) + ) => + TestTree +ptryFromLaws = testGroup groupName [pDataAgreementProp] + where + groupName :: String + groupName = tyConName . typeRepTyCon $ typeRep @a + pDataAgreementProp :: TestTree + pDataAgreementProp = testProperty "can parse toData of original" + . forAllShrinkShow arbitrary shrink prettyShow + $ \(x :: PLifted a) -> + plift (ptryFrom @a (pconstant . PlutusLA.toData $ x) fst) === x + +-- Helpers + +prettyShow :: forall (a :: Type). Pretty a => a -> String +prettyShow = renderString . layoutCompact . pretty diff --git a/plutarch-orphanage/CHANGELOG.md b/plutarch-orphanage/CHANGELOG.md index c48e3b367..1911616ee 100644 --- a/plutarch-orphanage/CHANGELOG.md +++ b/plutarch-orphanage/CHANGELOG.md @@ -8,7 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added -* Orphan instances for V1 ledger types +* Orphan instances for V1 and V2 ledger types + +### Changed + +* `UTxOValue` now ensures sortedness ## 1.0.1 -- 24-06-2024 diff --git a/plutarch-orphanage/src/PlutusLedgerApi/V1/Orphans/Value.hs b/plutarch-orphanage/src/PlutusLedgerApi/V1/Orphans/Value.hs index fc7021d19..0ded7ed36 100644 --- a/plutarch-orphanage/src/PlutusLedgerApi/V1/Orphans/Value.hs +++ b/plutarch-orphanage/src/PlutusLedgerApi/V1/Orphans/Value.hs @@ -1,12 +1,13 @@ {-# OPTIONS_GHC -Wno-orphans #-} module PlutusLedgerApi.V1.Orphans.Value ( + -- * Specialized Value wrappers + FeeValue (..), + getFeeValue, UTxOValue (..), getUtxoValue, NonAdaValue (..), getNonAdaValue, - FeeValue (..), - getFeeValue, ) where import Control.Monad (guard) @@ -34,6 +35,7 @@ import Test.QuickCheck ( functionMap, getNonEmpty, getNonNegative, + getPositive, resize, scale, sized, @@ -79,7 +81,12 @@ instance Function PLA.CurrencySymbol where {-# INLINEABLE function #-} function = functionMap coerce PLA.CurrencySymbol -{- | A Value with positive Ada, suitable for 'PLA.TxOut'. +{- | A 'PLA.Value' suitable for 'PLA.TxOut'. Specifically: + +* The `PLA.Value` is sorted by both keys (meaning 'PLA.CurrencySymbol' and + 'PLA.TokenName'); +* There exists an Ada amount; and +* All amounts are positive. = Note @@ -89,7 +96,7 @@ be /very/ certain that the Value being wrapped satisfies the invariants described above: failing to do so means all guarantees of this type are off the table. -@since 1.0.0 +@since 1.0.2 -} newtype UTxOValue = UTxOValue PLA.Value deriving @@ -102,21 +109,49 @@ newtype UTxOValue = UTxOValue PLA.Value Show ) --- | @since 1.0.0 +-- | @since 1.0.2 instance Arbitrary UTxOValue where {-# INLINEABLE arbitrary #-} - -- Generate a NonAdaValue, then force a positive Ada value into it. arbitrary = UTxOValue <$> do - NonAdaValue v <- arbitrary Positive adaQuantity <- arbitrary - pure $ v <> Value.singleton "" "" adaQuantity + -- Set of non-Ada currency symbols + csSet <- Set.fromList <$> liftArbitrary (PLA.CurrencySymbol . getBlake2b244Hash <$> arbitrary) + let cses = Set.toList csSet + -- For each key, generate a set of token names that aren't Ada, and a + -- positive value + table <- traverse (scale (`quot` 8) . mkInner) cses + -- Jam the Ada value in there + let table' = (Value.adaSymbol, [(Value.adaToken, adaQuantity)]) : table + pure . Value.Value . AssocMap.unsafeFromList . fmap (fmap AssocMap.unsafeFromList) $ table' + where + mkInner :: PLA.CurrencySymbol -> Gen (PLA.CurrencySymbol, [(PLA.TokenName, Integer)]) + mkInner cs = + (cs,) <$> do + -- Set of non-Ada token names + tnSet <- Set.fromList <$> liftArbitrary genNonAdaTokenName + let asList = Set.toList tnSet + traverse (\tn -> (tn,) . getPositive <$> arbitrary) asList + genNonAdaTokenName :: Gen PLA.TokenName + genNonAdaTokenName = + PLA.TokenName . PlutusTx.toBuiltin @ByteString . BS.pack <$> do + len <- chooseInt (1, 32) + -- ASCII printable range + vectorOf len . chooseBoundedIntegral $ (33, 126) {-# INLINEABLE shrink #-} - shrink (UTxOValue v) = - UTxOValue <$> do - v' <- shrink v - guard (Value.valueOf v' "" "" > 0) - pure v' + shrink (UTxOValue (Value.Value v)) = + UTxOValue . Value.Value <$> do + -- To ensure we don't break anything, we shrink in only two ways: + -- + -- 1. Dropping keys (outer or inner) + -- 2. Shrinking amounts + -- + -- To make this a bit easier on ourselves, we first 'unpack' the Value + -- completely, shrink the resulting (nested) list, then 'repack'. As neither + -- of these changes affect order or uniqueness, we're safe. + let asList = fmap AssocMap.toList <$> AssocMap.toList v + shrunk <- liftShrink (\(cs, inner) -> (cs,) <$> liftShrink (\(tn, amount) -> (tn,) . getPositive <$> shrink (Positive amount)) inner) asList + pure . AssocMap.unsafeFromList . fmap (fmap AssocMap.unsafeFromList) $ shrunk -- | @since 1.0.0 deriving via PLA.Value instance CoArbitrary UTxOValue diff --git a/plutarch-orphanage/src/PlutusLedgerApi/V2/Orphans.hs b/plutarch-orphanage/src/PlutusLedgerApi/V2/Orphans.hs index 7e38ed10f..8f81076cd 100644 --- a/plutarch-orphanage/src/PlutusLedgerApi/V2/Orphans.hs +++ b/plutarch-orphanage/src/PlutusLedgerApi/V2/Orphans.hs @@ -9,13 +9,12 @@ -- | QuickCheck orphans (plus a few helpers) for V2 Plutus ledger API types. module PlutusLedgerApi.V2.Orphans ( - -- * Specialized Value wrappers - Value.FeeValue (..), - Value.getFeeValue, Value.NonAdaValue (..), Value.getNonAdaValue, Value.UTxOValue (..), Value.getUtxoValue, + Value.FeeValue (..), + Value.getFeeValue, ) where import Data.ByteString (ByteString) diff --git a/plutarch-orphanage/src/PlutusLedgerApi/V3/Orphans.hs b/plutarch-orphanage/src/PlutusLedgerApi/V3/Orphans.hs index 3f68a3049..554088441 100644 --- a/plutarch-orphanage/src/PlutusLedgerApi/V3/Orphans.hs +++ b/plutarch-orphanage/src/PlutusLedgerApi/V3/Orphans.hs @@ -12,6 +12,7 @@ import PlutusLedgerApi.V1.Orphans.Credential () import PlutusLedgerApi.V1.Orphans.Interval () import PlutusLedgerApi.V1.Orphans.Time () import PlutusLedgerApi.V1.Orphans.Value () +import PlutusLedgerApi.V1.Orphans.Value qualified as Value import PlutusLedgerApi.V2.Orphans.Tx () import PlutusLedgerApi.V3 qualified as PLA import PlutusTx.AssocMap qualified as AssocMap @@ -23,11 +24,13 @@ import Test.QuickCheck ( Arbitrary1 (liftArbitrary, liftShrink), CoArbitrary (coarbitrary), Function (function), + NonEmptyList (NonEmpty), NonNegative (NonNegative), Positive (Positive), chooseInt, elements, functionMap, + getNonEmpty, getNonNegative, getPositive, oneof, @@ -856,15 +859,15 @@ instance Arbitrary PLA.TxInInfo where instance Arbitrary PLA.TxInfo where {-# INLINEABLE arbitrary #-} arbitrary = do - ins <- arbitrary + ins <- getNonEmpty <$> arbitrary routs <- arbitrary - outs <- arbitrary + outs <- getNonEmpty <$> arbitrary fee <- arbitrary - mint <- arbitrary + mint <- Value.getNonAdaValue <$> arbitrary cert <- arbitrary wdrl <- arbitrary valid <- arbitrary - sigs <- arbitrary + sigs <- Set.toList <$> arbitrary reds <- arbitrary dats <- arbitrary tid <- arbitrary @@ -875,11 +878,11 @@ instance Arbitrary PLA.TxInfo where pure . PLA.TxInfo ins routs outs fee mint cert wdrl valid sigs reds dats tid votes pps currT $ tDonation {-# INLINEABLE shrink #-} shrink (PLA.TxInfo ins routs outs fee mint cert wdrl valid sigs reds dats tid votes pps currT tDonation) = do - ins' <- shrink ins + NonEmpty ins' <- shrink (NonEmpty ins) routs' <- shrink routs - outs' <- shrink outs + NonEmpty outs' <- shrink (NonEmpty outs) fee' <- shrink fee - mint' <- shrink mint + (Value.NonAdaValue mint') <- shrink (Value.NonAdaValue mint) cert' <- shrink cert wdrl' <- shrink wdrl valid' <- shrink valid @@ -894,7 +897,6 @@ instance Arbitrary PLA.TxInfo where pure . PLA.TxInfo ins' routs' outs' fee' mint' cert' wdrl' valid' sigs' reds' dats' tid' votes' pps' currT' $ tDonation' -- TODO: CoArbitrary, Function --- TODO: Invariants? -- | @since 1.0.1 instance Arbitrary PLA.ScriptContext where