diff --git a/codec/decode_ptr.go b/codec/decode_ptr.go index a43a7e79af..32a66afdbf 100644 --- a/codec/decode_ptr.go +++ b/codec/decode_ptr.go @@ -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) diff --git a/codec/decode_ptr_test.go b/codec/decode_ptr_test.go index 37d2675941..1d6ccc91c0 100644 --- a/codec/decode_ptr_test.go +++ b/codec/decode_ptr_test.go @@ -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) { @@ -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") +} diff --git a/codec/encode.go b/codec/encode.go index fc7f9930d4..7bc861f591 100644 --- a/codec/encode.go +++ b/codec/encode.go @@ -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{} diff --git a/codec/encode_test.go b/codec/encode_test.go index f83e0b2753..0943b569a8 100644 --- a/codec/encode_test.go +++ b/codec/encode_test.go @@ -18,6 +18,7 @@ package codec import ( "bytes" + "errors" "math/big" "reflect" "strings" @@ -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") +} diff --git a/go.mod b/go.mod index c44521f440..cc77af72b1 100644 --- a/go.mod +++ b/go.mod @@ -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 @@ -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