ssz

package module
v0.0.0-...-5aa5fea Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 1, 2019 License: MIT Imports: 18 Imported by: 0

README

Build Status codecov Documentation Discord Gitter

Simple Serialize (SSZ)

Simple Serialize is the serialization algorithm standard for all data structures common across Ethereum 2.0 client implementations. It is outlined in the official Ethereum 2.0 specification.

Need assistance?

A more in-depth breakdown of SSZ is available in this section of our official documentation. If you still have questions, feel free to stop by either our Discord or Gitter and a member of the team or our community will be happy to assist you.

Functionality

Marshal & Unmarshal

Our simple serialize API is designed to match the syntax of the popular JSON marshal / unmarshal API from the Go standard library. Below are samples of each in use:

marshal example:

// Marshal val and output the result.
func Marshal(val interface{}) ([]byte, error)

unmarshal example:

// Unmarshal data from input and output it into the object pointed by pointer val.
func Unmarshal(input []byte, val interface{}) error
Tree hashing

HashTreeRoot SSZ marshals a value and packs its serialized bytes into leaves of a Merkle trie. It then determines the root of this trie.

HashTreeRootexample:

func HashTreeRoot(val interface{}) ([32]byte, error)

Usage examples

Notice: SSZ supports bool, uint8, uint16, uint32, uint64, slice, array, struct and pointer data types.

Encoding an object (Marshal)
  1. To begin, we create this basic struct:
type exampleStruct1 struct {
    Field1 uint8
    Field2 []byte
}
  1. Next, we can encode the defined object like so:
e1 := exampleStruct1{
    Field1: 10,
    Field2: []byte{1, 2, 3, 4},
}
encoded, err := Marshal(e1)
if err != nil {
    return fmt.Errorf("failed to marshal: %v", err)
}
  1. (Optional) It is also possible to specify the size of a struct's field by utilising ssz-specific field tags:
type exampleStruct struct {
    Field1 uint8
    Field2 []byte `ssz:"size=32"`
}

This will treat Field2 as as [32]byte array when marshaling.

  1. (Optional) For unbounded fields or multidimensional slices, SSZ size tags can also be used:
type exampleStruct struct {
    Field1 uint8
    Field2 [][]byte `ssz:"size=?,32"`
}

This will treat Field2 as type [][32]byte when marshaling a struct of that type.

Decoding an object (Unmarshal)
  1. Similarly, you can unmarshal encoded bytes into its original form:
var e2 exampleStruct
if err = Unmarshal(encoded, &e2); err != nil {
    return fmt.Errorf("failed to unmarshal: %v", err)
}
reflect.DeepEqual(e1, e2) // Returns true as e2 now has the same content as e1.
Calculating the tree-hash (HashTreeRoot)
  1. To calculate tree-hash root of the object run:
root, err := HashTreeRoot(e1)
if err != nil {
    return fmt.Errorf("failed to compute Merkle root: %v", err)
}

Contributing

We have put all of our contribution guidelines into CONTRIBUTING.md! Check it out to get started.

License

Brought to you by Prysmatic Labs with a permissive Apache License 2.0.

Documentation

Overview

Package ssz implements the Simple Serialize algorithm specified at https://github.com/ethereum/eth2.0-specs/blob/master/specs/simple-serialize.md

Currently directly supported types:

bool
uint8
uint16
uint32
uint64
bytes
slice
struct
ptr

Index

Constants

This section is empty.

Variables

View Source
var (
	// BytesPerChunk for an SSZ serialized object.
	BytesPerChunk = 32
	// BytesPerLengthOffset defines a constant for off-setting serialized chunks.
	BytesPerLengthOffset = uint64(4)
)
View Source
var (
	// ErrNotMerkleRoot will be returned when a cache object is not a merkle root.
	ErrNotMerkleRoot = errors.New("object is not a merkle root")
)
View Source
var UnboundedSSZFieldSizeMarker = "?"

UnboundedSSZFieldSizeMarker is the character used to specify a ssz field should have unbounded size, which is useful when describing slices of arrays such as [][32]byte. The ssz struct tag for such field type would be `ssz:"size=?,32"`. A question mark is chosen as the default value given its simplicity to represent unbounded size.

Functions

func DeepEqual

func DeepEqual(x, y interface{}) bool

DeepEqual reports whether two SSZ-able values x and y are “deeply equal,” defined as follows: Two values of identical type are deeply equal if one of the following cases applies:

Values of distinct types are never deeply equal.

Array values are deeply equal when their corresponding elements are deeply equal.

Struct values are deeply equal if their corresponding fields, both exported and unexported, are deeply equal.

Interface values are deeply equal if they hold deeply equal concrete values.

Pointer values are deeply equal if they are equal using Go's == operator or if they point to deeply equal values.

Slice values are deeply equal when all of the following are true: they are both nil, one is nil and the other is empty or vice-versa, they have the same length, and either they point to the same initial entry of the same array (that is, &x[0] == &y[0]) or their corresponding elements (up to length) are deeply equal.

Other values - numbers, bools, strings, and channels - are deeply equal if they are equal using Go's == operator.

In general DeepEqual is a recursive relaxation of Go's == operator. However, this idea is impossible to implement without some inconsistency. Specifically, it is possible for a value to be unequal to itself, either because it is of func type (uncomparable in general) or because it is a floating-point NaN value (not equal to itself in floating-point comparison), or because it is an array, struct, or interface containing such a value.

On the other hand, pointer values are always equal to themselves, even if they point at or contain such problematic values, because they compare equal using Go's == operator, and that is a sufficient condition to be deeply equal, regardless of content. DeepEqual has been defined so that the same short-cut applies to slices and maps: if x and y are the same slice or the same map, they are deeply equal regardless of content.

As DeepEqual traverses the data values it may find a cycle. The second and subsequent times that DeepEqual compares two pointer values that have been compared before, it treats the values as equal rather than examining the values to which they point. This ensures that DeepEqual terminates.

Credits go to the Go team as this is an extension of the official Go source code's reflect.DeepEqual function to handle special SSZ edge cases.

func HashTreeRoot

func HashTreeRoot(val interface{}) ([32]byte, error)

HashTreeRoot determines the root hash using SSZ's merkleization. Given a struct with the following fields, one can tree hash it as follows:

type exampleStruct struct {
    Field1 uint8
    Field2 []byte
}

ex := exampleStruct{
    Field1: 10,
    Field2: []byte{1, 2, 3, 4},
}
root, err := HashTreeRoot(ex)
if err != nil {
    return fmt.Errorf("failed to compute root: %v", err)
}

func HashTreeRootWithCapacity

func HashTreeRootWithCapacity(val interface{}, maxCapacity uint64) ([32]byte, error)

HashTreeRootWithCapacity determines the root hash of a dynamic list using SSZ's merkleization and applies a max capacity value when computing the root. If the input is not a slice, the function returns an error.

accountBalances := []uint64{1, 2, 3, 4}
root, err := HashTreeRootWithCapacity(accountBalances, 100) // Max 100 accounts.
if err != nil {
    return fmt.Errorf("failed to compute root: %v", err)
}

func Marshal

func Marshal(val interface{}) ([]byte, error)

Marshal a value and output the result into a byte slice. Given a struct with the following fields, one can marshal it as follows:

type exampleStruct struct {
    Field1 uint8
    Field2 []byte
}

ex := exampleStruct{
    Field1: 10,
    Field2: []byte{1, 2, 3, 4},
}
encoded, err := Marshal(ex)
if err != nil {
    return fmt.Errorf("failed to marshal: %v", err)
}

One can also specify the specific size of a struct's field by using ssz-specific field tags as follows:

type exampleStruct struct {
    Field1 uint8
    Field2 []byte `ssz:"size=32"`
}

This will treat `Field2` as as [32]byte array when marshaling. For unbounded fields or multidimensional slices, ssz size tags can also be used as follows:

type exampleStruct struct {
    Field1 uint8
    Field2 [][]byte `ssz:"size=?,32"`
}

This will treat `Field2` as type [][32]byte when marshaling a struct of that type.

func SigningRoot

func SigningRoot(val interface{}) ([32]byte, error)

SigningRoot truncates the last property of the struct passed in and returns its tree hash. This is done because the last property usually contains the signature that which this data is the root for.

func ToggleCache

func ToggleCache(enableTreeCache bool)

ToggleCache allows to programmatically enable/disable the hash tree root cache.

func Unmarshal

func Unmarshal(input []byte, val interface{}) error

Unmarshal SSZ encoded data and output it into the object pointed by pointer val. Given a struct with the following fields, and some encoded bytes of type []byte, one can then unmarshal the bytes into a pointer of the struct as follows:

type exampleStruct1 struct {
    Field1 uint8
    Field2 []byte
}

var targetStruct exampleStruct1
if err := Unmarshal(encodedBytes, &targetStruct); err != nil {
    return fmt.Errorf("failed to unmarshal: %v", err)
}

Types

This section is empty.

Directories

Path Synopsis
Code generated by yaml_to_go.
Code generated by yaml_to_go.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL