From 5c89f252e72e49ec1039c25e5e3cbba030572815 Mon Sep 17 00:00:00 2001 From: KtorZ Date: Sun, 28 Apr 2024 10:22:02 +0200 Subject: [PATCH] Implement new ledger state query: constitutionalCommittee This 'required' a small breaking change in one of the new Conway certificate, to better reflect on the relationship there is between those certificates and the new query. --- CHANGELOG.md | 48 ++++++ README.md | 1 + .../TypeScript/packages/schema/src/index.ts | 90 ++++++++++-- .../mini-protocols/local-state-query.md | 65 +++++++++ docs/static/api/specification.yaml | 31 +++- docs/static/cardano.json | 138 ++++++++++++++++-- docs/static/ogmios.json | 82 ++++++++++- server/src/Ogmios/Data/Json/Conway.hs | 90 +++++++++++- server/src/Ogmios/Data/Json/Query.hs | 30 +++- .../Data/Ledger/PredicateFailure/Alonzo.hs | 10 +- .../Data/Ledger/PredicateFailure/Conway.hs | 6 +- server/test/unit/Ogmios/Data/JsonSpec.hs | 12 ++ server/test/unit/Test/Generators.hs | 63 ++++++++ 13 files changed, 627 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 915d2c6bd..323f03039 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,16 +11,58 @@ pre: "5. " #### Added - Integrated with `cardano-node==8.10.1-pre`. + +- A new ledger state query [`queryLedgerState/constitutionalCommittee`](https://ogmios.dev/api/#operation-publish-/?QueryLedgerStateConstitutionalCommittee). + - A new transaction submission error: [ConflictingInputsAndReferences](https://ogmios.dev/mini-protocols/local-tx-submission#schema-3164/ConflictingInputsAndReferences) (`code=3164`). #### Changed +- > [!WARNING] + > Adjusted the schema of constitutional committee certificates in order to harmonize responses between certificates and the new `constitutionalCommittee` ledger query. + > + > + > + > + > + > + > + >
beforeafter
+ > ```json + > { + > "type": "constitutionalCommitteeHotKeyRegistration", + > "member": { + > "id": "0000", + > }, + > "hotKey": "0000" + > } + > ``` + > + > ```json + > { + > "type": "constitutionalCommitteeDelegation", + > "member": { + > "id": "0000", + > }, + > "delegate": { + > "status": "authorized", + > "id": "000" + > } + > } + > ``` + >
+ - Fixed integer overflow happening when encoding relative time bounds in era summary, causing times to be shown as negative values. +- Fixed parsing of the `constitution` ledger query which now resolves properly. + #### Removed - N/A +--- +--- + ### [6.2.0] - 2024-03-22 #### Added @@ -37,6 +79,9 @@ pre: "5. " - N/A +--- +--- + ### [6.1.0] - 2024-02-21 #### Added @@ -65,6 +110,9 @@ pre: "5. " - `InternalLedgerTypeConversionError` which can no longer occur. +--- +--- + ### [6.0.3] - 2024-02-02 #### Added diff --git a/README.md b/README.md index 5b7d1f75f..1a21efb75 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ See our [Ogmios client starter kit](https://github.com/CardanoSolutions/ogmios-t queryLedgerState | Information --- | --- `constitution` | The current on-chain constitution. + `constitutionalCommittee` | A complete summary of the constitutional committee. `epoch` | The current epoch of the ledger. `eraStart` | The information regarding the beginning of the current ledger era. `eraSummaries` | Era bounds and slot parameters details, required for proper slotting arithmetic. diff --git a/clients/TypeScript/packages/schema/src/index.ts b/clients/TypeScript/packages/schema/src/index.ts index f04d24576..1e6052a21 100644 --- a/clients/TypeScript/packages/schema/src/index.ts +++ b/clients/TypeScript/packages/schema/src/index.ts @@ -54,7 +54,7 @@ export type Certificate = | StakePoolRegistration | StakePoolRetirement | GenesisDelegation - | ConstitutionalCommitteeHotKeyRegistration + | ConstitutionalCommitteeDelegation | ConstitutionalCommitteeRetirement | DelegateRepresentativeRegistration | DelegateRepresentativeUpdate @@ -84,6 +84,18 @@ export type Relay = RelayByAddress | RelayByName; * An epoch number or length. */ export type Epoch = number; +export type ConstitutionalCommitteeDelegate = + | { + status: "authorized"; + id: DigestBlake2B224; + } + | { + status: "resigned"; + metadata?: Anchor; + } + | { + status: "none"; + }; export type None = null; /** * A network target, as defined since the Shelley era. @@ -298,6 +310,12 @@ export interface Ogmios { | QueryLedgerStateEraMismatch | QueryLedgerStateUnavailableInCurrentEra | QueryLedgerStateAcquiredExpired; + QueryLedgerStateConstitutionalCommittee: QueryLedgerStateConstitutionalCommittee; + QueryLedgerStateConstitutionalCommitteeResponse: + | QueryLedgerStateConstitutionalCommitteeResponse + | QueryLedgerStateEraMismatch + | QueryLedgerStateUnavailableInCurrentEra + | QueryLedgerStateAcquiredExpired; QueryLedgerStateEpoch: QueryLedgerStateEpoch; QueryLedgerStateEpochResponse: | QueryLedgerStateEpochResponse @@ -705,14 +723,14 @@ export interface GenesisDelegate { vrfVerificationKeyHash: DigestBlake2B256; } /** - * A constitutional committee member registers a hot key for voting on-chain. Constitutional committee members do not vote with their cold key directly. New registrations supersedes any preceding ones. + * A constitutional committee member delegates a hot credential for voting on-chain. Constitutional committee members do not vote with their cold key directly. New registrations supersedes any preceding ones. */ -export interface ConstitutionalCommitteeHotKeyRegistration { - type: "constitutionalCommitteeHotKeyRegistration"; +export interface ConstitutionalCommitteeDelegation { + type: "constitutionalCommitteeDelegation"; member: { id: DigestBlake2B224; }; - hotKey: DigestBlake2B224; + delegate: ConstitutionalCommitteeDelegate; } /** * A constitutional committee member resigns from the committee. @@ -902,18 +920,19 @@ export interface ValueDelta { export interface GovernanceActionConstitutionalCommittee { type: "constitutionalCommittee"; members: { - added: ConstitutionalCommitteeMember[]; + added: ConstitutionalCommitteeMemberSummary[]; removed: { id: DigestBlake2B224; }[]; }; quorum: Ratio; } -export interface ConstitutionalCommitteeMember { +export interface ConstitutionalCommitteeMemberSummary { id: DigestBlake2B224; - mandate: { - epoch: Epoch; - }; + mandate?: Mandate; +} +export interface Mandate { + epoch: Epoch; } /** * A change in the constitution. Only its hash is recorded on-chain. @@ -2122,6 +2141,8 @@ export interface ReleaseLedgerStateResponse { export interface QueryLedgerStateEraMismatch { jsonrpc: "2.0"; method: + | "queryLedgerState/constitution" + | "queryLedgerState/constitutionalCommittee" | "queryLedgerState/epoch" | "queryLedgerState/eraStart" | "queryLedgerState/eraSummaries" @@ -2147,6 +2168,8 @@ export interface QueryLedgerStateEraMismatch { export interface QueryLedgerStateUnavailableInCurrentEra { jsonrpc: "2.0"; method: + | "queryLedgerState/constitution" + | "queryLedgerState/constitutionalCommittee" | "queryLedgerState/epoch" | "queryLedgerState/eraStart" | "queryLedgerState/eraSummaries" @@ -2171,6 +2194,8 @@ export interface QueryLedgerStateUnavailableInCurrentEra { export interface QueryLedgerStateAcquiredExpired { jsonrpc: "2.0"; method: + | "queryLedgerState/constitution" + | "queryLedgerState/constitutionalCommittee" | "queryLedgerState/epoch" | "queryLedgerState/eraStart" | "queryLedgerState/eraSummaries" @@ -2216,6 +2241,49 @@ export interface Constitution { }; metadata: Anchor; } +/** + * Get the state of the constitutional committee (only available from Conway onwards). + */ +export interface QueryLedgerStateConstitutionalCommittee { + jsonrpc: "2.0"; + method: "queryLedgerState/constitutionalCommittee"; + id?: unknown; +} +export interface QueryLedgerStateConstitutionalCommitteeResponse { + jsonrpc: "2.0"; + method: "queryLedgerState/constitutionalCommittee"; + result: null | { + members: ConstitutionalCommitteeMember[]; + quorum: null | Ratio; + }; + id?: unknown; +} +/** + * A constitutional committee member as seen in the context of a specific epoch. Statuses and next states are to be seen from this specific epoch. The field 'next', when present, refers to any change happening to this member in the following epoch. + */ +export interface ConstitutionalCommitteeMember { + id: DigestBlake2B224; + /** + * A member status. 'active' indicates that this member vote will count during the ratification of the ongoing epoch. 'unrecognized' means that some hot credential currently points to a non-existing (or no longer existing) member. + */ + status: "active" | "expired" | "unrecognized"; + delegate: ConstitutionalCommitteeDelegate; + mandate?: Mandate; + next?: + | { + change: "toBeEnacted"; + } + | { + change: "toBeRemoved"; + } + | { + change: "expiring"; + } + | { + change: "adjustingMandate"; + mandate: Mandate; + }; +} /** * Query the current epoch number the ledger is at. */ @@ -2660,7 +2728,7 @@ export interface GenesisConway { era: "conway"; constitution: Constitution; constitutionalCommittee: { - members: ConstitutionalCommitteeMember[]; + members: ConstitutionalCommitteeMemberSummary[]; quorum: Ratio; }; updatableParameters: { diff --git a/docs/content/mini-protocols/local-state-query.md b/docs/content/mini-protocols/local-state-query.md index 20f68f21b..67f1a13b6 100644 --- a/docs/content/mini-protocols/local-state-query.md +++ b/docs/content/mini-protocols/local-state-query.md @@ -101,6 +101,7 @@ queryNetwork | Information queryLedgerState | Information --- | --- `constitution` | The on-chain constitution. +`constitutionalCommittee` | A complete summary of the constitutional committee. `epoch` | The current epoch of the ledger. `eraStart` | The information regarding the beginning of the current ledger era. `eraSummaries` | Era bounds and slot parameters details, required for proper slotting arithmetic. @@ -333,6 +334,70 @@ Be aware that it is possible for an acquire request to fail even if (and in part ### Network +#### constitution + +```json +{ + "jsonrpc": "2.0", + "method": "queryLedgerState/constitution", + "result": { + "metadata": { + "url": "", + "hash": "0000000000000000000000000000000000000000000000000000000000000000" + }, + "guardrails": null + }, + "id": null +} +``` + +#### constitutionalCommittee + +```json +{ + "jsonrpc": "2.0", + "method": "queryLedgerState/constitutionalCommittee", + "result": { + "members": [ + { + "id": "5f1b4429fe3bda963a7b70ab81135112a785afcf55ccd695b122e794", + "delegate": { + "status": "authorized", + "credential": "5aa349227e4068c85c03400396bcea13c7fd57d0ec78c604bc768fc5" + }, + "status": "active", + "mandate": { + "epoch": 379 + } + }, + { + "id": "9393c87a66b1f7dd4f9b486a49232de92e39e18b3b20ac4a539b4df2", + "delegate": { + "status": "authorized", + "credential": "670994283668cea40218e0ef33c51aff39ca00a74f68ed428cf305ce" + }, + "status": "active", + "mandate": { + "epoch": 379 + } + }, + { + "id": "b7bfc26ddc6718133a204af6872149b69de83dd3350f60b257e55773", + "delegate": { + "status": "none" + }, + "status": "active", + "mandate": { + "epoch": 379 + } + } + ], + "quorum": "2/3" + }, + "id": null +} +``` + #### blockHeight ```json diff --git a/docs/static/api/specification.yaml b/docs/static/api/specification.yaml index 4e55c4ab8..7c1a6eeb3 100644 --- a/docs/static/api/specification.yaml +++ b/docs/static/api/specification.yaml @@ -1,7 +1,7 @@ asyncapi: '2.4.0' info: title: Ogmios - version: '6.1.*' + version: '6.2.*' description: | ### Protocols @@ -18,6 +18,7 @@ info: - [Ledger state queries](#operation-publish-/?AcquireLedgerState) - [Error codes](/mini-protocols/local-state-query/#errors) - [LedgerState/Constitution](#operation-publish-/?QueryLedgerStateConstitution) + - [LedgerState/ConstitutionalCommittee](#operation-publish-/?QueryLedgerStateConstitutionalCommittee) - [LedgerState/Epoch](#operation-publish-/?QueryLedgerStateEpoch) - [LedgerState/EraStart](#operation-publish-/?QueryLedgerStateEraStart) - [LedgerState/EraSummaries](#operation-publish-/?QueryLedgerStateEraSummaries) @@ -253,6 +254,20 @@ channels: oneOf: - $ref: "#/components/messages/QueryLedgerStateConstitutionResponse" + /?QueryLedgerStateConstitutionalCommittee: + description: | +

Top ⬆️

+ publish: + operationId: QueryLedgerStateConstitutionalCommittee + message: + oneOf: + - $ref: "#/components/messages/QueryLedgerStateConstitutionalCommittee" + subscribe: + operationId: QueryLedgerStateConstitutionalCommitteeResponse + message: + oneOf: + - $ref: "#/components/messages/QueryLedgerStateConstitutionalCommitteeResponse" + /?QueryLedgerStateEpoch: description: |

Top ⬆️

@@ -737,6 +752,14 @@ components: payload: $ref: "/ogmios.json#/properties/QueryLedgerStateConstitution" + QueryLedgerStateConstitutionalCommittee: + title: QueryLedgerStateConstitutionalCommittee + name: "since v6.2.0" + description: | + Query the current state of the constitutional committee (only available from Conway onwards) + payload: + $ref: "/ogmios.json#/properties/QueryLedgerStateConstitutionalCommittee" + QueryNetworkBlockHeight: title: QueryNetworkBlockHeight name: "since v6.0.0" @@ -847,6 +870,12 @@ components: payload: $ref: "/ogmios.json#/properties/QueryLedgerStateConstitutionResponse" + QueryLedgerStateConstitutionalCommitteeResponse: + title: QueryLedgerStateConstitutionalCommitteeResponse + name: "since v6.2.0" + payload: + $ref: "/ogmios.json#/properties/QueryLedgerStateConstitutionalCommitteeResponse" + QueryNetworkBlockHeightResponse: title: QueryNetworkBlockHeightResponse name: "since v6.0.0" diff --git a/docs/static/cardano.json b/docs/static/cardano.json index 7dc310b0b..e3711ea84 100644 --- a/docs/static/cardano.json +++ b/docs/static/cardano.json @@ -467,14 +467,14 @@ } } , { "type": "object" - , "title": "ConstitutionalCommitteeHotKeyRegistration" - , "description": "A constitutional committee member registers a hot key for voting on-chain. Constitutional committee members do not vote with their cold key directly. New registrations supersedes any preceding ones." + , "title": "ConstitutionalCommitteeDelegation" + , "description": "A constitutional committee member delegates a hot credential for voting on-chain. Constitutional committee members do not vote with their cold key directly. New registrations supersedes any preceding ones." , "additionalProperties": false - , "required": [ "type", "member", "hotKey"] + , "required": [ "type", "member", "delegate"] , "properties": { "type": { "type": "string" - , "enum": [ "constitutionalCommitteeHotKeyRegistration" ] + , "enum": [ "constitutionalCommitteeDelegation" ] } , "member": { "type": "object" @@ -484,8 +484,8 @@ { "id": { "$ref": "cardano.json#/definitions/Digest" } } } - , "hotKey": - { "$ref": "cardano.json#/definitions/Digest" + , "delegate": + { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeDelegate" } } } @@ -620,19 +620,123 @@ , "ConstitutionalCommitteeMember": { "title": "ConstitutionalCommitteeMember" + , "description": "A constitutional committee member as seen in the context of a specific epoch. Statuses and next states are to be seen from this specific epoch. The field 'next', when present, refers to any change happening to this member in the following epoch." , "type": "object" , "additionalProperties": false - , "required": [ "id", "mandate" ] + , "required": [ "id", "delegate", "status" ] , "properties": { "id": { "$ref": "cardano.json#/definitions/Digest" } + , "status": + { "type": "string" + , "description": "A member status. 'active' indicates that this member vote will count during the ratification of the ongoing epoch. 'unrecognized' means that some hot credential currently points to a non-existing (or no longer existing) member." + , "enum": [ "active", "expired", "unrecognized" ] + } + , "delegate": + { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeDelegate" + } , "mandate": - { "type": "object" + { "$ref": "cardano.json#/definitions/Mandate" + } + , "next": + { "oneOf": + [ { "type": "object" + , "additionalProperties": false + , "required": [ "change" ] + , "properties": + { "change": + { "type": "string" + , "enum": [ "toBeEnacted" ] + } + } + } + , { "type": "object" + , "additionalProperties": false + , "required": [ "change" ] + , "properties": + { "change": + { "type": "string" + , "enum": [ "toBeRemoved" ] + } + } + } + , { "type": "object" + , "additionalProperties": false + , "required": [ "change" ] + , "properties": + { "change": + { "type": "string" + , "enum": [ "expiring"] + } + } + } + , { "type": "object" + , "additionalProperties": false + , "required": [ "change", "mandate" ] + , "properties": + { "change": + { "type": "string" + , "enum": [ "adjustingMandate" ] + } + , "mandate": + { "$ref": "cardano.json#/definitions/Mandate" + } + } + } + ] + } + } + } + + , "ConstitutionalCommitteeDelegate": + { "title": "ConstitutionalCommitteeDelegate" + , "oneOf": + [ { "type": "object" + , "required": [ "status", "id" ] + , "additionalProperties": false + , "properties": + { "status": + { "type": "string" + , "enum": [ "authorized" ] + } + , "id": + { "$ref": "cardano.json#/definitions/Digest" + } + } + } + , { "type": "object" + , "required": [ "status" ] + , "additionalProperties": false + , "properties": + { "status": + { "type": "string" + , "enum": [ "resigned" ] + } + , "metadata": + { "$ref": "cardano.json#/definitions/Anchor" + } + } + } + , { "type": "object" + , "required": [ "status" ] , "additionalProperties": false - , "required": [ "epoch" ] , "properties": - { "epoch": { "$ref": "cardano.json#/definitions/Epoch" } + { "status": + { "type": "string" + , "enum": [ "none" ] + } } } + ] + } + + , "ConstitutionalCommitteeMemberSummary": + { "title": "ConstitutionalCommitteeMemberSummary" + , "type": "object" + , "additionalProperties": false + , "required": [ "id" ] + , "properties": + { "id": { "$ref": "cardano.json#/definitions/Digest" } + , "mandate": { "$ref": "cardano.json#/definitions/Mandate" } } } @@ -1005,7 +1109,7 @@ , "properties": { "members": { "type": "array" - , "items": { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeMember" } + , "items": { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeMemberSummary" } } , "quorum": { "$ref": "cardano.json#/definitions/Ratio" @@ -1187,7 +1291,7 @@ { "added": { "type": "array" , "items": - { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeMember" + { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeMemberSummary" } } , "removed": @@ -1448,6 +1552,16 @@ , "contentEncoding": "base16" } + , "Mandate": + { "title": "Mandate" + , "type": "object" + , "additionalProperties": false + , "required": [ "epoch" ] + , "properties": + { "epoch": { "$ref": "cardano.json#/definitions/Epoch" } + } + } + , "Metadata": { "title": "Metadata" , "type": "object" diff --git a/docs/static/ogmios.json b/docs/static/ogmios.json index ef5c4fdf5..7106b2d9d 100644 --- a/docs/static/ogmios.json +++ b/docs/static/ogmios.json @@ -12,6 +12,7 @@ , "AcquireLedgerState", "AcquireLedgerStateResponse" , "QueryLedgerStateConstitution", "QueryLedgerStateConstitutionResponse" + , "QueryLedgerStateConstitutionalCommittee", "QueryLedgerStateConstitutionalCommitteeResponse" , "QueryLedgerStateEpoch", "QueryLedgerStateEpochResponse" , "QueryLedgerStateEraStart", "QueryLedgerStateEraStartResponse" , "QueryLedgerStateEraSummaries", "QueryLedgerStateEraSummariesResponse" @@ -666,7 +667,9 @@ , "method": { "type": "string" , "enum": - [ "queryLedgerState/epoch" + [ "queryLedgerState/constitution" + , "queryLedgerState/constitutionalCommittee" + , "queryLedgerState/epoch" , "queryLedgerState/eraStart" , "queryLedgerState/eraSummaries" , "queryLedgerState/liveStakeDistribution" @@ -710,7 +713,9 @@ , "method": { "type": "string" , "enum": - [ "queryLedgerState/epoch" + [ "queryLedgerState/constitution" + , "queryLedgerState/constitutionalCommittee" + , "queryLedgerState/epoch" , "queryLedgerState/eraStart" , "queryLedgerState/eraSummaries" , "queryLedgerState/liveStakeDistribution" @@ -753,7 +758,9 @@ , "method": { "type": "string" , "enum": - [ "queryLedgerState/epoch" + [ "queryLedgerState/constitution" + , "queryLedgerState/constitutionalCommittee" + , "queryLedgerState/epoch" , "queryLedgerState/eraStart" , "queryLedgerState/eraSummaries" , "queryLedgerState/liveStakeDistribution" @@ -834,6 +841,75 @@ ] } + , "QueryLedgerStateConstitutionalCommittee": + { "title": "QueryLedgerStateConstitutionalCommittee" + , "description": "Get the state of the constitutional committee (only available from Conway onwards)." + , "type": "object" + , "required": [ "jsonrpc", "method" ] + , "additionalProperties": false + , "properties": + { "jsonrpc": + { "type": "string" + , "enum": [ "2.0" ] + } + , "method": + { "type": "string" + , "enum": [ "queryLedgerState/constitutionalCommittee" ] + } + , "id": + { "description": "An arbitrary JSON value that will be mirrored back in the response." + } + } + } + + , "QueryLedgerStateConstitutionalCommitteeResponse": + { "oneOf": + [ { "title": "QueryLedgerStateConstitutionalCommitteeResponse" + , "type": "object" + , "required": [ "jsonrpc", "method", "result" ] + , "additionalProperties": false + , "properties": + { "jsonrpc": + { "type": "string" + , "enum": [ "2.0" ] + } + , "method": + { "type": "string" + , "enum": [ "queryLedgerState/constitutionalCommittee" ] + } + , "result": + { "oneOf": + [ { "type": "null" } + , { "type": "object" + , "additionalProperties": false + , "required": [ "members", "quorum" ] + , "properties": + { "members": + { "type": "array" + , "items": { "$ref": "cardano.json#/definitions/ConstitutionalCommitteeMember" } + } + , "quorum": + { "oneOf": + [ { "type": "null" } + , { "$ref": "cardano.json#/definitions/Ratio" } + ] + } + } + } + ] + } + , "id": + { "description": "Any value that was set by a client request in the 'id' field." + } + } + } + , { "$ref": "ogmios.json#/properties/QueryLedgerStateEraMismatch" } + , { "$ref": "ogmios.json#/properties/QueryLedgerStateUnavailableInCurrentEra" } + , { "$ref": "ogmios.json#/properties/QueryLedgerStateAcquiredExpire" } + ] + } + + , "QueryLedgerStateEpoch": { "title": "QueryLedgerStateEpoch" , "description": "Query the current epoch number the ledger is at." diff --git a/server/src/Ogmios/Data/Json/Conway.hs b/server/src/Ogmios/Data/Json/Conway.hs index fdb4f58fe..aee370a2e 100644 --- a/server/src/Ogmios/Data/Json/Conway.hs +++ b/server/src/Ogmios/Data/Json/Conway.hs @@ -63,6 +63,7 @@ import qualified Cardano.Ledger.Conway.TxCert as Cn import qualified Cardano.Ledger.Alonzo.Plutus.TxInfo as Al import qualified Cardano.Ledger.Alonzo.Scripts as Al +import qualified Cardano.Ledger.Api.State.Query as Cn import qualified Cardano.Ledger.Babbage.TxInfo as Ba import qualified Cardano.Ledger.Conway.Scripts as Cn import qualified Cardano.Ledger.Conway.TxInfo as Cn @@ -116,6 +117,90 @@ encodeCommittee x = encodeObject encodeUnitInterval (Cn.committeeThreshold x) ) +encodeCommitteeMembersState + :: Crypto crypto + => Cn.CommitteeMembersState crypto + -> Json +encodeCommitteeMembersState x = encodeObject + ( "members" .= + encodeMapAsList encodeConstitutionalCommitteeMemberState (Cn.csCommittee x) + <> "quorum" .= + encodeMaybe encodeUnitInterval (Cn.csThreshold x) + ) + +encodeConstitutionalCommitteeMemberState + :: forall crypto. Crypto crypto + => Ledger.Credential 'ColdCommitteeRole crypto + -> Cn.CommitteeMemberState crypto + -> Json +encodeConstitutionalCommitteeMemberState memberId st = encodeObject + ( "id" .= + Shelley.encodeCredential memberId + <> "delegate" .= + encodeHotCredAuthStatus (Cn.cmsHotCredAuthStatus st) + <> "status" .= + encodeMemberStatus (Cn.cmsStatus st) + <> "mandate" .=? OmitWhenNothing + (encodeSingleton "epoch" . encodeEpochNo) (maybeToStrictMaybe (Cn.cmsExpiration st)) + <> "next" .=? OmitWhenNothing + identity (encodeNextEpochChange (Cn.cmsNextEpochChange st)) + ) + +encodeMemberStatus :: Cn.MemberStatus -> Json +encodeMemberStatus = encodeText . \case + -- Votes of this member will count during ratification + Cn.Active -> "active" + Cn.Expired -> "expired" + -- This can happen when a hot credential for an unknown cold credential exists. + -- Such Committee member will be either removed from the state at the next + -- epoch boundary or enacted as a new member. + Cn.Unrecognized -> "unrecognized" + +encodeHotCredAuthStatus + :: Crypto crypto + => Cn.HotCredAuthStatus crypto + -> Json +encodeHotCredAuthStatus = \case + Cn.MemberNotAuthorized -> + encodeObject + ( "status" .= encodeText "none" + ) + Cn.MemberAuthorized hot -> + encodeObject + ( "status" .= encodeText "authorized" + <> "id" .= Shelley.encodeCredential hot + ) + Cn.MemberResigned anchor -> + encodeObject + ( "status" .= encodeText "resigned" + <> "metadata" .=? OmitWhenNothing + encodeAnchor (maybeToStrictMaybe anchor) + ) + +encodeNextEpochChange + :: Cn.NextEpochChange + -> StrictMaybe Json +encodeNextEpochChange = fmap encodeObject . \case + Cn.NoChangeExpected -> + SNothing + Cn.ToBeEnacted -> + SJust + ( "change" .= encodeText "toBeEnacted" + ) + Cn.ToBeRemoved -> + SJust + ( "change" .= encodeText "toBeRemoved" + ) + Cn.ToBeExpired -> + SJust + ( "change" .= encodeText "expiring" + ) + Cn.TermAdjusted mandate -> + SJust + ( "change" .= encodeText "adjustingMandate" + <> "mandate" .= encodeSingleton "epoch" (encodeEpochNo mandate) + ) + encodeContextError :: ( Crypto (EraCrypto era) , PlutusPurpose AsIx era ~ Cn.ConwayPlutusPurpose AsIx era @@ -221,13 +306,12 @@ encodeConwayGovCert = \case encodeStrictMaybe encodeAnchor anchor Cn.ConwayAuthCommitteeHotKey cold hot -> "type" .= - encodeText "constitutionalCommitteeHotKeyRegistration" + encodeText "constitutionalCommitteeDelegation" <> "member" .= encodeConstitutionalCommitteeMember cold SNothing <> - "hotKey" .= - Shelley.encodeCredential hot + "delegate" .= encodeHotCredAuthStatus (Cn.MemberAuthorized hot) Cn.ConwayResignCommitteeColdKey cold anchor -> "type" .= encodeText "constitutionalCommitteeRetirement" diff --git a/server/src/Ogmios/Data/Json/Query.hs b/server/src/Ogmios/Data/Json/Query.hs index 82e10123a..e383959a8 100644 --- a/server/src/Ogmios/Data/Json/Query.hs +++ b/server/src/Ogmios/Data/Json/Query.hs @@ -68,6 +68,7 @@ module Ogmios.Data.Json.Query -- * Parsers , parseQueryLedgerConstitution + , parseQueryLedgerConstitutionalCommittee , parseQueryLedgerEpoch , parseQueryLedgerEraStart , parseQueryLedgerEraSummaries @@ -262,6 +263,7 @@ import qualified Cardano.Ledger.Shelley.RewardProvenance as Sh import qualified Cardano.Ledger.Shelley.UTxO as Sh import qualified Cardano.Ledger.Api as Ledger +import qualified Cardano.Ledger.Api.State.Query as Ledger import qualified Ogmios.Data.Json.Allegra as Allegra import qualified Ogmios.Data.Json.Alonzo as Alonzo import qualified Ogmios.Data.Json.Babbage as Babbage @@ -367,6 +369,8 @@ instance Crypto crypto => FromJSON (Query Proxy (CardanoBlock crypto)) where parseQueryLedgerUtxo (const id) queryParams "LedgerState/constitution" -> parseQueryLedgerConstitution (const id) queryParams + "LedgerState/constitutionalCommittee" -> + parseQueryLedgerConstitutionalCommittee id queryParams "Network/blockHeight" -> parseQueryNetworkBlockHeight id queryParams "Network/genesisConfiguration" -> @@ -621,7 +625,7 @@ parseQueryLedgerConstitution -> Json.Value -> Json.Parser (QueryInEra f (CardanoBlock crypto)) parseQueryLedgerConstitution genResult = - Json.withObject "epoch" $ \obj -> do + Json.withObject "constitution" $ \obj -> do guard (null obj) $> \case SomeShelleyEra ShelleyBasedEraShelley -> Nothing @@ -639,6 +643,30 @@ parseQueryLedgerConstitution genResult = (eraMismatchOrResult (encodeObject . Conway.encodeConstitution)) (genResult $ Proxy @(ConwayEra crypto)) +parseQueryLedgerConstitutionalCommittee + :: forall f crypto. (Crypto crypto) + => GenResult crypto f (Ledger.CommitteeMembersState crypto) + -> Json.Value + -> Json.Parser (QueryInEra f (CardanoBlock crypto)) +parseQueryLedgerConstitutionalCommittee genResult = + Json.withObject "constitutionalCommittee" $ \obj -> do + guard (null obj) $> \case + SomeShelleyEra ShelleyBasedEraShelley -> + Nothing + SomeShelleyEra ShelleyBasedEraAllegra -> + Nothing + SomeShelleyEra ShelleyBasedEraMary -> + Nothing + SomeShelleyEra ShelleyBasedEraAlonzo -> + Nothing + SomeShelleyEra ShelleyBasedEraBabbage -> + Nothing + SomeShelleyEra ShelleyBasedEraConway -> + Just $ SomeStandardQuery + (LSQ.BlockQuery (QueryIfCurrentConway $ GetCommitteeMembersState mempty mempty mempty)) + (eraMismatchOrResult Conway.encodeCommitteeMembersState) + genResult + parseQueryLedgerEpoch :: forall crypto f. () => GenResult crypto f EpochNo diff --git a/server/src/Ogmios/Data/Ledger/PredicateFailure/Alonzo.hs b/server/src/Ogmios/Data/Ledger/PredicateFailure/Alonzo.hs index 2103f73d8..2b018656e 100644 --- a/server/src/Ogmios/Data/Ledger/PredicateFailure/Alonzo.hs +++ b/server/src/Ogmios/Data/Ledger/PredicateFailure/Alonzo.hs @@ -6,6 +6,9 @@ module Ogmios.Data.Ledger.PredicateFailure.Alonzo where import Ogmios.Prelude +import Cardano.Ledger.Alonzo.Plutus.Evaluate + ( CollectError (..) + ) import Cardano.Ledger.Core ( EraRule ) @@ -25,17 +28,14 @@ import Ogmios.Data.Ledger.PredicateFailure import Ogmios.Data.Ledger.PredicateFailure.Shelley ( encodeDelegsFailure ) -import Cardano.Ledger.Alonzo.Plutus.Evaluate - ( CollectError (..) - ) import Relude.Unsafe ( fromJust ) -import qualified Data.Map as Map -import qualified Ogmios.Data.Ledger.PredicateFailure.Shelley as Shelley import qualified Cardano.Ledger.Alonzo.Rules as Al import qualified Cardano.Ledger.Shelley.Rules as Sh +import qualified Data.Map as Map +import qualified Ogmios.Data.Ledger.PredicateFailure.Shelley as Shelley encodeLedgerFailure :: forall crypto. diff --git a/server/src/Ogmios/Data/Ledger/PredicateFailure/Conway.hs b/server/src/Ogmios/Data/Ledger/PredicateFailure/Conway.hs index 2193ef2b7..ee52b7eb1 100644 --- a/server/src/Ogmios/Data/Ledger/PredicateFailure/Conway.hs +++ b/server/src/Ogmios/Data/Ledger/PredicateFailure/Conway.hs @@ -14,15 +14,15 @@ import Ogmios.Data.Ledger.PredicateFailure , MultiEraPredicateFailure (..) , pickPredicateFailure ) +import Ogmios.Data.Ledger.PredicateFailure.Alonzo + ( encodeCollectErrors + ) import Ogmios.Data.Ledger.PredicateFailure.Babbage ( encodeUtxowFailure ) import Ogmios.Data.Ledger.PredicateFailure.Shelley ( encodePoolFailure ) -import Ogmios.Data.Ledger.PredicateFailure.Alonzo - ( encodeCollectErrors - ) import qualified Cardano.Ledger.Api as Ledger import qualified Cardano.Ledger.Conway.Rules as Cn diff --git a/server/test/unit/Ogmios/Data/JsonSpec.hs b/server/test/unit/Ogmios/Data/JsonSpec.hs index 8a29b80cb..57bf293f3 100644 --- a/server/test/unit/Ogmios/Data/JsonSpec.hs +++ b/server/test/unit/Ogmios/Data/JsonSpec.hs @@ -79,6 +79,8 @@ import Ogmios.Data.Json.Prelude import Ogmios.Data.Json.Query ( QueryInEra , SomeQuery (..) + , parseQueryLedgerConstitution + , parseQueryLedgerConstitutionalCommittee , parseQueryLedgerEpoch , parseQueryLedgerEraStart , parseQueryLedgerEraSummaries @@ -186,6 +188,8 @@ import Test.Generators , genBlock , genBlockNo , genBoundResult + , genCommitteeMembersStateResult + , genConstitutionResult , genData , genEpochResult , genEvaluateTransactionResponse @@ -677,6 +681,14 @@ spec = do |]) (parseQueryLedgerStakePools genPoolParametersResult) + validateLedgerStateQuery 10 "constitution" + Nothing + (parseQueryLedgerConstitution genConstitutionResult) + + validateLedgerStateQuery 10 "constitutionalCommittee" + Nothing + (parseQueryLedgerConstitutionalCommittee genCommitteeMembersStateResult) + validateNetworkQuery 10 "blockHeight" Nothing (parseQueryNetworkBlockHeight (const $ genWithOrigin genBlockNo)) diff --git a/server/test/unit/Test/Generators.hs b/server/test/unit/Test/Generators.hs index bc6b2b06e..bdaeb846c 100644 --- a/server/test/unit/Test/Generators.hs +++ b/server/test/unit/Test/Generators.hs @@ -129,6 +129,7 @@ import Test.QuickCheck , choose , elements , frequency + , listOf , listOf1 , oneof , scale @@ -136,6 +137,9 @@ import Test.QuickCheck , suchThat , vector ) +import Test.QuickCheck.Arbitrary.Generic + ( genericArbitrary + ) import Test.QuickCheck.Gen ( Gen (..) ) @@ -156,11 +160,13 @@ import qualified Data.Map as Map import qualified Ouroboros.Network.Point as Point +import qualified Cardano.Ledger.Api.Governance as Ledger import qualified Cardano.Ledger.Block as Ledger import qualified Cardano.Ledger.Core as Ledger import qualified Cardano.Ledger.Keys as Ledger import qualified Cardano.Ledger.PoolDistr as Ledger +import qualified Cardano.Ledger.Api.State.Query as Ledger import qualified Cardano.Ledger.Shelley.API.Wallet as Sh.Api import qualified Cardano.Ledger.Shelley.PParams as Sh import qualified Cardano.Ledger.Shelley.UTxO as Sh @@ -606,6 +612,63 @@ genPParamsResult _ _ = Nothing -> Nothing +genConstitutionResult + :: forall crypto era. (crypto ~ StandardCrypto, Typeable era) + => Proxy era + -> Proxy (QueryResult crypto (Ledger.Constitution era)) + -> Gen (QueryResult crypto (Ledger.Constitution era)) +genConstitutionResult _ _ = + maybe (error "genConstitutionResult: unsupported era") identity genConway + where + genConway = + case testEquality (typeRep @era) (typeRep @StandardConway) of + Just Refl{} -> + Just $ frequency + [ (1, Left <$> genMismatchEraInfo) + , (10, Right <$> arbitrary) + ] + Nothing -> + Nothing + +genGovStateResult + :: forall crypto era. (crypto ~ StandardCrypto, Typeable era) + => Proxy era + -> Proxy (QueryResult crypto (Ledger.GovState era)) + -> Gen (QueryResult crypto (Ledger.GovState era)) +genGovStateResult _ _ = + maybe (error "genGovStateResult: unsupported era") identity genConway + where + genConway = + case testEquality (typeRep @era) (typeRep @StandardConway) of + Just Refl{} -> + Just $ frequency + [ (1, Left <$> genMismatchEraInfo) + , (10, Right <$> arbitrary) + ] + Nothing -> + Nothing + +genCommitteeMembersStateResult + :: forall crypto era. (crypto ~ StandardCrypto) + => Proxy era + -> Gen (QueryResult crypto (Ledger.CommitteeMembersState crypto)) +genCommitteeMembersStateResult _ = + frequency + [ (1, Left <$> genMismatchEraInfo) + , (10, fmap Right (Ledger.CommitteeMembersState <$> genMembers <*> arbitrary <*> arbitrary)) + ] + where + genMembers = fmap Map.fromList + (listOf $ (,) <$> arbitrary <*> genCommitteeMemberState) + +genCommitteeMemberState + :: Gen (Ledger.CommitteeMemberState StandardCrypto) +genCommitteeMemberState = Ledger.CommitteeMemberState + <$> genericArbitrary + <*> genericArbitrary + <*> arbitrary + <*> genericArbitrary + genProposedPParamsResult :: forall crypto era. (crypto ~ StandardCrypto, Typeable era) => Proxy era