From a4195dc879f156eee0aa87a7627995fed5769ef4 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Fri, 5 Jan 2024 16:55:31 +0100 Subject: [PATCH] Re-enable old metadata format under a specific flag. Turns out some people liked it (looking at you Andrew). I don't think it makes sense to keep it as a default however, since many people got surprised by it. But having it enabled on demand via a flag sounds like a good compromise. --- .../017-api-version-6-major-rewrite.md | 6 +- .../TypeScript/packages/schema/src/index.ts | 27 ++++++- docs/static/cardano.json | 81 ++++++++++++++++++- server/src/Ogmios/App/Configuration.hs | 3 + .../Ogmios/App/Inspect/InspectTransaction.hs | 5 +- server/src/Ogmios/App/Server/WebSocket.hs | 6 +- server/src/Ogmios/Data/Json.hs | 6 +- server/src/Ogmios/Data/Json/Allegra.hs | 10 +-- server/src/Ogmios/Data/Json/Alonzo.hs | 10 +-- server/src/Ogmios/Data/Json/Babbage.hs | 8 +- server/src/Ogmios/Data/Json/Conway.hs | 8 +- server/src/Ogmios/Data/Json/Mary.hs | 10 +-- server/src/Ogmios/Data/Json/Prelude.hs | 6 ++ server/src/Ogmios/Data/Json/Shelley.hs | 40 +++++++-- server/src/Ogmios/Options.hs | 22 ++++- .../unit/Ogmios/App/Protocol/ChainSyncSpec.hs | 3 +- .../unit/Ogmios/App/Protocol/TxMonitorSpec.hs | 5 +- server/test/unit/Ogmios/Data/JsonSpec.hs | 13 ++- .../vectors/NextTransactionResponse/009.json | 2 +- 19 files changed, 213 insertions(+), 58 deletions(-) diff --git a/architectural-decisions/accepted/017-api-version-6-major-rewrite.md b/architectural-decisions/accepted/017-api-version-6-major-rewrite.md index 3e050093cd..54a0ee1415 100644 --- a/architectural-decisions/accepted/017-api-version-6-major-rewrite.md +++ b/architectural-decisions/accepted/017-api-version-6-major-rewrite.md @@ -754,7 +754,7 @@ The representation of `Value` has been changed to be more compact, more extensib The representation of transaction metadata has been both simplified and made more user-friendly, while remaining safe for more complex use-cases. In fact, many people in the community have grown to expect transaction metadata to be JSON objects. However, they aren't. Or more specifically, they aren't necessarily. There are actually plenty of transaction metadata on-chain that aren't representable as valid JSON. Prior to version 6, Ogmios would give a so-called detailed JSON schema representation of those metadata, by encoding the binary encoding as a JSON object. This has created a lot of confusion for rookie users not yet familiar with Cardano entrails who would be expecting a plain JSON object. Plus, the format was unpractical to parse for client down the line as it used object keys as type discriminant, leaving decoders no choice to try various encoding alternatively. -Starting from version 6, Ogmios returns transaction metadata as JSON object _when possible_ and fallback to CBOR otherwise. In fact, when metadata aren't representable as JSON object, this is probably because they are some elaborated binary encoding and users consuming them are most seemingly capable of decoding that themselves in the way they intended. Ogmios can be configured to always return the CBOR output using the `--include-metadata-cbor` flag on start. +Starting from version 6, **by default** (see note below) Ogmios returns transaction metadata as JSON object _when possible_ and fallback to CBOR otherwise. In fact, when metadata aren't representable as JSON object, this is probably because they are some elaborated binary encoding and users consuming them are most seemingly capable of decoding that themselves in the way they intended. Ogmios can be configured to always return the CBOR output using the `--include-metadata-cbor` flag on start. To give a concrete example: @@ -809,3 +809,7 @@ To give a concrete example: When it isn't possible to represent the metadata as a plain JSON object, the `json` field is simply omitted and the metadata is only provided as CBOR. + +> [!INFO] +> +> The old behavior can be requested on-demand by enabling the `--metadata-detailed-schema` flag. When enabled, the `json` metadata will always be present and use the old declarative representation. This can be used in combination with the new `--include-metadata-cbor` flag as well. diff --git a/clients/TypeScript/packages/schema/src/index.ts b/clients/TypeScript/packages/schema/src/index.ts index 7943c0ba48..90f2d3bd0d 100644 --- a/clients/TypeScript/packages/schema/src/index.ts +++ b/clients/TypeScript/packages/schema/src/index.ts @@ -94,10 +94,12 @@ export type Nonce = Neutral | DigestBlake2B256; export type Neutral = "neutral"; export type Int64 = number; export type CostModel = Int64[]; -export type Metadatum = Integer | String | ArrayMetadatum | ObjectMetadatum; +export type Metadatum = MetadatumNoSchema | MetadatumDetailedSchema; +export type MetadatumNoSchema = Integer | String | ArrayMetadatum | ObjectMetadatum; export type Integer = bigint; export type String = string; -export type ArrayMetadatum = Metadatum[]; +export type ArrayMetadatum = MetadatumNoSchema[]; +export type MetadatumDetailedSchema = Int | String1 | Bytes | List | Map; /** * An Ed25519 verification key. */ @@ -951,7 +953,26 @@ export interface MetadataLabels { }; } export interface ObjectMetadatum { - [k: string]: Metadatum; + [k: string]: MetadatumNoSchema; +} +export interface Int { + int: bigint; +} +export interface String1 { + string: string; +} +export interface Bytes { + bytes: string; +} +export interface List { + list: MetadatumDetailedSchema[]; +} +export interface Map { + map: MetadatumMap[]; +} +export interface MetadatumMap { + k: MetadatumDetailedSchema; + v: MetadatumDetailedSchema; } /** * A signatory (EdDSA) for the transaction. The fields 'chainCode' and 'addressAttributes' are only present on bootstrap signatures (when spending from a Byron/Bootstrap address). diff --git a/docs/static/cardano.json b/docs/static/cardano.json index 0bbdc2e55e..8086448704 100644 --- a/docs/static/cardano.json +++ b/docs/static/cardano.json @@ -1434,13 +1434,86 @@ , "Metadatum": { "title": "Metadatum" , "oneOf": - [ { "title": "Integer", "type": "integer" } - , { "title": "String", "type": "string" } - , { "title": "Array", "type": "array", "items": { "$ref": "cardano.json#/definitions/Metadatum" } } - , { "title": "Object", "type": "object", "additionalProperties": { "$ref": "cardano.json#/definitions/Metadatum" } } + [ { "title": "Metadatum" + , "oneOf": + [ { "title": "Integer", "type": "integer" } + , { "title": "String", "type": "string" } + , { "title": "Array", "type": "array", "items": { "$ref": "cardano.json#/definitions/Metadatum/oneOf/0" } } + , { "title": "Object", "type": "object", "additionalProperties": { "$ref": "cardano.json#/definitions/Metadatum/oneOf/0" } } + ] + } + , { "title": "Metadatum" + , "oneOf": + [ { "type": "object" + , "title": "int" + , "additionalProperties": false + , "required": ["int"] + , "properties": + { "int": + { "type": "integer" + } + } + } + , { "type": "object" + , "title": "string" + , "additionalProperties": false + , "required": ["string"] + , "properties": + { "string": + { "type": "string" + } + } + } + , { "type": "object" + , "title": "bytes" + , "additionalProperties": false + , "required": ["bytes"] + , "properties": + { "bytes": + { "type": "string" + , "contentEncoding": "base16" + , "pattern": "^[0-9a-f]*$" + } + } + } + , { "type": "object" + , "title": "list" + , "additionalProperties": false + , "required": ["list"] + , "properties": + { "list": + { "type": "array" + , "items": { "$ref": "cardano.json#/definitions/Metadatum/oneOf/1" } + } + } + } + , { "type": "object" + , "title": "map" + , "additionalProperties": false + , "required": ["map"] + , "properties": + { "map": + { "type": "array" + , "items": { "$ref": "cardano.json#/definitions/MetadatumMap" } + } + } + } + ] + } ] } + , "MetadatumMap": + { "title": "MetadatumMap" + , "type": "object" + , "additionalProperties": false + , "required": ["k", "v"] + , "properties": + { "k": { "$ref": "cardano.json#/definitions/Metadatum/oneOf/1" } + , "v": { "$ref": "cardano.json#/definitions/Metadatum/oneOf/1" } + } + } + , "Network": { "title": "Network" , "type": "string" diff --git a/server/src/Ogmios/App/Configuration.hs b/server/src/Ogmios/App/Configuration.hs index 4bec538472..248f6d281d 100644 --- a/server/src/Ogmios/App/Configuration.hs +++ b/server/src/Ogmios/App/Configuration.hs @@ -13,6 +13,7 @@ module Ogmios.App.Configuration -- * Configuration Configuration (..) , IncludeCbor (..) + , MetadataFormat (..) , includeAllCbor , omitOptionalCbor , Severity (..) @@ -56,6 +57,7 @@ import Ogmios.Control.MonadLog ) import Ogmios.Data.Json.Prelude ( IncludeCbor (..) + , MetadataFormat (..) , includeAllCbor , omitOptionalCbor ) @@ -84,6 +86,7 @@ data Configuration = Configuration , connectionTimeout :: !Int , maxInFlight :: !Int , includeCbor :: !IncludeCbor + , metadataFormat :: !MetadataFormat } deriving (Generic, Eq, Show) data NetworkParameters = NetworkParameters diff --git a/server/src/Ogmios/App/Inspect/InspectTransaction.hs b/server/src/Ogmios/App/Inspect/InspectTransaction.hs index 6bfb8a830d..6f2bdfe4ef 100644 --- a/server/src/Ogmios/App/Inspect/InspectTransaction.hs +++ b/server/src/Ogmios/App/Inspect/InspectTransaction.hs @@ -19,7 +19,8 @@ import Ogmios.Data.Json , jsonToByteString ) import Ogmios.Data.Json.Prelude - ( encodeMaybe + ( MetadataFormat (..) + , encodeMaybe ) import System.Exit ( ExitCode (..) @@ -46,4 +47,4 @@ inspectTransaction input = errs exitWith (ExitFailure 1) Right (MultiEraDecoderSuccess transaction) -> - B8.putStrLn $ jsonToByteString $ encodeTx @StandardCrypto omitOptionalCbor transaction + B8.putStrLn $ jsonToByteString $ encodeTx @StandardCrypto (MetadataDetailedSchema, omitOptionalCbor) transaction diff --git a/server/src/Ogmios/App/Server/WebSocket.hs b/server/src/Ogmios/App/Server/WebSocket.hs index 2107215040..f1d5fb0692 100644 --- a/server/src/Ogmios/App/Server/WebSocket.hs +++ b/server/src/Ogmios/App/Server/WebSocket.hs @@ -192,7 +192,7 @@ newWebSocketApp -> m WebSocketApp newWebSocketApp tr unliftIO = do NetworkParameters{slotsPerEpoch,networkMagic} <- asks (view typed) - Configuration{nodeSocket,maxInFlight,nodeConfig,includeCbor} <- asks (view typed) + Configuration{nodeSocket,maxInFlight,nodeConfig,includeCbor,metadataFormat} <- asks (view typed) sensors <- asks (view typed) let getGenesisConfig = GetGenesisConfig { getByronGenesis = readByronGenesis nodeConfig @@ -203,7 +203,7 @@ newWebSocketApp tr unliftIO = do let codecs = ( mkChainSyncCodecs - (encodeBlock includeCbor) + (encodeBlock (metadataFormat, includeCbor)) encodePoint encodeTip @@ -214,7 +214,7 @@ newWebSocketApp tr unliftIO = do , mkTxMonitorCodecs encodeTxId - (encodeTx includeCbor) + (encodeTx (metadataFormat, includeCbor)) , mkTxSubmissionCodecs encodeTxId diff --git a/server/src/Ogmios/Data/Json.hs b/server/src/Ogmios/Data/Json.hs index d04a29aab5..d4f5a1d8e0 100644 --- a/server/src/Ogmios/Data/Json.hs +++ b/server/src/Ogmios/Data/Json.hs @@ -153,12 +153,12 @@ encodeAcquireExpired = \case encodeBlock :: forall crypto. (Era (ByronEra crypto)) - =>IncludeCbor + => (MetadataFormat, IncludeCbor) -> CardanoBlock crypto -> Json encodeBlock opts = \case BlockByron blk -> - Byron.encodeABlockOrBoundary @crypto opts (byronBlockRaw blk) + Byron.encodeABlockOrBoundary @crypto (snd opts) (byronBlockRaw blk) BlockShelley blk -> Shelley.encodeBlock opts blk BlockAllegra blk -> @@ -245,7 +245,7 @@ encodeTx :: forall crypto. ( Crypto crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> GenTx (CardanoBlock crypto) -> Json encodeTx opts = \case diff --git a/server/src/Ogmios/Data/Json/Allegra.hs b/server/src/Ogmios/Data/Json/Allegra.hs index a7f7f8ec76..be77736993 100644 --- a/server/src/Ogmios/Data/Json/Allegra.hs +++ b/server/src/Ogmios/Data/Json/Allegra.hs @@ -48,7 +48,7 @@ type AuxiliaryScripts crypto = -- encodeAuxiliaryData :: forall crypto era. (Era era, era ~ AllegraEra crypto) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Al.AllegraTxAuxData era -> (Json, AuxiliaryScripts crypto) encodeAuxiliaryData opts (Al.AllegraTxAuxData blob scripts) = @@ -61,7 +61,7 @@ encodeAuxiliaryData opts (Al.AllegraTxAuxData blob scripts) = encodeBlock :: Crypto crypto - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> ShelleyBlock (TPraos crypto) (AllegraEra crypto) -> Json encodeBlock opts (ShelleyBlock (Ledger.Block blkHeader txs) headerHash) = @@ -128,10 +128,10 @@ encodeTx ( Crypto crypto , era ~ AllegraEra crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Sh.ShelleyTx era -> Json -encodeTx opts x = +encodeTx (fmt, opts) x = encodeObject ( Shelley.encodeTxId (Ledger.txid @(AllegraEra crypto) (Sh.body x)) <> @@ -151,7 +151,7 @@ encodeTx opts x = where auxiliary = do hash <- Shelley.encodeAuxiliaryDataHash <$> Al.atbAuxDataHash (Sh.body x) - (labels, scripts) <- encodeAuxiliaryData opts <$> Sh.auxiliaryData x + (labels, scripts) <- encodeAuxiliaryData (fmt, opts) <$> Sh.auxiliaryData x pure ( encodeObject ("hash" .= hash <> "labels" .= labels) , scripts diff --git a/server/src/Ogmios/Data/Json/Alonzo.hs b/server/src/Ogmios/Data/Json/Alonzo.hs index 9e291775ae..9355019cec 100644 --- a/server/src/Ogmios/Data/Json/Alonzo.hs +++ b/server/src/Ogmios/Data/Json/Alonzo.hs @@ -71,7 +71,7 @@ encodeAuxiliaryData ( Ledger.Script era ~ Al.AlonzoScript era , Ledger.Api.EraScript era ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Al.AlonzoTxAuxData era -> (Json, AuxiliaryScripts era) encodeAuxiliaryData opts (Al.AlonzoTxAuxData blob timelocks plutus) = @@ -99,7 +99,7 @@ encodeBinaryData = encodeBlock :: Crypto crypto - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> ShelleyBlock (TPraos crypto) (AlonzoEra crypto) -> Json encodeBlock opts (ShelleyBlock (Ledger.Block blkHeader txs) headerHash) = @@ -436,10 +436,10 @@ encodeTx ( Crypto crypto , era ~ AlonzoEra crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Al.AlonzoTx era -> Json -encodeTx opts x = +encodeTx (fmt, opts) x = encodeObject ( Shelley.encodeTxId (Ledger.txid @(AlonzoEra crypto) (Al.body x)) <> @@ -459,7 +459,7 @@ encodeTx opts x = where auxiliary = do hash <- Shelley.encodeAuxiliaryDataHash <$> Al.atbAuxDataHash (Al.body x) - (labels, scripts) <- encodeAuxiliaryData opts <$> Al.auxiliaryData x + (labels, scripts) <- encodeAuxiliaryData (fmt, opts) <$> Al.auxiliaryData x pure ( encodeObject ("hash" .= hash <> "labels" .= labels) , scripts diff --git a/server/src/Ogmios/Data/Json/Babbage.hs b/server/src/Ogmios/Data/Json/Babbage.hs index eb28a181c1..80080c4619 100644 --- a/server/src/Ogmios/Data/Json/Babbage.hs +++ b/server/src/Ogmios/Data/Json/Babbage.hs @@ -58,7 +58,7 @@ import qualified Ogmios.Data.Json.Shelley as Shelley encodeBlock :: ( Crypto crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> ShelleyBlock (Praos crypto) (BabbageEra crypto) -> Json encodeBlock opts (ShelleyBlock (Ledger.Block blkHeader txs) headerHash) = @@ -232,10 +232,10 @@ encodeTx ( Crypto crypto , era ~ BabbageEra crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Ba.AlonzoTx era -> Json -encodeTx opts x = +encodeTx (fmt, opts) x = encodeObject ( Shelley.encodeTxId (Ledger.txid @(BabbageEra crypto) (Ba.body x)) <> @@ -255,7 +255,7 @@ encodeTx opts x = where auxiliary = do hash <- Shelley.encodeAuxiliaryDataHash <$> Ba.btbAuxDataHash (Ba.body x) - (labels, scripts) <- Alonzo.encodeAuxiliaryData opts <$> Ba.auxiliaryData x + (labels, scripts) <- Alonzo.encodeAuxiliaryData (fmt, opts) <$> Ba.auxiliaryData x pure ( encodeObject ("hash" .= hash <> "labels" .= labels) , scripts diff --git a/server/src/Ogmios/Data/Json/Conway.hs b/server/src/Ogmios/Data/Json/Conway.hs index 951131e3a2..39c9f2cd00 100644 --- a/server/src/Ogmios/Data/Json/Conway.hs +++ b/server/src/Ogmios/Data/Json/Conway.hs @@ -80,7 +80,7 @@ encodeAnchor x = encodeObject encodeBlock :: ( Crypto crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> ShelleyBlock (Praos crypto) (ConwayEra crypto) -> Json encodeBlock opts (ShelleyBlock (Ledger.Block blkHeader txs) headerHash) = @@ -516,10 +516,10 @@ encodeTx ( Crypto crypto , era ~ ConwayEra crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Ba.AlonzoTx era -> Json -encodeTx opts x = +encodeTx (fmt, opts) x = encodeObject ( Shelley.encodeTxId (Ledger.txid @(ConwayEra crypto) (Cn.body x)) <> @@ -539,7 +539,7 @@ encodeTx opts x = where auxiliary = do hash <- Shelley.encodeAuxiliaryDataHash <$> Cn.ctbAdHash (Cn.body x) - (labels, scripts) <- Alonzo.encodeAuxiliaryData opts <$> Ba.auxiliaryData x + (labels, scripts) <- Alonzo.encodeAuxiliaryData (fmt, opts) <$> Ba.auxiliaryData x pure ( encodeObject ("hash" .= hash <> "labels" .= labels) , scripts diff --git a/server/src/Ogmios/Data/Json/Mary.hs b/server/src/Ogmios/Data/Json/Mary.hs index 9a32622d27..7a2b16ec6e 100644 --- a/server/src/Ogmios/Data/Json/Mary.hs +++ b/server/src/Ogmios/Data/Json/Mary.hs @@ -51,7 +51,7 @@ type AuxiliaryScripts crypto = encodeAuxiliaryData :: forall crypto era. (Era era, era ~ MaryEra crypto) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Al.AllegraTxAuxData era -> (Json, AuxiliaryScripts crypto) encodeAuxiliaryData opts (Al.AllegraTxAuxData blob scripts) = @@ -65,7 +65,7 @@ encodeAuxiliaryData opts (Al.AllegraTxAuxData blob scripts) = encodeBlock :: ( Crypto crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> ShelleyBlock (TPraos crypto) (MaryEra crypto) -> Json encodeBlock opts (ShelleyBlock (Ledger.Block blkHeader txs) headerHash) = @@ -107,10 +107,10 @@ encodeTx ( Crypto crypto , era ~ MaryEra crypto ) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Sh.ShelleyTx era -> Json -encodeTx opts x = +encodeTx (fmt, opts) x = encodeObject ( Shelley.encodeTxId (Ledger.txid @(MaryEra crypto) (Sh.body x)) <> @@ -130,7 +130,7 @@ encodeTx opts x = where auxiliary = do hash <- Shelley.encodeAuxiliaryDataHash <$> Ma.mtbAuxDataHash (Sh.body x) - (labels, scripts) <- encodeAuxiliaryData opts <$> Sh.auxiliaryData x + (labels, scripts) <- encodeAuxiliaryData (fmt, opts) <$> Sh.auxiliaryData x pure ( encodeObject ("hash" .= hash <> "labels" .= labels) , scripts diff --git a/server/src/Ogmios/Data/Json/Prelude.hs b/server/src/Ogmios/Data/Json/Prelude.hs index 3b4884b4ec..3c514aea7c 100644 --- a/server/src/Ogmios/Data/Json/Prelude.hs +++ b/server/src/Ogmios/Data/Json/Prelude.hs @@ -13,6 +13,7 @@ module Ogmios.Data.Json.Prelude , ToJSON , ViaEncoding (..) , IncludeCbor (..) + , MetadataFormat (..) , omitOptionalCbor , includeAllCbor , jsonToByteString @@ -206,6 +207,11 @@ import qualified Ogmios.Prelude type Json = Json.Encoding +data MetadataFormat + = MetadataNoSchema + | MetadataDetailedSchema + deriving (Generic, Eq, Show) + data IncludeCbor = IncludeCbor { includeTransactionCbor :: !Bool , includeMetadataCbor :: !Bool diff --git a/server/src/Ogmios/Data/Json/Shelley.hs b/server/src/Ogmios/Data/Json/Shelley.hs index f9aa27f9df..a4d96be921 100644 --- a/server/src/Ogmios/Data/Json/Shelley.hs +++ b/server/src/Ogmios/Data/Json/Shelley.hs @@ -131,7 +131,7 @@ encodeBHeader (TPraos.BHeader hBody _hSig) = encodeBlock :: Crypto crypto - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> ShelleyBlock (TPraos crypto) (ShelleyEra crypto) -> Json encodeBlock opts (ShelleyBlock (Ledger.Block blkHeader txs) headerHash) = @@ -396,7 +396,7 @@ encodeKESPeriod = encodeMetadata :: forall era. (Era era) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Sh.ShelleyTxAuxData era -> Json encodeMetadata opts (Sh.ShelleyTxAuxData blob) = @@ -404,10 +404,10 @@ encodeMetadata opts (Sh.ShelleyTxAuxData blob) = encodeMetadataBlob :: forall era. (Era era) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Map Word64 Sh.Metadatum -> Json -encodeMetadataBlob opts = +encodeMetadataBlob (fmt, opts) = encodeMap show encodeMetadatum where encodeMetadatum :: Sh.Metadatum -> Json @@ -423,7 +423,31 @@ encodeMetadataBlob opts = identity json ) where - json = tryEncodeMetadatumAsJson meta + json = case fmt of + MetadataNoSchema -> + tryEncodeMetadatumAsJson meta + MetadataDetailedSchema -> + SJust (encodeMetadatumAsDetailedSchema meta) + + encodeMetadatumAsDetailedSchema :: Sh.Metadatum -> Json + encodeMetadatumAsDetailedSchema = encodeObject . \case + Sh.I n -> + "int" .= encodeInteger n + Sh.B bytes -> + "bytes" .= encodeByteStringBase16 bytes + Sh.S txt -> + "string" .= encodeText txt + Sh.List xs -> + "list" .= encodeList encodeMetadatumAsDetailedSchema xs + Sh.Map xs -> + "map" .= encodeList encodeKeyPair xs + where + encodeKeyPair :: (Sh.Metadatum, Sh.Metadatum) -> Json + encodeKeyPair (k, v) = + encodeObject + ( "k" .= encodeMetadatumAsDetailedSchema k + <> "v" .= encodeMetadatumAsDetailedSchema v + ) tryEncodeMetadatumAsJson :: Sh.Metadatum -> StrictMaybe Json tryEncodeMetadatumAsJson = \case @@ -803,10 +827,10 @@ encodeStakePoolRelay = encodeObject . \case encodeTx :: forall crypto era. (Crypto crypto, era ~ ShelleyEra crypto) - => IncludeCbor + => (MetadataFormat, IncludeCbor) -> Sh.ShelleyTx era -> Json -encodeTx opts x = +encodeTx (fmt, opts) x = encodeObject ( encodeTxId (Ledger.txid @(ShelleyEra crypto) (Sh.body x)) <> @@ -827,7 +851,7 @@ encodeTx opts x = metadata = liftA2 (\hash body -> encodeObject ("hash" .= hash <> "labels" .= body)) (encodeAuxiliaryDataHash <$> Sh.stbMDHash (Sh.body x)) - (encodeMetadata opts <$> Sh.auxiliaryData x) + (encodeMetadata (fmt, opts) <$> Sh.auxiliaryData x) encodeTxBody :: Crypto crypto diff --git a/server/src/Ogmios/Options.hs b/server/src/Ogmios/Options.hs index 67bb0b0775..da7b66fa86 100644 --- a/server/src/Ogmios/Options.hs +++ b/server/src/Ogmios/Options.hs @@ -76,6 +76,7 @@ import Ogmios.App.Configuration ( Configuration (..) , EpochSlots (..) , IncludeCbor (..) + , MetadataFormat (..) , NetworkMagic (..) , NetworkParameters (..) , SystemStart (..) @@ -166,6 +167,7 @@ parserInfo = info (helper <*> parser) $ mempty <*> includeMetadataCborFlag <*> includeScriptCborFlag )) + <*> metadataFormatFlag ) <*> (tracersOption <|> Tracers <$> fmap Const (logLevelOption "health") @@ -235,11 +237,23 @@ maxInFlightOption = option auto $ mempty <> value 1000 <> showDefault +-- | [--metadata-detailed-schema] +metadataFormatFlag :: Parser MetadataFormat +metadataFormatFlag = fmap toMetadataFormat $ switch $ mempty + <> long "metadata-detailed-schema" + <> help "When set, metadata will be encoded as 'detailed schema'. See for more details about the different metadata formats." + <> showDefault + where + toMetadataFormat = \case + True -> MetadataDetailedSchema + False -> MetadataNoSchema + + -- | [--include-cbor] includeCborFlag :: Parser IncludeCbor includeCborFlag = fmap toIncludeCbor $ switch $ mempty <> long "include-cbor" - <> help "In chain-synchronization, always include a 'cbor' field for transaction, metadata and scripts that contain the original binary serialized representation of each object." + <> help "Shorthand for: --include-transaction-cbor --include-metadata-cbor --include-script-cbor" <> showDefault where toIncludeCbor = \case @@ -250,21 +264,21 @@ includeCborFlag = fmap toIncludeCbor $ switch $ mempty includeTransactionCborFlag :: Parser Bool includeTransactionCborFlag = switch $ mempty <> long "include-transaction-cbor" - <> help "In chain-synchronization, always include a 'cbor' field for all transactions that contain the original binary serialized representation of that transaction" + <> help "Always include a 'cbor' field for all transactions that contain the original binary serialized representation of that transaction in the server's responses." <> showDefault -- | [--include-metadata-cbor] includeMetadataCborFlag :: Parser Bool includeMetadataCborFlag = switch $ mempty <> long "include-metadata-cbor" - <> help "In chain-synchronization, always include a 'cbor' field for all metadata containing the original binary serialized representation of that metadata. Otherwise, the field is only present when the metadata can't be safely represented as JSON." + <> help "Always include a 'cbor' field for all metadata containing the original binary serialized representation of that metadata in the server's responses. Otherwise, the field is only present when the metadata can't be safely represented as JSON." <> showDefault -- | [--include-script-cbor] includeScriptCborFlag :: Parser Bool includeScriptCborFlag = switch $ mempty <> long "include-script-cbor" - <> help "In chain-synchronization, always include a 'cbor' field for all phase-1 native scripts that contain the original binary serialized representation of that script." + <> help "Always include a 'cbor' field for all phase-1 native scripts that contain the original binary serialized representation of that script in the server's responses." <> showDefault -- | [--log-level=SEVERITY] diff --git a/server/test/unit/Ogmios/App/Protocol/ChainSyncSpec.hs b/server/test/unit/Ogmios/App/Protocol/ChainSyncSpec.hs index 9cf6069d3b..d6d88e8845 100644 --- a/server/test/unit/Ogmios/App/Protocol/ChainSyncSpec.hs +++ b/server/test/unit/Ogmios/App/Protocol/ChainSyncSpec.hs @@ -29,6 +29,7 @@ import Network.TypedProtocol.Codec ) import Ogmios.App.Configuration ( EpochSlots (..) + , MetadataFormat (..) , omitOptionalCbor ) import Ogmios.App.Protocol.ChainSync @@ -209,7 +210,7 @@ withChainSyncClient -> m a withChainSyncClient action seed = do (recvQ, sendQ) <- atomically $ (,) <$> newTQueue <*> newTQueue - let innerCodecs = mkChainSyncCodecs (encodeBlock omitOptionalCbor) encodePoint encodeTip + let innerCodecs = mkChainSyncCodecs (encodeBlock (MetadataNoSchema, omitOptionalCbor)) encodePoint encodeTip let client = mkChainSyncClient maxInFlight innerCodecs recvQ (atomically . writeTQueue sendQ) let codec = codecs defaultSlotsPerEpoch nodeToClientV_Latest & cChainSyncCodec withMockChannel (chainSyncMockPeer seed codec) $ \channel -> do diff --git a/server/test/unit/Ogmios/App/Protocol/TxMonitorSpec.hs b/server/test/unit/Ogmios/App/Protocol/TxMonitorSpec.hs index 1faaaab013..94b6f7eaba 100644 --- a/server/test/unit/Ogmios/App/Protocol/TxMonitorSpec.hs +++ b/server/test/unit/Ogmios/App/Protocol/TxMonitorSpec.hs @@ -66,7 +66,8 @@ import Ogmios.Data.Json import Ogmios.Data.Json.Orphans () import Ogmios.Data.Json.Prelude - ( at + ( MetadataFormat (..) + , at ) import Ogmios.Data.Protocol.TxMonitor ( AcquireMempool (..) @@ -208,7 +209,7 @@ withTxMonitorClient -> m a withTxMonitorClient action seed = do (recvQ, sendQ) <- atomically $ (,) <$> newTQueue <*> newTQueue - let innerCodecs = mkTxMonitorCodecs encodeTxId (encodeTx omitOptionalCbor) + let innerCodecs = mkTxMonitorCodecs encodeTxId (encodeTx (MetadataNoSchema, omitOptionalCbor)) let client = mkTxMonitorClient innerCodecs recvQ (atomically . writeTQueue sendQ) let codec = codecs defaultSlotsPerEpoch nodeToClientV_Latest & cTxMonitorCodec withMockChannel (txMonitorMockPeer seed codec) $ \channel -> do diff --git a/server/test/unit/Ogmios/Data/JsonSpec.hs b/server/test/unit/Ogmios/Data/JsonSpec.hs index 81fb4323a0..9345a82f8f 100644 --- a/server/test/unit/Ogmios/Data/JsonSpec.hs +++ b/server/test/unit/Ogmios/Data/JsonSpec.hs @@ -69,7 +69,8 @@ import Ogmios.Data.Json import Ogmios.Data.Json.Orphans () import Ogmios.Data.Json.Prelude - ( encodeSlotLength + ( MetadataFormat (..) + , encodeSlotLength , omitOptionalCbor ) import Ogmios.Data.Json.Query @@ -446,7 +447,7 @@ spec = do validateToJSON (arbitrary @(Rpc.Response (NextBlockResponse Block))) - (_encodeNextBlockResponse (encodeBlock omitOptionalCbor) encodePoint encodeTip) + (_encodeNextBlockResponse (encodeBlock (MetadataNoSchema, omitOptionalCbor)) encodePoint encodeTip) (50, "NextBlockResponse") "ogmios.json#/properties/NextBlockResponse" @@ -497,7 +498,13 @@ spec = do validateToJSON (arbitrary @(Rpc.Response (NextTransactionResponse Block))) - (_encodeNextTransactionResponse encodeTxId (encodeTx omitOptionalCbor)) + (_encodeNextTransactionResponse encodeTxId (encodeTx (MetadataNoSchema, omitOptionalCbor))) + (10, "NextTransactionResponse") + "ogmios.json#/properties/NextTransactionResponse" + + validateToJSON + (arbitrary @(Rpc.Response (NextTransactionResponse Block))) + (_encodeNextTransactionResponse encodeTxId (encodeTx (MetadataDetailedSchema, omitOptionalCbor))) (10, "NextTransactionResponse") "ogmios.json#/properties/NextTransactionResponse" diff --git a/server/test/vectors/NextTransactionResponse/009.json b/server/test/vectors/NextTransactionResponse/009.json index 69070c1bac..d0c383a6e7 100644 --- a/server/test/vectors/NextTransactionResponse/009.json +++ b/server/test/vectors/NextTransactionResponse/009.json @@ -1 +1 @@ -{"jsonrpc":"2.0","method":"nextTransaction","result":{"transaction":{"id":"a74c101f9882b90890670edb9192ca11b8c11bcd4f2cddcbf4aeb99417ede24b","spends":"collaterals","inputs":[{"transaction":{"id":"bc1e3bdf8823035ad702689c0ee15fa125efc095c72657f7ea4763f8b1637e78"},"index":0},{"transaction":{"id":"bdebac33fe7d5c2d4e944a5dc61a5407df5e4c2b711ecab7586390bbcefbf70e"},"index":16}],"outputs":[{"address":"addr1zyq0dd6rrprwgkse73llm8efgdh2n5etgtfdguxgnsv85z2pls73tphnu4gztaman89ysfy5sawt5nra37tyq8qhzl4st0c6xd","value":{"ada":{"lovelace":6047762285524111773},"1a02f4ce28c0a6aa69e7ce6261d48417050ea6bf3018c046bd5efe22":{"7c7bef2e7274a0538a3c4ed744f3fe8cde6b":1922001680567756769}}},{"address":"addr_test1wzsrpv34qjaa5c66g2fu00exf9s9fy8jc3d93zyy45zhrpsudxc2m","value":{"ada":{"lovelace":8350204260369575132},"7b15c067adb30e229b332f1bed441ad4c22edc4deae6a8afd6d3ee5e":{"f46a561c49d54df948e97caf609f419c43":8836530895022711182}}}],"withdrawals":{"stake_test17qenvntldry9sqyc87n2t5xa6x34z932m2jnm8ttqfs9l5cxv2udw":{"ada":{"lovelace":694745}},"stake_test1upcnny2qsfg7nzdtsn9akmd8vq0adplqt74ppdnsj8n4lqsterwta":{"ada":{"lovelace":134991}},"stake_test1uzfrmxls7ud29jyk6f45qpnpwgzdwzlrdqkuqnjk3ut02ycp8k0t5":{"ada":{"lovelace":746569}}},"mint":{"8f461954fe2f18fee1dca233f358907e643ff839ed1f995e4bf325e3":{"37":1365414550950093562}},"requiredExtraScripts":["2c7bf24a0551da33cee8dc161d8f615bb7a0ff45b59018784cd24106","3566e65e2117f7f858df598511400f461cc9b539431a6d0995bd54f0","4d10ed3f34e030491787b3e4cfcb2bda0a3a3aec7146971ca9e24949","73a1349db4afb3a4445e30b38f69c885d5220b195a6c0b46f7d2ced9","ffa4feb39434a4482c56ddcf1dc293d1a513fece976321ee6e15135f"],"network":"testnet","scriptIntegrityHash":"59c12388049d2f27e8377c5011312563945475c1f15150c8a1709f024e643a93","fee":{"ada":{"lovelace":39071}},"validityInterval":{"invalidBefore":4},"proposals":[{"action":{"type":"treasuryWithdrawals","withdrawals":{}}}],"metadata":{"hash":"39d33a4178e2fbe73dbea34203dbe815635b36ebdfb98db847e702cb4aa96642","labels":{"9":{"json":-5},"15":{"json":{"":-2,"-4":""}}}},"signatories":[{"key":"3355cd9ea5146a39b55dd2c941b4172df5f0c8c6ec706ebd00cc421214e5e7c4","signature":"f835b2a1bf4371a5baa63082692f0aa8a96351da1d4deabb622efa901944150278b66849080b1b16368218814f584de1c46895d2fd871318f9c3a2cdd1f47d49"},{"key":"ccdb0a60ef0578a28ac6d6940ac2d68ccc13c7f199ddb9b9398a288fbaeb67e6","signature":"4ba96287e8943265074eeb1bac63c1162b2ec235eb845f78b30e7c8ed25dd37dc4d06020cfdc3b17366d8f60b804043018df0076515753ece106ab829628d76d","chainCode":"26","addressAttributes":"6b"}],"scripts":{"2c7bf24a0551da33cee8dc161d8f615bb7a0ff45b59018784cd24106":{"language":"native","json":{"clause":"all","from":[{"clause":"any","from":[]},{"clause":"any","from":[{"clause":"all","from":[{"clause":"signature","from":"bd039f956f4b302f3ab6fc7c4bac3350a540f44af81a8492194dd2c2"},{"clause":"signature","from":"b16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5"},{"clause":"signature","from":"a646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a"}]}]},{"clause":"some","atLeast":5,"from":[{"clause":"before","slot":0},{"clause":"signature","from":"0d94e174732ef9aae73f395ab44507bfa983d65023c11a951f0c32e4"},{"clause":"before","slot":8},{"clause":"any","from":[{"clause":"signature","from":"58e1b65718531b42494610c506cef10ff031fa817a8ff75c0ab180e7"},{"clause":"signature","from":"b16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5"},{"clause":"signature","from":"a646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a"},{"clause":"signature","from":"b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54"},{"clause":"signature","from":"bd039f956f4b302f3ab6fc7c4bac3350a540f44af81a8492194dd2c2"}]},{"clause":"before","slot":10}]},{"clause":"signature","from":"a646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a"}]}},"3566e65e2117f7f858df598511400f461cc9b539431a6d0995bd54f0":{"language":"native","json":{"clause":"before","slot":4}},"4d10ed3f34e030491787b3e4cfcb2bda0a3a3aec7146971ca9e24949":{"language":"native","json":{"clause":"before","slot":14}},"62d38461b1c576888e6f9a0f4bdb658404bb24418617cbd1208c4b6e":{"language":"native","json":{"clause":"signature","from":"65fc709a5e019b8aba76f6977c1c8770e4b36fa76f434efc588747b7"}},"73a1349db4afb3a4445e30b38f69c885d5220b195a6c0b46f7d2ced9":{"language":"native","json":{"clause":"any","from":[{"clause":"before","slot":0},{"clause":"signature","from":"b16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5"}]}},"ffa4feb39434a4482c56ddcf1dc293d1a513fece976321ee6e15135f":{"language":"native","json":{"clause":"some","atLeast":2,"from":[{"clause":"any","from":[{"clause":"all","from":[{"clause":"signature","from":"b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54"},{"clause":"signature","from":"e0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b082541"},{"clause":"signature","from":"b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54"},{"clause":"signature","from":"58e1b65718531b42494610c506cef10ff031fa817a8ff75c0ab180e7"}]}]},{"clause":"before","slot":11}]}}},"datums":{"f63498b4ae65be466e4a71878971b9c524458996450b0ff8262cddf3f0d99229":"24"},"redeemers":[{"validator":{"index":2,"purpose":"spend"},"redeemer":"d87d9f05d87c9fd8799f01ff8001a52144cbc4988e0005404402e9bfd7419a202244632b9f06ffd87e9f04ffff","executionUnits":{"memory":2631544229333854821,"cpu":6463510031116116813}},{"validator":{"index":16,"purpose":"mint"},"redeemer":"22","executionUnits":{"memory":4665708758421570269,"cpu":398919171589932735}},{"validator":{"index":7,"purpose":"publish"},"redeemer":"42b3f5","executionUnits":{"memory":1950311835155922143,"cpu":6493736883786868403}}]}},"id":null} \ No newline at end of file +{"jsonrpc":"2.0","method":"nextTransaction","result":{"transaction":{"id":"a74c101f9882b90890670edb9192ca11b8c11bcd4f2cddcbf4aeb99417ede24b","spends":"collaterals","inputs":[{"transaction":{"id":"bc1e3bdf8823035ad702689c0ee15fa125efc095c72657f7ea4763f8b1637e78"},"index":0},{"transaction":{"id":"bdebac33fe7d5c2d4e944a5dc61a5407df5e4c2b711ecab7586390bbcefbf70e"},"index":16}],"outputs":[{"address":"addr1zyq0dd6rrprwgkse73llm8efgdh2n5etgtfdguxgnsv85z2pls73tphnu4gztaman89ysfy5sawt5nra37tyq8qhzl4st0c6xd","value":{"ada":{"lovelace":6047762285524111773},"1a02f4ce28c0a6aa69e7ce6261d48417050ea6bf3018c046bd5efe22":{"7c7bef2e7274a0538a3c4ed744f3fe8cde6b":1922001680567756769}}},{"address":"addr_test1wzsrpv34qjaa5c66g2fu00exf9s9fy8jc3d93zyy45zhrpsudxc2m","value":{"ada":{"lovelace":8350204260369575132},"7b15c067adb30e229b332f1bed441ad4c22edc4deae6a8afd6d3ee5e":{"f46a561c49d54df948e97caf609f419c43":8836530895022711182}}}],"withdrawals":{"stake_test17qenvntldry9sqyc87n2t5xa6x34z932m2jnm8ttqfs9l5cxv2udw":{"ada":{"lovelace":694745}},"stake_test1upcnny2qsfg7nzdtsn9akmd8vq0adplqt74ppdnsj8n4lqsterwta":{"ada":{"lovelace":134991}},"stake_test1uzfrmxls7ud29jyk6f45qpnpwgzdwzlrdqkuqnjk3ut02ycp8k0t5":{"ada":{"lovelace":746569}}},"mint":{"8f461954fe2f18fee1dca233f358907e643ff839ed1f995e4bf325e3":{"37":1365414550950093562}},"requiredExtraScripts":["2c7bf24a0551da33cee8dc161d8f615bb7a0ff45b59018784cd24106","3566e65e2117f7f858df598511400f461cc9b539431a6d0995bd54f0","4d10ed3f34e030491787b3e4cfcb2bda0a3a3aec7146971ca9e24949","73a1349db4afb3a4445e30b38f69c885d5220b195a6c0b46f7d2ced9","ffa4feb39434a4482c56ddcf1dc293d1a513fece976321ee6e15135f"],"network":"testnet","scriptIntegrityHash":"59c12388049d2f27e8377c5011312563945475c1f15150c8a1709f024e643a93","fee":{"ada":{"lovelace":39071}},"validityInterval":{"invalidBefore":4},"proposals":[{"action":{"type":"treasuryWithdrawals","withdrawals":{}}}],"metadata":{"hash":"39d33a4178e2fbe73dbea34203dbe815635b36ebdfb98db847e702cb4aa96642","labels":{"9":{"json":{"int":-5}},"15":{"json":{"map":[{"k":{"map":[{"k":{"int":2},"v":{"int":-5}}]},"v":{"bytes":"01d340"}},{"k":{"string":""},"v":{"int":-2}},{"k":{"int":-4},"v":{"string":""}}]}}}},"signatories":[{"key":"3355cd9ea5146a39b55dd2c941b4172df5f0c8c6ec706ebd00cc421214e5e7c4","signature":"f835b2a1bf4371a5baa63082692f0aa8a96351da1d4deabb622efa901944150278b66849080b1b16368218814f584de1c46895d2fd871318f9c3a2cdd1f47d49"},{"key":"ccdb0a60ef0578a28ac6d6940ac2d68ccc13c7f199ddb9b9398a288fbaeb67e6","signature":"4ba96287e8943265074eeb1bac63c1162b2ec235eb845f78b30e7c8ed25dd37dc4d06020cfdc3b17366d8f60b804043018df0076515753ece106ab829628d76d","chainCode":"26","addressAttributes":"6b"}],"scripts":{"2c7bf24a0551da33cee8dc161d8f615bb7a0ff45b59018784cd24106":{"language":"native","json":{"clause":"all","from":[{"clause":"any","from":[]},{"clause":"any","from":[{"clause":"all","from":[{"clause":"signature","from":"bd039f956f4b302f3ab6fc7c4bac3350a540f44af81a8492194dd2c2"},{"clause":"signature","from":"b16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5"},{"clause":"signature","from":"a646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a"}]}]},{"clause":"some","atLeast":5,"from":[{"clause":"before","slot":0},{"clause":"signature","from":"0d94e174732ef9aae73f395ab44507bfa983d65023c11a951f0c32e4"},{"clause":"before","slot":8},{"clause":"any","from":[{"clause":"signature","from":"58e1b65718531b42494610c506cef10ff031fa817a8ff75c0ab180e7"},{"clause":"signature","from":"b16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5"},{"clause":"signature","from":"a646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a"},{"clause":"signature","from":"b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54"},{"clause":"signature","from":"bd039f956f4b302f3ab6fc7c4bac3350a540f44af81a8492194dd2c2"}]},{"clause":"before","slot":10}]},{"clause":"signature","from":"a646474b8f5431261506b6c273d307c7569a4eb6c96b42dd4a29520a"}]}},"3566e65e2117f7f858df598511400f461cc9b539431a6d0995bd54f0":{"language":"native","json":{"clause":"before","slot":4}},"4d10ed3f34e030491787b3e4cfcb2bda0a3a3aec7146971ca9e24949":{"language":"native","json":{"clause":"before","slot":14}},"62d38461b1c576888e6f9a0f4bdb658404bb24418617cbd1208c4b6e":{"language":"native","json":{"clause":"signature","from":"65fc709a5e019b8aba76f6977c1c8770e4b36fa76f434efc588747b7"}},"73a1349db4afb3a4445e30b38f69c885d5220b195a6c0b46f7d2ced9":{"language":"native","json":{"clause":"any","from":[{"clause":"before","slot":0},{"clause":"signature","from":"b16b56f5ec064be6ac3cab6035efae86b366cc3dc4a0d571603d70e5"}]}},"ffa4feb39434a4482c56ddcf1dc293d1a513fece976321ee6e15135f":{"language":"native","json":{"clause":"some","atLeast":2,"from":[{"clause":"any","from":[{"clause":"all","from":[{"clause":"signature","from":"b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54"},{"clause":"signature","from":"e0a714319812c3f773ba04ec5d6b3ffcd5aad85006805b047b082541"},{"clause":"signature","from":"b5ae663aaea8e500157bdf4baafd6f5ba0ce5759f7cd4101fc132f54"},{"clause":"signature","from":"58e1b65718531b42494610c506cef10ff031fa817a8ff75c0ab180e7"}]}]},{"clause":"before","slot":11}]}}},"datums":{"f63498b4ae65be466e4a71878971b9c524458996450b0ff8262cddf3f0d99229":"24"},"redeemers":[{"validator":{"index":2,"purpose":"spend"},"redeemer":"d87d9f05d87c9fd8799f01ff8001a52144cbc4988e0005404402e9bfd7419a202244632b9f06ffd87e9f04ffff","executionUnits":{"memory":2631544229333854821,"cpu":6463510031116116813}},{"validator":{"index":16,"purpose":"mint"},"redeemer":"22","executionUnits":{"memory":4665708758421570269,"cpu":398919171589932735}},{"validator":{"index":7,"purpose":"publish"},"redeemer":"42b3f5","executionUnits":{"memory":1950311835155922143,"cpu":6493736883786868403}}]}},"id":null} \ No newline at end of file