Repository: protolambda/remerkleable

Issue:
Error when computing `__len__` generalized index on `List` type
The following code fails when using the [`get_generalized_indices` function defined in the `consensus-specs`](https://github.com/ethereum/consensus-specs/blob/cdb6725275ea9aaa6d4a323c72be59cc34b458a3/pysetup/spec_builders/altair.py#L30):

```python
    index = get_generalized_index(List[uint8, 256], "__len__")
    print(index)
```

with:

```
    if key >= cls.limit():
TypeError: '>=' not supported between instances of 'str' and 'int'
```

from here: https://github.com/protolambda/remerkleable/blob/91ed092d08ef0ba5ab076f0a34b0b371623db728/remerkleable/complex.py#L464

I would think this method should be extended to avoid this check for the special key `__len__`. Happy to open a PR if this is the preferred route!

Discussion:

Patch:
diff --git a/remerkleable/bitfields.py b/remerkleable/bitfields.py
index 1434c8c..66b4e51 100644
--- a/remerkleable/bitfields.py
+++ b/remerkleable/bitfields.py
@@ -326,6 +326,8 @@ class Bitlist(BitsView):
 
     @classmethod
     def navigate_type(cls, key: Any) -> Type[View]:
+        if key == '__len__':
+            return uint256
         bit_limit = cls.limit()
         if key < 0 or key >= bit_limit:
             raise KeyError
diff --git a/remerkleable/complex.py b/remerkleable/complex.py
index 4574d16..5971925 100644
--- a/remerkleable/complex.py
+++ b/remerkleable/complex.py
@@ -461,6 +461,8 @@ class List(MonoSubtreeView):
 
     @classmethod
     def navigate_type(cls, key: Any) -> Type[View]:
+        if key == '__len__':
+            return uint256
         if key >= cls.limit():
             raise KeyError
         return super().navigate_type(key)


Test patch:
diff --git a/remerkleable/test_typing.py b/remerkleable/test_typing.py
index 4c8ee2a..6862414 100644
--- a/remerkleable/test_typing.py
+++ b/remerkleable/test_typing.py
@@ -347,12 +347,21 @@ def test_paths():
     assert (Wrapper / 'b' / 'quix').navigate_view(w) == 42
 
     assert (List[uint32, 123] / 0).navigate_type() == uint32
+    assert (List[uint32, 123] / "__len__").navigate_type() == uint256
     try:
         (List[uint32, 123] / 123).navigate_type()
         assert False
     except KeyError:
         pass
 
+    assert (Bitlist[123] / 0).navigate_type() == boolean
+    assert (Bitlist[123] / "__len__").navigate_type() == uint256
+    try:
+        (Bitlist[123] / 123).navigate_type()
+        assert False
+    except KeyError:
+        pass
+
 
 def test_bitvector():
     for size in [1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 17, 31, 32, 33, 63, 64, 65, 511, 512, 513, 1023, 1024, 1025]: