From 1b18f25dbf81a350fd234f0bbdc2c011136860a7 Mon Sep 17 00:00:00 2001 From: ilija Date: Fri, 24 Jan 2025 17:02:07 +0100 Subject: [PATCH] Fix Solana codec encoder and decoder lenient codecs init --- pkg/solana/codec/decoder.go | 36 +++++++++++++++++++++----------- pkg/solana/codec/decoder_test.go | 28 +++++++++---------------- pkg/solana/codec/encoder.go | 28 ++++++++++++++++++------- pkg/solana/codec/encoder_test.go | 31 +++++++-------------------- pkg/solana/codec/parsed_types.go | 4 ++-- pkg/solana/codec/solana.go | 4 ++-- 6 files changed, 66 insertions(+), 65 deletions(-) diff --git a/pkg/solana/codec/decoder.go b/pkg/solana/codec/decoder.go index ec734b36a..a988c88e6 100644 --- a/pkg/solana/codec/decoder.go +++ b/pkg/solana/codec/decoder.go @@ -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) diff --git a/pkg/solana/codec/decoder_test.go b/pkg/solana/codec/decoder_test.go index 0f21c43e8..6576d941d 100644 --- a/pkg/solana/codec/decoder_test.go +++ b/pkg/solana/codec/decoder_test.go @@ -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)) }) } @@ -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) }) } diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go index e3f734768..0b528ff79 100644 --- a/pkg/solana/codec/encoder.go +++ b/pkg/solana/codec/encoder.go @@ -8,14 +8,25 @@ 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) @@ -23,16 +34,17 @@ func (e *Encoder) Encode(ctx context.Context, item any, itemType string) (res [] }() 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) diff --git a/pkg/solana/codec/encoder_test.go b/pkg/solana/codec/encoder_test.go index fb098d884..105f95dcf 100644 --- a/pkg/solana/codec/encoder_test.go +++ b/pkg/solana/codec/encoder_test.go @@ -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") }) } @@ -81,8 +72,7 @@ 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") @@ -90,13 +80,8 @@ func TestEncoder_GetMaxEncodingSize_Errors(t *testing.T) { 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") }) diff --git a/pkg/solana/codec/parsed_types.go b/pkg/solana/codec/parsed_types.go index 3144f6dd0..d0d1d4693 100644 --- a/pkg/solana/codec/parsed_types.go +++ b/pkg/solana/codec/parsed_types.go @@ -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...) diff --git a/pkg/solana/codec/solana.go b/pkg/solana/codec/solana.go index 3323ced10..2e7c1f821 100644 --- a/pkg/solana/codec/solana.go +++ b/pkg/solana/codec/solana.go @@ -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 }