Skip to content

Conversation

fernantho
Copy link
Contributor

@fernantho fernantho commented Oct 15, 2025

What type of PR is this?
Feature

Which issues(s) does this PR fix?
Partially #15598

What does this PR do? Why is it needed?
This PR develop a way to calculate the Generalized Indices of a given path within a SSZ Object. To do so, it follows Consensus Spec's Merkle proofs.

A conversion from PathElement to Generalized Index is necessary to work with fastssz proofs library.

The code implemented walks the path within the SSZInfo struct in a consensus layer spec way. We have to take into account that the "path" input is slightly different:

def get_generalized_index(typ: SSZType, *path: PyUnion[int, SSZVariableName]) -> GeneralizedIndex:
    """
    Converts a path (eg. `[7, "foo", 3]` for `x[7].foo[3]`, `[12, "bar", "__len__"]` for
    `len(x[12].bar)`) into the generalized index representing its position in the Merkle tree.
    """
// GetGeneralizedIndexFromPath calculates the generalized index for a given path.
// To calculate the generalized index, two inputs are needed:
// 1. The sszInfo of the root info, to be able to navigate the SSZ structure
// 2. The path to the field (e.g., "field_a.field_b[3].field_c")
// It walks the path step by step, updating the generalized index at each step.
func GetGeneralizedIndexFromPath(info *sszInfo, path []PathElement) (uint64, error) {

The pythonic version expects a Path input [FieldA,"FieldB",3] while the Go version expects field_a.field_b[3].

Other notes for review

  • At the beginning, I considered implementing an implementation using recursion but this approach was discarded because of the inputs, as we already have the PathElement array, we can just loop this array.
  • We did not have data format validation for the "raw path" string. We had stated that the input format should be snake case as the ssz generated type names are in snake case, but no validation was done, so it a different data input would fail at a later stage of the query. Taking this into account, I added an any-to-snake case conversion that runs at the beginning of the ParsePath func.
  • Generalized Indices computed in tests were gotten from a Python script that relies on the Consensus Layer spec: https://github.com/fernantho/generalized-indices-ground-truth/blob/main/generalized_indices.ipynb
  • Multidimensional arrays GI will be handled at a later iteration.

Acknowledgements

@fernantho fernantho marked this pull request as ready for review October 17, 2025 12:56
@fernantho fernantho force-pushed the feat/ssz-ql-parse-path-to-generalized-index branch from ce17749 to 73e3ee7 Compare October 21, 2025 08:02
fernantho and others added 4 commits October 21, 2025 12:27
…e with length >= 1

If s does not contain sep and sep is not empty, Split returns a slice of
length 1 whose only element is s.
@fernantho fernantho force-pushed the feat/ssz-ql-parse-path-to-generalized-index branch from c84b530 to 2718dc9 Compare October 21, 2025 13:06
Comment on lines 64 to 74
if element.Index != nil {
return 0, fmt.Errorf("len() is not supported for indexed elements (multi-dimensional arrays)")
}
// Length field is only valid for List and Bitlist types
if fieldSsz.sszType != List && fieldSsz.sszType != Bitlist {
return 0, fmt.Errorf("len() is only supported for List and Bitlist types, got %s", fieldSsz.sszType)
}
// Length is a uint64 per SSZ spec
currentInfo = &SszInfo{sszType: Uint64}
root = root*2 + 1
continue
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it possible to move this to a helper function?

Copy link
Contributor Author

@fernantho fernantho Oct 22, 2025

Choose a reason for hiding this comment

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

Regarding all these comments, yes. I really hate a switch clause without helpers 😅
In my experience with Go, I’ve dealt with a few protoactor Receive() handlers for message processing, and the pattern I liked the most were those that push as much logic as possible into helpers outside of the switch. That way, the different cases you’re dealing with are crystal clear.
In this case, I didn’t use helpers because I thought the cases were already clear, but maybe they only felt clear to me because I had just written the code. I’ll export all this logic to helpers so that new readers can also benefit from that clarity.

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.

3 participants