Skip to content

Commit

Permalink
Name change
Browse files Browse the repository at this point in the history
  • Loading branch information
Jimbo4350 committed Nov 23, 2020
1 parent a9abdf1 commit f9e9545
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 86 deletions.
8 changes: 4 additions & 4 deletions cardano-api/src/Cardano/Api/Typed.hs
Expand Up @@ -406,10 +406,10 @@ module Cardano.Api.Typed (
secondsToNominalDiffTime,

-- Testing purposes
AssetsBundle(..),
AssetIdBundle(..),
flatten,
unflatten
ValueNestedAsset(..),
ValueNestedRep(..),
toValueNestedRep,
fromValueNestedRep
) where

import Prelude
Expand Down
104 changes: 47 additions & 57 deletions cardano-api/src/Cardano/Api/Value.hs
Expand Up @@ -37,12 +37,12 @@ module Cardano.Api.Value
, toShelleyLovelace

-- * Intermediate Value representation
, AssetsBundle(..)
, AssetIdBundle(..)
, ValueNestedAsset(..)
, ValueNestedRep(..)

-- * Exported for testing purposes
, flatten
, unflatten
, fromValueNestedRep
, toValueNestedRep
) where

import Prelude
Expand All @@ -55,7 +55,6 @@ import qualified Data.HashMap.Strict as HashMap
import qualified Data.Map.Merge.Strict as Map
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import qualified Data.Scientific as Scientific
import Data.String (IsString)
import Data.Text (Text)
import qualified Data.Text as Text
Expand Down Expand Up @@ -92,7 +91,7 @@ toShelleyLovelace (Lovelace l) = Shelley.Coin l
--

newtype Quantity = Quantity Integer
deriving newtype (Eq, Ord, Num, Show)
deriving newtype (Eq, Ord, Num, Show, ToJSON, FromJSON)

instance Semigroup Quantity where
Quantity a <> Quantity b = Quantity (a + b)
Expand Down Expand Up @@ -132,63 +131,54 @@ instance Monoid Value where
mempty = Value Map.empty

instance ToJSON Value where
toJSON = toJSON . unflatten

unflatten :: Value -> AssetsBundle
unflatten v@(Value flatMap) =
-- unflatten all the non-ada assets, and add ada separately
AssetsBundle $
[ AdaAsset q | let q = selectAsset v AdaAssetId, q /= 0 ]
++ [ AssetIdBundle pId qs | (pId, qs) <- Map.toList nonAdaAssets ]
where
nonAdaAssets :: Map PolicyId (Map AssetName Quantity)
nonAdaAssets =
Map.fromListWith (Map.unionWith (<>))
[ (pId, Map.singleton aName q)
| (AssetId pId aName, q) <- Map.toList flatMap ]
toJSON = toJSON . toValueNestedRep

toValueNestedRep :: Value -> ValueNestedRep
toValueNestedRep v@(Value flatMap) =
-- toValueNestedRep all the non-ada assets, and add ada separately
ValueNestedRep $
[ AdaAsset q | let q = selectAsset v AdaAssetId, q /= 0 ]
++ [ ValueNestedAsset pId qs | (pId, qs) <- Map.toList nonAdaAssets ]
where
nonAdaAssets :: Map PolicyId (Map AssetName Quantity)
nonAdaAssets =
Map.fromListWith (Map.unionWith (<>))
[ (pId, Map.singleton aName q)
| (AssetId pId aName, q) <- Map.toList flatMap ]

instance FromJSON Value where
parseJSON jv = flatten <$> parseJSON jv

flatten :: AssetsBundle -> Value
flatten (AssetsBundle bundles) =
Value $
Map.fromList
[ (aId, q)
| bundle <- bundles
, (aId, q) <- case bundle of
AdaAsset q -> [ (AdaAssetId, q) ]
AssetIdBundle pId qs -> [ (AssetId pId aName, q)
| (aName, q) <- Map.toList qs ]
]
parseJSON jv = fromValueNestedRep <$> parseJSON jv

fromValueNestedRep :: ValueNestedRep -> Value
fromValueNestedRep (ValueNestedRep bundles) =
Value $
Map.fromList
[ (aId, q)
| bundle <- bundles
, (aId, q) <- case bundle of
AdaAsset q -> [ (AdaAssetId, q) ]
ValueNestedAsset pId qs -> [ (AssetId pId aName, q)
| (aName, q) <- Map.toList qs ]
]

-- | Intermediate representation used in the JSON parsing\/rendering of 'Value'
newtype AssetsBundle = AssetsBundle [AssetIdBundle]
deriving (Eq, Ord, Show)
newtype ValueNestedRep = ValueNestedRep [ValueNestedAsset]
deriving (Eq, Ord, Show)

data AssetIdBundle = AssetIdBundle PolicyId (Map AssetName Quantity)
| AdaAsset Quantity
deriving (Eq, Ord, Show)
data ValueNestedAsset
= ValueNestedAsset PolicyId (Map AssetName Quantity)
| AdaAsset Quantity
deriving (Eq, Ord, Show)

instance ToJSON AssetsBundle where
toJSON (AssetsBundle bundles) = object $ map toPair bundles
instance ToJSON ValueNestedRep where
toJSON (ValueNestedRep bundles) = object $ map toPair bundles
where
toPair :: AssetIdBundle -> (Text, Aeson.Value)
toPair :: ValueNestedAsset -> (Text, Aeson.Value)
toPair (AdaAsset q) = ("lovelace", toJSON q)
toPair (AssetIdBundle pid assets) = (renderPolicyId pid, toJSON assets)
toPair (ValueNestedAsset pid assets) = (renderPolicyId pid, toJSON assets)

renderPolicyId :: PolicyId -> Text
renderPolicyId (PolicyId sh) = Text.decodeUtf8 (serialiseToRawBytesHex sh)

instance ToJSON Quantity where
toJSON (Quantity q) = toJSON q

instance FromJSON Quantity where
parseJSON = withScientific "MultiAssetQuantity" $ \sci ->
case Scientific.floatingOrInteger sci :: Either Double Integer of
Left d -> fail $ "Expected an integer but got: " <> show d
Right n -> return $ Quantity n

instance ToJSON AssetName where
toJSON (AssetName an) = Aeson.String $ Text.decodeUtf8 an

Expand All @@ -204,20 +194,20 @@ instance FromJSONKey AssetName where
fromJSONKey = FromJSONKeyText
$ \assetName -> AssetName $ Text.encodeUtf8 assetName

instance FromJSON AssetsBundle where
parseJSON = withObject "AssetsBundle" $ \obj ->
AssetsBundle
instance FromJSON ValueNestedRep where
parseJSON = withObject "ValueNestedRep" $ \obj ->
ValueNestedRep
<$> sequenceA [ parsePid keyValTuple | keyValTuple <- HashMap.toList obj ]
where
parsePid :: (Text, Aeson.Value) -> Parser AssetIdBundle
parsePid :: (Text, Aeson.Value) -> Parser ValueNestedAsset
parsePid (pid,q) =
case pid of
"lovelace" ->
AdaAsset <$> parseJSON q
other ->
let pIdBS = Text.encodeUtf8 other
in case deserialiseFromRawBytesHex AsScriptHash pIdBS of
Just sHash -> AssetIdBundle (PolicyId sHash) <$> (parseJSON q)
Just sHash -> ValueNestedAsset (PolicyId sHash) <$> (parseJSON q)
Nothing -> fail $ "Failure when deserialising PolicyId: "
<> Text.unpack pid

Expand Down
30 changes: 16 additions & 14 deletions cardano-api/test/Test/Cardano/Api/Typed/Gen.hs
@@ -1,7 +1,7 @@
module Test.Cardano.Api.Typed.Gen
( genAddressByron
, genAddressShelley
, genAssetsBundle
, genValueNestedRep
, genAssetIdBundleValue
, genByronKeyWitness
, genRequiredSig
Expand Down Expand Up @@ -162,34 +162,36 @@ genAssetId :: Gen AssetId
genAssetId = Gen.choice [ AssetId <$> genPolicyId <*> genAssetName
, return AdaAssetId
]

genQuantity :: Gen Quantity
genQuantity =
fromInteger <$> Gen.integral_ (Range.exponential 0 (toInteger (maxBound :: Int64)))
fromInteger <$> Gen.integral_ (Range.exponential 1 (toInteger (maxBound :: Int64)))

genValue :: Gen Value
genValue =
valueFromList <$> Gen.list (Range.constant 0 10) ((,)
<$> genAssetId <*> genQuantity)

-- We do not generate duplicate keys as "AssetsBundle" is created via
-- We do not generate duplicate keys as "ValueNestedRep" is created via
-- flattening a 'Map'
genAssetsBundle :: Gen AssetsBundle
genAssetsBundle = Gen.choice [ AssetsBundle <$> Gen.list (Range.singleton 1) genAssetIdBundleValue
, AssetsBundle <$> sequenceA [genAssetIdBundle, genAdaAssetIdBundle]
]

genAssetIdBundle :: Gen AssetIdBundle
genAssetIdBundle =
AssetIdBundle
genValueNestedRep :: Gen ValueNestedRep
genValueNestedRep =
Gen.choice [ ValueNestedRep <$> Gen.list (Range.singleton 1) genAssetIdBundleValue
, ValueNestedRep <$> sequenceA [genValueNestedAsset, genAdaAssetIdBundle]
]

genValueNestedAsset :: Gen ValueNestedAsset
genValueNestedAsset =
ValueNestedAsset
<$> genPolicyId
<*> Gen.map (Range.singleton 1) ((,) <$> genAssetName <*> genQuantity)

genAdaAssetIdBundle :: Gen AssetIdBundle
genAdaAssetIdBundle :: Gen ValueNestedAsset
genAdaAssetIdBundle = AdaAsset <$> genQuantity

genAssetIdBundleValue :: Gen AssetIdBundle
genAssetIdBundleValue :: Gen ValueNestedAsset
genAssetIdBundleValue =
Gen.choice [ genAssetIdBundle
Gen.choice [ genValueNestedAsset
, genAdaAssetIdBundle
]

Expand Down
22 changes: 11 additions & 11 deletions cardano-api/test/Test/Cardano/Api/Typed/Value.hs
Expand Up @@ -21,21 +21,21 @@ prop_roundtrip_Value_JSON =
tripping v encode eitherDecode


prop_roundtrip_Value_unflatten_flatten :: Property
prop_roundtrip_Value_unflatten_flatten =
prop_roundtrip_Value_flatten_unflatten :: Property
prop_roundtrip_Value_flatten_unflatten =
property $ do v <- forAll genValue
flatten (unflatten v) === v
fromValueNestedRep (toValueNestedRep v) === v

-- Note when going from AssetsBundle -> Value (via flatten)
-- Note when going from ValueNestedRep -> Value (via fromValueNestedRep)
-- we merge maps, which combines all common keys. Therefore
-- we must generate an AssetsBundle with no duplicate values.
-- we must generate an ValueNestedRep with no duplicate values.
-- Remember that Maps cannot have duplicate keys and therefore
-- we will never unflatten (Value -> AssetsBundle) to an
-- AssetsBundle with duplicate values.
prop_roundtrip_Value_flatten_unflatten :: Property
prop_roundtrip_Value_flatten_unflatten =
property $ do aBundle@(AssetsBundle v) <- forAll genAssetsBundle
let AssetsBundle roundtripped = unflatten (flatten aBundle)
-- we will never go from Value -> ValueNestedRep (via toValueNestedRep) to a
-- ValueNestedRep with duplicate values.
prop_roundtrip_Value_unflatten_flatten :: Property
prop_roundtrip_Value_unflatten_flatten =
property $ do aBundle@(ValueNestedRep v) <- forAll genValueNestedRep
let ValueNestedRep roundtripped = toValueNestedRep (fromValueNestedRep aBundle)
sort roundtripped === sort v

-- -----------------------------------------------------------------------------
Expand Down

0 comments on commit f9e9545

Please sign in to comment.