Skip to content

JSON output for cardano-cli #3548

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 13 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 113 additions & 1 deletion cardano-api/src/Cardano/Api/Certificate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ module Cardano.Api.Certificate (
AsType(..)
) where

import Data.Aeson (ToJSON, Value (..), object, toJSON, (.=))
import qualified Data.Aeson as Aeson
import Data.ByteString (ByteString)
import qualified Data.Foldable as Foldable
import qualified Data.Map.Strict as Map
Expand Down Expand Up @@ -68,7 +70,9 @@ import Cardano.Api.HasTypeProxy
import Cardano.Api.Keys.Byron
import Cardano.Api.Keys.Praos
import Cardano.Api.Keys.Shelley
import Cardano.Api.Orphans ()
import Cardano.Api.SerialiseCBOR
import Cardano.Api.SerialiseRaw (serialiseToRawBytesHexText)
import Cardano.Api.SerialiseTextEnvelope
import Cardano.Api.StakePoolMetadata
import Cardano.Api.Value
Expand Down Expand Up @@ -108,6 +112,52 @@ instance ToCBOR Certificate where
instance FromCBOR Certificate where
fromCBOR = fromShelleyCertificate <$> Shelley.fromEraCBOR @Shelley.Shelley

instance ToJSON Certificate where
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it would be possible here to change constructors to records and then use genericToJSON with constructorTagModifier instead of manual instances?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we prefer explicit instances. @Jimbo4350 do you have an opinion on genericToJSON?

toJSON a = case a of
StakeAddressRegistrationCertificate credential ->
object
[ "type" .= String "StakeAddressRegistration"
, "credential" .= credential
]
StakeAddressDeregistrationCertificate credential ->
object
[ "type" .= String "StakeAddressDeregistration"
, "credential" .= credential
]
StakeAddressPoolDelegationCertificate credential pool ->
object
[ "type" .= String "StakeAddressDelegation"
, "credential" .= credential
, "pool" .= pool
]
StakePoolRegistrationCertificate parameters ->
object
[ "type" .= String "StakePoolRegistration"
, "parameters" .= parameters
]
StakePoolRetirementCertificate pool epoch ->
object
[ "type" .= String "StakePoolRetirement"
, "pool" .= pool
, "epoch" .= epoch
]
GenesisKeyDelegationCertificate
genesisKeyHash
genesisDelegateKeyHash
vrfKeyHash ->
object
[ "type" .= String "GenesisKeyDelegation"
, "genesisKeyHash" .= serialiseToRawBytesHexText genesisKeyHash
, "genesisDelegateKeyHash" .= serialiseToRawBytesHexText genesisDelegateKeyHash
, "vrfKeyHash" .= serialiseToRawBytesHexText vrfKeyHash
]
MIRCertificate pot target ->
object
[ "type" .= String "MIR"
, "pot" .= pot
, "target" .= target
]

instance HasTextEnvelope Certificate where
textEnvelopeType _ = "CertificateShelley"
textEnvelopeDefaultDescr cert = case cert of
Expand Down Expand Up @@ -137,6 +187,24 @@ data MIRTarget =
| SendToTreasuryMIR Lovelace
deriving stock (Eq, Show)

instance ToJSON MIRTarget where
toJSON a = case a of
StakeAddressesMIR addresses ->
object
[ "type" .= String "StakeAddresses"
, "addresses" .= addresses
]
SendToReservesMIR lovelace ->
object
[ "type" .= String "SendToReserves"
, "lovelace" .= lovelace
]
SendToTreasuryMIR lovelace ->
object
[ "type" .= String "SendToTreasury"
, "lovelace" .= lovelace
]

-- ----------------------------------------------------------------------------
-- Stake pool parameters
--
Expand All @@ -157,13 +225,27 @@ data StakePoolParameters =
}
deriving (Eq, Show)

instance ToJSON StakePoolParameters where
toJSON ref =
object
[ "id" .= stakePoolId ref
, "vrf" .= serialiseToRawBytesHexText (stakePoolVRF ref)
, "cost" .= stakePoolCost ref
, "margin" .= stakePoolMargin ref
, "rewardAccount" .= stakePoolRewardAccount ref
, "pledge" .= stakePoolPledge ref
, "owners" .= map serialiseToRawBytesHexText (stakePoolOwners ref)
, "relays" .= stakePoolRelays ref
, "metadata" .= stakePoolMetadata ref
]

data StakePoolRelay =

-- | One or both of IPv4 & IPv6
StakePoolRelayIp
(Maybe IPv4) (Maybe IPv6) (Maybe PortNumber)

-- | An DNS name pointing to a @A@ or @AAAA@ record.
-- | A DNS name pointing to a @A@ or @AAAA@ record.
| StakePoolRelayDnsARecord
ByteString (Maybe PortNumber)

Expand All @@ -173,13 +255,43 @@ data StakePoolRelay =

deriving (Eq, Show)

instance ToJSON StakePoolRelay where
toJSON a = case a of
StakePoolRelayIp ipv4 ipv6 port ->
object
[ "type" .= String "IP"
, "ipv4" .= ipv4
, "ipv6" .= ipv6
, "port" .= (portToJson <$> port)
]
StakePoolRelayDnsARecord name port ->
object
[ "type" .= String "DnsA"
, "name" .= Text.decodeUtf8 name
, "port" .= (portToJson <$> port)
]
StakePoolRelayDnsSrvRecord name ->
object
[ "type" .= String "DnsSrv"
, "name" .= Text.decodeUtf8 name
]
where
portToJson = Aeson.Number . fromIntegral

data StakePoolMetadataReference =
StakePoolMetadataReference {
stakePoolMetadataURL :: Text,
stakePoolMetadataHash :: Hash StakePoolMetadata
}
deriving (Eq, Show)

instance ToJSON StakePoolMetadataReference where
toJSON ref =
object
[ "url" .= stakePoolMetadataURL ref
, "hash" .= serialiseToRawBytesHexText (stakePoolMetadataHash ref)
]


-- ----------------------------------------------------------------------------
-- Constructor functions
Expand Down
28 changes: 28 additions & 0 deletions cardano-api/src/Cardano/Api/Orphans.hs
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UndecidableInstances #-}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if there is a way to avoid using Undecidableinstances here? Maybe this could be solved by OVERLAPPING / OVERLAPPABLE?


{-# OPTIONS_GHC -Wno-orphans #-}

module Cardano.Api.Orphans () where

import Data.Aeson (ToJSON (..), object, pairs, (.=))
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Base16 as Base16
import Data.Text (Text)
import qualified Data.Text.Encoding as Text

import qualified Cardano.Ledger.Crypto as Crypto

import Cardano.Ledger.Shelley.API (MIRPot (..))
import qualified Cardano.Ledger.Shelley.API as Ledger (KeyRole (..), WitVKey)

import Cardano.Api.SerialiseCBOR (ToCBOR (..))
import Cardano.Binary (encodeListLen, serialize')
import Codec.CBOR.Encoding (encodeWord)
import qualified Ouroboros.Consensus.Shelley.Ledger.Query as Consensus

-- Orphan instances involved in the JSON output of the API queries.
Expand Down Expand Up @@ -47,3 +64,14 @@ stakeSnapshotToPair Consensus.StakeSnapshot
, "stakeSet" .= ssSetPool
, "stakeGo" .= ssGoPool
]

instance ToJSON MIRPot where
toJSON pot = toJSON @Text $
case pot of
ReservesMIR -> "reserves"
TreasuryMIR -> "treasury"

instance Crypto.Crypto crypto => ToJSON (Ledger.WitVKey 'Ledger.Witness crypto) where
toJSON = toJSON . Text.decodeLatin1 . Base16.encode . serialize' . prefixWithTag
where
prefixWithTag wit = encodeListLen 2 <> encodeWord 0 <> toCBOR wit
47 changes: 42 additions & 5 deletions cardano-api/src/Cardano/Api/ProtocolParameters.hs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
Expand Down Expand Up @@ -538,7 +537,7 @@ data ProtocolParametersUpdate =
-- /Introduced in Babbage. Supercedes 'protocolUpdateUTxOCostPerWord'/
protocolUpdateUTxOCostPerByte :: Maybe Lovelace
}
deriving (Eq, Show)
deriving (Eq, Generic, Show)

instance Semigroup ProtocolParametersUpdate where
ppu1 <> ppu2 =
Expand Down Expand Up @@ -673,6 +672,37 @@ instance FromCBOR ProtocolParametersUpdate where
<*> fromCBOR
<*> fromCBOR

instance ToJSON ProtocolParametersUpdate where
toJSON ref =
object $
( ("protocolVersion" ..=? protocolUpdateProtocolVersion ref)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have Generic instance available here already, why not use genericToJSON? 😃

. ("decentralization" ..=? protocolUpdateDecentralization ref)
. ("extraPraosEntropy" ..=? protocolUpdateExtraPraosEntropy ref)
. ("maxBlockHeaderSize" ..=? protocolUpdateMaxBlockHeaderSize ref)
. ("maxBlockBodySize" ..=? protocolUpdateMaxBlockBodySize ref)
. ("maxTxSize" ..=? protocolUpdateMaxTxSize ref)
. ("txFeeFixed" ..=? protocolUpdateTxFeeFixed ref)
. ("txFeePerByte" ..=? protocolUpdateTxFeePerByte ref)
. ("minUTxOValue" ..=? protocolUpdateMinUTxOValue ref)
. ("stakeAddressDeposit" ..=? protocolUpdateStakeAddressDeposit ref)
. ("stakePoolDeposit" ..=? protocolUpdateStakePoolDeposit ref)
. ("minPoolCost" ..=? protocolUpdateMinPoolCost ref)
. ("poolRetireMaxEpoch" ..=? protocolUpdatePoolRetireMaxEpoch ref)
. ("stakePoolTargetNum" ..=? protocolUpdateStakePoolTargetNum ref)
. ("poolPledgeInfluence" ..=? protocolUpdatePoolPledgeInfluence ref)
. ("monetaryExpansion" ..=? protocolUpdateMonetaryExpansion ref)
. ("treasuryCut" ..=? protocolUpdateTreasuryCut ref)
. ("utxoCostPerWord" ..=? protocolUpdateUTxOCostPerWord ref)
. ("costModels" ..= protocolUpdateCostModels ref)
. ("prices" ..=? protocolUpdatePrices ref)
. ("maxTxExUnits" ..=? protocolUpdateMaxTxExUnits ref)
. ("maxBlockExUnits" ..=? protocolUpdateMaxBlockExUnits ref)
. ("maxValueSize" ..=? protocolUpdateMaxValueSize ref)
. ("collateralPercent" ..=? protocolUpdateCollateralPercent ref)
. ("maxCollateralInputs" ..=? protocolUpdateMaxCollateralInputs ref)
. ("utxoCostPerByte" ..=? protocolUpdateUTxOCostPerByte ref)
) []


-- ----------------------------------------------------------------------------
-- Praos nonce
Expand Down Expand Up @@ -778,7 +808,7 @@ fromAlonzoPrices Alonzo.Prices{Alonzo.prSteps, Alonzo.prMem} =

newtype CostModel = CostModel [Integer]
deriving (Eq, Show)
deriving newtype (ToCBOR, FromCBOR)
deriving newtype (ToCBOR, FromCBOR, ToJSON)

newtype CostModels = CostModels { unCostModels :: Map AnyPlutusScriptVersion CostModel }
deriving (Eq, Show)
Expand Down Expand Up @@ -845,7 +875,7 @@ data UpdateProposal =
UpdateProposal
!(Map (Hash GenesisKey) ProtocolParametersUpdate)
!EpochNo
deriving stock (Eq, Show)
deriving stock (Eq, Generic, Show)
deriving anyclass SerialiseAsCBOR

instance HasTypeProxy UpdateProposal where
Expand All @@ -868,6 +898,13 @@ instance FromCBOR UpdateProposal where
<$> fromCBOR
<*> fromCBOR

instance ToJSON UpdateProposal where
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not convert UpdateProposal to a record and then use genericToJSON?

toJSON (UpdateProposal updates epoch) =
object
[ "updates" .= Map.mapKeys serialiseToRawBytesHexText updates
, "epoch" .= epoch
]

makeShelleyUpdateProposal :: ProtocolParametersUpdate
-> [Hash GenesisKey]
-> EpochNo
Expand Down Expand Up @@ -1247,7 +1284,7 @@ unbundleLedgerShelleyBasedProtocolParams
:: ShelleyBasedEra era
-> BundledProtocolParameters era
-> Ledger.PParams (ShelleyLedgerEra era)
unbundleLedgerShelleyBasedProtocolParams = \case
unbundleLedgerShelleyBasedProtocolParams era = case era of
ShelleyBasedEraShelley -> \(BundleAsShelleyBasedProtocolParameters _ _ lpp) -> lpp
ShelleyBasedEraAllegra -> \(BundleAsShelleyBasedProtocolParameters _ _ lpp) -> lpp
ShelleyBasedEraMary -> \(BundleAsShelleyBasedProtocolParameters _ _ lpp) -> lpp
Expand Down
34 changes: 32 additions & 2 deletions cardano-api/src/Cardano/Api/Script.hs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
Expand Down Expand Up @@ -106,6 +106,7 @@ module Cardano.Api.Script (
Hash(..),
) where

import qualified Data.ByteString.Base16 as Base16
import qualified Data.ByteString.Lazy as LBS
import Data.ByteString.Short (ShortByteString)
import qualified Data.ByteString.Short as SBS
Expand Down Expand Up @@ -247,6 +248,9 @@ instance TestEquality PlutusScriptVersion where
testEquality PlutusScriptV2 PlutusScriptV2 = Just Refl
testEquality _ _ = Nothing

instance ToJSON (PlutusScriptVersion lang) where
toJSON = toJSON . fromEnum . AnyScriptLanguage . PlutusScriptLanguage


data AnyScriptLanguage where
AnyScriptLanguage :: ScriptLanguage lang -> AnyScriptLanguage
Expand Down Expand Up @@ -428,6 +432,19 @@ instance IsScriptLanguage lang => HasTextEnvelope (Script lang) where
PlutusScriptLanguage PlutusScriptV1 -> "PlutusScriptV1"
PlutusScriptLanguage PlutusScriptV2 -> "PlutusScriptV2"

instance ToJSON (Script lang) where
toJSON a = case a of
SimpleScript script ->
object
[ "type" .= String "simple"
, "script" .= script
]
PlutusScript version script ->
object
[ "type" .= String "plutus"
, "version" .= version
, "script" .= script
]

-- ----------------------------------------------------------------------------
-- Scripts in any language
Expand Down Expand Up @@ -513,6 +530,13 @@ instance Eq (ScriptInEra era) where
Nothing -> False
Just Refl -> script == script'

instance ToJSON (ScriptInEra era) where
toJSON (ScriptInEra language script) =
object
[ "language" .= language
, "script" .= script
]


data ScriptLanguageInEra lang era where

Expand Down Expand Up @@ -983,6 +1007,12 @@ instance IsPlutusScriptLanguage lang => HasTextEnvelope (PlutusScript lang) wher
PlutusScriptV1 -> "PlutusScriptV1"
PlutusScriptV2 -> "PlutusScriptV2"

instance ToJSON (PlutusScript lang) where
toJSON (PlutusScriptSerialised bytes) =
object
[ "base16" .= Text.decodeUtf8 (Base16.encode $ SBS.fromShort bytes)
]


-- | An example Plutus script that always succeeds, irrespective of inputs.
--
Expand Down Expand Up @@ -1324,7 +1354,7 @@ instance IsCardanoEra era => FromJSON (ReferenceScript era) where
ReferenceScript refSupInEra <$> o .: "referenceScript"

instance EraCast ReferenceScript where
eraCast toEra = \case
eraCast toEra a = case a of
ReferenceScriptNone -> pure ReferenceScriptNone
v@(ReferenceScript (_ :: ReferenceTxInsScriptsInlineDatumsSupportedInEra fromEra) scriptInAnyLang) ->
case refInsScriptsAndInlineDatsSupportedInEra toEra of
Expand Down
Loading