Skip to content

Commit

Permalink
Fix Solana codec encoder and decoder lenient codecs init
Browse files Browse the repository at this point in the history
  • Loading branch information
ilija42 committed Jan 24, 2025
1 parent b3093a3 commit 1b18f25
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 65 deletions.
36 changes: 24 additions & 12 deletions pkg/solana/codec/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,43 @@ import (
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
)

type Decoder struct {
definitions map[string]Entry
lenientFromTypeCodec encodings.LenientCodecFromTypeCodec
// decoder should be initialized with newDecoder
type decoder struct {
definitions map[string]Entry
lenientCodecFromTypeCodec encodings.LenientCodecFromTypeCodec
}

var _ commontypes.Decoder = &Decoder{}
func newDecoder(definitions map[string]Entry) commontypes.Decoder {
lenientCodecFromTypeCodec := make(encodings.LenientCodecFromTypeCodec)
for k, v := range definitions {
lenientCodecFromTypeCodec[k] = v
}

return &decoder{
definitions: definitions,
lenientCodecFromTypeCodec: lenientCodecFromTypeCodec,
}
}

func (d *Decoder) Decode(ctx context.Context, raw []byte, into any, itemType string) (err error) {
func (d *decoder) Decode(ctx context.Context, raw []byte, into any, itemType string) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from: %v, while decoding %q", r, itemType)
}
}()

if d.lenientFromTypeCodec == nil {
d.lenientFromTypeCodec = make(encodings.LenientCodecFromTypeCodec)
for k, v := range d.definitions {
d.lenientFromTypeCodec[k] = v
}
if d.lenientCodecFromTypeCodec == nil {
return fmt.Errorf("decoder is not properly initalised, underlying lenientCodecFromTypeCodec is nil")
}

return d.lenientFromTypeCodec.Decode(ctx, raw, into, itemType)
return d.lenientCodecFromTypeCodec.Decode(ctx, raw, into, itemType)
}

func (d *Decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) {
func (d *decoder) GetMaxDecodingSize(_ context.Context, n int, itemType string) (int, error) {
if d.definitions == nil {
return 0, fmt.Errorf("decoder is not properly initalised, type definitions are nil")
}

codecEntry, ok := d.definitions[itemType]
if !ok {
return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType)
Expand Down
28 changes: 10 additions & 18 deletions pkg/solana/codec/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,24 +31,20 @@ func TestDecoder_Decode_Errors(t *testing.T) {
var into interface{}
someType := "some-type"
t.Run("error when item type not found", func(t *testing.T) {
d := &Decoder{definitions: map[string]Entry{}}
d.definitions[someType] = &entry{}

nonExistentType := "non-existent"
err := d.Decode(tests.Context(t), []byte{}, &into, nonExistentType)
err := newDecoder(map[string]Entry{someType: &entry{}}).
Decode(tests.Context(t), []byte{}, &into, nonExistentType)
require.ErrorIs(t, err, fmt.Errorf("%w: cannot find type %s", commontypes.ErrInvalidType, nonExistentType))
})

t.Run("error when underlying entry decode fails", func(t *testing.T) {
d := &Decoder{definitions: map[string]Entry{}}
d.definitions[someType] = &testErrDecodeEntry{}
require.Error(t, d.Decode(tests.Context(t), []byte{}, &into, someType))
require.Error(t, newDecoder(map[string]Entry{someType: &testErrDecodeEntry{}}).
Decode(tests.Context(t), []byte{}, &into, someType))
})

t.Run("remaining bytes exist after decode is ok", func(t *testing.T) {
d := &Decoder{definitions: map[string]Entry{}}
d.definitions[someType] = &testErrDecodeRemainingBytes{}
require.NoError(t, d.Decode(tests.Context(t), []byte{}, &into, someType))
require.NoError(t, newDecoder(map[string]Entry{someType: &testErrDecodeRemainingBytes{}}).
Decode(tests.Context(t), []byte{}, &into, someType))
})
}

Expand All @@ -72,19 +68,15 @@ func TestDecoder_GetMaxDecodingSize_Errors(t *testing.T) {
someType := "some-type"

t.Run("error when entry for item type is missing", func(t *testing.T) {
d := &Decoder{definitions: map[string]Entry{}}
d.definitions[someType] = &entry{}

nonExistentType := "non-existent"
_, err := d.GetMaxDecodingSize(tests.Context(t), 0, nonExistentType)
_, err := newDecoder(map[string]Entry{someType: &entry{}}).
GetMaxDecodingSize(tests.Context(t), 0, nonExistentType)
require.ErrorIs(t, err, fmt.Errorf("%w: cannot find type %s", commontypes.ErrInvalidType, nonExistentType))
})

t.Run("error when underlying entry decode fails", func(t *testing.T) {
d := &Decoder{definitions: map[string]Entry{}}
d.definitions[someType] = &testErrGetMaxDecodingSize{}

_, err := d.GetMaxDecodingSize(tests.Context(t), 0, someType)
_, err := newDecoder(map[string]Entry{someType: &testErrGetMaxDecodingSize{}}).
GetMaxDecodingSize(tests.Context(t), 0, someType)
require.Error(t, err)
})
}
28 changes: 20 additions & 8 deletions pkg/solana/codec/encoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,43 @@ import (
commontypes "github.com/smartcontractkit/chainlink-common/pkg/types"
)

type Encoder struct {
// encoder should be initialized with newEncoder
type encoder struct {
definitions map[string]Entry
lenientCodecFromTypeCodec encodings.LenientCodecFromTypeCodec
}

var _ commontypes.Encoder = &Encoder{}
func newEncoder(definitions map[string]Entry) commontypes.Encoder {
lenientCodecFromTypeCodec := make(encodings.LenientCodecFromTypeCodec)
for k, v := range definitions {
lenientCodecFromTypeCodec[k] = v
}

return &encoder{
lenientCodecFromTypeCodec: lenientCodecFromTypeCodec,
definitions: definitions,
}
}

func (e *Encoder) Encode(ctx context.Context, item any, itemType string) (res []byte, err error) {
func (e *encoder) Encode(ctx context.Context, item any, itemType string) (res []byte, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("recovered from: %v, while encoding %q", r, itemType)
}
}()

if e.lenientCodecFromTypeCodec == nil {
e.lenientCodecFromTypeCodec = make(encodings.LenientCodecFromTypeCodec)
for k, v := range e.definitions {
e.lenientCodecFromTypeCodec[k] = v
}
return nil, fmt.Errorf("encoder is not properly initalised, underlying lenientCodecFromTypeCodec is nil")
}

return e.lenientCodecFromTypeCodec.Encode(ctx, item, itemType)
}

func (e *Encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) {
func (e *encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) (int, error) {
if e.definitions == nil {
return 0, fmt.Errorf("encoder is not properly initalised, type definitions are nil")
}

entry, ok := e.definitions[itemType]
if !ok {
return 0, fmt.Errorf("%w: nil entry", commontypes.ErrInvalidType)
Expand Down
31 changes: 8 additions & 23 deletions pkg/solana/codec/encoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,30 +38,21 @@ func TestEncoder_Encode_Errors(t *testing.T) {
someType := "some-type"

t.Run("error when item type not found", func(t *testing.T) {
e := &Encoder{definitions: map[string]Entry{}}
_, err := e.Encode(tests.Context(t), nil, "non-existent-type")
_, err := newEncoder(map[string]Entry{}).
Encode(tests.Context(t), nil, "non-existent-type")
require.Error(t, err)
require.ErrorIs(t, err, commontypes.ErrInvalidType)
require.Contains(t, err.Error(), "cannot find type non-existent-type")
})

t.Run("error when convert fails because of unexpected type", func(t *testing.T) {
e := &Encoder{
definitions: map[string]Entry{
someType: &testErrEncodeEntry{},
},
}
_, err := e.Encode(tests.Context(t), nil, someType)
_, err := newEncoder(map[string]Entry{someType: &testErrEncodeEntry{}}).Encode(tests.Context(t), nil, someType)
require.Error(t, err)
})

t.Run("error when entry encode fails", func(t *testing.T) {
e := &Encoder{
definitions: map[string]Entry{
someType: &testErrEncodeEntry{codecType: commonencodings.Empty{}},
},
}
_, err := e.Encode(tests.Context(t), make(map[string]interface{}), someType)
_, err := newEncoder(map[string]Entry{someType: &testErrEncodeEntry{codecType: commonencodings.Empty{}}}).
Encode(tests.Context(t), make(map[string]interface{}), someType)
require.ErrorContains(t, err, "encode error")
})
}
Expand All @@ -81,22 +72,16 @@ func (t testErrGetSize) Size(_ int) (int, error) {

func TestEncoder_GetMaxEncodingSize_Errors(t *testing.T) {
t.Run("error when entry for item type is missing", func(t *testing.T) {
e := &Encoder{definitions: map[string]Entry{}}
_, err := e.GetMaxEncodingSize(tests.Context(t), 10, "no-entry-type")
_, err := newEncoder(map[string]Entry{}).GetMaxEncodingSize(tests.Context(t), 10, "no-entry-type")
require.Error(t, err)
require.ErrorIs(t, err, commontypes.ErrInvalidType)
require.Contains(t, err.Error(), "nil entry")
})

t.Run("error when size calculation fails", func(t *testing.T) {
someType := "some-type"
e := &Encoder{
definitions: map[string]Entry{
someType: &testErrEncodeTypeEntry{tCodec: testErrGetSize{}},
},
}

_, err := e.GetMaxEncodingSize(tests.Context(t), 0, someType)
_, err := newEncoder(map[string]Entry{someType: &testErrEncodeTypeEntry{tCodec: testErrGetSize{}}}).
GetMaxEncodingSize(tests.Context(t), 0, someType)
require.Error(t, err)
require.Contains(t, err.Error(), "size error")
})
Expand Down
4 changes: 2 additions & 2 deletions pkg/solana/codec/parsed_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func (parsed *ParsedTypes) ToCodec() (commontypes.RemoteCodec, error) {
return nil, err
}
underlying := &solanaCodec{
Encoder: &Encoder{definitions: parsed.EncoderDefs},
Decoder: &Decoder{definitions: parsed.DecoderDefs},
Encoder: newEncoder(parsed.EncoderDefs),
Decoder: newDecoder(parsed.DecoderDefs),
ParsedTypes: parsed,
}
return commoncodec.NewModifierCodec(underlying, mod, DecoderHooks...)
Expand Down
4 changes: 2 additions & 2 deletions pkg/solana/codec/solana.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ const (
var DecoderHooks = []mapstructure.DecodeHookFunc{commoncodec.EpochToTimeHook, commoncodec.BigIntHook, commoncodec.SliceToArrayVerifySizeHook}

type solanaCodec struct {
*Encoder
*Decoder
commontypes.Encoder
commontypes.Decoder
*ParsedTypes
}

Expand Down

0 comments on commit 1b18f25

Please sign in to comment.