Skip to content

Add REST endpoint to retrieve historical_summaries #6675

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

Merged
merged 9 commits into from
May 6, 2025

Conversation

kdeme
Copy link
Contributor

@kdeme kdeme commented Oct 23, 2024

In draft currently as it is not in spec. Needs to be accompanied still with a proposal for beacon REST API specifications.

The idea is that historical_summaries is very useful for verifying if beacon blocks (with their accompanied proofs) are part of the canonical chain, however there is no way to actually retrieve the historical_summaries except by using the /eth/v2/debug/beacon/states/{state_id} endpoint, which is quite the download in terms of size (We are currently using this endpoint in Portal/fluffy for our portal_bridge, but it takes long).

This PR adds an endpoint that provides the historical_summaries with its proof against the state root. This is done so that the historical_summaries can then be verified for example with the finalized state root that a (light) node might hold from its finality update.
Again, this is specifically useful in Portal (as a portal_bridge can inject it in the p2p network as is, and receiving nodes can verify it if they are light client synced) . But you could make an argument that it could also be useful on a consensus light client (assuming blocks with proofs would be somehow available).

edit: moved to /nimbus/v1/debug/ namespace and removed draft

Copy link

github-actions bot commented Oct 23, 2024

Unit Test Results

       15 files  ±0    2 630 suites  ±0   1h 15m 59s ⏱️ - 1m 24s
  6 372 tests ±0    5 870 ✔️ ±0  502 💤 ±0  0 ±0 
44 281 runs  ±0  43 603 ✔️ ±0  678 💤 ±0  0 ±0 

Results for commit baff4da. ± Comparison against base commit dd896bf.

♻️ This comment has been updated with latest results.

@tersec
Copy link
Contributor

tersec commented Oct 24, 2024

One consideration for me is that if this does get standardized, we don't end up with both a pre-standardized and standardized version which needs to be supported.

@kdeme
Copy link
Contributor Author

kdeme commented Oct 24, 2024

One consideration for me is that if this does get standardized, we don't end up with both a pre-standardized and standardized version which needs to be supported.

One approach could be that I move it under the /nimbus/v1/debug/ prefix for now, assuming that /nimbus/v1/debug/ would mean custom and subject to removal without notice (like for the debug cli flags).

@kdeme kdeme force-pushed the historical-summaries-endpoint branch from e3d435b to d7047dd Compare October 24, 2024 12:48
@kdeme kdeme marked this pull request as ready for review October 24, 2024 12:49
@cheatfate
Copy link
Contributor

One consideration for me is that if this does get standardized, we don't end up with both a pre-standardized and standardized version which needs to be supported.

One approach could be that I move it under the /nimbus/v1/debug/ prefix for now, assuming that /nimbus/v1/debug/ would mean custom and subject to removal without notice (like for the debug cli flags).

Why you need to remove it? You can establish redirect() call from one endpoint to another when it will be part of specification, we was doing this many times.

@tersec
Copy link
Contributor

tersec commented Oct 25, 2024

One consideration for me is that if this does get standardized, we don't end up with both a pre-standardized and standardized version which needs to be supported.

One approach could be that I move it under the /nimbus/v1/debug/ prefix for now, assuming that /nimbus/v1/debug/ would mean custom and subject to removal without notice (like for the debug cli flags).

Why you need to remove it? You can establish redirect() call from one endpoint to another when it will be part of specification, we was doing this many times.

Well, part of the point is not to end up with extra, redundant endpoints. The point of this subject to removal without notice would be to exactly allow experimentation without obligation such directions later.

@cheatfate
Copy link
Contributor

One consideration for me is that if this does get standardized, we don't end up with both a pre-standardized and standardized version which needs to be supported.

One approach could be that I move it under the /nimbus/v1/debug/ prefix for now, assuming that /nimbus/v1/debug/ would mean custom and subject to removal without notice (like for the debug cli flags).

Why you need to remove it? You can establish redirect() call from one endpoint to another when it will be part of specification, we was doing this many times.

Well, part of the point is not to end up with extra, redundant endpoints. The point of this subject to removal without notice would be to exactly allow experimentation without obligation such directions later.

There could be exactly 2 situations:

  1. Change will be proposed, but declined.
  2. Change will be proposed and accepted.

In both this cases it could be better to start with /nimbus/ endpoint. Because /nimbus/ endpoint could not be regulated by EF, so we could add/remove any functions. Otherwise in case 1 you will be forced to remove it from /debug/.

@kdeme kdeme force-pushed the historical-summaries-endpoint branch from d7047dd to 566e69b Compare November 8, 2024 09:26
@kdeme kdeme force-pushed the historical-summaries-endpoint branch 3 times, most recently from e123f65 to 631a155 Compare February 13, 2025 18:00
@kdeme
Copy link
Contributor Author

kdeme commented Feb 19, 2025

@etan-status and/or @cheatfate Would you mind having a look at this again? It is something crucial for our development on Portal network.

As mentioned in the comments, fork management is added, which I think was the only issue (at least for getting it under nimbus/debug namespace)?

Some remarks:

  • Those HistoricalSummariesProof and ForkedHistoricalSummariesProof are added in the rest types code as I did not want to pollute the specs and forks code with types that are not actually part of the consensus specifications. But if you believe they now pollute too much the rest types, I can also move them into another new file.
  • The Forked type is required as we use arrays for the proofs (which differ in size between forks). We could also opt for going with a sequence instead, but that is not consistent with what is done for example for FinalityBranch, CurrentSyncCommitteeBranch and NextSyncCommitteeBranch.

Copy link
Contributor

@etan-status etan-status left a comment

Choose a reason for hiding this comment

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

There's also an effort from @Inspector-Butters and @wemeetagain to look into a general proof API. But I think as part of Nimbus namespace it's alright to have specific endpoints to unblock portal progress.

Code looks correct to me, I have put some remarks to make it less maintenance-heavy and reduce duplication. Once comments are addressed, good to go.

template init*(
T: type ForkedHistoricalSummariesWithProof,
historical_summaries: GetHistoricalSummariesV1Response,
fork: ConsensusFork,
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm. Do we really need to retain the exact consensusFork?

Or would a HistoricalSummariesFork make sense, similar to LightClientDataFork, EpochInfoFork or (experimental BlobFork in #6451)? That way, this code no longer has to be maintained with every fork, by simply having a capellaData and electraData member, and no denebData / fuluData etc

case x.kind
of HistoricalSummariesFork.Electra:
  const historicalFork {.inject, used.} = HistoricalSummariesFork.Electra
  template forkySummaries: untyped {.inject, used.} = x.electraData
  body
of ConsensusFork.Capella:
  const historicalFork {.inject, used.} = HistoricalSummariesFork.Capella
  template forkySummaries: untyped {.inject, used.} = x.capellaData
  body

One can still recover exact consensusFork if needed from Eth-Consensus-Version HTTP header, if needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, I don't think we need it.

In the past, I mostly found it confusing to work with these different fork types and always had to look back at which one contained which forks, hence why I did not go for that.

But I think I can understand how it makes maintenance probably quite a bit easier when you are dealing with that all the time. Though for an external/new reader of the code, having all these different fork types without a reference in the spec, might be confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So in the case we would add a HistoricalSummariesFork, I assume the code would work in such way that in the metrics API where there is now the statement:

    node.withStateForBlockSlotId(bslot):
      return withState(state):
        when consensusFork >= ConsensusFork.Capella:

we would set the HistoricalSummariesFork based on the ConsensusFork?

And then from then onward work with the HistoricalSummariesFork?

I can see the benefit of not having to update the fork templates for that, but it also allows for not failing compilation when a fork gets added which might require a change again?

I am currently undecided what I like the most, but if there is a strong pull towards adding HistoricalSummariesFork then I don't mind doing so (especially since it is already the habit in the code base).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@tersec any opinion on this? Else I'll go ahead and merge as is now.

Copy link
Contributor

@etan-status etan-status Mar 6, 2025

Choose a reason for hiding this comment

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

but it also allows for not failing compilation when a fork gets added which might require a change again

Typically, adding a new fork is quite a mechanical process, so I guess if this doesn't compile, in practice the first step I expect is that the person adding a new fork would do a simple copy paste of the prior fork's logic, effectively ending up with the same bugs as if the code would implicitly inherit the prior fork's logic rather than explicitly. The mental effort is comparable, one has to be actually aware of generalized indices getting re-indexed during a fork to prevent the mistake.

If you want to protect against the bugs, you can add a static: doAssert that checks that log2trunc(consensusFork.BeaconState.totalSerializedFields) matches the compatible value, to prevent someone from messing it up with a mechanical change when adding a future fork. Such an assertion cannot be lazily adjusted to make the code work and requires more thought to address :-) That way, you have the maintenance-free implicit inheritance for all the forks that don't need a new HistoricalSummariesFork, and you also have the compile-time error in case a new HistoricalSummariesFork is required.

Most ConsensusFork won't need a new HistoricalSummariesFork, I would personally slightly tend towards the HistoricalSummariesFork. But I'm also fine with the current solution. Up to @tersec

Copy link
Contributor

Choose a reason for hiding this comment

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

And, consensusFork.BeaconState actually works. There's a template in forks.nim to support accessing fork specific types based on static ConsensusFork constant.

withConsensusFork(consensusFork):  # or withState / withBlock, they also inject `consensusFork` constant
  static: doAssert log2trunc(consensusFork.BeaconState.totalSerializedFields) == ....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Introduced HistoricalSummariesFork in c3166c5

Assuming this still is the preferred way, if not I can revert the last commit and we go with that.

@kdeme kdeme force-pushed the historical-summaries-endpoint branch from 62293d6 to ebb2ec4 Compare March 4, 2025 16:13
@kdeme
Copy link
Contributor Author

kdeme commented Mar 5, 2025

@etan-status I addressed latest feedback, aside from the specific fork addition, which I commented on (but am open to add still).

@KolbyML
Copy link

KolbyML commented Apr 22, 2025

Is this still blocked? Because I want to use it!

@KolbyML
Copy link

KolbyML commented Apr 22, 2025

Ideally we standardized this endpoint across the board and with loadstar ChainSafe/lodestar#7245

@tersec
Copy link
Contributor

tersec commented Apr 27, 2025

The failing CI, https://ci.status.im/blue/organizations/jenkins/nimbus-eth2%2Fplatforms%2Flinux%2Fx86_64%2Fnimv2_2/detail/PR-6675/2/pipeline is a bit confused; it's supposed to be Nim 2.2, but it's apparently using Nim 2.0.6:


[2025-04-26T13:53:52.899Z] + make LOG_LEVEL=TRACE
[2025-04-26T13:53:53.682Z] Building: build/deposit_contract
[2025-04-26T13:53:53.682Z] Building: build/resttest
[2025-04-26T13:53:53.682Z] Building: build/mev_mock
[2025-04-26T13:53:53.682Z] Building: build/ncli
[2025-04-26T13:53:53.682Z] [using Nim version v2.0.6]
[2025-04-26T13:53:53.682Z] [using Nim version v2.0.6]
[2025-04-26T13:53:53.682Z] [using Nim version v2.0.6]
[2025-04-26T13:53:53.682Z] [using Nim version v2.0.6]
[2025-04-26T13:54:07.291Z] @["Unknown constants in file: ", "MAX_REQUEST_DATA_COLUMN_SIDECARS", "MAX_BLOBS_PER_BLOCK_FULU", "NUMBER_OF_CUSTODY_GROUPS", "CUSTODY_REQUIREMENT", "MAX_REQUEST_PAYLOADS", "WHISK_PROPOSER_SELECTION_GAP", "WHISK_EPOCHS_PER_SHUFFLING_PHASE", "DATA_COLUMN_SIDECAR_SUBNET_COUNT", "SAMPLES_PER_SLOT", "MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS", "TARGET_BLOBS_PER_BLOCK_ELECTRA", "NUMBER_OF_COLUMNS"]
[2025-04-26T13:54:07.291Z] @["Unknown constants in file: ", "MAX_REQUEST_DATA_COLUMN_SIDECARS", "MAX_BLOBS_PER_BLOCK_FULU", "NUMBER_OF_CUSTODY_GROUPS", "CUSTODY_REQUIREMENT", "MAX_REQUEST_PAYLOADS", "WHISK_PROPOSER_SELECTION_GAP", "WHISK_EPOCHS_PER_SHUFFLING_PHASE", "DATA_COLUMN_SIDECAR_SUBNET_COUNT", "SAMPLES_PER_SLOT", "MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS", "TARGET_BLOBS_PER_BLOCK_ELECTRA", "NUMBER_OF_COLUMNS"]
[2025-04-26T13:54:07.291Z] /home/jenkins/workspace/rms_linux_x86_64_nimv2_2_PR-6675/beacon_chain/validator_bucket_sort.nim(50, 19) Error: undeclared identifier: 'newSeqUninit'
[2025-04-26T13:54:07.291Z] candidates (edit distance, scope distance); see '--spellSuggest': 
[2025-04-26T13:54:07.291Z]  (6, 4): 'newSeq'
[2025-04-26T13:54:07.291Z]  (6, 4): 'newSeqOfCap'
[2025-04-26T13:54:07.291Z]  (6, 4): 'newString'
[2025-04-26T13:54:07.291Z] make: *** [Makefile:441: ncli] Error 1
[2025-04-26T13:54:07.291Z] make: *** Waiting for unfinished jobs....
[2025-04-26T13:54:08.074Z] @["Unknown constants in file: ", "MAX_REQUEST_DATA_COLUMN_SIDECARS", "MAX_BLOBS_PER_BLOCK_FULU", "NUMBER_OF_CUSTODY_GROUPS", "CUSTODY_REQUIREMENT", "MAX_REQUEST_PAYLOADS", "WHISK_PROPOSER_SELECTION_GAP", "WHISK_EPOCHS_PER_SHUFFLING_PHASE", "DATA_COLUMN_SIDECAR_SUBNET_COUNT", "SAMPLES_PER_SLOT", "MIN_EPOCHS_FOR_DATA_COLUMN_SIDECARS_REQUESTS", "TARGET_BLOBS_PER_BLOCK_ELECTRA", "NUMBER_OF_COLUMNS"]
[2025-04-26T13:54:08.074Z] /home/jenkins/workspace/rms_linux_x86_64_nimv2_2_PR-6675/beacon_chain/validator_bucket_sort.nim(50, 19) Error: undeclared identifier: 'newSeqUninit'
[2025-04-26T13:54:08.074Z] candidates (edit distance, scope distance); see '--spellSuggest': 
[2025-04-26T13:54:08.074Z]  (6, 4): 'newSeq'
[2025-04-26T13:54:08.074Z]  (6, 4): 'newSeqOfCap'
[2025-04-26T13:54:08.074Z]  (6, 4): 'newString'
[2025-04-26T13:54:08.074Z] make: *** [Makefile:441: mev_mock] Error 1
[2025-04-26T13:54:11.249Z] /home/jenkins/workspace/rms_linux_x86_64_nimv2_2_PR-6675/beacon_chain/validator_bucket_sort.nim(50, 19) Error: undeclared identifier: 'newSeqUninit'
[2025-04-26T13:54:11.249Z] candidates (edit distance, scope distance); see '--spellSuggest': 
[2025-04-26T13:54:11.249Z]  (6, 4): 'newSeq'
[2025-04-26T13:54:11.249Z]  (6, 4): 'newSeqOfCap'
[2025-04-26T13:54:11.249Z]  (6, 4): 'newString'
[2025-04-26T13:54:11.249Z] make: *** [Makefile:441: deposit_contract] Error 1
[2025-04-26T13:54:20.545Z] Build completed successfully: build/resttest
script returned exit code 2

It can be safely ignored.

@kdeme
Copy link
Contributor Author

kdeme commented May 5, 2025

@etan-status I addressed latest feedback, aside from the specific fork addition, which I commented on (but am open to add still).

So I added this historical summaries fork in c3166c5 (see msg #6675 (comment)).

@etan-status @tersec Any preference on with or without historical summaries fork implementation?
If no preference, I'll merge as is. If yes, and preferred not, I don't mind reverting last commit and merging that.

Copy link
Contributor

@etan-status etan-status left a comment

Choose a reason for hiding this comment

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

Personally preferring the version that includes the HistoricalSummariesFork, seems to require less maintenance than the earlier version.

@kdeme kdeme force-pushed the historical-summaries-endpoint branch from c3166c5 to fbbbdda Compare May 5, 2025 14:36
@kdeme kdeme force-pushed the historical-summaries-endpoint branch from fbbbdda to baff4da Compare May 5, 2025 15:09
@kdeme kdeme merged commit d6d2f00 into unstable May 6, 2025
12 checks passed
@kdeme kdeme deleted the historical-summaries-endpoint branch May 6, 2025 06:40
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.

5 participants