From 5b636b9146946117fae28a12acf30c7ebb57e942 Mon Sep 17 00:00:00 2001 From: ilija Date: Tue, 10 Dec 2024 19:37:49 -0500 Subject: [PATCH] temp --- go.mod | 2 +- pkg/solana/codec/codec_test.go | 146 ++++++++++++++++-- pkg/solana/codec/discriminator.go | 14 +- pkg/solana/codec/encoder.go | 75 +++++++++ pkg/solana/codec/solana.go | 3 +- .../codec/testutils/itemArray1TypeIDL.json | 1 + pkg/solana/codec/testutils/itemIDL.json | 9 +- 7 files changed, 225 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index f9363c1cc..feef696a5 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,6 @@ require ( go.uber.org/zap v1.27.0 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 golang.org/x/sync v0.8.0 - golang.org/x/text v0.18.0 ) require ( @@ -124,6 +123,7 @@ require ( golang.org/x/net v0.29.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.3.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect diff --git a/pkg/solana/codec/codec_test.go b/pkg/solana/codec/codec_test.go index bae12497d..f531c7205 100644 --- a/pkg/solana/codec/codec_test.go +++ b/pkg/solana/codec/codec_test.go @@ -1,9 +1,14 @@ package codec_test import ( + "bytes" _ "embed" "encoding/json" + "fmt" + "math/big" + "reflect" "slices" + "strings" "testing" bin "github.com/gagliardetto/binary" @@ -16,6 +21,7 @@ import ( clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec" + "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/generated/test_item_type" "github.com/smartcontractkit/chainlink-solana/pkg/solana/codec/testutils" ) @@ -34,14 +40,8 @@ type codecInterfaceTester struct { func (it *codecInterfaceTester) Setup(_ *testing.T) {} func (it *codecInterfaceTester) GetAccountBytes(i int) []byte { - account := [20]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - // fuzz tests can make -ve numbers - if i < 0 { - i = -i - } - account[i%20] += byte(i) - account[(i+3)%20] += byte(i + 3) - return account[:] + pk, _ := solana.NewRandomPrivateKey() + return pk.PublicKey().Bytes() } func (it *codecInterfaceTester) GetAccountString(i int) string { @@ -57,11 +57,56 @@ func (it *codecInterfaceTester) EncodeFields(t *testing.T, request *EncodeReques } func encodeFieldsOnItem(t *testing.T, request *EncodeRequest) ocr2types.Report { - borshBytes, err := bin.MarshalBorsh(argsFromTestStruct(request.TestStructs[0])) - if err != nil { - t.Errorf(err.Error()) + buf := new(bytes.Buffer) + if err := encodeRequestToTestStruct(request).MarshalWithEncoder(bin.NewBorshEncoder(buf)); err != nil { + require.NoError(t, err) } - return borshBytes + return buf.Bytes() +} + +func encodeRequestToTestStruct(request *EncodeRequest) test_item_type.TestStruct { + byt := [32]byte{} + for i, v := range request.TestStructs[0].OracleIDs { + byt[i] = byte(v) + } + + k, _ := solana.PublicKeyFromBase58(request.TestStructs[0].AccountStruct.AccountStr) + + accs := make([]solana.PublicKey, len(request.TestStructs[0].Accounts)) + for i, v := range request.TestStructs[0].Accounts { + accs[i] = solana.PublicKeyFromBytes(v) + } + + testStruct := test_item_type.TestStruct{ + Field: *request.TestStructs[0].Field, + OracleId: uint8(request.TestStructs[0].OracleID), + OracleIds: byt, + AccountStruct: test_item_type.AccountStruct{ + Account: solana.PublicKeyFromBytes(request.TestStructs[0].AccountStruct.Account), + AccountStr: k, + }, + Accounts: accs, + DifferentField: request.TestStructs[0].DifferentField, + BigField: bin.Int128{ + Lo: request.TestStructs[0].BigField.Uint64(), + Hi: new(big.Int).Rsh(request.TestStructs[0].BigField, 64).Uint64(), + }, + NestedDynamicStruct: test_item_type.NestedDynamic{ + FixedBytes: request.TestStructs[0].NestedDynamicStruct.FixedBytes, + Inner: test_item_type.InnerDynamic{ + IntVal: int64(request.TestStructs[0].NestedDynamicStruct.Inner.I), + S: request.TestStructs[0].NestedDynamicStruct.Inner.S, + }, + }, + NestedStaticStruct: test_item_type.NestedStatic{ + FixedBytes: request.TestStructs[0].NestedStaticStruct.FixedBytes, + Inner: test_item_type.InnerStatic{ + IntVal: int64(request.TestStructs[0].NestedStaticStruct.Inner.I), + A: solana.PublicKeyFromBytes(request.TestStructs[0].NestedStaticStruct.Inner.A), + }, + }, + } + return testStruct } func encodeFieldsOnSliceOrArray(t *testing.T, request *EncodeRequest) []byte { @@ -79,7 +124,7 @@ func encodeFieldsOnSliceOrArray(t *testing.T, request *EncodeRequest) []byte { args[0] = tmp } - borshBytes, err := bin.MarshalBorsh(argsFromTestStruct(request.TestStructs[0])) + borshBytes, err := bin.MarshalBorsh(cpy) if err != nil { t.Errorf(err.Error()) } @@ -101,6 +146,7 @@ func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { if k != sizeItemType && k != NilType { entry.ModifierConfigs = commoncodec.ModifiersConfig{ + &commoncodec.DropModifierConfig{Fields: []string{"DiscriminatorTestStruct"}}, &commoncodec.RenameModifierConfig{Fields: map[string]string{"NestedDynamicStruct.Inner.IntVal": "I"}}, &commoncodec.RenameModifierConfig{Fields: map[string]string{"NestedStaticStruct.Inner.IntVal": "I"}}, } @@ -111,6 +157,8 @@ func (it *codecInterfaceTester) GetCodec(t *testing.T) clcommontypes.Codec { Fields: []string{"AccountStruct.AccountStr"}, Modifier: codec.SolanaAddressModifier{}, } + //cpy {Field:0 OracleID:1 OracleIDs:[2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] AccountStruct:{Account:[230 246 124 140 101 222 233 141 21 37 83 232 157 45 101 15 165 209 220 244 224 64 240 236 28 111 178 18 51 245 55 105] AccountStr:5rujdkG6iAzdtzf1JPjGF82tjeSwqM1QV4KCtrqaSNrX} Accounts:[[202 126 200 254 65 52 23 23 136 249 239 73 81 38 216 127 205 238 235 253 174 61 44 136 35 46 156 74 233 63 28 226] [202 195 18 0 144 219 77 163 99 129 127 77 185 142 8 121 12 253 71 49 75 123 189 213 118 119 129 87 26 74 71 177]] DifferentField:field0 BigField:{} NestedDynamicStruct:{FixedBytes:[0 1] Inner:{I:0 S:field0}} NestedStaticStruct:{FixedBytes:[0 1] Inner:{I:0 A:[3 134 218 122 6 116 161 93 12 75 126 131 168 213 211 126 159 168 145 110 168 226 230 212 110 87 25 251 255 78 130 55]}}} + //to encode: {DiscriminatorTestStruct:nil Field:0 DifferentField:field0 OracleId:1 OracleIds:[2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] AccountStruct:{Account:[230 246 124 140 101 222 233 141 21 37 83 232 157 45 101 15 165 209 220 244 224 64 240 236 28 111 178 18 51 245 55 105] AccountStr:[72 55 213 199 102 214 15 160 144 134 159 170 4 211 4 50 206 167 85 101 28 99 67 254 12 247 191 123 214 119 110 228]} Accounts:[[202 126 200 254 65 52 23 23 136 249 239 73 81 38 216 127 205 238 235 253 174 61 44 136 35 46 156 74 233 63 28 226] [202 195 18 0 144 219 77 163 99 129 127 77 185 142 8 121 12 253 71 49 75 123 189 213 118 119 129 87 26 74 71 177]] BigField:{} NestedDynamicStruct:{FixedBytes:[0 1] Inner:{IntVal:0 S:field0}} NestedStaticStruct:{FixedBytes:[0 1] Inner:{IntVal:0 A:[3 134 218 122 6 116 161 93 12 75 126 131 168 213 211 126 159 168 145 110 168 226 230 212 110 87 25 251 255 78 130 55]}}} entry.ModifierConfigs = append(entry.ModifierConfigs, addressByteModifier) } @@ -261,3 +309,75 @@ var codecDefs = map[string]string{ //TestItemWithConfigExtra: itemWithConfigExtraJSONIDL, //NilType: nilTypeJSONIDL, } + +// LogFields recursively logs fields of a struct or pointer to a struct with tight formatting. +func LogFields(v interface{}) { + logFields(reflect.ValueOf(v), "") +} + +func logFields(v reflect.Value, indent string) { + str := valueToString(v) + fmt.Printf("%s%s\n", indent, str) +} + +func valueToString(v reflect.Value) string { + // Resolve pointers to their underlying value + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "nil" + } + v = v.Elem() + } + + switch v.Kind() { + case reflect.Struct: + t := v.Type() + var fields []string + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := t.Field(i) + + // Skip unexported fields + if !field.CanInterface() { + continue + } + + fieldName := fieldType.Name + fieldValue := valueToString(field) + fields = append(fields, fmt.Sprintf("%s:%s", fieldName, fieldValue)) + } + return fmt.Sprintf("{%s}", strings.Join(fields, " ")) + case reflect.Map: + if v.Len() == 0 { + return "{}" + } + var pairs []string + for _, key := range v.MapKeys() { + value := v.MapIndex(key) + keyStr := valueToString(key) + valueStr := valueToString(value) + pairs = append(pairs, fmt.Sprintf("%s:%s", keyStr, valueStr)) + } + return fmt.Sprintf("map[%s]", strings.Join(pairs, " ")) + case reflect.Slice, reflect.Array: + if v.Len() == 0 { + return "[]" + } + var elems []string + for i := 0; i < v.Len(); i++ { + elem := v.Index(i) + elemStr := valueToString(elem) + elems = append(elems, elemStr) + } + return fmt.Sprintf("[%s]", strings.Join(elems, " ")) + case reflect.Invalid: + return "nil" + default: + // Check if the value can be interfaced + if v.CanInterface() { + return fmt.Sprintf("%v", v.Interface()) + } else { + return "" + } + } +} diff --git a/pkg/solana/codec/discriminator.go b/pkg/solana/codec/discriminator.go index f712a3f68..7037d5687 100644 --- a/pkg/solana/codec/discriminator.go +++ b/pkg/solana/codec/discriminator.go @@ -14,14 +14,14 @@ const discriminatorLength = 8 func NewDiscriminator(name string) encodings.TypeCodec { sum := sha256.Sum256([]byte("account:" + name)) - return &discriminator{hashPrefix: sum[:discriminatorLength]} + return &Discriminator{hashPrefix: sum[:discriminatorLength]} } -type discriminator struct { +type Discriminator struct { hashPrefix []byte } -func (d discriminator) Encode(value any, into []byte) ([]byte, error) { +func (d Discriminator) Encode(value any, into []byte) ([]byte, error) { if value == nil { return append(into, d.hashPrefix...), nil } @@ -44,7 +44,7 @@ func (d discriminator) Encode(value any, into []byte) ([]byte, error) { return append(into, *raw...), nil } -func (d discriminator) Decode(encoded []byte) (any, []byte, error) { +func (d Discriminator) Decode(encoded []byte) (any, []byte, error) { raw, remaining, err := encodings.SafeDecode(encoded, discriminatorLength, func(raw []byte) []byte { return raw }) if err != nil { return nil, nil, err @@ -57,15 +57,15 @@ func (d discriminator) Decode(encoded []byte) (any, []byte, error) { return &raw, remaining, nil } -func (d discriminator) GetType() reflect.Type { +func (d Discriminator) GetType() reflect.Type { // Pointer type so that nil can inject values and so that the NamedCodec won't wrap with no-nil pointer. return reflect.TypeOf(&[]byte{}) } -func (d discriminator) Size(_ int) (int, error) { +func (d Discriminator) Size(_ int) (int, error) { return discriminatorLength, nil } -func (d discriminator) FixedSize() (int, error) { +func (d Discriminator) FixedSize() (int, error) { return discriminatorLength, nil } diff --git a/pkg/solana/codec/encoder.go b/pkg/solana/codec/encoder.go index 2605bc9ee..227b917a3 100644 --- a/pkg/solana/codec/encoder.go +++ b/pkg/solana/codec/encoder.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "reflect" + "strings" "github.com/smartcontractkit/chainlink-common/pkg/codec" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -39,6 +40,8 @@ func (e *encoder) Encode(_ context.Context, item any, itemType string) (res []by } } + fmt.Printf("about to log these: ") + LogFields(item) return info.Encode(item, nil) } @@ -49,3 +52,75 @@ func (e *encoder) GetMaxEncodingSize(_ context.Context, n int, itemType string) } return entry.GetCodecType().Size(n) } + +// LogFields recursively logs fields of a struct or pointer to a struct with tight formatting. +func LogFields(v interface{}) { + logFields(reflect.ValueOf(v), "") +} + +func logFields(v reflect.Value, indent string) { + str := valueToString(v) + fmt.Printf("%s%s\n", indent, str) +} + +func valueToString(v reflect.Value) string { + // Resolve pointers to their underlying value + for v.Kind() == reflect.Ptr { + if v.IsNil() { + return "nil" + } + v = v.Elem() + } + + switch v.Kind() { + case reflect.Struct: + t := v.Type() + var fields []string + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + fieldType := t.Field(i) + + // Skip unexported fields + if !field.CanInterface() { + continue + } + + fieldName := fieldType.Name + fieldValue := valueToString(field) + fields = append(fields, fmt.Sprintf("%s:%s", fieldName, fieldValue)) + } + return fmt.Sprintf("{%s}", strings.Join(fields, " ")) + case reflect.Map: + if v.Len() == 0 { + return "{}" + } + var pairs []string + for _, key := range v.MapKeys() { + value := v.MapIndex(key) + keyStr := valueToString(key) + valueStr := valueToString(value) + pairs = append(pairs, fmt.Sprintf("%s:%s", keyStr, valueStr)) + } + return fmt.Sprintf("map[%s]", strings.Join(pairs, " ")) + case reflect.Slice, reflect.Array: + if v.Len() == 0 { + return "[]" + } + var elems []string + for i := 0; i < v.Len(); i++ { + elem := v.Index(i) + elemStr := valueToString(elem) + elems = append(elems, elemStr) + } + return fmt.Sprintf("[%s]", strings.Join(elems, " ")) + case reflect.Invalid: + return "nil" + default: + // Check if the value can be interfaced + if v.CanInterface() { + return fmt.Sprintf("%v", v.Interface()) + } else { + return "" + } + } +} diff --git a/pkg/solana/codec/solana.go b/pkg/solana/codec/solana.go index b0839c83a..9a4c3adc3 100644 --- a/pkg/solana/codec/solana.go +++ b/pkg/solana/codec/solana.go @@ -66,7 +66,7 @@ func (s solanaCodec) CreateType(itemType string, forEncoding bool) (any, error) // we don't need double pointers, and they can also mess up reflection variable creation and mapstruct decode if def.GetType().Kind() == reflect.Pointer { - return reflect.New(def.GetCodecType().GetType()).Elem().Interface(), nil + return reflect.New(def.GetCodecType().GetType().Elem()).Interface(), nil } return reflect.New(def.GetType()).Interface(), nil @@ -224,7 +224,6 @@ func asStruct( return name, nil, err } - fmt.Println("no caser ", fieldName) named[idx+desLen] = commonencodings.NamedTypeCodec{Name: fieldName, Codec: typedCodec} } diff --git a/pkg/solana/codec/testutils/itemArray1TypeIDL.json b/pkg/solana/codec/testutils/itemArray1TypeIDL.json index 48e7d04ed..589c7e0a6 100644 --- a/pkg/solana/codec/testutils/itemArray1TypeIDL.json +++ b/pkg/solana/codec/testutils/itemArray1TypeIDL.json @@ -47,6 +47,7 @@ { "name": "OracleIds", "type": { "array": ["u8", 32] } }, { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "DifferentField", "type": "string" }, { "name": "BigField", "type": "i128" }, { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } } diff --git a/pkg/solana/codec/testutils/itemIDL.json b/pkg/solana/codec/testutils/itemIDL.json index 024dcab7a..02483eab1 100644 --- a/pkg/solana/codec/testutils/itemIDL.json +++ b/pkg/solana/codec/testutils/itemIDL.json @@ -3,12 +3,17 @@ "name": "test_item_type", "instructions": [ { - "name": "ProcessTestItemType", + "name": "processTestItemType", "accounts": [ { "name": "TestStruct", "isMut": true, "isSigner": false + }, + { + "name": "User", + "isMut": false, + "isSigner": true } ], "args": [] @@ -21,11 +26,11 @@ "kind": "struct", "fields": [ { "name": "Field", "type": "i32" }, - { "name": "DifferentField", "type": "string" }, { "name": "OracleId", "type": "u8" }, { "name": "OracleIds", "type": { "array": ["u8", 32] } }, { "name": "AccountStruct", "type": { "defined": "AccountStruct" } }, { "name": "Accounts", "type": { "vec": "publicKey" } }, + { "name": "DifferentField", "type": "string" }, { "name": "BigField", "type": "i128" }, { "name": "NestedDynamicStruct", "type": { "defined": "NestedDynamic" } }, { "name": "NestedStaticStruct", "type": { "defined": "NestedStatic" } }