From 039372f0ea67ef7f1a020f1ad17acb70d35a5e5c Mon Sep 17 00:00:00 2001 From: Rodney Lorrimar Date: Thu, 27 Aug 2020 16:21:26 +1000 Subject: [PATCH 1/3] Add transaction metadata to API types --- lib/core/src/Cardano/Wallet/Api/Server.hs | 24 +- lib/core/src/Cardano/Wallet/Api/Types.hs | 26 + .../src/Cardano/Wallet/Primitive/Types.hs | 6 + .../Cardano/Wallet/Api/ApiTTxMetadata.json | 1034 +++++++++++++++++ .../Wallet/Api/ApiTransactionTestnet0.json | 595 ++++++++-- .../Cardano/Wallet/Api/ApiTxMetadata.json | 828 +++++++++++++ .../test/unit/Cardano/Wallet/Api/TypesSpec.hs | 26 +- .../Jormungandr/Scenario/API/Transactions.hs | 2 +- 8 files changed, 2442 insertions(+), 99 deletions(-) create mode 100644 lib/core/test/data/Cardano/Wallet/Api/ApiTTxMetadata.json create mode 100644 lib/core/test/data/Cardano/Wallet/Api/ApiTxMetadata.json diff --git a/lib/core/src/Cardano/Wallet/Api/Server.hs b/lib/core/src/Cardano/Wallet/Api/Server.hs index ad2433391ce..ca6a27de5d6 100644 --- a/lib/core/src/Cardano/Wallet/Api/Server.hs +++ b/lib/core/src/Cardano/Wallet/Api/Server.hs @@ -180,6 +180,7 @@ import Cardano.Wallet.Api.Types , ApiTransaction (..) , ApiTxId (..) , ApiTxInput (..) + , ApiTxMetadata (..) , ApiUtxoStatistics (..) , ApiWallet (..) , ApiWalletDelegation (..) @@ -1224,7 +1225,7 @@ postTransaction postTransaction ctx genChange (ApiT wid) body = do let pwd = coerce $ getApiT $ body ^. #passphrase let outs = coerceCoin <$> (body ^. #payments) - let md = Nothing -- fixme: implement in #2073 + let md = getApiT <$> body ^. #metadata let selfRewardCredentials (rootK, pwdP) = (getRawKey $ deriveRewardAccount @k pwdP rootK, pwdP) @@ -1264,6 +1265,7 @@ postTransaction ctx genChange (ApiT wid) body = do (tx ^. #outputs) (tx ^. #withdrawals) (meta, time) + (tx ^. #metadata) #pendingSince where ti :: TimeInterpreter IO @@ -1327,9 +1329,8 @@ mkApiTransactionFromInfo => TimeInterpreter m -> TransactionInfo -> m (ApiTransaction n) -mkApiTransactionFromInfo ti (TransactionInfo txid ins outs ws meta depth txtime _txmeta) = do - -- fixme: include _txmeta in API transaction #2074 - apiTx <- mkApiTransaction ti txid (drop2nd <$> ins) outs ws (meta, txtime) $ +mkApiTransactionFromInfo ti (TransactionInfo txid ins outs ws meta depth txtime txmeta) = do + apiTx <- mkApiTransaction ti txid (drop2nd <$> ins) outs ws (meta, txtime) txmeta $ case meta ^. #status of Pending -> #pendingSince InLedger -> #insertedAt @@ -1355,7 +1356,10 @@ postTransactionFee -> PostTransactionFeeData n -> Handler ApiFee postTransactionFee ctx (ApiT wid) body = do - let outs = coerceCoin <$> (body ^. #payments) + let outs = coerceCoin <$> body ^. #payments + -- fixme: #2075 include metadata in fee calculation + let _md = getApiT <$> body ^. #metadata + withWorkerCtx ctx wid liftE liftE $ \wrk -> do wdrl <- case body ^. #withdrawal of Nothing -> @@ -1419,6 +1423,7 @@ joinStakePool ctx knownPools getPoolStatus apiPoolId (ApiT wid) body = do (tx ^. #outputs) (tx ^. #withdrawals) (txMeta, txTime) + Nothing #pendingSince where -- Not forecasting into the future. Should be safe. @@ -1467,6 +1472,7 @@ quitStakePool ctx (ApiT wid) body = do (tx ^. #outputs) (tx ^. #withdrawals) (txMeta, txTime) + Nothing #pendingSince where -- Not forecasting into the future. Should be safe. @@ -1539,6 +1545,7 @@ migrateWallet ctx (ApiT wid) migrateData = do (NE.toList (W.unsignedOutputs cs)) (tx ^. #withdrawals) (meta, time) + Nothing #pendingSince where pwd = coerce $ getApiT $ migrateData ^. #passphrase @@ -1787,9 +1794,10 @@ mkApiTransaction -> [TxOut] -> Map ChimericAccount Coin -> (W.TxMeta, UTCTime) + -> Maybe W.TxMetadata -> Lens' (ApiTransaction n) (Maybe ApiTimeReference) -> m (ApiTransaction n) -mkApiTransaction ti txid ins outs ws (meta, timestamp) setTimeReference = do +mkApiTransaction ti txid ins outs ws (meta, timestamp) txMeta setTimeReference = do timeRef <- timeReference return $ tx & setTimeReference .~ Just timeRef where @@ -1805,6 +1813,7 @@ mkApiTransaction ti txid ins outs ws (meta, timestamp) setTimeReference = do , outputs = toAddressAmount <$> outs , withdrawals = mkApiWithdrawal @n <$> Map.toList ws , status = ApiT (meta ^. #status) + , metadata = apiTxMetadata txMeta } timeReference :: m ApiTimeReference @@ -1828,6 +1837,9 @@ mkApiTransaction ti txid ins outs ws (meta, timestamp) setTimeReference = do toAddressAmount (TxOut addr c) = AddressAmount (ApiT addr, Proxy @n) (mkApiCoin c) + apiTxMetadata :: Maybe W.TxMetadata -> ApiTxMetadata + apiTxMetadata = ApiTxMetadata . fmap ApiT + mkApiCoin :: Coin -> Quantity "lovelace" Natural diff --git a/lib/core/src/Cardano/Wallet/Api/Types.hs b/lib/core/src/Cardano/Wallet/Api/Types.hs index 1509504ab8d..bf97c33b0fc 100644 --- a/lib/core/src/Cardano/Wallet/Api/Types.hs +++ b/lib/core/src/Cardano/Wallet/Api/Types.hs @@ -64,6 +64,7 @@ module Cardano.Wallet.Api.Types , ApiFee (..) , ApiTxId (..) , ApiTxInput (..) + , ApiTxMetadata (..) , AddressAmount (..) , ApiErrorCode (..) , ApiNetworkInformation (..) @@ -127,6 +128,8 @@ import Prelude import Cardano.Address.Derivation ( XPrv, XPub, xpubToBytes ) +import Cardano.Api.MetaData + ( jsonFromMetadata, jsonToMetadata ) import Cardano.Mnemonic ( MkSomeMnemonic (..) , MkSomeMnemonicError (..) @@ -173,6 +176,7 @@ import Cardano.Wallet.Primitive.Types , StakePoolMetadata , StartTime (..) , TxIn (..) + , TxMetadata , TxStatus (..) , WalletBalance (..) , WalletId (..) @@ -180,6 +184,7 @@ import Cardano.Wallet.Primitive.Types , decodePoolIdBech32 , encodePoolIdBech32 , isValidCoin + , txMetadataIsNull , unsafeEpochNo ) import Control.Applicative @@ -492,11 +497,13 @@ data PostTransactionData (n :: NetworkDiscriminant) = PostTransactionData { payments :: !(NonEmpty (AddressAmount (ApiT Address, Proxy n))) , passphrase :: !(ApiT (Passphrase "lenient")) , withdrawal :: !(Maybe ApiWithdrawalPostData) + , metadata :: !(Maybe (ApiT TxMetadata)) } deriving (Eq, Generic, Show) data PostTransactionFeeData (n :: NetworkDiscriminant) = PostTransactionFeeData { payments :: (NonEmpty (AddressAmount (ApiT Address, Proxy n))) , withdrawal :: !(Maybe ApiWithdrawalPostData) + , metadata :: !(Maybe (ApiT TxMetadata)) } deriving (Eq, Generic, Show) newtype PostExternalTransactionData = PostExternalTransactionData @@ -557,6 +564,11 @@ data ApiTransaction (n :: NetworkDiscriminant) = ApiTransaction , outputs :: ![AddressAmount (ApiT Address, Proxy n)] , withdrawals :: ![ApiWithdrawal n] , status :: !(ApiT TxStatus) + , metadata :: !ApiTxMetadata + } deriving (Eq, Generic, Show) + +newtype ApiTxMetadata = ApiTxMetadata + { getApiTxMetadata :: Maybe (ApiT TxMetadata) } deriving (Eq, Generic, Show) data ApiWithdrawal n = ApiWithdrawal @@ -1228,6 +1240,20 @@ instance where toJSON = genericToJSON defaultRecordTypeOptions +instance FromJSON (ApiT TxMetadata) where + parseJSON = fmap ApiT . eitherToParser . jsonToMetadata +instance ToJSON (ApiT TxMetadata) where + toJSON = jsonFromMetadata . getApiT + +instance FromJSON ApiTxMetadata where + parseJSON Aeson.Null = pure $ ApiTxMetadata Nothing + parseJSON v = ApiTxMetadata . Just <$> parseJSON v +instance ToJSON ApiTxMetadata where + toJSON (ApiTxMetadata x) = case x of + Nothing -> Aeson.Null + Just (ApiT md) | txMetadataIsNull md -> Aeson.Null + Just md -> toJSON md + instance (DecodeAddress n , PassphraseMaxLength s , PassphraseMinLength s) => FromJSON (ApiWalletMigrationPostData n s) where parseJSON = genericParseJSON defaultRecordTypeOptions diff --git a/lib/core/src/Cardano/Wallet/Primitive/Types.hs b/lib/core/src/Cardano/Wallet/Primitive/Types.hs index 1cffca13405..41a5370079d 100644 --- a/lib/core/src/Cardano/Wallet/Primitive/Types.hs +++ b/lib/core/src/Cardano/Wallet/Primitive/Types.hs @@ -52,6 +52,7 @@ module Cardano.Wallet.Primitive.Types , inputs , fromTransactionInfo , toTxHistory + , txMetadataIsNull -- * Address , Address (..) @@ -280,6 +281,7 @@ import qualified Data.Set as Set import qualified Data.Text as T import qualified Data.Text.Encoding as T import qualified Data.Text.Lazy.Builder as Builder +import qualified Shelley.Spec.Ledger.MetaData as MD {------------------------------------------------------------------------------- Wallet Metadata @@ -1018,6 +1020,10 @@ fromTransactionInfo info = Tx , metadata = txInfoMetadata info } +-- | Test whether the given metadata map is empty. +txMetadataIsNull :: TxMetadata -> Bool +txMetadataIsNull (TxMetadata (MD.MetaData md)) = Map.null md + -- | Drop time-specific information toTxHistory :: TransactionInfo -> (Tx, TxMeta) toTxHistory info = diff --git a/lib/core/test/data/Cardano/Wallet/Api/ApiTTxMetadata.json b/lib/core/test/data/Cardano/Wallet/Api/ApiTTxMetadata.json new file mode 100644 index 00000000000..f1f30f6620e --- /dev/null +++ b/lib/core/test/data/Cardano/Wallet/Api/ApiTTxMetadata.json @@ -0,0 +1,1034 @@ +{ + "seed": -2635329256558185394, + "samples": [ + { + "12414324": 27, + "12793482": { + "": [ + { + "hex": "" + } + ], + "b": [ + -2, + 4, + "𫄱U\"qE" + ], + "1TsC": 1 + }, + "12962227": [ + 3, + "=n", + "", + { + "hex": "010201" + } + ] + }, + { + "4651590": "}'i", + "7965096": { + "": { + "G": { + "hex": "" + }, + "": { + "hex": "020301" + }, + "Y0 36": "", + "x": "4,J/t", + "z'Z$": { + "hex": "0001" + } + }, + "k": { + "hex": "02" + }, + "O": [ + -1 + ], + "f": { + "7": { + "hex": "040003" + }, + "u7": { + "hex": "0401040400" + }, + "PB": "LPD*\\" + } + }, + "11581754": { + "hex": "94902f85" + }, + "8556672": "ZpQebQA[4HWbc/T0g>QtnShQ+vx(r", + "15030089": "z<`l", + "11803456": { + "d$_": [ + { + "hex": "000303" + }, + 5 + ], + "s51": { + "hex": "0102" + } + }, + "16203727": [ + 3, + { + "hex": "0301" + }, + 0 + ], + "15930958": { + "!yAKN": [ + 3 + ], + "0[PCB": "oẍh'", + "Q?MFu": 1, + "(": 1 + }, + "8291345": [ + { + "hex": "0103" + }, + { + "hex": "020104" + }, + { + "hex": "" + }, + -4, + "VQ" + ], + "7648017": 12, + "4626495": { + "hex": "af3431" + }, + "16039333": -8, + "12527288": 26, + "3511942": [ + { + "hex": "0401040100" + }, + "3F_pz", + { + "hex": "0403010401" + } + ] + }, + { + "16154239": ":tV3pA(䙣HDQ)[P]Qi]MIKTR\"A_mfI?", + "6853728": [ + { + "hex": "0204" + }, + -5, + -2 + ], + "7988147": { + "": { + "96": "[!zt", + "ilT": "itr\\", + "uljz": "Mo&b", + "𤝰": 2 + }, + "!y#O": [ + -4 + ] + }, + "12565103": "T8wTlw": { + "WoO": -5, + "1/jXk": 5, + "vbU2E": { + "hex": "" + } + }, + "Q*\\K": 0, + "0\\Q*": { + "hex": "02000004" + } + }, + "7810029": { + "hex": "d1e10488a5652812a69e" + }, + "15553520": { + "sjB.b": 5, + "e=EP": [ + "H", + 3, + 2 + ], + "P": [ + { + "hex": "02" + }, + "}", + { + "hex": "04040200" + }, + -2, + 0 + ], + "[": 0, + ":k;1": { + "D㸈1(": 2, + "~r7bc": "", + "8~*8`": -3, + "Lbo([": 2, + "E*z91": 5 + } + }, + "1360135": { + "hex": "11" + }, + "1256443": "#O}R_f9:&?7?%+<녪", + "10575432": { + "": { + "hex": "0404000100" + } + }, + "3504081": { + " ": "~" + }, + "8000964": { + ":": "aMXI%", + "6z]y`": [ + { + "hex": "01" + }, + "", + { + "hex": "03040303" + }, + -2 + ] + }, + "14040412": [ + "19", + 4 + ], + "2012304": "z𧧫=.-$AtoAB4{\"'X~}lq", + "10173992": -15, + "3934037": ">I~𤅙@o9uuD.^s3ろ4" + }, + { + "14790917": { + "": { + "=Y+kO": "z", + " fiC": { + "hex": "01040004" + }, + "鄒": { + "hex": "04" + }, + "/7e>": { + "hex": "" + } + }, + "jQ1": [ + { + "hex": "0100040003" + }, + { + "hex": "04030100" + }, + { + "hex": "0103000204" + }, + -1 + ] + }, + "3984021": { + "WE>3r": "e", + "/k<": "q", + "2]3u\"": -1, + "->-:": 3, + "93⯋&": [ + "", + -1, + -3, + { + "hex": "0400000101" + } + ], + "tE=&": 1 + }, + "1286394": [ + -4, + "", + -5, + { + "hex": "04000202" + }, + -3 + ], + "9091002": { + "hex": "2d8c699f96b1350c56693b4ce5065d25b5791e99f7711d66a1fed30d" + }, + "782824": { + "TK;": 5, + "i𥛊M(^": { + "izPsc": -1, + "9A_Y": 4, + "k9pZo": { + "hex": "0403" + }, + "QvE": "rw i\"" + }, + "": [ + 5 + ], + "]l%": -5, + "E": "I" + }, + "12126176": { + "hex": "e9f526e2b5" + }, + "12362651": [ + "", + "" + ], + "3911985": -25, + "6366641": { + "": ".ttN㧑", + "ONkB": { + "": "z7T~", + ".": { + "hex": "040200" + }, + "[hjݔ": "L .", + ")": { + "hex": "03" + } + } + }, + "2641415": [ + { + "hex": "04020000" + }, + "n", + "hK>K>", + "#B}" + ], + "10464781": -27, + "12949392": "╈idTr8[𝖨*Aih)ml", + "4744256": -15, + "10914412": { + "hex": "6e6ddc" + }, + "16701657": { + "fSwN.": [ + "c", + "9O", + { + "hex": "0002040000" + }, + -4 + ] + }, + "9146648": { + "N'𣲻긧#": [ + { + "hex": "0001" + }, + -5, + 1, + -4, + -3 + ], + "c%r]q": "{s;", + "9s辰qj": { + "": { + "hex": "00040401" + }, + "P$": "5[*8", + "_ iT": -1, + "a`9f-": "X" + } + }, + "4690102": { + "hex": "62838c" + }, + "9059665": -20, + "3914985": { + "hex": "41f8" + }, + "1407966": "9yXNMVVN ;1JQ2Gx/kg|", + "4952985": 15, + "9613613": { + "hex": "af189539d88f3436b13694b40e3b9d9464c2db6bd1bafe" + }, + "9155911": { + "": "", + "b!": [ + ";,(", + { + "hex": "0202" + }, + { + "hex": "0101020404" + } + ], + "&": [ + "ઇQ", + "x6𐦛}B", + 4 + ] + } + }, + { + "1738652": 15, + "15052197": 15, + "10299095": { + "G<": "$.S|", + "j8a": "m" + }, + "11699172": { + "f": { + "hex": "01" + } + }, + "15916968": { + "hex": "2dd680722a5236869921916869d532c597aa9815fdf5a7912d5e4d0d06" + }, + "11657351": { + "": { + "Z": "J", + "": { + "hex": "02" + } + }, + "qG": -4, + "}xmF": [ + "J", + "", + "\"?", + 5 + ], + "rc": { + "hex": "0404040204" + } + }, + "16355971": { + "hex": "901fe1dffaa03cf24c46d3e6" + }, + "11559028": { + "hex": "4d9c34d7eae9f26ded2462e523f185e9a9378571c1e4ac9d23" + }, + "6223352": { + "hex": "acd201256eee00dd9ff23d54d446637c" + }, + "6104062": -30, + "3487016": [ + -5, + { + "hex": "000303" + }, + "..", + -3, + -1 + ], + "10824822": 21, + "458970": { + "hex": "63e116" + }, + "8429606": -7, + "16015643": { + "hex": "77ad19cbf258487ccd32e19b9e564c2ab7d720cbcc9055dda5e2d1" + }, + "5992508": { + "&\\": [ + -3, + 5, + -4, + { + "hex": "030300" + }, + 3 + ], + "zfCM": { + "hex": "0004020004" + }, + "\"@": { + "'Iuek": -5 + } + }, + "1657799": [ + { + "hex": "01" + }, + ">x" + ], + "418572": 20, + "8815551": [ + 4, + { + "hex": "020004" + }, + -1, + "S2Xw" + ], + "13694256": [ + { + "hex": "0404" + }, + -4, + { + "hex": "03010104" + }, + { + "hex": "" + } + ], + "15669925": -14, + "1063214": "1Hi8JZP", + "5978176": { + "}zl䨅": "", + "": 0, + "f)": { + "hex": "" + }, + "*Y": { + "$": "", + "(-": "c;7Mh" + }, + "Oq p": "}5^:B" + }, + "39311": { + "hex": "70b511635bd6ad24179ad7b6b3d8e08076c41a4978" + } + }, + { + "10610343": { + "䪔": { + "hex": "0400" + }, + "": { + ";Mvt": -2, + "t": "%1䌜^" + }, + "t00": 5, + "?": "" + }, + "347072": { + "xb:P": { + ";NvQ": { + "hex": "" + }, + "SawU": 2, + "E\\2": 4, + ";hK\\": -2, + "p1C䅆M": "- " + }, + "%qw": { + "LUJ>\\": 5 + }, + "%s0+I": { + "hex": "0101030400" + }, + "2": "n&xw", + "r)N&": "i漐l" + }, + "11572353": { + "": { + "6B𤷨m": 0, + "3": -1 + }, + "~zt|R": { + "hex": "01030304" + }, + "R0j": "_", + "f": 2 + }, + "16001803": [ + 0 + ], + "8099355": [ + 3, + "$S", + 1 + ], + "896271": { + "iK9\\|": [ + { + "hex": "0202020104" + }, + { + "hex": "020202" + }, + 3 + ] + }, + "4337967": [ + -4 + ], + "10697831": { + "hex": "a309f4513d9162dae97bbec2d524" + }, + "13717418": { + "hex": "21bd33651dc699" + }, + "14788532": [ + "@", + "", + "[3" + ], + "7378525": { + "hex": "9289" + }, + "2283007": { + "'XlA": 0 + }, + "11451501": [ + "6!", + { + "hex": "0404010304" + }, + "d", + { + "hex": "0304" + }, + { + "hex": "04030301" + } + ], + "786907": [ + 2, + "y+" + ], + "7675646": [ + { + "hex": "040100" + } + ], + "11194384": -19, + "14303568": { + "hex": "256e279f1f76e716abea1972c95c" + }, + "8052537": [ + { + "hex": "0404040104" + }, + "`", + -1 + ], + "6065469": [ + { + "hex": "02" + } + ], + "6302042": "8Q#%>n]\\w^7NW7 '𫚋<5kE}+Dqw?HK", + "2986589": "hI6P/<34d4[5accUs`X`" + }, + { + "240785": 0, + "10321583": [ + { + "hex": "03" + }, + 3, + -2, + 0, + -5 + ], + "3848791": [ + { + "hex": "030000" + }, + { + "hex": "00" + }, + -2 + ], + "12001974": "+𝈅@03]c52|;rIX1q=2561X:", + "7895293": { + "hex": "66ddd30c8721cd95377cc366f2c0962a2b21c6dddd19fe48a2" + }, + "4047119": [ + -4, + "ydCHb", + { + "hex": "020401" + }, + -5 + ], + "13429524": 9, + "6104453": [ + "痢mAgu", + "BA", + { + "hex": "0301020202" + }, + -2, + 3 + ], + "16458935": "#`", + "15458760": [ + -4 + ], + "11660675": { + "xU.D": { + "7": -3, + "f祄 Vs": { + "hex": "0001040203" + }, + "}$": { + "hex": "000000" + } + }, + "/Vry": "Oemm", + "{h": { + "hex": "" + }, + "8J": { + "N𠻗2": "", + "": { + "hex": "0102000100" + }, + "tO": { + "hex": "040204" + }, + "r𡇦M": 2, + "X.": { + "hex": "0402" + } + } + }, + "8546257": 4, + "4028393": { + "": 2 + }, + "14829693": -28, + "15068294": { + "hex": "87ff1b947cde053d" + }, + "10600304": "r\"; k6n@}.M[", + "3725333": { + "_+;": -4, + "yNJ": [ + "_", + { + "hex": "03" + }, + { + "hex": "0203040001" + } + ], + "Ra": { + ",": 0, + "sw": "nU", + "~5IE": "diIS" + }, + "~/p": { + "hex": "02040000" + } + }, + "10952357": "{fHf*!d0.F5s=M1XBWXg?ryrTxI\\J", + "11641839": -17 + }, + { + "15121726": [ + "5", + "", + { + "hex": "0002" + } + ] + } + ] +} \ No newline at end of file diff --git a/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json b/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json index 0b87c558a93..2e21b3b1ec5 100644 --- a/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json +++ b/lib/core/test/data/Cardano/Wallet/Api/ApiTransactionTestnet0.json @@ -1,226 +1,639 @@ { - "seed": 6053236950585574193, + "seed": -6859683091746399099, "samples": [ { "status": "pending", "withdrawals": [], "amount": { - "quantity": 164, + "quantity": 171, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "outputs": [], + "direction": "outgoing", + "outputs": [ + { + "amount": { + "quantity": 232, + "unit": "lovelace" + }, + "address": "" + } + ], "pending_since": { - "time": "1865-11-27T00:45:02.891766615378Z", + "time": "1860-06-04T12:20:53Z", "block": { "height": { - "quantity": 8562, + "quantity": 24995, "unit": "block" }, - "epoch_number": 6541, - "slot_number": 1999 + "epoch_number": 24814, + "slot_number": 11323 } }, - "id": "525d0964716030bd190e3e3b256f795d6c731173a76047191f5c78601e934120" - }, - { - "status": "pending", - "withdrawals": [], - "amount": { - "quantity": 98, - "unit": "lovelace" + "metadata": {}, + "depth": { + "quantity": 29670, + "unit": "block" }, - "inputs": [], - "direction": "outgoing", - "outputs": [], - "id": "3a037b7019005a4721683804e21b573217625c68280f3e8e2f5d57fa6f3732f0" + "id": "15d40c74395e6ed0613d140828d9734dd9955870400c608518aac2522845cb24" }, { "status": "pending", "withdrawals": [], "amount": { - "quantity": 226, + "quantity": 152, "unit": "lovelace" }, "inputs": [], "direction": "incoming", - "outputs": [], + "outputs": [ + { + "amount": { + "quantity": 246, + "unit": "lovelace" + }, + "address": "" + } + ], "pending_since": { - "time": "1883-06-08T10:47:06Z", + "time": "1902-12-15T18:00:00Z", "block": { "height": { - "quantity": 20027, + "quantity": 488, "unit": "block" }, - "epoch_number": 31665, - "slot_number": 547 + "epoch_number": 6896, + "slot_number": 11337 } }, + "metadata": { + "797824": "d\"p]_~^;M8ThV", + "11643182": { + "hex": "abb3900cf7" + }, + "10495989": { + "A": { + "hex": "" + }, + "x": { + "hex": "01010002" + } + }, + "13107228": -3, + "11431662": 20, + "15120490": 1, + "11750471": [ + "_{UV4", + 2, + "𡰌", + 2, + "TQ7" + ], + "10320198": "AY?]]$KZhsl|m,z{7a#dG48N.'" + }, "depth": { - "quantity": 10530, + "quantity": 11559, "unit": "block" }, - "id": "751429625c321e537377af7615d0767e3a015f12496211520fd44c496b4c060d" + "id": "3a1b114d1c75af1757450b296a0b340c326a769234335f690d5d454e7d194c18" }, { "inserted_at": { - "time": "1878-12-18T19:38:35.958580553853Z", + "time": "1904-04-28T13:00:00Z", "block": { "height": { - "quantity": 30534, + "quantity": 26949, "unit": "block" }, - "epoch_number": 27805, - "slot_number": 27205 + "epoch_number": 11768, + "slot_number": 22308 } }, "status": "in_ledger", "withdrawals": [], "amount": { - "quantity": 14, + "quantity": 245, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "outputs": [], + "direction": "outgoing", + "outputs": [ + { + "amount": { + "quantity": 45, + "unit": "lovelace" + }, + "address": "" + } + ], + "metadata": { + "1732514": "N&|켒z𝚏{Tx)P", + "13279914": { + "hex": "a0fa7017cf586d355ca4a04169c5da5c9caa7dfcce" + }, + "10337419": { + "3YB": { + "": "xk{4m", + "ﺅ": -2, + "UVv": 2, + "h": "~*eW", + "hS#)": -4 + }, + "y(?H": { + "hex": "04" + } + }, + "7660317": [ + -2, + 3 + ], + "12436998": 17, + "8990435": [ + { + "hex": "0002" + }, + 3 + ], + "2943643": [ + { + "hex": "01010203" + }, + 0, + -5, + -3 + ], + "9782197": { + "i2,": { + "If": { + "hex": "" + }, + "\\,U𧗗'": -5, + "": 0 + }, + "*": "RCO", + "h`_4S": [ + "", + "G[}", + "AUv9", + "ZS^+j" + ], + "n%^~": { + "hex": "0403" + }, + "_b)": [ + "", + -2, + { + "hex": "020304" + }, + -2 + ] + }, + "7362669": [ + 0 + ], + "15396991": "Fya", + "16319697": [ + 4 + ], + "2597629": [ + { + "hex": "030104" + }, + "0y%8", + 4, + -4, + -2 + ], + "15533952": { + "n2.~": { + "\"l3,l": "E%\"e", + "0o-H": "}uG$s", + "WT5-v": { + "hex": "" + } + }, + "li": { + "": { + "hex": "" + }, + "w䷄": { + "hex": "02" + }, + "~𧧖@Z_": "" + } + }, + "7300289": { + "hex": "a479a5dd88a84263b704e05f9b" + }, + "3975852": -6, + "4760052": { + "hex": "f8bdba5defca663608299d00b8cc144492989d660a380540be4aac35" + }, + "12393564": [ + { + "hex": "04" + }, + { + "hex": "01" + }, + { + "hex": "010402" + }, + "7", + "" + ], + "13328319": -20, + "3819437": { + "": [ + -2, + -2, + -2, + { + "hex": "04" + } + ], + "E": 3, + "hl<": [ + "'", + 0, + { + "hex": "04" + } + ], + "v'W]]": [ + { + "hex": "00" + } + ] + }, + "11051043": { + "hex": "180f217dfa6de54c395188ee08a101e50d35c84a" + }, + "8809585": { + "b3VVe": [ + ",]%+", + { + "hex": "04040303" + } + ], + "r": ":" + } + }, "depth": { - "quantity": 14442, + "quantity": 3114, "unit": "block" }, - "id": "2a211a3430407b601f6a59ea1e822640675e423e461a8d6a14576579615bbb0d" + "id": "586d660f34cd530f7d8c3f2ca6204c6477427e5a6062463a252703667a553ef6" }, { - "status": "pending", + "inserted_at": { + "time": "1877-08-08T14:42:36Z", + "block": { + "height": { + "quantity": 14761, + "unit": "block" + }, + "epoch_number": 1640, + "slot_number": 17447 + } + }, + "status": "in_ledger", "withdrawals": [], "amount": { - "quantity": 54, + "quantity": 248, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "outputs": [], + "direction": "outgoing", + "outputs": [ + { + "amount": { + "quantity": 152, + "unit": "lovelace" + }, + "address": "" + } + ], + "metadata": { + "7234086": [ + { + "hex": "010001" + }, + 1, + { + "hex": "0202" + } + ] + }, "depth": { - "quantity": 14281, + "quantity": 31653, "unit": "block" }, - "id": "34c75472935253434c7668c0f9b64503121a5b2d071b4a30fd3629375c5f086f" + "id": "313b432c532d6a51560678b67b156f4f20b60b5def4748177a6067264ca97b20" }, { "inserted_at": { - "time": "1902-10-08T23:11:39Z", + "time": "1887-06-26T00:00:00Z", "block": { "height": { - "quantity": 16538, + "quantity": 17281, "unit": "block" }, - "epoch_number": 16561, - "slot_number": 24304 + "epoch_number": 16486, + "slot_number": 30977 } }, "status": "in_ledger", "withdrawals": [], "amount": { - "quantity": 40, + "quantity": 222, + "unit": "lovelace" + }, + "inputs": [], + "direction": "outgoing", + "outputs": [ + { + "amount": { + "quantity": 41, + "unit": "lovelace" + }, + "address": "" + } + ], + "metadata": null, + "id": "394449133f2e231ec74e6b383d35163e02f49e1b2c116c7d012723658891282b" + }, + { + "inserted_at": { + "time": "1887-06-05T05:00:00Z", + "block": { + "height": { + "quantity": 30317, + "unit": "block" + }, + "epoch_number": 25038, + "slot_number": 2318 + } + }, + "status": "in_ledger", + "withdrawals": [], + "amount": { + "quantity": 106, "unit": "lovelace" }, "inputs": [], "direction": "incoming", - "outputs": [], + "outputs": [ + { + "amount": { + "quantity": 148, + "unit": "lovelace" + }, + "address": "" + } + ], + "metadata": null, + "id": "71eee8065c03697510425619591215041c6fd46c6264c70529d624506d454853" + }, + { + "status": "in_ledger", + "withdrawals": [], + "amount": { + "quantity": 50, + "unit": "lovelace" + }, + "inputs": [], + "direction": "outgoing", + "outputs": [ + { + "amount": { + "quantity": 251, + "unit": "lovelace" + }, + "address": "" + } + ], + "metadata": null, "depth": { - "quantity": 8010, + "quantity": 11303, "unit": "block" }, - "id": "40782433736e114a36186c105e2602ae3f02100b7d8a1e04000d7c541e1ed74c" + "id": "387d204b04137b723731877505370b3a2a33c75716494d13566b1614198b873c" }, { "status": "pending", "withdrawals": [], "amount": { - "quantity": 121, + "quantity": 158, "unit": "lovelace" }, "inputs": [], "direction": "incoming", - "outputs": [], + "outputs": [ + { + "amount": { + "quantity": 78, + "unit": "lovelace" + }, + "address": "" + } + ], "pending_since": { - "time": "1864-05-10T04:19:09Z", + "time": "1902-10-28T00:20:21Z", "block": { "height": { - "quantity": 12912, + "quantity": 22622, "unit": "block" }, - "epoch_number": 15822, - "slot_number": 8822 + "epoch_number": 7952, + "slot_number": 803 } }, - "id": "5f7f2401130757880f14316b64299605527715156d4b5f4aeb4052122c7f45b6" - }, - { - "inserted_at": { - "time": "1893-08-22T17:00:00Z", - "block": { - "height": { - "quantity": 18283, - "unit": "block" + "metadata": { + "16113778": [ + 2, + "oY>83", + { + "hex": "02" + } + ], + "11983148": "Cvrkq𡻕2#'%q⁁uoV4a\"\\0U", + "777123": "HS뉣t$8v/𦌣@\\*#a]3H)dKbLzT8`E", + "7094241": { + "p*scw": 1, + "9": { + "\\": { + "hex": "02030402" + }, + "M/⼮d": { + "hex": "0103040101" + }, + "nTIh": "Y", + "Vh$|": "cMvSh" }, - "epoch_number": 22762, - "slot_number": 20049 - } + "@c2": { + "": { + "hex": "0001" + }, + "[jrh8": { + "hex": "010304" + }, + "s(}`": 3, + ",vi=": { + "hex": "00" + } + } + }, + "16451395": [ + "QU7;2", + -4, + 4, + { + "hex": "0001030202" + }, + -4 + ], + "1522722": { + "j=": [ + 0 + ], + "4%$": { + "vJ": -4, + ";_$3": 0, + "b": -5, + "𖠤8\\}": "Qj\\(", + "c": "lLT" + }, + "r𦹹": "", + "?!": { + "hex": "0402" + }, + "C}": [ + -2, + { + "hex": "0302" + }, + 3, + 3, + "J$" + ] + }, + "4322873": "]", + "12568245": "b#!wu,#X=F-D(w/T\"YdSb'B ", + "1300062": { + "hex": "c0" + }, + "9788451": { + "hex": "" + }, + "13851264": { + "+z𠮰T": 1 + }, + "14742973": 29, + "5001448": [ + { + "hex": "0104" + } + ], + "16468661": -19, + "3943002": "", + "11873655": ")z$H Ի+", + "9496091": [ + -3, + { + "hex": "04010204" + }, + 5 + ], + "1810865": [ + 5 + ], + "6131657": { + " AE": "~|0", + "": { + "<^de ": "𠘟{", + "XijZ": 0 + } + }, + "9155203": "cc1\"vndS\\O^,GYN_Q|", + "11094958": [ + { + "hex": "02010200" + }, + { + "hex": "0301" + }, + "h}b", + "!`sk" + ] }, - "status": "in_ledger", - "withdrawals": [], - "amount": { - "quantity": 162, - "unit": "lovelace" + "depth": { + "quantity": 8988, + "unit": "block" }, - "inputs": [], - "direction": "incoming", - "outputs": [], - "id": "654d191c1b6f69047e2a56180e69070533683b3d7a0803b96944ab6f5b658511" + "id": "11512301762f7405732b001c114807791214385fae552a251b5f05514229b501" }, { "status": "in_ledger", "withdrawals": [], "amount": { - "quantity": 119, + "quantity": 247, "unit": "lovelace" }, "inputs": [], - "direction": "incoming", - "outputs": [], + "direction": "outgoing", + "outputs": [ + { + "amount": { + "quantity": 104, + "unit": "lovelace" + }, + "address": "" + } + ], + "metadata": null, "depth": { - "quantity": 30408, + "quantity": 20787, "unit": "block" }, - "id": "69f624217e7ea8332137742c7e1f5937125d4c521d365d76286a04637b3e2a33" + "id": "6527414f72116b7d263b022ee352612335400d2f174d5d685a1f545f37333c60" }, { "status": "pending", "withdrawals": [], "amount": { - "quantity": 192, + "quantity": 91, "unit": "lovelace" }, "inputs": [], "direction": "outgoing", - "outputs": [], + "outputs": [ + { + "amount": { + "quantity": 180, + "unit": "lovelace" + }, + "address": "" + } + ], "pending_since": { - "time": "1873-04-02T18:09:54Z", + "time": "1872-01-04T22:53:03Z", "block": { "height": { - "quantity": 31840, + "quantity": 5081, "unit": "block" }, - "epoch_number": 9169, - "slot_number": 8592 + "epoch_number": 14684, + "slot_number": 20338 } }, + "metadata": null, "depth": { - "quantity": 31984, + "quantity": 13789, "unit": "block" }, - "id": "292f1d4f1a0d7519657e09d22421595275067b26501c53861d762043138d0b77" + "id": "6c4a7c2347cb004c2f4c11692d3308f2ca9c4560786c106339cc131966ac7339" } ] } \ No newline at end of file diff --git a/lib/core/test/data/Cardano/Wallet/Api/ApiTxMetadata.json b/lib/core/test/data/Cardano/Wallet/Api/ApiTxMetadata.json new file mode 100644 index 00000000000..da047e7fe81 --- /dev/null +++ b/lib/core/test/data/Cardano/Wallet/Api/ApiTxMetadata.json @@ -0,0 +1,828 @@ +{ + "seed": 2147738271768211312, + "samples": [ + null, + { + "10398034": "m", + "15414174": { + "JDd*": [ + { + "hex": "040002" + }, + 1 + ], + "HB%<": { + "": 0, + "1/9": -5 + }, + "@t5": [ + "" + ] + }, + "4580774": "ES4*&E\"sZQU?R8J𝒽.}Y$n^d\\X=E50{", + "215215": [ + "}^irl", + "f\\!", + "𩊻}", + "gMgO" + ], + "2168621": { + "Nu^O": [ + "Ue", + "", + { + "hex": "00" + } + ], + "": { + "hex": "01000002" + }, + "!sn": { + "hex": "04030204" + } + }, + "15383062": [ + { + "hex": "" + }, + { + "hex": "0404" + } + ], + "8392506": { + "kS": 4, + "WhC3u": { + "": "", + "/|Qi": "", + "~": { + "hex": "03" + }, + "M i": 4, + "𢿹}0;": "lF:\"" + }, + "ퟘh]9": { + "\\": "=t)", + "T5f": 5, + "0+,MU": -4 + } + }, + "14031890": { + "": 0 + }, + "6751486": "homm<@tY'U\"Hs4P! F07u", + "8460340": { + "W>XzL": { + "hyq4": 5, + "}": 5 + } + }, + "16229245": [ + { + "hex": "03" + }, + -2, + "", + -4 + ], + "7515083": { + "": { + "hex": "" + } + }, + "639985": { + "O\"\\*g": "-;P", + "XR": { + "xz~oD": -3, + "[\\臄": 0 + }, + "W?Nl": { + "hex": "" + }, + "uE": "r[p", + "A7W": { + "=r": -4, + "]SL": -5, + "HGLC}": { + "hex": "01010002" + }, + "B8Q(": { + "hex": "00040403" + }, + "遶": "[ h" + } + }, + "11291714": { + "78,!": [ + -1, + { + "hex": "" + }, + { + "hex": "0304010303" + }, + 4 + ] + }, + "6597060": { + "hex": "5555887a484a71402eacd24726cfc99ebbd710eea65770f35f331ddb0c" + }, + "4497330": { + "o nx": [ + "+.CI", + { + "hex": "03" + } + ] + }, + "11462447": [ + "r<(n", + "", + { + "hex": "0103" + }, + 1, + { + "hex": "0003" + } + ], + "4255559": { + "/a\"": { + "hex": "04" + }, + "0|rs7": [ + { + "hex": "03000002" + }, + "^", + "", + 2 + ], + "I#!": [ + "d", + { + "hex": "04" + }, + "^", + 3 + ], + "Vx(": [ + 4 + ], + ")tS/O": { + "L": 1, + "I98i": { + "hex": "040403" + } + } + }, + "3310835": "'◲", + "13253735": 23, + "16130834": { + "=|": { + "a_Z": "K𤺈z䀘]", + "M": { + "hex": "0000020404" + } + } + }, + "7803024": { + "f'ge": [ + 4 + ] + }, + "15060034": { + "hex": "d16ff124bca051da71" + }, + "1468512": [ + { + "hex": "0200020200" + }, + -2 + ], + "1995477": 24, + "5589991": "3CIv'6C'N0fH", + "4547376": "~hSrsS4}/21Cj6F{ME#:/gp;{C;", + "3539733": { + "4|": "L" + }, + "2747621": { + "hex": "c9b1541d7f58c84fc20172830fa5be0d4d941a344fc3589b5d" + }, + "6983539": { + "": { + "`5'0": 4, + "c6": { + "hex": "04010103" + }, + "P": "=b", + "t": "", + "8$LV": 0 + }, + ",1z<": ")taQ#", + "\"\\L": [ + { + "hex": "01" + }, + { + "hex": "040204" + } + ] + }, + "11185447": [ + { + "hex": "02" + }, + 1, + "NiSn", + 2 + ], + "875703": -9, + "4365409": { + "!yHu+": -5, + "卛-kvr": { + "\\3": 3, + "c": "" + } + }, + "47831": [ + "Ua/%" + ] + }, + null, + { + "4275606": 29, + "15017896": [ + 1, + 1, + "", + { + "hex": "0304030403" + }, + ")y" + ], + "13957136": { + "ꛀ": 3, + "%8Z7r": [ + { + "hex": "0201030404" + }, + "wmd", + { + "hex": "000401" + }, + { + "hex": "" + } + ], + "g+B2": 1 + }, + "4345737": -17, + "13889685": 17, + "1034085": { + "hex": "e8eabc4ab097735788ba3a4328f6c06cc456d00f30eb2b69d2091fab0c" + }, + "6899216": -18, + "10983225": "", + "6970978": -29, + "7137554": { + "hex": "daf206024ba55c3af60849" + }, + "13206610": { + "^(9{l": { + "w": "", + "9": -5 + }, + "T𡅟137": { + "wcMS": { + "hex": "" + }, + "=0Mm": { + "hex": "02040104" + }, + "s$Y": "[b{-c" + } + }, + "15247040": 21, + "12094667": "bY𨠅stoV0\\G𨣖[q2d|fC2oaO", + "2407962": [ + 2, + "2", + -3, + 3, + { + "hex": "0403" + } + ], + "12390006": [ + "dq{" + ], + "16483552": { + "hex": "" + }, + "303282": { + "": { + "hex": "040203" + }, + "jG|r": { + "A2i": "{^a", + "l'{C": -3, + "<-": ">?", + "Ɣ": "az", + "x": "3" + } + }, + "13226851": { + "u": [ + "OrU+", + { + "hex": "" + }, + "9]" + ], + "hw1": "-?o=/", + "02am": { + "P]": { + "hex": "0302" + }, + "}": "", + "B?": { + "hex": "020202" + }, + "(>h": "M'(~": " @pH" + }, + "15105191": [ + " !烶-(", + "_", + "{t" + ] + }, + { + "1202805": "掭I~I/S50v:rt@](Q1vxG%": { + "hex": "" + }, + "9H": -4, + "-,": "溞1O~" + } + }, + "3391605": 16, + "12440299": -9, + "13049754": "/1z&/0H", + "6989486": ",.aQ{AYmlosQM_.YGFE}Tyb", + "6860766": { + "-쏫": { + "8Ow/": { + "hex": "0203" + }, + "k": { + "hex": "0403010102" + } + }, + "F5ZO": [ + 5, + -4 + ] + }, + "16247054": 18, + "9957173": "", + "510314": 22, + "10631481": "", + "10018829": { + "H<": [ + "P", + 5, + "pP|*", + { + "hex": "010003" + } + ], + "89P": "" + }, + "16583448": [ + { + "hex": "0103" + }, + -4, + " !\"v(", + "[" + ] + }, + { + "9054711": 17, + "16313936": { + "hex": "b0129cf6a57ac516" + }, + "11992536": "|\\FC9@XlkH'pLow%_yjI6r+jxs\"": { + "hex": "" + } + }, + "15849290": { + "<": { + "DyB": 3, + "": { + "hex": "02030302" + }, + "`": "ufFe", + "#t": { + "hex": "04000301" + }, + "1Rd&": "p\\`5" + }, + "": "1", + "sk{": -4, + "v/0?R": 0 + }, + "7240407": -17, + "5262789": [ + { + "hex": "0104010004" + }, + -2, + "@9Gm", + { + "hex": "0002030100" + } + ], + "604183": { + "hex": "d835511adeb075585f83423ed35115adee763e4f62" + }, + "8255203": { + "": { + "𠑻]ks}": { + "hex": "" + }, + "s@=0h": { + "hex": "0404" + } + }, + "𩥆d&": [ + 3, + 2, + "o0h虩" + ] + }, + "5243732": -20, + "12235248": "r\\1Y", + "7990446": 20 + }, + { + "14997600": [ + "ZJ!", + { + "hex": "04040103" + }, + { + "hex": "03000303" + } + ], + "2107605": { + "hex": "47beb23450d276982e0d8eb96ac4c4aaa968adba80dedf3850afb59b22" + }, + "6727267": [ + { + "hex": "03020104" + }, + { + "hex": "0103" + }, + { + "hex": "0201" + } + ], + "16010485": { + "hex": "cea0cad1071bd3883ac30f6e7d0bb3b29ab866eb3fe4244afa7bdc36" + }, + "8893888": "+B.𨮔pho.s$y`z)iB,MG`Z\"", + "14631084": -1, + "2003738": "*t", + "3994580": [ + { + "hex": "020101" + }, + { + "hex": "" + } + ], + "8535098": { + "𥴮": { + "2": "2>t2" + }, + "T?PU": { + "$": "\"j" + }, + "HEᬠ4": [ + 3, + 1, + { + "hex": "04010401" + }, + 5, + { + "hex": "04" + } + ], + "i'": { + "hex": "" + } + }, + "13051062": { + "hex": "ae9cefda1292eff5" + }, + "15315187": "?$gh4PyKm>+6kJK;lQaww5opxvay5", + "476481": [ + { + "hex": "0101" + }, + -4, + 0 + ], + "15390137": -5, + "8351730": ")L6Yq3cY1SQ'Bc#Nfk~\"PGG$𣍐MQ/]e", + "4347971": { + ",IA": { + "": { + "hex": "" + }, + "Yl$": { + "hex": "0203040101" + }, + "j`H": "h\"", + "9RM#": 4 + }, + "k_<8D": "yS", + "M 6n𡎚": [ + { + "hex": "" + }, + 0 + ], + "|j": "osg" + }, + "13986628": "|6", + "663659": 21 + }, + null, + null + ] +} \ 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 73ed6e1eed1..1c8c82bfdf6 100644 --- a/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs @@ -66,6 +66,7 @@ import Cardano.Wallet.Api.Types , ApiTransaction (..) , ApiTxId (..) , ApiTxInput (..) + , ApiTxMetadata (..) , ApiUtxoStatistics (..) , ApiWallet (..) , ApiWalletDelegation (..) @@ -99,7 +100,12 @@ import Cardano.Wallet.Api.Types , WalletPutPassphraseData (..) ) import Cardano.Wallet.Gen - ( genMnemonic, genPercentage, shrinkPercentage ) + ( genMnemonic + , genPercentage + , genTxMetadata + , shrinkPercentage + , shrinkTxMetadata + ) import Cardano.Wallet.Primitive.AddressDerivation ( HardDerivation (..) , NetworkDiscriminant (..) @@ -137,6 +143,8 @@ import Cardano.Wallet.Primitive.Types , StartTime (..) , TxIn (..) , TxIn (..) + , TxMetadata (..) + , TxMetadata (..) , TxOut (..) , TxStatus (..) , UTxO (..) @@ -334,6 +342,7 @@ spec = do jsonRoundtripAndGolden $ Proxy @(ApiT Address, Proxy ('Testnet 0)) jsonRoundtripAndGolden $ Proxy @(ApiT AddressPoolGap) jsonRoundtripAndGolden $ Proxy @(ApiT Direction) + jsonRoundtripAndGolden $ Proxy @(ApiT TxMetadata) jsonRoundtripAndGolden $ Proxy @(ApiT TxStatus) jsonRoundtripAndGolden $ Proxy @(ApiT WalletBalance) jsonRoundtripAndGolden $ Proxy @(ApiT WalletId) @@ -342,6 +351,7 @@ spec = do jsonRoundtripAndGolden $ Proxy @(ApiT SyncProgress) jsonRoundtripAndGolden $ Proxy @(ApiT StakePoolMetadata) jsonRoundtripAndGolden $ Proxy @ApiPostRandomAddressData + jsonRoundtripAndGolden $ Proxy @ApiTxMetadata describe "Textual encoding" $ do describe "Can perform roundtrip textual encoding & decoding" $ do @@ -698,6 +708,7 @@ spec = do { payments = payments (x :: PostTransactionData ('Testnet 0)) , passphrase = passphrase (x :: PostTransactionData ('Testnet 0)) , withdrawal = withdrawal (x :: PostTransactionData ('Testnet 0)) + , metadata = metadata (x :: PostTransactionData ('Testnet 0)) } in x' === x .&&. show x' === show x @@ -706,6 +717,7 @@ spec = do x' = PostTransactionFeeData { payments = payments (x :: PostTransactionFeeData ('Testnet 0)) , withdrawal = withdrawal (x :: PostTransactionFeeData ('Testnet 0)) + , metadata = metadata (x :: PostTransactionFeeData ('Testnet 0)) } in x' === x .&&. show x' === show x @@ -729,6 +741,7 @@ spec = do , outputs = outputs (x :: ApiTransaction ('Testnet 0)) , status = status (x :: ApiTransaction ('Testnet 0)) , withdrawals = withdrawals (x :: ApiTransaction ('Testnet 0)) + , metadata = metadata (x :: ApiTransaction ('Testnet 0)) } in x' === x .&&. show x' === show x @@ -1243,6 +1256,7 @@ instance Arbitrary (PostTransactionData t) where <$> arbitrary <*> arbitrary <*> elements [Just SelfWithdrawal, Nothing] + <*> arbitrary instance Arbitrary ApiWithdrawalPostData where arbitrary = genericArbitrary @@ -1258,6 +1272,7 @@ instance Arbitrary (PostTransactionFeeData t) where arbitrary = PostTransactionFeeData <$> arbitrary <*> elements [Just SelfWithdrawal, Nothing] + <*> arbitrary instance Arbitrary PostExternalTransactionData where arbitrary = do @@ -1267,6 +1282,14 @@ instance Arbitrary PostExternalTransactionData where shrink (PostExternalTransactionData bytes) = PostExternalTransactionData . BS.pack <$> shrink (BS.unpack bytes) +instance Arbitrary TxMetadata where + arbitrary = genTxMetadata + shrink = shrinkTxMetadata + +instance Arbitrary ApiTxMetadata where + arbitrary = genericArbitrary + shrink = genericShrink + instance Arbitrary (ApiTransaction t) where shrink = filter outputsNonEmpty . genericShrink where @@ -1291,6 +1314,7 @@ instance Arbitrary (ApiTransaction t) where <*> genOutputs <*> genWithdrawals <*> pure txStatus + <*> arbitrary where genInputs = Test.QuickCheck.scale (`mod` 3) arbitrary diff --git a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs index 6fedb65c259..8b4b97523e2 100644 --- a/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs +++ b/lib/jormungandr/test/integration/Test/Integration/Jormungandr/Scenario/API/Transactions.hs @@ -473,7 +473,7 @@ fixtureExternalTx ctx toSend = do let wSrc = getFromResponse Prelude.id r0 -- we take input by lookking at transactions of the faucet wallet txsSrc <- listAllTransactions @n ctx wSrc - let (ApiTransaction (ApiT theTxId) _ _ _ _ _ _ outs _ _):_ = reverse txsSrc + let (ApiTransaction (ApiT theTxId) _ _ _ _ _ _ outs _ _ _):_ = reverse txsSrc let (AddressAmount ((ApiT addrSrc),_) (Quantity amt)):_ = outs let (rootXPrv, pwd, st) = getSeqState mnemonicFaucet fixturePassphrase -- we create change address From 6654f04fd0525f0a1a5fe79ce68449842d18832a Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 2 Sep 2020 14:28:36 +0200 Subject: [PATCH 2/3] update swagger specifications for tx metadata according to latest agreement --- specifications/api/swagger.yaml | 51 +++++++++++---------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index d5f9efc89b7..1e0ff10b2a0 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -564,27 +564,22 @@ x-transactionMetadata: &transactionMetadata Extra application data attached to the transaction. Cardano allows users and developers to embed their own - authenticated metadata when submitting transactions. + authenticated metadata when submitting transactions. Metadata can + be expressed as a JSON object with some restrictions: - The transaction metadata can be `null`, or a mapping from natural - numbers to metadata entries. These top-level keys can be any - number between `0` and `2^64 - 1`. + 1. All top-level keys must be integers between `0` and `2^64 - 1`. - Note that metadata is not free-form JSON. Only a subset of JSON is - accepted as values. String values are limited in length, and - metadata is included in the overall transaction size limit. + 2. Strings must be at most 64 characters - See the example on the right to get an overview and the - equivalence table below to understand how metadata are mapped to - on-chain data types. + 3. Bytestrings may be encoded as hex-encoded strings prefix with `0x` - | On-chain type | JSON type | - | --- | --- | - | string | JSON string | - | number | JSON number | - | bytestring | JSON singleton with a field `"hex"` and a value in base16 | - | array | JSON monomorphic array | - | map | JSON array of 2-element ([key, value]) monomorphic arrays | + Metadata aren't stored as JSON on the Cardano blockchain but stored using + another structured encoding called CBOR which is better suited for binary + encoding. JSON can be fully expressed in CBOR but the reciprocal isn't + true. So, in order to leverage some of the CBOR optimization, we rely on + conventions from JSON. In particular, strings starting with `0x` and that + are otherwise encoded as base16 will be decoded and stored as plain byte + strings on-chain (saving space and therefore, lowering fees). type: object nullable: true @@ -597,9 +592,9 @@ x-transactionMetadata: &transactionMetadata example: 0: "cardano" 1: 14 - 2: { "hex": "2512a00e9653fe49a44a5886202e24d77eeb998f" } + 2: "0x2512a00e9653fe49a44a5886202e24d77eeb998f" 3: [14, 42, 1337] - 4: [["k1", "v1"], ["k2", "v2"]] + 4: { "key": "value", "14": 42 } x-stakePoolApparentPerformance: &stakePoolApparentPerformance description: | @@ -1408,26 +1403,14 @@ components: oneOf: - type: string - type: number - - title: bytestring - type: object - required: - - hex - properties: - hex: - type: string - pattern: '^[0-9a-fA-F]$' - title: list type: array items: $ref: "#/components/schemas/TransactionMetadatum" - title: map - type: array - items: - type: array - minItems: 2 - maxItems: 2 - items: - $ref: "#/components/schemas/TransactionMetadatum" + type: object + additionalProperties: + $ref: "#/components/schemas/TransactionMetadatum" ############################################################################# # # From 85a3269bb6d8f219373e1fc4ba7944bc67b8307c Mon Sep 17 00:00:00 2001 From: KtorZ Date: Wed, 2 Sep 2020 15:06:56 +0200 Subject: [PATCH 3/3] add missing TransactionMetadatum to definitions for OpenAPI specs verification --- .../test/unit/Cardano/Wallet/Api/TypesSpec.hs | 44 ++++++++++++++++--- specifications/api/swagger.yaml | 1 - 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs b/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs index 1c8c82bfdf6..6a96f590510 100644 --- a/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs +++ b/lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs @@ -160,7 +160,7 @@ import Cardano.Wallet.Primitive.Types import Cardano.Wallet.Unsafe ( unsafeFromText, unsafeXPrv ) import Control.Lens - ( at, (.~), (^.) ) + ( at, (.~), (?~), (^.) ) import Control.Monad ( forM_, replicateM ) import Control.Monad.IO.Class @@ -190,19 +190,21 @@ import Data.Proxy import Data.Quantity ( Percentage, Quantity (..) ) import Data.Swagger - ( Definitions + ( AdditionalProperties (..) + , Definitions , NamedSchema (..) , Referenced (..) , Schema , SwaggerType (..) , ToSchema (..) + , additionalProperties , enum_ , properties , required , type_ ) import Data.Swagger.Declare - ( Declare ) + ( Declare, MonadDeclare (..) ) import Data.Text ( Text ) import Data.Text.Class @@ -1568,13 +1570,19 @@ instance ToSchema ByronWalletPutPassphraseData where declareSchemaForDefinition "ApiByronWalletPutPassphraseData" instance ToSchema (PostTransactionData t) where - declareNamedSchema _ = declareSchemaForDefinition "ApiPostTransactionData" + declareNamedSchema _ = do + addDefinition transactionMetadatumSchema + declareSchemaForDefinition "ApiPostTransactionData" instance ToSchema (PostTransactionFeeData t) where - declareNamedSchema _ = declareSchemaForDefinition "ApiPostTransactionFeeData" + declareNamedSchema _ = do + addDefinition transactionMetadatumSchema + declareSchemaForDefinition "ApiPostTransactionFeeData" instance ToSchema (ApiTransaction t) where - declareNamedSchema _ = declareSchemaForDefinition "ApiTransaction" + declareNamedSchema _ = do + addDefinition transactionMetadatumSchema + declareSchemaForDefinition "ApiTransaction" instance ToSchema ApiUtxoStatistics where declareNamedSchema _ = declareSchemaForDefinition "ApiWalletUTxOsStatistics" @@ -1603,6 +1611,19 @@ instance ToSchema ApiWalletDelegation where instance ToSchema ApiPostRandomAddressData where declareNamedSchema _ = declareSchemaForDefinition "ApiPostRandomAddressData" +-- FIXME: #ADP-417 +-- +-- OpenAPI 2.0 does not support sum-types and the 'oneOf' combinator. When we +-- switched to a library that supports OpenAPI 3.0, we can remove this empty +-- schema and use instead something like: +-- +-- addDefinition =<< declareSchemaForDefinition "TransactionMetadatum" +-- +transactionMetadatumSchema :: NamedSchema +transactionMetadatumSchema = + NamedSchema (Just "TransactionMetadatum") $ mempty + & additionalProperties ?~ AdditionalPropertiesAllowed True + -- | Utility function to provide an ad-hoc 'ToSchema' instance for a definition: -- we simply look it up within the Swagger specification. declareSchemaForDefinition :: Text -> Declare (Definitions Schema) NamedSchema @@ -1621,6 +1642,17 @@ declareSchemaForDefinition ref = do where replace = TL.replace "components/schemas" "definitions" +-- | Add a known definition to the set of definitions, this may be necessary +-- when we can't inline a definition because it is recursive or, when a +-- definition is only used in an existing schema but has no top-level type for +-- which to define a 'ToSchema' instance. +addDefinition :: NamedSchema -> Declare (Definitions Schema) () +addDefinition (NamedSchema Nothing _) = + error "Trying to add definition for an unnamed NamedSchema!" +addDefinition (NamedSchema (Just k) s) = do + defs <- look + declare $ defs & at k ?~ s + unsafeLookupKey :: Aeson.Value -> Text -> Aeson.Value unsafeLookupKey json k = case json of Aeson.Object m -> fromMaybe bombMissing (HM.lookup k m) diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index 1e0ff10b2a0..3a9dc0f6782 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -583,7 +583,6 @@ x-transactionMetadata: &transactionMetadata type: object nullable: true - minProperties: 1 additionalProperties: $ref: "#/components/schemas/TransactionMetadatum" # Note: propertyNames pattern not supported in current OpenAPI version.