Skip to content

Commit

Permalink
Merge #2489
Browse files Browse the repository at this point in the history
2489: Add missing APIs for multi-asset r=rvl a=rvl

## Issue Number

ADP-698

## Overview

- Adds assets to the responses of the coin selections endpoints.
- Adds assets to the `/byron-wallets` endpoints.

## Comments



Co-authored-by: Rodney Lorrimar <[email protected]>
  • Loading branch information
iohk-bors[bot] and rvl authored Feb 5, 2021
2 parents 2428be4 + b458c8c commit 4f2753e
Show file tree
Hide file tree
Showing 37 changed files with 7,238 additions and 3,462 deletions.
1 change: 1 addition & 0 deletions lib/core-integration/cardano-wallet-core-integration.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ library
, time
, unliftio
, unliftio-core
, unordered-containers
hs-source-dirs:
src
exposed-modules:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import Test.Integration.Framework.TestData
( errMsg403NotAnIcarusWallet, errMsg404NoWallet )

import qualified Cardano.Wallet.Api.Link as Link
import qualified Data.List as L
import qualified Data.HashSet as Set
import qualified Data.List.NonEmpty as NE
import qualified Network.HTTP.Types.Status as HTTP

Expand Down Expand Up @@ -100,7 +100,7 @@ spec = describe "BYRON_COIN_SELECTION" $ do
targetAddress : _ <- fmap (view #id) <$> listAddresses @n ctx target
let amt = Quantity minUTxOValue
let payment = AddressAmount targetAddress amt mempty
let output = ApiCoinSelectionOutput targetAddress amt
let output = ApiCoinSelectionOutput targetAddress amt mempty
let isValidDerivationPath path =
( length path == 5 )
&&
Expand Down Expand Up @@ -133,19 +133,19 @@ spec = describe "BYRON_COIN_SELECTION" $ do
target <- emptyWallet ctx
targetAddresses <- fmap (view #id) <$> listAddresses @n ctx target
let amounts = Quantity <$> [minUTxOValue ..]
let targetAssets = repeat mempty
let payments = NE.fromList
$ take paymentCount
$ map ($ mempty)
$ zipWith AddressAmount targetAddresses amounts
let outputs =
take paymentCount
$ zipWith ApiCoinSelectionOutput targetAddresses amounts
let outputs = take paymentCount $ zipWith3 ApiCoinSelectionOutput
targetAddresses amounts targetAssets
selectCoins @_ @'Byron ctx source payments >>= flip verify
[ expectResponseCode HTTP.status200
, expectField #inputs (`shouldSatisfy` (not . null))
, expectField #change (`shouldSatisfy` (not . null))
, expectField
#outputs (`shouldSatisfy` ((L.sort outputs ==) . L.sort))
, expectField #outputs
(`shouldSatisfy` ((Set.fromList outputs ==) . Set.fromList))
]

it "BYRON_COIN_SELECTION_03 - \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,11 @@ import Test.Integration.Framework.TestData

import qualified Cardano.Wallet.Api.Link as Link
import qualified Cardano.Wallet.Primitive.AddressDerivation.Icarus as Icarus
import qualified Data.List as L
import qualified Data.HashSet as Set
import qualified Data.List.NonEmpty as NE
import qualified Data.Text.Encoding as T
import qualified Network.HTTP.Types.Status as HTTP


spec :: forall n.
( DecodeAddress n
, DecodeStakeAddress n
Expand Down Expand Up @@ -330,16 +329,17 @@ spec = describe "BYRON_HW_WALLETS" $ do
icarusAddresses @n mnemonics
let targetAmounts = take paymentCount $
Quantity <$> [minUTxOValue ..]
let targetAssets = repeat mempty
let payments = NE.fromList $ map ($ mempty) $
zipWith AddressAmount targetAddresses targetAmounts
let outputs =
zipWith ApiCoinSelectionOutput targetAddresses targetAmounts
let outputs = zipWith3 ApiCoinSelectionOutput
targetAddresses targetAmounts targetAssets
selectCoins @n @'Byron ctx source payments >>= flip verify
[ expectResponseCode HTTP.status200
, expectField #inputs
(`shouldSatisfy` (not . null))
, expectField #outputs
(`shouldSatisfy` ((L.sort outputs ==) . L.sort))
(`shouldSatisfy` ((Set.fromList outputs ==) . Set.fromList))
, expectField #change
(`shouldSatisfy` (not . null))
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ import Test.Integration.Framework.TestData
( errMsg404NoWallet, errMsg406, errMsg415 )

import qualified Cardano.Wallet.Api.Link as Link
import qualified Data.List as L
import qualified Data.HashSet as Set
import qualified Data.List.NonEmpty as NE
import qualified Network.HTTP.Types as HTTP

Expand All @@ -97,7 +97,7 @@ spec = describe "SHELLEY_COIN_SELECTION" $ do
targetAddress : _ <- fmap (view #id) <$> listAddresses @n ctx target
let amount = Quantity minUTxOValue
let payment = AddressAmount targetAddress amount mempty
let output = ApiCoinSelectionOutput targetAddress amount
let output = ApiCoinSelectionOutput targetAddress amount mempty
let isValidDerivationPath path =
( length path == 5 )
&&
Expand Down Expand Up @@ -130,19 +130,19 @@ spec = describe "SHELLEY_COIN_SELECTION" $ do
target <- emptyWallet ctx
targetAddresses <- fmap (view #id) <$> listAddresses @n ctx target
let amounts = Quantity <$> [minUTxOValue ..]
let assets = repeat mempty
let payments = NE.fromList
$ take paymentCount
$ map ($ mempty)
$ zipWith AddressAmount targetAddresses amounts
let outputs =
take paymentCount
$ zipWith ApiCoinSelectionOutput targetAddresses amounts
let outputs = take paymentCount $ zipWith3 ApiCoinSelectionOutput
targetAddresses amounts assets
selectCoins @_ @'Shelley ctx source payments >>= flip verify
[ expectResponseCode HTTP.status200
, expectField #inputs (`shouldSatisfy` (not . null))
, expectField #change (`shouldSatisfy` (not . null))
, expectField
#outputs (`shouldSatisfy` ((L.sort outputs ==) . L.sort))
, expectField #outputs
(`shouldSatisfy` ((Set.fromList outputs ==) . Set.fromList))
]

it "WALLETS_COIN_SELECTION_03 - \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ import Test.Integration.Framework.TestData
( errMsg403NoRootKey, payloadWith, updateNamePayload, updatePassPayload )

import qualified Cardano.Wallet.Api.Link as Link
import qualified Data.List as L
import qualified Data.HashSet as Set
import qualified Data.List.NonEmpty as NE
import qualified Network.HTTP.Types.Status as HTTP

Expand Down Expand Up @@ -314,16 +314,17 @@ spec = describe "SHELLEY_HW_WALLETS" $ do
fmap (view #id) <$> listAddresses @n ctx target
let targetAmounts = take paymentCount $
Quantity <$> [minUTxOValue ..]
let targetAssets = repeat mempty
let payments = NE.fromList $ map ($ mempty) $
zipWith AddressAmount targetAddresses targetAmounts
let outputs =
zipWith ApiCoinSelectionOutput targetAddresses targetAmounts
let outputs = zipWith3 ApiCoinSelectionOutput
targetAddresses targetAmounts targetAssets
selectCoins @n @'Shelley ctx source payments >>= flip verify
[ expectResponseCode HTTP.status200
, expectField #inputs
(`shouldSatisfy` (not . null))
, expectField #outputs
(`shouldSatisfy` ((L.sort outputs ==) . L.sort))
(`shouldSatisfy` ((Set.fromList outputs ==) . Set.fromList))
, expectField #change
(`shouldSatisfy` (not . null))
]
Expand Down
1 change: 1 addition & 0 deletions lib/core/cardano-wallet-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ library
, fmt
, foldl
, generic-lens >=1.1.0.0 && < 1.2.0.0
, hashable
, http-api-data
, http-client
, http-client-tls
Expand Down
40 changes: 39 additions & 1 deletion lib/core/src/Cardano/Wallet/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ module Cardano.Wallet.Api
, GetByronUTxOsStatistics
, PutByronWalletPassphrase

, ByronAssets
, ListByronAssets
, GetByronAsset
, GetByronAssetDefault

, ByronAddresses
, PostByronAddress
, PutByronAddress
Expand Down Expand Up @@ -246,6 +251,7 @@ type Api n apiPool =
:<|> ShelleyMigrations n
:<|> StakePools n apiPool
:<|> ByronWallets
:<|> ByronAssets
:<|> ByronAddresses n
:<|> ByronCoinSelections n
:<|> ByronTransactions n
Expand Down Expand Up @@ -369,7 +375,7 @@ type GetAsset = "wallets"
:> Capture "assetName" (ApiT TokenName)
:> Get '[JSON] ApiAsset

-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/getAsset
-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/getAssetDefault
type GetAssetDefault = "wallets"
:> Capture "walletId" (ApiT WalletId)
:> "assets"
Expand Down Expand Up @@ -616,6 +622,38 @@ type PutByronWalletPassphrase = "byron-wallets"
:> ReqBody '[JSON] ByronWalletPutPassphraseData
:> PutNoContent

{-------------------------------------------------------------------------------
Assets
See also: https://input-output-hk.github.io/cardano-wallet/api/#tag/ByronAssets
-------------------------------------------------------------------------------}

type ByronAssets =
ListByronAssets
:<|> GetByronAsset
:<|> GetByronAssetDefault

-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/listByronAssets
type ListByronAssets = "byron-wallets"
:> Capture "walletId" (ApiT WalletId)
:> "assets"
:> Get '[JSON] [ApiAsset]

-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/getByronAsset
type GetByronAsset = "byron-wallets"
:> Capture "walletId" (ApiT WalletId)
:> "assets"
:> Capture "policyId" (ApiT TokenPolicyId)
:> Capture "assetName" (ApiT TokenName)
:> Get '[JSON] ApiAsset

-- | https://input-output-hk.github.io/cardano-wallet/api/#operation/getByronAssetDefault
type GetByronAssetDefault = "byron-wallets"
:> Capture "walletId" (ApiT WalletId)
:> "assets"
:> Capture "policyId" (ApiT TokenPolicyId)
:> Get '[JSON] ApiAsset

{-------------------------------------------------------------------------------
Addresses
Expand Down
31 changes: 18 additions & 13 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ import Cardano.Wallet.Primitive.Types.Coin
( Coin (..) )
import Cardano.Wallet.Primitive.Types.Hash
( Hash (..) )
import Cardano.Wallet.Primitive.Types.TokenBundle
( TokenBundle (..) )
import Cardano.Wallet.Primitive.Types.TokenPolicy
( TokenName (..), TokenPolicyId (..), nullTokenName )
import Cardano.Wallet.Primitive.Types.Tx
Expand Down Expand Up @@ -1191,7 +1193,7 @@ selectCoins ctx genChange (ApiT wid) body = do
-- TODO 2:
-- Allow passing around metadata as part of external coin selections.
let txCtx = defaultTransactionCtx
let outs = coerceCoin <$> body ^. #payments
let outs = addressAmountToTxOut <$> body ^. #payments

let transform = \s sel ->
W.assignChangeAddresses genChange sel s
Expand Down Expand Up @@ -1421,7 +1423,7 @@ postTransaction
-> Handler (ApiTransaction n)
postTransaction ctx genChange (ApiT wid) body = do
let pwd = coerce $ body ^. #passphrase . #getApiT
let outs = coerceCoin <$> body ^. #payments
let outs = addressAmountToTxOut <$> body ^. #payments
let md = body ^? #metadata . traverse . #getApiT
let mTTL = body ^? #timeToLive . traverse . #getQuantity

Expand Down Expand Up @@ -1544,7 +1546,7 @@ postTransactionFee ctx (ApiT wid) body = do
withWorkerCtx ctx wid liftE liftE $ \wrk -> do
w <- liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid
let runSelection = W.selectAssets @_ @s @k wrk w txCtx outs getFee
where outs = coerceCoin <$> body ^. #payments
where outs = addressAmountToTxOut <$> body ^. #payments
getFee = const (selectionDelta TokenBundle.getCoin)
liftHandler $ mkApiFee Nothing <$> W.estimateFee runSelection

Expand Down Expand Up @@ -2040,29 +2042,32 @@ mkApiCoinSelection deps mcerts (UnsignedTx inputs outputs change) =
apiStakePath = ApiT <$> xs

mkApiCoinSelectionInput :: input -> ApiCoinSelectionInput n
mkApiCoinSelectionInput (TxIn txid index, TxOut addr tokens, path) =
mkApiCoinSelectionInput
(TxIn txid index, TxOut addr (TokenBundle amount assets), path) =
ApiCoinSelectionInput
{ id = ApiT txid
, index = index
, address = (ApiT addr, Proxy @n)
, amount = Quantity $
fromIntegral $ unCoin $ TokenBundle.getCoin tokens
, amount = coinToQuantity amount
, assets = ApiT assets
, derivationPath = ApiT <$> path
}

mkApiCoinSelectionOutput :: output -> ApiCoinSelectionOutput n
mkApiCoinSelectionOutput (TxOut addr tokens) =
ApiCoinSelectionOutput
(ApiT addr, Proxy @n)
(Quantity $ fromIntegral $ unCoin $ TokenBundle.getCoin tokens)
mkApiCoinSelectionOutput (TxOut addr (TokenBundle amount assets)) =
ApiCoinSelectionOutput (ApiT addr, Proxy @n)
(coinToQuantity amount)
(ApiT assets)

mkApiCoinSelectionChange :: change -> ApiCoinSelectionChange n
mkApiCoinSelectionChange txChange =
ApiCoinSelectionChange
{ address =
(ApiT $ view #address txChange, Proxy @n)
, amount =
Quantity $ fromIntegral $ unCoin $ view #amount txChange
coinToQuantity $ view #amount txChange
, assets =
ApiT $ view #assets txChange
, derivationPath =
ApiT <$> view #derivationPath txChange
}
Expand Down Expand Up @@ -2188,10 +2193,10 @@ mkApiWithdrawal
mkApiWithdrawal (acct, c) =
ApiWithdrawal (ApiT acct, Proxy @n) (mkApiCoin c)

coerceCoin
addressAmountToTxOut
:: forall (n :: NetworkDiscriminant). AddressAmount (ApiT Address, Proxy n)
-> TxOut
coerceCoin (AddressAmount (ApiT addr, _) c (ApiT assets)) =
addressAmountToTxOut (AddressAmount (ApiT addr, _) c (ApiT assets)) =
TxOut addr (TokenBundle.TokenBundle (coinFromQuantity c) assets)

natural :: Quantity q Word32 -> Quantity q Natural
Expand Down
9 changes: 7 additions & 2 deletions lib/core/src/Cardano/Wallet/Api/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,8 @@ import Data.Function
( (&) )
import Data.Generics.Internal.VL.Lens
( view, (^.) )
import Data.Hashable
( Hashable )
import Data.List
( intercalate )
import Data.List.NonEmpty
Expand Down Expand Up @@ -536,6 +538,7 @@ data ApiCoinSelection (n :: NetworkDiscriminant) = ApiCoinSelection
data ApiCoinSelectionChange (n :: NetworkDiscriminant) = ApiCoinSelectionChange
{ address :: !(ApiT Address, Proxy n)
, amount :: !(Quantity "lovelace" Natural)
, assets :: !(ApiT W.TokenMap)
, derivationPath :: NonEmpty (ApiT DerivationIndex)
} deriving (Eq, Generic, Show)
deriving anyclass NFData
Expand All @@ -546,14 +549,16 @@ data ApiCoinSelectionInput (n :: NetworkDiscriminant) = ApiCoinSelectionInput
, address :: !(ApiT Address, Proxy n)
, derivationPath :: NonEmpty (ApiT DerivationIndex)
, amount :: !(Quantity "lovelace" Natural)
, assets :: !(ApiT W.TokenMap)
} deriving (Eq, Generic, Show)
deriving anyclass NFData

data ApiCoinSelectionOutput (n :: NetworkDiscriminant) = ApiCoinSelectionOutput
{ address :: !(ApiT Address, Proxy n)
, amount :: !(Quantity "lovelace" Natural)
, assets :: !(ApiT W.TokenMap)
} deriving (Eq, Ord, Generic, Show)
deriving anyclass NFData
deriving anyclass (NFData, Hashable)

data ApiWallet = ApiWallet
{ id :: !(ApiT WalletId)
Expand Down Expand Up @@ -1197,7 +1202,7 @@ instance KnownDiscovery (SeqState network key) where
newtype ApiT a =
ApiT { getApiT :: a }
deriving (Generic, Show, Eq, Functor)
deriving newtype (Semigroup, Monoid)
deriving newtype (Semigroup, Monoid, Hashable)
deriving anyclass NFData
deriving instance Ord a => Ord (ApiT a)

Expand Down
7 changes: 5 additions & 2 deletions lib/core/src/Cardano/Wallet/Primitive/Types/Address.hs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}

-- |
-- Copyright: © 2018-2020 IOHK
Expand All @@ -21,6 +23,8 @@ import Data.ByteArray.Encoding
( Base (Base16), convertFromBase, convertToBase )
import Data.ByteString
( ByteString )
import Data.Hashable
( Hashable )
import Data.Text.Class
( CaseStyle (..)
, FromText (..)
Expand Down Expand Up @@ -85,8 +89,7 @@ import qualified Data.Text.Encoding as T
newtype Address = Address
{ unAddress :: ByteString
} deriving (Read, Show, Generic, Eq, Ord)

instance NFData Address
deriving anyclass (NFData, Hashable)

instance Buildable Address where
build addr = mempty
Expand Down
Loading

0 comments on commit 4f2753e

Please sign in to comment.