Skip to content

Commit

Permalink
Merge #3416
Browse files Browse the repository at this point in the history
3416: Reduce overestimation of minimum UTxO values for Shelley-era addresses. r=jonathanknowles a=jonathanknowles

### Issue Number

ADP-2039

### Summary

This PR reduces our overestimation of minimum UTxO values for outputs with Shelley-era addresses.

### Details

- When computing minimum ada quantities for _**user-specified outputs**_, coin selection now uses the _actual addresses specified by the user_.
- When computing minimum ada quantities for _**auto-generated change outputs**_, coin selection now uses a _maximum length change address_ that is specific to the current wallet key type.
    - This allows us to use a smaller overestimation in the case of Shelley-era addresses, as the longest Shelley-era address is shorter than the longest Byron-era address.

Co-authored-by: Jonathan Knowles <[email protected]>
Co-authored-by: Johannes Lund <[email protected]>
  • Loading branch information
3 people authored Aug 3, 2022
2 parents 8dba4af + cbc2c20 commit dd20f94
Show file tree
Hide file tree
Showing 38 changed files with 814 additions and 441 deletions.
17 changes: 14 additions & 3 deletions lib/core-integration/src/Test/Integration/Framework/DSL.hs
Original file line number Diff line number Diff line change
Expand Up @@ -674,9 +674,20 @@ walletId =
-- | Min UTxO parameter for the test cluster.
minUTxOValue :: ApiEra -> Natural
minUTxOValue e
| e >= ApiBabbage = 1_107_670 -- needs to be overestimated for the sake of
-- long byron addresses
| e >= ApiAlonzo = 999_978 -- From 34482 lovelace per word
| e >= ApiBabbage = 995_610
-- This value is a slight overestimation for outputs with Shelley
-- addresses and no tokens.
--
-- However, it would be incorrect for outputs with Byron addresses,
-- where the lower bound would be greater by the following amount:
--
-- 4310 lovelace/byte * (86 - 57) byte ≈ 0.125 ada
--
-- However, this value appears to be fine for the purposes of
-- integration tests.
--
| e >= ApiAlonzo = 999_978
-- From 34482 lovelace/word.
| otherwise = 1_000_000

-- | Parameter in test cluster shelley genesis.
Expand Down
12 changes: 7 additions & 5 deletions lib/core-integration/src/Test/Integration/Framework/TestData.hs
Original file line number Diff line number Diff line change
Expand Up @@ -316,11 +316,13 @@ errMsg403InvalidConstructTx =
\Include at least one of them."

errMsg403MinUTxOValue :: String
errMsg403MinUTxOValue =
"Some outputs have ada values that are too small. There's a \
\minimum ada value specified by the protocol that each output must satisfy. \
\I'll handle that minimum value myself when you do not explicitly specify \
\an ada value for an output. Otherwise, you must specify enough ada."
errMsg403MinUTxOValue = unwords
[ "One of the outputs you've specified has an ada quantity that is"
, "below the minimum required. Either increase the ada quantity to"
, "at least the minimum, or specify an ada quantity of zero, in"
, "which case the wallet will automatically assign the correct"
, "minimum ada quantity to the output."
]

errMsg409WalletExists :: String -> String
errMsg409WalletExists walId = "This operation would yield a wallet with the following\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import Test.Integration.Framework.Request
( RequestException )
import Test.Integration.Framework.TestData
( errMsg400StartTimeLaterThanEndTime
, errMsg403MinUTxOValue
, errMsg404NoAsset
, errMsg404NoWallet
, steveToken
Expand Down Expand Up @@ -186,7 +187,7 @@ spec = describe "BYRON_TRANSACTIONS" $ do
rtx <- request @(ApiTransaction n) ctx
(Link.createTransactionOld @'Byron wSrc) Default payload
expectResponseCode HTTP.status403 rtx
expectErrorMessage "Some outputs have ada values that are too small." rtx
expectErrorMessage errMsg403MinUTxOValue rtx

describe "BYRON_TRANS_ASSETS_CREATE_02a - Multi-asset transaction with no ADA" $
forM_ [ (fixtureMultiAssetRandomWallet @n, "Byron wallet")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,11 @@ import Test.Integration.Framework.DSL
, verify
)
import Test.Integration.Framework.TestData
( errMsg403Fee, errMsg403InvalidConstructTx, errMsg403NotEnoughMoney )
( errMsg403Fee
, errMsg403InvalidConstructTx
, errMsg403MinUTxOValue
, errMsg403NotEnoughMoney
)

import qualified Cardano.Wallet.Api.Link as Link
import qualified Network.HTTP.Types.Status as HTTP
Expand Down Expand Up @@ -324,7 +328,7 @@ spec = describe "NEW_BYRON_TRANSACTIONS" $ do
(Link.createUnsignedTransaction @'Byron wa) Default payload
verify rTx
[ expectResponseCode HTTP.status403
, expectErrorMessage "Some outputs have ada values that are too small."
, expectErrorMessage errMsg403MinUTxOValue
]

describe "BYRON_TRANS_NEW_ASSETS_CREATE_01c - Multi-asset tx without Ada" $
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import Cardano.Wallet.Api.Types
)
import Cardano.Wallet.Primitive.SyncProgress
( SyncProgress (..) )
import Cardano.Wallet.Primitive.Types
import Cardano.Wallet.Primitive.Types.ProtocolMagic
( getProtocolMagic, mainnetMagic )
import Control.Monad
( when )
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1433,8 +1433,8 @@ spec = describe "SHELLEY_STAKE_POOLS" $ do
costOfJoining :: Context -> Natural
costOfJoining ctx =
if _mainEra ctx >= ApiBabbage
then costOf (\coeff cst -> 458 * coeff + cst) ctx
else costOf (\coeff cst -> 454 * coeff + cst) ctx
then costOf (\coeff cst -> 454 * coeff + cst) ctx
else costOf (\coeff cst -> 450 * coeff + cst) ctx

costOfQuitting :: Context -> Natural
costOfQuitting ctx =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do
(Link.createTransactionOld @'Shelley wSrc) Default payload
-- It should fail with InsufficientMinCoinValueError
expectResponseCode HTTP.status403 rtx
expectErrorMessage "Some outputs have ada values that are too small." rtx
expectErrorMessage errMsg403MinUTxOValue rtx

it "TRANS_ASSETS_CREATE_02a - Multi-asset transaction without Ada" $ \ctx -> runResourceT $ do
wSrc <- fixtureMultiAssetWallet ctx
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1051,7 +1051,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do
(Link.createUnsignedTransaction @'Shelley wa) Default payload
verify rTx
[ expectResponseCode HTTP.status403
, expectErrorMessage "Some outputs have ada values that are too small."
, expectErrorMessage errMsg403MinUTxOValue
]

it "TRANS_NEW_ASSETS_CREATE_01c - Multi-asset tx without Ada" $ \ctx -> runResourceT $ do
Expand Down
2 changes: 2 additions & 0 deletions lib/core/cardano-wallet-core.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -252,10 +252,12 @@ library
Cardano.Wallet.Primitive.Passphrase.Types
Cardano.Wallet.Primitive.Types
Cardano.Wallet.Primitive.Types.Address
Cardano.Wallet.Primitive.Types.Address.Constants
Cardano.Wallet.Primitive.Types.Coin
Cardano.Wallet.Primitive.Types.Hash
Cardano.Wallet.Primitive.Types.MinimumUTxO
Cardano.Wallet.Primitive.Types.MinimumUTxO.Gen
Cardano.Wallet.Primitive.Types.ProtocolMagic
Cardano.Wallet.Primitive.Types.Redeemer
Cardano.Wallet.Primitive.Types.RewardAccount
Cardano.Wallet.Primitive.Types.TokenBundle
Expand Down
4 changes: 2 additions & 2 deletions lib/core/src/Cardano/Byron/Codec/Cbor.hs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ import Cardano.Wallet.Primitive.AddressDerivation
( Depth (..), DerivationType (..), Index (..) )
import Cardano.Wallet.Primitive.Passphrase
( Passphrase (..) )
import Cardano.Wallet.Primitive.Types
( ProtocolMagic (..) )
import Cardano.Wallet.Primitive.Types.Address
( Address (..) )
import Cardano.Wallet.Primitive.Types.Hash
( Hash (..) )
import Cardano.Wallet.Primitive.Types.ProtocolMagic
( ProtocolMagic (..) )
import Cardano.Wallet.Primitive.Types.Tx
( TxIn (..), TxOut (..), unsafeCoinToTxOutCoinValue )
import Control.Monad
Expand Down
46 changes: 27 additions & 19 deletions lib/core/src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ import Cardano.Wallet.Network
, NetworkLayer (..)
)
import Cardano.Wallet.Primitive.AddressDerivation
( DelegationAddress (..)
( BoundedAddressLength (..)
, DelegationAddress (..)
, Depth (..)
, DerivationIndex (..)
, DerivationPrefix (..)
Expand Down Expand Up @@ -421,8 +422,6 @@ import Cardano.Wallet.Primitive.Types.RewardAccount
( RewardAccount (..) )
import Cardano.Wallet.Primitive.Types.TokenBundle
( TokenBundle (..) )
import Cardano.Wallet.Primitive.Types.TokenMap
( TokenMap )
import Cardano.Wallet.Primitive.Types.TokenPolicy
( TokenName (UnsafeTokenName), TokenPolicyId (UnsafeTokenPolicyId) )
import Cardano.Wallet.Primitive.Types.TokenQuantity
Expand Down Expand Up @@ -473,7 +472,7 @@ import Cardano.Wallet.Transaction
import Control.Applicative
( (<|>) )
import Control.Arrow
( left )
( first, left )
import Control.DeepSeq
( NFData )
import Control.Monad
Expand Down Expand Up @@ -546,7 +545,7 @@ import Data.Map.Strict
import Data.Maybe
( fromMaybe, isJust, mapMaybe )
import Data.Proxy
( Proxy )
( Proxy (..) )
import Data.Quantity
( Quantity (..) )
import Data.Set
Expand Down Expand Up @@ -922,26 +921,28 @@ getWalletUtxoSnapshot ctx wid = do
(wallet, _, pending) <- withExceptT id (readWallet @ctx @s @k ctx wid)
pp <- liftIO $ currentProtocolParameters nl
era <- liftIO $ currentNodeEra nl
let bundles = availableUTxO @s pending wallet
let txOuts = availableUTxO @s pending wallet
& unUTxO
& F.toList
& fmap (view #tokens)
pure $ pairBundleWithMinAdaQuantity era pp <$> bundles
pure $ first (view #tokens) . pairTxOutWithMinAdaQuantity era pp <$> txOuts
where
nl = ctx ^. networkLayer
tl = ctx ^. transactionLayer @k

pairBundleWithMinAdaQuantity
pairTxOutWithMinAdaQuantity
:: Cardano.AnyCardanoEra
-> ProtocolParameters
-> TokenBundle
-> (TokenBundle, Coin)
pairBundleWithMinAdaQuantity era pp bundle =
(bundle, computeMinAdaQuantity $ view #tokens bundle)
-> TxOut
-> (TxOut, Coin)
pairTxOutWithMinAdaQuantity era pp out =
(out, computeMinAdaQuantity out)
where
computeMinAdaQuantity :: TokenMap -> Coin
computeMinAdaQuantity =
view #txOutputMinimumAdaQuantity (constraints tl era pp)
computeMinAdaQuantity :: TxOut -> Coin
computeMinAdaQuantity (TxOut addr bundle) =
view #txOutputMinimumAdaQuantity
(constraints tl era pp)
(addr)
(view #tokens bundle)

-- | List the wallet's UTxO statistics.
listUtxoStatistics
Expand Down Expand Up @@ -1568,6 +1569,7 @@ balanceTransaction
, MonadRandom m
, HasLogger m WalletWorkerLog ctx
, Cardano.IsShelleyBasedEra era
, BoundedAddressLength k
)
=> ctx
-> ArgGenChange s
Expand All @@ -1591,6 +1593,7 @@ balanceTransactionWithSelectionStrategy
:: forall era m s k ctx.
( HasTransactionLayer k ctx
, GenChange s
, BoundedAddressLength k
, MonadRandom m
, HasLogger m WalletWorkerLog ctx
, Cardano.IsShelleyBasedEra era
Expand Down Expand Up @@ -1985,6 +1988,8 @@ balanceTransactionWithSelectionStrategy
intCast @Word16 @Int $ view #maximumCollateralInputCount pp
, minimumCollateralPercentage =
view #minimumCollateralPercentage pp
, maximumLengthChangeAddress =
maxLengthAddressFor $ Proxy @k
}

selectionParams = SelectionParams
Expand Down Expand Up @@ -2171,8 +2176,8 @@ calcMinimumCoinValues
calcMinimumCoinValues ctx era outs = do
pp <- currentProtocolParameters nl
pure
$ view #txOutputMinimumAdaQuantity (constraints tl era pp)
. view (#tokens . #tokens) <$> outs
$ uncurry (view #txOutputMinimumAdaQuantity (constraints tl era pp))
. (\o -> (view #address o, view (#tokens . #tokens) o)) <$> outs
where
nl = ctx ^. networkLayer
tl = ctx ^. transactionLayer @k
Expand Down Expand Up @@ -2213,7 +2218,8 @@ data SelectAssetsParams s result = SelectAssetsParams
--
selectAssets
:: forall ctx m s k result.
( HasTransactionLayer k ctx
( BoundedAddressLength k
, HasTransactionLayer k ctx
, HasLogger m WalletWorkerLog ctx
, MonadRandom m
)
Expand Down Expand Up @@ -2247,6 +2253,8 @@ selectAssets ctx era pp params transform = do
intCast @Word16 @Int $ view #maximumCollateralInputCount pp
, minimumCollateralPercentage =
view #minimumCollateralPercentage pp
, maximumLengthChangeAddress =
maxLengthAddressFor $ Proxy @k
}
let selectionParams = SelectionParams
{ assetsToMint =
Expand Down
Loading

0 comments on commit dd20f94

Please sign in to comment.