scale

package
v0.8.0 Latest Latest
Warning

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

Go to latest
Published: Dec 11, 2023 License: LGPL-3.0 Imports: 11 Imported by: 4

README

go-scale Codec

Go implementation of the SCALE (Simple Concatenated Aggregate Little-Endian) data format for types used in the Parity Substrate framework.

SCALE is a light-weight format which allows encoding (and decoding) which makes it highly suitable for resource-constrained execution environments like blockchain runtimes and low-power, low-memory devices.

It is important to note that the encoding context (knowledge of how the types and data structures look) needs to be known separately at both encoding and decoding ends. The encoded data does not include this contextual information.

This codec attempts to translate the primitive Go types to the associated types in SCALE. It also introduces a few custom types to implement Rust primitives that have no direct translation to a Go primitive type.

Translating From SCALE to Go

When translating from SCALE to native Go data, go-scale returns primitive Go data values for corresponding SCALE data values. The table below shows how go-scale translates SCALE types to Go.

Primitives
SCALE/Rust Go
i8 int8
u8 uint8
i16 int16
u16 uint16
i32 int32
u32 uint32
i64 int64
u64 uint64
i128 *big.Int
u128 *scale.Uint128
bytes []byte
string string
enum scale.VaryingDataType
struct struct
Structs

When decoding SCALE data, knowledge of the structure of the destination data type is required to decode. Structs are encoded as a SCALE Tuple, where each struct field is encoded in the sequence of the fields.

Struct Tags

go-scale uses a scale struct tag to modify the order of the field values during encoding. This is also used when decoding attributes back to the original type. This essentially allows you to modify struct field ordering but preserve the encoding/decoding ordering.

See the usage example.

Option

For all Option<T> a pointer to the underlying type is used in go-scale. In the None case the pointer value is nil.

SCALE/Rust Go
Option<i8> *int8
Option<u8> *uint8
Option<i16> *int16
Option<u16> *uint16
Option<i32> *int32
Option<u32> *uint32
Option<i64> *int64
Option<u64> *uint64
Option<i128> **big.Int
Option<u128> **scale.Uint128
Option<bytes> *[]byte
Option<string> *string
Option<enum> *scale.VaryingDataType
Option<struct> *struct
None nil
Compact Encoding

SCALE uses a compact encoding for variable width unsigned integers.

SCALE/Rust Go
Compact<u8> uint
Compact<u16> uint
Compact<u32> uint
Compact<u64> uint
Compact<u128> *big.Int

Usage

Basic Example

Basic example which encodes and decodes a uint.

import (
	"fmt"
	"github.com/ChainSafe/gossamer/pkg/scale"
)

func ExampleBasic() {
	// compact length encoded uint
	var ui uint = 999
	bytes, err := scale.Marshal(ui)
	if err != nil {
		panic(err)
	}

	var unmarshaled uint
	err = scale.Unmarshal(bytes, &unmarshaled)
	if err != nil {
		panic(err)
	}

	// 999
	fmt.Printf("%d", unmarshaled)
}
Struct Tag Example

Use the scale struct tag for struct fields to conform to specific encoding sequence of struct field values. A struct tag of "-" will be omitted from encoding and decoding.

import (
	"fmt"
	"github.com/ChainSafe/gossamer/pkg/scale"
)

func ExampleStruct() {
	type MyStruct struct {
		Baz bool      `scale:"3"`
		Bar int32     `scale:"2"`
		Foo []byte    `scale:"1"`
		Ignored int64 `scale:"-"`
	}
	var ms = MyStruct{
		Baz: true,
		Bar: 999,
		Foo: []byte{1, 2},
		Ignored: 999
	}
	bytes, err := scale.Marshal(ms)
	if err != nil {
		panic(err)
	}

	var unmarshaled MyStruct
	err = scale.Unmarshal(bytes, &unmarshaled)
	if err != nil {
		panic(err)
	}

	// {Baz:true Bar:999 Foo:[1 2] Ignored:0}
	fmt.Printf("%+v", unmarshaled)
}
Result

A Result is custom type analogous to a rust result. A Result needs to be constructed using the NewResult constructor. The two parameters accepted are the expected types that are associated to the Ok, and Err cases.

// Rust
Result<i32, i32> = Ok(10)

// go-scale
result := scale.NewResult(int32(0), int32(0)
result.Set(scale.Ok, 10)
import (
	"fmt"
	"github.com/ChainSafe/gossamer/pkg/scale"
)

func ExampleResult() {
	// pass in zero or non-zero values of the types for Ok and Err cases
	res := scale.NewResult(bool(false), string(""))

	// set the OK case with a value of true, any values for OK that are not bool will return an error
	err := res.Set(scale.OK, true)
	if err != nil {
		panic(err)
	}

	bytes, err := scale.Marshal(res)
	if err != nil {
		panic(err)
	}

	// [0x00, 0x01]
	fmt.Printf("%v\n", bytes)

	res1 := scale.NewResult(bool(false), string(""))

	err = scale.Unmarshal(bytes, &res1)
	if err != nil {
		panic(err)
	}

	// res1 should be Set with OK mode and value of true
	ok, err := res1.Unwrap()
	if err != nil {
		panic(err)
	}

	switch ok := ok.(type) {
	case bool:
		if !ok {
			panic(fmt.Errorf("unexpected ok value: %v", ok))
		}
	default:
		panic(fmt.Errorf("unexpected type: %T", ok))
	}
}

Varying Data Type

A VaryingDataType is analogous to a Rust enum. A VaryingDataType needs to be constructed using the NewVaryingDataType constructor. VaryingDataTypeValue is an interface with one Index() uint method that needs to be implemented. The returned uint index should be unique per type and needs to be the same index as defined in the Rust enum to ensure interopability. To set the value of the VaryingDataType, the VaryingDataType.Set() function should be called with an associated VaryingDataTypeValue.

import (
	"fmt"
	"github.com/ChainSafe/gossamer/pkg/scale"
)

type MyStruct struct {
	Baz bool
	Bar uint32
	Foo []byte
}

func (ms MyStruct) Index() uint {
	return 1
}

type MyOtherStruct struct {
	Foo string
	Bar uint64
	Baz uint
}

func (mos MyOtherStruct) Index() uint {
	return 2
}

type MyInt16 int16

func (mi16 MyInt16) Index() uint {
	return 3
}

func ExampleVaryingDataType() {
	vdt, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
	if err != nil {
		panic(err)
	}

	err = vdt.Set(MyStruct{
		Baz: true,
		Bar: 999,
		Foo: []byte{1, 2},
	})
	if err != nil {
		panic(err)
	}

	bytes, err := scale.Marshal(vdt)
	if err != nil {
		panic(err)
	}

	vdt1, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
	if err != nil {
		panic(err)
	}

	err = scale.Unmarshal(bytes, &vdt1)
	if err != nil {
		panic(err)
	}

	if !reflect.DeepEqual(vdt, vdt1) {
		panic(fmt.Errorf("uh oh: %+v %+v", vdt, vdt1))
	}
}

A VaryingDataTypeSlice is a slice containing multiple VaryingDataType elements. Each VaryingDataTypeValue must be of a supported type of the VaryingDataType passed into the NewVaryingDataTypeSlice constructor. The method to call to add VaryingDataTypeValue instances is VaryingDataTypeSlice.Add().

func ExampleVaryingDataTypeSlice() {
	vdt, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
	if err != nil {
		panic(err)
	}

	vdts := scale.NewVaryingDataTypeSlice(vdt)

	err = vdts.Add(
		MyStruct{
			Baz: true,
			Bar: 999,
			Foo: []byte{1, 2},
		},
		MyInt16(1),
	)
	if err != nil {
		panic(err)
	}

	bytes, err := scale.Marshal(vdts)
	if err != nil {
		panic(err)
	}

	vdts1 := scale.NewVaryingDataTypeSlice(vdt)
	if err != nil {
		panic(err)
	}

	err = scale.Unmarshal(bytes, &vdts1)
	if err != nil {
		panic(err)
	}

	if !reflect.DeepEqual(vdts, vdts1) {
		panic(fmt.Errorf("uh oh: %+v %+v", vdts, vdts1))
	}
}
Nested VaryingDataType

See varying_data_type_nested_example.go for a working example of a custom VaryingDataType with another custom VaryingDataType as a value of the parent VaryingDataType. In the case of nested VaryingDataTypes, a custom type needs to be created for the child VaryingDataType because it needs to fulfill the VaryingDataTypeValue interface.

import (
	"fmt"
	"reflect"

	"github.com/ChainSafe/gossamer/pkg/scale"
)

// ParentVDT is a VaryingDataType that consists of multiple nested VaryingDataType
// instances (aka. a rust enum containing multiple enum options)
type ParentVDT scale.VaryingDataType

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (pvdt *ParentVDT) Set(val scale.VaryingDataTypeValue) (err error) {
	// cast to VaryingDataType to use VaryingDataType.Set method
	vdt := scale.VaryingDataType(*pvdt)
	err = vdt.Set(val)
	if err != nil {
		return
	}
	// store original ParentVDT with VaryingDataType that has been set
	*pvdt = ParentVDT(vdt)
	return
}

// Value will return value from underying VaryingDataType
func (pvdt *ParentVDT) Value() (val scale.VaryingDataTypeValue, err error) {
	vdt := scale.VaryingDataType(*pvdt)
	return vdt.Value()
}

// NewParentVDT is constructor for ParentVDT
func NewParentVDT() ParentVDT {
	// use standard VaryingDataType constructor to construct a VaryingDataType
	vdt, err := scale.NewVaryingDataType(NewChildVDT(), NewOtherChildVDT())
	if err != nil {
		panic(err)
	}
	// cast to ParentVDT
	return ParentVDT(vdt)
}

// ChildVDT type is used as a VaryingDataTypeValue for ParentVDT
type ChildVDT scale.VaryingDataType

// Index fulfills the VaryingDataTypeValue interface.  T
func (cvdt ChildVDT) Index() uint {
	return 1
}

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (cvdt *ChildVDT) Set(val scale.VaryingDataTypeValue) (err error) {
	// cast to VaryingDataType to use VaryingDataType.Set method
	vdt := scale.VaryingDataType(*cvdt)
	err = vdt.Set(val)
	if err != nil {
		return
	}
	// store original ParentVDT with VaryingDataType that has been set
	*cvdt = ChildVDT(vdt)
	return
}

// Value will return value from underying VaryingDataType
func (cvdt *ChildVDT) Value() (val scale.VaryingDataTypeValue, err error) {
	vdt := scale.VaryingDataType(*cvdt)
	return vdt.Value()
}

// NewChildVDT is constructor for ChildVDT
func NewChildVDT() ChildVDT {
	// use standard VaryingDataType constructor to construct a VaryingDataType
	// constarined to types ChildInt16, ChildStruct, and ChildString
	vdt, err := scale.NewVaryingDataType(ChildInt16(0), ChildStruct{}, ChildString(""))
	if err != nil {
		panic(err)
	}
	// cast to ParentVDT
	return ChildVDT(vdt)
}

// OtherChildVDT type is used as a VaryingDataTypeValue for ParentVDT
type OtherChildVDT scale.VaryingDataType

// Index fulfills the VaryingDataTypeValue interface.
func (ocvdt OtherChildVDT) Index() uint {
	return 2
}

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (cvdt *OtherChildVDT) Set(val scale.VaryingDataTypeValue) (err error) {
	// cast to VaryingDataType to use VaryingDataType.Set method
	vdt := scale.VaryingDataType(*cvdt)
	err = vdt.Set(val)
	if err != nil {
		return
	}
	// store original ParentVDT with VaryingDataType that has been set
	*cvdt = OtherChildVDT(vdt)
	return
}

// NewOtherChildVDT is constructor for OtherChildVDT
func NewOtherChildVDT() OtherChildVDT {
	// use standard VaryingDataType constructor to construct a VaryingDataType
	// constarined to types ChildInt16 and ChildStruct
	vdt, err := scale.NewVaryingDataType(ChildInt16(0), ChildStruct{}, ChildString(""))
	if err != nil {
		panic(err)
	}
	// cast to ParentVDT
	return OtherChildVDT(vdt)
}

// ChildInt16 is used as a VaryingDataTypeValue for ChildVDT and OtherChildVDT
type ChildInt16 int16

// Index fulfills the VaryingDataTypeValue interface.  The ChildVDT type is used as a
// VaryingDataTypeValue for ParentVDT
func (ci ChildInt16) Index() uint {
	return 1
}

// ChildStruct is used as a VaryingDataTypeValue for ChildVDT and OtherChildVDT
type ChildStruct struct {
	A string
	B bool
}

// Index fulfills the VaryingDataTypeValue interface
func (cs ChildStruct) Index() uint {
	return 2
}

// ChildString is used as a VaryingDataTypeValue for ChildVDT and OtherChildVDT
type ChildString string

// Index fulfills the VaryingDataTypeValue interface
func (cs ChildString) Index() uint {
	return 3
}

func ExampleNestedVaryingDataType() {
	parent := NewParentVDT()

	// populate parent with ChildVDT
	child := NewChildVDT()
	child.Set(ChildInt16(888))
	err := parent.Set(child)
	if err != nil {
		panic(err)
	}

	// validate ParentVDT.Value()
	parentValue, err := parent.Value()
	if err != nil {
		panic(err)
	}
	fmt.Printf("parent.Value(): %+v\n", parentValue)
	// should cast to ChildVDT, since that was set earlier
	valChildVDT := parentValue.(ChildVDT)
	// validate ChildVDT.Value() as ChildInt16(888)
	valChildVDTValue, err := valChildVDT.Value()
	if err != nil {
		panic(err)
	}
	fmt.Printf("child.Value(): %+v\n", valChildVDTValue)

	// marshal into scale encoded bytes
	bytes, err := scale.Marshal(parent)
	if err != nil {
		panic(err)
	}
	fmt.Printf("bytes: % x\n", bytes)

	// unmarshal into another ParentVDT
	dstParent := NewParentVDT()
	err = scale.Unmarshal(bytes, &dstParent)
	if err != nil {
		panic(err)
	}
	// assert both ParentVDT instances are the same
	fmt.Println(reflect.DeepEqual(parent, dstParent))

	// Output:
	// parent.Value(): {value:888 cache:map[1:0 2:{A: B:false} 3:]}
	// child.Value(): 888
	// bytes: 01 01 78 03
	// true
}

Documentation

Overview

Example
package main

import (
	"fmt"
	"reflect"

	"github.com/ChainSafe/gossamer/pkg/scale"
)

// ParentVDT is a VaryingDataType that consists of multiple nested VaryingDataType
// instances (aka. a rust enum containing multiple enum options)
type ParentVDT scale.VaryingDataType

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (pvdt *ParentVDT) Set(val scale.VaryingDataTypeValue) (err error) {
	// cast to VaryingDataType to use VaryingDataType.Set method
	vdt := scale.VaryingDataType(*pvdt)
	err = vdt.Set(val)
	if err != nil {
		return
	}
	// store original ParentVDT with VaryingDataType that has been set
	*pvdt = ParentVDT(vdt)
	return
}

// Value will return value from underying VaryingDataType
func (pvdt *ParentVDT) Value() (val scale.VaryingDataTypeValue, err error) {
	vdt := scale.VaryingDataType(*pvdt)
	return vdt.Value()
}

// NewParentVDT is constructor for ParentVDT
func NewParentVDT() ParentVDT {
	// use standard VaryingDataType constructor to construct a VaryingDataType
	vdt, err := scale.NewVaryingDataType(NewChildVDT(), NewOtherChildVDT())
	if err != nil {
		panic(err)
	}
	// cast to ParentVDT
	return ParentVDT(vdt)
}

// ChildVDT type is used as a VaryingDataTypeValue for ParentVDT
type ChildVDT scale.VaryingDataType

// Index fulfils the VaryingDataTypeValue interface.  T
func (ChildVDT) Index() uint { //skipcq: GO-W1029
	return 1
}

func (cvdt ChildVDT) String() string { //skipcq: GO-W1029
	value, err := cvdt.Value()
	if err != nil {
		return "ChildVDT()"
	}
	return fmt.Sprintf("ChildVDT(%s)", value)
}

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (cvdt *ChildVDT) Set(val scale.VaryingDataTypeValue) (err error) { //skipcq: GO-W1029
	// cast to VaryingDataType to use VaryingDataType.Set method
	vdt := scale.VaryingDataType(*cvdt)
	err = vdt.Set(val)
	if err != nil {
		return
	}
	// store original ParentVDT with VaryingDataType that has been set
	*cvdt = ChildVDT(vdt)
	return
}

// Value will return value from underying VaryingDataType
func (cvdt *ChildVDT) Value() (val scale.VaryingDataTypeValue, err error) { //skipcq: GO-W1029
	vdt := scale.VaryingDataType(*cvdt)
	return vdt.Value()
}

// NewChildVDT is constructor for ChildVDT
func NewChildVDT() ChildVDT {
	// use standard VaryingDataType constructor to construct a VaryingDataType
	// constarined to types ChildInt16, ChildStruct, and ChildString
	vdt, err := scale.NewVaryingDataType(ChildInt16(0), ChildStruct{}, ChildString(""))
	if err != nil {
		panic(err)
	}
	// cast to ParentVDT
	return ChildVDT(vdt)
}

// OtherChildVDT type is used as a VaryingDataTypeValue for ParentVDT
type OtherChildVDT scale.VaryingDataType

// Index fulfils the VaryingDataTypeValue interface.
func (OtherChildVDT) Index() uint { //skipcq: GO-W1029
	return 2
}

func (cvdt OtherChildVDT) String() string { //skipcq: GO-W1029
	vdt := scale.VaryingDataType(cvdt)
	vdtPtr := &vdt
	value, err := vdtPtr.Value()
	if err != nil {
		return "OtherChildVDT()"
	}
	return fmt.Sprintf("OtherChildVDT(%s)", value)
}

// Set will set a VaryingDataTypeValue using the underlying VaryingDataType
func (cvdt *OtherChildVDT) Set(val scale.VaryingDataTypeValue) (err error) { //skipcq: GO-W1029
	// cast to VaryingDataType to use VaryingDataType.Set method
	vdt := scale.VaryingDataType(*cvdt)
	err = vdt.Set(val)
	if err != nil {
		return
	}
	// store original ParentVDT with VaryingDataType that has been set
	*cvdt = OtherChildVDT(vdt)
	return
}

// NewOtherChildVDT is constructor for OtherChildVDT
func NewOtherChildVDT() OtherChildVDT {
	// use standard VaryingDataType constructor to construct a VaryingDataType
	// constarined to types ChildInt16 and ChildStruct
	vdt, err := scale.NewVaryingDataType(ChildInt16(0), ChildStruct{}, ChildString(""))
	if err != nil {
		panic(err)
	}
	// cast to ParentVDT
	return OtherChildVDT(vdt)
}

// ChildInt16 is used as a VaryingDataTypeValue for ChildVDT and OtherChildVDT
type ChildInt16 int16

// Index fulfils the VaryingDataTypeValue interface.  The ChildVDT type is used as a
// VaryingDataTypeValue for ParentVDT
func (ChildInt16) Index() uint {
	return 1
}

func (c ChildInt16) String() string { return fmt.Sprintf("ChildInt16(%d)", c) }

// ChildStruct is used as a VaryingDataTypeValue for ChildVDT and OtherChildVDT
type ChildStruct struct {
	A string
	B bool
}

// Index fulfils the VaryingDataTypeValue interface
func (ChildStruct) Index() uint {
	return 2
}

func (c ChildStruct) String() string {
	return fmt.Sprintf("ChildStruct{A=%s, B=%t}", c.A, c.B)
}

// ChildString is used as a VaryingDataTypeValue for ChildVDT and OtherChildVDT
type ChildString string

// Index fulfils the VaryingDataTypeValue interface
func (ChildString) Index() uint {
	return 3
}

func (c ChildString) String() string { return fmt.Sprintf("ChildString(%s)", string(c)) }

func main() {
	parent := NewParentVDT()

	// populate parent with ChildVDT
	child := NewChildVDT()
	child.Set(ChildInt16(888))
	err := parent.Set(child)
	if err != nil {
		panic(err)
	}

	// validate ParentVDT.Value()
	parentVal, err := parent.Value()
	if err != nil {
		panic(err)
	}
	fmt.Printf("parent.Value(): %+v\n", parentVal)
	// should cast to ChildVDT, since that was set earlier
	valChildVDT := parentVal.(ChildVDT)
	// validate ChildVDT.Value() as ChildInt16(888)
	childVdtValue, err := valChildVDT.Value()
	if err != nil {
		panic(err)
	}
	fmt.Printf("child.Value(): %+v\n", childVdtValue)

	// marshal into scale encoded bytes
	bytes, err := scale.Marshal(parent)
	if err != nil {
		panic(err)
	}
	fmt.Printf("bytes: % x\n", bytes)

	// unmarshal into another ParentVDT
	dstParent := NewParentVDT()
	err = scale.Unmarshal(bytes, &dstParent)
	if err != nil {
		panic(err)
	}
	// assert both ParentVDT instances are the same
	fmt.Println(reflect.DeepEqual(parent, dstParent))

}
Output:

parent.Value(): ChildVDT(ChildInt16(888))
child.Value(): ChildInt16(888)
bytes: 01 01 78 03
true

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrU16OutOfRange            = errors.New("uint16 out of range")
	ErrU32OutOfRange            = errors.New("uint32 out of range")
	ErrU64OutOfRange            = errors.New("uint64 out of range")
	ErrU64NotSupported          = errors.New("uint64 is not supported")
	ErrCompactUintPrefixUnknown = errors.New("unknown prefix for compact uint")
)
View Source
var (
	ErrUnsupportedDestination = errors.New("must be a non-nil pointer to a destination")

	ErrUnsupportedType   = errors.New("unsupported type")
	ErrUnsupportedResult = errors.New("unsupported result")

	ErrResultNotSet                    = errors.New("result not set")
	ErrResultAlreadySet                = errors.New("result already has an assigned value")
	ErrUnsupportedVaryingDataTypeValue = errors.New("unsupported VaryingDataTypeValue")
	ErrMustProvideVaryingDataTypeValue = errors.New("must provide at least one VaryingDataTypeValue")

	ErrVaryingDataTypeNotSet      = errors.New("varying data type not set")
	ErrUnsupportedCustomPrimitive = errors.New("unsupported type for custom primitive")
	ErrInvalidScaleIndex          = errors.New("invalid scale index")
)
View Source
var MaxUint128 = &Uint128{
	Upper: ^uint64(0),
	Lower: ^uint64(0),
}

MaxUint128 is the maximum uint128 value

Functions

func Marshal

func Marshal(v interface{}) (b []byte, err error)

Marshal takes in an interface{} and attempts to marshal into []byte

func MustMarshal added in v0.8.0

func MustMarshal(v interface{}) (b []byte)

MustMarshal runs Marshal and panics on error.

func Unmarshal

func Unmarshal(data []byte, dst interface{}) (err error)

Unmarshal takes data and a destination pointer to unmarshal the data to.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder is used to decode from an io.Reader

func NewDecoder

func NewDecoder(r io.Reader) (d *Decoder)

NewDecoder is constructor for Decoder

func (*Decoder) Decode

func (d *Decoder) Decode(dst interface{}) (err error)

Decode accepts a pointer to a destination and decodes into supplied destination

func (*Decoder) ReadByte

func (ds *Decoder) ReadByte() (byte, error)

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder scale encodes to a given io.Writer.

func NewEncoder

func NewEncoder(writer io.Writer) (encoder *Encoder)

NewEncoder creates a new encoder with the given writer.

func (*Encoder) Encode

func (e *Encoder) Encode(value interface{}) (err error)

Encode scale encodes value to the encoder writer.

type Marshaler added in v0.8.0

type Marshaler interface {
	MarshalSCALE() ([]byte, error)
}

Marshaler is the interface for custom SCALE marshalling for a given type

type Result

type Result struct {
	// contains filtered or unexported fields
}

Result encapsulates an Ok or an Err case

Example
package main

import (
	"fmt"

	"github.com/ChainSafe/gossamer/pkg/scale"
)

func main() {
	// pass in zero or non-zero values of the types for Ok and Err cases
	res := scale.NewResult(bool(false), string(""))

	// set the OK case with a value of true, any values for OK that are not bool will return an error
	err := res.Set(scale.OK, true)
	if err != nil {
		panic(err)
	}

	bytes, err := scale.Marshal(res)
	if err != nil {
		panic(err)
	}

	// [0x00, 0x01]
	fmt.Printf("%v\n", bytes)

	res1 := scale.NewResult(bool(false), string(""))

	err = scale.Unmarshal(bytes, &res1)
	if err != nil {
		panic(err)
	}

	// res1 should be Set with OK mode and value of true
	ok, err := res1.Unwrap()
	if err != nil {
		panic(err)
	}

	switch ok := ok.(type) {
	case bool:
		if !ok {
			panic(fmt.Errorf("unexpected ok value: %v", ok))
		}
	default:
		panic(fmt.Errorf("unexpected type: %T", ok))
	}
}
Output:

func NewResult

func NewResult(okIn, errIn interface{}) (res Result)

NewResult is constructor for Result. Use nil to represent empty tuple () in Rust.

func (*Result) IsSet

func (r *Result) IsSet() bool

IsSet returns whether the Result is set with an Ok or Err value.

func (*Result) Set

func (r *Result) Set(mode ResultMode, in interface{}) (err error)

Set takes in a mode (OK/Err) and the associated interface and sets the Result value

func (*Result) Unwrap

func (r *Result) Unwrap() (ok interface{}, err error)

Unwrap returns the result in go standard wrapping the Err case in a ResultErr

type ResultMode

type ResultMode int

ResultMode is the mode the Result is set to

const (
	// Unset ResultMode is zero value mode
	Unset ResultMode = iota
	// OK case
	OK
	// Err case
	Err
)

type Uint128

type Uint128 struct {
	Upper uint64
	Lower uint64
}

Uint128 represents an unsigned 128 bit integer

func MustNewUint128

func MustNewUint128(in interface{}, order ...binary.ByteOrder) (u *Uint128)

MustNewUint128 will panic if NewUint128 returns an error

func NewUint128

func NewUint128(in interface{}, order ...binary.ByteOrder) (u *Uint128, err error)

NewUint128 is constructor for Uint128 that accepts an option binary.ByteOrder option is only used when inputted interface{} is of type []byte by default binary.LittleEndian is used for []byte since this is SCALE

func (*Uint128) Bytes

func (u *Uint128) Bytes(order ...binary.ByteOrder) (b []byte)

Bytes returns the Uint128 in little endian format by default. A variadic parameter order can be used to specify the binary.ByteOrder used

func (*Uint128) Compare

func (u *Uint128) Compare(other *Uint128) int

Compare returns 1 if the receiver is greater than other, 0 if they are equal, and -1 otherwise.

func (Uint128) MarshalJSON added in v0.8.0

func (u Uint128) MarshalJSON() ([]byte, error)

MarshalJSON converts Uint128 to []byte.

func (*Uint128) String

func (u *Uint128) String() string

String returns the string format from the Uint128 value

func (*Uint128) UnmarshalJSON

func (u *Uint128) UnmarshalJSON(data []byte) error

UnmarshalJSON converts data to Uint128.

type Unmarshaler added in v0.8.0

type Unmarshaler interface {
	UnmarshalSCALE(io.Reader) error
}

Unmarshaler is the interface for custom SCALE unmarshalling for a given type

type UnsetResult

type UnsetResult error

UnsetResult is error when Result is unset with a value.

type VaryingDataType

type VaryingDataType struct {
	// contains filtered or unexported fields
}

VaryingDataType is analogous to a rust enum. Name is taken from polkadot spec.

Example
package main

import (
	"fmt"
	"reflect"

	"github.com/ChainSafe/gossamer/pkg/scale"
)

type MyStruct struct {
	Baz bool
	Bar uint32
	Foo []byte
}

func (MyStruct) Index() uint {
	return 1
}

func (m MyStruct) String() string {
	return fmt.Sprintf("MyStruct{Baz: %t, Bar: %d, Foo: %x}", m.Baz, m.Bar, m.Foo)
}

type MyOtherStruct struct {
	Foo string
	Bar uint64
	Baz uint
}

func (MyOtherStruct) Index() uint {
	return 2
}

func (m MyOtherStruct) String() string {
	return fmt.Sprintf("MyOtherStruct{Foo: %s, Bar: %d, Baz: %d}", m.Foo, m.Bar, m.Baz)
}

type MyInt16 int16

func (MyInt16) Index() uint {
	return 3
}

func (m MyInt16) String() string { return fmt.Sprintf("MyInt16(%d)", m) }

func main() {
	vdt, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
	if err != nil {
		panic(err)
	}

	err = vdt.Set(MyStruct{
		Baz: true,
		Bar: 999,
		Foo: []byte{1, 2},
	})
	if err != nil {
		panic(err)
	}

	bytes, err := scale.Marshal(vdt)
	if err != nil {
		panic(err)
	}

	vdt1, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
	if err != nil {
		panic(err)
	}

	err = scale.Unmarshal(bytes, &vdt1)
	if err != nil {
		panic(err)
	}

	if !reflect.DeepEqual(vdt, vdt1) {
		panic(fmt.Errorf("uh oh: %+v %+v", vdt, vdt1))
	}
}
Output:

func MustNewVaryingDataType

func MustNewVaryingDataType(values ...VaryingDataTypeValue) (vdt VaryingDataType)

MustNewVaryingDataType is constructor for VaryingDataType

func NewVaryingDataType

func NewVaryingDataType(values ...VaryingDataTypeValue) (vdt VaryingDataType, err error)

NewVaryingDataType is constructor for VaryingDataType

func (*VaryingDataType) Set

func (vdt *VaryingDataType) Set(value VaryingDataTypeValue) (err error)

Set will set the VaryingDataType value

func (*VaryingDataType) String added in v0.8.0

func (vdt *VaryingDataType) String() string

func (*VaryingDataType) Value

func (vdt *VaryingDataType) Value() (VaryingDataTypeValue, error)

Value returns value stored in vdt

type VaryingDataTypeSlice

type VaryingDataTypeSlice struct {
	VaryingDataType
	Types []VaryingDataType
}

VaryingDataTypeSlice is used to represent []VaryingDataType. SCALE requires knowledge of the underlying data, so it is required to have the VaryingDataType required for decoding

Example
package main

import (
	"fmt"
	"reflect"

	"github.com/ChainSafe/gossamer/pkg/scale"
)

type MyStruct struct {
	Baz bool
	Bar uint32
	Foo []byte
}

func (MyStruct) Index() uint {
	return 1
}

func (m MyStruct) String() string {
	return fmt.Sprintf("MyStruct{Baz: %t, Bar: %d, Foo: %x}", m.Baz, m.Bar, m.Foo)
}

type MyOtherStruct struct {
	Foo string
	Bar uint64
	Baz uint
}

func (MyOtherStruct) Index() uint {
	return 2
}

func (m MyOtherStruct) String() string {
	return fmt.Sprintf("MyOtherStruct{Foo: %s, Bar: %d, Baz: %d}", m.Foo, m.Bar, m.Baz)
}

type MyInt16 int16

func (MyInt16) Index() uint {
	return 3
}

func (m MyInt16) String() string { return fmt.Sprintf("MyInt16(%d)", m) }

func main() {
	vdt, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
	if err != nil {
		panic(err)
	}

	vdts := scale.NewVaryingDataTypeSlice(vdt)

	err = vdts.Add(
		MyStruct{
			Baz: true,
			Bar: 999,
			Foo: []byte{1, 2},
		},
		MyInt16(1),
	)
	if err != nil {
		panic(err)
	}

	bytes, err := scale.Marshal(vdts)
	if err != nil {
		panic(err)
	}

	vdts1 := scale.NewVaryingDataTypeSlice(vdt)
	if err != nil {
		panic(err)
	}

	err = scale.Unmarshal(bytes, &vdts1)
	if err != nil {
		panic(err)
	}

	if !reflect.DeepEqual(vdts, vdts1) {
		panic(fmt.Errorf("uh oh: %+v %+v", vdts, vdts1))
	}
}
Output:

func NewVaryingDataTypeSlice

func NewVaryingDataTypeSlice(vdt VaryingDataType) (vdts VaryingDataTypeSlice)

NewVaryingDataTypeSlice is constructor for VaryingDataTypeSlice

func (*VaryingDataTypeSlice) Add

func (vdts *VaryingDataTypeSlice) Add(values ...VaryingDataTypeValue) (err error)

Add takes variadic parameter values to add VaryingDataTypeValue(s)

func (VaryingDataTypeSlice) String added in v0.8.0

func (vdts VaryingDataTypeSlice) String() string

type VaryingDataTypeValue

type VaryingDataTypeValue interface {
	Index() uint
}

VaryingDataTypeValue is used to represent scale encodable types of an associated VaryingDataType

type WrappedErr

type WrappedErr struct {
	Err interface{}
}

WrappedErr is returned by Result.Unwrap(). The underlying Err value is wrapped and stored in Err attribute

func (WrappedErr) Error

func (r WrappedErr) Error() string

Error fulfils the error interface

Jump to

Keyboard shortcuts

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