Skip to content

Commit

Permalink
Define MultiAsset type wrapping multiasset map and use it in Value
Browse files Browse the repository at this point in the history
  • Loading branch information
teodanciu committed Aug 8, 2022
1 parent d696f1d commit b9ace67
Show file tree
Hide file tree
Showing 20 changed files with 211 additions and 165 deletions.
4 changes: 2 additions & 2 deletions eras/alonzo/impl/src/Cardano/Ledger/Alonzo/Tx.hs
Expand Up @@ -123,7 +123,7 @@ import Cardano.Ledger.Coin (Coin (..))
import Cardano.Ledger.Core hiding (TxBody)
import qualified Cardano.Ledger.Core as Core
import qualified Cardano.Ledger.Crypto as CC
import Cardano.Ledger.Mary.Value (AssetName, MaryValue (..), PolicyID (..))
import Cardano.Ledger.Mary.Value (AssetName, MaryValue (..), MultiAsset (..), PolicyID (..))
import Cardano.Ledger.SafeHash
( HashAnnotated,
SafeToHash (..),
Expand Down Expand Up @@ -429,7 +429,7 @@ rdptrInv txBody (RdmrPtr Cert idx) =
Certifying <$> fromIndex idx (txBody ^. certsTxBodyL)

getMapFromValue :: MaryValue crypto -> Map.Map (PolicyID crypto) (Map.Map AssetName Integer)
getMapFromValue (MaryValue _ m) = m
getMapFromValue (MaryValue _ (MultiAsset m)) = m

-- | Find the Data and ExUnits assigned to a script.
indexedRdmrs ::
Expand Down
4 changes: 2 additions & 2 deletions eras/alonzo/impl/src/Cardano/Ledger/Alonzo/TxInfo.hs
Expand Up @@ -100,7 +100,7 @@ import Cardano.Ledger.Credential
)
import qualified Cardano.Ledger.Crypto as CC (Crypto)
import Cardano.Ledger.Keys (KeyHash (..), hashKey)
import Cardano.Ledger.Mary.Value (AssetName (..), MaryValue (..), PolicyID (..))
import Cardano.Ledger.Mary.Value (AssetName (..), MaryValue (..), MultiAsset (..), PolicyID (..))
import Cardano.Ledger.SafeHash (SafeHash, extractHash, hashAnnotated)
import Cardano.Ledger.Serialization (Sized (sizedValue))
import qualified Cardano.Ledger.Shelley.HardForks as HardForks
Expand Down Expand Up @@ -360,7 +360,7 @@ transAssetName :: AssetName -> PV1.TokenName
transAssetName (AssetName bs) = PV1.TokenName (PV1.toBuiltin (SBS.fromShort bs))

transValue :: MaryValue c -> PV1.Value
transValue (MaryValue n mp) = Map.foldlWithKey' accum1 justada mp
transValue (MaryValue n (MultiAsset mp)) = Map.foldlWithKey' accum1 justada mp
where
accum1 ans sym mp2 = Map.foldlWithKey' accum2 ans mp2
where
Expand Down
Expand Up @@ -31,7 +31,7 @@ import Cardano.Ledger.BaseTypes (StrictMaybe (..), boundRational)
import Cardano.Ledger.Block (Block (..))
import Cardano.Ledger.Coin (Coin (..))
import Cardano.Ledger.Era (EraSegWits (..))
import Cardano.Ledger.Mary.Value (MaryValue (..), valueFromList)
import Cardano.Ledger.Mary.Value (MaryValue (..), MultiAsset (..), valueFromList)
import Cardano.Protocol.TPraos.BHeader (BHeader)
import Codec.CBOR.Read (deserialiseFromBytes)
import qualified Data.ByteString.Base16 as B16
Expand Down Expand Up @@ -175,7 +175,7 @@ goldenUTxOEntryMinAda =
-- with the old parameter minUTxOValue.
-- If we wish to keep the ada-only, no datum hash, minimum value nearly the same,
-- we can divide minUTxOValue by 29 and round.
utxoEntrySize @(AlonzoEra StandardCrypto) (AlonzoTxOut aliceAddr (MaryValue 0 mempty) SNothing) @?= 29
utxoEntrySize @(AlonzoEra StandardCrypto) (AlonzoTxOut aliceAddr (MaryValue 0 (MultiAsset mempty)) SNothing) @?= 29
]

goldenSerialization :: TestTree
Expand Down
Expand Up @@ -26,7 +26,7 @@ import Cardano.Ledger.Coin (Coin (..))
import Cardano.Ledger.Core (EraScript (hashScript), TxBody)
import Cardano.Ledger.Crypto (StandardCrypto)
import Cardano.Ledger.Keys (asWitness)
import Cardano.Ledger.Mary.Value (MaryValue (..))
import Cardano.Ledger.Mary.Value (MaryValue (..), MultiAsset (..))
import Cardano.Ledger.SafeHash (hashAnnotated)
import Cardano.Ledger.Serialization (mkSized)
import Cardano.Ledger.Shelley.API
Expand Down Expand Up @@ -104,7 +104,7 @@ collateralOutput :: BabbageTxOut StandardBabbage
collateralOutput =
BabbageTxOut
(mkAddr (SLE.examplePayKey, SLE.exampleStakeKey))
(MaryValue 8675309 mempty)
(MaryValue 8675309 (MultiAsset mempty))
NoDatum
SNothing

Expand Down
97 changes: 54 additions & 43 deletions eras/shelley-ma/impl/src/Cardano/Ledger/Mary/Value.hs
Expand Up @@ -15,6 +15,7 @@ module Cardano.Ledger.Mary.Value
( PolicyID (..),
AssetName (..),
MaryValue (..),
MultiAsset (..),
Value,
insert,
lookup,
Expand Down Expand Up @@ -126,8 +127,14 @@ instance FromCBOR AssetName where
newtype PolicyID crypto = PolicyID {policyID :: ScriptHash crypto}
deriving (Show, Eq, ToCBOR, FromCBOR, Ord, NoThunks, NFData)

-- | The MultiAssets map
newtype MultiAsset crypto = MultiAsset (Map (PolicyID crypto) (Map AssetName Integer))
deriving (Show, Generic)

instance NoThunks (MultiAsset crypto)

-- | The Value representing MultiAssets
data MaryValue crypto = MaryValue !Integer !(Map (PolicyID crypto) (Map AssetName Integer))
data MaryValue crypto = MaryValue !Integer !(MultiAsset crypto)
deriving (Show, Generic)

type Value = MaryValue
Expand All @@ -138,47 +145,47 @@ instance CC.Crypto crypto => Eq (MaryValue crypto) where
x == y = pointwise (==) x y

instance NFData (MaryValue crypto) where
rnf (MaryValue c m) = c `deepseq` rnf m
rnf (MaryValue c (MultiAsset m)) = c `deepseq` rnf m

instance NoThunks (MaryValue crypto)

instance Semigroup (MaryValue crypto) where
MaryValue c m <> MaryValue c1 m1 =
MaryValue (c + c1) (canonicalMapUnion (canonicalMapUnion (+)) m m1)
MaryValue c (MultiAsset m) <> MaryValue c1 (MultiAsset m1) =
MaryValue (c + c1) (MultiAsset (canonicalMapUnion (canonicalMapUnion (+)) m m1))

instance Monoid (MaryValue crypto) where
mempty = MaryValue 0 mempty
mempty = MaryValue 0 (MultiAsset mempty)

instance Group (MaryValue crypto) where
invert (MaryValue c m) =
invert (MaryValue c (MultiAsset m)) =
MaryValue
(-c)
(canonicalMap (canonicalMap ((-1 :: Integer) *)) m)
(MultiAsset (canonicalMap (canonicalMap ((-1 :: Integer) *)) m))

instance Abelian (MaryValue crypto)

-- ===================================================
-- Make the Val instance of MaryValue

instance CC.Crypto crypto => Val (MaryValue crypto) where
s <×> MaryValue c v =
s <×> MaryValue c (MultiAsset m) =
MaryValue
(fromIntegral s * c)
(canonicalMap (canonicalMap (fromIntegral s *)) v)
isZero (MaryValue c v) = c == 0 && Map.null v
(MultiAsset (canonicalMap (canonicalMap (fromIntegral s *)) m))
isZero (MaryValue c (MultiAsset m)) = c == 0 && Map.null m
coin (MaryValue c _) = Coin c
inject (Coin c) = MaryValue c mempty
inject (Coin c) = MaryValue c (MultiAsset mempty)
modifyCoin f (MaryValue c m) = MaryValue n m where (Coin n) = f (Coin c)
pointwise p (MaryValue c x) (MaryValue d y) = p c d && pointWise (pointWise p) x y
pointwise p (MaryValue c (MultiAsset x)) (MaryValue d (MultiAsset y)) = p c d && pointWise (pointWise p) x y

-- returns the size, in Word64's, of the CompactValue representation of MaryValue
size vv@(MaryValue _ v)
size vv@(MaryValue _ (MultiAsset m))
-- when MaryValue contains only ada
-- !WARNING! This branch is INCORRECT in the Mary era and should ONLY be
-- used in the Alonzo ERA.
-- TODO - find a better way to reconcile the mistakes in Mary with what needs
-- to be the case in Alonzo.
| v == mempty = 2
| m == mempty = 2
-- when MaryValue contains ada as well as other tokens
-- sums up :
-- i) adaWords : the space taken up by the ada amount
Expand All @@ -192,7 +199,7 @@ instance CC.Crypto crypto => Val (MaryValue crypto) where
+ repOverhead
)

isAdaOnly (MaryValue _ v) = Map.null v
isAdaOnly (MaryValue _ (MultiAsset m)) = Map.null m

isAdaOnlyCompact = \case
CompactValue (CompactValueAdaOnly _) -> True
Expand Down Expand Up @@ -264,16 +271,16 @@ decodeValuePair decodeAmount =

encodeMultiAssetMaps ::
CC.Crypto crypto =>
Map (PolicyID crypto) (Map AssetName Integer) ->
MultiAsset crypto ->
Encoding
encodeMultiAssetMaps = encodeMap toCBOR (encodeMap toCBOR toCBOR)
encodeMultiAssetMaps (MultiAsset m) = encodeMap toCBOR (encodeMap toCBOR toCBOR) m

decodeMultiAssetMaps ::
CC.Crypto crypto =>
Decoder s Integer ->
Decoder s (Map (PolicyID crypto) (Map AssetName Integer))
Decoder s (MultiAsset crypto)
decodeMultiAssetMaps decodeAmount =
prune <$> decodeMap fromCBOR (decodeMap fromCBOR decodeAmount)
MultiAsset . prune <$> decodeMap fromCBOR (decodeMap fromCBOR decodeAmount)

decodeNonNegativeInteger :: Decoder s Integer
decodeNonNegativeInteger = fromIntegral <$> decodeWord64
Expand All @@ -295,14 +302,14 @@ instance
CC.Crypto crypto =>
ToCBOR (MaryValue crypto)
where
toCBOR (MaryValue c v) =
if Map.null v
toCBOR (MaryValue c (MultiAsset m)) =
if Map.null m
then toCBOR c
else
encode $
Rec MaryValue
!> To c
!> E encodeMultiAssetMaps v
!> E encodeMultiAssetMaps (MultiAsset m)

instance
CC.Crypto crypto =>
Expand Down Expand Up @@ -509,8 +516,8 @@ to ::
-- The Nothing case of the return value corresponds to a quantity that is outside
-- the bounds of a Word64. x < 0 or x > (2^64 - 1)
Maybe (CompactValue crypto)
to (MaryValue ada ma)
| Map.null ma =
to (MaryValue ada (MultiAsset m))
| Map.null m =
CompactValueAdaOnly . CompactCoin <$> integerToWord64 ada
to v = do
c <- integerToWord64 ada
Expand Down Expand Up @@ -646,7 +653,7 @@ representationSize xs = abcRegionSize + pidBlockSize + anameBlockSize
Semigroup.getSum $ foldMap' (Semigroup.Sum . SBS.length . assetName) assetNames

from :: forall crypto. (CC.Crypto crypto) => CompactValue crypto -> MaryValue crypto
from (CompactValueAdaOnly (CompactCoin c)) = MaryValue (fromIntegral c) mempty
from (CompactValueAdaOnly (CompactCoin c)) = MaryValue (fromIntegral c) (MultiAsset mempty)
from (CompactValueMultiAsset (CompactCoin c) numAssets rep) =
valueFromList (fromIntegral c) triples
where
Expand Down Expand Up @@ -730,10 +737,10 @@ readShortByteString sbs start len =
-- This function is equivalent to computing the support of the value in the
-- spec.
policies :: MaryValue crypto -> Set (PolicyID crypto)
policies (MaryValue _ m) = Map.keysSet m
policies (MaryValue _ (MultiAsset m)) = Map.keysSet m

lookup :: PolicyID crypto -> AssetName -> MaryValue crypto -> Integer
lookup pid aid (MaryValue _ m) =
lookup pid aid (MaryValue _ (MultiAsset m)) =
case Map.lookup pid m of
Nothing -> 0
Just m2 -> Map.findWithDefault 0 aid m2
Expand All @@ -749,7 +756,7 @@ insert ::
Integer ->
MaryValue crypto ->
MaryValue crypto
insert combine pid aid new (MaryValue cn m1) =
insert combine pid aid new (MaryValue cn (MultiAsset m1)) =
case splitLookup pid m1 of
(l1, Just m2, l2) ->
case splitLookup aid m2 of
Expand All @@ -758,29 +765,33 @@ insert combine pid aid new (MaryValue cn m1) =
then
let m3 = link2 v1 v2
in if Map.null m3
then MaryValue cn (link2 l1 l2)
else MaryValue cn (link pid m3 l1 l2)
else MaryValue cn (link pid (link aid n v1 v2) l1 l2)
then MaryValue cn (MultiAsset (link2 l1 l2))
else MaryValue cn (MultiAsset (link pid m3 l1 l2))
else MaryValue cn (MultiAsset (link pid (link aid n v1 v2) l1 l2))
where
n = combine old new
(_, Nothing, _) ->
MaryValue
cn
( link
pid
( if new == 0
then m2
else Map.insert aid new m2
( MultiAsset
( link
pid
( if new == 0
then m2
else Map.insert aid new m2
)
l1
l2
)
l1
l2
)
(l1, Nothing, l2) ->
MaryValue
cn
( if new == 0
then link2 l1 l2
else link pid (Map.singleton aid new) l1 l2
( MultiAsset
( if new == 0
then link2 l1 l2
else link pid (Map.singleton aid new) l1 l2
)
)

-- ========================================================
Expand All @@ -798,7 +809,7 @@ valueFromList :: Integer -> [(PolicyID era, AssetName, Integer)] -> MaryValue er
valueFromList ada =
foldr
(\(p, n, i) ans -> insert (+) p n i ans)
(MaryValue ada Map.empty)
(MaryValue ada (MultiAsset Map.empty))

-- | Display a MaryValue as a String, one token per line
showValue :: MaryValue crypto -> String
Expand All @@ -815,7 +826,7 @@ showValue v = show c ++ "\n" ++ unlines (map trans ts)
-- | Turn the nested 'MaryValue' map-of-maps representation into a flat sequence
-- of policyID, asset name and quantity, plus separately the ada quantity.
gettriples' :: MaryValue crypto -> (Integer, [(PolicyID crypto, AssetName, Integer)], [PolicyID crypto])
gettriples' (MaryValue c m1) = (c, triples, bad)
gettriples' (MaryValue c (MultiAsset m1)) = (c, triples, bad)
where
triples =
[ (policyId, aname, amount)
Expand Down
Expand Up @@ -36,7 +36,7 @@ exampleMultiAssetValue ::
Int ->
MaryValue c
exampleMultiAssetValue x =
MaryValue 100 $ Map.singleton policyId $ Map.singleton couttsCoin 1000
MaryValue 100 $ (MultiAsset (Map.singleton policyId $ Map.singleton couttsCoin 1000))
where
policyId :: PolicyID c
policyId = PolicyID $ mkScriptHash x
Expand Down

0 comments on commit b9ace67

Please sign in to comment.