Skip to content

Add era summary endpoint #147

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 4 commits into from

Conversation

SebastienGllmt
Copy link
Contributor

@SebastienGllmt SebastienGllmt commented Mar 24, 2025

Closes #144

EDIT: likely deprecated, and this PR is instead replaced by #151

Comment on lines +28 to +29
message ReadEraSummaryRequest {
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know what you want as a convention when the request is empty. Should it be an empty object (like here), or just omit this entirely?

@@ -135,7 +147,8 @@ service QueryService {
rpc ReadUtxos(ReadUtxosRequest) returns (ReadUtxosResponse); // Read specific UTxOs by reference.
rpc SearchUtxos(SearchUtxosRequest) returns (SearchUtxosResponse); // Search for UTxO based on a pattern.
rpc ReadData(ReadDataRequest) returns (ReadDataResponse); // Read specific datum by hash
rpc ReadTx(GetTxRequest) returns (GetTxResponse); // Get Txs by by chain-specific criteria.
rpc ReadTx(ReadTxRequest) returns (ReadTxResponse); // Get Txs by chain-specific criteria.
Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Mar 24, 2025

Choose a reason for hiding this comment

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

I noticed ReadTx was wrong causing a compiler error (it used the wrong name for the request & response types - there is no type called GetTxRequest)

// a lot of tools expect a string name (ex: "shelley") to look up era info
EraBoundary start = 1; // start of this era
EraBoundary end = 2; // end of this era (if the era has a well-defined ending)
// TODO: do we want a summary of parameter changed?
Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Mar 24, 2025

Choose a reason for hiding this comment

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

I left this out since I don't think this is required to unblock the core value proposition of this feature (and it's maybe harder to implement for nodes). Notably, if we add the era name identifier I mention above, we can get the parameters directly from the node/file

I'm open to thoughts on how the structure of including parameter changes here would work though if anybody has a preference. Some things I noticed:

  1. It doesn't seem BlockFrost gives you all the parameters, just the relevant ones (do we want to do something similar? It's a bit arbitrary)
  2. Now with dReps, parameters can change through onchain votes, and so are no longer constrained to epoch boundaries

If nobody has any thoughts on this, we can just leave it out for now. My feeling is that we'll probably end up with another set of endpoints to

  1. Get the genesis file for an era by name
  2. Get a list of all parameter updates (which can include those triggered by eras and those triggered by onchain actions)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've since added a ReadEraConfig endpoint which partially alleviates the issue (it doesn't handle parameters that change through governance, but it's a start and still useful)

uint64 time = 1; // ms timestamp
uint64 slot = 2; // absolute slot number of the first block of this era
uint64 epoch = 3; // first epoch for this era
optional bytes hash = 4; // block hash of the first block of this era (if the era has started)
Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Mar 24, 2025

Choose a reason for hiding this comment

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

This is maybe the main controversial part of this PR

Having a hash here helps, because it means you can use it to construct a ChainPoint. If you only get a slot and nothing else, you can't do anything with it in utxorpc

However, I'm not sure if nodes keep track of this information, or how easy it is to support this. The hard part mostly comes with dealing with eras that haven't started/ended yet (see #144 (comment))

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't track this in Dingo, but we could. Is that tracked in the Haskell node? It might not work in cardano-node-api's implementation, but that happens sometimes and we consider it acceptable if the Haskell node doesn't give a way.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The other alternative is that we could change the endpoints that accept a ChainPoint to instead allow simply passing in a slot number (with no hash required)

It depends if the hash field in ChainPoint is used for performance reasons, or if it's just used to avoid rollbacks (i.e. ensure the slot number really is the hash you expect)

But if we can get away with just using slot only, not only is the API easier to work with, it also solves the issue here (since we would no longer need the hash)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I created an issue to track the alternative solution to this where only slots are required for ChainPoint: #148

Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Mar 26, 2025

Choose a reason for hiding this comment

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

I also added a new ReadChainId endpoint to this PR so we can get the genesis hash for Byron. Of course, getting the hash for other eras (notably Shelley) would be nice, but getting the Byron genesis hash already unblocks multiple cases

gen/docs/docs.md Outdated
Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Mar 24, 2025

Choose a reason for hiding this comment

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

The doc file looks like it was out of date. It contains my changes, but other changes up until now too

Comment on lines 606 to 607
// TODO: do we want any kind of "era name" identifier?
// a lot of tools expect a string name (ex: "shelley") to look up era info
Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Mar 24, 2025

Choose a reason for hiding this comment

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

(edit: I ended up adding the name as a field)

My feeling is we should probably add this, but open to thoughts.

The rationale is that the EraSummary endpoint does not actually tell you the protocol parameters for this era. Rather, you have to query a node (or read from a disk) the right genesis file to get the full data. However, these fields & endpoints use a string name (ex: shelley) as the identifier, so having a string here would help fetch this info

The alternative, if there is no name, is that clients would have to hardcode it (ex: the first "summary" is for "shelley"), but this is boilerplate and also error-prone (you have to remember to update this mapping every hardfork)

Notably, this is used in cooperation with the new ReadEraConfig endpoint

}

message EraConfig {
google.protobuf.Struct config = 1;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Here I used the Struct type since one of the recommended ways to deal with JSON (which Cardano genesis files use). However, the generated code for this is pretty verbose (it's not just a JSON object)

The other alternative is to just make the type be string (then people can use it with a JSON parser of their choice). I'm open to either option

// Response containing the summary of eras
message ReadChainIdResponse {
bytes genesis = 1; // genesis hash for the chain
string caip2 = 2; // the caip-2 ID for this network
Copy link
Contributor Author

Choose a reason for hiding this comment

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

CAIP2 is a chain-agnostic way of specifying networks for blockchains. You can see the spec here

Notably, the caip2 notation for Cardano is defined by cip34

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if we should add a "name" field

Tentatively, I think not because nodes like Cardano don't expose a "name" field so it would probably end up being annoying to deal with

Comment on lines +173 to +175
rpc ReadEraSummary(ReadEraSummaryRequest) returns (ReadEraSummaryResponse); // Get summary of all eras
rpc ReadEraConfig(ReadEraConfigRequest) returns (ReadEraConfigResponse); // Get configuration for an era
rpc ReadChainId(ReadChainIdRequest) returns (ReadChainIdResponse); // Get the chain ID for this network
Copy link
Member

Choose a reason for hiding this comment

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

@SebastienGllmt do we need to split this three concerns into different methods?

I feel that many of these calls would probably go together in most scenarios and having them separated would make the interface too chatty.

Maybe having something like a single ReadChain method and rely on a nested structure that we can filter using field_masks.

Something like:

  • chain
    • genesis
    • caip2
    • eras[]
      • name
      • start
      • end
      • config

What are your thoughts?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm totally fine with that as well

Copy link
Contributor Author

@SebastienGllmt SebastienGllmt Apr 1, 2025

Choose a reason for hiding this comment

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

@scarmuega how do you want to do the field mask for config given it's nested in the eras array?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Let me know if #151 is what you were thinking of

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

We need a way to query Era Summaries
3 participants