diff --git a/.gitignore b/.gitignore index 2f63ca54907..f02fd512172 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,4 @@ lib/shelley/test/data/balanceTx/**/actual ### Docs build /_build/ +.vscode/settings.json diff --git a/docs/contributing/Testing.md b/docs/contributing/Testing.md index 243d4a78e24..80855bf248c 100644 --- a/docs/contributing/Testing.md +++ b/docs/contributing/Testing.md @@ -175,6 +175,26 @@ The default log level for log files is Info. Only Error level logs are shown on stdout during test execution. To change this, set the `*_MIN_SEVERITY` variables shown above. +#### Common Failures and Resolution + +##### No More Wallets + +If your test fails with something like: + +``` +user error (No more faucet wallet available in MVar!) +``` + +Generate more wallet mnemonics and populate the appropriate list in `lib/core-integration/src/Test/Integration/Faucet.hs`. + +Generate new mnemonics with: + +``` +nix build .#cardano-wallet +# Size may vary depending on which array you need to add to. +./result/bin/cardano-wallet recovery-phrase generate --size 15 +``` + ## Mock servers Use the `cardano-wallet:mock-token-metadata-server` executable as a diff --git a/lib/cli/src/Cardano/CLI.hs b/lib/cli/src/Cardano/CLI.hs index e34765fc124..14eba9f1118 100644 --- a/lib/cli/src/Cardano/CLI.hs +++ b/lib/cli/src/Cardano/CLI.hs @@ -149,7 +149,6 @@ import Cardano.Wallet.Api.Types , ApiPostRandomAddressData (..) , ApiT (..) , ApiTxId (ApiTxId) - , ApiTxMetadata (..) , ApiWallet , Base (Base16) , ByronWalletPostData (..) @@ -162,6 +161,8 @@ import Cardano.Wallet.Api.Types , WalletPutPassphraseData (..) , fmtAllowedWords ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..), TxMetadataWithSchema ) import Cardano.Wallet.Orphans () import Cardano.Wallet.Primitive.AddressDerivation @@ -729,6 +730,12 @@ cmdWalletGetUtxoStatistics mkClient = data TransactionFeatures = NoShelleyFeatures | ShelleyFeatures deriving (Show, Eq) +-- | Which json schema to use for output +metadataSchemaOption :: Parser TxMetadataSchema +metadataSchemaOption = flag TxMetadataDetailedSchema TxMetadataNoSchema + $ long "simple-metadata" + <> help "output metadata json in no-schema encoding" + -- | cardano-wallet transaction cmdTransaction :: ToJSON wallet @@ -760,7 +767,7 @@ data TransactionCreateArgs t = TransactionCreateArgs { _port :: Port "Wallet" , _id :: WalletId , _payments :: NonEmpty Text - , _metadata :: ApiTxMetadata + , _metadata :: Maybe TxMetadataWithSchema , _timeToLive :: Maybe (Quantity "second" NominalDiffTime) } @@ -783,7 +790,7 @@ cmdTransactionCreate isShelley mkTxClient mkWalletClient = <$> portOption <*> walletIdArgument <*> fmap NE.fromList (some paymentOption) - <*> whenShelley (ApiTxMetadata Nothing) metadataOption isShelley + <*> whenShelley Nothing metadataOption isShelley <*> whenShelley Nothing timeToLiveOption isShelley exec (TransactionCreateArgs wPort wId wAddressAmounts md ttl) = do wPayments <- either (fail . getTextDecodingError) pure $ @@ -819,7 +826,7 @@ cmdTransactionFees isShelley mkTxClient mkWalletClient = <$> portOption <*> walletIdArgument <*> fmap NE.fromList (some paymentOption) - <*> whenShelley (ApiTxMetadata Nothing) metadataOption isShelley + <*> whenShelley Nothing metadataOption isShelley <*> whenShelley Nothing timeToLiveOption isShelley exec (TransactionCreateArgs wPort wId wAddressAmounts md ttl) = do wPayments <- either (fail . getTextDecodingError) pure $ @@ -845,6 +852,7 @@ data TransactionListArgs = TransactionListArgs , _timeRangeStart :: Maybe Iso8601Time , _timeRangeEnd :: Maybe Iso8601Time , _sortOrder :: Maybe SortOrder + , _schema :: TxMetadataSchema } cmdTransactionList @@ -860,13 +868,17 @@ cmdTransactionList mkTxClient = <*> optional timeRangeStartOption <*> optional timeRangeEndOption <*> optional sortOrderOption - exec (TransactionListArgs wPort wId mTimeRangeStart mTimeRangeEnd mOrder) = + <*> metadataSchemaOption + exec + (TransactionListArgs + wPort wId mTimeRangeStart mTimeRangeEnd mOrder metadataSchema) = runClient wPort Aeson.encodePretty $ listTransactions mkTxClient (ApiT wId) mTimeRangeStart mTimeRangeEnd (ApiT <$> mOrder) + (metadataSchema == TxMetadataNoSchema) -- | Arguments for 'transaction submit' command data TransactionSubmitArgs = TransactionSubmitArgs @@ -916,6 +928,7 @@ data TransactionGetArgs = TransactionGetArgs { _port :: Port "Wallet" , _wid :: WalletId , _txid :: TxId + , _schema :: TxMetadataSchema } cmdTransactionGet @@ -929,10 +942,12 @@ cmdTransactionGet mkClient = <$> portOption <*> walletIdArgument <*> transactionIdArgument - exec (TransactionGetArgs wPort wId txId) = do + <*> metadataSchemaOption + exec (TransactionGetArgs wPort wId txId metadataSchema ) = do runClient wPort Aeson.encodePretty $ getTransaction mkClient (ApiT wId) (ApiTxId $ ApiT $ getTxId txId) + metadataSchema {------------------------------------------------------------------------------- Commands - 'address' @@ -1435,16 +1450,16 @@ transactionSubmitPayloadArgument = argumentT $ mempty -- | [--metadata=JSON] -- -- Note: we decode the JSON just so that we can validate more client-side. -metadataOption :: Parser ApiTxMetadata +metadataOption :: Parser (Maybe TxMetadataWithSchema) metadataOption = option txMetadataReader $ mempty <> long "metadata" <> metavar "JSON" - <> value (ApiTxMetadata Nothing) + <> value Nothing <> help ("Application-specific transaction metadata as a JSON object. " <> "The value must match the schema defined in the " <> "cardano-wallet OpenAPI specification.") -txMetadataReader :: ReadM ApiTxMetadata +txMetadataReader :: ReadM (Maybe TxMetadataWithSchema) txMetadataReader = eitherReader (Aeson.eitherDecode' . BL8.pack) -- | [--ttl=DURATION] diff --git a/lib/cli/test/data/Cardano/CLISpec/transaction get --help b/lib/cli/test/data/Cardano/CLISpec/transaction get --help index f6616823e7b..36cab7120ca 100644 --- a/lib/cli/test/data/Cardano/CLISpec/transaction get --help +++ b/lib/cli/test/data/Cardano/CLISpec/transaction get --help @@ -1,7 +1,10 @@ -Usage: transaction get [--port INT] WALLET_ID TRANSACTION_ID +Usage: transaction get [--port INT] WALLET_ID TRANSACTION_ID + [--simple-metadata] Get a transaction with specified id. Available options: -h,--help Show this help text --port INT port used for serving the wallet API. (default: 8090) + --simple-metadata output metadata json in no-schema + encoding diff --git a/lib/cli/test/data/Cardano/CLISpec/transaction list --help b/lib/cli/test/data/Cardano/CLISpec/transaction list --help index 894cf70feba..00775354138 100644 --- a/lib/cli/test/data/Cardano/CLISpec/transaction list --help +++ b/lib/cli/test/data/Cardano/CLISpec/transaction list --help @@ -1,5 +1,6 @@ Usage: transaction list [--port INT] WALLET_ID [--start TIME] - [--end TIME] [--order ORDER] + [--end TIME] [--order ORDER] + [--simple-metadata] List the transactions associated with a wallet. Available options: @@ -14,3 +15,5 @@ Available options: 2016-11-21T10:15:00Z). --order ORDER specifies a sort order, either 'ascending' or 'descending'. + --simple-metadata output metadata json in no-schema + encoding diff --git a/lib/cli/test/unit/Cardano/CLISpec.hs b/lib/cli/test/unit/Cardano/CLISpec.hs index d5f6e45b844..873381b474c 100644 --- a/lib/cli/test/unit/Cardano/CLISpec.hs +++ b/lib/cli/test/unit/Cardano/CLISpec.hs @@ -40,8 +40,8 @@ import Cardano.Wallet.Api.Client , transactionClient , walletClient ) -import Cardano.Wallet.Api.Types - ( ApiT (..), ApiTxMetadata (..) ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( detailedMetadata, noSchemaMetadata ) import Cardano.Wallet.Primitive.Types ( PoolMetadataSource ) import Cardano.Wallet.Primitive.Types.Tx @@ -255,8 +255,9 @@ spec = do describe "Tx Metadata JSON option" $ do let parse arg = execParserPure defaultPrefs (info metadataOption mempty) ["--metadata", arg] - let md = ApiT (TxMetadata (Map.singleton 42 (TxMetaText "hi"))) - let ok ex (Success res) = ex == getApiTxMetadata res + let md = detailedMetadata + (TxMetadata (Map.singleton 42 (TxMetaText "hi"))) + let ok ex (Success res) = ex == res ok _ _ = False let err (Failure _) = True err _ = False @@ -268,7 +269,28 @@ spec = do , ("invalid", "{ \"json\": true }", err) , ("null 1", "{ \"0\": null }", err) , ("null 2", "null", ok Nothing) - , ("null 3", "{ }", ok (Just (ApiT mempty))) + , ("null 3", "{ }", ok (Just (detailedMetadata mempty))) + ] + describe "Tx No-Schema Metadata JSON option" $ do + let parse arg = execParserPure + defaultPrefs (info metadataOption mempty) ["--metadata", arg] + let md = noSchemaMetadata + (TxMetadata (Map.singleton 42 (TxMetaText "hi"))) + let ok ex (Success res) = ex == res + ok _ _ = False + let err (Failure _) = True + err _ = False + mapM_ + (\(desc, arg, tst) -> it desc (parse arg `shouldSatisfy` tst)) + [ ("valid", "{ \"42\": \"hi\" }", ok $ Just md) + , ("malformed", "testing", err) + , ("malformed trailing", "{ \"0\": \"\" } arstneio", err) + , ("invalid", "{ \"json\": true }", err) + , ("null 1", "{ \"0\": null }", err) + , ("null 2", "null", ok Nothing) + -- this is the default parsing success: + , ("null 3", "{ }", ok $ Just $ detailedMetadata mempty) + ] describe "Tx TTL option" $ do diff --git a/lib/core-integration/src/Test/Integration/Faucet.hs b/lib/core-integration/src/Test/Integration/Faucet.hs index 8356736f73f..6c258b5c301 100644 --- a/lib/core-integration/src/Test/Integration/Faucet.hs +++ b/lib/core-integration/src/Test/Integration/Faucet.hs @@ -961,6 +961,226 @@ seqMnemonics = unsafeMkMnemonic <$> , "drive", "grant", "tumble", "catalog", "plastic" , "hello", "stomach", "utility", "safe", "cradle" ] + , [ "garage" , "warm" , "blouse" , "peanut" , "pair" + , "trend" , "fine" , "hybrid" , "risk" , "mail" + , "fan" , "damage" , "push" , "shrug" , "boost" + ] + , [ "sleep" , "sword" , "chaos" , "taste" , "emotion" + , "achieve" , "prosper" , "scrap" , "delay" , "wing" + , "ketchup" , "neglect" , "foam" , "muffin" , "brisk" + ] + , [ "lawn" , "bring" , "diamond" , "network" , "remember" + , "slight" , "auto" , "select" , "vivid" , "silent" + , "letter" , "usual" , "pen" , "yellow" , "equip" + ] + , [ "coast" , "under" , "relax" , "vault" , "hip" + , "plastic" , "boat" , "dice" , "genuine" , "inject" + , "have" , "swim" , "miss" , "expire" , "tornado" + ] + , [ "damp" , "fabric" , "witness" , "height" , "hurdle" + , "office" , "midnight" , "gaze" , "engage" , "eagle" + , "into" , "cup" , "about" , "velvet" , "acquire" + ] + , [ "group", "farm", "ankle", "tobacco", "analyst" + , "stairs", "suspect", "average", "toward", "sweet" + , "someone", "auction", "large", "prevent", "later" + ] + , [ "employ", "scrap", "dilemma", "begin", "news" + , "grape", "nice", "office", "glass", "era" + , "maximum", "toe", "fork", "chunk", "you" + ] + , [ "east", "silly", "rhythm", "assist", "nasty" + , "toddler", "riot", "early", "talent", "height" + , "oblige", "today", "extend", "sketch", "pair" + ] + , [ "disease", "type", "hub", "enemy", "advice" + , "turkey", "sausage", "property", "grass", "tonight" + , "pipe", "expect", "express", "satisfy", "outside" + ] + , [ "demise", "illegal", "fan", "above", "cotton" + , "wool", "there", "gorilla", "receive", "blast" + , "polar", "gather", "awake", "supply", "grape" + ] + , [ "garden", "analyst", "fame", "outside", "below" + , "cherry", "journey", "mountain", "satoshi", "mind" + , "zoo", "tennis", "wedding", "cluster", "ranch" + ] + , [ "fog", "decrease", "calm", "elephant", "donor" + , "private", "slow", "guard", "cushion", "thank" + , "glare", "hawk", "total", "grass", "drama" + ] + , [ "unfold", "sad", "slim", "bind", "provide" + , "surprise", "correct", "mind", "dinner", "deposit" + , "whip", "hair", "wheel", "pill", "explain" + ] + , [ "oyster", "alone", "language", "maximum", "theory" + , "pumpkin", "brief", "coffee", "wrist", "captain" + , "differ", "once", "step", "exit", "switch" + ] + , [ "play", "west", "fire", "relief", "digital" + , "fog", "gloom", "prefer", "receive", "erase" + , "ankle", "ensure", "talent", "emerge", "exit" + ] + , [ "term", "offer", "salute", "supreme", "defense" + , "champion", "child", "tube", "pledge", "cube" + , "chapter", "friend", "margin", "weather", "wood" + ] + , [ "omit", "orphan", "pulse", "mutual", "milk" + , "cradle", "chase", "woman", "income", "heavy" + , "seven", "myself", "assault", "finger", "manual" + ] + , [ "boy", "input", "moon", "bread", "impose" + , "fix", "reason", "carry", "hill", "receive" + , "pen", "hard", "orbit", "toilet", "list" + ] + , [ "service", "price", "reward", "initial", "warrior" + , "ordinary", "always", "immense", "stumble", "wedding" + , "split", "range", "dirt", "equip", "blush" + ] + , [ "trumpet", "rescue", "firm", "carry", "fix" + , "vocal", "custom", "crash", "drive", "veteran" + , "milk", "toe", "execute", "abandon", "tourist" + ] + , [ "picture", "program", "medal", "put", "tumble" + , "typical", "ice", "medal", "wing", "inherit" + , "phrase", "control", "decorate", "kingdom", "series" + ] + , [ "enforce", "dial", "present", "bean", "mother" + , "vote", "clerk", "outside", "soda", "dog" + , "under", "tuition", "token", "silver", "exit" + ] + , [ "toss", "speak", "toddler", "slender", "bus" + , "ladder", "calm", "tank", "kitchen", "gallery" + , "result", "minor", "then", "tuna", "vintage" + ] + , [ "birth", "chef", "adjust", "funny", "galaxy" + , "squeeze", "foot", "evoke", "danger", "spot" + , "away", "pass", "wink", "armed", "weekend" + ] + , [ "broccoli", "bless", "where", "play", "ecology" + , "receive", "finish", "flight", "display", "soda" + , "adult", "cannon", "mansion", "jar", "guilt" + ] + , [ "layer", "amused", "traffic", "trim", "dose" + , "child", "carry", "crazy", "anger", "medal" + , "aerobic", "escape", "moon", "seed", "mango" + ] + , [ "inmate", "carpet", "hockey", "airport", "correct" + , "wise", "acoustic", "close", "trial", "angle" + , "fresh", "make", "select", "early", "glide" + ] + , [ "fancy", "space", "zebra", "opera", "salute" + , "ocean", "zoo", "inform", "deliver", "target" + , "flavor", "decorate", "match", "effort", "climb" + ] + , [ "give", "inject", "hire", "illegal", "danger" + , "drop", "expose", "knife", "height", "language" + , "cancel", "transfer", "course", "mirror", "puppy" + ] + , [ "today", "outdoor", "galaxy", "metal", "risk" + , "bargain", "describe", "hurdle", "dizzy", "hood" + , "huge", "document", "museum", "skill", "protect" + ] + , [ "pelican", "forest", "bulk", "happy", "thing" + , "glory", "lucky", "fitness", "inform", "search" + , "repair", "power", "moral", "degree", "series" + ] + , [ "input", "suffer", "ranch", "fringe", "coin" + , "mixture", "sausage", "jeans", "proof", "tattoo" + , "deer", "unaware", "circle", "tower", "egg" + ] + , [ "hobby", "slot", "grocery", "fetch", "bonus" + , "dumb", "also", "trend", "wire", "bonus" + , "develop", "section", "vintage", "code", "peanut" + ] + , [ "thumb", "comfort", "grocery", "carbon", "artist" + , "then", "draft", "ticket", "cannon", "hand" + , "wash", "genuine", "general", "found", "donkey" + ] + , [ "cable", "churn", "visual", "second", "grow" + , "pass", "tongue", "burden", "gorilla", "galaxy" + , "express", "humble", "muffin", "aisle", "outside" + ] + , [ "neglect", "quarter", "text", "that", "random" + , "cheese", "unfair", "symbol", "flush", "husband" + , "fold", "clinic", "alone", "bulk", "entire" + ] + , [ "fitness", "license", "settle", "mansion", "protect" + , "disease", "inspire", "uphold", "labor", "spider" + , "wheat", "lunar", "manage", "exile", "census" + ] + , [ "walk", "tray", "pudding", "nut", "craft" + , "remind", "mom", "twist", "umbrella", "voice" + , "cactus", "crush", "bicycle", "chicken", "magnet" + ] + , [ "vicious", "rose", "render", "hover", "spawn" + , "reopen", "switch", "trend", "parrot", "genius" + , "person", "giant", "dwarf", "quantum", "legend" + ] + , [ "digital", "advance", "ankle", "salad", "interest" + , "episode", "balance", "cram", "gospel", "father" + , "type", "improve", "sweet", "genuine", "siren" + ] + , [ "immune", "canoe", "caution", "property", "hole" + , "session", "labor", "cool", "total", "situate" + , "front", "that", "remove", "artwork", "win" + ] + , [ "anchor", "cake", "fiscal", "desk", "brush" + , "oak", "candy", "federal", "amount", "whip" + , "mother", "will", "mobile", "first", "suffer" + ] + , [ "issue", "test", "exclude", "report", "federal" + , "oblige", "speak", "wife", "saddle", "phrase" + , "dolphin", "shrug", "galaxy", "weekend", "trick" + ] + , [ "wagon", "question", "patch", "strong", "despair" + , "neck", "punch", "lunch", "defense", "blanket" + , "enjoy", "visit", "tip", "cliff", "someone" + ] + , [ "cube", "mutual", "panic", "evidence", "system" + , "devote", "fringe", "alpha", "call", "pig" + , "relax", "orange", "renew", "pizza", "broom" + ] + , [ "seed", "trim", "about", "blue", "suit" + , "insect", "snake", "planet", "muscle", "goat" + , "much", "unusual", "mule", "skull", "arrest" + ] + , [ "main", "leader", "memory", "vault", "six" + , "neck", "swamp", "size", "renew", "little" + , "drift", "lyrics", "weekend", "misery", "feature" + ] + , [ "birth", "tool", "figure", "brass", "twin" + , "suit", "joke", "elbow", "chronic", "oxygen" + , "bracket", "truly", "enter", "gaze", "absent" + ] + , [ "alley", "private", "else", "opera", "dizzy" + , "pen", "find", "april", "run", "recall" + , "put", "session", "chapter", "wheel", "expire" + ] + , [ "crystal", "bronze", "shove", "birth", "draw" + , "state", "wave", "name", "wrong", "ride" + , "nut", "unfold", "hospital", "donor", "alpha" + ] + , [ "system", "sausage", "mandate", "mutual", "trigger" + , "home", "volume", "frame", "false", "final" + , "service", "weekend", "broccoli", "romance", "stick" + ] + , [ "crazy", "link", "arrest", "giant", "judge" + , "cute", "input", "fall", "reveal", "still" + , "method", "method", "spy", "favorite", "infant" + ] + , [ "medal", "exit", "start", "knife", "coffee" + , "census", "crop", "cereal", "cube", "old" + , "shadow", "account", "little", "humor", "misery" + ] + , [ "letter", "cliff", "sleep", "noble", "health" + , "income", "miss", "dash", "inject", "access" + , "orange", "stem", "useful", "phone", "owner" + ] + , [ "fragile", "pluck", "dad", "torch", "garment" + , "sister", "album", "tongue", "special", "maid" + , "control", "boring", "slow", "obscure", "omit" + ] ] maMnemonics :: [Mnemonic 24] diff --git a/lib/core-integration/src/Test/Integration/Framework/DSL.hs b/lib/core-integration/src/Test/Integration/Framework/DSL.hs index 703e679ca8f..510677fdf48 100644 --- a/lib/core-integration/src/Test/Integration/Framework/DSL.hs +++ b/lib/core-integration/src/Test/Integration/Framework/DSL.hs @@ -264,6 +264,8 @@ import Cardano.Wallet.Api.Types , WalletStyle (..) , insertedAt ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema, toSimpleMetadataFlag ) import Cardano.Wallet.Compat ( (^?) ) import Cardano.Wallet.Primitive.AddressDerivation @@ -2803,11 +2805,13 @@ postTransactionFeeViaCLI ctx args = cardanoWalletCLI $ join listTransactionsViaCLI :: forall r s m. (CmdResult r, HasType (Port "wallet") s, MonadIO m) => s + -> TxMetadataSchema -> [String] -> m r -listTransactionsViaCLI ctx args = cardanoWalletCLI $ join +listTransactionsViaCLI ctx metadataSchema args = cardanoWalletCLI $ join [ ["transaction", "list"] , ["--port", show (ctx ^. typed @(Port "wallet"))] + , ["--simple-metadata" | toSimpleMetadataFlag metadataSchema] , args ] @@ -2838,10 +2842,12 @@ getTransactionViaCLI => s -> String -> String + -> TxMetadataSchema -> m r -getTransactionViaCLI ctx wid tid = cardanoWalletCLI $ join +getTransactionViaCLI ctx wid tid metadataSchema = cardanoWalletCLI $ join [ ["transaction", "get"] , ["--port", show (ctx ^. typed @(Port "wallet")), wid, tid] + , ["--simple-metadata" | toSimpleMetadataFlag metadataSchema] ] proc' :: FilePath -> [String] -> CreateProcess diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/StakePools.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/StakePools.hs index 17a70854d59..5b0e9126314 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/StakePools.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/StakePools.hs @@ -83,6 +83,8 @@ import Data.Text ( Text ) import Data.Text.Class ( showT, toText ) +import Data.Tuple.Extra + ( both ) import Numeric.Natural ( Natural ) import Test.Hspec @@ -154,8 +156,6 @@ import qualified Cardano.Wallet.Api.Link as Link import qualified Data.ByteString as BS import qualified Data.Set as Set import qualified Data.Text as T -import Data.Tuple.Extra - ( both ) import qualified Network.HTTP.Types.Status as HTTP spec :: forall n. @@ -256,7 +256,7 @@ spec = describe "SHELLEY_STAKE_POOLS" $ do [ expectResponseCode HTTP.status200 , expectField (#direction . #getApiT) (`shouldBe` Outgoing) , expectField (#status . #getApiT) (`shouldBe` InLedger) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField #metadata (`shouldBe` Nothing) , expectField #inputs $ \inputs' -> do inputs' `shouldSatisfy` all (isJust . source) ] @@ -941,7 +941,7 @@ spec = describe "SHELLEY_STAKE_POOLS" $ do [ expectResponseCode HTTP.status200 , expectField (#direction . #getApiT) (`shouldBe` Incoming) , expectField (#status . #getApiT) (`shouldBe` InLedger) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField #metadata (`shouldBe` Nothing) , expectField #inputs $ \inputs' -> do inputs' `shouldSatisfy` all (isJust . source) ] @@ -1208,7 +1208,10 @@ spec = describe "SHELLEY_STAKE_POOLS" $ do it "STAKE_POOLS_SMASH_01 - fetching metadata from SMASH works with delisted pools" $ \ctx -> runResourceT $ bracketSettings ctx $ do updateMetadataSource ctx (_smashUrl ctx) - eventually "metadata is fetched" $ do + -- This can be slow; let's retry less frequently and with a longer + -- timeout. + let s = 1_000_000 + eventuallyUsingDelay (10 * s) 300 "metadata is fetched" $ do r <- listPools ctx arbitraryStake verify r [ expectListSize 3 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 eb8dd2a0333..0235fc8b051 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 @@ -40,6 +40,8 @@ import Cardano.Wallet.Api.Types , insertedAt , pendingSince ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( detailedMetadata ) import Cardano.Wallet.Primitive.AddressDerivation ( PaymentAddress ) import Cardano.Wallet.Primitive.AddressDerivation.Icarus @@ -325,7 +327,7 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do inputs' `shouldSatisfy` all (isJust . source) , expectField (#direction . #getApiT) (`shouldBe` Outgoing) , expectField (#status . #getApiT) (`shouldBe` Pending) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField #metadata (`shouldBe` Nothing) ] verify ra @@ -352,7 +354,7 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do inputs' `shouldSatisfy` all (isJust . source) , expectField (#direction . #getApiT) (`shouldBe` Outgoing) , expectField (#status . #getApiT) (`shouldBe` InLedger) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField (#metadata) (`shouldBe` Nothing) ] let linkDest = Link.getTransaction @'Shelley wb (ApiTxId txid) @@ -365,7 +367,7 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do inputs' `shouldSatisfy` all (isNothing . source) , expectField (#direction . #getApiT) (`shouldBe` Incoming) , expectField (#status . #getApiT) (`shouldBe` InLedger) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField (#metadata) (`shouldBe` Nothing) ] eventually "wa and wb balances are as expected" $ do @@ -957,7 +959,10 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do } ] - it "TRANSMETA_CREATE_02 - Transaction with invalid metadata" $ \ctx -> runResourceT $ do + it "TRANSMETA_CREATE_02a - \ + \Transaction with invalid metadata" $ + \ctx -> runResourceT $ do + (wa, wb) <- (,) <$> fixtureWallet ctx <*> fixtureWallet ctx let amt = minUTxOValue (_mainEra ctx) :: Natural @@ -972,6 +977,28 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do expectResponseCode HTTP.status400 r expectErrorMessage errMsg400TxMetadataStringTooLong r + it "TRANSMETA_CREATE_02b - \ + \Transaction with invalid no-schema metadata" $ + \ctx -> runResourceT $ do + + (wa, wb) <- (,) <$> fixtureWallet ctx <*> fixtureWallet ctx + let amt = minUTxOValue (_mainEra ctx) :: Natural + + basePayload <- mkTxPayload ctx wb amt fixturePassphrase + + let txMeta = [json|{ "1": #{T.replicate 65 "a"} }|] + let payload = addTxMetadata txMeta basePayload + + r <- + request @(ApiTransaction n) + ctx + (Link.createTransactionOld @'Shelley wa) + Default + payload + + expectResponseCode HTTP.status400 r + expectErrorMessage errMsg400TxMetadataStringTooLong r + it "TRANSMETA_CREATE_03 - Transaction with too much metadata" $ \ctx -> runResourceT $ do (wa, wb) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx let amt = minUTxOValue (_mainEra ctx) :: Natural @@ -992,7 +1019,10 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do expectResponseCode HTTP.status403 r expectErrorMessage errMsg403TxTooBig r - it "TRANSMETA_ESTIMATE_01 - fee estimation includes metadata" $ \ctx -> runResourceT $ do + it "TRANSMETA_ESTIMATE_01a - \ + \fee estimation includes metadata" $ + \ctx -> runResourceT $ do + (wa, wb) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx let amt = minUTxOValue (_mainEra ctx) :: Natural @@ -1020,7 +1050,51 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do , expectField (#estimatedMax . #getQuantity) (.< feeEstMax) ] - it "TRANSMETA_ESTIMATE_02 - fee estimation with invalid metadata" $ \ctx -> runResourceT $ do + it "TRANSMETA_ESTIMATE_01b - \ + \fee estimation includes no-schema metadata" $ + \ctx -> runResourceT $ do + + (wa, wb) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx + let amt = minUTxOValue (_mainEra ctx) :: Natural + + payload <- mkTxPayload ctx wb amt fixturePassphrase + + let txMeta = [json|{ "1": "hello" }|] + let payloadWithMetadata = addTxMetadata txMeta payload + + ra <- + request @ApiFee + ctx + (Link.getTransactionFeeOld @'Shelley wa) + Default + payloadWithMetadata + verify + ra + [ expectSuccess + , expectResponseCode HTTP.status202 + ] + let (Quantity feeEstMin) = getFromResponse #estimatedMin ra + let (Quantity feeEstMax) = getFromResponse #estimatedMax ra + + -- check that it's estimated to have less fees for transactions without + -- metadata. + rb <- + request @ApiFee + ctx + (Link.getTransactionFeeOld @'Shelley wa) + Default + payload + verify + rb + [ expectResponseCode HTTP.status202 + , expectField (#estimatedMin . #getQuantity) (.< feeEstMin) + , expectField (#estimatedMax . #getQuantity) (.< feeEstMax) + ] + + it "TRANSMETA_ESTIMATE_02a - \ + \fee estimation with invalid metadata" $ + \ctx -> runResourceT $ do + (wa, wb) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx let amt = minUTxOValue (_mainEra ctx) :: Natural @@ -1035,6 +1109,28 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do expectResponseCode HTTP.status400 r expectErrorMessage errMsg400TxMetadataStringTooLong r + it "TRANSMETA_ESTIMATE_02b - \ + \fee estimation with invalid no-schema metadata" $ + \ctx -> runResourceT $ do + + (wa, wb) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx + let amt = minUTxOValue (_mainEra ctx) :: Natural + + basePayload <- mkTxPayload ctx wb amt fixturePassphrase + + let txMeta = [json|{ "1": #{T.replicate 65 "a" } }|] + let payload = addTxMetadata txMeta basePayload + + r <- + request @ApiFee + ctx + (Link.getTransactionFeeOld @'Shelley wa) + Default + payload + + expectResponseCode HTTP.status400 r + expectErrorMessage errMsg400TxMetadataStringTooLong r + it "TRANSMETA_ESTIMATE_03 - fee estimation with too much metadata" $ \ctx -> runResourceT $ do (wa, wb) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx let amt = minUTxOValue (_mainEra ctx) :: Natural @@ -1607,7 +1703,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do eventually "Transactions are available and in ledger" $ do -- Verify Tx in source wallet is Outgoing and InLedger - let linkSrc = Link.getTransaction @'Shelley wSrc (ApiTxId txid) + let linkSrc = Link.getTransaction @'Shelley + wSrc (ApiTxId txid) r1 <- request @(ApiTransaction n) ctx linkSrc Default Empty verify r1 [ expectResponseCode HTTP.status200 @@ -1616,7 +1713,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do ] -- Verify Tx in destination wallet is Incoming and InLedger - let linkDest = Link.getTransaction @'Shelley wDest (ApiTxId txid) + let linkDest = Link.getTransaction + @'Shelley wDest (ApiTxId txid) r2 <- request @(ApiTransaction n) ctx linkDest Default Empty verify r2 [ expectResponseCode HTTP.status200 @@ -1649,7 +1747,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do ] let txid = Hash $ BS.pack $ replicate 32 1 - let link = Link.getTransaction @'Shelley wSrc (ApiTxId $ ApiT txid) + let link = Link.getTransaction @'Shelley + wSrc (ApiTxId $ ApiT txid) r <- request @(ApiTransaction n) ctx link Default Empty expectResponseCode HTTP.status404 r expectErrorMessage (errMsg404CannotFindTx $ toText txid) r @@ -1830,7 +1929,7 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do eventually "withdrawal transaction is listed on other" $ do rTxOther <- request @(ApiTransaction n) ctx - (Link.getTransaction @'Shelley wOther tid) Default payload + (Link.getTransaction @'Shelley wOther tid) Default payload verify rTxOther [ expectResponseCode HTTP.status200 @@ -2152,8 +2251,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do , expectResponseCode HTTP.status202 , expectField (#status . #getApiT) (`shouldBe` Pending) , expectField - (#metadata . #getApiTxMetadata) - (`shouldBe` fmap ApiT txMetadata) + #metadata + (`shouldBe` detailedMetadata <$> txMetadata) , expectField (#fee) (`shouldBe` expectedFee) ] @@ -2169,8 +2268,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do , expectListField 0 (#direction . #getApiT) (`shouldBe` Outgoing) , expectListField 0 - (#metadata . #getApiTxMetadata) - (`shouldBe` fmap ApiT txMetadata) + (#metadata ) + (`shouldBe` detailedMetadata <$> txMetadata) ] -- on dst wallet let linkDstList = Link.listTransactions @'Shelley wb @@ -2182,8 +2281,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do , expectListField 0 (#direction . #getApiT) (`shouldBe` Incoming) , expectListField 0 - (#metadata . #getApiTxMetadata) - (`shouldBe` fmap ApiT txMetadata) + (#metadata ) + (`shouldBe` detailedMetadata <$> txMetadata) ] let txid = getFromResponse #id ra @@ -2198,8 +2297,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do , expectField (#status . #getApiT) (`shouldBe` InLedger) , expectField - (#metadata . #getApiTxMetadata) - (`shouldBe` fmap ApiT txMetadata) + #metadata + (`shouldBe` detailedMetadata <$> txMetadata) ] -- on dst wallet let linkDst = Link.getTransaction @'Shelley wb (ApiTxId txid) @@ -2211,8 +2310,8 @@ spec = describe "SHELLEY_TRANSACTIONS" $ do , expectField (#status . #getApiT) (`shouldBe` InLedger) , expectField - (#metadata . #getApiTxMetadata) - (`shouldBe` fmap ApiT txMetadata) + #metadata + (`shouldBe` detailedMetadata <$> txMetadata) ] txDeleteNotExistsingTxIdTest eWallet resource = diff --git a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs index 64c48e2fa76..230684a6b09 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs @@ -135,8 +135,6 @@ import Data.Function ( (&) ) import Data.Generics.Internal.VL.Lens ( view, (^.) ) -import Data.Generics.Sum - ( _Ctor ) import Data.Maybe ( fromJust, isJust ) import Data.Proxy @@ -291,7 +289,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do [ expectResponseCode HTTP.status400 ] - it "TRANS_NEW_CREATE_02 - Only metadata" $ \ctx -> runResourceT $ do + it "TRANS_NEW_CREATE_02a - Only metadata" $ \ctx -> runResourceT $ do wa <- fixtureWallet ctx let metadata = Json [json|{ "metadata": { "1": { "string": "hello" } } }|] @@ -308,15 +306,13 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do signedTx <- signTx ctx wa apiTx [ expectResponseCode HTTP.status202 ] -- Check for the presence of metadata on signed transaction - let - getMetadata (InAnyCardanoEra _ tx) = Cardano.getTxBody tx - & (\(Cardano.TxBody bodyContent) -> - Cardano.txMetadata bodyContent - & \case Cardano.TxMetadataNone -> - Nothing - Cardano.TxMetadataInEra _ (Cardano.TxMetadata m) -> - Just m - ) + let getMetadata (InAnyCardanoEra _ tx) = Cardano.getTxBody tx & + \(Cardano.TxBody bodyContent) -> + Cardano.txMetadata bodyContent & \case + Cardano.TxMetadataNone -> + Nothing + Cardano.TxMetadataInEra _ (Cardano.TxMetadata m) -> + Just m case getMetadata (cardanoTx $ getApiT (signedTx ^. #transaction)) of Nothing -> error "Tx doesn't include metadata" @@ -325,14 +321,17 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do Just (Cardano.TxMetaText "hello") -> pure () Just _ -> error "Tx metadata incorrect" - let txCbor = getFromResponse #transaction (HTTP.status202, Right signedTx) + let txCbor = + getFromResponse #transaction (HTTP.status202, Right signedTx) let decodePayload = Json (toJSON $ ApiSerialisedTransaction txCbor) - let expMetadata = ApiT (TxMetadata (Map.fromList [(1,TxMetaText "hello")])) + let expMetadata = + ApiT (TxMetadata (Map.fromList [(1,TxMetaText "hello")])) rDecodedTx <- request @(ApiDecodedTransaction n) ctx (Link.decodeTransaction @'Shelley wa) Default decodePayload verify rDecodedTx [ expectResponseCode HTTP.status202 - , expectField #metadata (`shouldBe` (ApiTxMetadata (Just expMetadata))) + , expectField #metadata + (`shouldBe` (ApiTxMetadata (Just expMetadata))) ] -- Submit tx @@ -353,6 +352,69 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do (`shouldBe` (fromIntegral oneMillionAda - expectedFee)) ] + it "TRANS_NEW_CREATE_02b - Only metadata, untyped" $ + \ctx -> runResourceT $ do + + wa <- fixtureWallet ctx + let metadata = Json [json|{ "metadata": { "1": "hello" } }|] + + rTx <- request @(ApiConstructTransaction n) ctx + (Link.createUnsignedTransaction @'Shelley wa) Default metadata + verify rTx + [ expectResponseCode HTTP.status202 + , expectField (#coinSelection . #metadata) (`shouldSatisfy` isJust) + , expectField (#fee . #getQuantity) (`shouldSatisfy` (>0)) + ] + + let expectedFee = getFromResponse (#fee . #getQuantity) rTx + let apiTx = getFromResponse #transaction rTx + signedTx <- signTx ctx wa apiTx [ expectResponseCode HTTP.status202 ] + + -- Check for the presence of metadata on signed transaction + let getMetadata (InAnyCardanoEra _ tx) = Cardano.getTxBody tx & + \(Cardano.TxBody bodyContent) -> + Cardano.txMetadata bodyContent & \case + Cardano.TxMetadataNone -> + Nothing + Cardano.TxMetadataInEra _ (Cardano.TxMetadata m) -> + Just m + + case getMetadata (cardanoTx $ getApiT (signedTx ^. #transaction)) of + Nothing -> error "Tx doesn't include metadata" + Just m -> case Map.lookup 1 m of + Nothing -> error "Tx doesn't include metadata" + Just (Cardano.TxMetaText "hello") -> pure () + Just _ -> error "Tx metadata incorrect" + + let txCbor = + getFromResponse #transaction (HTTP.status202, Right signedTx) + let decodePayload = Json (toJSON $ ApiSerialisedTransaction txCbor) + let expMetadata = ApiT (TxMetadata (Map.fromList [(1,TxMetaText "hello")])) + rDecodedTx <- request @(ApiDecodedTransaction n) ctx + (Link.decodeTransaction @'Shelley wa) Default decodePayload + verify rDecodedTx + [ expectResponseCode HTTP.status202 + , expectField #metadata (`shouldBe` (ApiTxMetadata (Just expMetadata))) + ] + + -- Submit tx + submittedTx <- submitTxWithWid ctx wa signedTx + verify submittedTx + [ expectSuccess + , expectResponseCode HTTP.status202 + ] + + -- Make sure only fee is deducted from fixtureWallet + eventually "Wallet balance is as expected" $ do + rWa <- request @ApiWallet ctx + (Link.getWallet @'Shelley wa) Default Empty + verify rWa + [ expectSuccess + , expectField + (#balance . #available . #getQuantity) + (`shouldBe` (fromIntegral oneMillionAda - expectedFee)) + ] + it "TRANS_NEW_CREATE_03a - Withdrawal from self, 0 rewards" $ \ctx -> runResourceT $ do wa <- fixtureWallet ctx let initialBalance = wa ^. #balance . #available . #getQuantity @@ -2115,6 +2177,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do , [ expectResponseCode HTTP.status202 ] ) , ( "currency", \ctx w -> do + liftIO $ pendingWith "flaky #3124" (addr,proxy) <- view #id . head <$> listAddresses @n ctx w let getFreshUTxO = do -- To obtain a fresh UTxO, we perform @@ -2277,7 +2340,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do [ expectResponseCode HTTP.status200 , expectField (#direction . #getApiT) (`shouldBe` Outgoing) , expectField (#status . #getApiT) (`shouldBe` InLedger) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField #metadata (`shouldBe` Nothing) , expectField #inputs $ \inputs' -> do inputs' `shouldSatisfy` all (isJust . source) ] @@ -2599,7 +2662,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do [ expectResponseCode HTTP.status200 , expectField (#direction . #getApiT) (`shouldBe` Outgoing) , expectField (#status . #getApiT) (`shouldBe` InLedger) - , expectField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectField #metadata (`shouldBe` Nothing) , expectField #inputs $ \inputs' -> do inputs' `shouldSatisfy` all (isJust . source) ] @@ -2934,7 +2997,7 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do (#status . #getApiT) (`shouldBe` InLedger) , expectField - (#metadata . #getApiTxMetadata . _Ctor @"Just" . #getApiT) + (#metadata . traverse . #txMetadataWithSchema_metadata) (`shouldBe` Cardano.TxMetadata (Map.fromList [(1, Cardano.TxMetaText "hello")])) ] @@ -2969,68 +3032,6 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do (`shouldBe` balance) ] - it "TRANS_NEW_CREATE_10a - Minting/burning assets - more than one cosigner \ - \in template" $ \ctx -> runResourceT $ do - wa <- fixtureWallet ctx - addrs <- listAddresses @n ctx wa - let destination = (addrs !! 1) ^. #id - - let payload = Json [json|{ - "mint_burn": [{ - "policy_script_template": - { "all": - [ "cosigner#0", - "cosigner#1", - { "active_from": 120 } - ] - }, - "asset_name": "ab12", - "operation": - { "mint" : - { "receiving_address": #{destination}, - "quantity": 10000 - } - } - }] - }|] - - rTx <- request @(ApiConstructTransaction n) ctx - (Link.createUnsignedTransaction @'Shelley wa) Default payload - verify rTx - [ expectResponseCode HTTP.status403 - , expectErrorMessage errMsg403CreatedWrongPolicyScriptTemplateTx - ] - - it "TRANS_NEW_CREATE_10b - Minting/burning assets - incorrect template \ - \" $ \ctx -> runResourceT $ do - wa <- fixtureWallet ctx - addrs <- listAddresses @n ctx wa - let destination = (addrs !! 1) ^. #id - - let payload = Json [json|{ - "mint_burn": [{ - "policy_script_template": - { "all": - [ { "active_from": 120 } - ] - }, - "asset_name": "ab12", - "operation": - { "mint" : - { "receiving_address": #{destination}, - "quantity": 10000 - } - } - }] - }|] - - rTx <- request @(ApiConstructTransaction n) ctx - (Link.createUnsignedTransaction @'Shelley wa) Default payload - verify rTx - [ expectResponseCode HTTP.status403 - , expectErrorMessage errMsg403CreatedWrongPolicyScriptTemplateTx - ] - it "TRANS_NEW_CREATE_10c - Minting/burning assets - \ \one cosigner in template other than cosigner#0" $ \ctx -> runResourceT $ do @@ -3458,27 +3459,77 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do (`shouldBe` tokens') ] - describe "TRANS_NEW_CREATE_MINT_SCRIPTS - I can mint and burn with different policy scripts" $ do + describe "TRANS_NEW_CREATE_MINT_SCRIPTS_WRONG - I cannot mint with incorrect policy scripts" $ do let scenarios = - [ ( "all", [json|{ "all": [ "cosigner#0" ] }|] ) - , ( "any", [json|{ "any": [ "cosigner#0" ] }|] ) - , ( "some", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0" ]} }|] ) - , ( "all, active_until 57297561", [json|{ "all": [ "cosigner#0", { "active_until": 57297561 } ] }|] ) - , ( "any, active_until 57297561", [json|{ "any": [ "cosigner#0", { "active_until": 57297561 } ] }|] ) - , ( "some, active_until 57297561", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0", { "active_until": 57297561 } ]} }|] ) - , ( "all, active_from 10", [json|{ "all": [ "cosigner#0", { "active_from": 10 } ] }|] ) - , ( "any, active_from 10", [json|{ "any": [ "cosigner#0", { "active_from": 10 } ] }|] ) - , ( "some, active_from 10", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0", { "active_from": 10 } ]} }|] ) - , ( "all, active_from 10, active_until 57297561", [json|{ "all": [ "cosigner#0", { "active_from": 10 }, { "active_until": 57297561 } ] }|] ) - , ( "any, active_from 10, active_until 57297561", [json|{ "any": [ "cosigner#0", { "active_from": 10 }, { "active_until": 57297561 } ] }|] ) - , ( "some, active_from 10, active_until 57297561", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0", { "active_from": 10 }, { "active_until": 57297561 } ]} }|] ) + [ ( "no cosigner", [json|{ "active_from": 0 }|] ) + , ( "all, no cosigner", [json|{ "all": [ { "active_from": 120 } ] }|] ) + , ( "any, no cosigner", [json|{ "any": [ { "active_until": 120 } ] }|] ) + , ( "some, no cosigner", [json|{ "some": { "at_least": 1, "from": [ { "active_from": 120 } ]} }|] ) + , ( "some, at least 2", [json|{ "some": { "at_least": 2, "from": [ "cosigner#0", { "active_from": 120 } ]} }|] ) + , ( "all, many cosigners", [json|{ "all": [ "cosigner#0", "cosigner#1", { "active_from": 120 } ] }|] ) + , ( "any, many cosigners", [json|{ "any": [ "cosigner#0", "cosigner#1" ] }|] ) + , ( "all, cosigner#1", [json|{ "all": [ "cosigner#1" ] }|] ) ] forM_ scenarios $ \(title, policyScriptTemplate) -> it title $ \ctx -> runResourceT $ do - liftIO $ pendingWith "ADP-1738" + wa <- emptyWallet ctx + addrs <- listAddresses @n ctx wa + let destination = (addrs !! 1) ^. #id + + let payload = Json [json|{ + "mint_burn": [{ + "policy_script_template": #{policyScriptTemplate}, + "asset_name": "ab12", + "operation": + { "mint" : + { "receiving_address": #{destination}, + "quantity": 10000 + } + } + }] + }|] + + rTx <- request @(ApiConstructTransaction n) ctx + (Link.createUnsignedTransaction @'Shelley wa) Default payload + verify rTx + [ expectResponseCode HTTP.status403 + , expectErrorMessage errMsg403CreatedWrongPolicyScriptTemplateTx + ] + + describe "TRANS_NEW_CREATE_MINT_SCRIPTS - I can mint and burn with correct policy scripts" $ do + let scenarios = + [ ( "all", [json|{ "all": [ "cosigner#0" ] }|], False ) + , ( "any", [json|{ "any": [ "cosigner#0" ] }|], False ) + , ( "some", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0" ]} }|], False ) + , ( "all, active_until 57297561", [json|{ "all": [ "cosigner#0", { "active_until": 57297561 } ] }|], False ) + , ( "any, active_until 57297561", [json|{ "any": [ "cosigner#0", { "active_until": 57297561 } ] }|], False ) + , ( "some, active_until 57297561", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0", { "active_until": 57297561 } ]} }|], False ) + , ( "all, active_from 10", [json|{ "all": [ "cosigner#0", { "active_from": 10 } ] }|], True ) + , ( "any, active_from 10", [json|{ "any": [ "cosigner#0", { "active_from": 10 } ] }|], True ) + , ( "some, active_from 10", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0", { "active_from": 10 } ]} }|], True ) + , ( "all, active_from 10, active_until 57297561", [json|{ "all": [ "cosigner#0", { "active_from": 10 }, { "active_until": 57297561 } ] }|] , True) + , ( "any, active_until 57297561, active_from 58297561", [json|{ "any": [ "cosigner#0", { "active_until": 57297561 }, { "active_from": 58297561 } ] }|] , False) + , ( "some, active_from 10, active_until 57297561", [json|{ "some": {"at_least": 1, "from": [ "cosigner#0", { "active_from": 10 }, { "active_until": 57297561 } ]} }|], False ) + ] + forM_ scenarios $ \(title, policyScriptTemplate, addInvalidBefore ) -> it title $ \ctx -> runResourceT $ do w <- fixtureWallet ctx -- Mint it! - let payloadMint = Json [json|{ + let payloadMint = + if addInvalidBefore then Json [json|{ + "mint_burn": [ + { "policy_script_template": #{policyScriptTemplate} + , "asset_name": "1111" + , "operation": { "mint": { "quantity": 50000 } } + } + ], + "validity_interval": + { "invalid_before": + { "quantity": 11 + , "unit": "slot" + } + } + }|] + else Json [json|{ "mint_burn": [ { "policy_script_template": #{policyScriptTemplate} , "asset_name": "1111" @@ -3513,7 +3564,22 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do ] -- Burn it! - let payloadBurn = Json [json|{ + let payloadBurn = + if addInvalidBefore then Json [json|{ + "mint_burn": [ + { "policy_script_template": #{policyScriptTemplate} + , "asset_name": "1111" + , "operation": { "burn": { "quantity": 50000 } } + } + ], + "validity_interval": + { "invalid_before": + { "quantity": 11 + , "unit": "slot" + } + } + }|] + else Json [json|{ "mint_burn": [ { "policy_script_template": #{policyScriptTemplate} , "asset_name": "1111" diff --git a/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/HWWallets.hs b/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/HWWallets.hs index e28aa205b78..271fb082870 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/HWWallets.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/HWWallets.hs @@ -23,6 +23,8 @@ import Cardano.Wallet.Api.Types , encodeAddress , getApiT ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..) ) import Cardano.Wallet.Primitive.AddressDiscovery.Sequential ( defaultAddressPoolGap, getAddressPoolGap ) import Cardano.Wallet.Primitive.Types.Address @@ -307,10 +309,9 @@ spec = describe "SHELLEY_CLI_HW_WALLETS" $ do it "Can list transactions" $ \ctx -> runResourceT $ do w <- emptyWalletFromPubKeyViaCLI ctx restoredWalletName - (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx [T.unpack $ w ^. walletId] - + listTransactionsViaCLI ctx TxMetadataDetailedSchema + [T.unpack $ w ^. walletId] err `shouldBe` cmdOk out `shouldBe` "[]\n" code `shouldBe` ExitSuccess diff --git a/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/Transactions.hs b/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/Transactions.hs index edd4507dcdd..6aa746a93e7 100644 --- a/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/Transactions.hs +++ b/lib/core-integration/src/Test/Integration/Scenario/CLI/Shelley/Transactions.hs @@ -25,6 +25,8 @@ import Cardano.Wallet.Api.Types , EncodeAddress (..) , getApiT ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..), detailedMetadata, noSchemaMetadata ) import Cardano.Wallet.Primitive.Types ( SortOrder (..) ) import Cardano.Wallet.Primitive.Types.Tx @@ -138,7 +140,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do (between (feeMin + amt, feeMax + amt)) , expectCliField (#direction . #getApiT) (`shouldBe` Outgoing) , expectCliField (#status . #getApiT) (`shouldBe` Pending) - , expectCliField (#metadata . #getApiTxMetadata) (`shouldBe` Nothing) + , expectCliField #metadata (`shouldBe` Nothing) ] -- verify balance on src wallet @@ -317,12 +319,18 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do out `shouldBe` "" c `shouldBe` ExitFailure 1 - it "TRANSMETA_CREATE_01 - Transaction with metadata via CLI" $ \ctx -> runResourceT $ do + it "TRANSMETA_CREATE_01a - \ + \Transaction with metadata via CLI" $ + \ctx -> runResourceT $ do + (wSrc, wDest) <- (,) <$> fixtureWallet ctx <*> emptyWallet ctx let amt = 10_000_000 let md = Just "{ \"1\": { \"string\": \"hello\" } }" - let expected = Just $ ApiT $ TxMetadata $ - Map.singleton 1 (TxMetaText "hello") + let expected = + Just $ + detailedMetadata $ + TxMetadata $ + Map.singleton 1 (TxMetaText "hello") args <- postTxArgs ctx wSrc wDest amt md Nothing Stdout feeOut <- postTransactionFeeViaCLI ctx args @@ -330,21 +338,87 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do txJson <- postTxViaCLI ctx wSrc wDest amt md Nothing verify txJson - [ expectCliField (#amount . #getQuantity) + [ expectCliField + (#amount . #getQuantity) (between (feeMin + amt, feeMax + amt)) , expectCliField (#direction . #getApiT) (`shouldBe` Outgoing) , expectCliField (#status . #getApiT) (`shouldBe` Pending) - , expectCliField (#metadata . #getApiTxMetadata) (`shouldBe` expected) + , expectCliField #metadata (`shouldBe` expected) ] eventually "metadata is confirmed in transaction list" $ do (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx [T.unpack $ wSrc ^. walletId] + listTransactionsViaCLI ctx TxMetadataDetailedSchema + [T.unpack $ wSrc ^. walletId] err `shouldBe` "Ok.\n" code `shouldBe` ExitSuccess outJson <- expectValidJSON (Proxy @([ApiTransaction n])) out - verify outJson - [ expectCliListField 0 (#metadata . #getApiTxMetadata) (`shouldBe` expected) + verify + outJson + [ expectCliListField 0 #metadata (`shouldBe` expected) + , expectCliListField 0 + (#status . #getApiT) + (`shouldBe` InLedger) + ] + + it "TRANSMETA_CREATE_01b - \ + \Transaction with metadata via CLI with simple metadata" $ + \ctx -> runResourceT $ do + + wSrc <- fixtureWallet ctx + wDest <- emptyWallet ctx + let amt = 10_000_000 + let md = Just "{ \"1\": { \"string\": \"hello\" } }" + let expected = Just $ + detailedMetadata $ + TxMetadata $ + Map.singleton 1 (TxMetaText "hello") + + args <- postTxArgs ctx wSrc wDest amt md Nothing + Stdout feeOut <- postTransactionFeeViaCLI ctx args + ApiFee (Quantity feeMin) (Quantity feeMax) _ _ <- + expectValidJSON Proxy feeOut + + txJson <- postTxViaCLI ctx wSrc wDest amt md Nothing + verify txJson + [ expectCliField + (#amount . #getQuantity) + (between (feeMin + amt, feeMax + amt)) + , expectCliField (#direction . #getApiT) (`shouldBe` Outgoing) + , expectCliField (#status . #getApiT) (`shouldBe` Pending) + , expectCliField #metadata (`shouldBe` expected) + ] + + let wSrcId = T.unpack (wSrc ^. walletId) + let txId = getTxId txJson + + (Exit code, Stdout out, Stderr err) <- + getTransactionViaCLI ctx wSrcId txId TxMetadataNoSchema + err `shouldBe` "Ok.\n" + code `shouldBe` ExitSuccess + outJson <- expectValidJSON (Proxy @(ApiTransaction n)) out + let expectedNoSchema = Just $ + noSchemaMetadata $ + TxMetadata $ + Map.singleton 1 (TxMetaText "hello") + verify outJson + [ expectCliField + (#amount . #getQuantity) + (between (feeMin + amt, feeMax + amt)) + , expectCliField (#direction . #getApiT) (`shouldBe` Outgoing) + , expectCliField (#status . #getApiT) (`shouldBe` Pending) + , expectCliField #metadata (`shouldBe` expectedNoSchema) + ] + eventually "metadata is confirmed in transaction list" $ do + (Exit codeL, Stdout outL, Stderr errL) <- + listTransactionsViaCLI ctx TxMetadataNoSchema + [T.unpack $ wSrc ^. walletId] + errL `shouldBe` "Ok.\n" + codeL `shouldBe` ExitSuccess + outJsonL <- expectValidJSON (Proxy @([ApiTransaction n])) outL + verify + outJsonL + [ expectCliListField 0 #metadata (`shouldBe` expectedNoSchema) , expectCliListField 0 (#status . #getApiT) (`shouldBe` InLedger) ] @@ -367,7 +441,8 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do eventually "transaction with ttl is confirmed in transaction list" $ do (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx [T.unpack $ wDest ^. walletId] + listTransactionsViaCLI ctx TxMetadataDetailedSchema + [T.unpack $ wDest ^. walletId] err `shouldBe` "Ok.\n" code `shouldBe` ExitSuccess outJson <- expectValidJSON (Proxy @([ApiTransaction n])) out @@ -418,12 +493,13 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do it title $ \ctx -> runResourceT $ do wallet <- emptyWallet ctx (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx $ join - [ [T.unpack $ wallet ^. walletId] - , maybe [] (\t -> ["--start", t]) mStart - , maybe [] (\t -> ["--end" , t]) mEnd - , maybe [] (\o -> ["--order", showT o]) mOrder - ] + listTransactionsViaCLI ctx TxMetadataDetailedSchema $ + join + [ [T.unpack $ wallet ^. walletId] + , maybe [] (\t -> ["--start", t]) mStart + , maybe [] (\t -> ["--end" , t]) mEnd + , maybe [] (\o -> ["--order", showT o]) mOrder + ] err `shouldBe` "Ok.\n" out `shouldBe` "[]\n" code `shouldBe` ExitSuccess @@ -456,7 +532,8 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do -- Verify Tx list contains Incoming and Outgoing (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx [T.unpack $ wSrc ^. walletId] + listTransactionsViaCLI ctx TxMetadataDetailedSchema + [T.unpack $ wSrc ^. walletId] err `shouldBe` "Ok.\n" code `shouldBe` ExitSuccess outJson <- expectValidJSON (Proxy @([ApiTransaction n])) out @@ -480,7 +557,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do it title $ \ctx -> runResourceT $ do wid <- emptyWallet' ctx (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx $ join + listTransactionsViaCLI ctx TxMetadataDetailedSchema $ join [ [ wid ] , [ "--start", startTime ] , [ "--end" , endTime ] @@ -524,7 +601,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do forM_ orderings $ \(order, expects) -> do let args = T.unpack <$> w ^. walletId : order (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx args + listTransactionsViaCLI ctx TxMetadataDetailedSchema args err `shouldBe` "Ok.\n" code `shouldBe` ExitSuccess outJson <- expectValidJSON (Proxy @([ApiTransaction n])) out @@ -565,7 +642,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do wid <- emptyWallet' ctx let args = wid : q (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx args + listTransactionsViaCLI ctx TxMetadataDetailedSchema args out `shouldBe` mempty code `shouldBe` ExitFailure 1 err `shouldContain` errorMess @@ -574,7 +651,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do wid <- emptyWallet' ctx let invalidWid = wid ++ "0" (Exit code, Stdout out, Stderr err) <- - listTransactionsViaCLI ctx [invalidWid] + listTransactionsViaCLI ctx TxMetadataDetailedSchema [invalidWid] err `shouldContain` "wallet id should be a hex-encoded\ \ string of 40 characters" @@ -586,14 +663,16 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do Exit d <- deleteWalletViaCLI ctx wid d `shouldBe` ExitSuccess - (Exit c, Stdout o, Stderr e) <- listTransactionsViaCLI ctx [wid] + (Exit c, Stdout o, Stderr e) <- + listTransactionsViaCLI ctx TxMetadataDetailedSchema [wid] e `shouldContain` errMsg404NoWallet (T.pack wid) o `shouldBe` mempty c `shouldBe` ExitFailure 1 describe "TRANS_LIST_04 - False wallet ids" $ do forM_ falseWalletIds $ \(title, walId) -> it title $ \ctx -> runResourceT $ do - (Exit c, Stdout o, Stderr e) <- listTransactionsViaCLI ctx [walId] + (Exit c, Stdout o, Stderr e) <- + listTransactionsViaCLI ctx TxMetadataDetailedSchema [walId] o `shouldBe` "" c `shouldBe` ExitFailure 1 if (title == "40 chars hex") then @@ -614,14 +693,14 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do [ "--start", utcIso8601ToText t1 , "--end", utcIso8601ToText t2 ] - Stdout o1 <- listTransactionsViaCLI ctx - ( T.unpack <$> walId : (query t t) ) - Stdout o2 <- listTransactionsViaCLI ctx - ( T.unpack <$> walId : (query te t) ) - Stdout o3 <- listTransactionsViaCLI ctx - ( T.unpack <$> walId : (query t tl) ) - Stdout o4 <- listTransactionsViaCLI ctx - ( T.unpack <$> walId : (query te tl) ) + Stdout o1 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> walId : (query t t)) + Stdout o2 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> walId : (query te t)) + Stdout o3 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> walId : (query t tl)) + Stdout o4 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> walId : (query te tl)) oJson1 <- expectValidJSON (Proxy @([ApiTransaction n])) o1 oJson2 <- expectValidJSON (Proxy @([ApiTransaction n])) o2 oJson3 <- expectValidJSON (Proxy @([ApiTransaction n])) o3 @@ -635,10 +714,10 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do let walId = w ^. walletId t <- unsafeGetTransactionTime =<< listAllTransactions @n ctx w let tl = utcIso8601ToText $ utcTimeSucc t - Stdout o1 <- listTransactionsViaCLI ctx - ( T.unpack <$> [walId, "--start", tl] ) - Stdout o2 <- listTransactionsViaCLI ctx - ( T.unpack <$> [walId, "--start", tl, "--end", tl] ) + Stdout o1 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> [walId, "--start", tl]) + Stdout o2 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> [walId, "--start", tl, "--end", tl]) oJson1 <- expectValidJSON (Proxy @([ApiTransaction n])) o1 oJson2 <- expectValidJSON (Proxy @([ApiTransaction n])) o2 length <$> [oJson1, oJson2] `shouldSatisfy` all (== 0) @@ -650,10 +729,10 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do let walId = w ^. walletId t <- unsafeGetTransactionTime =<< listAllTransactions @n ctx w let te = utcIso8601ToText $ utcTimePred t - Stdout o1 <- listTransactionsViaCLI ctx - ( T.unpack <$> [walId, "--end", te] ) - Stdout o2 <- listTransactionsViaCLI ctx - ( T.unpack <$> [walId, "--start", te, "--end", te] ) + Stdout o1 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> [walId, "--end", te]) + Stdout o2 <- listTransactionsViaCLI ctx TxMetadataDetailedSchema + (T.unpack <$> [walId, "--start", te, "--end", te]) oJson1 <- expectValidJSON (Proxy @([ApiTransaction n])) o1 oJson2 <- expectValidJSON (Proxy @([ApiTransaction n])) o2 length <$> [oJson1, oJson2] `shouldSatisfy` all (== 0) @@ -694,7 +773,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do -- Verify Tx in source wallet is Outgoing and InLedger (Exit code1, Stdout out1, Stderr err1) <- - getTransactionViaCLI ctx wSrcId txId + getTransactionViaCLI ctx wSrcId txId TxMetadataDetailedSchema err1 `shouldBe` "Ok.\n" code1 `shouldBe` ExitSuccess outJson1 <- expectValidJSON (Proxy @(ApiTransaction n)) out1 @@ -706,7 +785,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do let wDestId = T.unpack (wDest ^. walletId) -- Verify Tx in destination wallet is Incoming and InLedger (Exit code2, Stdout out2, Stderr err2) <- - getTransactionViaCLI ctx wDestId txId + getTransactionViaCLI ctx wDestId txId TxMetadataDetailedSchema err2 `shouldBe` "Ok.\n" code2 `shouldBe` ExitSuccess outJson2 <- expectValidJSON (Proxy @(ApiTransaction n)) out2 @@ -721,7 +800,8 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do d `shouldBe` ExitSuccess let txId = "3e6ec12da4414aa0781ff8afa9717ae53ee8cb4aa55d622f65bc62619a4f7b12" - (Exit c, Stdout o, Stderr e) <- getTransactionViaCLI ctx wid txId + (Exit c, Stdout o, Stderr e) <- + getTransactionViaCLI ctx wid txId TxMetadataDetailedSchema e `shouldContain` errMsg404NoWallet (T.pack wid) o `shouldBe` mempty c `shouldBe` ExitFailure 1 @@ -747,7 +827,8 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do let wid = T.unpack (wSrc ^. walletId) let txId = "3e6ec12da4414aa0781ff8afa9717ae53ee8cb4aa55d622f65bc62619a4f7b12" - (Exit c2, Stdout o2, Stderr e2) <- getTransactionViaCLI ctx wid txId + (Exit c2, Stdout o2, Stderr e2) <- + getTransactionViaCLI ctx wid txId TxMetadataDetailedSchema e2 `shouldContain` errMsg404CannotFindTx (T.pack txId) o2 `shouldBe` mempty c2 `shouldBe` ExitFailure 1 @@ -767,7 +848,7 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do let txId = getTxId txJson eventually "Tx is in ledger" $ do - listTransactionsViaCLI ctx [wSrcId] + listTransactionsViaCLI ctx TxMetadataDetailedSchema [wSrcId] >>= expectValidJSON (Proxy @([ApiTransaction n])) . fromStdout >>= flip verify [ expectCliListField 0 @@ -843,7 +924,8 @@ spec = describe "SHELLEY_CLI_TRANSACTIONS" $ do it "BYRON_TX_LIST_03 -\ \ Shelley CLI does not list Byron wallet transactions" $ \ctx -> runResourceT $ do wid <- emptyRandomWallet' ctx - (Exit c, Stdout o, Stderr e) <- listTransactionsViaCLI ctx [wid] + (Exit c, Stdout o, Stderr e) <- + listTransactionsViaCLI ctx TxMetadataDetailedSchema [wid] e `shouldContain` errMsg404NoWallet (T.pack wid) o `shouldBe` mempty c `shouldBe` ExitFailure 1 diff --git a/lib/core/cardano-wallet-core.cabal b/lib/core/cardano-wallet-core.cabal index 7d6a82c89d5..f2f7d887069 100644 --- a/lib/core/cardano-wallet-core.cabal +++ b/lib/core/cardano-wallet-core.cabal @@ -185,6 +185,7 @@ library Cardano.Wallet.Api.Server Cardano.Wallet.Api.Server.Tls Cardano.Wallet.Api.Types + Cardano.Wallet.Api.Types.SchemaMetadata Cardano.Wallet.CoinSelection Cardano.Wallet.CoinSelection.Internal Cardano.Wallet.CoinSelection.Internal.Balance diff --git a/lib/core/src/Cardano/Wallet/Api.hs b/lib/core/src/Cardano/Wallet/Api.hs index 093e7d9adb4..e3a0af3c78f 100644 --- a/lib/core/src/Cardano/Wallet/Api.hs +++ b/lib/core/src/Cardano/Wallet/Api.hs @@ -584,6 +584,7 @@ type ListTransactions n = "wallets" :> QueryParam "start" Iso8601Time :> QueryParam "end" Iso8601Time :> QueryParam "order" (ApiT SortOrder) + :> QueryFlag "simple-metadata" :> Get '[JSON] [ApiTransactionT n] -- | https://input-output-hk.github.io/cardano-wallet/api/#operation/getTransaction @@ -591,6 +592,7 @@ type GetTransaction n = "wallets" :> Capture "walletId" (ApiT WalletId) :> "transactions" :> Capture "transactionId" ApiTxId + :> QueryFlag "simple-metadata" :> Get '[JSON] (ApiTransactionT n) -- | https://input-output-hk.github.io/cardano-wallet/api/#operation/postTransactionFee diff --git a/lib/core/src/Cardano/Wallet/Api/Client.hs b/lib/core/src/Cardano/Wallet/Api/Client.hs index 3f2aea02124..d0488fe53e9 100644 --- a/lib/core/src/Cardano/Wallet/Api/Client.hs +++ b/lib/core/src/Cardano/Wallet/Api/Client.hs @@ -94,6 +94,8 @@ import Cardano.Wallet.Api.Types , WalletPutData (..) , WalletPutPassphraseData (..) ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema, toSimpleMetadataFlag ) import Cardano.Wallet.Primitive.Types ( SortOrder, WalletId ) import Cardano.Wallet.Primitive.Types.Address @@ -163,6 +165,7 @@ data TransactionClient = TransactionClient -> Maybe Iso8601Time -> Maybe Iso8601Time -> Maybe (ApiT SortOrder) + -> Bool -> ClientM [ApiTransactionT Aeson.Value] , signTransaction :: ApiT WalletId @@ -186,6 +189,7 @@ data TransactionClient = TransactionClient , getTransaction :: ApiT WalletId -> ApiTxId + -> TxMetadataSchema -> ClientM (ApiTransactionT Aeson.Value) , constructTransaction :: ApiT WalletId @@ -333,7 +337,9 @@ transactionClient = , postTransactionFee = _postTransactionFee , postExternalTransaction = _postExternalTransaction . fromSerialisedTx , deleteTransaction = _deleteTransaction - , getTransaction = _getTransaction + , getTransaction = + \wid txid metadataSchema -> + _getTransaction wid txid (toSimpleMetadataFlag metadataSchema) , constructTransaction = _constructTransaction , balanceTransaction = _balanceTransaction , decodeTransaction = _decodeTransaction @@ -361,13 +367,14 @@ byronTransactionClient = = client (Proxy @("v2" :> Proxy_)) in TransactionClient - { listTransactions = _listTransactions + { listTransactions = \wid start end order _ -> + _listTransactions wid start end order , signTransaction = _signTransaction , postTransaction = _postTransaction , postTransactionFee = _postTransactionFee , postExternalTransaction = _postExternalTransaction . fromSerialisedTx , deleteTransaction = _deleteTransaction - , getTransaction = _getTransaction + , getTransaction = \wid txid _ -> _getTransaction wid txid , constructTransaction = _constructTransaction , balanceTransaction = error "balance transaction endpoint not supported for byron" , decodeTransaction = error "decode transaction endpoint not supported for byron" diff --git a/lib/core/src/Cardano/Wallet/Api/Link.hs b/lib/core/src/Cardano/Wallet/Api/Link.hs index 94d90ea7d5e..cb3e771d4e4 100644 --- a/lib/core/src/Cardano/Wallet/Api/Link.hs +++ b/lib/core/src/Cardano/Wallet/Api/Link.hs @@ -133,6 +133,8 @@ import Cardano.Wallet.Api.Types , MinWithdrawal (..) , WalletStyle (..) ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..), toSimpleMetadataFlag ) import Cardano.Wallet.Primitive.AddressDerivation ( DerivationIndex, NetworkDiscriminant (..), Role ) import Cardano.Wallet.Primitive.AddressDiscovery.Shared @@ -626,7 +628,15 @@ listTransactions' -> (Method, Text) listTransactions' w minWithdrawal inf sup order = discriminate @style (endpoint @(Api.ListTransactions Net) - (\mk -> mk wid (MinWithdrawal <$> minWithdrawal) inf sup (ApiT <$> order))) + (\mk -> mk + wid + (MinWithdrawal <$> minWithdrawal) + inf + sup + (ApiT <$> order) + (toSimpleMetadataFlag TxMetadataDetailedSchema) + ) + ) (endpoint @(Api.ListByronTransactions Net) (\mk -> mk wid inf sup (ApiT <$> order))) (notSupported "Shared") @@ -678,13 +688,17 @@ getTransaction -> t -> (Method, Text) getTransaction w t = discriminate @style - (endpoint @(Api.GetTransaction Net) mkURL) - (endpoint @(Api.GetByronTransaction Net) mkURL) + (endpoint @(Api.GetTransaction Net) mkShelleyURL) + (endpoint @(Api.GetByronTransaction Net) mkByronURL) (notSupported "Shared") where wid = w ^. typed @(ApiT WalletId) tid = ApiTxId (t ^. typed @(ApiT (Hash "Tx"))) - mkURL mk = mk wid tid + mkByronURL mk = mk wid tid + + mkShelleyURL :: (ApiT WalletId -> ApiTxId -> Bool -> Text) -> Text + mkShelleyURL mk = + mk wid tid (toSimpleMetadataFlag TxMetadataDetailedSchema) createUnsignedTransaction :: forall style w. diff --git a/lib/core/src/Cardano/Wallet/Api/Server.hs b/lib/core/src/Cardano/Wallet/Api/Server.hs index e4592669a69..d6bb23c1d5a 100644 --- a/lib/core/src/Cardano/Wallet/Api/Server.hs +++ b/lib/core/src/Cardano/Wallet/Api/Server.hs @@ -330,6 +330,8 @@ import Cardano.Wallet.Api.Types , toApiNetworkParameters , toApiUtxoStatistics ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..), TxMetadataWithSchema (TxMetadataWithSchema) ) import Cardano.Wallet.CoinSelection ( SelectionBalanceError (..) , SelectionCollateralError @@ -2002,7 +2004,7 @@ postTransactionOld postTransactionOld ctx genChange (ApiT wid) body = do let pwd = coerce $ body ^. #passphrase . #getApiT let outs = addressAmountToTxOut <$> body ^. #payments - let md = body ^? #metadata . traverse . #getApiT + let md = body ^? #metadata . traverse . #txMetadataWithSchema_metadata let mTTL = body ^? #timeToLive . traverse . #getQuantity (wdrl, mkRwdAcct) <- @@ -2046,8 +2048,8 @@ postTransactionOld ctx genChange (ApiT wid) body = do liftIO $ mkApiTransaction (timeInterpreter $ ctx ^. networkLayer) - (#pendingSince) - MkApiTransactionParams + #pendingSince + $ MkApiTransactionParams { txId = tx ^. #txId , txFee = tx ^. #fee , txInputs = NE.toList $ second Just <$> sel ^. #inputs @@ -2061,6 +2063,7 @@ postTransactionOld ctx genChange (ApiT wid) body = do , txTime , txScriptValidity = tx ^. #scriptValidity , txDeposit = W.stakeKeyDeposit pp + , txMetadataSchema = TxMetadataDetailedSchema } where ti :: TimeInterpreter (ExceptT PastHorizonException IO) @@ -2085,18 +2088,26 @@ listTransactions -> Maybe Iso8601Time -> Maybe Iso8601Time -> Maybe (ApiT SortOrder) + -> TxMetadataSchema -> Handler [ApiTransaction n] -listTransactions ctx (ApiT wid) mMinWithdrawal mStart mEnd mOrder = do - (txs, depo) <- withWorkerCtx ctx wid liftE liftE $ \wrk -> do - txs <- liftHandler $ - W.listTransactions @_ @_ @_ wrk wid - (Coin . fromIntegral . getMinWithdrawal <$> mMinWithdrawal) - (getIso8601Time <$> mStart) - (getIso8601Time <$> mEnd) - (maybe defaultSortOrder getApiT mOrder) - depo <- liftIO $ W.stakeKeyDeposit <$> NW.currentProtocolParameters (wrk ^. networkLayer) - pure (txs, depo) - liftIO $ mapM (mkApiTransactionFromInfo (timeInterpreter (ctx ^. networkLayer)) depo) txs +listTransactions + ctx (ApiT wid) mMinWithdrawal mStart mEnd mOrder metadataSchema = do + (txs, depo) <- withWorkerCtx ctx wid liftE liftE $ \wrk -> do + txs <- liftHandler $ + W.listTransactions @_ @_ @_ wrk wid + (Coin . fromIntegral . getMinWithdrawal <$> mMinWithdrawal) + (getIso8601Time <$> mStart) + (getIso8601Time <$> mEnd) + (maybe defaultSortOrder getApiT mOrder) + depo <- liftIO $ W.stakeKeyDeposit <$> + NW.currentProtocolParameters (wrk ^. networkLayer) + pure (txs, depo) + liftIO $ forM txs $ \tx -> + mkApiTransactionFromInfo + (timeInterpreter (ctx ^. networkLayer)) + depo + tx + metadataSchema where defaultSortOrder :: SortOrder defaultSortOrder = Descending @@ -2106,13 +2117,18 @@ getTransaction => ctx -> ApiT WalletId -> ApiTxId + -> TxMetadataSchema -> Handler (ApiTransaction n) -getTransaction ctx (ApiT wid) (ApiTxId (ApiT (tid))) = do +getTransaction ctx (ApiT wid) (ApiTxId (ApiT (tid))) metadataSchema = do (tx, depo) <- withWorkerCtx ctx wid liftE liftE $ \wrk -> do tx <- liftHandler $ W.getTransaction wrk wid tid - depo <- liftIO $ W.stakeKeyDeposit <$> NW.currentProtocolParameters (wrk ^. networkLayer) + depo <- liftIO $ W.stakeKeyDeposit <$> + NW.currentProtocolParameters (wrk ^. networkLayer) pure (tx, depo) - liftIO $ mkApiTransactionFromInfo (timeInterpreter (ctx ^. networkLayer)) depo tx + liftIO + $ mkApiTransactionFromInfo + (timeInterpreter (ctx ^. networkLayer)) depo tx + metadataSchema -- Populate an API transaction record with 'TransactionInfo' from the wallet -- layer. @@ -2121,9 +2137,12 @@ mkApiTransactionFromInfo => TimeInterpreter (ExceptT PastHorizonException IO) -> Coin -> TransactionInfo + -> TxMetadataSchema -> m (ApiTransaction n) -mkApiTransactionFromInfo ti deposit info = do - apiTx <- liftIO $ mkApiTransaction ti status +mkApiTransactionFromInfo ti deposit info metadataSchema = do + apiTx <- liftIO $ mkApiTransaction + ti + status MkApiTransactionParams { txId = info ^. #txInfoId , txFee = info ^. #txInfoFee @@ -2137,6 +2156,7 @@ mkApiTransactionFromInfo ti deposit info = do , txTime = info ^. #txInfoTime , txScriptValidity = info ^. #txInfoScriptValidity , txDeposit = deposit + , txMetadataSchema = metadataSchema } return $ case info ^. (#txInfoMeta . #status) of Pending -> apiTx @@ -2167,7 +2187,10 @@ postTransactionFeeOld ctx (ApiT wid) body = do (wdrl, _) <- mkRewardAccountBuilder @_ @s @_ @n ctx wid (body ^. #withdrawal) let txCtx = defaultTransactionCtx { txWithdrawal = wdrl - , txMetadata = getApiT <$> body ^. #metadata + , txMetadata + = body ^? #metadata + . traverse + . #txMetadataWithSchema_metadata } withWorkerCtx ctx wid liftE liftE $ \wrk -> do (utxoAvailable, wallet, pendingTxs) <- @@ -2228,7 +2251,7 @@ constructTransaction ctx genChange knownPools getPoolStatus (ApiT wid) body = do let retrieveAllCosigners = foldScript (:) [] let wrongMintingTemplate (ApiMintBurnData (ApiT scriptTempl) _ _) = isLeft (validateScriptOfTemplate RecommendedValidation scriptTempl) - || length (retrieveAllCosigners scriptTempl) > 1 + || length (retrieveAllCosigners scriptTempl) /= 1 || (L.any (/= Cosigner 0)) (retrieveAllCosigners scriptTempl) when ( isJust mintingBurning' && @@ -2268,7 +2291,7 @@ constructTransaction ctx genChange knownPools getPoolStatus (ApiT wid) body = do when notall0Haccount $ liftHandler $ throwE ErrConstructTxMultiaccountNotSupported - let md = body ^? #metadata . traverse . #getApiT + let md = body ^? #metadata . traverse . #txMetadataWithSchema_metadata let isValidityBoundTimeNegative (ApiValidityBoundAsTimeFromNow (Quantity sec)) = sec < 0 @@ -2964,6 +2987,7 @@ joinStakePool ctx knownPools getPoolStatus apiPoolId (ApiT wid) body = do , txTime , txScriptValidity = tx ^. #scriptValidity , txDeposit = W.stakeKeyDeposit pp + , txMetadataSchema = TxMetadataDetailedSchema } where ti :: TimeInterpreter (ExceptT PastHorizonException IO) @@ -3081,6 +3105,7 @@ quitStakePool ctx (ApiT wid) body = do , txTime , txScriptValidity = tx ^. #scriptValidity , txDeposit = W.stakeKeyDeposit pp + , txMetadataSchema = TxMetadataDetailedSchema } where ti :: TimeInterpreter (ExceptT PastHorizonException IO) @@ -3338,6 +3363,7 @@ migrateWallet ctx withdrawalType (ApiT wid) postData = do , txTime , txScriptValidity = tx ^. #scriptValidity , txDeposit = W.stakeKeyDeposit pp + , txMetadataSchema = TxMetadataDetailedSchema } where addresses = getApiT . fst <$> view #addresses postData @@ -3794,6 +3820,7 @@ data MkApiTransactionParams = MkApiTransactionParams , txTime :: UTCTime , txScriptValidity :: Maybe W.TxScriptValidity , txDeposit :: Coin + , txMetadataSchema :: TxMetadataSchema } deriving (Eq, Generic, Show) @@ -3844,7 +3871,8 @@ mkApiTransaction timeInterpreter setTimeReference tx = do toAddressAmount @n <$> tx ^. #txCollateralOutput , withdrawals = mkApiWithdrawal @n <$> Map.toList (tx ^. #txWithdrawals) , status = ApiT (tx ^. (#txMeta . #status)) - , metadata = ApiTxMetadata $ ApiT <$> (tx ^. #txMetadata) + , metadata = TxMetadataWithSchema (tx ^. #txMetadataSchema) + <$> tx ^. #txMetadata , scriptValidity = ApiT <$> tx ^. #txScriptValidity } diff --git a/lib/core/src/Cardano/Wallet/Api/Types.hs b/lib/core/src/Cardano/Wallet/Api/Types.hs index 30fd5a3f0a1..3ee6bd30295 100644 --- a/lib/core/src/Cardano/Wallet/Api/Types.hs +++ b/lib/core/src/Cardano/Wallet/Api/Types.hs @@ -270,6 +270,8 @@ import Cardano.Mnemonic , mnemonicToText , natVals ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataWithSchema ) import Cardano.Wallet.Primitive.AddressDerivation ( Depth (..) , DerivationIndex (..) @@ -958,7 +960,7 @@ data ApiMultiDelegationAction data ApiConstructTransactionData (n :: NetworkDiscriminant) = ApiConstructTransactionData { payments :: !(Maybe (ApiPaymentDestination n)) , withdrawal :: !(Maybe ApiWithdrawalPostData) - , metadata :: !(Maybe (ApiT TxMetadata)) + , metadata :: !(Maybe TxMetadataWithSchema) , mintBurn :: !(Maybe (NonEmpty (ApiMintBurnData n))) , delegations :: !(Maybe (NonEmpty ApiMultiDelegationAction)) , validityInterval :: !(Maybe ApiValidityInterval) @@ -1003,7 +1005,7 @@ data PostTransactionOldData (n :: NetworkDiscriminant) = PostTransactionOldData { payments :: !(NonEmpty (ApiTxOutput n)) , passphrase :: !(ApiT (Passphrase "lenient")) , withdrawal :: !(Maybe ApiWithdrawalPostData) - , metadata :: !(Maybe (ApiT TxMetadata)) + , metadata :: !(Maybe TxMetadataWithSchema) , timeToLive :: !(Maybe (Quantity "second" NominalDiffTime)) } deriving (Eq, Generic, Show, Typeable) @@ -1011,7 +1013,7 @@ data PostTransactionOldData (n :: NetworkDiscriminant) = PostTransactionOldData data PostTransactionFeeOldData (n :: NetworkDiscriminant) = PostTransactionFeeOldData { payments :: !(NonEmpty (ApiTxOutput n)) , withdrawal :: !(Maybe ApiWithdrawalPostData) - , metadata :: !(Maybe (ApiT TxMetadata)) + , metadata :: !(Maybe TxMetadataWithSchema ) , timeToLive :: !(Maybe (Quantity "second" NominalDiffTime)) } deriving (Eq, Generic, Show, Typeable) @@ -1154,7 +1156,7 @@ data ApiTransaction (n :: NetworkDiscriminant) = ApiTransaction !(ApiAsArray "collateral_outputs" (Maybe (ApiTxOutput n))) , withdrawals :: ![ApiWithdrawal n] , status :: !(ApiT TxStatus) - , metadata :: !ApiTxMetadata + , metadata :: !(Maybe TxMetadataWithSchema) , scriptValidity :: !(Maybe (ApiT TxScriptValidity)) } deriving (Eq, Generic, Show, Typeable) diff --git a/lib/core/src/Cardano/Wallet/Api/Types/SchemaMetadata.hs b/lib/core/src/Cardano/Wallet/Api/Types/SchemaMetadata.hs new file mode 100644 index 00000000000..19ca44e6e45 --- /dev/null +++ b/lib/core/src/Cardano/Wallet/Api/Types/SchemaMetadata.hs @@ -0,0 +1,93 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE DeriveAnyClass #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE DerivingStrategies #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE KindSignatures #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE NoMonomorphismRestriction #-} +{-# LANGUAGE StrictData #-} + +-- | +-- Copyright: Β© 2018-2022 IOHK +-- License: Apache-2.0 +-- +-- A wrapper around TxMetadata to allow different JSON codecs. (ADP-1596) +-- see https://github.com/input-output-hk/cardano-node/blob/master/cardano-api/src/Cardano/Api/TxMetadata.hs +module Cardano.Wallet.Api.Types.SchemaMetadata where + +import Cardano.Api + ( Error (displayError) + , TxMetadataJsonSchema (..) + , metadataFromJson + , metadataToJson + ) +import Cardano.Wallet.Primitive.Types.Tx + ( TxMetadata ) +import Control.Applicative + ( liftA2, (<|>) ) +import Control.DeepSeq + ( NFData ) +import Data.Aeson + ( FromJSON (parseJSON), ToJSON (toJSON) ) +import GHC.Generics + ( Generic ) +import Prelude + +-- | A tag to select the json codec +data TxMetadataSchema = TxMetadataNoSchema | TxMetadataDetailedSchema + deriving (Show, Eq, Generic, NFData) + +-- | A wrapper to drive the json codec of metadata +data TxMetadataWithSchema = TxMetadataWithSchema + { -- | How to codec the metadata into json + txMetadataWithSchema_schema :: TxMetadataSchema + , -- | The metadata + txMetadataWithSchema_metadata :: TxMetadata + } + deriving (Show, Eq, Generic, NFData) + +-- | Parses a Boolean "simple-metadata" API flag. +-- +-- prop> toSimpleMetadataFlag . parseSimpleMetadataFlag == id +-- prop> parseSimpleMetadataFlag . toSimpleMetadataFlag == id +-- +parseSimpleMetadataFlag :: Bool -> TxMetadataSchema +parseSimpleMetadataFlag flag = + if flag + then TxMetadataNoSchema + else TxMetadataDetailedSchema + +-- | Produces a Boolean "simple-metadata" API flag. +-- +-- prop> toSimpleMetadataFlag . parseSimpleMetadataFlag == id +-- prop> parseSimpleMetadataFlag . toSimpleMetadataFlag == id +-- +toSimpleMetadataFlag :: TxMetadataSchema -> Bool +toSimpleMetadataFlag = \case + TxMetadataNoSchema -> True + TxMetadataDetailedSchema -> False + +instance ToJSON TxMetadataWithSchema where + toJSON (TxMetadataWithSchema TxMetadataDetailedSchema x) = + metadataToJson TxMetadataJsonDetailedSchema x + toJSON (TxMetadataWithSchema TxMetadataNoSchema x) = + metadataToJson TxMetadataJsonNoSchema x + +detailedMetadata :: TxMetadata -> TxMetadataWithSchema +detailedMetadata = TxMetadataWithSchema TxMetadataDetailedSchema + +noSchemaMetadata :: TxMetadata -> TxMetadataWithSchema +noSchemaMetadata = TxMetadataWithSchema TxMetadataNoSchema + +instance FromJSON TxMetadataWithSchema where + parseJSON = liftA2 + (<|>) + (fmap detailedMetadata + . either (fail . displayError) pure + . metadataFromJson TxMetadataJsonDetailedSchema + ) + (fmap noSchemaMetadata + . either (fail . displayError) pure + . metadataFromJson TxMetadataJsonNoSchema + ) diff --git a/lib/core/src/Cardano/Wallet/DB/Sqlite/Migration.hs b/lib/core/src/Cardano/Wallet/DB/Sqlite/Migration.hs index d0d7e3771df..7d4fb9487c6 100644 --- a/lib/core/src/Cardano/Wallet/DB/Sqlite/Migration.hs +++ b/lib/core/src/Cardano/Wallet/DB/Sqlite/Migration.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE BlockArguments #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DerivingStrategies #-} diff --git a/lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/MintBurn.hs b/lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/MintBurn.hs index 39de473447f..ccb4d1d9def 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/MintBurn.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/MintBurn.hs @@ -92,6 +92,7 @@ import qualified Data.List as L import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as Map + -- | Purpose for forged policy keys is a constant set to 1855' (or 0x8000073F) -- following the original CIP-1855: "Forging policy keys for HD Wallets". -- @@ -216,9 +217,9 @@ scriptSlotIntervals = \case $ I.intersections (concatMap scriptSlotIntervals timelocks) : concatMap scriptSlotIntervals rest RequireAnyOf xs -> - trimAllSlots $ concatMap scriptSlotIntervals (filterOutSig xs) + trimAllSlots $ concatMap scriptSlotIntervals xs RequireSomeOf _ xs -> - trimAllSlots $ concatMap scriptSlotIntervals (filterOutSig xs) + trimAllSlots $ concatMap scriptSlotIntervals xs ActiveFromSlot s -> [I.Finite s <=..<= maxSlot] ActiveUntilSlot s -> @@ -228,10 +229,6 @@ scriptSlotIntervals = \case maxSlot = I.Finite $ intCast $ maxBound @Word64 allSlots = minSlot <=..<= maxSlot - isNotSig = \case - RequireSignatureOf _ -> False - _ -> True - isTimelockOrSig = \case ActiveFromSlot _ -> True ActiveUntilSlot _ -> True @@ -245,8 +242,6 @@ scriptSlotIntervals = \case then interval else notAllSlots - filterOutSig = filter isNotSig - -- tx validity interval must be a subset of a interval from script's timelock -- tx validity interval is defined by specifying (from,to) slot interval withinSlotInterval diff --git a/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json b/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json index a3c2e5acc3a..567c2c28be2 100644 --- a/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json +++ b/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json @@ -1,114 +1,85 @@ { - "seed": -4866043259366810391, + "seed": 7813698319704820484, "samples": [ { - "status": "expired", + "status": "pending", "withdrawals": [], "deposit_taken": { - "quantity": 97, + "quantity": 127, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "collateral_outputs": [ - { - "address": "", - "amount": { - "quantity": 1, - "unit": "lovelace" - }, - "assets": [] - } - ], + "direction": "outgoing", + "collateral_outputs": [], "outputs": [], "expires_at": { - "epoch_number": 12051, - "time": "1865-05-09T02:00:00Z", - "absolute_slot_number": 2739720, - "slot_number": 29170 + "epoch_number": 9556, + "time": "1892-02-22T14:13:44Z", + "absolute_slot_number": 1949529, + "slot_number": 29282 }, "script_validity": "valid", - "pending_since": { - "height": { - "quantity": 29707, - "unit": "block" - }, - "epoch_number": 2014, - "time": "1863-07-06T18:00:00Z", - "absolute_slot_number": 16263, - "slot_number": 23641 - }, - "id": "333e513456230a65786634c133550055946018190f7f623d8a172dcf05661523", + "id": "39bb2156335a6d3f50417f111e624529c96546335b1ba612f94d3d496072154b", "depth": { - "quantity": 20102, + "quantity": 18636, "unit": "block" }, "amount": { - "quantity": 99, + "quantity": 201, "unit": "lovelace" }, "fee": { - "quantity": 211, + "quantity": 228, "unit": "lovelace" }, "deposit_returned": { - "quantity": 205, + "quantity": 103, "unit": "lovelace" }, - "metadata": { - "3": { - "bytes": "1e4872067a15091eba67512357" - } - }, "collateral": [] }, { "status": "in_ledger", "withdrawals": [], "deposit_taken": { - "quantity": 35, + "quantity": 65, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", + "direction": "outgoing", "collateral_outputs": [], "outputs": [], "script_validity": "valid", - "id": "3c4d1877492b0c4b4d33591b73014e10293e0a0f0d791a142ee5379d707d4655", + "id": "6b396905187c5120310a646943056f7e091937c424375f4d072c327d6572565d", "depth": { - "quantity": 19453, + "quantity": 13480, "unit": "block" }, "amount": { - "quantity": 179, + "quantity": 14, "unit": "lovelace" }, - "inserted_at": { - "height": { - "quantity": 22669, - "unit": "block" - }, - "epoch_number": 25931, - "time": "1893-12-16T12:27:11.705258843013Z", - "absolute_slot_number": 13453142, - "slot_number": 30689 - }, "fee": { - "quantity": 42, + "quantity": 36, "unit": "lovelace" }, "deposit_returned": { - "quantity": 123, + "quantity": 5, "unit": "lovelace" }, - "metadata": null, + "metadata": { + "8": [ + [], + "0x66df47000133531548042d6c7f565c677451220e63044f2db54159314c1e7e431d465252621f1e401b5d4a4f0513f06a6fb6643acd" + ] + }, "collateral": [] }, { - "status": "in_ledger", + "status": "pending", "withdrawals": [], "deposit_taken": { - "quantity": 138, + "quantity": 198, "unit": "lovelace" }, "inputs": [], @@ -117,75 +88,54 @@ { "address": "", "amount": { - "quantity": 1, + "quantity": 0, "unit": "lovelace" }, "assets": [] } ], "outputs": [], - "id": "3c3c09e4a25d10603c1d4d23155e2d01221212102b730c1b855d263f33096c25", - "depth": { - "quantity": 13180, - "unit": "block" - }, - "amount": { - "quantity": 117, - "unit": "lovelace" + "expires_at": { + "epoch_number": 1426, + "time": "1882-08-17T12:27:02.40792126994Z", + "absolute_slot_number": 11506716, + "slot_number": 30441 }, - "inserted_at": { + "script_validity": "invalid", + "pending_since": { "height": { - "quantity": 7240, + "quantity": 27459, "unit": "block" }, - "epoch_number": 6107, - "time": "1901-05-01T08:00:00Z", - "absolute_slot_number": 13879225, - "slot_number": 6756 + "epoch_number": 3157, + "time": "1869-04-26T07:41:50Z", + "absolute_slot_number": 3110251, + "slot_number": 4180 + }, + "id": "730626412a4b5a504a452a174c644a4f503812bf725273f02130794869013134", + "amount": { + "quantity": 198, + "unit": "lovelace" }, "fee": { - "quantity": 104, + "quantity": 66, "unit": "lovelace" }, "deposit_returned": { - "quantity": 100, + "quantity": 150, "unit": "lovelace" }, "metadata": { - "16": { - "map": [ + "19": { + "list": [ + { + "list": [] + }, { - "k": { - "string": "놱" - }, - "v": { - "map": [ - { - "k": { - "string": "π’‚„τ‹±”" - }, - "v": { - "int": -2 - } - }, - { - "k": { - "string": "τ…¨²" - }, - "v": { - "bytes": "9a3eda7418653035195403693e7d06cc7fd846372ef0831538584848584c509f3707ca" - } - } - ] - } + "int": 3 }, { - "k": { - "string": "τ‹©" - }, - "v": { - "list": [] - } + "map": [] } ] } @@ -193,278 +143,206 @@ "collateral": [] }, { - "status": "in_ledger", + "status": "expired", "withdrawals": [], "deposit_taken": { - "quantity": 98, + "quantity": 152, "unit": "lovelace" }, "inputs": [], - "direction": "outgoing", + "direction": "incoming", "collateral_outputs": [ { "address": "", "amount": { - "quantity": 1, + "quantity": 0, "unit": "lovelace" }, "assets": [] } ], "outputs": [], - "id": "4e710a6706041733342dfe1031303d2a544b351d42a4032a7e26ed7d7601505f", + "expires_at": { + "epoch_number": 7422, + "time": "1875-10-11T01:34:20.761696884932Z", + "absolute_slot_number": 8948876, + "slot_number": 4149 + }, + "pending_since": { + "height": { + "quantity": 6171, + "unit": "block" + }, + "epoch_number": 1879, + "time": "1875-02-20T12:40:41Z", + "absolute_slot_number": 8329297, + "slot_number": 26906 + }, + "id": "232d711d7d10105d532c57995864a33b09f576720af522571366b71241211744", "depth": { - "quantity": 6092, + "quantity": 21297, "unit": "block" }, "amount": { - "quantity": 48, + "quantity": 32, "unit": "lovelace" }, - "inserted_at": { - "height": { - "quantity": 25614, - "unit": "block" - }, - "epoch_number": 149, - "time": "1881-03-29T04:00:00Z", - "absolute_slot_number": 15333495, - "slot_number": 27299 - }, "fee": { - "quantity": 160, + "quantity": 77, "unit": "lovelace" }, "deposit_returned": { - "quantity": 196, + "quantity": 175, "unit": "lovelace" }, "metadata": { - "0": { - "bytes": "20338500314ef79005257e1323530414eb324163" + "7": { + "list": [ + { + "int": 3 + }, + { + "int": -2 + } + ] } }, "collateral": [] }, { - "status": "expired", + "status": "pending", "withdrawals": [], "deposit_taken": { - "quantity": 226, + "quantity": 2, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "collateral_outputs": [ - { - "address": "", - "amount": { - "quantity": 1, - "unit": "lovelace" - }, - "assets": [] - } - ], + "direction": "outgoing", + "collateral_outputs": [], "outputs": [], - "expires_at": { - "epoch_number": 22745, - "time": "1873-08-02T13:50:18.347642065807Z", - "absolute_slot_number": 7809228, - "slot_number": 18661 - }, - "script_validity": "invalid", "pending_since": { "height": { - "quantity": 9960, + "quantity": 12707, "unit": "block" }, - "epoch_number": 25945, - "time": "1865-11-10T02:20:52Z", - "absolute_slot_number": 12904136, - "slot_number": 31431 + "epoch_number": 32471, + "time": "1889-05-03T07:25:25Z", + "absolute_slot_number": 10025182, + "slot_number": 16446 + }, + "id": "6406251723321637544246e11dcd256868175f1130532de0064d5217233e077f", + "depth": { + "quantity": 25779, + "unit": "block" }, - "id": "544f391b4f3734425971f9728b273c2c7f10792608b735ffa90848b04c667f5b", "amount": { - "quantity": 29, + "quantity": 223, "unit": "lovelace" }, "fee": { - "quantity": 162, + "quantity": 109, "unit": "lovelace" }, "deposit_returned": { - "quantity": 239, + "quantity": 105, "unit": "lovelace" }, "metadata": { - "30": { - "int": 0 - } + "12": "0x4a720a3730db54264c6b6b4f170eb619026556f175571475551a701062ecd11403651e1726c303436d79283a3a011f4d610046c10b0c006e" }, "collateral": [] }, { - "status": "pending", + "status": "in_ledger", "withdrawals": [], "deposit_taken": { - "quantity": 179, + "quantity": 193, "unit": "lovelace" }, "inputs": [], "direction": "outgoing", - "collateral_outputs": [ - { - "address": "", - "amount": { - "quantity": 1, - "unit": "lovelace" - }, - "assets": [] - } - ], + "collateral_outputs": [], "outputs": [], - "expires_at": { - "epoch_number": 6016, - "time": "1905-07-18T01:30:18.320653333638Z", - "absolute_slot_number": 11387964, - "slot_number": 31694 - }, - "script_validity": "invalid", - "pending_since": { - "height": { - "quantity": 665, - "unit": "block" - }, - "epoch_number": 24248, - "time": "1868-10-24T05:00:00Z", - "absolute_slot_number": 7833053, - "slot_number": 22284 - }, - "id": "05cf64731802676543021a032a4db857453f790a36505405193d71904b2e227c", + "script_validity": "valid", + "id": "06c43db86f07432b66173cdc724cec004b750b14f50c01500937604616004b58", "depth": { - "quantity": 15790, + "quantity": 25392, "unit": "block" }, "amount": { - "quantity": 73, + "quantity": 55, "unit": "lovelace" }, + "inserted_at": { + "height": { + "quantity": 31447, + "unit": "block" + }, + "epoch_number": 3183, + "time": "1885-06-01T17:42:45.126273899983Z", + "absolute_slot_number": 6202870, + "slot_number": 11272 + }, "fee": { - "quantity": 20, + "quantity": 120, "unit": "lovelace" }, "deposit_returned": { - "quantity": 192, + "quantity": 84, "unit": "lovelace" }, - "metadata": null, + "metadata": { + "12": {} + }, "collateral": [] }, { - "status": "pending", + "status": "expired", "withdrawals": [], "deposit_taken": { - "quantity": 25, + "quantity": 232, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "collateral_outputs": [ - { - "address": "", - "amount": { - "quantity": 1, - "unit": "lovelace" - }, - "assets": [] - } - ], + "direction": "outgoing", + "collateral_outputs": [], "outputs": [], "expires_at": { - "epoch_number": 26760, - "time": "1884-12-28T14:37:01Z", - "absolute_slot_number": 14693967, - "slot_number": 13746 + "epoch_number": 11379, + "time": "1884-04-06T09:02:07Z", + "absolute_slot_number": 11373412, + "slot_number": 13143 }, - "script_validity": "invalid", - "id": "42525c622b249f5d6f6b79277b3245698c358c222779734a7178702a2d10bb01", + "script_validity": "valid", + "id": "fd55ea5b3c7c053e7b4ae4197508564609700015547b07214d442962fb047a72", "depth": { - "quantity": 31946, + "quantity": 16681, "unit": "block" }, "amount": { - "quantity": 31, + "quantity": 238, "unit": "lovelace" }, "fee": { - "quantity": 177, + "quantity": 159, "unit": "lovelace" }, "deposit_returned": { - "quantity": 23, + "quantity": 24, "unit": "lovelace" }, "metadata": { - "15": { - "list": [ - { - "list": [ - { - "map": [ - { - "k": { - "string": "󾔬" - }, - "v": { - "int": -1 - } - } - ] - }, - { - "map": [ - { - "k": { - "string": "󲬧" - }, - "v": { - "string": "τ‰†§α £" - } - } - ] - } - ] - }, - { - "map": [ - { - "k": { - "string": "π£Ό’" - }, - "v": { - "bytes": "3a3d6e1869665e3c48084b6b285fe070c85d692c64245110399b10187630c6b555c504f519171f6b" - } - }, - { - "k": { - "string": "τ”­τ‡Ž³" - }, - "v": { - "list": [] - } - } - ] - } - ] + "14": { + "σ·­˜σΈ™›": -3 } }, "collateral": [] }, { - "status": "expired", + "status": "in_ledger", "withdrawals": [], "deposit_taken": { - "quantity": 56, + "quantity": 139, "unit": "lovelace" }, "inputs": [], @@ -480,156 +358,145 @@ } ], "outputs": [], - "expires_at": { - "epoch_number": 29568, - "time": "1877-03-10T20:26:10.065320592865Z", - "absolute_slot_number": 13492235, - "slot_number": 15038 - }, - "script_validity": "invalid", - "pending_since": { - "height": { - "quantity": 30456, - "unit": "block" - }, - "epoch_number": 17540, - "time": "1888-06-30T12:14:54Z", - "absolute_slot_number": 6500318, - "slot_number": 10773 - }, - "id": "525f013fb1145e2a1c375b044e1f39474d3b200b4c1e35ea415934701b452db4", + "script_validity": "valid", + "id": "ee0a7671857349e065514751570c5f813f11c6786f06f947642c452126283d09", "depth": { - "quantity": 16438, + "quantity": 29515, "unit": "block" }, "amount": { - "quantity": 116, + "quantity": 18, "unit": "lovelace" }, + "inserted_at": { + "height": { + "quantity": 25904, + "unit": "block" + }, + "epoch_number": 7032, + "time": "1894-02-12T07:37:34Z", + "absolute_slot_number": 8232121, + "slot_number": 17689 + }, "fee": { - "quantity": 208, + "quantity": 177, "unit": "lovelace" }, "deposit_returned": { - "quantity": 166, + "quantity": 154, "unit": "lovelace" }, "metadata": { - "17": { - "list": [ - { - "bytes": "59406d292666c4042128056e3fe775e53d453f440f6d1b32043d16" - }, - { - "list": [ - { - "list": [] - } - ] - }, - { - "list": [ - { - "map": [ - { - "k": { - "string": "β²™" - }, - "v": { - "int": 1 - } - } - ] - }, - { - "list": [] - } - ] - } - ] + "12": { + "σ²ˆ’σΌž™": "κ“«π¦·˜π—“©", + "σΌž”π¦™§ε˜©": [ + {}, + [] + ], + "π–Ό§π§›•σΌ©π°΄Š": {} } }, "collateral": [] }, { - "status": "in_ledger", + "status": "pending", "withdrawals": [], "deposit_taken": { - "quantity": 91, + "quantity": 224, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "collateral_outputs": [], + "direction": "outgoing", + "collateral_outputs": [ + { + "address": "", + "amount": { + "quantity": 1, + "unit": "lovelace" + }, + "assets": [] + } + ], "outputs": [], - "id": "30756306481722033039be407e9252975636748f6d7375274b387a502e04385c", + "script_validity": "invalid", + "id": "1d471a5f06eed342b03fbd7d76551d2518465c69312838216257444f7b52fe1f", "amount": { - "quantity": 149, + "quantity": 165, "unit": "lovelace" }, - "inserted_at": { - "height": { - "quantity": 30713, - "unit": "block" - }, - "epoch_number": 3487, - "time": "1884-10-04T02:51:13.770809250957Z", - "absolute_slot_number": 12100795, - "slot_number": 31394 - }, "fee": { - "quantity": 118, + "quantity": 75, "unit": "lovelace" }, "deposit_returned": { - "quantity": 86, + "quantity": 224, "unit": "lovelace" }, - "metadata": null, + "metadata": { + "29": { + "σΉ¨Έ": "τŒ½‹π™…" + } + }, "collateral": [] }, { - "status": "pending", + "status": "in_ledger", "withdrawals": [], "deposit_taken": { - "quantity": 143, + "quantity": 162, "unit": "lovelace" }, "inputs": [], "direction": "incoming", - "collateral_outputs": [], + "collateral_outputs": [ + { + "address": "", + "amount": { + "quantity": 0, + "unit": "lovelace" + }, + "assets": [] + } + ], "outputs": [], "script_validity": "invalid", - "pending_since": { - "height": { - "quantity": 27204, - "unit": "block" - }, - "epoch_number": 7607, - "time": "1883-06-01T22:56:55Z", - "absolute_slot_number": 2343272, - "slot_number": 18457 - }, - "id": "d1443c7e29399e375d54d7242a1442a53539607d8f6951f929a5b3414037aa61", + "id": "6f5a4a2d500f115e040ce75c324b462d7371601f61449442294fadee25360240", "depth": { - "quantity": 28258, + "quantity": 3156, "unit": "block" }, "amount": { - "quantity": 172, + "quantity": 28, "unit": "lovelace" }, + "inserted_at": { + "height": { + "quantity": 28374, + "unit": "block" + }, + "epoch_number": 26759, + "time": "1889-10-08T23:29:20Z", + "absolute_slot_number": 4999981, + "slot_number": 28286 + }, "fee": { - "quantity": 199, + "quantity": 189, "unit": "lovelace" }, "deposit_returned": { - "quantity": 226, + "quantity": 124, "unit": "lovelace" }, "metadata": { "1": { - "string": "𫘚" + "list": [ + { + "list": [] + }, + { + "bytes": "cecb737a5ecb2a149d943df4557cd0dd13243d4c7c053d477b7d3d0a401773699471115ab543006d7c9bca026f0c6c5af10e24" + } + ] } }, "collateral": [] diff --git a/lib/core/test/data/Cardano/Wallet/Api/TxMetadataWithSchema.json b/lib/core/test/data/Cardano/Wallet/Api/TxMetadataWithSchema.json new file mode 100644 index 00000000000..ae3056638ed --- /dev/null +++ b/lib/core/test/data/Cardano/Wallet/Api/TxMetadataWithSchema.json @@ -0,0 +1,150 @@ +{ + "seed": -8485690430696820165, + "samples": [ + { + "12": { + "list": [ + { + "map": [ + { + "k": { + "string": "πŽ™σΈ›Žο›†" + }, + "v": { + "string": "τ¨—" + } + }, + { + "k": { + "string": "󰐲σΊ¨ͺ" + }, + "v": { + "map": [] + } + } + ] + } + ] + } + }, + { + "25": { + "int": 0 + } + }, + { + "2": { + "string": "π­΅‹" + } + }, + { + "9": { + "map": [ + { + "k": { + "string": "󢁃𬡰" + }, + "v": { + "map": [ + { + "k": { + "string": "𑄏" + }, + "v": { + "int": -1 + } + }, + { + "k": { + "string": "π’’‘π€˜¨γ»†" + }, + "v": { + "bytes": "4675370072112c1e5c996d27076a5e8b02bf93445f1d30295c41392c4e3c7518473e1f833f2b103c4e65" + } + } + ] + } + }, + { + "k": { + "string": "󿨄䳾" + }, + "v": { + "bytes": "695c387a12d41a1f19653c007e7e1831544701757340863e30652f331858" + } + }, + { + "k": { + "string": "τ†›„" + }, + "v": { + "map": [ + { + "k": { + "string": "󲚚᱿" + }, + "v": { + "list": [] + } + } + ] + } + } + ] + } + }, + { + "1": { + "list": [ + { + "map": [] + } + ] + } + }, + { + "0": { + "bytes": "5409b84d6d661f3a215b4d6516713467b0cb9465145c79474c0d181f" + } + }, + { + "18": { + "list": [ + { + "map": [ + { + "k": { + "string": "τŠ›š" + }, + "v": { + "string": "τˆ„€" + } + }, + { + "k": { + "string": "τŠœŽ" + }, + "v": { + "int": 0 + } + } + ] + } + ] + } + }, + { + "22": {} + }, + { + "18": { + "string": "α€€" + } + }, + { + "1": { + "map": [] + } + } + ] +} \ No newline at end of file diff --git a/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs b/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs index c9c0412f7aa..ad9c902098b 100644 --- a/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs @@ -199,6 +199,8 @@ import Cardano.Wallet.Api.Types , WalletPutPassphraseData (..) , toApiAsset ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..), TxMetadataWithSchema (..) ) import Cardano.Wallet.Gen ( genMnemonic , genNatural @@ -598,6 +600,7 @@ spec = parallel $ do jsonTest @ByronWalletPutPassphraseData jsonTest @SettingsPutData jsonTest @SomeByronWalletPostData + jsonTest @TxMetadataWithSchema jsonTest @WalletOrAccountPostData jsonTest @WalletPostData jsonTest @WalletPutData @@ -2157,6 +2160,11 @@ instance Arbitrary (PostTransactionOldData n) where <*> arbitrary <*> arbitrary +instance Arbitrary TxMetadataWithSchema where + arbitrary = TxMetadataWithSchema + <$> elements [TxMetadataNoSchema, TxMetadataDetailedSchema] + <*> arbitrary + instance Arbitrary (ApiConstructTransactionData n) where arbitrary = ApiConstructTransactionData <$> arbitrary @@ -2810,6 +2818,7 @@ instance Typeable n => ToSchema (ApiPutAddressesData n) where instance Typeable n => ToSchema (ApiSelectCoinsData n) where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" declareSchemaForDefinition "ApiSelectCoinsData" instance ToSchema (ApiT SmashServer) where @@ -2928,11 +2937,13 @@ instance ToSchema (ApiBytesT 'Base64 SerialisedTx) where instance Typeable n => ToSchema (PostTransactionOldData n) where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" declareSchemaForDefinition "ApiPostTransactionData" instance Typeable n => ToSchema (PostTransactionFeeOldData n) where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" declareSchemaForDefinition "ApiPostTransactionFeeData" instance Typeable n => ToSchema (ApiExternalInput n) where @@ -2944,6 +2955,7 @@ instance Typeable n => ToSchema (ApiBalanceTransactionPostData n) where instance Typeable n => ToSchema (ApiTransaction n) where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" declareSchemaForDefinition "ApiTransaction" instance ToSchema ApiUtxoStatistics where @@ -3042,6 +3054,7 @@ instance ToSchema ApiPostRandomAddressData where instance ToSchema ApiWalletSignData where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" declareSchemaForDefinition "ApiWalletSignData" instance ToSchema ApiPostAccountKeyData where @@ -3088,6 +3101,7 @@ instance ToSchema ApiAssetMintBurn where instance Typeable n => ToSchema (ApiConstructTransactionData n) where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" addDefinition =<< declareSchemaForDefinition "ScriptTemplateValue" declareSchemaForDefinition "ApiConstructTransactionData" @@ -3115,6 +3129,7 @@ instance Typeable n => ToSchema (ApiWithdrawalsGeneral n) where instance Typeable n => ToSchema (ApiDecodedTransaction n) where declareNamedSchema _ = do addDefinition =<< declareSchemaForDefinition "TransactionMetadataValue" + addDefinition =<< declareSchemaForDefinition "TransactionMetadataValueNoSchema" addDefinition =<< declareSchemaForDefinition "ScriptValue" declareSchemaForDefinition "ApiDecodedTransaction" diff --git a/lib/shelley/bench/latency-bench.hs b/lib/shelley/bench/latency-bench.hs index cf773661712..0edd0ab1970 100644 --- a/lib/shelley/bench/latency-bench.hs +++ b/lib/shelley/bench/latency-bench.hs @@ -311,8 +311,11 @@ walletApiBench capture ctx = do (_, txs) <- unsafeRequest @[ApiTransaction n] ctx (Link.listTransactions @'Shelley wal1) Empty let txid = (head txs) ^. #id - t5a <- measureApiLogs capture - (request @[ApiTransaction n] ctx (Link.getTransaction @'Shelley wal1 (ApiTxId txid)) Default Empty) + t5a <- measureApiLogs capture $ + request @[ApiTransaction n] + ctx (Link.getTransaction @'Shelley wal1 (ApiTxId txid)) + Default + Empty fmtResult "getTransaction " t5a (_, addrs) <- unsafeRequest @[ApiAddress n] ctx (Link.listAddresses @'Shelley wal2) Empty diff --git a/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs b/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs index 46c9ee63449..879fd540e02 100644 --- a/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs +++ b/lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs @@ -154,6 +154,8 @@ import Cardano.Wallet.Api.Types , SettingsPutData (..) , SomeByronWalletPostData (..) ) +import Cardano.Wallet.Api.Types.SchemaMetadata + ( TxMetadataSchema (..), parseSimpleMetadataFlag ) import Cardano.Wallet.Primitive.AddressDerivation ( DelegationAddress (..), PaymentAddress (..) ) import Cardano.Wallet.Primitive.AddressDerivation.Byron @@ -315,8 +317,16 @@ server byron icarus shelley multisig spl ntp = shelleyTransactions = constructTransaction shelley (delegationAddress @n) (knownPools spl) (getPoolLifeCycleStatus spl) :<|> signTransaction shelley - :<|> listTransactions shelley - :<|> getTransaction shelley + :<|> + (\wid mMinWithdrawal mStart mEnd mOrder simpleMetadataFlag -> + listTransactions shelley wid mMinWithdrawal mStart mEnd mOrder + (parseSimpleMetadataFlag simpleMetadataFlag) + ) + :<|> + (\wid txId simpleMetadataFlag -> + getTransaction shelley wid txId + (parseSimpleMetadataFlag simpleMetadataFlag) + ) :<|> deleteTransaction shelley :<|> postTransactionOld shelley (delegationAddress @n) :<|> postTransactionFeeOld shelley @@ -384,8 +394,8 @@ server byron icarus shelley multisig spl ntp = ) ) :<|> liftA2 (\xs ys -> fmap fst $ sortOn snd $ xs ++ ys) - (listWallets byron mkLegacyWallet) - (listWallets icarus mkLegacyWallet) + (listWallets byron mkLegacyWallet) + (listWallets icarus mkLegacyWallet) :<|> (\wid name -> withLegacyLayer wid (byron , putWallet byron mkLegacyWallet wid name) (icarus, putWallet icarus mkLegacyWallet wid name) @@ -467,13 +477,25 @@ server byron icarus shelley multisig spl ntp = (byron, signTransaction byron wid tx) (icarus, signTransaction icarus wid tx) ) - :<|> (\wid r0 r1 s -> withLegacyLayer wid - (byron , listTransactions byron wid Nothing r0 r1 s) - (icarus, listTransactions icarus wid Nothing r0 r1 s) + :<|> + (\wid r0 r1 s -> withLegacyLayer wid + ( byron + , listTransactions + byron wid Nothing r0 r1 s TxMetadataDetailedSchema + ) + ( icarus + , listTransactions + icarus wid Nothing r0 r1 s TxMetadataDetailedSchema + ) ) - :<|> (\wid txid -> withLegacyLayer wid - (byron , getTransaction byron wid txid) - (icarus, getTransaction icarus wid txid) + :<|> + (\wid txid -> withLegacyLayer wid + ( byron + , getTransaction byron wid txid TxMetadataDetailedSchema + ) + ( icarus + , getTransaction icarus wid txid TxMetadataDetailedSchema + ) ) :<|> (\wid txid -> withLegacyLayer wid (byron , deleteTransaction byron wid txid) diff --git a/nix/materialized/stack-nix/cardano-wallet-core.nix b/nix/materialized/stack-nix/cardano-wallet-core.nix index c3752048f0e..f87ccf674e6 100644 --- a/nix/materialized/stack-nix/cardano-wallet-core.nix +++ b/nix/materialized/stack-nix/cardano-wallet-core.nix @@ -173,6 +173,7 @@ "Cardano/Wallet/Api/Server" "Cardano/Wallet/Api/Server/Tls" "Cardano/Wallet/Api/Types" + "Cardano/Wallet/Api/Types/SchemaMetadata" "Cardano/Wallet/CoinSelection" "Cardano/Wallet/CoinSelection/Internal" "Cardano/Wallet/CoinSelection/Internal/Balance" diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index db72845a89e..3a639c1605c 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -1,4 +1,4 @@ -openapi: 3.0.0 +openapi: 3.0.3 info: title: Cardano Wallet Backend API version: v2022-04-27 @@ -1614,19 +1614,34 @@ x-transactionMetadata: &transactionMetadata metadata from chain, be aware that integers may exceed the javascript numeric range, and may need special "bigint" parsing. - type: object - nullable: true - additionalProperties: - $ref: "#/components/schemas/TransactionMetadataValue" + # with oneOf we get errors about the 2 schema being both valid + # anyOf seems broken trying to find properties + anyOf: + - type: object + title: full + nullable: true + additionalProperties: + $ref: "#/components/schemas/TransactionMetadataValue" + example: + 0: { "string": "cardano" } + 1: { "int": 14 } + 2: { "bytes": "2512a00e9653fe49a44a5886202e24d77eeb998f" } + 3: { "list": [ { "int": 14 }, { "int": 42 }, { "string": "1337" } ] } + 4: { "map": [{ "k": { "string": "key" }, "v": { "string": "value" }}, { "k": { "int": 14 }, "v": { "int": 42 } }] } + - type: object + title: simple + nullable: true + additionalProperties: + $ref: "#/components/schemas/TransactionMetadataValueNoSchema" + example: + 0: "cardano" + 1: 14 + 3: [14, 42 , "1337" ] + 4: {"key1": "value" , "key2": 42 } + # Note: propertyNames pattern not supported in current OpenAPI version. # propertyNames: # pattern: '^[0-9]+$' - example: - 0: { "string": "cardano" } - 1: { "int": 14 } - 2: { "bytes": "2512a00e9653fe49a44a5886202e24d77eeb998f" } - 3: { "list": [ { "int": 14 }, { "int": 42 }, { "string": "1337" } ] } - 4: { "map": [{ "k": { "string": "key" }, "v": { "string": "value" }}, { "k": { "int": 14 }, "v": { "int": 42 } }] } x-transactionTTL: &transactionTTL description: | @@ -3492,12 +3507,31 @@ components: type: array items: type: object + nullable: true properties: k: $ref: "#/components/schemas/TransactionMetadataValue" v: $ref: "#/components/schemas/TransactionMetadataValue" + TransactionMetadataValueNoSchema: &TransactionMetadataValueNoSchema + oneOf: + - title: String + type: string + + - title: Int + type: integer + + - title: List + type: array + items: + $ref: "#/components/schemas/TransactionMetadataValueNoSchema" + + - title: Map + type: object + additionalProperties: + $ref: "#/components/schemas/TransactionMetadataValueNoSchema" + ApiGetSettings: &ApiGetSettings type: object required: @@ -3821,6 +3855,17 @@ x-parametersForceNtpCheck: ¶metersForceNtpCheck schema: type: boolean +x-parametersMetadataFormat: ¶metersMetadataFormat + in: query + name: simple-metadata + schema: + type: boolean + allowEmptyValue: true + description: | + When present (or equal to true) in the query, the metadata format for the + transaction(s) in the output will be untyped plain json as specified in + [CIP 25](https://cips.cardano.org/cips/cip25/) + x-parametersMinWithdrawal: ¶metersMinWithdrawal in: query name: minWithdrawal @@ -5939,6 +5984,7 @@ paths: - *parametersEndDate - *parametersSortOrder - *parametersMinWithdrawal + - *parametersMetadataFormat responses: *responsesListTransactions /wallets/{walletId}/transactions/{transactionId}: @@ -5953,6 +5999,7 @@ paths: parameters: - *parametersWalletId - *parametersTransactionId + - *parametersMetadataFormat responses: *responsesGetTransaction delete: diff --git a/test/e2e/spec/e2e_spec.rb b/test/e2e/spec/e2e_spec.rb index d6271bdc527..fb6fedd7d3c 100644 --- a/test/e2e/spec/e2e_spec.rb +++ b/test/e2e/spec/e2e_spec.rb @@ -656,7 +656,6 @@ def get_policy_id_from_decode(tx_decoded_mint_or_burn) # Tx2: Burns 3 x 500 of each and verifies 500 of each remain on wallet # Tx3: Burns remaining 3 x 500 and verifies they're no longer on balance it "Can mint and then burn" do - pending "ADP-1738" src_before = get_shelley_balances(@wid) address = SHELLEY.addresses.list(@wid).first['id'] policy_script1 = 'cosigner#0' @@ -759,7 +758,6 @@ def get_policy_id_from_decode(tx_decoded_mint_or_burn) # Tx1: Mints 3 x 1 assets with metadata # Tx2: Burns 3 x 1 assets and also assign metadata to tx it "Can mint and burn with metadata" do - pending "ADP-1738" src_before = get_shelley_balances(@wid) address = SHELLEY.addresses.list(@wid).first['id'] policy_script1 = 'cosigner#0' @@ -843,7 +841,6 @@ def get_policy_id_from_decode(tx_decoded_mint_or_burn) # Tx2: Mints 500 more of A1 and burns 500 of A2 => A1 = 1000, A2 = 0 # Tx3: Burns remaining 1000 of A1 => A1 = 0, A2 = 0 it "Can mint and burn in the same tx" do - pending "ADP-1738" src_before = get_shelley_balances(@wid) address = SHELLEY.addresses.list(@wid).first['id'] policy_script1 = { "some" => {"at_least" => 1, "from" => [ "cosigner#0" ]} }