# EPF6 - Week 15 Updates
## tl;dr
- Merged one PR([#15725](https://github.com/OffchainLabs/prysm/pull/15725)) for handling `List` over `List`.
- Opened one PR([#15767](https://github.com/OffchainLabs/prysm/pull/15767)) that parses an index for accessing an element in `List`/`Vector`.
- Now we're almost ready! Section *"Implement the SSZ-QL engine."* in the [meta tracking issue](https://github.com/OffchainLabs/prysm/issues/15587) is done. We can move onto the next section, *Expose SSZ-QL as a new endpoint.*.
## Details
### Accesing n-th element in `List`/`Vector`
{%preview https://github.com/OffchainLabs/prysm/pull/15767 %}
So far, my SSZ-QL engine can only parse the full `List`/`Vector`, which means it's not possible to get each element on homogenous collection type. The PR above adds an accessor (by index) feature for `PathElement`. For example, we can now query with the path like `.validators[42]`.
```go=
// PathElement represents a single element in a path.
type PathElement struct {
Name string
// [Optional] Index for List/Vector elements
Index *uint64
}
```
Each `PathElement` might have `Index` as a member. It is the pointer of `uint64` which means it can be `nil` (and most of the case, it should be `nil` unless it is either `List` or `Vector`.).
```go=
// Check for accessing List/Vector elements by index
if elem.Index != nil {
switch walk.sszType {
case List:
index := *elem.Index
listInfo := walk.listInfo
if index >= listInfo.length {
return nil, 0, 0, fmt.Errorf("index %d out of bounds for field %s", index, elem.Name)
}
walk = listInfo.element
if walk.isVariable {
// Cumulative sum of sizes of previous elements to get the offset.
for i := range index {
offset += listInfo.elementSizes[i]
}
// NOTE: When populating recursively, the shared element template is updated for each
// list item, causing it to retain the size information of the last processed element.
// This wouldn't be an issue if this is in the middle of the path, as the walk would be updated
// to the next field's sszInfo, which would have the correct size information.
// However, if this is the last element in the path, we need to ensure we return the correct size
// for the indexed element. Hence, we return the size from elementSizes.
if pathIndex == len(path)-1 {
return walk, offset, listInfo.elementSizes[index], nil
}
} else {
offset += index * listInfo.element.Size()
}
case Vector:
index := *elem.Index
vectorInfo := walk.vectorInfo
if index >= vectorInfo.length {
return nil, 0, 0, fmt.Errorf("index %d out of bounds for field %s", index, elem.Name)
}
offset += index * vectorInfo.element.Size()
walk = vectorInfo.element
default:
return nil, 0, 0, fmt.Errorf("field %s type %s cannot apply index", elem.Name, walk.sszType)
}
}
```
Now in `CalculateOffsetAndLength` function, it checks whether `Index` is `nil` or not. If not, the current `walk` should be either `List`/`Vector`. For `Vector` case, it is quite easy: adding a multiple of each element's size on current `offset`.
However, for the case of `List`, it becomes sophisticated. We should check whether the element is variable-sized or not. Fortunately, we've saved all the size (in bytes) at `elementSizes` slice, so that we can sum up until we reaches the index.
I added a "hacky" part for the case If this is the last element of given path: As `elementSizes` is managed by `listInfo` instance, it is impossible for the element to know its size in its own. If the path ends with index accessing (e.g., `.validators[42]`), I thought it would be better to return with the correct byte sizes when it encounters the end of the path.
### Implementing a SSZ-QL engine is done!

In the [meta tracking issue](https://github.com/OffchainLabs/prysm/issues/15587), I divided my **must have** part into two. Provided the PR above get merged in a few days, I'm confident that the first part of **must have** (*"Implement the SSZ-QL engine"*) is DONE. Adding a couple of new endpoints shouldn't be a blocker, as the core feature is already implemented for last few weeks. 🚀