-
Notifications
You must be signed in to change notification settings - Fork 721
/
Friendly.hs
333 lines (295 loc) · 11.6 KB
/
Friendly.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DisambiguateRecordFields #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | User-friendly pretty-printing for textual user interfaces (TUI)
module Cardano.CLI.Run.Friendly (friendlyTxBodyBS) where
import Cardano.Prelude
import Data.Aeson (Value (..), object, toJSON, (.=))
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Types as Aeson
import qualified Data.ByteString.Char8 as BSC
import qualified Data.Map.Strict as Map
import qualified Data.Text as Text
import Data.Yaml (array)
import Data.Yaml.Pretty (defConfig, encodePretty, setConfCompare)
import Cardano.Ledger.Shelley.TxBody (MIRPot (ReservesMIR, TreasuryMIR))
import Cardano.Api as Api
import Cardano.Api.Shelley (Address (ShelleyAddress), StakeAddress (..),
StakeCredential (..), StakePoolParameters (..), fromShelleyPaymentCredential,
fromShelleyStakeCredential, fromShelleyStakeReference)
import Cardano.CLI.Helpers (textShow)
friendlyTxBodyBS :: CardanoEra era -> TxBody era -> ByteString
friendlyTxBodyBS era =
encodePretty (setConfCompare compare defConfig) . friendlyTxBody era
friendlyTxBody :: CardanoEra era -> TxBody era -> Aeson.Value
friendlyTxBody
era
(TxBody
TxBodyContent
{ txAuxScripts
, txCertificates
, txFee
, txIns
, txMetadata
, txMintValue
, txOuts
, txUpdateProposal
, txValidityRange
, txWithdrawals
}) =
object
[ "auxiliary scripts" .= friendlyAuxScripts txAuxScripts
, "certificates" .= friendlyCertificates txCertificates
, "era" .= era
, "fee" .= friendlyFee txFee
, "inputs" .= friendlyInputs txIns
, "metadata" .= friendlyMetadata txMetadata
, "mint" .= friendlyMintValue txMintValue
, "outputs" .= map friendlyTxOut txOuts
, "update proposal" .= friendlyUpdateProposal txUpdateProposal
, "validity range" .= friendlyValidityRange era txValidityRange
, "withdrawals" .= friendlyWithdrawals txWithdrawals
]
-- | Special case of validity range:
-- in Shelley, upper bound is TTL, and no lower bound
pattern ShelleyTtl
:: SlotNo -> (TxValidityLowerBound era, TxValidityUpperBound era)
pattern ShelleyTtl ttl <-
( TxValidityNoLowerBound
, TxValidityUpperBound ValidityUpperBoundInShelleyEra ttl
)
friendlyValidityRange
:: CardanoEra era
-> (TxValidityLowerBound era, TxValidityUpperBound era)
-> Aeson.Value
friendlyValidityRange era = \case
ShelleyTtl ttl -> object ["time to live" .= ttl]
(lowerBound, upperBound)
| isLowerBoundSupported || isUpperBoundSupported ->
object
[ "lower bound" .=
case lowerBound of
TxValidityNoLowerBound -> Null
TxValidityLowerBound _ s -> toJSON s
, "upper bound" .=
case upperBound of
TxValidityNoUpperBound _ -> Null
TxValidityUpperBound _ s -> toJSON s
]
| otherwise -> Null
where
isLowerBoundSupported = isJust $ validityLowerBoundSupportedInEra era
isUpperBoundSupported = isJust $ validityUpperBoundSupportedInEra era
friendlyWithdrawals :: TxWithdrawals ViewTx era -> Aeson.Value
friendlyWithdrawals TxWithdrawalsNone = Null
friendlyWithdrawals (TxWithdrawals _ withdrawals) =
array
[ object $
"address" .= serialiseAddress addr :
"amount" .= friendlyLovelace amount :
friendlyStakeAddress addr
| (addr, amount, _) <- withdrawals
]
friendlyStakeAddress :: StakeAddress -> [(Text, Aeson.Value)]
friendlyStakeAddress (StakeAddress net cred) =
[ "network" .= net
, friendlyStakeCredential $ fromShelleyStakeCredential cred
]
friendlyTxOut :: TxOut CtxTx era -> Aeson.Value
friendlyTxOut (TxOut addr amount mdatum) =
object $
case addr of
AddressInEra ByronAddressInAnyEra byronAdr ->
[ "address era" .= String "Byron"
, "address" .= serialiseAddress byronAdr
, "amount" .= friendlyTxOutValue amount
]
AddressInEra (ShelleyAddressInEra sbe) saddr@(ShelleyAddress net cred stake) ->
let preAlonzo :: [Aeson.Pair]
preAlonzo =
friendlyPaymentCredential (fromShelleyPaymentCredential cred) :
[ "address era" .= Aeson.String "Shelley"
, "network" .= net
, "address" .= serialiseAddress saddr
, "amount" .= friendlyTxOutValue amount
, "stake reference" .=
friendlyStakeReference (fromShelleyStakeReference stake)
]
datum :: ShelleyBasedEra era -> [Aeson.Pair]
datum ShelleyBasedEraShelley = []
datum ShelleyBasedEraAllegra = []
datum ShelleyBasedEraMary = []
datum ShelleyBasedEraAlonzo = ["datum" .= renderDatum mdatum]
in preAlonzo ++ datum sbe
where
renderDatum :: TxOutDatum CtxTx era -> Aeson.Value
renderDatum TxOutDatumNone = Aeson.Null
renderDatum (TxOutDatumHash _ h) =
Aeson.String $ serialiseToRawBytesHexText h
renderDatum (TxOutDatum _ sData) =
scriptDataToJson ScriptDataJsonDetailedSchema sData
friendlyStakeReference :: StakeAddressReference -> Aeson.Value
friendlyStakeReference = \case
NoStakeAddress -> Null
StakeAddressByPointer ptr -> String (show ptr)
StakeAddressByValue cred -> object [friendlyStakeCredential cred]
friendlyUpdateProposal :: TxUpdateProposal era -> Aeson.Value
friendlyUpdateProposal = \case
TxUpdateProposalNone -> Null
TxUpdateProposal _ p -> String $ textShow p
friendlyCertificates :: TxCertificates ViewTx era -> Aeson.Value
friendlyCertificates = \case
TxCertificatesNone -> Null
TxCertificates _ cs _ -> array $ map friendlyCertificate cs
friendlyCertificate :: Certificate -> Aeson.Value
friendlyCertificate =
object . (:[]) .
\case
-- Stake address certificates
StakeAddressRegistrationCertificate credential ->
"stake address registration" .=
object [friendlyStakeCredential credential]
StakeAddressDeregistrationCertificate credential ->
"stake address deregistration" .=
object [friendlyStakeCredential credential]
StakeAddressDelegationCertificate credential poolId ->
"stake address delegation" .=
object [friendlyStakeCredential credential, "pool" .= poolId]
-- Stake pool certificates
StakePoolRegistrationCertificate parameters ->
"stake pool registration" .= friendlyStakePoolParameters parameters
StakePoolRetirementCertificate poolId epochNo ->
"stake pool retirement" .= object ["pool" .= poolId, "epoch" .= epochNo]
-- Special certificates
GenesisKeyDelegationCertificate genesisKeyHash delegateKeyHash vrfKeyHash ->
"genesis key delegation" .=
object
[ "genesis key hash" .= serialiseToRawBytesHexText genesisKeyHash
, "delegate key hash" .= serialiseToRawBytesHexText delegateKeyHash
, "VRF key hash" .= serialiseToRawBytesHexText vrfKeyHash
]
MIRCertificate pot target ->
"MIR" .= object ["pot" .= friendlyMirPot pot, friendlyMirTarget target]
friendlyMirTarget :: MIRTarget -> (Text, Aeson.Value)
friendlyMirTarget = \case
StakeAddressesMIR addresses ->
"target stake addresses" .=
[ object
[ friendlyStakeCredential credential
, "amount" .= friendlyLovelace lovelace
]
| (credential, lovelace) <- addresses
]
SendToReservesMIR amount -> "send to reserves" .= friendlyLovelace amount
SendToTreasuryMIR amount -> "send to treasury" .= friendlyLovelace amount
friendlyStakeCredential :: StakeCredential -> (Text, Aeson.Value)
friendlyStakeCredential = \case
StakeCredentialByKey keyHash ->
"stake credential key hash" .= serialiseToRawBytesHexText keyHash
StakeCredentialByScript scriptHash ->
"stake credential script hash" .= serialiseToRawBytesHexText scriptHash
friendlyPaymentCredential :: PaymentCredential -> (Text, Aeson.Value)
friendlyPaymentCredential = \case
PaymentCredentialByKey keyHash ->
"payment credential key hash" .= serialiseToRawBytesHexText keyHash
PaymentCredentialByScript scriptHash ->
"payment credential script hash" .= serialiseToRawBytesHexText scriptHash
friendlyMirPot :: MIRPot -> Aeson.Value
friendlyMirPot = \case
ReservesMIR -> "reserves"
TreasuryMIR -> "treasury"
friendlyStakePoolParameters :: StakePoolParameters -> Aeson.Value
friendlyStakePoolParameters
StakePoolParameters
{ stakePoolId
, stakePoolVRF
, stakePoolCost
, stakePoolMargin
, stakePoolRewardAccount
, stakePoolPledge
, stakePoolOwners
, stakePoolRelays
, stakePoolMetadata
} =
object
[ "pool" .= stakePoolId
, "VRF key hash" .= serialiseToRawBytesHexText stakePoolVRF
, "cost" .= friendlyLovelace stakePoolCost
, "margin" .= friendlyRational stakePoolMargin
, "reward account" .= object (friendlyStakeAddress stakePoolRewardAccount)
, "pledge" .= friendlyLovelace stakePoolPledge
, "owners (stake key hashes)"
.= map serialiseToRawBytesHexText stakePoolOwners
, "relays" .= map textShow stakePoolRelays
, "metadata" .= fmap textShow stakePoolMetadata
]
friendlyRational :: Rational -> Aeson.Value
friendlyRational r =
String $
case d of
1 -> textShow n
_ -> textShow n <> "/" <> textShow d
where
n = numerator r
d = denominator r
friendlyFee :: TxFee era -> Aeson.Value
friendlyFee = \case
TxFeeImplicit _ -> "implicit"
TxFeeExplicit _ fee -> friendlyLovelace fee
friendlyLovelace :: Lovelace -> Aeson.Value
friendlyLovelace (Lovelace value) = String $ textShow value <> " Lovelace"
friendlyMintValue :: TxMintValue ViewTx era -> Aeson.Value
friendlyMintValue = \case
TxMintNone -> Null
TxMintValue _ v _ -> friendlyValue v
friendlyTxOutValue :: TxOutValue era -> Aeson.Value
friendlyTxOutValue = \case
TxOutAdaOnly _ lovelace -> friendlyLovelace lovelace
TxOutValue _ v -> friendlyValue v
friendlyValue :: Api.Value -> Aeson.Value
friendlyValue v =
object
[ case bundle of
ValueNestedBundleAda q -> "lovelace" .= q
ValueNestedBundle policy assets ->
friendlyPolicyId policy .= friendlyAssets assets
| bundle <- bundles
]
where
ValueNestedRep bundles = valueToNestedRep v
friendlyPolicyId = ("policy " <>) . serialiseToRawBytesHexText
friendlyAssets = Map.mapKeys friendlyAssetName
friendlyAssetName = \case
"" -> "default asset"
name@(AssetName nameBS) ->
"asset " <> serialiseToRawBytesHexText name <> nameAsciiSuffix
where
nameAsciiSuffix
| nameIsAscii = " (" <> nameAscii <> ")"
| otherwise = ""
nameIsAscii = BSC.all (\c -> isAscii c && isAlphaNum c) nameBS
nameAscii = Text.pack $ BSC.unpack nameBS
friendlyMetadata :: TxMetadataInEra era -> Aeson.Value
friendlyMetadata = \case
TxMetadataNone -> Null
TxMetadataInEra _ (TxMetadata m) -> toJSON $ friendlyMetadataValue <$> m
friendlyMetadataValue :: TxMetadataValue -> Aeson.Value
friendlyMetadataValue = \case
TxMetaNumber int -> toJSON int
TxMetaBytes bytes -> String $ textShow bytes
TxMetaList lst -> array $ map friendlyMetadataValue lst
TxMetaMap m ->
array
[array [friendlyMetadataValue k, friendlyMetadataValue v] | (k, v) <- m]
TxMetaText text -> toJSON text
friendlyAuxScripts :: TxAuxScripts era -> Aeson.Value
friendlyAuxScripts = \case
TxAuxScriptsNone -> Null
TxAuxScripts _ scripts -> String $ textShow scripts
friendlyInputs :: [(TxIn, build)] -> Aeson.Value
friendlyInputs = toJSON . map fst