mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
SSZ-QL: Handle Vector type & Add SSZ tag parser for multi-dimensional parsing (#15668)
* Add vectorInfo * Add 2D bytes field for test * Add tag_parser for parsing SSZ tags * Integrate tag parser with analyzer * Add ByteList test case * Changelog * Better printing feature with Stringer implementation * Return error for non-determined case without printing other values * Update tag_parser.go: handle Vector and List mutually exclusive (inspired by OffchainLabs/fastssz) * Make linter happy --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
4
changelog/syjn99_ssz-ql-tag-parser.md
Normal file
4
changelog/syjn99_ssz-ql-tag-parser.md
Normal file
@@ -0,0 +1,4 @@
|
||||
### Added
|
||||
|
||||
- SSZ-QL: Add element information for `Vector` type.
|
||||
- SSZ-QL: Support multi-dimensional tag parsing.
|
||||
@@ -10,6 +10,8 @@ go_library(
|
||||
"query.go",
|
||||
"ssz_info.go",
|
||||
"ssz_type.go",
|
||||
"tag_parser.go",
|
||||
"vector.go",
|
||||
],
|
||||
importpath = "github.com/OffchainLabs/prysm/v6/encoding/ssz/query",
|
||||
visibility = ["//visibility:public"],
|
||||
@@ -21,6 +23,7 @@ go_test(
|
||||
"analyzer_test.go",
|
||||
"path_test.go",
|
||||
"query_test.go",
|
||||
"tag_parser_test.go",
|
||||
],
|
||||
deps = [
|
||||
":go_default_library",
|
||||
|
||||
@@ -4,20 +4,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
offsetBytes = 4
|
||||
|
||||
// sszMaxTag specifies the maximum capacity of a variable-sized collection, like an SSZ List.
|
||||
sszMaxTag = "ssz-max"
|
||||
|
||||
// sszSizeTag specifies the length of a fixed-sized collection, like an SSZ Vector.
|
||||
// A wildcard ('?') indicates that the dimension is variable-sized (a List).
|
||||
sszSizeTag = "ssz-size"
|
||||
)
|
||||
const offsetBytes = 4
|
||||
|
||||
// AnalyzeObject analyzes given object and returns its SSZ information.
|
||||
func AnalyzeObject(obj any) (*sszInfo, error) {
|
||||
@@ -176,46 +166,46 @@ func analyzeHomogeneousColType(typ reflect.Type, tag *reflect.StructTag) (*sszIn
|
||||
return nil, fmt.Errorf("can only analyze slice types, got %v", typ.Kind())
|
||||
}
|
||||
|
||||
if tag == nil {
|
||||
return nil, errors.New("tag is required for slice types")
|
||||
// Parse the first dimension from the tag and get remaining tag for element
|
||||
sszDimension, remainingTag, err := ParseSSZTag(tag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not parse SSZ tag: %w", err)
|
||||
}
|
||||
if sszDimension == nil {
|
||||
return nil, errors.New("ssz tag is required for slice types")
|
||||
}
|
||||
|
||||
elementInfo, err := analyzeType(typ.Elem(), nil)
|
||||
// Analyze element type with remaining dimensions
|
||||
elementInfo, err := analyzeType(typ.Elem(), remainingTag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not analyze element type for homogeneous collection: %w", err)
|
||||
}
|
||||
|
||||
// 1. Check if the type is List/Bitlist by checking `ssz-max` tag.
|
||||
sszMax := tag.Get(sszMaxTag)
|
||||
if sszMax != "" {
|
||||
dims := strings.Split(sszMax, ",")
|
||||
if len(dims) > 1 {
|
||||
return nil, fmt.Errorf("multi-dimensional lists are not supported, got %d dimensions", len(dims))
|
||||
}
|
||||
|
||||
limit, err := strconv.ParseUint(dims[0], 10, 64)
|
||||
// 1. Handle List/Bitlist type
|
||||
if sszDimension.IsList() {
|
||||
limit, err := sszDimension.GetListLimit()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid ssz-max tag (%s): %w", sszMax, err)
|
||||
return nil, fmt.Errorf("could not get list limit: %w", err)
|
||||
}
|
||||
|
||||
return analyzeListType(typ, elementInfo, limit)
|
||||
}
|
||||
|
||||
// 2. Handle Vector/Bitvector type.
|
||||
sszSize := tag.Get(sszSizeTag)
|
||||
dims := strings.Split(sszSize, ",")
|
||||
if len(dims) > 1 {
|
||||
return nil, fmt.Errorf("multi-dimensional vectors are not supported, got %d dimensions", len(dims))
|
||||
}
|
||||
|
||||
length, err := strconv.ParseUint(dims[0], 10, 64)
|
||||
// 2. Handle Vector/Bitvector type
|
||||
if sszDimension.IsVector() {
|
||||
length, err := sszDimension.GetVectorLength()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid ssz-size tag (%s): %w", sszSize, err)
|
||||
return nil, fmt.Errorf("could not get vector length: %w", err)
|
||||
}
|
||||
|
||||
return analyzeVectorType(typ, elementInfo, length)
|
||||
}
|
||||
|
||||
// Parsing ssz tag doesn't provide enough information to determine the collection type,
|
||||
// return an error.
|
||||
return nil, errors.New("could not determine collection type from tags")
|
||||
}
|
||||
|
||||
// analyzeListType analyzes SSZ List type and returns its SSZ info.
|
||||
func analyzeListType(typ reflect.Type, elementInfo *sszInfo, limit uint64) (*sszInfo, error) {
|
||||
if elementInfo == nil {
|
||||
@@ -242,12 +232,23 @@ func analyzeVectorType(typ reflect.Type, elementInfo *sszInfo, length uint64) (*
|
||||
return nil, errors.New("element info is required for Vector")
|
||||
}
|
||||
|
||||
// Validate the given length.
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/ssz/simple-serialize.md#illegal-types
|
||||
if length == 0 {
|
||||
return nil, fmt.Errorf("vector length must be greater than 0, got %d", length)
|
||||
}
|
||||
|
||||
return &sszInfo{
|
||||
sszType: Vector,
|
||||
typ: typ,
|
||||
|
||||
fixedSize: length * elementInfo.Size(),
|
||||
isVariable: false,
|
||||
|
||||
vectorInfo: &vectorInfo{
|
||||
length: length,
|
||||
element: elementInfo,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -13,5 +13,5 @@ func TestAnalyzeSSZInfo(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, info, "Expected non-nil SSZ info")
|
||||
require.Equal(t, uint64(333), info.FixedSize(), "Expected fixed size to be 333")
|
||||
require.Equal(t, uint64(493), info.FixedSize(), "Expected fixed size to be 333")
|
||||
}
|
||||
|
||||
@@ -73,11 +73,18 @@ func TestCalculateOffsetAndLength(t *testing.T) {
|
||||
expectedOffset: 85,
|
||||
expectedLength: 192, // 24 * 8 bytes
|
||||
},
|
||||
// 2D bytes field
|
||||
{
|
||||
name: "two_dimension_bytes_field",
|
||||
path: ".two_dimension_bytes_field",
|
||||
expectedOffset: 277,
|
||||
expectedLength: 160, // 5 * 32 bytes
|
||||
},
|
||||
// Trailing field
|
||||
{
|
||||
name: "trailing_field",
|
||||
path: ".trailing_field",
|
||||
expectedOffset: 277,
|
||||
expectedOffset: 437,
|
||||
expectedLength: 56,
|
||||
},
|
||||
}
|
||||
@@ -112,20 +119,26 @@ func TestCalculateOffsetAndLength(t *testing.T) {
|
||||
{
|
||||
name: "field_list_uint64",
|
||||
path: ".field_list_uint64",
|
||||
expectedOffset: 100, // First part of variable-sized type.
|
||||
expectedOffset: 104, // First part of variable-sized type.
|
||||
expectedLength: 40, // 5 elements * uint64 (8 bytes each)
|
||||
},
|
||||
{
|
||||
name: "field_list_container",
|
||||
path: ".field_list_container",
|
||||
expectedOffset: 140, // Second part of variable-sized type.
|
||||
expectedOffset: 144, // Second part of variable-sized type.
|
||||
expectedLength: 120, // 3 elements * FixedNestedContainer (40 bytes each)
|
||||
},
|
||||
{
|
||||
name: "field_list_bytes32",
|
||||
path: ".field_list_bytes32",
|
||||
expectedOffset: 264,
|
||||
expectedLength: 96, // 3 elements * 32 bytes each
|
||||
},
|
||||
// Nested paths
|
||||
{
|
||||
name: "nested",
|
||||
path: ".nested",
|
||||
expectedOffset: 260,
|
||||
expectedOffset: 360,
|
||||
// Calculated with:
|
||||
// - Value1: 8 bytes
|
||||
// - field_list_uint64 offset: 4 bytes
|
||||
@@ -135,20 +148,20 @@ func TestCalculateOffsetAndLength(t *testing.T) {
|
||||
{
|
||||
name: "nested.value1",
|
||||
path: ".nested.value1",
|
||||
expectedOffset: 260,
|
||||
expectedOffset: 360,
|
||||
expectedLength: 8,
|
||||
},
|
||||
{
|
||||
name: "nested.field_list_uint64",
|
||||
path: ".nested.field_list_uint64",
|
||||
expectedOffset: 272,
|
||||
expectedOffset: 372,
|
||||
expectedLength: 40,
|
||||
},
|
||||
// Fixed trailing field
|
||||
{
|
||||
name: "trailing_field",
|
||||
path: ".trailing_field",
|
||||
expectedOffset: 44, // After leading_field + 2 offset pointers
|
||||
expectedOffset: 48, // After leading_field + 4 offset pointers
|
||||
expectedLength: 56,
|
||||
},
|
||||
}
|
||||
@@ -220,6 +233,15 @@ func createFixedTestContainer() *sszquerypb.FixedTestContainer {
|
||||
// Vector field
|
||||
VectorField: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24},
|
||||
|
||||
// 2D bytes field
|
||||
TwoDimensionBytesField: [][]byte{
|
||||
make([]byte, 32),
|
||||
make([]byte, 32),
|
||||
make([]byte, 32),
|
||||
make([]byte, 32),
|
||||
make([]byte, 32),
|
||||
},
|
||||
|
||||
// Trailing field
|
||||
TrailingField: trailingField,
|
||||
}
|
||||
@@ -269,6 +291,11 @@ func getFixedTestContainerSpec() testutil.TestSpec {
|
||||
Path: ".vector_field",
|
||||
Expected: testContainer.VectorField,
|
||||
},
|
||||
// 2D bytes field
|
||||
{
|
||||
Path: ".two_dimension_bytes_field",
|
||||
Expected: testContainer.TwoDimensionBytesField,
|
||||
},
|
||||
// Trailing field
|
||||
{
|
||||
Path: ".trailing_field",
|
||||
@@ -308,6 +335,11 @@ func createVariableTestContainer() *sszquerypb.VariableTestContainer {
|
||||
// Variable-size lists
|
||||
FieldListUint64: []uint64{100, 200, 300, 400, 500},
|
||||
FieldListContainer: nestedContainers,
|
||||
FieldListBytes32: [][]byte{
|
||||
make([]byte, 32),
|
||||
make([]byte, 32),
|
||||
make([]byte, 32),
|
||||
},
|
||||
|
||||
// Variable nested container
|
||||
Nested: &sszquerypb.VariableNestedContainer{
|
||||
@@ -343,6 +375,11 @@ func getVariableTestContainerSpec() testutil.TestSpec {
|
||||
Path: ".field_list_container",
|
||||
Expected: testContainer.FieldListContainer,
|
||||
},
|
||||
// Variable-size list of bytes32
|
||||
{
|
||||
Path: ".field_list_bytes32",
|
||||
Expected: testContainer.FieldListBytes32,
|
||||
},
|
||||
// Variable nested container with every path
|
||||
{
|
||||
Path: ".nested",
|
||||
|
||||
@@ -24,6 +24,9 @@ type sszInfo struct {
|
||||
|
||||
// For List types.
|
||||
listInfo *listInfo
|
||||
|
||||
// For Vector types.
|
||||
vectorInfo *vectorInfo
|
||||
}
|
||||
|
||||
func (info *sszInfo) FixedSize() uint64 {
|
||||
@@ -95,6 +98,41 @@ func (info *sszInfo) ListInfo() (*listInfo, error) {
|
||||
return info.listInfo, nil
|
||||
}
|
||||
|
||||
func (info *sszInfo) VectorInfo() (*vectorInfo, error) {
|
||||
if info == nil {
|
||||
return nil, errors.New("sszInfo is nil")
|
||||
}
|
||||
|
||||
if info.sszType != Vector {
|
||||
return nil, fmt.Errorf("sszInfo is not a Vector type, got %s", info.sszType)
|
||||
}
|
||||
|
||||
return info.vectorInfo, nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface for sszInfo.
|
||||
// This follows the notation used in the consensus specs.
|
||||
func (info *sszInfo) String() string {
|
||||
if info == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
|
||||
switch info.sszType {
|
||||
case List:
|
||||
return fmt.Sprintf("List[%s, %d]", info.listInfo.element, info.listInfo.limit)
|
||||
case Vector:
|
||||
if info.vectorInfo.element.String() == "uint8" {
|
||||
// Handle byte vectors as BytesN
|
||||
// See Aliases section in SSZ spec:
|
||||
// https://github.com/ethereum/consensus-specs/blob/master/ssz/simple-serialize.md#aliases
|
||||
return fmt.Sprintf("Bytes%d", info.vectorInfo.length)
|
||||
}
|
||||
return fmt.Sprintf("Vector[%s, %d]", info.vectorInfo.element, info.vectorInfo.length)
|
||||
default:
|
||||
return info.typ.Name()
|
||||
}
|
||||
}
|
||||
|
||||
// Print returns a string representation of the sszInfo, which is useful for debugging.
|
||||
func (info *sszInfo) Print() string {
|
||||
if info == nil {
|
||||
@@ -115,7 +153,7 @@ func printRecursive(info *sszInfo, builder *strings.Builder, prefix string) {
|
||||
|
||||
switch info.sszType {
|
||||
case Container:
|
||||
builder.WriteString(fmt.Sprintf("%s: %s (%s / fixed size: %d, total size: %d)\n", info.sszType, info.typ.Name(), sizeDesc, info.FixedSize(), info.Size()))
|
||||
builder.WriteString(fmt.Sprintf("%s (%s / fixed size: %d, total size: %d)\n", info, sizeDesc, info.FixedSize(), info.Size()))
|
||||
|
||||
for i, key := range info.containerInfo.order {
|
||||
connector := "├─"
|
||||
@@ -135,9 +173,9 @@ func printRecursive(info *sszInfo, builder *strings.Builder, prefix string) {
|
||||
}
|
||||
|
||||
case List:
|
||||
builder.WriteString(fmt.Sprintf("%s[%s] (%s / limit: %d, length: %d, size: %d)\n", info.sszType, info.listInfo.element.typ.Name(), sizeDesc, info.listInfo.limit, info.listInfo.length, info.Size()))
|
||||
builder.WriteString(fmt.Sprintf("%s (%s / length: %d, size: %d)\n", info, sizeDesc, info.listInfo.length, info.Size()))
|
||||
|
||||
default:
|
||||
builder.WriteString(fmt.Sprintf("%s (%s / size: %d)\n", info.sszType, sizeDesc, info.Size()))
|
||||
builder.WriteString(fmt.Sprintf("%s (%s / size: %d)\n", info, sizeDesc, info.Size()))
|
||||
}
|
||||
}
|
||||
|
||||
130
encoding/ssz/query/tag_parser.go
Normal file
130
encoding/ssz/query/tag_parser.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// sszMaxTag specifies the maximum capacity of a variable-sized collection, like an SSZ List.
|
||||
sszMaxTag = "ssz-max"
|
||||
|
||||
// sszSizeTag specifies the length of a fixed-sized collection, like an SSZ Vector.
|
||||
// A wildcard ('?') indicates that the dimension is variable-sized (a List).
|
||||
sszSizeTag = "ssz-size"
|
||||
)
|
||||
|
||||
// SSZDimension holds parsed SSZ tag information for current dimension.
|
||||
// Mutually exclusive fields indicate whether the dimension is a vector or a list.
|
||||
type SSZDimension struct {
|
||||
vectorLength *uint64
|
||||
listLimit *uint64
|
||||
}
|
||||
|
||||
// ParseSSZTag parses SSZ-specific tags (like `ssz-max` and `ssz-size`)
|
||||
// and returns the first dimension and the remaining SSZ tags.
|
||||
// This function validates the tags and returns an error if they are malformed.
|
||||
func ParseSSZTag(tag *reflect.StructTag) (*SSZDimension, *reflect.StructTag, error) {
|
||||
if tag == nil {
|
||||
return nil, nil, errors.New("nil struct tag")
|
||||
}
|
||||
|
||||
var newTagParts []string
|
||||
var sizeStr, maxStr string
|
||||
|
||||
// Parse ssz-size tag
|
||||
if sszSize := tag.Get(sszSizeTag); sszSize != "" {
|
||||
dims := strings.Split(sszSize, ",")
|
||||
if len(dims) > 0 {
|
||||
sizeStr = dims[0]
|
||||
|
||||
if len(dims) > 1 {
|
||||
remainingSize := strings.Join(dims[1:], ",")
|
||||
newTagParts = append(newTagParts, fmt.Sprintf(`%s:"%s"`, sszSizeTag, remainingSize))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse ssz-max tag
|
||||
if sszMax := tag.Get(sszMaxTag); sszMax != "" {
|
||||
dims := strings.Split(sszMax, ",")
|
||||
if len(dims) > 0 {
|
||||
maxStr = dims[0]
|
||||
|
||||
if len(dims) > 1 {
|
||||
remainingMax := strings.Join(dims[1:], ",")
|
||||
newTagParts = append(newTagParts, fmt.Sprintf(`%s:"%s"`, sszMaxTag, remainingMax))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create new tag with remaining dimensions only.
|
||||
// We don't have to preserve other tags like json, protobuf.
|
||||
var newTag *reflect.StructTag
|
||||
if len(newTagParts) > 0 {
|
||||
newTagStr := strings.Join(newTagParts, " ")
|
||||
t := reflect.StructTag(newTagStr)
|
||||
newTag = &t
|
||||
}
|
||||
|
||||
// Parse the first dimension based on ssz-size and ssz-max rules.
|
||||
// 1. If ssz-size is not specified (wildcard or empty), it must be a list.
|
||||
if sizeStr == "?" || sizeStr == "" {
|
||||
if maxStr == "?" {
|
||||
return nil, nil, errors.New("ssz-size and ssz-max cannot both be '?'")
|
||||
}
|
||||
if maxStr == "" {
|
||||
return nil, nil, errors.New("list requires ssz-max value")
|
||||
}
|
||||
|
||||
limit, err := strconv.ParseUint(maxStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid ssz-max value: %w", err)
|
||||
}
|
||||
if limit == 0 {
|
||||
return nil, nil, errors.New("ssz-max must be greater than 0")
|
||||
}
|
||||
|
||||
return &SSZDimension{listLimit: &limit}, newTag, nil
|
||||
}
|
||||
|
||||
// 2. If ssz-size is specified, it must be a vector.
|
||||
length, err := strconv.ParseUint(sizeStr, 10, 64)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid ssz-size value: %w", err)
|
||||
}
|
||||
if length == 0 {
|
||||
return nil, nil, errors.New("ssz-size must be greater than 0")
|
||||
}
|
||||
|
||||
return &SSZDimension{vectorLength: &length}, newTag, nil
|
||||
}
|
||||
|
||||
// IsVector returns true if this dimension represents a vector.
|
||||
func (d *SSZDimension) IsVector() bool {
|
||||
return d.vectorLength != nil
|
||||
}
|
||||
|
||||
// IsList returns true if this dimension represents a list.
|
||||
func (d *SSZDimension) IsList() bool {
|
||||
return d.listLimit != nil
|
||||
}
|
||||
|
||||
// GetVectorLength returns the length for a vector in current dimension
|
||||
func (d *SSZDimension) GetVectorLength() (uint64, error) {
|
||||
if !d.IsVector() {
|
||||
return 0, errors.New("not a vector dimension")
|
||||
}
|
||||
return *d.vectorLength, nil
|
||||
}
|
||||
|
||||
// GetListLimit returns the limit for a list in current dimension
|
||||
func (d *SSZDimension) GetListLimit() (uint64, error) {
|
||||
if !d.IsList() {
|
||||
return 0, errors.New("not a list dimension")
|
||||
}
|
||||
return *d.listLimit, nil
|
||||
}
|
||||
187
encoding/ssz/query/tag_parser_test.go
Normal file
187
encoding/ssz/query/tag_parser_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package query_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v6/encoding/ssz/query"
|
||||
"github.com/OffchainLabs/prysm/v6/testing/require"
|
||||
)
|
||||
|
||||
func TestParseSSZTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
wantErr bool
|
||||
wantIsList bool
|
||||
wantIsVector bool
|
||||
wantListLimit uint64
|
||||
wantVectorLength uint64
|
||||
wantRemainingTag string
|
||||
tag string
|
||||
name string
|
||||
}{
|
||||
// Vector tests
|
||||
{
|
||||
name: "single dimension vector",
|
||||
tag: `ssz-size:"32"`,
|
||||
wantIsVector: true,
|
||||
wantVectorLength: 32,
|
||||
},
|
||||
{
|
||||
name: "multi-dimensional vector",
|
||||
tag: `ssz-size:"5,32"`,
|
||||
wantIsVector: true,
|
||||
wantVectorLength: 5,
|
||||
wantRemainingTag: `ssz-size:"32"`,
|
||||
},
|
||||
{
|
||||
name: "three-dimensional vector",
|
||||
tag: `ssz-size:"5,10,32"`,
|
||||
wantIsVector: true,
|
||||
wantVectorLength: 5,
|
||||
wantRemainingTag: `ssz-size:"10,32"`,
|
||||
},
|
||||
{
|
||||
name: "large vector",
|
||||
tag: `ssz-size:"1048576"`,
|
||||
wantIsVector: true,
|
||||
wantVectorLength: 1048576,
|
||||
},
|
||||
|
||||
// List tests
|
||||
{
|
||||
name: "single dimension list",
|
||||
tag: `ssz-max:"100"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 100,
|
||||
},
|
||||
{
|
||||
name: "multi-dimensional list",
|
||||
tag: `ssz-max:"100,200"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 100,
|
||||
wantRemainingTag: `ssz-max:"200"`,
|
||||
},
|
||||
{
|
||||
name: "large list",
|
||||
tag: `ssz-max:"1048576"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 1048576,
|
||||
},
|
||||
{
|
||||
name: "wildcard size becomes list",
|
||||
tag: `ssz-size:"?" ssz-max:"100"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 100,
|
||||
},
|
||||
{
|
||||
name: "wildcard with remaining dimensions",
|
||||
tag: `ssz-size:"?,32" ssz-max:"100"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 100,
|
||||
wantRemainingTag: `ssz-size:"32"`,
|
||||
},
|
||||
{
|
||||
name: "empty size becomes list",
|
||||
tag: `ssz-size:"" ssz-max:"100"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 100,
|
||||
},
|
||||
{
|
||||
name: "list of vectors",
|
||||
tag: `ssz-size:"?,32" ssz-max:"100"`,
|
||||
wantIsList: true,
|
||||
wantListLimit: 100,
|
||||
wantRemainingTag: `ssz-size:"32"`,
|
||||
},
|
||||
|
||||
// Error cases
|
||||
{
|
||||
name: "empty tag",
|
||||
tag: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "zero vector length",
|
||||
tag: `ssz-size:"0"`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "zero list limit",
|
||||
tag: `ssz-max:"0"`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid vector length",
|
||||
tag: `ssz-size:"abc"`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid list limit",
|
||||
tag: `ssz-max:"xyz"`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "both wildcard",
|
||||
tag: `ssz-size:"?" ssz-max:"?"`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list without max",
|
||||
tag: `ssz-size:"?"`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var tag *reflect.StructTag
|
||||
if tt.tag != "" {
|
||||
structTag := reflect.StructTag(tt.tag)
|
||||
tag = &structTag
|
||||
}
|
||||
|
||||
dim, remainingTag, err := query.ParseSSZTag(tag)
|
||||
if tt.wantErr {
|
||||
require.NotNil(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, dim)
|
||||
|
||||
// Check dimension type
|
||||
require.Equal(t, tt.wantIsVector, dim.IsVector())
|
||||
require.Equal(t, tt.wantIsList, dim.IsList())
|
||||
|
||||
// Verify vector length if it's a vector
|
||||
if tt.wantIsVector {
|
||||
length, err := dim.GetVectorLength()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.wantVectorLength, length)
|
||||
|
||||
// Trying to get list limit should error
|
||||
_, err = dim.GetListLimit()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
// Verify list limit if it's a list
|
||||
if tt.wantIsList {
|
||||
limit, err := dim.GetListLimit()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.wantListLimit, limit)
|
||||
|
||||
// Trying to get vector length should error
|
||||
_, err = dim.GetVectorLength()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
// Check remaining tag
|
||||
if tt.wantRemainingTag == "" {
|
||||
require.Equal(t, remainingTag == nil, true)
|
||||
} else {
|
||||
require.NotNil(t, remainingTag)
|
||||
require.Equal(t, tt.wantRemainingTag, string(*remainingTag))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
27
encoding/ssz/query/vector.go
Normal file
27
encoding/ssz/query/vector.go
Normal file
@@ -0,0 +1,27 @@
|
||||
package query
|
||||
|
||||
import "errors"
|
||||
|
||||
// vectorInfo holds information about a SSZ Vector type.
|
||||
type vectorInfo struct {
|
||||
// element is the SSZ info of the vector's element type.
|
||||
element *sszInfo
|
||||
// length is the fixed length of the vector.
|
||||
length uint64
|
||||
}
|
||||
|
||||
func (v *vectorInfo) Length() uint64 {
|
||||
if v == nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
return v.length
|
||||
}
|
||||
|
||||
func (v *vectorInfo) Element() (*sszInfo, error) {
|
||||
if v == nil {
|
||||
return nil, errors.New("vectorInfo is nil")
|
||||
}
|
||||
|
||||
return v.element, nil
|
||||
}
|
||||
99
proto/ssz_query/ssz_query.pb.go
generated
99
proto/ssz_query/ssz_query.pb.go
generated
@@ -88,7 +88,8 @@ type FixedTestContainer struct {
|
||||
FieldBytes32 []byte `protobuf:"bytes,8,opt,name=field_bytes32,json=fieldBytes32,proto3" json:"field_bytes32,omitempty" ssz-size:"32"`
|
||||
Nested *FixedNestedContainer `protobuf:"bytes,9,opt,name=nested,proto3" json:"nested,omitempty"`
|
||||
VectorField []uint64 `protobuf:"varint,10,rep,packed,name=vector_field,json=vectorField,proto3" json:"vector_field,omitempty" ssz-size:"24"`
|
||||
TrailingField []byte `protobuf:"bytes,11,opt,name=trailing_field,json=trailingField,proto3" json:"trailing_field,omitempty" ssz-size:"56"`
|
||||
TwoDimensionBytesField [][]byte `protobuf:"bytes,11,rep,name=two_dimension_bytes_field,json=twoDimensionBytesField,proto3" json:"two_dimension_bytes_field,omitempty" ssz-size:"5,32"`
|
||||
TrailingField []byte `protobuf:"bytes,12,opt,name=trailing_field,json=trailingField,proto3" json:"trailing_field,omitempty" ssz-size:"56"`
|
||||
}
|
||||
|
||||
func (x *FixedTestContainer) Reset() {
|
||||
@@ -165,6 +166,13 @@ func (x *FixedTestContainer) GetVectorField() []uint64 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *FixedTestContainer) GetTwoDimensionBytesField() [][]byte {
|
||||
if x != nil {
|
||||
return x.TwoDimensionBytesField
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *FixedTestContainer) GetTrailingField() []byte {
|
||||
if x != nil {
|
||||
return x.TrailingField
|
||||
@@ -235,8 +243,9 @@ type VariableTestContainer struct {
|
||||
LeadingField []byte `protobuf:"bytes,1,opt,name=leading_field,json=leadingField,proto3" json:"leading_field,omitempty" ssz-size:"32"`
|
||||
FieldListUint64 []uint64 `protobuf:"varint,2,rep,packed,name=field_list_uint64,json=fieldListUint64,proto3" json:"field_list_uint64,omitempty" ssz-max:"2048"`
|
||||
FieldListContainer []*FixedNestedContainer `protobuf:"bytes,3,rep,name=field_list_container,json=fieldListContainer,proto3" json:"field_list_container,omitempty" ssz-max:"128"`
|
||||
Nested *VariableNestedContainer `protobuf:"bytes,4,opt,name=nested,proto3" json:"nested,omitempty"`
|
||||
TrailingField []byte `protobuf:"bytes,5,opt,name=trailing_field,json=trailingField,proto3" json:"trailing_field,omitempty" ssz-size:"56"`
|
||||
FieldListBytes32 [][]byte `protobuf:"bytes,4,rep,name=field_list_bytes32,json=fieldListBytes32,proto3" json:"field_list_bytes32,omitempty" ssz-max:"100" ssz-size:"?,32"`
|
||||
Nested *VariableNestedContainer `protobuf:"bytes,5,opt,name=nested,proto3" json:"nested,omitempty"`
|
||||
TrailingField []byte `protobuf:"bytes,6,opt,name=trailing_field,json=trailingField,proto3" json:"trailing_field,omitempty" ssz-size:"56"`
|
||||
}
|
||||
|
||||
func (x *VariableTestContainer) Reset() {
|
||||
@@ -292,6 +301,13 @@ func (x *VariableTestContainer) GetFieldListContainer() []*FixedNestedContainer
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VariableTestContainer) GetFieldListBytes32() [][]byte {
|
||||
if x != nil {
|
||||
return x.FieldListBytes32
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *VariableTestContainer) GetNested() *VariableNestedContainer {
|
||||
if x != nil {
|
||||
return x.Nested
|
||||
@@ -318,7 +334,7 @@ var file_proto_ssz_query_ssz_query_proto_rawDesc = []byte{
|
||||
0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x04, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x12, 0x1e, 0x0a, 0x06, 0x76, 0x61, 0x6c,
|
||||
0x75, 0x65, 0x32, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33,
|
||||
0x32, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x22, 0xb9, 0x02, 0x0a, 0x12, 0x46, 0x69,
|
||||
0x32, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x22, 0xfe, 0x02, 0x0a, 0x12, 0x46, 0x69,
|
||||
0x78, 0x65, 0x64, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
|
||||
0x12, 0x21, 0x0a, 0x0c, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x33, 0x32,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x55, 0x69, 0x6e,
|
||||
@@ -335,40 +351,49 @@ var file_proto_ssz_query_ssz_query_proto_rawDesc = []byte{
|
||||
0x6e, 0x65, 0x72, 0x52, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x29, 0x0a, 0x0c, 0x76,
|
||||
0x65, 0x63, 0x74, 0x6f, 0x72, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x0a, 0x20, 0x03, 0x28,
|
||||
0x04, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x32, 0x34, 0x52, 0x0b, 0x76, 0x65, 0x63, 0x74, 0x6f,
|
||||
0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69,
|
||||
0x6e, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06,
|
||||
0x72, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x43, 0x0a, 0x19, 0x74, 0x77, 0x6f, 0x5f, 0x64, 0x69,
|
||||
0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x66, 0x69,
|
||||
0x65, 0x6c, 0x64, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x08, 0x8a, 0xb5, 0x18, 0x04, 0x35,
|
||||
0x2c, 0x33, 0x32, 0x52, 0x16, 0x74, 0x77, 0x6f, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f,
|
||||
0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x2d, 0x0a, 0x0e, 0x74,
|
||||
0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x0c, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x35, 0x36, 0x52, 0x0d, 0x74, 0x72, 0x61,
|
||||
0x69, 0x6c, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x66, 0x0a, 0x17, 0x56, 0x61,
|
||||
0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74,
|
||||
0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x18,
|
||||
0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x12, 0x33, 0x0a,
|
||||
0x11, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x75, 0x69, 0x6e, 0x74,
|
||||
0x36, 0x34, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x30,
|
||||
0x30, 0x52, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x69, 0x6e, 0x74,
|
||||
0x36, 0x34, 0x22, 0x80, 0x03, 0x0a, 0x15, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x54,
|
||||
0x65, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0d,
|
||||
0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0c, 0x6c, 0x65, 0x61,
|
||||
0x64, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x12, 0x34, 0x0a, 0x11, 0x66, 0x69, 0x65,
|
||||
0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x02,
|
||||
0x20, 0x03, 0x28, 0x04, 0x42, 0x08, 0x92, 0xb5, 0x18, 0x04, 0x32, 0x30, 0x34, 0x38, 0x52, 0x0f,
|
||||
0x66, 0x69, 0x65, 0x6c, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12,
|
||||
0x5a, 0x0a, 0x14, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x6f,
|
||||
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
|
||||
0x73, 0x73, 0x7a, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4e,
|
||||
0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x07,
|
||||
0x92, 0xb5, 0x18, 0x03, 0x31, 0x32, 0x38, 0x52, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4c, 0x69,
|
||||
0x73, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x3d, 0x0a, 0x12, 0x66,
|
||||
0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x33,
|
||||
0x32, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x0f, 0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x33,
|
||||
0x32, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x30, 0x30, 0x52, 0x10, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4c,
|
||||
0x69, 0x73, 0x74, 0x42, 0x79, 0x74, 0x65, 0x73, 0x33, 0x32, 0x12, 0x3a, 0x0a, 0x06, 0x6e, 0x65,
|
||||
0x73, 0x74, 0x65, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x73, 0x7a,
|
||||
0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4e,
|
||||
0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x06,
|
||||
0x6e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x12, 0x2d, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69,
|
||||
0x6e, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06,
|
||||
0x8a, 0xb5, 0x18, 0x02, 0x35, 0x36, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67,
|
||||
0x46, 0x69, 0x65, 0x6c, 0x64, 0x22, 0x66, 0x0a, 0x17, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
|
||||
0x65, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72,
|
||||
0x12, 0x16, 0x0a, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
|
||||
0x52, 0x06, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x12, 0x33, 0x0a, 0x11, 0x66, 0x69, 0x65, 0x6c,
|
||||
0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x02, 0x20,
|
||||
0x03, 0x28, 0x04, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31, 0x30, 0x30, 0x52, 0x0f, 0x66, 0x69,
|
||||
0x65, 0x6c, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x22, 0xc1, 0x02,
|
||||
0x0a, 0x15, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x54, 0x65, 0x73, 0x74, 0x43, 0x6f,
|
||||
0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x2b, 0x0a, 0x0d, 0x6c, 0x65, 0x61, 0x64, 0x69,
|
||||
0x6e, 0x67, 0x5f, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06,
|
||||
0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x46,
|
||||
0x69, 0x65, 0x6c, 0x64, 0x12, 0x34, 0x0a, 0x11, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x5f, 0x6c, 0x69,
|
||||
0x73, 0x74, 0x5f, 0x75, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x42,
|
||||
0x08, 0x92, 0xb5, 0x18, 0x04, 0x32, 0x30, 0x34, 0x38, 0x52, 0x0f, 0x66, 0x69, 0x65, 0x6c, 0x64,
|
||||
0x4c, 0x69, 0x73, 0x74, 0x55, 0x69, 0x6e, 0x74, 0x36, 0x34, 0x12, 0x5a, 0x0a, 0x14, 0x66, 0x69,
|
||||
0x65, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
|
||||
0x65, 0x72, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x73, 0x73, 0x7a, 0x5f, 0x71,
|
||||
0x75, 0x65, 0x72, 0x79, 0x2e, 0x46, 0x69, 0x78, 0x65, 0x64, 0x4e, 0x65, 0x73, 0x74, 0x65, 0x64,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x42, 0x07, 0x92, 0xb5, 0x18, 0x03, 0x31,
|
||||
0x32, 0x38, 0x52, 0x12, 0x66, 0x69, 0x65, 0x6c, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e,
|
||||
0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x3a, 0x0a, 0x06, 0x6e, 0x65, 0x73, 0x74, 0x65, 0x64,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x73, 0x73, 0x7a, 0x5f, 0x71, 0x75, 0x65,
|
||||
0x72, 0x79, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x4e, 0x65, 0x73, 0x74, 0x65,
|
||||
0x64, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x52, 0x06, 0x6e, 0x65, 0x73, 0x74,
|
||||
0x65, 0x64, 0x12, 0x2d, 0x0a, 0x0e, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x5f, 0x66,
|
||||
0x69, 0x65, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02,
|
||||
0x35, 0x36, 0x52, 0x0d, 0x74, 0x72, 0x61, 0x69, 0x6c, 0x69, 0x6e, 0x67, 0x46, 0x69, 0x65, 0x6c,
|
||||
0x64, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f,
|
||||
0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79,
|
||||
0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x73, 0x73, 0x7a, 0x5f,
|
||||
0x71, 0x75, 0x65, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x46, 0x69, 0x65, 0x6c, 0x64, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
|
||||
0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73,
|
||||
0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
|
||||
0x73, 0x73, 0x7a, 0x5f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
|
||||
0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@@ -20,7 +20,7 @@ message FixedNestedContainer {
|
||||
|
||||
// FixedTestContainer - comprehensive fixed-size container for SSZ query testing
|
||||
// Tests: All basic fixed-size SSZ types, nested containers, vectors, offset/length calculations
|
||||
// Total size: 333 bytes (4+8+1+32+40+192+56)
|
||||
// Total size: 493 bytes (4+8+1+32+40+192+160+56)
|
||||
message FixedTestContainer {
|
||||
// Basic integer types - test different integer sizes and their SSZ serialization
|
||||
uint32 field_uint32 = 3; // Test: uint32 basic type, offset: 0
|
||||
@@ -38,8 +38,11 @@ message FixedTestContainer {
|
||||
// Vector type - test fixed-size array of basic elements
|
||||
repeated uint64 vector_field = 10 [ (ethereum.eth.ext.ssz_size) = "24" ]; // Test: Vector[24] of uint64 (24*8=192 bytes), offset: 85
|
||||
|
||||
// 2D bytes type - test 2-dimensional byte arrays, common in blockchain state roots (e.g., beacon_state.block_roots)
|
||||
repeated bytes two_dimension_bytes_field = 11 [ (ethereum.eth.ext.ssz_size) = "5,32" ]; // Test: Vector[Bytes32, 5] (32*5=160 bytes), offset: 277
|
||||
|
||||
// Additional bytes field - test field ordering and offset calculation
|
||||
bytes trailing_field = 11 [ (ethereum.eth.ext.ssz_size) = "56" ]; // Test: trailing field after vector, offset: 277
|
||||
bytes trailing_field = 12 [ (ethereum.eth.ext.ssz_size) = "56" ]; // Test: trailing field after vector, offset: 437
|
||||
}
|
||||
|
||||
// ===== VARIABLE-SIZE TEST CONTAINERS =====
|
||||
@@ -66,10 +69,14 @@ message VariableTestContainer {
|
||||
// Each container is fixed-size (40 bytes), but list itself is variable
|
||||
repeated FixedNestedContainer field_list_container = 3 [ (ethereum.eth.ext.ssz_max) = "128" ]; // Test: List[FixedNestedContainer, 128]
|
||||
|
||||
// Variable-size list of bytes32 - test list with fixed-size byte arrays
|
||||
// e.g., beacon_state.historical_roots
|
||||
repeated bytes field_list_bytes32 = 4 [ (ethereum.eth.ext.ssz_size) = "?,32", (ethereum.eth.ext.ssz_max) = "100"]; // Test: List[Bytes32, 100]
|
||||
|
||||
// Variable nested container - test nested container access within variable container
|
||||
VariableNestedContainer nested = 4;
|
||||
VariableNestedContainer nested = 5;
|
||||
|
||||
// Fixed-size trailing field - test fixed field after variable fields
|
||||
// Verifies correct offset calculation after variable-size fields
|
||||
bytes trailing_field = 5 [ (ethereum.eth.ext.ssz_size) = "56" ]; // Test: fixed 56-byte field at end, offset: 32 + 4 + 4 + 4 = 44
|
||||
bytes trailing_field = 6 [ (ethereum.eth.ext.ssz_size) = "56" ]; // Test: fixed 56-byte field at end, offset: 32 + 4 + 4 + 4 + 4 = 48
|
||||
}
|
||||
|
||||
@@ -118,7 +118,20 @@ func (f *FixedTestContainer) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = ssz.MarshalUint64(dst, f.VectorField[ii])
|
||||
}
|
||||
|
||||
// Field (6) 'TrailingField'
|
||||
// Field (6) 'TwoDimensionBytesField'
|
||||
if size := len(f.TwoDimensionBytesField); size != 5 {
|
||||
err = ssz.ErrVectorLengthFn("--.TwoDimensionBytesField", size, 5)
|
||||
return
|
||||
}
|
||||
for ii := 0; ii < 5; ii++ {
|
||||
if size := len(f.TwoDimensionBytesField[ii]); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("--.TwoDimensionBytesField[ii]", size, 32)
|
||||
return
|
||||
}
|
||||
dst = append(dst, f.TwoDimensionBytesField[ii]...)
|
||||
}
|
||||
|
||||
// Field (7) 'TrailingField'
|
||||
if size := len(f.TrailingField); size != 56 {
|
||||
err = ssz.ErrBytesLengthFn("--.TrailingField", size, 56)
|
||||
return
|
||||
@@ -132,7 +145,7 @@ func (f *FixedTestContainer) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
func (f *FixedTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size != 333 {
|
||||
if size != 493 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
@@ -168,18 +181,27 @@ func (f *FixedTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
f.VectorField[ii] = ssz.UnmarshallUint64(buf[85:277][ii*8 : (ii+1)*8])
|
||||
}
|
||||
|
||||
// Field (6) 'TrailingField'
|
||||
if cap(f.TrailingField) == 0 {
|
||||
f.TrailingField = make([]byte, 0, len(buf[277:333]))
|
||||
// Field (6) 'TwoDimensionBytesField'
|
||||
f.TwoDimensionBytesField = make([][]byte, 5)
|
||||
for ii := 0; ii < 5; ii++ {
|
||||
if cap(f.TwoDimensionBytesField[ii]) == 0 {
|
||||
f.TwoDimensionBytesField[ii] = make([]byte, 0, len(buf[277:437][ii*32:(ii+1)*32]))
|
||||
}
|
||||
f.TrailingField = append(f.TrailingField, buf[277:333]...)
|
||||
f.TwoDimensionBytesField[ii] = append(f.TwoDimensionBytesField[ii], buf[277:437][ii*32:(ii+1)*32]...)
|
||||
}
|
||||
|
||||
// Field (7) 'TrailingField'
|
||||
if cap(f.TrailingField) == 0 {
|
||||
f.TrailingField = make([]byte, 0, len(buf[437:493]))
|
||||
}
|
||||
f.TrailingField = append(f.TrailingField, buf[437:493]...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the FixedTestContainer object
|
||||
func (f *FixedTestContainer) SizeSSZ() (size int) {
|
||||
size = 333
|
||||
size = 493
|
||||
return
|
||||
}
|
||||
|
||||
@@ -226,7 +248,24 @@ func (f *FixedTestContainer) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
hh.Merkleize(subIndx)
|
||||
}
|
||||
|
||||
// Field (6) 'TrailingField'
|
||||
// Field (6) 'TwoDimensionBytesField'
|
||||
{
|
||||
if size := len(f.TwoDimensionBytesField); size != 5 {
|
||||
err = ssz.ErrVectorLengthFn("--.TwoDimensionBytesField", size, 5)
|
||||
return
|
||||
}
|
||||
subIndx := hh.Index()
|
||||
for _, i := range f.TwoDimensionBytesField {
|
||||
if len(i) != 32 {
|
||||
err = ssz.ErrBytesLength
|
||||
return
|
||||
}
|
||||
hh.Append(i)
|
||||
}
|
||||
hh.Merkleize(subIndx)
|
||||
}
|
||||
|
||||
// Field (7) 'TrailingField'
|
||||
if size := len(f.TrailingField); size != 56 {
|
||||
err = ssz.ErrBytesLengthFn("--.TrailingField", size, 56)
|
||||
return
|
||||
@@ -354,7 +393,7 @@ func (v *VariableTestContainer) MarshalSSZ() ([]byte, error) {
|
||||
// MarshalSSZTo ssz marshals the VariableTestContainer object to a target array
|
||||
func (v *VariableTestContainer) MarshalSSZTo(buf []byte) (dst []byte, err error) {
|
||||
dst = buf
|
||||
offset := int(100)
|
||||
offset := int(104)
|
||||
|
||||
// Field (0) 'LeadingField'
|
||||
if size := len(v.LeadingField); size != 32 {
|
||||
@@ -371,14 +410,18 @@ func (v *VariableTestContainer) MarshalSSZTo(buf []byte) (dst []byte, err error)
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(v.FieldListContainer) * 40
|
||||
|
||||
// Offset (3) 'Nested'
|
||||
// Offset (3) 'FieldListBytes32'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
offset += len(v.FieldListBytes32) * 32
|
||||
|
||||
// Offset (4) 'Nested'
|
||||
dst = ssz.WriteOffset(dst, offset)
|
||||
if v.Nested == nil {
|
||||
v.Nested = new(VariableNestedContainer)
|
||||
}
|
||||
offset += v.Nested.SizeSSZ()
|
||||
|
||||
// Field (4) 'TrailingField'
|
||||
// Field (5) 'TrailingField'
|
||||
if size := len(v.TrailingField); size != 56 {
|
||||
err = ssz.ErrBytesLengthFn("--.TrailingField", size, 56)
|
||||
return
|
||||
@@ -405,7 +448,20 @@ func (v *VariableTestContainer) MarshalSSZTo(buf []byte) (dst []byte, err error)
|
||||
}
|
||||
}
|
||||
|
||||
// Field (3) 'Nested'
|
||||
// Field (3) 'FieldListBytes32'
|
||||
if size := len(v.FieldListBytes32); size > 100 {
|
||||
err = ssz.ErrListTooBigFn("--.FieldListBytes32", size, 100)
|
||||
return
|
||||
}
|
||||
for ii := 0; ii < len(v.FieldListBytes32); ii++ {
|
||||
if size := len(v.FieldListBytes32[ii]); size != 32 {
|
||||
err = ssz.ErrBytesLengthFn("--.FieldListBytes32[ii]", size, 32)
|
||||
return
|
||||
}
|
||||
dst = append(dst, v.FieldListBytes32[ii]...)
|
||||
}
|
||||
|
||||
// Field (4) 'Nested'
|
||||
if dst, err = v.Nested.MarshalSSZTo(dst); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -417,12 +473,12 @@ func (v *VariableTestContainer) MarshalSSZTo(buf []byte) (dst []byte, err error)
|
||||
func (v *VariableTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
var err error
|
||||
size := uint64(len(buf))
|
||||
if size < 100 {
|
||||
if size < 104 {
|
||||
return ssz.ErrSize
|
||||
}
|
||||
|
||||
tail := buf
|
||||
var o1, o2, o3 uint64
|
||||
var o1, o2, o3, o4 uint64
|
||||
|
||||
// Field (0) 'LeadingField'
|
||||
if cap(v.LeadingField) == 0 {
|
||||
@@ -435,7 +491,7 @@ func (v *VariableTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
if o1 != 100 {
|
||||
if o1 != 104 {
|
||||
return ssz.ErrInvalidVariableOffset
|
||||
}
|
||||
|
||||
@@ -444,16 +500,21 @@ func (v *VariableTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
// Offset (3) 'Nested'
|
||||
// Offset (3) 'FieldListBytes32'
|
||||
if o3 = ssz.ReadOffset(buf[40:44]); o3 > size || o2 > o3 {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
|
||||
// Field (4) 'TrailingField'
|
||||
if cap(v.TrailingField) == 0 {
|
||||
v.TrailingField = make([]byte, 0, len(buf[44:100]))
|
||||
// Offset (4) 'Nested'
|
||||
if o4 = ssz.ReadOffset(buf[44:48]); o4 > size || o3 > o4 {
|
||||
return ssz.ErrOffset
|
||||
}
|
||||
v.TrailingField = append(v.TrailingField, buf[44:100]...)
|
||||
|
||||
// Field (5) 'TrailingField'
|
||||
if cap(v.TrailingField) == 0 {
|
||||
v.TrailingField = make([]byte, 0, len(buf[48:104]))
|
||||
}
|
||||
v.TrailingField = append(v.TrailingField, buf[48:104]...)
|
||||
|
||||
// Field (1) 'FieldListUint64'
|
||||
{
|
||||
@@ -486,9 +547,25 @@ func (v *VariableTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Field (3) 'Nested'
|
||||
// Field (3) 'FieldListBytes32'
|
||||
{
|
||||
buf = tail[o3:]
|
||||
buf = tail[o3:o4]
|
||||
num, err := ssz.DivideInt2(len(buf), 32, 100)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
v.FieldListBytes32 = make([][]byte, num)
|
||||
for ii := 0; ii < num; ii++ {
|
||||
if cap(v.FieldListBytes32[ii]) == 0 {
|
||||
v.FieldListBytes32[ii] = make([]byte, 0, len(buf[ii*32:(ii+1)*32]))
|
||||
}
|
||||
v.FieldListBytes32[ii] = append(v.FieldListBytes32[ii], buf[ii*32:(ii+1)*32]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Field (4) 'Nested'
|
||||
{
|
||||
buf = tail[o4:]
|
||||
if v.Nested == nil {
|
||||
v.Nested = new(VariableNestedContainer)
|
||||
}
|
||||
@@ -501,7 +578,7 @@ func (v *VariableTestContainer) UnmarshalSSZ(buf []byte) error {
|
||||
|
||||
// SizeSSZ returns the ssz encoded size in bytes for the VariableTestContainer object
|
||||
func (v *VariableTestContainer) SizeSSZ() (size int) {
|
||||
size = 100
|
||||
size = 104
|
||||
|
||||
// Field (1) 'FieldListUint64'
|
||||
size += len(v.FieldListUint64) * 8
|
||||
@@ -509,7 +586,10 @@ func (v *VariableTestContainer) SizeSSZ() (size int) {
|
||||
// Field (2) 'FieldListContainer'
|
||||
size += len(v.FieldListContainer) * 40
|
||||
|
||||
// Field (3) 'Nested'
|
||||
// Field (3) 'FieldListBytes32'
|
||||
size += len(v.FieldListBytes32) * 32
|
||||
|
||||
// Field (4) 'Nested'
|
||||
if v.Nested == nil {
|
||||
v.Nested = new(VariableNestedContainer)
|
||||
}
|
||||
@@ -566,12 +646,31 @@ func (v *VariableTestContainer) HashTreeRootWith(hh *ssz.Hasher) (err error) {
|
||||
hh.MerkleizeWithMixin(subIndx, num, 128)
|
||||
}
|
||||
|
||||
// Field (3) 'Nested'
|
||||
// Field (3) 'FieldListBytes32'
|
||||
{
|
||||
if size := len(v.FieldListBytes32); size > 100 {
|
||||
err = ssz.ErrListTooBigFn("--.FieldListBytes32", size, 100)
|
||||
return
|
||||
}
|
||||
subIndx := hh.Index()
|
||||
for _, i := range v.FieldListBytes32 {
|
||||
if len(i) != 32 {
|
||||
err = ssz.ErrBytesLength
|
||||
return
|
||||
}
|
||||
hh.Append(i)
|
||||
}
|
||||
|
||||
numItems := uint64(len(v.FieldListBytes32))
|
||||
hh.MerkleizeWithMixin(subIndx, numItems, 100)
|
||||
}
|
||||
|
||||
// Field (4) 'Nested'
|
||||
if err = v.Nested.HashTreeRootWith(hh); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Field (4) 'TrailingField'
|
||||
// Field (5) 'TrailingField'
|
||||
if size := len(v.TrailingField); size != 56 {
|
||||
err = ssz.ErrBytesLengthFn("--.TrailingField", size, 56)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user