From 645e14fd09d318a02aaf6ce19c04afbbc2277cd8 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Fri, 31 Jul 2020 15:30:41 +0200 Subject: [PATCH 1/4] additional tests for redeem rewards --- .../Test/Integration/Framework/TestData.hs | 6 + .../Scenario/API/Shelley/Transactions.hs | 136 ++++++++++++++---- 2 files changed, 116 insertions(+), 26 deletions(-) diff --git a/lib/core-integration/src/Test/Integration/Framework/TestData.hs b/lib/core-integration/src/Test/Integration/Framework/TestData.hs index 40a1eb6c4ce..e5cdedc8738 100644 --- a/lib/core-integration/src/Test/Integration/Framework/TestData.hs +++ b/lib/core-integration/src/Test/Integration/Framework/TestData.hs @@ -77,6 +77,7 @@ module Test.Integration.Framework.TestData , errMsg400MinWithdrawalWrong , errMsg403WithdrawalNotWorth , errMsg403NotAShelleyWallet + , errMsg403InputsDepleted ) where import Prelude @@ -251,6 +252,11 @@ versionLine = "Running as v" <> pack (showFullVersion version gitRevision) --- Error messages --- +errMsg403InputsDepleted :: String +errMsg403InputsDepleted = "I cannot select enough UTxO from your wallet to construct\ + \ an adequate transaction. Try sending a smaller amount or increasing the number\ + \ of available UTxO." + errMsg409WalletExists :: String -> String errMsg409WalletExists walId = "This operation would yield a wallet with the following\ \ id: " ++ walId ++ " However, I already know of a wallet with this id." diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs index ec2bba09294..20aef1575c7 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs @@ -111,6 +111,7 @@ import Test.Integration.Framework.TestData ( errMsg400MinWithdrawalWrong , errMsg400StartTimeLaterThanEndTime , errMsg403Fee + , errMsg403InputsDepleted , errMsg403NoPendingAnymore , errMsg403NotAShelleyWallet , errMsg403NotEnoughMoney @@ -1255,47 +1256,60 @@ spec = do , expectErrorMessage (errMsg404NoWallet wid) ] - it "SHELLEY_TX_REDEEM_01 - Can redeem rewards from self" $ \ctx -> do + -- Reward wallets have 100k ADA UTxO and 1M ADA rewards, + -- fixtureWalletsWith have 100k ADA UTxO too + -- Verifying withdrawals for some boundary values + let rewardsMatrix = [ ("redeem sending exactly rewards", oneMillionAda) + , ("redeem sending exactly UTxO", 100 * oneThousandAda) + , ("redeem sending more than UTxO", 100 * oneThousandAda + oneAda) + , ("redeem sending less than UTxO", 100 * oneThousandAda - oneAda) + , ("redeem sending more than rewards", oneMillionAda + oneAda) + , ("redeem sending less than rewards", oneMillionAda - oneAda) + ] + describe "SHELLEY_TX_REDEEM_01 - Can redeem rewards from self" $ do + + forM_ rewardsMatrix $ \(name, amt) -> it name $ \ctx -> do (wSrc,_) <- rewardWallet ctx addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSrc + print wSrc let payload = Json [json|{ - "withdrawal": "self", - "payments": [{ - "address": #{addr}, - "amount": { "quantity": 1, "unit": "lovelace" } - }], - "passphrase": #{fixturePassphrase} - }|] + "withdrawal": "self", + "payments": [{ + "address": #{addr}, + "amount": { "quantity": #{amt}, "unit": "lovelace" } + }], + "passphrase": #{fixturePassphrase} + }|] rTx <- request @(ApiTransaction n) ctx - (Link.createTransaction @'Shelley wSrc) Default payload + (Link.createTransaction @'Shelley wSrc) Default payload verify rTx - [ expectResponseCode HTTP.status202 - , expectField #withdrawals - (`shouldSatisfy` (not . null)) - ] + [ expectResponseCode HTTP.status202 + , expectField #withdrawals + (`shouldSatisfy` (not . null)) + ] eventually "rewards are transferred from self to self" $ do - rW <- request @ApiWallet ctx - (Link.getWallet @'Shelley wSrc) Default payload - print rW - verify rW - [ expectField (#balance . #getApiT . #available) - (.> (wSrc ^. #balance . #getApiT . #available)) - , expectField (#balance . #getApiT . #reward) - (`shouldBe` Quantity 0) - ] - - it "SHELLEY_TX_REDEEM_02 - Can redeem rewards from other" $ \ctx -> do + rW <- request @ApiWallet ctx + (Link.getWallet @'Shelley wSrc) Default payload + verify rW + [ expectField (#balance . #getApiT . #available) + (.> (wSrc ^. #balance . #getApiT . #available)) + , expectField (#balance . #getApiT . #reward) + (`shouldBe` Quantity 0) + ] + + describe "SHELLEY_TX_REDEEM_02 - Can redeem rewards from other" $ do + forM_ rewardsMatrix $ \(name, amt) -> it name $ \ctx -> do (wOther, mw) <- rewardWallet ctx - wSelf <- fixtureWallet ctx + wSelf <- fixtureWalletWith @n ctx [100 * oneThousandAda] addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf let payload = Json [json|{ "withdrawal": #{mnemonicToText mw}, "payments": [{ "address": #{addr}, - "amount": { "quantity": 1, "unit": "lovelace" } + "amount": { "quantity": #{amt}, "unit": "lovelace" } }], "passphrase": #{fixturePassphrase} }|] @@ -1416,6 +1430,67 @@ spec = do [ expectResponseCode HTTP.status403 , expectErrorMessage errMsg403NotAShelleyWallet ] + + it "SHELLEY_TX_REDEEM_06a - Can't redeem rewards if utxo = 0 from other" $ \ctx -> do + (_, mw) <- rewardWallet ctx + wSelf <- emptyWallet ctx + addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf + + let payload = Json [json|{ + "withdrawal": #{mnemonicToText mw}, + "payments": [{ + "address": #{addr}, + "amount": { "quantity": 1000000, "unit": "lovelace" } + }], + "passphrase": #{fixturePassphrase} + }|] + + -- Try withdrawing when no UTxO on a wallet + rTx <- request @(ApiTransaction n) ctx + (Link.createTransaction @'Shelley wSelf) Default payload + verify rTx + [ expectResponseCode HTTP.status403 + , expectErrorMessage errMsg403InputsDepleted + ] + + it "SHELLEY_TX_REDEEM_06b - Can't redeem rewards if utxo = 0 from self" $ \ctx -> do + (wRewards, mw) <- rewardWallet ctx + wOther <- emptyWallet ctx + + -- migrate all utxo from rewards wallet + addr:_ <- fmap (view #id) <$> listAddresses @n ctx wOther + let payloadMigr = Json [json|{ + "passphrase": #{fixturePassphrase}, + "addresses": [#{addr}] + }|] + + let ep = Link.migrateWallet @'Shelley wRewards + rM <- request @[ApiTransaction n] ctx ep Default payloadMigr + expectResponseCode HTTP.status202 rM + + eventually "No UTxO is on rewards wallet" $ do + rWOther <- request @ApiWallet ctx + (Link.getWallet @'Shelley wRewards) Default Empty + verify rWOther + [ expectField (#balance . #getApiT . #available) + (`shouldBe` Quantity 0) + ] + + -- Try withdrawing when no UTxO on a wallet + let payload = Json [json|{ + "withdrawal": #{mnemonicToText mw}, + "payments": [{ + "address": #{addr}, + "amount": { "quantity": #{oneAda}, "unit": "lovelace" } + }], + "passphrase": #{fixturePassphrase} + }|] + rTx <- request @(ApiTransaction n) ctx + (Link.createTransaction @'Shelley wRewards) Default payload + verify rTx + [ expectResponseCode HTTP.status403 + , expectErrorMessage errMsg403InputsDepleted + ] where txDeleteNotExistsingTxIdTest eWallet resource = it resource $ \ctx -> do @@ -1506,3 +1581,12 @@ spec = do plusDelta, minusDelta :: UTCTime -> UTCTime plusDelta = addUTCTime (toEnum 1000000000) minusDelta = addUTCTime (toEnum (-1000000000)) + + oneAda :: Natural + oneAda = 1_000_000 + + oneThousandAda :: Natural + oneThousandAda = 1_000 * oneAda + + oneMillionAda :: Natural + oneMillionAda = 1_000 * oneThousandAda From 7d1e13a59900cbf9d88b3b1de1ca2f8834552670 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Fri, 31 Jul 2020 16:31:56 +0200 Subject: [PATCH 2/4] Add more reward wallets --- .../src/Test/Integration/Faucet.hs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/lib/core-integration/src/Test/Integration/Faucet.hs b/lib/core-integration/src/Test/Integration/Faucet.hs index 32c9694415e..e1b55ffcbc8 100644 --- a/lib/core-integration/src/Test/Integration/Faucet.hs +++ b/lib/core-integration/src/Test/Integration/Faucet.hs @@ -1402,6 +1402,66 @@ mirMnemonics = unsafeMkMnemonic <$> ,"crumble","staff","ten","potato","laptop","off" ,"action","chuckle","medal","bread","blind","peanut","horse" ] + , ["version", "reason", "distance", "cargo", "fancy", "anxiety" + , "renew", "grace", "jealous", "brother", "live", "wheel", "lava" + , "exercise", "tragic", "foster", "office", "govern", "title", "inquiry" + , "fit", "twist", "powder", "subway" + ] + , ["dentist", "diagram", "eternal", "tuition", "leave", "library" + , "coffee", "power", "brief", "syrup", "six", "donkey", "inner" + , "valley", "carpet", "drop", "labor", "observe", "decade", "okay" + , "play", "stable", "wagon", "blind" + ] + , ["gallery", "approve", "trophy", "side", "lawn", "soldier", "gentle" + , "wire", "enact", "illegal", "chef", "sentence", "nation", "beach" + , "glimpse", "term", "unlock", "chalk", "monitor", "panel", "famous" + , "alert", "matter", "female" + ] + , ["reason", "grow", "memory", "spray", "gossip", "middle", "grocery" + , "lesson", "poem", "cannon", "dilemma", "elegant", "point", "east" + , "evil", "sauce", "exile", "typical", "cram", "ride", "remove" + , "phrase", "lecture", "degree" + ] + , ["else", "normal", "rotate", "flash", "nose", "east", "weasel", "hammer" + , "priority", "pig", "seven", "mention", "model", "profit", "oxygen" + , "tomato", "foot", "age", "glad", "jazz", "retire", "okay" + , "village", "crater" + ] + , ["negative", "purpose", "outdoor", "slush", "beach", "radar" + , "canoe", "course", "donkey", "earn", "bone", "bar", "frost" + , "manual", "inhale", "humor", "this", "reflect", "learn", "special" + , "horse", "course", "start", "debris" + ] + , ["wealth", "float", "steak", "oil", "rare", "gift", "put", "stool" + , "vault", "give", "gorilla", "indicate", "inside", "comfort" + , "lawn", "assault", "urban", "ancient", "identify", "depth", "injury" + , "solution", "warrior", "exercise" + ] + , ["syrup", "shield", "chef", "child", "dwarf", "frog", "hire" + , "script", "suit", "jelly", "point", "degree", "brisk", "oak" + , "minute", "absurd", "refuse", "iron", "forum", "effort" + , "regret", "kidney", "drama", "still" + ] + , ["moral", "stem", "myth", "awesome", "crime", "slush", "try" + , "wood", "coconut", "erase", "patient", "trigger", "crew", "solve" + , "element", "million", "nasty", "raven", "innocent", "happy" + , "behind", "ankle", "trick", "museum" + ] + , ["wish", "peasant", "void", "nature", "position", "dial", "grant" + , "recycle", "raw", "melody", "equal", "stool", "parent", "category" + , "limb", "apart", "indoor", "six", "float", "happy", "insane" + , "guide", "burst", "other" + ] + , ["fury", "possible", "relax", "eyebrow", "supply", "embrace" + , "decide", "wolf", "boring", "blossom", "credit", "drill", "theme" + , "skate", "focus", "trick", "field", "wrist", "update", "hawk" + , "renew", "motor", "learn", "cook" + ] + , ["gas", "woman", "grief", "story", "evidence", "actor", "filter" + , "lion", "pilot", "illness", "abuse", "palm", "hurry", "mail", "equal" + , "pen", "element", "nut", "lobster", "enemy", "base", "steel" + , "aisle", "lamp" + ] ] -- | Generate faucets addresses and mnemonics to a file. From 14f245d2839c024ae655957957c01c60bf8290a2 Mon Sep 17 00:00:00 2001 From: Piotr Stachyra Date: Fri, 31 Jul 2020 17:01:43 +0200 Subject: [PATCH 3/4] Additional cases to cover not enough fee and not enough money on withdrawals --- .../Scenario/API/Shelley/Transactions.hs | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs index 20aef1575c7..e5279514f32 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs @@ -115,6 +115,7 @@ import Test.Integration.Framework.TestData , errMsg403NoPendingAnymore , errMsg403NotAShelleyWallet , errMsg403NotEnoughMoney + , errMsg403NotEnoughMoney_ , errMsg403WithdrawalNotWorth , errMsg403WrongPass , errMsg404CannotFindTx @@ -1271,7 +1272,6 @@ spec = do forM_ rewardsMatrix $ \(name, amt) -> it name $ \ctx -> do (wSrc,_) <- rewardWallet ctx addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSrc - print wSrc let payload = Json [json|{ "withdrawal": "self", @@ -1491,6 +1491,52 @@ spec = do [ expectResponseCode HTTP.status403 , expectErrorMessage errMsg403InputsDepleted ] + + it "SHELLEY_TX_REDEEM_07a - Can't redeem rewards if amt = utxo + reward" $ \ctx -> do + (_, mw) <- rewardWallet ctx + wSelf <- fixtureWalletWith @n ctx [oneThousandAda] + addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf + let amt = oneThousandAda + oneMillionAda + + let payload = Json [json|{ + "withdrawal": #{mnemonicToText mw}, + "payments": [{ + "address": #{addr}, + "amount": { "quantity": #{amt}, "unit": "lovelace" } + }], + "passphrase": #{fixturePassphrase} + }|] + + -- Try withdrawing when cannot cover fee + rTx <- request @(ApiTransaction n) ctx + (Link.createTransaction @'Shelley wSelf) Default payload + verify rTx + [ expectResponseCode HTTP.status403 + , expectErrorMessage errMsg403Fee + ] + + it "SHELLEY_TX_REDEEM_07b - Can't redeem rewards if amt > utxo + reward" $ \ctx -> do + (_, mw) <- rewardWallet ctx + wSelf <- fixtureWalletWith @n ctx [oneThousandAda] + addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf + let amt = oneThousandAda + oneMillionAda + oneAda + + let payload = Json [json|{ + "withdrawal": #{mnemonicToText mw}, + "payments": [{ + "address": #{addr}, + "amount": { "quantity": #{amt}, "unit": "lovelace" } + }], + "passphrase": #{fixturePassphrase} + }|] + + -- Try withdrawing when no not enough money + rTx <- request @(ApiTransaction n) ctx + (Link.createTransaction @'Shelley wSelf) Default payload + verify rTx + [ expectResponseCode HTTP.status403 + , expectErrorMessage errMsg403NotEnoughMoney_ + ] where txDeleteNotExistsingTxIdTest eWallet resource = it resource $ \ctx -> do From 457c5fb111e9ecc4bb428b00980cfc4ca90e2780 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Wed, 5 Aug 2020 17:37:13 +0200 Subject: [PATCH 4/4] reverting and cleaning --- .../Scenario/API/Shelley/Transactions.hs | 68 ++++++++----------- 1 file changed, 28 insertions(+), 40 deletions(-) diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs index e5279514f32..145cc7cf37f 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/Transactions.hs @@ -1257,59 +1257,47 @@ spec = do , expectErrorMessage (errMsg404NoWallet wid) ] - -- Reward wallets have 100k ADA UTxO and 1M ADA rewards, - -- fixtureWalletsWith have 100k ADA UTxO too - -- Verifying withdrawals for some boundary values - let rewardsMatrix = [ ("redeem sending exactly rewards", oneMillionAda) - , ("redeem sending exactly UTxO", 100 * oneThousandAda) - , ("redeem sending more than UTxO", 100 * oneThousandAda + oneAda) - , ("redeem sending less than UTxO", 100 * oneThousandAda - oneAda) - , ("redeem sending more than rewards", oneMillionAda + oneAda) - , ("redeem sending less than rewards", oneMillionAda - oneAda) - ] - describe "SHELLEY_TX_REDEEM_01 - Can redeem rewards from self" $ do - - forM_ rewardsMatrix $ \(name, amt) -> it name $ \ctx -> do + it "SHELLEY_TX_REDEEM_01 - Can redeem rewards from self" $ \ctx -> do (wSrc,_) <- rewardWallet ctx addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSrc let payload = Json [json|{ - "withdrawal": "self", - "payments": [{ - "address": #{addr}, - "amount": { "quantity": #{amt}, "unit": "lovelace" } - }], - "passphrase": #{fixturePassphrase} - }|] + "withdrawal": "self", + "payments": [{ + "address": #{addr}, + "amount": { "quantity": 1, "unit": "lovelace" } + }], + "passphrase": #{fixturePassphrase} + }|] rTx <- request @(ApiTransaction n) ctx - (Link.createTransaction @'Shelley wSrc) Default payload + (Link.createTransaction @'Shelley wSrc) Default payload verify rTx - [ expectResponseCode HTTP.status202 - , expectField #withdrawals - (`shouldSatisfy` (not . null)) - ] + [ expectResponseCode HTTP.status202 + , expectField #withdrawals + (`shouldSatisfy` (not . null)) + ] eventually "rewards are transferred from self to self" $ do - rW <- request @ApiWallet ctx - (Link.getWallet @'Shelley wSrc) Default payload - verify rW - [ expectField (#balance . #getApiT . #available) - (.> (wSrc ^. #balance . #getApiT . #available)) - , expectField (#balance . #getApiT . #reward) - (`shouldBe` Quantity 0) - ] - - describe "SHELLEY_TX_REDEEM_02 - Can redeem rewards from other" $ do - forM_ rewardsMatrix $ \(name, amt) -> it name $ \ctx -> do + rW <- request @ApiWallet ctx + (Link.getWallet @'Shelley wSrc) Default payload + print rW + verify rW + [ expectField (#balance . #getApiT . #available) + (.> (wSrc ^. #balance . #getApiT . #available)) + , expectField (#balance . #getApiT . #reward) + (`shouldBe` Quantity 0) + ] + + it "SHELLEY_TX_REDEEM_02 - Can redeem rewards from other" $ \ctx -> do (wOther, mw) <- rewardWallet ctx - wSelf <- fixtureWalletWith @n ctx [100 * oneThousandAda] + wSelf <- fixtureWallet ctx addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf let payload = Json [json|{ "withdrawal": #{mnemonicToText mw}, "payments": [{ "address": #{addr}, - "amount": { "quantity": #{amt}, "unit": "lovelace" } + "amount": { "quantity": 1, "unit": "lovelace" } }], "passphrase": #{fixturePassphrase} }|] @@ -1492,7 +1480,7 @@ spec = do , expectErrorMessage errMsg403InputsDepleted ] - it "SHELLEY_TX_REDEEM_07a - Can't redeem rewards if amt = utxo + reward" $ \ctx -> do + it "SHELLEY_TX_REDEEM_07a - Can't redeem rewards if cannot cover fee" $ \ctx -> do (_, mw) <- rewardWallet ctx wSelf <- fixtureWalletWith @n ctx [oneThousandAda] addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf @@ -1515,7 +1503,7 @@ spec = do , expectErrorMessage errMsg403Fee ] - it "SHELLEY_TX_REDEEM_07b - Can't redeem rewards if amt > utxo + reward" $ \ctx -> do + it "SHELLEY_TX_REDEEM_07b - Can't redeem rewards if not enough money" $ \ctx -> do (_, mw) <- rewardWallet ctx wSelf <- fixtureWalletWith @n ctx [oneThousandAda] addr:_ <- fmap (view #id) <$> listAddresses @n ctx wSelf