Dynamic SSZ is a Go library for SSZ encoding/decoding with support for dynamic field sizes and code generation. It provides runtime flexibility while maintaining high performance through optional static code generation.
- 🔧 Dynamic Field Sizes - Support for runtime-determined field sizes based on configuration
- ⚡ Reflection-Based Processing - Works instantly with any SSZ-compatible types - no code generation required for prototyping
- 🏗️ Code Generation - Optional static code generation for maximum performance (2-3x faster than dynamic processing)
- 🚀 CLI Tool - Standalone
dynssz-gen
command for easy code generation from any Go package - 🔄 Hybrid Approach - Seamlessly combines with fastssz for optimal efficiency
- 📦 Minimal Dependencies - Core library has minimal external dependencies
- ✅ Spec Compliant - Fully compliant with SSZ specification and Ethereum consensus tests
- ✅ Reflection-based dynamic marshaling/unmarshaling/HTR: Production ready - battle-tested in various toolings and stable
- 🚧 Code generator: Feature complete but in beta stage - hasn't been extensively tested in production environments
go get github.com/pk910/dynamic-ssz
import "github.com/pk910/dynamic-ssz"
// Define your types with SSZ tags
type MyStruct struct {
FixedArray [32]byte
DynamicList []uint64 `ssz-max:"1000"`
ConfigBased []byte `ssz-max:"1024" dynssz-max:"MAX_SIZE"`
}
// Create a DynSsz instance with your configuration
specs := map[string]any{
"MAX_SIZE": uint64(2048),
}
ds := dynssz.NewDynSsz(specs)
// Marshal
data, err := ds.MarshalSSZ(myObject)
// Unmarshal
err = ds.UnmarshalSSZ(&myObject, data)
// Hash Tree Root
root, err := ds.HashTreeRoot(myObject)
For maximum performance, use code generation. You can use either the CLI tool or the programmatic API:
Install the CLI tool:
go install github.com/pk910/dynamic-ssz/dynssz-gen@latest
Generate SSZ methods:
# Generate for types in current package
dynssz-gen -package . -types "MyStruct,OtherType" -output generated.go
# Generate for types in external package
dynssz-gen -package github.com/example/types -types "Block" -output block_ssz.go
For integration with build systems:
//go:generate go run codegen.go
// codegen.go
package main
import (
"github.com/pk910/dynamic-ssz/codegen"
"reflect"
)
func main() {
generator := codegen.NewCodeGenerator(nil)
generator.BuildFile(
"generated.go",
codegen.WithReflectType(reflect.TypeOf(MyStruct{})),
)
generator.Generate()
}
Both approaches generate optimized SSZ methods that are faster than reflection-based encoding.
The performance of dynssz
has been benchmarked against fastssz
using BeaconBlocks and BeaconStates from small kurtosis testnets, providing a consistent and comparable set of data. These benchmarks compare four scenarios: exclusively using fastssz
, exclusively using dynssz
, a hybrid approach where dynssz
defaults to fastssz
for static types, and dynssz
with code generation for maximum performance. The results highlight the balance between flexibility and speed:
Legend:
- First number: Unmarshalling time in milliseconds.
- Second number: Marshalling time in milliseconds.
- Third number: Hash tree root time in milliseconds.
- fastssz only: [6 ms / 2 ms / 87 ms] success
- dynssz only: [29 ms / 15 ms / 57 ms] success
- dynssz + fastssz: [9 ms / 3 ms / 64 ms] success
- dynssz + codegen: [6 ms / 2 ms / 55 ms] success
- fastssz only: [5963 ms / 4026 ms / 70919 ms] success
- dynssz only: [15728 ms / 13841 ms / 49248 ms] success
- dynssz + fastssz: [6139 ms / 4094 ms / 36042 ms] success
- dynssz + codegen: [6344 ms / 4869 ms / 36084 ms] success
- fastssz only: failed (unmarshal error: invalid ssz encoding. first variable element offset indexes into fixed value data)
- dynssz only: [34 ms / 20 ms / 78 ms] success
- dynssz + fastssz: [18 ms / 12 ms / 120 ms] success
- dynssz + codegen: [8 ms / 8 ms / 69 ms] success
- fastssz only: failed (unmarshal error: incorrect size)
- dynssz only: [762 ms / 434 ms / 1553 ms] success
- dynssz + fastssz: [413 ms / 264 ms / 3921 ms] success
- dynssz + codegen: [172 ms / 100 ms / 1329 ms] success
These results showcase the dynamic processing capabilities of dynssz
, particularly its ability to handle data structures that fastssz
cannot process due to its static nature. The code generation option provides the best of both worlds: the flexibility to handle any preset configuration while delivering performance that matches or exceeds fastssz
. The hybrid approach with fastssz
provides excellent performance for compatible types, while code generation delivers optimal performance across all scenarios.
The library includes comprehensive testing infrastructure:
- Unit Tests: Fast, isolated tests for core functionality
- Spec Tests: Ethereum consensus specification compliance tests
- Examples: Working examples that are automatically tested
- Performance Tests: Benchmarking and regression testing
- Getting Started Guide
- API Reference
- Supported Types
- Code Generation Guide
- Struct Tags & Annotations
- Performance Guide
- Examples
Check out the examples directory for:
- Basic encoding/decoding
- Code generation setup
- Ethereum types integration
- Custom specifications
- Multi-dimensional arrays
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
Dynamic SSZ is licensed under the Apache 2.0 License.