Skip to content

Commit

Permalink
refactor(lib/scale): Revise VaryingDataType interfaces, and introduce…
Browse files Browse the repository at this point in the history
… VaryingDataTypeSlice (#1651)

* wip VaryingDataType and VaryingDataTypeSlice

* refactor VaryingDataType and add VaryingDataTypeSlice

* update README

* fix lint

* update examples

* fix deepsource

* check decoded in comparison_test

* cr feedback

* fix result.set with nil value
  • Loading branch information
timwu20 committed Jun 23, 2021
1 parent a12f505 commit 5a423b7
Show file tree
Hide file tree
Showing 13 changed files with 975 additions and 232 deletions.
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

0 comments on commit 5a423b7

Please sign in to comment.