Skip to content

Commit 66b2402

Browse files
authored
Add Data-backed MintValue (#6931)
1 parent a4c776d commit 66b2402

File tree

22 files changed

+523
-96
lines changed

22 files changed

+523
-96
lines changed

plutus-benchmark/script-contexts/src/PlutusBenchmark/V3/Data/ScriptContexts.hs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ module PlutusBenchmark.V3.Data.ScriptContexts where
99

1010
import PlutusLedgerApi.Data.V1 qualified as PlutusTx
1111
import PlutusLedgerApi.Data.V3 (PubKeyHash (..), Redeemer (..), ScriptContext, TxId (..), TxInfo,
12-
TxOut, always, pattern NoOutputDatum, pattern ScriptContext,
13-
pattern SpendingScript, pattern TxInfo, pattern TxOut,
14-
pattern TxOutRef, txInfoCurrentTreasuryAmount, txInfoData,
15-
txInfoFee, txInfoId, txInfoInputs, txInfoMint, txInfoOutputs,
16-
txInfoProposalProcedures, txInfoRedeemers, txInfoReferenceInputs,
17-
txInfoSignatories, txInfoTreasuryDonation, txInfoTxCerts,
18-
txInfoValidRange, txInfoVotes, txInfoWdrl, txOutAddress, txOutDatum,
19-
txOutReferenceScript, txOutValue)
12+
TxOut, always, emptyMintValue, pattern NoOutputDatum,
13+
pattern ScriptContext, pattern SpendingScript, pattern TxInfo,
14+
pattern TxOut, pattern TxOutRef, txInfoCurrentTreasuryAmount,
15+
txInfoData, txInfoFee, txInfoId, txInfoInputs, txInfoMint,
16+
txInfoOutputs, txInfoProposalProcedures, txInfoRedeemers,
17+
txInfoReferenceInputs, txInfoSignatories, txInfoTreasuryDonation,
18+
txInfoTxCerts, txInfoValidRange, txInfoVotes, txInfoWdrl,
19+
txOutAddress, txOutDatum, txOutReferenceScript, txOutValue)
2020
import PlutusLedgerApi.V1.Data.Address
2121
import PlutusLedgerApi.V1.Data.Value
2222
import PlutusTx qualified
@@ -43,7 +43,7 @@ mkTxInfo i =
4343
, txInfoReferenceInputs = mempty
4444
, txInfoOutputs = Data.List.map mkTxOut (Data.List.fromSOP ([1 .. i] :: [Integer]))
4545
, txInfoFee = 10000
46-
, txInfoMint = mempty
46+
, txInfoMint = emptyMintValue
4747
, txInfoTxCerts = mempty
4848
, txInfoWdrl = Map.empty
4949
, txInfoValidRange = always
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
### Added
2+
3+
- Added a Data-backed version of `MintValue` for Plutus V3.
4+
5+
### Changed
6+
7+
- The Data-backed V3 `ScriptContext` is updated to now use the Data-backed `MintValue`, similar to how the SOP `ScriptContext` uses the SOP `MintValue`.

plutus-ledger-api/plutus-ledger-api.cabal

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ library
9494
PlutusLedgerApi.V3
9595
PlutusLedgerApi.V3.Contexts
9696
PlutusLedgerApi.V3.Data.Contexts
97+
PlutusLedgerApi.V3.Data.MintValue
9798
PlutusLedgerApi.V3.Data.Tx
9899
PlutusLedgerApi.V3.EvaluationContext
99100
PlutusLedgerApi.V3.MintValue
@@ -140,6 +141,7 @@ library plutus-ledger-api-testlib
140141
PlutusLedgerApi.Test.V2.Data.EvaluationContext
141142
PlutusLedgerApi.Test.V2.EvaluationContext
142143
PlutusLedgerApi.Test.V3.Data.EvaluationContext
144+
PlutusLedgerApi.Test.V3.Data.MintValue
143145
PlutusLedgerApi.Test.V3.EvaluationContext
144146
PlutusLedgerApi.Test.V3.MintValue
145147

@@ -216,6 +218,7 @@ test-suite plutus-ledger-api-plugin-test
216218
other-modules:
217219
Spec.Budget
218220
Spec.Data.Budget
221+
Spec.Data.MintValue.V3
219222
Spec.Data.ScriptContext
220223
Spec.Data.Value
221224
Spec.MintValue.V3

plutus-ledger-api/src/PlutusLedgerApi/Data/V2.hs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,13 @@ module PlutusLedgerApi.Data.V2 (
158158

159159
-- *** Association maps
160160
Map,
161-
unsafeFromList,
161+
unsafeFromDataList,
162+
unsafeFromBuiltinList,
163+
unsafeFromSOPList,
164+
safeFromSOPList,
165+
toSOPList,
166+
toDataList,
167+
toBuiltinList,
162168

163169
-- *** Newtypes and hash types
164170
V1.ScriptHash (..),
@@ -192,7 +198,8 @@ import PlutusLedgerApi.V2.Data.Tx qualified as Tx
192198
import PlutusLedgerApi.V2.EvaluationContext qualified as EvaluationContext
193199
import PlutusLedgerApi.V2.ParamName qualified as ParamName
194200

195-
import PlutusTx.Data.AssocMap (Map, unsafeFromList)
201+
import PlutusTx.Data.AssocMap (Map, safeFromSOPList, toBuiltinList, toDataList, toSOPList,
202+
unsafeFromBuiltinList, unsafeFromDataList, unsafeFromSOPList)
196203

197204
{-| An alias to the Plutus ledger language this module exposes at runtime.
198205
MAYBE: Use CPP '__FILE__' + some TH to automate this.

plutus-ledger-api/src/PlutusLedgerApi/Data/V3.hs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ module PlutusLedgerApi.Data.V3 (
149149
V2.adaToken,
150150
V2.Lovelace (..),
151151

152+
-- *** Mint Value
153+
MintValue.MintValue,
154+
MintValue.emptyMintValue,
155+
MintValue.mintValueToMap,
156+
MintValue.mintValueMinted,
157+
MintValue.mintValueBurned,
158+
152159
-- *** Time
153160
V2.POSIXTime (..),
154161
V2.POSIXTimeRange,
@@ -227,7 +234,7 @@ module PlutusLedgerApi.Data.V3 (
227234

228235
-- *** Association maps
229236
V2.Map,
230-
V2.unsafeFromList,
237+
V2.unsafeFromSOPList,
231238

232239
-- *** Newtypes and hash types
233240
V2.ScriptHash (..),
@@ -257,6 +264,7 @@ module PlutusLedgerApi.Data.V3 (
257264
import PlutusLedgerApi.Common qualified as Common
258265
import PlutusLedgerApi.Data.V2 qualified as V2
259266
import PlutusLedgerApi.V3.Data.Contexts qualified as Contexts
267+
import PlutusLedgerApi.V3.Data.MintValue qualified as MintValue
260268
import PlutusLedgerApi.V3.Data.Tx qualified as Tx
261269
import PlutusLedgerApi.V3.EvaluationContext qualified as EvaluationContext
262270
import PlutusLedgerApi.V3.ParamName qualified as ParamName

plutus-ledger-api/src/PlutusLedgerApi/V1/Data/Value.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ import PlutusTx.Builtins.Internal (BuiltinList, BuiltinPair)
9090
import PlutusTx.Builtins.Internal qualified as BI
9191
import PlutusTx.Data.AssocMap (Map)
9292
import PlutusTx.Data.AssocMap qualified as Map
93+
import PlutusTx.Data.List (List)
9394
import PlutusTx.Lift (makeLift)
9495
import PlutusTx.Ord qualified as Ord
9596
import PlutusTx.Prelude as PlutusTx hiding (sort)
@@ -397,7 +398,7 @@ currencySymbolValueOf value cur = withCurrencySymbol cur value 0 \tokens ->
397398
{-# INLINEABLE currencySymbolValueOf #-}
398399

399400
-- | The list of 'CurrencySymbol's of a 'Value'.
400-
symbols :: Value -> BuiltinList BuiltinData
401+
symbols :: Value -> List CurrencySymbol
401402
symbols (Value mp) = Map.keys mp
402403
{-# INLINEABLE symbols #-}
403404

@@ -460,10 +461,10 @@ Note that the result isn't sorted, meaning @v1 == v2@ doesn't generally imply
460461
Also assumes that there are no duplicate keys in the 'Value' 'Map'.
461462
-}
462463
flattenValue :: Value -> [(CurrencySymbol, TokenName, Integer)]
463-
flattenValue v = goOuter [] (Map.toList $ getValue v)
464+
flattenValue v = goOuter [] (Map.toSOPList $ getValue v)
464465
where
465466
goOuter acc [] = acc
466-
goOuter acc ((cs, m) : tl) = goOuter (goInner cs acc (Map.toList m)) tl
467+
goOuter acc ((cs, m) : tl) = goOuter (goInner cs acc (Map.toSOPList m)) tl
467468

468469
goInner _ acc [] = acc
469470
goInner cs acc ((tn, a) : tl)

plutus-ledger-api/src/PlutusLedgerApi/V2/Data/Contexts.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ findDatum dsh TxInfo{txInfoData} = lookup dsh txInfoData
226226
hashes
227227
-}
228228
findDatumHash :: Datum -> TxInfo -> Maybe DatumHash
229-
findDatumHash ds TxInfo{txInfoData} = fst <$> find f (toList txInfoData)
229+
findDatumHash ds TxInfo{txInfoData} = fst <$> find f (toSOPList txInfoData)
230230
where
231231
f (_, ds') = ds' == ds
232232
{-# INLINEABLE findDatumHash #-}

plutus-ledger-api/src/PlutusLedgerApi/V3/Data/Contexts.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ import Prettyprinter (nest, vsep, (<+>))
135135
import Prettyprinter.Extras
136136

137137
import PlutusLedgerApi.Data.V2 qualified as V2
138+
import PlutusLedgerApi.V3.Data.MintValue qualified as V3
138139
import PlutusLedgerApi.V3.Data.Tx qualified as V3
139140
import PlutusTx qualified
140141
import PlutusTx.AsData qualified as PlutusTx
@@ -571,7 +572,7 @@ PlutusTx.asData
571572
, txInfoReferenceInputs :: List TxInInfo
572573
, txInfoOutputs :: List V2.TxOut
573574
, txInfoFee :: V2.Lovelace
574-
, txInfoMint :: V2.Value
575+
, txInfoMint :: V3.MintValue
575576
, -- \^ The 'Value' minted by this transaction.
576577
--
577578
-- /Invariant:/ This field does not contain Ada with zero quantity, unlike
@@ -640,7 +641,7 @@ hashes
640641
-}
641642
findDatumHash :: V2.Datum -> TxInfo -> Haskell.Maybe V2.DatumHash
642643
findDatumHash ds TxInfo{txInfoData} =
643-
PlutusTx.fst PlutusTx.<$> PlutusTx.find f (toList txInfoData)
644+
PlutusTx.fst PlutusTx.<$> PlutusTx.find f (toSOPList txInfoData)
644645
where
645646
f (_, ds') = ds' PlutusTx.== ds
646647

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE DeriveAnyClass #-}
3+
{-# LANGUAGE DeriveDataTypeable #-}
4+
{-# LANGUAGE DerivingVia #-}
5+
{-# LANGUAGE FlexibleInstances #-}
6+
{-# LANGUAGE TemplateHaskell #-}
7+
{-# LANGUAGE TypeApplications #-}
8+
{-# LANGUAGE TypeFamilies #-}
9+
{-# OPTIONS_GHC -fexpose-all-unfoldings #-}
10+
{-# OPTIONS_GHC -fno-full-laziness #-}
11+
{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-}
12+
{-# OPTIONS_GHC -fno-omit-interface-pragmas #-}
13+
{-# OPTIONS_GHC -fno-spec-constr #-}
14+
{-# OPTIONS_GHC -fno-specialise #-}
15+
{-# OPTIONS_GHC -fno-strictness #-}
16+
{-# OPTIONS_GHC -fno-unbox-small-strict-fields #-}
17+
{-# OPTIONS_GHC -fno-unbox-strict-fields #-}
18+
19+
module PlutusLedgerApi.V3.Data.MintValue
20+
( MintValue (..) -- Constructor is exported for testing
21+
, emptyMintValue
22+
, mintValueToMap
23+
, mintValueMinted
24+
, mintValueBurned
25+
)
26+
where
27+
28+
import PlutusTx.Prelude
29+
30+
import GHC.Generics (Generic)
31+
import PlutusLedgerApi.V1.Data.Value (CurrencySymbol, TokenName, Value (..))
32+
import PlutusTx (FromData (..), ToData (..), UnsafeFromData (..))
33+
import PlutusTx.Blueprint.Class (HasBlueprintSchema (..))
34+
import PlutusTx.Blueprint.Definition (HasBlueprintDefinition (..), definitionIdFromType,
35+
definitionRef)
36+
import PlutusTx.Blueprint.Schema (MapSchema (..), Schema (..))
37+
import PlutusTx.Blueprint.Schema.Annotation (emptySchemaInfo, title)
38+
import PlutusTx.Data.AssocMap (Map)
39+
import PlutusTx.Data.AssocMap qualified as Map
40+
import PlutusTx.Lift (makeLift)
41+
import Prelude qualified as Haskell
42+
import Prettyprinter (Pretty)
43+
import Prettyprinter.Extras (PrettyShow (PrettyShow))
44+
45+
{- Note [MintValue vs Value]
46+
47+
'MintValue' differs conceptually from 'Value' in how negative quantities are interpreted:
48+
49+
In 'MintValue', negative quantities are interpreted as assets being burned. For 'Value',
50+
negative quantities are either don't make sense (e.g. in a transaction output) or interpreted
51+
as a negative balance.
52+
53+
We want to distinguish these at the type level to avoid using 'MintValue' where 'Value' is assumed.
54+
Users should project 'MintValue' into 'Value' using 'mintValueMinted' or 'mintValueBurned'.
55+
-}
56+
57+
-- | A 'MintValue' represents assets that are minted and burned in a transaction.
58+
newtype MintValue = UnsafeMintValue (Map CurrencySymbol (Map TokenName Integer))
59+
deriving stock (Generic, Haskell.Show)
60+
deriving newtype (ToData, FromData, UnsafeFromData)
61+
deriving (Pretty) via (PrettyShow MintValue)
62+
63+
instance Haskell.Eq MintValue where
64+
l == r = mintValueMinted l == mintValueMinted r && mintValueBurned l == mintValueBurned r
65+
66+
instance HasBlueprintDefinition MintValue where
67+
type Unroll MintValue = '[MintValue, CurrencySymbol, TokenName, Integer]
68+
definitionId = definitionIdFromType @MintValue
69+
70+
instance HasBlueprintSchema MintValue referencedTypes where
71+
schema =
72+
SchemaMap
73+
emptySchemaInfo{title = Just "MintValue"}
74+
MkMapSchema
75+
{ keySchema = definitionRef @CurrencySymbol
76+
, valueSchema =
77+
SchemaMap
78+
emptySchemaInfo
79+
MkMapSchema
80+
{ keySchema = definitionRef @TokenName
81+
, valueSchema = definitionRef @Integer
82+
, minItems = Nothing
83+
, maxItems = Nothing
84+
}
85+
, minItems = Nothing
86+
, maxItems = Nothing
87+
}
88+
{-# INLINEABLE schema #-}
89+
90+
emptyMintValue :: MintValue
91+
emptyMintValue = UnsafeMintValue Map.empty
92+
{-# INLINEABLE emptyMintValue #-}
93+
94+
mintValueToMap :: MintValue -> Map CurrencySymbol (Map TokenName Integer)
95+
mintValueToMap (UnsafeMintValue m) = m
96+
{-# INLINEABLE mintValueToMap #-}
97+
98+
-- | Get the 'Value' minted by the 'MintValue'.
99+
mintValueMinted :: MintValue -> Value
100+
mintValueMinted (UnsafeMintValue values) = filterQuantities (\x -> [x | x > 0]) values
101+
{-# INLINEABLE mintValueMinted #-}
102+
103+
{- | Get the 'Value' burned by the 'MintValue'.
104+
All the negative quantities in the 'MintValue' become positive in the resulting 'Value'.
105+
-}
106+
mintValueBurned :: MintValue -> Value
107+
mintValueBurned (UnsafeMintValue values) = filterQuantities (\x -> [abs x | x < 0]) values
108+
{-# INLINEABLE mintValueBurned #-}
109+
110+
filterQuantities :: (Integer -> [Integer]) -> Map CurrencySymbol (Map TokenName Integer) -> Value
111+
filterQuantities mapQuantity values =
112+
Value (Map.unsafeFromSOPList (foldr filterTokenQuantities [] (Map.toSOPList values)))
113+
where
114+
{-# INLINEABLE filterTokenQuantities #-}
115+
filterTokenQuantities
116+
:: (CurrencySymbol, Map TokenName Integer)
117+
-> [(CurrencySymbol, Map TokenName Integer)]
118+
-> [(CurrencySymbol, Map TokenName Integer)]
119+
filterTokenQuantities (currency, tokenQuantities) =
120+
case concatMap (traverse mapQuantity) (Map.toSOPList tokenQuantities) of
121+
[] -> id
122+
quantities -> ((currency, Map.unsafeFromSOPList quantities) :)
123+
{-# INLINEABLE filterQuantities #-}
124+
125+
----------------------------------------------------------------------------------------------------
126+
-- TH Splices --------------------------------------------------------------------------------------
127+
128+
$(makeLift ''MintValue)
129+

plutus-ledger-api/test-plugin/Spec.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module Main where
22

33
import Spec.Budget qualified
44
import Spec.Data.Budget qualified
5+
import Spec.Data.MintValue.V3 qualified
56
import Spec.Data.ScriptContext qualified
67
import Spec.Data.Value qualified
78
import Spec.MintValue.V3 qualified
@@ -25,6 +26,7 @@ tests =
2526
, Spec.Data.Budget.tests
2627
, Spec.Data.ScriptContext.tests
2728
, Spec.Data.Value.test_EqValue
29+
, Spec.Data.MintValue.V3.tests
2830
, Spec.ReturnUnit.V1.tests
2931
, Spec.ReturnUnit.V2.tests
3032
, Spec.ReturnUnit.V3.tests

plutus-ledger-api/test-plugin/Spec/Data/Budget.hs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ compiledCurrencySymbolValueOf = $$(compile [||currencySymbolValueOf||])
5050

5151
mkValue :: [(Integer, [(Integer, Integer)])] -> Value
5252
mkValue =
53-
Value . Map.unsafeFromList . fmap (bimap toSymbol (Map.unsafeFromList . fmap (first toToken)))
53+
Value
54+
. Map.unsafeFromSOPList
55+
. fmap (bimap toSymbol (Map.unsafeFromSOPList . fmap (first toToken)))
5456

5557
toSymbol :: Integer -> CurrencySymbol
5658
toSymbol = currencySymbol . fromString . show

0 commit comments

Comments
 (0)