# EPF6 - Week 16 & 17 Updates ## tl;dr - Trying to use `Marshaler` interface in `fastssz`, so that we can remove several logics for `Size()` function. (draft [PR](https://github.com/syjn99/prysm/pull/6)) - Not related to this cohort, but I've been busy for a couple of weeks participating [PQ Workshop](https://x.com/corcoranwill/status/1974417509773353230) at Cambridge, UK. ## Details ```go! func (info *sszInfo) Size() uint64 { if info == nil { return 0 } // Easy case: if the type is not variable, we can return the fixed size. if !info.isVariable { return info.fixedSize } switch info.sszType { case List: return info.listInfo.Size() case Bitlist: return info.bitlistInfo.Size() case Container: panic("PLACEHOLDER PANIC IF THIS IS CALLED OR NOT") size := info.fixedSize for _, fieldInfo := range info.containerInfo.fields { if !fieldInfo.sszInfo.isVariable { continue } // Include offset bytes inside nested lists. if fieldInfo.sszInfo.sszType == List { size += fieldInfo.sszInfo.listInfo.OffsetBytes() } size += fieldInfo.sszInfo.Size() } return size default: return 0 } } ``` Current version has `Size` method that calculates the actual size of SSZ object in bytes. But my mentor suggested to leverage existing `fastssz` APIs like `SizeSSZ()`: ```go! // Marshaler is the interface implemented by types that can marshal themselves into valid SZZ. type Marshaler interface { MarshalSSZTo(dst []byte) ([]byte, error) MarshalSSZ() ([]byte, error) SizeSSZ() int } ``` `SizeSSZ()` is *automatically* generated with `sszgen` tool like: ```go! // SizeSSZ returns the ssz encoded size in bytes for the VariableNestedContainer object func (v *VariableNestedContainer) SizeSSZ() (size int) { size = 16 // Field (1) 'FieldListUint64' size += len(v.FieldListUint64) * 8 // Field (2) 'NestedListField' for ii := 0; ii < len(v.NestedListField); ii++ { size += 4 size += len(v.NestedListField[ii]) } return } ``` So it would be best if we can replace the whole `Size()` function like: ```go! func (info *sszInfo) Size() uint64 { if info == nil { return 0 } return uint64(info.source.SizeSSZ()) } ``` where `source` is the pointer to the original SSZ object. (e.g., `*VariableNestedContainer`) --- In the meantime, Nando was also trying to use `HashTreeRoot()` method that is also *automatically* generated in this [PR](https://github.com/OffchainLabs/prysm/pull/15805). I found we're facing same issue about using `fastssz` interfaces. We need to (somehow) store the **pointer** of SSZ object in `sszInfo` struct. Nando's approach was great to start this, by 1) accepting an interface `SSZObject` as an argument and 2) setting `source` in the function `AnalyzeObject`. ```go func AnalyzeObject(obj SSZObject) (*sszInfo, error) { // ... // Store the original object interface info.source = obj // ... } ``` I started with his idea and found that this approach has a limitation to **recursively** call `SizeSSZ()` method. `AnalyzeObject` is an entry function for building a new `sszInfo` instance, and the actual recursion is happening in `analyzeType` function: ```go! // analyzeType is an entry point that inspects a reflect.Type and computes its SSZ layout information. func analyzeType(typ reflect.Type, tag *reflect.StructTag) (*sszInfo, error) { switch typ.Kind() { // Basic types (e.g., uintN where N is 8, 16, 32, 64) // NOTE: uint128 and uint256 are represented as []byte in Go, // so we handle them as slices. See `analyzeHomogeneousColType`. case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Bool: return analyzeBasicType(typ) case reflect.Slice: return analyzeHomogeneousColType(typ, tag) case reflect.Struct: return analyzeContainerType(typ) case reflect.Ptr: // Dereference pointer types. return analyzeType(typ.Elem(), tag) default: return nil, fmt.Errorf("unsupported type %v for SSZ calculation", typ.Kind()) } } ``` {%preview https://github.com/syjn99/prysm/pull/6 %} Therefore, I'm trying to achieve **recursively store pointers in `sszInfo`** in the above PR. Playing with pointer and reflection is quite tough but I'm quite sure I found some breakthrough to resolve the issue that Radek raised a couple of weeks ago.