Skip to content

Commit

Permalink
codec; Enable custom scale methods (#597)
Browse files Browse the repository at this point in the history
* checks if type has Encode function

* Added DecodeCustom to handle calling Decode func

DecodeCustom handles calling Decode function for types that have Decode
defined, otherwise uses regular Scale Decoding functions

Signed-off-by: edwardmack <ed@edwardmack.com>

* go fmt'ed

Signed-off-by: edwardmack <ed@edwardmack.com>

* fixed lint rants

Signed-off-by: edwardmack <ed@edwardmack.com>

Co-authored-by: noot <36753753+noot@users.noreply.github.com>
  • Loading branch information
edwardmack and noot authored Feb 10, 2020
1 parent 1f0208c commit 6167ab6
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 5 deletions.
17 changes: 17 additions & 0 deletions codec/decode_ptr.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,23 @@ import (
"reflect"
)

// DecodeCustom check if interface has method Decode, if so use that, otherwise use regular scale decoding
func DecodeCustom(in []byte, t interface{}) error {
someType := reflect.TypeOf(t)
_, ok := someType.MethodByName("Decode")
if ok {
meth := reflect.ValueOf(t).MethodByName("Decode")
inVal := []reflect.Value{reflect.ValueOf(in)}
res := meth.Call(inVal)
err := res[0].Interface()
if err != nil {
return err.(error)
}
return nil
}
return DecodePtr(in, t)
}

// DecodePtr is the high level function wrapping the specific type decoding functions
// The results of decode are written to t interface by reference (instead of returning
// value as Decode does)
Expand Down
80 changes: 80 additions & 0 deletions codec/decode_ptr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ package codec

import (
"bytes"
"errors"
"math/big"
"reflect"
"testing"

"github.com/ChainSafe/gossamer/consensus/babe/types"
"github.com/ChainSafe/gossamer/crypto/sr25519"
"github.com/stretchr/testify/require"
)

func TestDecodePtrFixedWidthInts(t *testing.T) {
Expand Down Expand Up @@ -206,3 +211,78 @@ func TestDecodePtrArrays(t *testing.T) {
}
}
}

// test decoding with DecodeCustom on BabeHeader type
func TestDecodeCustom_DecodeBabeHeader(t *testing.T) {
// arbitrary test data
expected := &types.BabeHeader{
VrfOutput: [sr25519.VrfOutputLength]byte{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},
VrfProof: [sr25519.VrfProofLength]byte{120, 23, 235, 159, 115, 122, 207, 206, 123, 232, 75, 243, 115, 255, 131, 181, 219, 241, 200, 206, 21, 22, 238, 16, 68, 49, 86, 99, 76, 139, 39, 0, 102, 106, 181, 136, 97, 141, 187, 1, 234, 183, 241, 28, 27, 229, 133, 8, 32, 246, 245, 206, 199, 142, 134, 124, 226, 217, 95, 30, 176, 246, 5, 3},
BlockProducerIndex: 17,
SlotNumber: 420,
}
encoded := []byte{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, 120, 23, 235, 159, 115, 122, 207, 206, 123, 232, 75, 243, 115, 255, 131, 181, 219, 241, 200, 206, 21, 22, 238, 16, 68, 49, 86, 99, 76, 139, 39, 0, 102, 106, 181, 136, 97, 141, 187, 1, 234, 183, 241, 28, 27, 229, 133, 8, 32, 246, 245, 206, 199, 142, 134, 124, 226, 217, 95, 30, 176, 246, 5, 3, 17, 0, 0, 0, 0, 0, 0, 0, 164, 1, 0, 0, 0, 0, 0, 0}
decodedBabeHeader := new(types.BabeHeader)

err := DecodeCustom(encoded, decodedBabeHeader)
require.Nil(t, err)
require.Equal(t, expected, decodedBabeHeader)
}

// add Decode func to MockTypeA
func (tr *MockTypeA) Decode(in []byte) error {
return DecodePtr(in, tr)
}

// test decoding for MockTypeA (which has Decode func)
func TestDecodeCustom_DecodeMockTypeA(t *testing.T) {
expected := &MockTypeA{A: "hello"}
encoded := []byte{20, 104, 101, 108, 108, 111}
mockType := new(MockTypeA)

err := DecodeCustom(encoded, mockType)
require.Nil(t, err)
require.Equal(t, expected, mockType)
}

// test decoding for MockTypeB (which does not have Decode func)
func TestDecodeCustom_DecodeMockTypeB(t *testing.T) {
expected := &MockTypeB{A: "hello"}
encoded := []byte{20, 104, 101, 108, 108, 111}
mockType := new(MockTypeB)

err := DecodeCustom(encoded, mockType)
require.Nil(t, err)
require.Equal(t, expected, mockType)
}

// add Decode func to MockTypeC which will return fake data (so we'll know when it was called)
func (tr *MockTypeC) Decode(in []byte) error {
tr.A = "goodbye"
return nil
}

// test decoding for MockTypeC (which has Decode func that returns fake data (A: "goodbye"))
func TestDecodeCustom_DecodeMockTypeC(t *testing.T) {
expected := &MockTypeC{A: "goodbye"}
encoded := []byte{20, 104, 101, 108, 108, 111}
mockType := new(MockTypeC)

err := DecodeCustom(encoded, mockType)
require.Nil(t, err)
require.Equal(t, expected, mockType)
}

// add Decode func to MockTypeD which will return an error
func (tr *MockTypeD) Decode(in []byte) error {
return errors.New("error decoding")
}

// test decoding for MockTypeD (which has Decode func that returns error)
func TestDecodeCustom_DecodeMockTypeD(t *testing.T) {
encoded := []byte{20, 104, 101, 108, 108, 111}
mockType := new(MockTypeD)

err := DecodeCustom(encoded, mockType)
require.EqualError(t, err, "error decoding")
}
16 changes: 16 additions & 0 deletions codec/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,22 @@ type Encoder struct {
Writer io.Writer
}

// EncodeCustom check if interface has method Encode, if so use that, otherwise use regular scale encoding
func EncodeCustom(in interface{}) ([]byte, error) {
someType := reflect.TypeOf(in)
_, ok := someType.MethodByName("Encode")
if ok {
res := reflect.ValueOf(in).MethodByName("Encode").Call([]reflect.Value{})
val := res[0].Interface()
err := res[1].Interface()
if err != nil {
return val.([]byte), err.(error)
}
return val.([]byte), nil
}
return Encode(in)
}

// Encode to byte array
func Encode(in interface{}) ([]byte, error) {
buffer := bytes.Buffer{}
Expand Down
81 changes: 81 additions & 0 deletions codec/encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package codec

import (
"bytes"
"errors"
"math/big"
"reflect"
"strings"
Expand Down Expand Up @@ -210,3 +211,83 @@ func TestEncodeAndDecodeStringArrayInStruct(t *testing.T) {
require.Nil(t, err)
require.Equal(t, test, result, "Decoding failed")
}

// test type for encoding
type MockTypeA struct {
A string
}

// Encode func for TypeReal that uses actual Scale Encode
func (tr *MockTypeA) Encode() ([]byte, error) {
return Encode(tr)
}

// test to confirm EncodeCustom is return Scale Encoded result
func TestEncodeCustomMockTypeA(t *testing.T) {
test := &MockTypeA{A: "hello"}

encCust, err := EncodeCustom(test)
require.Nil(t, err)

encScale, err := Encode(test)
require.Nil(t, err)

require.Equal(t, encScale, encCust)
}

// test type for encoding, this type does not have Encode func
type MockTypeB struct {
A string
}

// test to confirm EncodeCustom is return Scale Encoded result
func TestEncodeCustomMockTypeB(t *testing.T) {
test := &MockTypeB{A: "hello"}

encCust, err := EncodeCustom(test)
require.Nil(t, err)

encScale, err := Encode(test)
require.Nil(t, err)

require.Equal(t, encScale, encCust)
}

// test types for encoding
type MockTypeC struct {
A string
}

// Encode func for MockTypeC that return fake byte array [1, 2, 3]
func (tr *MockTypeC) Encode() ([]byte, error) {
return []byte{1, 2, 3}, nil
}

// test to confirm EncodeCustom is using type's Encode function
func TestEncodeCustomMockTypeC(t *testing.T) {
test := &MockTypeC{A: "hello"}
expected := []byte{1, 2, 3}

encCust, err := EncodeCustom(test)
require.Nil(t, err)

require.Equal(t, expected, encCust)
}

// test types for encoding
type MockTypeD struct {
A string
}

// Encode func for MockTypeD that return an error
func (tr *MockTypeD) Encode() ([]byte, error) {
return nil, errors.New("error encoding")
}

// test to confirm EncodeCustom is handling errors
func TestEncodeCustomMockTypeD(t *testing.T) {
test := &MockTypeD{A: "hello"}

_, err := EncodeCustom(test)
require.EqualError(t, err, "error encoding")
}
6 changes: 1 addition & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ require (
github.com/disiqueira/gotree v1.0.0
github.com/ethereum/go-ethereum v1.9.6
github.com/filecoin-project/go-leb128 v0.0.0-20190212224330-8d79a5489543
github.com/golang/protobuf v1.3.2 // indirect
github.com/golang/snappy v0.0.1
github.com/golangci/golangci-lint v1.23.1 // indirect
github.com/google/go-cmp v0.3.1 // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/ipfs/go-datastore v0.3.1
Expand All @@ -24,16 +24,12 @@ require (
github.com/multiformats/go-multiaddr v0.2.0
github.com/naoina/go-stringutil v0.1.0 // indirect
github.com/naoina/toml v0.1.1
github.com/onsi/ginkgo v1.10.1 // indirect
github.com/onsi/gomega v1.7.0 // indirect
github.com/opentracing/opentracing-go v1.1.0 // indirect
github.com/stretchr/testify v1.4.0
github.com/urfave/cli v1.20.0
github.com/wasmerio/go-ext-wasm v0.0.0-20191206132826-225d01fcd22c
golang.org/x/crypto v0.0.0-20191227163750-53104e6ec876
golang.org/x/net v0.0.0-20190916140828-c8589233b77d // indirect
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
gopkg.in/yaml.v2 v2.2.7 // indirect
)

go 1.13

0 comments on commit 6167ab6

Please sign in to comment.