-
Notifications
You must be signed in to change notification settings - Fork 268
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
Add REST endpoint to retrieve historical_summaries #6675
base: unstable
Are you sure you want to change the base?
Changes from all commits
a414bfb
ec598f0
55e68e4
b1a27bb
5ea738f
ebb2ec4
6cebf61
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
|
||
import | ||
std/[json, tables], | ||
stew/base10, web3/primitives, httputils, | ||
stew/base10, web3/primitives, httputils, stew/bitops2, | ||
".."/[deposit_snapshots, forks], | ||
".."/mev/[deneb_mev] | ||
|
||
|
@@ -1076,3 +1076,135 @@ func toValidatorIndex*(value: RestValidatorIndex): Result[ValidatorIndex, | |
err(ValidatorIndexError.TooHighValue) | ||
else: | ||
doAssert(false, "ValidatorIndex type size is incorrect") | ||
|
||
## Types and helpers for historical_summaries + proof endpoint | ||
const | ||
# gIndex for historical_summaries field (27th field in BeaconState) | ||
HISTORICAL_SUMMARIES_GINDEX* = GeneralizedIndex(59) # 32 + 27 = 59 | ||
HISTORICAL_SUMMARIES_GINDEX_ELECTRA* = GeneralizedIndex(91) # 64 + 27 = 91 | ||
|
||
type | ||
# Note: these could go in separate Capella/Electra spec files if they were | ||
# part of the specification. | ||
HistoricalSummariesProof* = array[log2trunc(HISTORICAL_SUMMARIES_GINDEX), Eth2Digest] | ||
HistoricalSummariesProofElectra* = | ||
array[log2trunc(HISTORICAL_SUMMARIES_GINDEX_ELECTRA), Eth2Digest] | ||
|
||
# REST API types | ||
GetHistoricalSummariesV1Response* = object | ||
historical_summaries*: HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT] | ||
proof*: HistoricalSummariesProof | ||
slot*: Slot | ||
|
||
GetHistoricalSummariesV1ResponseElectra* = object | ||
historical_summaries*: HashList[HistoricalSummary, Limit HISTORICAL_ROOTS_LIMIT] | ||
proof*: HistoricalSummariesProofElectra | ||
slot*: Slot | ||
|
||
# REST client response type | ||
ForkedHistoricalSummariesWithProof* = object | ||
case kind*: ConsensusFork | ||
of ConsensusFork.Phase0: discard | ||
of ConsensusFork.Altair: discard | ||
of ConsensusFork.Bellatrix: discard | ||
of ConsensusFork.Capella: capellaData*: GetHistoricalSummariesV1Response | ||
of ConsensusFork.Deneb: denebData*: GetHistoricalSummariesV1Response | ||
of ConsensusFork.Electra: electraData*: GetHistoricalSummariesV1ResponseElectra | ||
of ConsensusFork.Fulu: fuluData*: GetHistoricalSummariesV1ResponseElectra | ||
|
||
template historical_summaries_gindex*( | ||
kind: static ConsensusFork): GeneralizedIndex = | ||
when kind >= ConsensusFork.Electra: | ||
HISTORICAL_SUMMARIES_GINDEX_ELECTRA | ||
elif kind >= ConsensusFork.Capella: | ||
HISTORICAL_SUMMARIES_GINDEX | ||
else: | ||
{.error: "historical_summaries_gindex does not support " & $kind.} | ||
|
||
template GetHistoricalSummariesResponse*( | ||
kind: static ConsensusFork): auto = | ||
when kind >= ConsensusFork.Electra: | ||
GetHistoricalSummariesV1ResponseElectra | ||
elif kind >= ConsensusFork.Capella: | ||
GetHistoricalSummariesV1Response | ||
else: | ||
{.error: "GetHistoricalSummariesResponse does not support " & $kind.} | ||
|
||
template init*( | ||
T: type ForkedHistoricalSummariesWithProof, | ||
historical_summaries: GetHistoricalSummariesV1Response, | ||
fork: ConsensusFork, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So in the case we would add a
we would set the And then from then onward work with the 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 Most There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And, withConsensusFork(consensusFork): # or withState / withBlock, they also inject `consensusFork` constant
static: doAssert log2trunc(consensusFork.BeaconState.totalSerializedFields) == .... |
||
): T = | ||
case fork | ||
of ConsensusFork.Phase0: | ||
raiseAssert $fork & " fork should not be used for historical summaries" | ||
of ConsensusFork.Altair: | ||
raiseAssert $fork & " fork should not be used for historical summaries" | ||
of ConsensusFork.Bellatrix: | ||
raiseAssert $fork & " fork should not be used for historical summaries" | ||
of ConsensusFork.Capella: | ||
ForkedHistoricalSummariesWithProof( | ||
kind: ConsensusFork.Capella, capellaData: historical_summaries | ||
) | ||
of ConsensusFork.Deneb: | ||
ForkedHistoricalSummariesWithProof( | ||
kind: ConsensusFork.Deneb, denebData: historical_summaries | ||
) | ||
of ConsensusFork.Electra: | ||
raiseAssert $fork & " fork should not be used for this type of historical summaries" | ||
of ConsensusFork.Fulu: | ||
raiseAssert $fork & " fork should not be used for this type of historical summaries" | ||
|
||
template init*( | ||
T: type ForkedHistoricalSummariesWithProof, | ||
historical_summaries: GetHistoricalSummariesV1ResponseElectra, | ||
fork: ConsensusFork, | ||
): T = | ||
case fork | ||
of ConsensusFork.Phase0: | ||
raiseAssert $fork & " fork should not be used for historical summaries" | ||
of ConsensusFork.Altair: | ||
raiseAssert $fork & " fork should not be used for historical summaries" | ||
of ConsensusFork.Bellatrix: | ||
raiseAssert $fork & " fork should not be used for historical summaries" | ||
of ConsensusFork.Capella: | ||
raiseAssert $fork & " fork should not be used for this type of historical summaries" | ||
of ConsensusFork.Deneb: | ||
raiseAssert $fork & " fork should not be used for this type of historical summaries" | ||
of ConsensusFork.Electra: | ||
ForkedHistoricalSummariesWithProof( | ||
kind: ConsensusFork.Electra, electraData: historical_summaries | ||
) | ||
of ConsensusFork.Fulu: | ||
ForkedHistoricalSummariesWithProof( | ||
kind: ConsensusFork.Fulu, fuluData: historical_summaries | ||
) | ||
|
||
template withForkyHistoricalSummariesWithProof*( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used anywhere? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is used in this PR: https://github.com/status-im/nimbus-eth1/pull/2775/files#diff-9f656396622d7567025b612acdc5da46a6a2b795b1346fd3ecf773de786417e4R255 The consumer of this API basically. But it isn't required functionality for the actual server side of the endpoint. |
||
x: ForkedHistoricalSummariesWithProof, body: untyped): untyped = | ||
case x.kind | ||
of ConsensusFork.Fulu: | ||
const consensusFork {.inject, used.} = ConsensusFork.Fulu | ||
template forkySummaries: untyped {.inject, used.} = x.fuluData | ||
body | ||
of ConsensusFork.Electra: | ||
const consensusFork {.inject, used.} = ConsensusFork.Electra | ||
template forkySummaries: untyped {.inject, used.} = x.electraData | ||
body | ||
of ConsensusFork.Deneb: | ||
const consensusFork {.inject, used.} = ConsensusFork.Deneb | ||
template forkySummaries: untyped {.inject, used.} = x.denebData | ||
body | ||
of ConsensusFork.Capella: | ||
const consensusFork {.inject, used.} = ConsensusFork.Capella | ||
template forkySummaries: untyped {.inject, used.} = x.capellaData | ||
body | ||
of ConsensusFork.Bellatrix: | ||
const consensusFork {.inject, used.} = ConsensusFork.Bellatrix | ||
body | ||
of ConsensusFork.Altair: | ||
const consensusFork {.inject, used.} = ConsensusFork.Altair | ||
body | ||
of ConsensusFork.Phase0: | ||
const consensusFork {.inject, used.} = ConsensusFork.Phase0 | ||
body |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went with different
array
s for the different proofs as this is for example also what is done forFinalityBranch
,CurrentSyncCommitteeBranch
andNextSyncCommitteeBranch
.But could in theory also just use a
seq[Eth2Digest]
which I think would make the wholeForkedHistoricalSummariesWithProof
helpers not required.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having it consistent with other endpoints is better than
seq
, even though it adds some boilerplate here. There is for every fork only a single correct proof length, and usingarray
guarantees that incorrect proof lengths are rejected early at deserialization time.The real solution would be to adopt EIP-7688... then there are no more random proof length changes for different consensusFork. It's a design flaw that every client has to keep maintaining gindices whenever ethereum decides to release some random functionality unrelated to the client's interests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree. But that is not in my sole hands here :)