mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
SSZ-QL: use FastSSZ-generated HashTreeRoot through SSZObject in sszInfo (#15805)
* stored CL object to enable the usage Fastssz's HashTreeRoot(). added basic test * refactorization - using interfaces instead of storing original object * added tests covering ssz custom types * renamed hash_tree_root to ssz_interface as it contains MarshalSSZ and UnmarshalSSZ functions * run gazelle * renamed test and improved comments * refactored test and extend to marshalSSZ and UnmarshalSSZ * added changelog * updated comment * Changed SSZIface name to SSZObject. Removed MarshalSSZ and UnmarshalSSZ function signatures from interface as they are not used still. Refactored tests. * renamed file ssz_interface.go to ssz_object.go. merge test from ssz_interface_test.go into query_test.go. reordered source SSZObject field from sszInfo struct * sticked SSZObject interface to HashTreeRoot() function, the only one needed so far * run gazelle :) --------- Co-authored-by: Radosław Kapka <rkapka@wp.pl>
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Delegate sszInfo HashTreeRoot to FastSSZ-generated implementations via SSZObject, enabling roots calculation for generated types while avoiding duplicate logic.
|
||||
@@ -11,6 +11,7 @@ go_library(
|
||||
"path.go",
|
||||
"query.go",
|
||||
"ssz_info.go",
|
||||
"ssz_object.go",
|
||||
"ssz_type.go",
|
||||
"tag_parser.go",
|
||||
"vector.go",
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
const offsetBytes = 4
|
||||
|
||||
// AnalyzeObject analyzes given object and returns its SSZ information.
|
||||
func AnalyzeObject(obj any) (*sszInfo, error) {
|
||||
func AnalyzeObject(obj SSZObject) (*sszInfo, error) {
|
||||
value := dereferencePointer(obj)
|
||||
|
||||
info, err := analyzeType(value.Type(), nil)
|
||||
@@ -18,6 +18,9 @@ func AnalyzeObject(obj any) (*sszInfo, error) {
|
||||
return nil, fmt.Errorf("could not analyze type %s: %w", value.Type().Name(), err)
|
||||
}
|
||||
|
||||
// Store the original object interface
|
||||
info.source = obj
|
||||
|
||||
// Populate variable-length information using the actual value.
|
||||
err = PopulateVariableLengthInfo(info, value.Interface())
|
||||
if err != nil {
|
||||
|
||||
@@ -302,7 +302,7 @@ func getFixedTestContainerSpec() testutil.TestSpec {
|
||||
|
||||
return testutil.TestSpec{
|
||||
Name: "FixedTestContainer",
|
||||
Type: sszquerypb.FixedTestContainer{},
|
||||
Type: &sszquerypb.FixedTestContainer{},
|
||||
Instance: testContainer,
|
||||
PathTests: []testutil.PathTest{
|
||||
// Basic types
|
||||
@@ -364,6 +364,62 @@ func getFixedTestContainerSpec() testutil.TestSpec {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSSZObject_batch(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
obj any
|
||||
}{
|
||||
{
|
||||
name: "FixedNestedContainer",
|
||||
obj: &sszquerypb.FixedNestedContainer{
|
||||
Value1: 42,
|
||||
Value2: []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "FixedTestContainer",
|
||||
obj: createFixedTestContainer(),
|
||||
},
|
||||
{
|
||||
name: "VariableNestedContainer",
|
||||
obj: &sszquerypb.VariableNestedContainer{
|
||||
Value1: 84,
|
||||
FieldListUint64: []uint64{1, 2, 3, 4, 5},
|
||||
NestedListField: [][]byte{
|
||||
{0x0a, 0x0b, 0x0c},
|
||||
{0x1a, 0x1b, 0x1c, 0x1d},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "VariableTestContainer",
|
||||
obj: createVariableTestContainer(),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Analyze the object to get its sszInfo
|
||||
object, ok := tt.obj.(query.SSZObject)
|
||||
require.Equal(t, true, ok, "Expected object to implement SSZObject")
|
||||
info, err := query.AnalyzeObject(object)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, info, "Expected non-nil SSZ info")
|
||||
|
||||
// Ensure the original object implements SSZObject
|
||||
originalFunctions, ok := tt.obj.(query.SSZObject)
|
||||
require.Equal(t, ok, true, "Original object does not implement SSZObject")
|
||||
|
||||
// Call HashTreeRoot on the sszInfo and compare results
|
||||
hashTreeRoot, err := info.HashTreeRoot()
|
||||
require.NoError(t, err, "HashTreeRoot should not return an error")
|
||||
expectedHashTreeRoot, err := originalFunctions.HashTreeRoot()
|
||||
require.NoError(t, err, "HashTreeRoot on original object should not return an error")
|
||||
require.Equal(t, expectedHashTreeRoot, hashTreeRoot, "HashTreeRoot from sszInfo should match original object's HashTreeRoot")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func createVariableTestContainer() *sszquerypb.VariableTestContainer {
|
||||
leadingField := make([]byte, 32)
|
||||
for i := range leadingField {
|
||||
@@ -439,7 +495,7 @@ func getVariableTestContainerSpec() testutil.TestSpec {
|
||||
|
||||
return testutil.TestSpec{
|
||||
Name: "VariableTestContainer",
|
||||
Type: sszquerypb.VariableTestContainer{},
|
||||
Type: &sszquerypb.VariableTestContainer{},
|
||||
Instance: testContainer,
|
||||
PathTests: []testutil.PathTest{
|
||||
// Fixed leading field
|
||||
|
||||
@@ -13,6 +13,8 @@ type sszInfo struct {
|
||||
sszType SSZType
|
||||
// Type in Go. Need this for unmarshaling.
|
||||
typ reflect.Type
|
||||
// Original object being analyzed
|
||||
source SSZObject
|
||||
|
||||
// isVariable is true if the struct contains any variable-size fields.
|
||||
isVariable bool
|
||||
|
||||
22
encoding/ssz/query/ssz_object.go
Normal file
22
encoding/ssz/query/ssz_object.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package query
|
||||
|
||||
import "errors"
|
||||
|
||||
type SSZObject interface {
|
||||
HashTreeRoot() ([32]byte, error)
|
||||
}
|
||||
|
||||
// HashTreeRoot calls the HashTreeRoot method on the stored interface if it implements SSZObject.
|
||||
// Returns the 32-byte hash tree root or an error if the interface doesn't support hashing.
|
||||
func (info *sszInfo) HashTreeRoot() ([32]byte, error) {
|
||||
if info == nil {
|
||||
return [32]byte{}, errors.New("sszInfo is nil")
|
||||
}
|
||||
|
||||
if info.source == nil {
|
||||
return [32]byte{}, errors.New("sszInfo.source is nil")
|
||||
}
|
||||
|
||||
// Check if the value implements the Hashable interface
|
||||
return info.source.HashTreeRoot()
|
||||
}
|
||||
@@ -10,7 +10,10 @@ import (
|
||||
|
||||
func RunStructTest(t *testing.T, spec TestSpec) {
|
||||
t.Run(spec.Name, func(t *testing.T) {
|
||||
info, err := query.AnalyzeObject(spec.Type)
|
||||
object, ok := spec.Type.(query.SSZObject)
|
||||
require.Equal(t, true, ok, "spec.Type must implement SSZObject interface")
|
||||
require.NotNil(t, object, "spec.Type must not be nil")
|
||||
info, err := query.AnalyzeObject(object)
|
||||
require.NoError(t, err)
|
||||
|
||||
testInstance := spec.Instance
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package testutil
|
||||
|
||||
import "github.com/OffchainLabs/prysm/v6/encoding/ssz/query"
|
||||
|
||||
type PathTest struct {
|
||||
Path string
|
||||
Expected any
|
||||
@@ -7,7 +9,7 @@ type PathTest struct {
|
||||
|
||||
type TestSpec struct {
|
||||
Name string
|
||||
Type any
|
||||
Instance any
|
||||
Type query.SSZObject
|
||||
Instance query.SSZObject
|
||||
PathTests []PathTest
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user