From e9ffd30dbe10c84af4603395f1f6700b3810c3bc Mon Sep 17 00:00:00 2001 From: KtorZ Date: Thu, 3 Jan 2019 16:17:46 +0100 Subject: [PATCH 1/2] Make CoinSelection unit test fail The strategy here is: - 1/ To actually test the fee sanity check :( ... - 2/ To create a more fragmented UTxO such that more inputs are needed, increasing the overall fees --- .../Kernel/CoinSelection/FromGeneric.hs | 19 +++++++++++++++++-- src/Cardano/Wallet/Kernel/Transactions.hs | 7 +------ test/unit/Test/Spec/CoinSelection.hs | 9 +++++---- .../Test/Spec/CoinSelection/Generators.hs | 16 ++++++++++++++++ 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs b/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs index 43ac46330..a179d538b 100644 --- a/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs +++ b/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs @@ -44,7 +44,8 @@ import Pos.Chain.Txp as Core (TxIn, TxOutAux, Utxo, toaOut, import Pos.Core as Core (AddrAttributes, Address, Coin (..), TxSizeLinear, addCoin, calculateTxSizeLinear, checkCoin, divCoin, isRedeemAddress, maxCoinVal, mkCoin, subCoin, - txSizeLinearMinValue, unsafeMulCoin, unsafeSubCoin) + sumCoins, txSizeLinearMinValue, unsafeMulCoin, + unsafeSubCoin) import Pos.Core.Attributes (Attributes) import Pos.Crypto (Signature) @@ -236,7 +237,21 @@ runCoinSelT opts pickUtxo policy (NE.sortBy (flip (comparing outVal)) -> request -- We adjust for fees /after/ potentially dealing with grouping -- Since grouping only affects the inputs we select, this makes no -- difference. - adjustForFees (feeOptions opts) pickUtxo css + csf <- adjustForFees (feeOptions opts) pickUtxo css + let fees = computeFees csf + if (csoFeesSanityCheck opts fees) + then return csf + else error $ "fee out of bound" <> show fees + + computeFees :: CoinSelFinalResult Cardano -> Core.Coin + computeFees csf = + let + inputs = Core.sumCoins $ fmap (Core.txOutValue . Core.toaOut . snd) $ NE.toList $ csrInputs csf + outputs = Core.sumCoins $ fmap (Core.txOutValue . Core.toaOut) $ NE.toList $ csrOutputs csf + change = Core.sumCoins $ csrChange csf + in -- NOTE: _Rather_ safe since we expect fee to be within reasonnable bounds + mkCoin $ fromIntegral (inputs - (outputs + change)) + intInputGrouping :: InputGrouping -> CoinSelT Core.Utxo CoinSelHardErr m [CoinSelResult Cardano] diff --git a/src/Cardano/Wallet/Kernel/Transactions.hs b/src/Cardano/Wallet/Kernel/Transactions.hs index b312e326c..52ab3cc4b 100644 --- a/src/Cardano/Wallet/Kernel/Transactions.hs +++ b/src/Cardano/Wallet/Kernel/Transactions.hs @@ -230,13 +230,8 @@ newUnsignedTransaction ActiveWallet{..} options accountId payees = runExceptT $ -- that it may change in the future. let attributes = def :: TxAttributes let tx = UnsignedTx inputs outputs attributes coins - - -- STEP 3: Sanity test. Here we check whether our fees are within a reasonable - -- range. let fees = computeFeesOfUnsignedTx tx - if csoFeesSanityCheck options fees - then return (snapshot, tx, fees, availableUtxo) - else error $ "fees out of bound " <> show fees + return (snapshot, tx, fees, availableUtxo) where -- Generate an initial seed for the random generator using the hash of -- the payees, which ensure that the coin selection (and the fee estimation) diff --git a/test/unit/Test/Spec/CoinSelection.hs b/test/unit/Test/Spec/CoinSelection.hs index efffee190..b20a4f164 100644 --- a/test/unit/Test/Spec/CoinSelection.hs +++ b/test/unit/Test/Spec/CoinSelection.hs @@ -49,9 +49,10 @@ import Cardano.Wallet.Kernel.Util.Core (paymentAmount, utxoBalance, import Pos.Crypto.Signing.Safe (fakeSigner) import Test.Pos.Configuration (withProvidedMagicConfig) import Test.Spec.CoinSelection.Generators (InitialBalance (..), - Pay (..), genFiddlyPayees, genFiddlyUtxo, genGroupedUtxo, - genPayee, genPayees, genRedeemPayee, - genUniqueChangeAddress, genUtxoWithAtLeast) + Pay (..), genFiddlyPayees, genFiddlyUtxo, + genFragmentedUtxo, genGroupedUtxo, genPayee, genPayees, + genRedeemPayee, genUniqueChangeAddress, + genUtxoWithAtLeast) {-# ANN module ("HLint: ignore Reduce duplication" :: Text) #-} @@ -663,7 +664,7 @@ spec = ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] prop "multiple payees, SenderPaysFee, fee = cardano" $ \pm -> forAll ( - payBatch pm cardanoFee cardanoFeeCheck identity (InitialADA 1000) (PayADA 100) random + pay pm genFragmentedUtxo genPayees cardanoFee cardanoFeeCheck identity (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] prop "one payee, ReceiverPaysFee, fee = cardano" $ \pm -> forAll ( diff --git a/test/unit/Test/Spec/CoinSelection/Generators.hs b/test/unit/Test/Spec/CoinSelection/Generators.hs index 9ead2ed9e..56a3724e8 100644 --- a/test/unit/Test/Spec/CoinSelection/Generators.hs +++ b/test/unit/Test/Spec/CoinSelection/Generators.hs @@ -14,6 +14,7 @@ module Test.Spec.CoinSelection.Generators ( , Pay(..) , genUniqueChangeAddress , genUtxoWithAtLeast + , genFragmentedUtxo , genRedeemPayee ) where @@ -204,6 +205,21 @@ genUtxoWithAtLeast payment = do , allowRedeemAddresses = False } +-- | Generate a very fragment Utxo with @at least@ the supplied amount of money. +genFragmentedUtxo :: InitialBalance -> Gen Core.Utxo +genFragmentedUtxo payment = do + let balance = toLovelaces payment + twoPercentOf = balance `div` 50 + genUtxo $ StakeGenOptions { + stakeMaxValue = Just (Core.mkCoin twoPercentOf) + , stakeGenerationTarget = AtLeast + , stakeNeeded = Core.mkCoin (toLovelaces payment) + , stakeMaxEntries = Just 1000 + , fiddlyAddresses = False + , allowRedeemAddresses = False + } + + {------------------------------------------------------------------------------- Dealing with grouping -------------------------------------------------------------------------------} From 172a1987fc59372296968f75760ef738d898f563 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 4 Jan 2019 14:12:44 +0100 Subject: [PATCH 2/2] Fix fee sanity check The main idea here is to relocate this check to be done during fee calculation. After adjusting fees, we simply verify that actual fees are somewhere close to the original estimation. If not, we throw hard and call that an invariant violation. This is a security measure as we do not expect such deviation from the original estimation. We may have to pick some extra inputs to cover for fees but if done in such way that the fees are more than double, we should better run the fee calculation on a different selection. This commits also removes some tests from the Spec/GetTransactions module that are now redundant with the checks occuring in Spec/CoinSelection. --- .../Kernel/CoinSelection/FromGeneric.hs | 34 +---- .../Wallet/Kernel/CoinSelection/Generic.hs | 4 + .../Kernel/CoinSelection/Generic/Fees.hs | 77 +++++++++-- src/Cardano/Wallet/Kernel/Transactions.hs | 10 +- .../Wallet/WalletLayer/Kernel/Active.hs | 2 +- test/unit/Test/Spec/CoinSelection.hs | 90 +++++------- test/unit/Test/Spec/GetTransactions.hs | 128 +----------------- test/unit/Test/Spec/NewPayment.hs | 13 +- 8 files changed, 118 insertions(+), 240 deletions(-) diff --git a/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs b/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs index a179d538b..1282f0988 100644 --- a/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs +++ b/src/Cardano/Wallet/Kernel/CoinSelection/FromGeneric.hs @@ -18,7 +18,6 @@ module Cardano.Wallet.Kernel.CoinSelection.FromGeneric ( , largestFirst -- * Estimating fees , estimateCardanoFee - , checkCardanoFeeSanity , boundAddrAttrSize , boundTxAttrSize -- * Estimating transaction limits @@ -44,7 +43,6 @@ import Pos.Chain.Txp as Core (TxIn, TxOutAux, Utxo, toaOut, import Pos.Core as Core (AddrAttributes, Address, Coin (..), TxSizeLinear, addCoin, calculateTxSizeLinear, checkCoin, divCoin, isRedeemAddress, maxCoinVal, mkCoin, subCoin, - sumCoins, txSizeLinearMinValue, unsafeMulCoin, unsafeSubCoin) import Pos.Core.Attributes (Attributes) @@ -148,8 +146,6 @@ data InputGrouping = data CoinSelectionOptions = CoinSelectionOptions { csoEstimateFee :: Int -> NonEmpty Core.Coin -> Core.Coin -- ^ A function to estimate the fees. - , csoFeesSanityCheck :: Core.Coin -> Bool - -- ^ A function we can use to check if fees are not too big or too small. , csoInputGrouping :: InputGrouping -- ^ A preference regarding input grouping. , csoExpenseRegulation :: ExpenseRegulation @@ -163,10 +159,9 @@ data CoinSelectionOptions = CoinSelectionOptions { -- | Creates new 'CoinSelectionOptions' using 'NoGrouping' as default -- 'InputGrouping' and 'SenderPaysFee' as default 'ExpenseRegulation'. newOptions :: (Int -> NonEmpty Core.Coin -> Core.Coin) - -> (Core.Coin -> Bool) -> CoinSelectionOptions -newOptions estimateFee check = CoinSelectionOptions { + -> CoinSelectionOptions +newOptions estimateFee = CoinSelectionOptions { csoEstimateFee = estimateFee - , csoFeesSanityCheck = check , csoInputGrouping = IgnoreGrouping , csoExpenseRegulation = SenderPaysFee , csoDustThreshold = Core.mkCoin 0 @@ -237,21 +232,7 @@ runCoinSelT opts pickUtxo policy (NE.sortBy (flip (comparing outVal)) -> request -- We adjust for fees /after/ potentially dealing with grouping -- Since grouping only affects the inputs we select, this makes no -- difference. - csf <- adjustForFees (feeOptions opts) pickUtxo css - let fees = computeFees csf - if (csoFeesSanityCheck opts fees) - then return csf - else error $ "fee out of bound" <> show fees - - computeFees :: CoinSelFinalResult Cardano -> Core.Coin - computeFees csf = - let - inputs = Core.sumCoins $ fmap (Core.txOutValue . Core.toaOut . snd) $ NE.toList $ csrInputs csf - outputs = Core.sumCoins $ fmap (Core.txOutValue . Core.toaOut) $ NE.toList $ csrOutputs csf - change = Core.sumCoins $ csrChange csf - in -- NOTE: _Rather_ safe since we expect fee to be within reasonnable bounds - mkCoin $ fromIntegral (inputs - (outputs + change)) - + adjustForFees (feeOptions opts) pickUtxo css intInputGrouping :: InputGrouping -> CoinSelT Core.Utxo CoinSelHardErr m [CoinSelResult Cardano] @@ -411,15 +392,6 @@ estimateCardanoFee linearFeePolicy ins outs = ceiling $ calculateTxSizeLinear linearFeePolicy $ hi $ estimateSize boundAddrAttrSize boundTxAttrSize ins outs -checkCardanoFeeSanity :: TxSizeLinear -> Coin -> Bool -checkCardanoFeeSanity linearFeePolicy fees = - let - maxCoeff :: Int = 2 - minFees = Core.mkCoin $ floor $ txSizeLinearMinValue linearFeePolicy - in - (fees >= minFees) && (fees <= Core.unsafeMulCoin minFees maxCoeff) - - -- | Size to use for a value of type @Attributes AddrAttributes@ when estimating -- encoded transaction sizes. The minimum possible value is 2. -- diff --git a/src/Cardano/Wallet/Kernel/CoinSelection/Generic.hs b/src/Cardano/Wallet/Kernel/CoinSelection/Generic.hs index 4579e6bee..eb32e33fa 100644 --- a/src/Cardano/Wallet/Kernel/CoinSelection/Generic.hs +++ b/src/Cardano/Wallet/Kernel/CoinSelection/Generic.hs @@ -12,6 +12,7 @@ module Cardano.Wallet.Kernel.CoinSelection.Generic ( , Rounding(..) , Fee(..) , adjustFee + , valueSum , unsafeFeeSum , utxoEntryVal , sizeOfEntries @@ -156,6 +157,9 @@ newtype Fee dom = Fee { getFee :: Value dom } adjustFee :: (Value dom -> Value dom) -> Fee dom -> Fee dom adjustFee f = Fee . f . getFee +valueSum :: CoinSelDom dom => [Value dom] -> Maybe (Value dom) +valueSum = foldM valueAdd valueZero + unsafeFeeSum :: CoinSelDom dom => [Fee dom] -> Fee dom unsafeFeeSum = Fee . unsafeValueSum . map getFee diff --git a/src/Cardano/Wallet/Kernel/CoinSelection/Generic/Fees.hs b/src/Cardano/Wallet/Kernel/CoinSelection/Generic/Fees.hs index 639380eb4..a1cc99f7a 100644 --- a/src/Cardano/Wallet/Kernel/CoinSelection/Generic/Fees.hs +++ b/src/Cardano/Wallet/Kernel/CoinSelection/Generic/Fees.hs @@ -97,15 +97,33 @@ adjustForFees feeOptions pickUtxo css = do SenderPaysFee -> senderPaysFee pickUtxo feeOptions inps outs chgs - let neInps = case inps' of - [] -> error "adjustForFees: empty list of inputs" - i:is -> i :| is - - let neOuts = case outs' of - [] -> error "adjustForFees: empty list of outputs" - o:os -> o :| os - - return $ CoinSelFinalResult neInps neOuts chgs' + let estimatedFee = getFee $ feeUpperBound feeOptions inps outs chgs + let actualFee = getFee $ computeFee inps' outs' chgs' + -- NOTE + -- We enforce the following invariant: + -- + -- estimatedFee < actualFee < 2 * estimatedFee + -- + -- This coefficient (2*...) is mostly taken out of nowhere, but if anything + -- go beyond that upper bound, we would know that our algorithm for fee + -- reconciliation below is messed up. + -- Similarly, the algorithm tries to take money from inputs until it reaches + -- the goal fixed by 'estimatedFee'. So, the actualFee just can't be lower + -- than the goal. + -- + -- (PS: using `valueDiv` instead of `valueMul` to avoid overflow) + if (actualFee < estimatedFee || actualFee `valueDiv` 2 > estimatedFee) then + error $ "adjustForFees: fee out of bounds: " <> pretty actualFee <> " while expecting ~" <> pretty estimatedFee + else do + let neInps = case inps' of + [] -> error "adjustForFees: empty list of inputs" + i:is -> i :| is + + let neOuts = case outs' of + [] -> error "adjustForFees: empty list of outputs" + o:os -> o :| os + + return $ CoinSelFinalResult neInps neOuts chgs' {------------------------------------------------------------------------------- @@ -287,6 +305,47 @@ feeUpperBound FeeOptions{..} inps outs chgs = numInputs = fromIntegral $ sizeToWord $ selectedSize $ foldl' (flip select) emptySelection inps outputs = map outVal outs <> chgs +-- Computing actual fee is a bit tricky in the generic realm because we don't +-- know what type representation is used by the underlying implementation. So, +-- we can't just sum up all the input and substract the sub of all outputs +-- (incl. change) because we'll risk an overflow with each sum. Instead, we +-- reduce the input value iteratively, coin by coin using a safe distance +-- between coins that are known to be within bounds. +-- The algorithm converge because we know that by construction, there are less +-- outputs than inputs. In essence, this computes: +-- +-- fees = ∑ inputs - (∑ outputs + ∑ changes) +computeFee + :: forall dom. (CoinSelDom dom) + => [UtxoEntry dom] + -> [Output dom] + -> [Value dom] + -> Fee dom +computeFee inps outs chgs = + Fee $ collapse (map utxoEntryVal inps) (map outVal outs <> chgs) + where + -- Some remaining inputs together. At this point, we've removed + -- all outputs and changes, so what's left are simply the actual fees. + -- It's unrealistic to imagine them being bigger than the max coin value. + collapse plus [] = case valueSum plus of + Nothing -> error "fees are bigger than max coin value" + Just a -> a + + -- In order to safely compute fees at this level, we need make sure we don't + -- overflow. Therefore, we remove outputs to inputs until there's no outputs + -- left to remove. + collapse (p:ps) (m:ms) + | p > m = let p' = valueDist p m in collapse (p':ps) ms + | p < m = let m' = valueDist p m in collapse ps (m':ms) + | otherwise = collapse ps ms + + -- This branch can only happens if we've depleted all our inputs and there + -- are still some outputs left to remove from them. If means the total value + -- of outputs (incl. change) was bigger than the total input value which is + -- by definition, impossible; unless we messed up real hard. + collapse [] _ = + error "invariant violation: outputs are bigger than inputs" + -- | divvy fee across outputs, discarding zero-output if any. Returns `Nothing` -- when there's no more outputs after filtering, in which case, we just can't diff --git a/src/Cardano/Wallet/Kernel/Transactions.hs b/src/Cardano/Wallet/Kernel/Transactions.hs index 52ab3cc4b..5430733e3 100644 --- a/src/Cardano/Wallet/Kernel/Transactions.hs +++ b/src/Cardano/Wallet/Kernel/Transactions.hs @@ -11,7 +11,6 @@ module Cardano.Wallet.Kernel.Transactions ( , EstimateFeesError(..) , RedeemAdaError(..) , cardanoFee - , cardanoFeeSanity , mkStdTx , prepareUnsignedTxWithSources , submitSignedTx @@ -44,8 +43,7 @@ import Cardano.Crypto.Wallet (DerivationIndex) import qualified Cardano.Wallet.Kernel.Addresses as Kernel import Cardano.Wallet.Kernel.CoinSelection.FromGeneric (CoinSelFinalResult (..), CoinSelectionOptions (..), - checkCardanoFeeSanity, estimateCardanoFee, - estimateMaxTxInputs) + estimateCardanoFee, estimateMaxTxInputs) import qualified Cardano.Wallet.Kernel.CoinSelection.FromGeneric as CoinSelection import Cardano.Wallet.Kernel.CoinSelection.Generic (CoinSelHardErr (..)) @@ -635,12 +633,6 @@ cardanoFee (TxFeePolicyTxSizeLinear policy) inputs outputs = cardanoFee TxFeePolicyUnknown{} _ _ = error "cardanoFee: unknown policy" -cardanoFeeSanity :: TxFeePolicy -> Coin -> Bool -cardanoFeeSanity (TxFeePolicyTxSizeLinear policy) fees = - checkCardanoFeeSanity policy fees -cardanoFeeSanity TxFeePolicyUnknown{} _ = - error "cardanoFeeSanity: unknown policy" - {------------------------------------------------------------------------------- Ada redemption diff --git a/src/Cardano/Wallet/WalletLayer/Kernel/Active.hs b/src/Cardano/Wallet/WalletLayer/Kernel/Active.hs index 4fac44c90..b6dabf238 100644 --- a/src/Cardano/Wallet/WalletLayer/Kernel/Active.hs +++ b/src/Cardano/Wallet/WalletLayer/Kernel/Active.hs @@ -222,7 +222,7 @@ setupPayment :: Monad m ) setupPayment policy grouping regulation payment = do rootId <- fromRootId wId - let opts = (newOptions (Kernel.cardanoFee policy) (Kernel.cardanoFeeSanity policy)) { + let opts = (newOptions (Kernel.cardanoFee policy)) { csoExpenseRegulation = regulation , csoInputGrouping = grouping } diff --git a/test/unit/Test/Spec/CoinSelection.hs b/test/unit/Test/Spec/CoinSelection.hs index b20a4f164..dc09ff675 100644 --- a/test/unit/Test/Spec/CoinSelection.hs +++ b/test/unit/Test/Spec/CoinSelection.hs @@ -64,15 +64,9 @@ import Test.Spec.CoinSelection.Generators (InitialBalance (..), freeLunch :: Int -> NonEmpty Core.Coin -> Core.Coin freeLunch _ _ = Core.mkCoin 0 -freeLunchCheck :: Core.Coin -> Bool -freeLunchCheck c = c == Core.mkCoin 0 - -- | The smallest fee possible. minFee :: Int -> NonEmpty Core.Coin -> Core.Coin -minFee _ _ = Core.mkCoin 1 - -minFeeCheck :: Core.Coin -> Bool -minFeeCheck c = c == Core.mkCoin 1 +minFee _ outs = Core.mkCoin (fromIntegral $ NE.length outs) -- | An hopefully-accurate estimate of the Tx fees in Cardano. cardanoFee :: Int -> NonEmpty Core.Coin -> Core.Coin @@ -81,21 +75,10 @@ cardanoFee inputs outputs = Core.mkCoin $ where linearFeePolicy = TxSizeLinear (Coeff 155381) (Coeff 43.946) -cardanoFeeCheck :: Core.Coin -> Bool -cardanoFeeCheck fees = - let - minFees = Core.mkCoin 155381 - maxCoeff :: Int = 2 - in - (fees >= minFees) && (fees <= Core.unsafeMulCoin minFees maxCoeff) - -- | A simple linear fee proportional in the #inputs & #outputs. linearFee :: Int -> NonEmpty Core.Coin -> Core.Coin linearFee inputsLen outputs = Core.mkCoin (fromIntegral $ inputsLen + length outputs) -linearFeeCheck :: Core.Coin -> Bool -linearFeeCheck _ = True - -- | For some reason the version of 'QuickCheck' we are using doesn't seem -- to export 'withMaxSuccess'. withMaxSuccess :: Int -> Spec -> Spec @@ -504,18 +487,17 @@ payRestrictInputsTo :: ProtocolMagic -> (InitialBalance -> Gen Core.Utxo) -> (Core.Utxo -> Pay -> Gen (NonEmpty Core.TxOut)) -> (Int -> NonEmpty Core.Coin -> Core.Coin) - -> (Core.Coin -> Bool) -> (CoinSelectionOptions -> CoinSelectionOptions) -> InitialBalance -> Pay -> Policy -> Gen RunResult -payRestrictInputsTo pm maxInputs genU genP feeFunction feeSanity adjustOptions bal amount policy = +payRestrictInputsTo pm maxInputs genU genP feeFunction adjustOptions bal amount policy = withProvidedMagicConfig pm $ \genesisConfig _ _ -> do utxo <- genU bal payee <- genP utxo amount key <- arbitrary - let options = adjustOptions (newOptions feeFunction feeSanity) + let options = adjustOptions (newOptions feeFunction) res <- policy options maxInputs (fmap Core.TxOutAux payee) @@ -535,7 +517,6 @@ pay :: ProtocolMagic -> (InitialBalance -> Gen Core.Utxo) -> (Core.Utxo -> Pay -> Gen (NonEmpty Core.TxOut)) -> (Int -> NonEmpty Core.Coin -> Core.Coin) - -> (Core.Coin -> Bool) -> (CoinSelectionOptions -> CoinSelectionOptions) -> InitialBalance -> Pay @@ -545,7 +526,6 @@ pay pm = payRestrictInputsTo pm maxNumInputs payOne :: ProtocolMagic -> (Int -> NonEmpty Core.Coin -> Core.Coin) - -> (Core.Coin -> Bool) -> (CoinSelectionOptions -> CoinSelectionOptions) -> InitialBalance -> Pay @@ -557,7 +537,6 @@ payOne pm = pay pm genUtxoWithAtLeast genPayee payOne' :: ProtocolMagic -> (Core.Utxo -> Pay -> Gen (NonEmpty Core.TxOut)) -> (Int -> NonEmpty Core.Coin -> Core.Coin) - -> (Core.Coin -> Bool) -> (CoinSelectionOptions -> CoinSelectionOptions) -> InitialBalance -> Pay @@ -567,7 +546,6 @@ payOne' pm payeeGenerator = pay pm genUtxoWithAtLeast payeeGenerator payBatch :: ProtocolMagic -> (Int -> NonEmpty Core.Coin -> Core.Coin) - -> (Core.Coin -> Bool) -> (CoinSelectionOptions -> CoinSelectionOptions) -> InitialBalance -> Pay @@ -592,65 +570,65 @@ spec = describe "Coin selection policies unit tests" $ do withMaxSuccess 5 $ describe "largestFirst" $ do prop "one payee, SenderPaysFee, fee = 0" $ \pm -> forAll ( - payOne pm freeLunch freeLunchCheck identity (InitialLovelace 1000) (PayLovelace 100) largestFirst + payOne pm freeLunch identity (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "one payee, ReceiverPaysFee, fee = 0" $ \pm -> forAll ( - payOne pm freeLunch freeLunchCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst + payOne pm freeLunch receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "multiple payees, SenderPaysFee, fee = 0" $ \pm -> forAll ( - payBatch pm freeLunch freeLunchCheck identity (InitialLovelace 1000) (PayLovelace 100) largestFirst + payBatch pm freeLunch identity (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "multiple payees, ReceiverPaysFee, fee = 0" $ \pm -> forAll ( - payBatch pm freeLunch freeLunchCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst + payBatch pm freeLunch receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res -- Minimal fee prop "one payee, SenderPaysFee, fee = 1 Lovelace" $ \pm -> forAll ( - payOne pm minFee minFeeCheck identity (InitialLovelace 1000) (PayLovelace 100) largestFirst + payOne pm minFee identity (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "one payee, ReceiverPaysFee, fee = 1 Lovelace" $ \pm -> forAll ( - payOne pm minFee minFeeCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst + payOne pm minFee receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "multiple payees, SenderPaysFee, fee = 1 Lovelace" $ \pm -> forAll ( - payBatch pm minFee minFeeCheck identity (InitialLovelace 1000) (PayLovelace 100) largestFirst + payBatch pm minFee identity (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "multiple payees, ReceiverPaysFee, fee = 1 Lovelace" $ \pm -> forAll ( - payBatch pm minFee minFeeCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst + payBatch pm minFee receiverPays (InitialLovelace 1000) (PayLovelace 100) largestFirst ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res withMaxSuccess 5 $ describe "random" $ do prop "one payee, SenderPaysFee, fee = 0" $ \pm -> forAll ( - payOne pm freeLunch freeLunchCheck identity (InitialLovelace 1000) (PayLovelace 100) random + payOne pm freeLunch identity (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "one payee, ReceiverPaysFee, fee = 0" $ \pm -> forAll ( - payOne pm freeLunch freeLunchCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) random + payOne pm freeLunch receiverPays (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "multiple payees, SenderPaysFee, fee = 0" $ \pm -> forAll ( - payBatch pm freeLunch freeLunchCheck identity (InitialLovelace 1000) (PayLovelace 100) random + payBatch pm freeLunch identity (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res prop "multiple payees, ReceiverPaysFee, fee = 0" $ \pm -> forAll ( - payBatch pm freeLunch freeLunchCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) random + payBatch pm freeLunch receiverPays (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceeded utxo payee res -- minimal fee. It doesn't make sense to use it for 'ReceiverPaysFee', because -- rounding will essentially cause the computed @epsilon@ will be 0 for each -- output. For those cases, we use the 'linear' fee policy. prop "one payee, SenderPaysFee, fee = 1 Lovelace" $ \pm -> forAll ( - payOne pm minFee minFeeCheck identity (InitialLovelace 1000) (PayLovelace 100) random + payOne pm minFee identity (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] prop "multiple payees, SenderPaysFee, fee = 1 Lovelace" $ \pm -> forAll ( - payBatch pm minFee minFeeCheck identity (InitialLovelace 1000) (PayLovelace 100) random + payBatch pm minFee identity (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] -- linear fee prop "one payee, ReceiverPaysFee, fee = linear" $ \pm -> forAll ( - payOne pm linearFee linearFeeCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) random + payOne pm linearFee receiverPays (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed ReceiverPaysFee] prop "multiple payees, ReceiverPaysFee, fee = linear" $ \pm -> forAll ( - payBatch pm linearFee linearFeeCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) random + payBatch pm linearFee receiverPays (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed ReceiverPaysFee] @@ -660,37 +638,37 @@ spec = -- like attributes, and trying to setup syntetic experiments with -- less than 1ADA (10^6 lovelaces) is probably counter-productive prop "one payee, SenderPaysFee, fee = cardano" $ \pm -> forAll ( - payOne pm cardanoFee cardanoFeeCheck identity (InitialADA 1000) (PayADA 100) random + payOne pm cardanoFee identity (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] prop "multiple payees, SenderPaysFee, fee = cardano" $ \pm -> forAll ( - pay pm genFragmentedUtxo genPayees cardanoFee cardanoFeeCheck identity (InitialADA 1000) (PayADA 100) random + pay pm genFragmentedUtxo genPayees cardanoFee identity (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] prop "one payee, ReceiverPaysFee, fee = cardano" $ \pm -> forAll ( - payOne pm cardanoFee cardanoFeeCheck receiverPays (InitialADA 1000) (PayADA 100) random + payOne pm cardanoFee receiverPays (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed ReceiverPaysFee] prop "multiple payees, ReceiverPaysFee, fee = cardano" $ \pm -> forAll ( - payBatch pm cardanoFee cardanoFeeCheck receiverPays (InitialADA 1000) (PayADA 100) random + payBatch pm cardanoFee receiverPays (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed ReceiverPaysFee] withMaxSuccess 5 $ describe "Expected failures" $ do prop "Paying a redeem address should always be rejected" $ \pm -> forAll ( - payOne' pm genRedeemPayee linearFee linearFeeCheck receiverPays (InitialLovelace 1000) (PayLovelace 100) random + payOne' pm genRedeemPayee linearFee receiverPays (InitialLovelace 1000) (PayLovelace 100) random ) $ \(utxo, payee, res) -> paymentFailedWith utxo payee res [errorWas outputWasRedeem] prop "Paying somebody not having enough money should fail" $ \pm -> forAll ( - payBatch pm linearFee linearFeeCheck receiverPays (InitialLovelace 10) (PayLovelace 100) random + payBatch pm linearFee receiverPays (InitialLovelace 10) (PayLovelace 100) random ) $ \(utxo, payee, res) -> do paymentFailedWith utxo payee res [errorWas notEnoughMoney] prop "Restricting too much the number of inputs results in an hard error for a single payee" $ \pm -> forAll ( - payRestrictInputsTo pm 1 genUtxoWithAtLeast genPayee freeLunch freeLunchCheck identity (InitialLovelace 200) (PayLovelace 100) random + payRestrictInputsTo pm 1 genUtxoWithAtLeast genPayee freeLunch identity (InitialLovelace 200) (PayLovelace 100) random ) $ \(utxo, payee, res) -> do paymentFailedWith utxo payee res [errorWas maxInputsReached] prop "Restricting too much the number of inputs results in an hard error for multiple payees" $ \pm -> forAll ( - payRestrictInputsTo pm 1 genUtxoWithAtLeast genPayees freeLunch freeLunchCheck identity (InitialLovelace 200) (PayLovelace 100) random + payRestrictInputsTo pm 1 genUtxoWithAtLeast genPayees freeLunch identity (InitialLovelace 200) (PayLovelace 100) random ) $ \(utxo, payee, res) -> do paymentFailedWith utxo payee res [errorWas maxInputsReached] @@ -700,11 +678,11 @@ spec = -- the average in Cardano. withMaxSuccess 5 $ describe "Fiddly Addresses" $ do prop "multiple payees, SenderPaysFee, fee = cardano" $ \pm -> forAll ( - pay pm genFiddlyUtxo genFiddlyPayees cardanoFee cardanoFeeCheck identity (InitialADA 1000) (PayADA 100) random + pay pm genFiddlyUtxo genFiddlyPayees cardanoFee identity (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed SenderPaysFee] prop "multiple payees, ReceiverPaysFee, fee = cardano" $ \pm -> forAll ( - pay pm genFiddlyUtxo genFiddlyPayees cardanoFee cardanoFeeCheck receiverPays (InitialADA 1000) (PayADA 100) random + pay pm genFiddlyUtxo genFiddlyPayees cardanoFee receiverPays (InitialADA 1000) (PayADA 100) random ) $ \(utxo, payee, res) -> paymentSucceededWith utxo payee res [feeWasPayed ReceiverPaysFee] @@ -720,23 +698,23 @@ spec = -- the associated inputs paying into the address we just picked. withMaxSuccess 5 $ describe "Input Grouping" $ do prop "Require grouping, fee = 0, one big group depletes the Utxo completely" $ \pm -> forAll ( - pay pm (genGroupedUtxo 1) genPayee freeLunch freeLunchCheck requireGrouping (InitialLovelace 1000) (PayLovelace 10) random + pay pm (genGroupedUtxo 1) genPayee freeLunch requireGrouping (InitialLovelace 1000) (PayLovelace 10) random ) $ \(utxo, payee, res) -> do paymentSucceededWith utxo payee res [utxoWasDepleted] prop "Require grouping, fee = cardano, one big group depletes the Utxo completely" $ \pm -> forAll ( - pay pm (genGroupedUtxo 1) genPayee freeLunch freeLunchCheck requireGrouping (InitialADA 1000) (PayADA 10) random + pay pm (genGroupedUtxo 1) genPayee freeLunch requireGrouping (InitialADA 1000) (PayADA 10) random ) $ \(utxo, payee, res) -> do paymentSucceededWith utxo payee res [utxoWasDepleted] prop "Require grouping, fee = 0, several groups allows the payment to be fullfilled" $ \pm -> forAll ( - pay pm (genGroupedUtxo 10) genPayee freeLunch freeLunchCheck requireGrouping (InitialLovelace 1000) (PayLovelace 10) random + pay pm (genGroupedUtxo 10) genPayee freeLunch requireGrouping (InitialLovelace 1000) (PayLovelace 10) random ) $ \(utxo, payee, res) -> do paymentSucceeded utxo payee res prop "Prefer grouping, fee = 0" $ \pm -> forAll ( - payOne pm freeLunch freeLunchCheck preferGrouping (InitialLovelace 1000) (PayLovelace 10) random + payOne pm freeLunch preferGrouping (InitialLovelace 1000) (PayLovelace 10) random ) $ \(utxo, payee, res) -> do paymentSucceeded utxo payee res prop "IgnoreGrouping, fee = 0 must not deplete the utxo" $ \pm -> forAll ( - pay pm (genGroupedUtxo 1) genPayee freeLunch freeLunchCheck ignoreGrouping (InitialLovelace 1000) (PayLovelace 10) random + pay pm (genGroupedUtxo 1) genPayee freeLunch ignoreGrouping (InitialLovelace 1000) (PayLovelace 10) random ) $ \(utxo, payee, res) -> do paymentSucceededWith utxo payee res [utxoWasNotDepleted] diff --git a/test/unit/Test/Spec/GetTransactions.hs b/test/unit/Test/Spec/GetTransactions.hs index f10d33d5a..d11f3d4db 100644 --- a/test/unit/Test/Spec/GetTransactions.hs +++ b/test/unit/Test/Spec/GetTransactions.hs @@ -56,7 +56,6 @@ import Cardano.Wallet.Kernel.DB.TxMeta import qualified Cardano.Wallet.Kernel.DB.Util.IxSet as IxSet import Cardano.Wallet.Kernel.Internal import qualified Cardano.Wallet.Kernel.Keystore as Keystore -import qualified Cardano.Wallet.Kernel.NodeStateAdaptor as Node import qualified Cardano.Wallet.Kernel.PrefilterTx as Kernel import qualified Cardano.Wallet.Kernel.Read as Kernel import qualified Cardano.Wallet.Kernel.Transactions as Kernel @@ -65,13 +64,12 @@ import Cardano.Wallet.WalletLayer (ActiveWalletLayer (..), walletPassiveLayer) import qualified Cardano.Wallet.WalletLayer as WalletLayer import qualified Cardano.Wallet.WalletLayer.Kernel.Accounts as Accounts -import qualified Cardano.Wallet.WalletLayer.Kernel.Active as Active import qualified Cardano.Wallet.WalletLayer.Kernel.Conv as Kernel.Conv import Cardano.Wallet.WalletLayer.Kernel.Transactions (toTransaction) import qualified Test.Spec.Addresses as Addresses import Test.Spec.CoinSelection.Generators (InitialBalance (..), - Pay (..), genPayeeWithNM, genUtxoWithAtLeast) + Pay (..), genUtxoWithAtLeast) import qualified Test.Spec.Fixture as Fixture import qualified Test.Spec.NewPayment as NewPayment import Test.Spec.TxMetaStorage (Isomorphic (..), genMeta) @@ -146,62 +144,6 @@ prepareFixtures nm initialBalance = do , fixturePw = pw } -prepareUTxoFixtures :: NetworkMagic - -> [Word64] - -> Fixture.GenActiveWalletFixture Fix -prepareUTxoFixtures nm coins = do - let (_, esk) = safeDeterministicKeyGen (B.pack $ replicate 32 0x42) mempty - let newRootId = eskToHdRootId nm esk - newRoot <- initHdRoot <$> pure newRootId - <*> pure (WalletName "A wallet") - <*> pure NoSpendingPassword - <*> pure AssuranceLevelNormal - <*> (InDb <$> pick arbitrary) - - newAccountId <- HdAccountId newRootId <$> deriveIndex (pick . choose) HdAccountIx HardDerivation - utxo <- foldlM (\acc coin -> do - newIndex <- deriveIndex (pick . choose) HdAddressIx HardDerivation - txIn <- pick $ Core.TxInUtxo <$> arbitrary <*> arbitrary - let Just (addr, _) = deriveLvl2KeyPair nm - (IsBootstrapEraAddr True) - (ShouldCheckPassphrase True) - mempty - esk - (newAccountId ^. hdAccountIdIx . to getHdAccountIx) - (getHdAddressIx newIndex) - return $ M.insert txIn (TxOutAux (TxOut addr coin)) acc - ) M.empty (mkCoin <$> coins) - return $ \keystore aw -> do - let pw = Kernel.walletPassive aw - Keystore.insert newRootId esk keystore - let accounts = Kernel.prefilterUtxo newRootId esk utxo - hdAccountId = Kernel.defaultHdAccountId newRootId - hdAddress = Kernel.defaultHdAddress nm esk emptyPassphrase newRootId - - void $ liftIO $ update (pw ^. wallets) (CreateHdWallet newRoot hdAccountId hdAddress accounts) - return $ Fix { - fixtureHdRootId = newRootId - , fixtureHdRoot = newRoot - , fixtureAccountId = newAccountId - , fixtureESK = esk - , fixtureUtxo = utxo - } - -withUtxosFixture :: MonadIO m - => ProtocolMagic - -> [Word64] - -> ( Keystore.Keystore - -> WalletLayer.ActiveWalletLayer m - -> Kernel.ActiveWallet - -> Fix - -> IO a - ) - -> PropertyM IO a -withUtxosFixture pm coins cc = - Fixture.withActiveWalletFixture pm (prepareUTxoFixtures nm coins) cc - where - nm = makeNetworkMagic pm - withFixture :: MonadIO m => ProtocolMagic -> InitialBalance @@ -263,75 +205,9 @@ getAccountBalanceNow pw Fix{..} = do constantFee :: Word64 -> Int -> NonEmpty Coin -> Coin constantFee c _ _ = mkCoin c -constantFeeCheck :: Word64 -> Coin -> Bool -constantFeeCheck c c' = mkCoin c == c' - spec :: Spec spec = do describe "GetTransactions" $ do - prop "utxo fixture creates the correct balance" $ withMaxSuccess 5 $ - monadicIO $ do - pm <- pick arbitrary - withUtxosFixture @IO pm [1,2,3] $ \_keystore _activeLayer aw f@Fix{..} -> do - let pw = Kernel.walletPassive aw - balance <- getAccountBalanceNow pw f - balance `shouldBe` 6 - - prop "sanity tests checks" $ withMaxSuccess 5 $ - monadicIO $ do - pm <- pick arbitrary - Fixture.withPassiveWalletFixture @IO pm (return $ \_ -> return ()) $ \_ _ pw _ -> do - policy <- Node.getFeePolicy (pw ^. Kernel.walletNode) - let checker = Kernel.cardanoFeeSanity policy . mkCoin - checker 100 `shouldBe` False - checker 155380 `shouldBe` False - checker 155381 `shouldBe` True - checker 213345 `shouldBe` True - checker (2 * 155381) `shouldBe` True - checker (2 * 155381 + 1) `shouldBe` False - checker 755381 `shouldBe` False - - prop "pay works normally for coin selection with additional utxos and changes" $ withMaxSuccess 5 $ - monadicIO $ do - pm <- pick arbitrary - let nm = makeNetworkMagic pm - distr <- fmap (\(TxOut addr coin) -> V1.PaymentDistribution (V1.WalAddress addr) (V1.WalletCoin coin)) - <$> pick (genPayeeWithNM nm mempty (PayLovelace 100)) - withUtxosFixture @IO pm [300, 400, 500, 600, 5000000] $ \_keystore _activeLayer aw f@Fix{..} -> do - let pw = Kernel.walletPassive aw - -- get the balance before the payment - coinsBefore <- getAccountBalanceNow pw f - -- do the payment - let src = V1.PaymentSource (Kernel.Conv.toRootId fixtureHdRootId) - (V1.unsafeMkAccountIndex $ getHdAccountIx $ fixtureAccountId ^. hdAccountIdIx) - payment = V1.Payment src distr Nothing Nothing - Right _ <- Active.pay aw emptyPassphrase PreferGrouping SenderPaysFee payment - -- get the balance after the payment. - coinsAfter <- getAccountBalanceNow pw f - -- sanity check. - policy <- Node.getFeePolicy (pw ^. Kernel.walletNode) - let checker = Kernel.cardanoFeeSanity policy . mkCoin - -- payment is very small so difference is almost equa to fees. - coinsBefore - coinsAfter `shouldSatisfy` checker - - prop "estimateFees looks sane for coin selection with additional utxos and changes" $ withMaxSuccess 5 $ - monadicIO $ do - pm <- pick arbitrary - let nm = makeNetworkMagic pm - distr <- fmap (\(TxOut addr coin) -> V1.PaymentDistribution (V1.WalAddress addr) (V1.WalletCoin coin)) - <$> pick (genPayeeWithNM nm mempty (PayLovelace 100)) - withUtxosFixture @IO pm [300, 400, 500, 600, 5000000] $ \_keystore _activeLayer aw Fix{..} -> do - let pw = Kernel.walletPassive aw - -- do the payment - let src = V1.PaymentSource (Kernel.Conv.toRootId fixtureHdRootId) - (V1.unsafeMkAccountIndex $ getHdAccountIx $ fixtureAccountId ^. hdAccountIdIx) - payment = V1.Payment src distr Nothing Nothing - Right c <- Active.estimateFees aw PreferGrouping SenderPaysFee payment - -- sanity check. - policy <- Node.getFeePolicy (pw ^. Kernel.walletNode) - let checker = Kernel.cardanoFeeSanity policy - c `shouldSatisfy` checker - prop "scenario: Layer.CreateAddress -> TxMeta.putTxMeta -> Layer.getTransactions works properly." $ withMaxSuccess 5 $ monadicIO $ do testMetaSTB <- pick genMeta @@ -593,7 +469,7 @@ spec = do payAux :: Kernel.ActiveWallet -> HdAccountId -> NonEmpty (Address, Coin) -> Word64 -> IO (Core.Tx, TxMeta) payAux aw hdAccountId payees fees = do - let opts = (newOptions (constantFee fees) (constantFeeCheck fees)) { + let opts = (newOptions (constantFee fees)) { csoExpenseRegulation = SenderPaysFee , csoInputGrouping = IgnoreGrouping } diff --git a/test/unit/Test/Spec/NewPayment.hs b/test/unit/Test/Spec/NewPayment.hs index cf81422ce..00a7a7774 100644 --- a/test/unit/Test/Spec/NewPayment.hs +++ b/test/unit/Test/Spec/NewPayment.hs @@ -148,9 +148,6 @@ withFixture pm initialBalance toPay cc = constantFee :: Int -> NonEmpty Coin -> Coin constantFee _ _ = mkCoin 10 -constantFeeCheck :: Coin -> Bool -constantFeeCheck c = c == mkCoin 10 - -- | Helper function to facilitate payments via the Layer or Servant. withPayment :: MonadIO n => ProtocolMagic @@ -200,7 +197,7 @@ spec = describe "NewPayment" $ do pm <- pick arbitrary withFixture @IO pm (InitialADA 10000) (PayLovelace 10) $ \_ _ aw Fixture{..} -> do policy <- Node.getFeePolicy (Kernel.walletPassive aw ^. Kernel.walletNode) - let opts = (newOptions (Kernel.cardanoFee policy) (Kernel.cardanoFeeSanity policy)) { + let opts = (newOptions (Kernel.cardanoFee policy)) { csoExpenseRegulation = SenderPaysFee , csoInputGrouping = IgnoreGrouping } @@ -217,7 +214,7 @@ spec = describe "NewPayment" $ do pm <- pick arbitrary withFixture @IO pm (InitialADA 10000) (PayADA 1) $ \_ _ aw Fixture{..} -> do policy <- Node.getFeePolicy (Kernel.walletPassive aw ^. Kernel.walletNode) - let opts = (newOptions (Kernel.cardanoFee policy) (Kernel.cardanoFeeSanity policy)) { + let opts = (newOptions (Kernel.cardanoFee policy)) { csoExpenseRegulation = ReceiverPaysFee , csoInputGrouping = IgnoreGrouping } @@ -260,7 +257,7 @@ spec = describe "NewPayment" $ do monadicIO $ do pm <- pick arbitrary withFixture @IO pm (InitialADA 10000) (PayADA 1) $ \_ _ aw Fixture{..} -> do - let opts = (newOptions constantFee constantFeeCheck) { + let opts = (newOptions constantFee) { csoExpenseRegulation = SenderPaysFee , csoInputGrouping = IgnoreGrouping } @@ -279,7 +276,7 @@ spec = describe "NewPayment" $ do monadicIO $ do pm <- pick arbitrary withFixture @IO pm (InitialADA 10000) (PayADA 1) $ \_ _ aw Fixture{..} -> do - let opts = (newOptions constantFee constantFeeCheck) { + let opts = (newOptions constantFee) { csoExpenseRegulation = SenderPaysFee , csoInputGrouping = IgnoreGrouping } @@ -298,7 +295,7 @@ spec = describe "NewPayment" $ do pm <- pick arbitrary withFixture @IO pm (InitialADA 10000) (PayADA 1) $ \_ _ aw Fixture{..} -> do policy <- Node.getFeePolicy (Kernel.walletPassive aw ^. Kernel.walletNode) - let opts = (newOptions (Kernel.cardanoFee policy) (Kernel.cardanoFeeSanity policy)) { + let opts = (newOptions (Kernel.cardanoFee policy)) { csoExpenseRegulation = SenderPaysFee , csoInputGrouping = IgnoreGrouping }