Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(lib/scale): Revise VaryingDataType interfaces, and introduce VaryingDataTypeSlice #1651

Merged
merged 9 commits into from
Jun 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 63 additions & 22 deletions pkg/scale/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ import (
"github.com/ChainSafe/gossamer/pkg/scale"
)

func basicExample() {
func ExampleBasic() {
// compact length encoded uint
var ui uint = 999
bytes, err := scale.Marshal(ui)
Expand Down Expand Up @@ -117,7 +117,7 @@ import (
"github.com/ChainSafe/gossamer/pkg/scale"
)

func structExample() {
func ExampleStruct() {
type MyStruct struct {
Baz bool `scale:"3"`
Bar int32 `scale:"2"`
Expand Down Expand Up @@ -165,7 +165,7 @@ import (
"github.com/ChainSafe/gossamer/pkg/scale"
)

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

Expand Down Expand Up @@ -210,10 +210,8 @@ func resultExample() {

### Varying Data Type

A `VaryingDataType` is analogous to a Rust enum. A `VaryingDataType` needs to be registered using the `RegisterVaryingDataType` function with its associated `VaryingDataTypeValue` types. `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.

> TODO: The only custom `VaryingDataTypeValue` types supported are currently `struct`, `int`, and `int16`. Need to add other supported primitives.
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 (
Expand Down Expand Up @@ -247,39 +245,82 @@ func (mi16 MyInt16) Index() uint {
return 3
}

type MyVaryingDataType scale.VaryingDataType
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 varyingDataTypeExample() {
err := scale.RegisterVaryingDataType(MyVaryingDataType{}, MyStruct{}, MyOtherStruct{}, MyInt16(0))
```
func ExampleVaryingDataTypeSlice() {
vdt, err := scale.NewVaryingDataType(MyStruct{}, MyOtherStruct{}, MyInt16(0))
if err != nil {
panic(err)
}

mvdt := MyVaryingDataType{
vdts := scale.NewVaryingDataTypeSlice(vdt)

err = vdts.Add(
MyStruct{
Baz: true,
Bar: 999,
Foo: []byte{1, 2},
},
MyOtherStruct{
Foo: "hello",
Bar: 999,
Baz: 888,
},
MyInt16(111),
MyInt16(1),
)
if err != nil {
panic(err)
}
bytes, err := scale.Marshal(mvdt)

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

var unmarshaled MyVaryingDataType
err = scale.Unmarshal(bytes, &unmarshaled)
vdts1 := scale.NewVaryingDataTypeSlice(vdt)
if err != nil {
panic(err)
}

// [{Baz:true Bar:999 Foo:[1 2]} {Foo:hello Bar:999 Baz:888} 111]
fmt.Printf("%+v", unmarshaled)
err = scale.Unmarshal(bytes, &vdts1)
if err != nil {
panic(err)
}

if !reflect.DeepEqual(vdts, vdts1) {
panic(fmt.Errorf("uh oh: %+v %+v", vdts, vdts1))
}
}
```
66 changes: 43 additions & 23 deletions pkg/scale/comparison_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (prd SealDigest) Index() uint {
}

func TestOldVsNewEncoding(t *testing.T) {
oldDigest := types.Digest{
oldDigests := types.Digest{
&types.ChangesTrieRootDigest{
Hash: common.Hash{0, 91, 50, 25, 214, 94, 119, 36, 71, 216, 33, 152, 85, 184, 34, 120, 61, 161, 164, 223, 76, 53, 40, 246, 76, 38, 235, 204, 43, 31, 179, 28},
},
Expand All @@ -81,34 +81,53 @@ func TestOldVsNewEncoding(t *testing.T) {
Data: []byte{1, 3, 5, 7},
},
}
oldEncode, err := oldDigest.Encode()
oldEncode, err := oldDigests.Encode()
if err != nil {
t.Errorf("unexpected err: %v", err)
return
}

type Digests VaryingDataType
err = RegisterVaryingDataType(Digests{}, ChangesTrieRootDigest{}, PreRuntimeDigest{}, ConsensusDigest{}, SealDigest{})
vdt, err := NewVaryingDataType(ChangesTrieRootDigest{}, PreRuntimeDigest{}, ConsensusDigest{}, SealDigest{})
if err != nil {
t.Errorf("unexpected err: %v", err)
return
}
newDigest := Digests{
ChangesTrieRootDigest{
Hash: common.Hash{0, 91, 50, 25, 214, 94, 119, 36, 71, 216, 33, 152, 85, 184, 34, 120, 61, 161, 164, 223, 76, 53, 40, 246, 76, 38, 235, 204, 43, 31, 179, 28},
},
PreRuntimeDigest{
ConsensusEngineID: types.BabeEngineID,
Data: []byte{1, 3, 5, 7},
},
ConsensusDigest{
ConsensusEngineID: types.BabeEngineID,
Data: []byte{1, 3, 5, 7},
},
SealDigest{
ConsensusEngineID: types.BabeEngineID,
Data: []byte{1, 3, 5, 7},
},
err = vdt.Set(ChangesTrieRootDigest{
Hash: common.Hash{0, 91, 50, 25, 214, 94, 119, 36, 71, 216, 33, 152, 85, 184, 34, 120, 61, 161, 164, 223, 76, 53, 40, 246, 76, 38, 235, 204, 43, 31, 179, 28},
})
if err != nil {
t.Errorf("unexpected err: %v", err)
return
}

newDigest := []VaryingDataType{
mustNewVaryingDataTypeAndSet(
ChangesTrieRootDigest{
Hash: common.Hash{0, 91, 50, 25, 214, 94, 119, 36, 71, 216, 33, 152, 85, 184, 34, 120, 61, 161, 164, 223, 76, 53, 40, 246, 76, 38, 235, 204, 43, 31, 179, 28},
},
ChangesTrieRootDigest{}, PreRuntimeDigest{}, ConsensusDigest{}, SealDigest{},
),
mustNewVaryingDataTypeAndSet(
PreRuntimeDigest{
ConsensusEngineID: types.BabeEngineID,
Data: []byte{1, 3, 5, 7},
},
ChangesTrieRootDigest{}, PreRuntimeDigest{}, ConsensusDigest{}, SealDigest{},
),
mustNewVaryingDataTypeAndSet(
ConsensusDigest{
ConsensusEngineID: types.BabeEngineID,
Data: []byte{1, 3, 5, 7},
},
ChangesTrieRootDigest{}, PreRuntimeDigest{}, ConsensusDigest{}, SealDigest{},
),
mustNewVaryingDataTypeAndSet(
SealDigest{
ConsensusEngineID: types.BabeEngineID,
Data: []byte{1, 3, 5, 7},
},
ChangesTrieRootDigest{}, PreRuntimeDigest{}, ConsensusDigest{}, SealDigest{},
),
}

newEncode, err := Marshal(newDigest)
Expand All @@ -120,13 +139,14 @@ func TestOldVsNewEncoding(t *testing.T) {
t.Errorf("encodeState.encodeStruct() = %v, want %v", oldEncode, newEncode)
}

var decoded Digests
decoded := NewVaryingDataTypeSlice(vdt)
err = Unmarshal(newEncode, &decoded)
if err != nil {
t.Errorf("unexpected err: %v", err)
}
if !reflect.DeepEqual(decoded, newDigest) {
t.Errorf("Unmarshal() = %v, want %v", decoded, newDigest)
// decoded.Types
if !reflect.DeepEqual(decoded.Types, newDigest) {
t.Errorf("Unmarshal() = %v, want %v", decoded.Types, newDigest)
}
}

Expand Down
Loading