Skip to content

Commit

Permalink
tests: pull in basic container tests to cover uint8 (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
karalabe authored Jul 26, 2024
1 parent e1d852e commit afe53d7
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
131 changes: 131 additions & 0 deletions tests/consensus_specs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"math/rand"
"os"
"path/filepath"
"strings"
"sync"
"testing"

Expand All @@ -21,6 +22,9 @@ import (
)

var (
// consensusSpecTestsBasicsRoot is the folder where the basic ssz tests are located.
consensusSpecTestsBasicsRoot = filepath.Join("testdata", "consensus-spec-tests", "tests", "general", "phase0", "ssz_generic", "containers")

// consensusSpecTestsRoot is the folder where the consensus ssz tests are located.
consensusSpecTestsRoot = filepath.Join("testdata", "consensus-spec-tests", "tests", "mainnet")

Expand All @@ -41,6 +45,133 @@ func commonPrefix(a []byte, b []byte) []byte {
return prefix
}

// TestConsensusSpecBasics iterates over the basic container tests from the
// consensus spec tests repo and runs the encoding/decoding/hashing round.
func TestConsensusSpecBasics(t *testing.T) {
testConsensusSpecBasicType[*types.SingleFieldTestStruct](t, "SingleFieldTestStruct")
testConsensusSpecBasicType[*types.BitsStruct](t, "BitsStruct")
}

func testConsensusSpecBasicType[T newableObject[U], U any](t *testing.T, kind string) {
// Filter out the valid tests for this specific type
path := filepath.Join(consensusSpecTestsBasicsRoot, "valid")

tests, err := os.ReadDir(path)
if err != nil {
t.Errorf("failed to walk valid test collection %v: %v", path, err)
return
}
for i := 0; i < len(tests); i++ {
if !strings.HasPrefix(tests[i].Name(), kind+"_") {
tests = append(tests[:i], tests[i+1:]...)
i--
}
}
// Run all the valid tests
for _, test := range tests {
t.Run(fmt.Sprintf("valid/%s/%s", kind, test.Name()), func(t *testing.T) {
// Parse the input SSZ data and the expected root for the test
inSnappy, err := os.ReadFile(filepath.Join(path, test.Name(), "serialized.ssz_snappy"))
if err != nil {
t.Fatalf("failed to load snapy ssz binary: %v", err)
}
inSSZ, err := snappy.Decode(nil, inSnappy)
if err != nil {
t.Fatalf("failed to parse snappy ssz binary: %v", err)
}
inYAML, err := os.ReadFile(filepath.Join(path, test.Name(), "meta.yaml"))
if err != nil {
t.Fatalf("failed to load yaml root: %v", err)
}
inRoot := struct {
Root string `yaml:"root"`
}{}
if err = yaml.Unmarshal(inYAML, &inRoot); err != nil {
t.Fatalf("failed to parse yaml root: %v", err)
}
// Do a decode/encode round. Would be nicer to parse out the value
// from yaml and check that too, but hex-in-yaml makes everything
// beyond annoying. C'est la vie.
obj := T(new(U))
if err := ssz.DecodeFromStream(bytes.NewReader(inSSZ), obj, uint32(len(inSSZ))); err != nil {
t.Fatalf("failed to decode SSZ stream: %v", err)
}
blob := new(bytes.Buffer)
if err := ssz.EncodeToStream(blob, obj); err != nil {
t.Fatalf("failed to re-encode SSZ stream: %v", err)
}
if !bytes.Equal(blob.Bytes(), inSSZ) {
prefix := commonPrefix(blob.Bytes(), inSSZ)
t.Fatalf("re-encoded stream mismatch: have %x, want %x, common prefix %d, have left %x, want left %x",
blob, inSSZ, len(prefix), blob.Bytes()[len(prefix):], inSSZ[len(prefix):])
}
obj = T(new(U))
if err := ssz.DecodeFromBytes(inSSZ, obj); err != nil {
t.Fatalf("failed to decode SSZ buffer: %v", err)
}
bin := make([]byte, ssz.Size(obj))
if err := ssz.EncodeToBytes(bin, obj); err != nil {
t.Fatalf("failed to re-encode SSZ buffer: %v", err)
}
if !bytes.Equal(bin, inSSZ) {
prefix := commonPrefix(bin, inSSZ)
t.Fatalf("re-encoded bytes mismatch: have %x, want %x, common prefix %d, have left %x, want left %x",
blob, inSSZ, len(prefix), bin[len(prefix):], inSSZ[len(prefix):])
}
// Encoder/decoder seems to work, check if the size reported by the
// encoded object actually matches the encoded stream
if size := ssz.Size(obj); size != uint32(len(inSSZ)) {
t.Fatalf("reported/generated size mismatch: reported %v, generated %v", size, len(inSSZ))
}
hash := ssz.HashSequential(obj)
if fmt.Sprintf("%#x", hash) != inRoot.Root {
t.Fatalf("sequential merkle root mismatch: have %#x, want %s", hash, inRoot.Root)
}
hash = ssz.HashConcurrent(obj)
if fmt.Sprintf("%#x", hash) != inRoot.Root {
t.Fatalf("concurrent merkle root mismatch: have %#x, want %s", hash, inRoot.Root)
}
})
}
// Filter out the valid tests for this specific type
path = filepath.Join(consensusSpecTestsBasicsRoot, "invalid")

tests, err = os.ReadDir(path)
if err != nil {
t.Errorf("failed to walk invalid test collection %v: %v", path, err)
return
}
for i := 0; i < len(tests); i++ {
if !strings.HasPrefix(tests[i].Name(), kind+"_") {
tests = append(tests[:i], tests[i+1:]...)
i--
}
}
// Run all the valid tests
for _, test := range tests {
t.Run(fmt.Sprintf("invalid/%s/%s", kind, test.Name()), func(t *testing.T) {
// Parse the input SSZ data and the expected root for the test
inSnappy, err := os.ReadFile(filepath.Join(path, test.Name(), "serialized.ssz_snappy"))
if err != nil {
t.Fatalf("failed to load snapy ssz binary: %v", err)
}
inSSZ, err := snappy.Decode(nil, inSnappy)
if err != nil {
t.Fatalf("failed to parse snappy ssz binary: %v", err)
}
// Try to decode, it should fail
obj := T(new(U))
if err := ssz.DecodeFromStream(bytes.NewReader(inSSZ), obj, uint32(len(inSSZ))); err == nil {
t.Fatalf("succeeded in decoding invalid SSZ stream")
}
obj = T(new(U))
if err := ssz.DecodeFromBytes(inSSZ, obj); err == nil {
t.Fatalf("succeeded in decoding invalid SSZ buffer")
}
})
}
}

// TestConsensusSpecs iterates over all the (supported) consensus SSZ types and
// runs the encoding/decoding/hashing round.
func TestConsensusSpecs(t *testing.T) {
Expand Down
32 changes: 32 additions & 0 deletions tests/testtypes/consensus-spec-tests/gen_bits_struct_ssz.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions tests/testtypes/consensus-spec-tests/types_basics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// ssz: Go Simple Serialize (SSZ) codec library
// Copyright 2024 ssz Authors
// SPDX-License-Identifier: BSD-3-Clause

package consensus_spec_tests

import "github.com/prysmaticlabs/go-bitfield"

//go:generate go run ../../../cmd/sszgen -type SingleFieldTestStruct -out gen_single_field_test_struct_ssz.go
//go:generate go run ../../../cmd/sszgen -type BitsStruct -out gen_bits_struct_ssz.go

type SingleFieldTestStruct struct {
A byte
}

type BitsStruct struct {
A bitfield.Bitlist `ssz-max:"5"`
B [1]byte `ssz-size:"2" ssz:"bits"`
C [1]byte `ssz-size:"1" ssz:"bits"`
D bitfield.Bitlist `ssz-max:"6"`
E [1]byte `ssz-size:"8" ssz:"bits"`
}

0 comments on commit afe53d7

Please sign in to comment.