From 103a8c550236403511c2cf81ffa85034dfb70c5d Mon Sep 17 00:00:00 2001 From: Ryan Tinianov Date: Wed, 15 Nov 2023 11:55:26 -0500 Subject: [PATCH] Add codec and make chain reader use it. Run the interface tests for both chain reader and codec. --- core/scripts/go.mod | 8 +- core/scripts/go.sum | 16 +- core/services/relay/evm/chain_reader.go | 190 ++++---- core/services/relay/evm/chain_reader_test.go | 456 +++++++++--------- core/services/relay/evm/codec.go | 72 +++ core/services/relay/evm/codec_entry.go | 7 +- core/services/relay/evm/codec_test.go | 189 ++++++++ core/services/relay/evm/decoder.go | 85 ++++ core/services/relay/evm/encoder.go | 141 ++++++ core/services/relay/evm/evm.go | 20 +- core/services/relay/evm/functions.go | 4 + core/services/relay/evm/mercury_provider.go | 7 + core/services/relay/evm/ocr2keeper.go | 4 + core/services/relay/evm/ocr2vrf.go | 8 + .../testfiles/chain_reader_test_contract.sol | 60 +++ .../chain_reader_test_contract_gen.abi | 1 + .../chain_reader_test_contract_gen.bin | 1 + .../chain_reader_test_contract_gen.go | 341 +++++++++++++ .../testfiles/chainlink_reader_test_setup.sh | 8 + core/services/relay/evm/types/types.go | 24 +- go.mod | 8 +- go.sum | 16 +- integration-tests/go.mod | 8 +- integration-tests/go.sum | 16 +- plugins/medianpoc/plugin_test.go | 5 + 25 files changed, 1328 insertions(+), 367 deletions(-) create mode 100644 core/services/relay/evm/codec.go create mode 100644 core/services/relay/evm/codec_test.go create mode 100644 core/services/relay/evm/decoder.go create mode 100644 core/services/relay/evm/encoder.go create mode 100644 core/services/relay/evm/testfiles/chain_reader_test_contract.sol create mode 100644 core/services/relay/evm/testfiles/chain_reader_test_contract_gen.abi create mode 100644 core/services/relay/evm/testfiles/chain_reader_test_contract_gen.bin create mode 100644 core/services/relay/evm/testfiles/chain_reader_test_contract_gen.go create mode 100755 core/services/relay/evm/testfiles/chainlink_reader_test_setup.sh diff --git a/core/scripts/go.mod b/core/scripts/go.mod index 25c792d4e3d..c8244a77e53 100644 --- a/core/scripts/go.mod +++ b/core/scripts/go.mod @@ -304,10 +304,10 @@ require ( github.com/shirou/gopsutil/v3 v3.23.9 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0 // indirect + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/tdh2/go/tdh2 v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect diff --git a/core/scripts/go.sum b/core/scripts/go.sum index cf278800f6d..6ba4a768b4c 100644 --- a/core/scripts/go.sum +++ b/core/scripts/go.sum @@ -1467,14 +1467,14 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7 h1:QLhOOIeFldRBc+ESAVZfEPrIpfeiduwnmHlvDt1iFmM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7/go.mod h1:ipa32mlAXbF3uUQqfVDNxQbBiyu2KKA3AwhK3ewhSSU= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606 h1:4RUoJlw/BEonMCkh7AuoTGBWbV+7CY8n0kpesz5tlqA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606/go.mod h1:xwuKim71kn4z+8r1MO6mjlxl3bGlGPpHnqWJW6fQsuU= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90 h1:tcnV7rhW0wC/oukNE/wPRQzC1K86YERbVaWF5KYda7g= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90/go.mod h1:VDRlSwdE7x/7dPIzg4mr+m80dvNm/vmub/7t9T9Mf/k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0 h1:rWrd5VnZWpFXH/UzcGI0PxhT6u4BAuoZDBr2QBy5YNc= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0/go.mod h1:L2d4754c/HjL8GzzdtzaRsWUxKh1OWMx2Kd6faRf/PA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7 h1:Q7Lif7+Hr3A1gsFooCp/StP93qVPuixn32XEllZnSdY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7/go.mod h1:ipa32mlAXbF3uUQqfVDNxQbBiyu2KKA3AwhK3ewhSSU= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 h1:iu1pNbUoSDTrp+7BUtfTygZ2C0f5C2ZOBQhIoJjp+S0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679/go.mod h1:2Jx7bTEk4ujFQdsZpZq3A0BydvaVPs6mX8clUfxHOEM= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a h1:JoyTazNcqXvZoMQjNfB0eapnlaoMS6pI0NeRvouRvog= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a/go.mod h1:rioELYwPY2xBtzPRN/D08Y7iTPbIQEjPknYdJK51CzQ= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006 h1:1GzOKT53e8N7ZPwsyf1hSbKsynZmXmLOIL3DMvGq9sc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006/go.mod h1:tJLhL7gJ+V3+4N/yLxXIvJ0DAiuyqZCa31+2BSbw7zo= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/core/services/relay/evm/chain_reader.go b/core/services/relay/evm/chain_reader.go index ce93da70ec9..77a959f813c 100644 --- a/core/services/relay/evm/chain_reader.go +++ b/core/services/relay/evm/chain_reader.go @@ -2,15 +2,19 @@ package evm import ( "context" - "errors" "fmt" "strings" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm" + evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller" "github.com/smartcontractkit/chainlink/v2/core/logger" "github.com/smartcontractkit/chainlink/v2/core/services" @@ -23,19 +27,30 @@ type ChainReaderService interface { } type chainReader struct { - lggr logger.Logger - contractID common.Address - lp logpoller.LogPoller + lggr logger.Logger + lp logpoller.LogPoller + codec *evmCodec + client evmclient.Client } -// NewChainReaderService constructor for ChainReader -func NewChainReaderService(lggr logger.Logger, lp logpoller.LogPoller, contractID common.Address, config types.ChainReaderConfig) (*chainReader, error) { - if err := validateChainReaderConfig(config); err != nil { - return nil, fmt.Errorf("%w err: %w", commontypes.ErrInvalidConfig, err) +// NewChainReaderService is a constructor for ChainReader, returns nil if there is any error +func NewChainReaderService(lggr logger.Logger, lp logpoller.LogPoller, chain evm.Chain, config types.ChainReaderConfig) (ChainReaderService, error) { + + parsed := &parsedTypes{ + encoderDefs: map[string]*CodecEntry{}, + decoderDefs: map[string]*CodecEntry{}, + } + + if err := addTypes(config.ChainContractReaders, parsed); err != nil { + return nil, err } - // TODO BCF-2814 implement initialisation of chain reading definitions and pass them into chainReader - return &chainReader{lggr.Named("ChainReader"), contractID, lp}, nil + return &chainReader{ + lggr: lggr.Named("ChainReader"), + lp: lp, + codec: codecFromTypes(parsed), + client: chain.Client(), + }, nil } func (cr *chainReader) Name() string { return cr.lggr.Name() } @@ -45,117 +60,124 @@ func (cr *chainReader) initialize() error { return nil } +func (cr *chainReader) CreateType(itemType string, forEncoding bool) (any, error) { + return cr.codec.CreateType(itemType, forEncoding) +} + +var _ commontypes.TypeProvider = &chainReader{} + +func (cr *chainReader) GetLatestValue(ctx context.Context, bc commontypes.BoundContract, method string, params any, returnVal any) error { + data, err := cr.codec.Encode(ctx, params, method) + if err != nil { + return err + } + + address := common.HexToAddress(bc.Address) + callMsg := ethereum.CallMsg{ + To: &address, + From: address, + Data: data, + } + + output, err := cr.client.CallContract(ctx, callMsg, nil) + + if err != nil { + return err + } + + return cr.codec.Decode(ctx, output, returnVal, method) +} + func (cr *chainReader) Start(ctx context.Context) error { if err := cr.initialize(); err != nil { return fmt.Errorf("Failed to initialize ChainReader: %w", err) } return nil } - func (cr *chainReader) Close() error { return nil } func (cr *chainReader) Ready() error { return nil } - func (cr *chainReader) HealthReport() map[string]error { return map[string]error{cr.Name(): nil} } -func (cr *chainReader) GetLatestValue(ctx context.Context, bc commontypes.BoundContract, method string, params any, returnVal any) error { - return fmt.Errorf("Unimplemented method GetLatestValue called %w", errors.ErrUnsupported) -} - -func validateChainReaderConfig(cfg types.ChainReaderConfig) error { - if len(cfg.ChainContractReaders) == 0 { - return fmt.Errorf("config is empty") +func addEventTypes(name string, contractABI abi.ABI, chainReaderDefinition types.ChainReaderDefinition, parsed *parsedTypes) error { + event, methodExists := contractABI.Events[chainReaderDefinition.ChainSpecificName] + if !methodExists { + return fmt.Errorf("method: %s doesn't exist", chainReaderDefinition.ChainSpecificName) } - for contractName, chainContractReader := range cfg.ChainContractReaders { - abi, err := abi.JSON(strings.NewReader(chainContractReader.ContractABI)) - if err != nil { - return fmt.Errorf("invalid abi: %w", err) - } - - for chainReadingDefinitionName, chainReaderDefinition := range chainContractReader.ChainReaderDefinitions { - switch chainReaderDefinition.ReadType { - case types.Method: - err = validateMethods(abi, chainReaderDefinition) - case types.Event: - err = validateEvents(abi, chainReaderDefinition) - default: - return fmt.Errorf("invalid chain reading definition read type: %d for contract: %q", chainReaderDefinition.ReadType, contractName) - } - if err != nil { - return fmt.Errorf("invalid chain reading definition: %q for contract: %q, err: %w", chainReadingDefinitionName, contractName, err) - } - } + if err := addOverrides(chainReaderDefinition, event.Inputs); err != nil { + return err } - return nil + return addDecoderDef(name, event.Inputs, parsed) } -func validateEvents(contractABI abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { - event, methodExists := contractABI.Events[chainReaderDefinition.ChainSpecificName] +func addMethods(name string, abi abi.ABI, chainReaderDefinition types.ChainReaderDefinition, parsed *parsedTypes) error { + method, methodExists := abi.Methods[chainReaderDefinition.ChainSpecificName] if !methodExists { - return fmt.Errorf("event: %s doesn't exist", chainReaderDefinition.ChainSpecificName) + return fmt.Errorf("method: %q doesn't exist", chainReaderDefinition.ChainSpecificName) } - var abiEventIndexedInputs []abi.Argument - for _, eventInput := range event.Inputs { - if eventInput.Indexed { - abiEventIndexedInputs = append(abiEventIndexedInputs, eventInput) - } + if err := addOverrides(chainReaderDefinition, method.Inputs); err != nil { + return err } - var chainReaderEventParams []string - for chainReaderEventParam := range chainReaderDefinition.Params { - chainReaderEventParams = append(chainReaderEventParams, chainReaderEventParam) + // ABI.Pack prepends the method.ID to the encodings, we'll need the encoder to do the same. + input := &CodecEntry{Args: method.Inputs, encodingPrefix: method.ID} + if err := input.Init(); err != nil { + return err } - if !areChainReaderArgumentsValid(abiEventIndexedInputs, chainReaderEventParams) { - var abiEventIndexedInputsNames []string - for _, abiEventIndexedInput := range abiEventIndexedInputs { - abiEventIndexedInputsNames = append(abiEventIndexedInputsNames, abiEventIndexedInput.Name) - } - return fmt.Errorf("params: [%s] don't match abi event indexed inputs: [%s]", strings.Join(chainReaderEventParams, ","), strings.Join(abiEventIndexedInputsNames, ",")) - } - return nil + parsed.encoderDefs[name] = input + return addDecoderDef(name, method.Outputs, parsed) } -func validateMethods(abi abi.ABI, chainReaderDefinition types.ChainReaderDefinition) error { - method, methodExists := abi.Methods[chainReaderDefinition.ChainSpecificName] - if !methodExists { - return fmt.Errorf("method: %q doesn't exist", chainReaderDefinition.ChainSpecificName) - } - - var methodNames []string - for methodName := range chainReaderDefinition.Params { - methodNames = append(methodNames, methodName) - } +func addDecoderDef(name string, outputs abi.Arguments, parsed *parsedTypes) error { + output := &CodecEntry{Args: outputs} + parsed.decoderDefs[name] = output + return output.Init() +} - if !areChainReaderArgumentsValid(method.Inputs, methodNames) { - var abiMethodInputs []string - for _, input := range method.Inputs { - abiMethodInputs = append(abiMethodInputs, input.Name) +func addOverrides(chainReaderDefinition types.ChainReaderDefinition, inputs abi.Arguments) error { + // TODO add transforms to add params artificially +paramsLoop: + for argName, param := range chainReaderDefinition.Params { + // TODO add type check too + _ = param + for _, input := range inputs { + if argName == input.Name { + continue paramsLoop + } } - return fmt.Errorf("params: [%s] don't match abi method inputs: [%s]", strings.Join(methodNames, ","), strings.Join(abiMethodInputs, ",")) + return fmt.Errorf("cannot find parameter %v in %v", argName, chainReaderDefinition.ChainSpecificName) } return nil } -func areChainReaderArgumentsValid(contractArgs []abi.Argument, chainReaderArgs []string) bool { - for _, contractArg := range contractArgs { - found := false - for _, chArgName := range chainReaderArgs { - if chArgName == contractArg.Name { - found = true - break - } +func addTypes(chainContractReaders map[string]types.ChainContractReader, parsed *parsedTypes) error { + for contractName, chainContractReader := range chainContractReaders { + contractAbi, err := abi.JSON(strings.NewReader(chainContractReader.ContractABI)) + if err != nil { + return err } - if !found { - return false + + for chainReadingDefinitionName, chainReaderDefinition := range chainContractReader.ChainReaderDefinitions { + switch chainReaderDefinition.ReadType { + case types.Method: + err = addMethods(chainReadingDefinitionName, contractAbi, chainReaderDefinition, parsed) + case types.Event: + err = addEventTypes(chainReadingDefinitionName, contractAbi, chainReaderDefinition, parsed) + default: + return fmt.Errorf("invalid chain reader definition read type: %d", chainReaderDefinition.ReadType) + } + if err != nil { + return errors.Wrap(err, fmt.Sprintf("invalid chain reader config for contract: %q chain reading definition: %q", contractName, chainReadingDefinitionName)) + } } } - return true + return nil } diff --git a/core/services/relay/evm/chain_reader_test.go b/core/services/relay/evm/chain_reader_test.go index d2eb5d30cd7..5e770841eb5 100644 --- a/core/services/relay/evm/chain_reader_test.go +++ b/core/services/relay/evm/chain_reader_test.go @@ -1,36 +1,81 @@ -package evm +package evm_test + +//go:generate ./testfiles/chainlink_reader_test_setup.sh import ( - "encoding/json" - "fmt" - "strings" + "context" + "crypto/ecdsa" + "math" + "math/big" "testing" - "github.com/stretchr/testify/assert" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/accounts/abi/bind/backends" + "github.com/ethereum/go-ethereum/core" + evmtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/smartcontractkit/libocr/commontypes" + "github.com/stretchr/testify/require" - commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + . "github.com/smartcontractkit/chainlink-common/pkg/types/interfacetests" //nolint common practice to import test mods with . mocklogpoller "github.com/smartcontractkit/chainlink/v2/core/chains/evm/logpoller/mocks" + + "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client" "github.com/smartcontractkit/chainlink/v2/core/chains/evm/mocks" - "github.com/smartcontractkit/chainlink/v2/core/internal/testutils" "github.com/smartcontractkit/chainlink/v2/core/logger" - evmtypes "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/testfiles" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" ) -type chainReaderTestHelper struct { +const commonGasLimitOnEvms = uint64(4712388) + +func TestChainReader(t *testing.T) { + RunChainReaderInterfaceTests(t, &chainReaderInterfaceTester{}) +} + +type chainReaderInterfaceTester struct { + chain *mocks.Chain + address string + chainConfig types.ChainReaderConfig + auth *bind.TransactOpts + sim *backends.SimulatedBackend + pk *ecdsa.PrivateKey + evmTest *testfiles.Testfiles +} + +func (it *chainReaderInterfaceTester) Setup(t *testing.T) { + if it.chain == nil { + it.setupNoClient(t) + it.chain.On("Client").Return(client.NewSimulatedBackendClient(t, it.sim, big.NewInt(1337))) + } } -func (crTestHelper chainReaderTestHelper) makeChainReaderConfig(abi string, params map[string]any) evmtypes.ChainReaderConfig { - return evmtypes.ChainReaderConfig{ - ChainContractReaders: map[string]evmtypes.ChainContractReader{ - "MyContract": { - ContractABI: abi, - ChainReaderDefinitions: map[string]evmtypes.ChainReaderDefinition{ - "MyGenericMethod": { - ChainSpecificName: "name", - Params: params, - CacheEnabled: false, - ReadType: evmtypes.Method, +func (it *chainReaderInterfaceTester) setupNoClient(t require.TestingT) { + // can re-use the same chain for tests, just make new contract for each test + if it.chain != nil { + return + } + + it.chain = &mocks.Chain{} + it.setupChainNoClient(t) + it.chain.On("LogPoller").Return(logger.NullLogger) + + it.chainConfig = types.ChainReaderConfig{ + ChainContractReaders: map[string]types.ChainContractReader{ + "LatestValueHolder": { + ContractABI: testfiles.TestfilesMetaData.ABI, + ChainReaderDefinitions: map[string]types.ChainReaderDefinition{ + MethodTakingLatestParamsReturningTestStruct: { + ChainSpecificName: "GetElementAtIndex", + }, + MethodReturningUint64: { + ChainSpecificName: "GetPrimitiveValue", + }, + MethodReturningUint64Slice: { + ChainSpecificName: "GetSliceValue", }, }, }, @@ -38,235 +83,182 @@ func (crTestHelper chainReaderTestHelper) makeChainReaderConfig(abi string, para } } -func (crTestHelper chainReaderTestHelper) makeChainReaderConfigFromStrings(abi string, chainReadingDefinitions string) (evmtypes.ChainReaderConfig, error) { - chainReaderConfigTemplate := `{ - "chainContractReaders": { - "testContract": { - "contractName": "testContract", - "contractABI": "[%s]", - "chainReaderDefinitions": { - %s - } - } - } - }` - - abi = strings.Replace(abi, `"`, `\"`, -1) - formattedCfgJsonString := fmt.Sprintf(chainReaderConfigTemplate, abi, chainReadingDefinitions) - var chainReaderConfig evmtypes.ChainReaderConfig - err := json.Unmarshal([]byte(formattedCfgJsonString), &chainReaderConfig) - return chainReaderConfig, err +func (it *chainReaderInterfaceTester) Teardown(_ *testing.T) { + it.address = "" } -func TestNewChainReader(t *testing.T) { - lggr := logger.TestLogger(t) - lp := mocklogpoller.NewLogPoller(t) - chain := mocks.NewChain(t) - contractID := testutils.NewAddress() - contractABI := `[{"inputs":[{"internalType":"string","name":"param","type":"string"}],"name":"name","stateMutability":"view","type":"function"}]` - - t.Run("happy path", func(t *testing.T) { - params := make(map[string]any) - params["param"] = "" - chainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, params) - chain.On("LogPoller").Return(lp) - _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, chainReaderConfig) - assert.NoError(t, err) - }) - - t.Run("invalid config", func(t *testing.T) { - invalidChainReaderConfig := chainReaderTestHelper{}.makeChainReaderConfig(contractABI, map[string]any{}) // missing param - _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, invalidChainReaderConfig) - assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) - }) - - t.Run("ChainReader config is empty", func(t *testing.T) { - emptyChainReaderConfig := evmtypes.ChainReaderConfig{} - _, err := NewChainReaderService(lggr, chain.LogPoller(), contractID, emptyChainReaderConfig) - assert.ErrorIs(t, err, commontypes.ErrInvalidConfig) - assert.ErrorContains(t, err, "config is empty") - }) +func (it *chainReaderInterfaceTester) Name() string { + return "EVM" } -func TestChainReaderStartClose(t *testing.T) { - lggr := logger.TestLogger(t) - lp := mocklogpoller.NewLogPoller(t) - cr := chainReader{ - lggr: lggr, - lp: lp, +func (it *chainReaderInterfaceTester) GetAccountBytes(i int) []byte { + account := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2} + account[i%32] += byte(i) + account[(i+3)%32] += byte(i + 3) + return account +} + +func (it *chainReaderInterfaceTester) GetChainReader(t *testing.T) clcommontypes.ChainReader { + cr, err := evm.NewChainReaderService(logger.NullLogger, mocklogpoller.NewLogPoller(t), it.chain, it.chainConfig) + require.NoError(t, err) + return cr +} + +func (it *chainReaderInterfaceTester) GetPrimitiveContract(ctx context.Context, t *testing.T) clcommontypes.BoundContract { + // Since most tests don't use the contract, it's set up lazily to save time + it.deployNewContract(ctx, t) + return clcommontypes.BoundContract{ + Address: it.address, + Name: MethodReturningUint64, } - err := cr.Start(testutils.Context(t)) - assert.NoError(t, err) - err = cr.Close() - assert.NoError(t, err) } -// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 -func TestValidateChainReaderConfig_HappyPath(t *testing.T) { - type testCase struct { - name string - abiInput string - chainReadingDefinitions string +func (it *chainReaderInterfaceTester) GetSliceContract(ctx context.Context, t *testing.T) clcommontypes.BoundContract { + // Since most tests don't use the contract, it's set up lazily to save time + it.deployNewContract(ctx, t) + return clcommontypes.BoundContract{ + Address: it.address, + Name: MethodReturningUint64Slice, } +} - var testCases []testCase - testCases = append(testCases, - testCase{ - name: "eventWithMultipleIndexedTopics", - abiInput: `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"}`, - chainReadingDefinitions: `"Swap":{ - "chainSpecificName": "Swap", - "params":{ - "sender": "0x0", - "to": "0x0" - }, - "readType": 1 - }`, - }) - - testCases = append(testCases, - testCase{ - name: "methodWithOneParamAndMultipleResponses", - abiInput: `{"constant":true,"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getUserAccountData","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"getUserAccountData":{ - "chainSpecificName": "getUserAccountData", - "params":{ - "_user": "0x0" - }, - "readType": 0 - }`, - }) - - testCases = append(testCases, - testCase{ - name: "methodWithMultipleParamsAndOneResult", - abiInput: `{"inputs":[{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"},{"internalType":"uint256","name":"_inputQuantity","type":"uint256"}],"name":"getSwapOutput","stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"getSwapOutput":{ - "chainSpecificName": "getSwapOutput", - "params":{ - "_input":"0x0", - "_output":"0x0", - "_inputQuantity":"0x0" - }, - "readType": 0 - }`, - }) - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) - assert.NoError(t, err) - assert.NoError(t, validateChainReaderConfig(cfg)) - }) +func (it *chainReaderInterfaceTester) SetLatestValue(ctx context.Context, t *testing.T, testStruct *TestStruct) clcommontypes.BoundContract { + // Since most tests don't use the contract, it's set up lazily to save time + it.deployNewContract(ctx, t) + + tx, err := it.evmTest.AddTestStruct( + it.auth, + testStruct.Field, + testStruct.DifferentField, + uint8(testStruct.OracleID), + convertOracleIDs(testStruct.OracleIDs), + [32]byte(testStruct.Account), + convertAccounts(testStruct.Accounts), + testStruct.BigField, + midToInternalType(testStruct.NestedStruct), + ) + require.NoError(t, err) + it.sim.Commit() + it.incNonce() + it.awaitTx(ctx, t, tx) + return clcommontypes.BoundContract{ + Address: it.address, + Name: MethodTakingLatestParamsReturningTestStruct, } +} - t.Run("large config with all test cases", func(t *testing.T) { - var largeABI string - var manyChainReadingDefinitions string - for _, tc := range testCases { - largeABI += tc.abiInput + "," - manyChainReadingDefinitions += tc.chainReadingDefinitions + "," - } - - largeABI = largeABI[:len(largeABI)-1] - manyChainReadingDefinitions = manyChainReadingDefinitions[:len(manyChainReadingDefinitions)-1] - cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(largeABI, manyChainReadingDefinitions) - assert.NoError(t, err) - assert.NoError(t, validateChainReaderConfig(cfg)) - }) +func convertOracleIDs(oracleIDs [32]commontypes.OracleID) [32]byte { + convertedIds := [32]byte{} + for i, id := range oracleIDs { + convertedIds[i] = byte(id) + } + return convertedIds } -// TODO Chain Reading Definitions return values are WIP, waiting on codec work and BCF-2789 -func TestValidateChainReaderConfig_BadPath(t *testing.T) { - type testCase struct { - name string - abiInput string - chainReadingDefinitions string - expected error +func convertAccounts(accounts [][]byte) [][32]byte { + convertedAccounts := make([][32]byte, len(accounts)) + for i, a := range accounts { + convertedAccounts[i] = [32]byte(a) } + return convertedAccounts +} - var testCases []testCase - mismatchedEventArgumentsTestABI := `{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"Swap","type":"event"}` - testCases = append(testCases, - testCase{ - name: "mismatched abi and event chain reading param values", - abiInput: mismatchedEventArgumentsTestABI, - chainReadingDefinitions: `"Swap":{ - "chainSpecificName": "Swap", - "params":{ - "malformedParam": "0x0" - }, - "readType": 1 - }`, - expected: fmt.Errorf("invalid chain reading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi event indexed inputs: [sender]"), - }) - - mismatchedFunctionArgumentsTestABI := `{"constant":true,"inputs":[{"internalType":"address","name":"from","type":"address"}],"name":"Swap","payable":false,"stateMutability":"view","type":"function"}` - testCases = append(testCases, - testCase{ - name: "mismatched abi and method chain reading param values", - abiInput: mismatchedFunctionArgumentsTestABI, - chainReadingDefinitions: `"Swap":{ - "chainSpecificName": "Swap", - "params":{ - "malformedParam": "0x0" - }, - "readType": 0 - }`, - expected: fmt.Errorf("invalid chain reading definition: \"Swap\" for contract: \"testContract\", err: params: [malformedParam] don't match abi method inputs: [from]"), - }, - ) +func (it *chainReaderInterfaceTester) setupChainNoClient(t require.TestingT) { + privateKey, err := crypto.GenerateKey() + require.NoError(t, err) + it.pk = privateKey - testCases = append(testCases, - testCase{ - name: "event doesn't exist", - abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"TestMethod":{ - "chainSpecificName": "Swap", - "readType": 1 - }`, - expected: fmt.Errorf("invalid chain reading definition: \"TestMethod\" for contract: \"testContract\", err: event: Swap doesn't exist"), - }, - ) + it.auth, err = bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(1337)) + require.NoError(t, err) - testCases = append(testCases, - testCase{ - name: "method doesn't exist", - abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"TestMethod":{ - "chainSpecificName": "Swap", - "readType": 0 - }`, - expected: fmt.Errorf("invalid chain reading definition: \"TestMethod\" for contract: \"testContract\", err: method: \"Swap\" doesn't exist"), - }, - ) + it.sim = backends.NewSimulatedBackend(core.GenesisAlloc{it.auth.From: {Balance: big.NewInt(math.MaxInt64)}}, commonGasLimitOnEvms*5000) + it.sim.Commit() +} + +func (it *chainReaderInterfaceTester) deployNewContract(ctx context.Context, t *testing.T) { + if it.address != "" { + return + } + gasPrice, err := it.sim.SuggestGasPrice(ctx) + require.NoError(t, err) + it.auth.GasPrice = gasPrice + + // 105528 was in the error: gas too low: have 0, want 105528 + // Not sure if there's a better way to get it. + it.auth.GasLimit = 1055280000 + + address, tx, ts, err := testfiles.DeployTestfiles(it.auth, it.sim) + + require.NoError(t, err) + it.sim.Commit() + it.evmTest = ts + it.incNonce() + it.awaitTx(ctx, t, tx) + it.address = address.String() +} + +func (it *chainReaderInterfaceTester) awaitTx(ctx context.Context, t *testing.T, tx *evmtypes.Transaction) { + receipt, err := it.sim.TransactionReceipt(ctx, tx.Hash()) + require.NoError(t, err) + require.Equal(t, evmtypes.ReceiptStatusSuccessful, receipt.Status) +} + +func (it *chainReaderInterfaceTester) incNonce() { + if it.auth.Nonce == nil { + it.auth.Nonce = big.NewInt(1) + } else { + it.auth.Nonce = it.auth.Nonce.Add(it.auth.Nonce, big.NewInt(1)) + } +} - testCases = append(testCases, testCase{ - name: "invalid abi", - abiInput: `broken abi`, - chainReadingDefinitions: `"TestMethod":{ - "chainSpecificName": "Swap", - "readType": 0 - }`, - expected: fmt.Errorf("invalid abi"), - }) - - testCases = append(testCases, testCase{ - name: "invalid read type", - abiInput: `{"constant":true,"inputs":[],"name":"someName","payable":false,"stateMutability":"view","type":"function"}`, - chainReadingDefinitions: `"TestMethod":{"readType": 59}`, - expected: fmt.Errorf("invalid chain reading definition read type: 59"), - }) - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - cfg, err := chainReaderTestHelper{}.makeChainReaderConfigFromStrings(tc.abiInput, tc.chainReadingDefinitions) - assert.NoError(t, err) - if tc.expected == nil { - assert.NoError(t, validateChainReaderConfig(cfg)) - } else { - assert.ErrorContains(t, validateChainReaderConfig(cfg), tc.expected.Error()) - } - }) +func getAccounts(first TestStruct) [][32]byte { + accountBytes := make([][32]byte, len(first.Accounts)) + for i, account := range first.Accounts { + accountBytes[i] = [32]byte(account) + } + return accountBytes +} + +func argsFromTestStruct(ts TestStruct) []any { + return []any{ + ts.Field, + ts.DifferentField, + uint8(ts.OracleID), + getOracleIDs(ts), + [32]byte(ts.Account), + getAccounts(ts), + ts.BigField, + midToInternalType(ts.NestedStruct), + } +} + +func getOracleIDs(first TestStruct) [32]byte { + oracleIDs := [32]byte{} + for i, oracleID := range first.OracleIDs { + oracleIDs[i] = byte(oracleID) + } + return oracleIDs +} + +func toInternalType(testStruct TestStruct) testfiles.TestStruct { + return testfiles.TestStruct{ + Field: testStruct.Field, + DifferentField: testStruct.DifferentField, + OracleId: byte(testStruct.OracleID), + OracleIds: convertOracleIDs(testStruct.OracleIDs), + Account: [32]byte(testStruct.Account), + Accounts: convertAccounts(testStruct.Accounts), + BigField: testStruct.BigField, + NestedStruct: midToInternalType(testStruct.NestedStruct), + } +} + +func midToInternalType(m MidLevelTestStruct) testfiles.MidLevelTestStruct { + return testfiles.MidLevelTestStruct{ + FixedBytes: m.FixedBytes, + Inner: testfiles.InnerTestStruct{ + I: int64(m.Inner.I), + S: m.Inner.S, + }, } } diff --git a/core/services/relay/evm/codec.go b/core/services/relay/evm/codec.go new file mode 100644 index 00000000000..f2cb447e120 --- /dev/null +++ b/core/services/relay/evm/codec.go @@ -0,0 +1,72 @@ +package evm + +import ( + "encoding/json" + + "github.com/ethereum/go-ethereum/accounts/abi" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +func NewCodec(conf types.CodecConfig) (commontypes.Codec, error) { + parsed := &parsedTypes{ + encoderDefs: map[string]*CodecEntry{}, + decoderDefs: map[string]*CodecEntry{}, + } + + for k, v := range conf.ChainCodecConfigs { + args := abi.Arguments{} + if err := json.Unmarshal(([]byte)(v.TypeAbi), &args); err != nil { + return nil, err + } + + item := &CodecEntry{Args: args} + if err := item.Init(); err != nil { + return nil, err + } + + parsed.encoderDefs[k] = item + parsed.decoderDefs[k] = item + } + + return codecFromTypes(parsed), nil +} + +func codecFromTypes(parsed *parsedTypes) *evmCodec { + return &evmCodec{ + encoder: &encoder{Definitions: parsed.encoderDefs}, + decoder: &decoder{Definitions: parsed.decoderDefs}, + types: parsed, + } +} + +var _ commontypes.TypeProvider = &evmCodec{} + +type evmCodec struct { + *encoder + *decoder + types *parsedTypes +} + +type parsedTypes struct { + encoderDefs map[string]*CodecEntry + decoderDefs map[string]*CodecEntry +} + +func (c *evmCodec) CreateType(itemType string, forEncoding bool) (any, error) { + var itemTypes map[string]*CodecEntry + if forEncoding { + itemTypes = c.types.encoderDefs + } else { + itemTypes = c.types.decoderDefs + } + + def, ok := itemTypes[itemType] + if !ok { + return nil, commontypes.ErrInvalidType + } + + return def.checkedType, nil +} diff --git a/core/services/relay/evm/codec_entry.go b/core/services/relay/evm/codec_entry.go index e336c4a7d9f..4231d5b01b5 100644 --- a/core/services/relay/evm/codec_entry.go +++ b/core/services/relay/evm/codec_entry.go @@ -12,9 +12,10 @@ import ( ) type CodecEntry struct { - Args abi.Arguments - checkedType reflect.Type - nativeType reflect.Type + Args abi.Arguments + encodingPrefix []byte + checkedType reflect.Type + nativeType reflect.Type } func (entry *CodecEntry) Init() error { diff --git a/core/services/relay/evm/codec_test.go b/core/services/relay/evm/codec_test.go new file mode 100644 index 00000000000..4a5d35a2870 --- /dev/null +++ b/core/services/relay/evm/codec_test.go @@ -0,0 +1,189 @@ +package evm_test + +import ( + "encoding/json" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi" + ocr2types "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + commontypes "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/v2/core/internal/testutils" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/testfiles" + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +func TestCodec(t *testing.T) { + RunCodecInterfaceTests(t, &codecInterfaceTester{}) + anyN := 10 + t.Run("GetMaxEncodingSize delegates to GetMaxSize", func(t *testing.T) { + codec := getCodec(t) + + actual, err := codec.GetMaxEncodingSize(testutils.Context(t), anyN, sizeItemType) + assert.NoError(t, err) + + expected, err := evm.GetMaxSize(anyN, parseDefs(t)[sizeItemType]) + require.NoError(t, err) + assert.Equal(t, expected, actual) + }) + + t.Run("GetMaxDecodingSize delegates to GetMaxSize", func(t *testing.T) { + codec := getCodec(t) + + actual, err := codec.GetMaxDecodingSize(testutils.Context(t), anyN, sizeItemType) + assert.NoError(t, err) + + expected, err := evm.GetMaxSize(anyN, parseDefs(t)[sizeItemType]) + require.NoError(t, err) + assert.Equal(t, expected, actual) + }) +} + +type codecInterfaceTester struct{} + +func (it *codecInterfaceTester) Setup(_ *testing.T) {} + +func (it *codecInterfaceTester) Teardown(_ *testing.T) {} + +func (it *codecInterfaceTester) GetAccountBytes(i int) []byte { + account := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2} + account[i%32] += byte(i) + account[(i+3)%32] += byte(i + 3) + return account +} + +func (it *codecInterfaceTester) EncodeFields(t *testing.T, request *EncodeRequest) []byte { + if request.TestOn == TestItemType { + return encodeFieldsOnItem(t, request) + } + + return encodeFieldsOnSliceOrArray(t, request) +} + +func (it *codecInterfaceTester) GetCodec(t *testing.T) commontypes.Codec { + return getCodec(t) +} + +func (it *codecInterfaceTester) IncludeArrayEncodingSizeEnforcement() bool { + return true +} +func (it *codecInterfaceTester) Name() string { + return "EVM" +} + +func encodeFieldsOnItem(t *testing.T, request *EncodeRequest) ocr2types.Report { + return packArgs(t, argsFromTestStruct(request.TestStructs[0]), parseDefs(t)[TestItemType], request) +} + +func encodeFieldsOnSliceOrArray(t *testing.T, request *EncodeRequest) []byte { + oargs := parseDefs(t)[request.TestOn] + args := make([]any, 1) + + switch request.TestOn { + case TestItemArray1Type: + args[0] = [1]testfiles.TestStruct{toInternalType(request.TestStructs[0])} + case TestItemArray2Type: + args[0] = [2]testfiles.TestStruct{toInternalType(request.TestStructs[0]), toInternalType(request.TestStructs[1])} + default: + tmp := make([]testfiles.TestStruct, len(request.TestStructs)) + for i, ts := range request.TestStructs { + tmp[i] = toInternalType(ts) + } + args[0] = tmp + } + + return packArgs(t, args, oargs, request) +} + +func packArgs(t *testing.T, allArgs []any, oargs abi.Arguments, request *EncodeRequest) []byte { + // extra capacity in case we add an argument + args := make(abi.Arguments, len(oargs), len(oargs)+1) + copy(args, oargs) + // decoding has extra field to decode + if request.ExtraField { + fakeType, err := abi.NewType("int32", "", []abi.ArgumentMarshaling{}) + require.NoError(t, err) + args = append(args, abi.Argument{Name: "FakeField", Type: fakeType}) + allArgs = append(allArgs, 11) + } + + if request.MissingField { + args = args[1:] //nolint we know it's non-zero len + allArgs = allArgs[1:] //nolint we know it's non-zero len + } + + bytes, err := args.Pack(allArgs...) + require.NoError(t, err) + return bytes +} + +var inner = []abi.ArgumentMarshaling{ + {Name: "I", Type: "int64"}, + {Name: "S", Type: "string"}, +} + +var nested = []abi.ArgumentMarshaling{ + {Name: "FixedBytes", Type: "bytes2"}, + {Name: "Inner", Type: "tuple", Components: inner}, +} + +var ts = []abi.ArgumentMarshaling{ + {Name: "Field", Type: "int32"}, + {Name: "DifferentField", Type: "string"}, + {Name: "OracleId", Type: "uint8"}, + {Name: "OracleIds", Type: "uint8[32]"}, + {Name: "Account", Type: "bytes32"}, + {Name: "Accounts", Type: "bytes32[]"}, + {Name: "BigField", Type: "int192"}, + {Name: "NestedStruct", Type: "tuple", Components: nested}, +} + +const sizeItemType = "item for size" + +var codecDefs = map[string][]abi.ArgumentMarshaling{ + TestItemType: ts, + TestItemSliceType: { + {Name: "", Type: "tuple[]", Components: ts}, + }, + TestItemArray1Type: { + {Name: "", Type: "tuple[1]", Components: ts}, + }, + TestItemArray2Type: { + {Name: "", Type: "tuple[2]", Components: ts}, + }, + sizeItemType: { + {Name: "Stuff", Type: "int256[]"}, + {Name: "OtherStuff", Type: "int256"}, + }, +} + +func getCodec(t require.TestingT) commontypes.Codec { + codecConfig := types.CodecConfig{ + ChainCodecConfigs: map[string]types.ChainCodedConfig{}, + } + + for k, v := range codecDefs { + defBytes, err := json.Marshal(v) + require.NoError(t, err) + entry := codecConfig.ChainCodecConfigs[k] + entry.TypeAbi = string(defBytes) + codecConfig.ChainCodecConfigs[k] = entry + } + + codec, err := evm.NewCodec(codecConfig) + require.NoError(t, err) + return codec +} + +func parseDefs(t *testing.T) map[string]abi.Arguments { + bytes, err := json.Marshal(codecDefs) + require.NoError(t, err) + var results map[string]abi.Arguments + require.NoError(t, json.Unmarshal(bytes, &results)) + return results +} diff --git a/core/services/relay/evm/decoder.go b/core/services/relay/evm/decoder.go new file mode 100644 index 00000000000..867f9c5037a --- /dev/null +++ b/core/services/relay/evm/decoder.go @@ -0,0 +1,85 @@ +package evm + +import ( + "context" + "reflect" + + "github.com/mitchellh/mapstructure" + + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" +) + +type decoder struct { + Definitions map[string]*CodecEntry +} + +var _ commontypes.Decoder = &decoder{} + +func (m *decoder) Decode(ctx context.Context, raw []byte, into any, itemType string) error { + info, ok := m.Definitions[itemType] + if !ok { + return commontypes.ErrInvalidType + } + + decode, err := extractDecoding(info, raw) + if err != nil { + return err + } + + rDecode := reflect.ValueOf(decode) + switch rDecode.Kind() { + case reflect.Array: + iInto := reflect.Indirect(reflect.ValueOf(into)) + length := rDecode.Len() + if length != iInto.Len() { + return commontypes.ErrWrongNumberOfElements + } + iInto.Set(reflect.New(iInto.Type()).Elem()) + return setElements(length, rDecode, iInto) + case reflect.Slice: + iInto := reflect.Indirect(reflect.ValueOf(into)) + length := rDecode.Len() + iInto.Set(reflect.MakeSlice(iInto.Type(), length, length)) + return setElements(length, rDecode, iInto) + default: + return mapstructureDecode(decode, into) + } +} + +func (m *decoder) GetMaxDecodingSize(ctx context.Context, n int, itemType string) (int, error) { + return m.Definitions[itemType].GetMaxSize(n) +} + +func extractDecoding(info *CodecEntry, raw []byte) (any, error) { + unpacked := map[string]any{} + if err := info.Args.UnpackIntoMap(unpacked, raw); err != nil { + return nil, commontypes.ErrInvalidEncoding + } + var decode any = unpacked + + if noName, ok := unpacked[""]; ok { + decode = noName + } + return decode, nil +} + +func setElements(length int, rDecode reflect.Value, iInto reflect.Value) error { + for i := 0; i < length; i++ { + if err := mapstructureDecode(rDecode.Index(i).Interface(), iInto.Index(i).Addr().Interface()); err != nil { + return err + } + } + + return nil +} + +func mapstructureDecode(src, dest any) error { + mDecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: evmDecoderHook, + Result: dest, + }) + if err != nil || mDecoder.Decode(src) != nil { + return commontypes.ErrInvalidType + } + return nil +} diff --git a/core/services/relay/evm/encoder.go b/core/services/relay/evm/encoder.go new file mode 100644 index 00000000000..699796a31eb --- /dev/null +++ b/core/services/relay/evm/encoder.go @@ -0,0 +1,141 @@ +package evm + +import ( + "context" + "math/big" + "reflect" + + "github.com/mitchellh/mapstructure" + ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types" + + "github.com/smartcontractkit/chainlink-common/pkg/codec" + commontypes "github.com/smartcontractkit/chainlink-common/pkg/types" + + "github.com/smartcontractkit/chainlink/v2/core/services/relay/evm/types" +) + +type encoder struct { + Definitions map[string]*CodecEntry +} + +var evmDecoderHook = mapstructure.ComposeDecodeHookFunc(codec.BigIntHook, codec.SliceToArrayVerifySizeHook, sizeVerifyBigIntHook) + +var _ commontypes.Encoder = &encoder{} + +func (e *encoder) Encode(ctx context.Context, item any, itemType string) ([]byte, error) { + info, ok := e.Definitions[itemType] + if !ok { + return nil, commontypes.ErrInvalidType + } + + if item == nil { + cpy := make([]byte, len(info.encodingPrefix)) + copy(cpy, info.encodingPrefix) + return cpy, nil + } + + return encode(reflect.ValueOf(item), info) +} + +func (e *encoder) GetMaxEncodingSize(ctx context.Context, n int, itemType string) (int, error) { + return e.Definitions[itemType].GetMaxSize(n) +} + +func encode(item reflect.Value, info *CodecEntry) (ocrtypes.Report, error) { + iType := item.Type() + for iType.Kind() == reflect.Pointer { + iType = iType.Elem() + } + switch iType.Kind() { + case reflect.Pointer: + return encode(item.Elem(), info) + case reflect.Array, reflect.Slice: + return encodeArray(item, info) + case reflect.Struct, reflect.Map: + return encodeItem(item, info) + default: + return nil, commontypes.ErrInvalidEncoding + } +} + +func encodeArray(item reflect.Value, info *CodecEntry) (ocrtypes.Report, error) { + length := item.Len() + var native reflect.Value + switch info.checkedType.Kind() { + case reflect.Array: + if info.checkedType.Len() != length { + return nil, commontypes.ErrWrongNumberOfElements + } + native = reflect.New(info.nativeType).Elem() + case reflect.Slice: + native = reflect.MakeSlice(info.nativeType, length, length) + default: + return nil, commontypes.ErrInvalidType + } + + checkedElm := info.checkedType.Elem() + nativeElm := info.nativeType.Elem() + for i := 0; i < length; i++ { + tmp := reflect.New(checkedElm) + if err := mapstructureDecode(item.Index(i).Interface(), tmp.Interface()); err != nil { + return nil, err + } + native.Index(i).Set(reflect.NewAt(nativeElm, tmp.UnsafePointer()).Elem()) + } + + return pack(info, native.Interface()) +} + +func encodeItem(item reflect.Value, info *CodecEntry) (ocrtypes.Report, error) { + if item.Type() == reflect.PointerTo(info.checkedType) { + item = reflect.NewAt(info.nativeType, item.UnsafePointer()) + } else if item.Type() != reflect.PointerTo(info.nativeType) { + checked := reflect.New(info.checkedType) + if err := mapstructureDecode(item.Interface(), checked.Interface()); err != nil { + return nil, err + } + item = reflect.NewAt(info.nativeType, checked.UnsafePointer()) + } + + item = reflect.Indirect(item) + length := item.NumField() + values := make([]any, length) + iType := item.Type() + for i := 0; i < length; i++ { + if iType.Field(i).IsExported() { + values[i] = item.Field(i).Interface() + } + } + + return pack(info, values...) +} + +func pack(info *CodecEntry, values ...any) (ocrtypes.Report, error) { + if bytes, err := info.Args.Pack(values...); err == nil { + withPrefix := make([]byte, 0, len(info.encodingPrefix)+len(bytes)) + withPrefix = append(withPrefix, info.encodingPrefix...) + return append(withPrefix, bytes...), nil + } + + return nil, commontypes.ErrInvalidType +} + +func sizeVerifyBigIntHook(from, to reflect.Type, data any) (any, error) { + if !to.Implements(types.SizedBigIntType()) { + return data, nil + } + + var err error + data, err = codec.BigIntHook(from, reflect.TypeOf((*big.Int)(nil)), data) + if err != nil { + return nil, err + } + + bi, ok := data.(*big.Int) + if !ok { + return data, nil + } + + converted := reflect.ValueOf(bi).Convert(to).Interface().(types.SizedBigInt) + return converted, converted.Verify() +} diff --git a/core/services/relay/evm/evm.go b/core/services/relay/evm/evm.go index d57cc176642..a269dbdf37b 100644 --- a/core/services/relay/evm/evm.go +++ b/core/services/relay/evm/evm.go @@ -55,6 +55,7 @@ type Relayer struct { eventBroadcaster pg.EventBroadcaster pgCfg pg.QConfig chainReader commontypes.ChainReader + codec commontypes.Codec } type CSAETHKeystore interface { @@ -190,7 +191,7 @@ func (r *Relayer) NewMercuryProvider(rargs commontypes.RelayArgs, pargs commonty } transmitter := mercury.NewTransmitter(lggr, cw.ContractConfigTracker(), client, privKey.PublicKey, rargs.JobID, *relayConfig.FeedID, r.db, r.pgCfg, transmitterCodec) - return NewMercuryProvider(cw, r.chainReader, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil + return NewMercuryProvider(cw, r.chainReader, r.codec, NewMercuryChainReader(r.chain.HeadTracker()), transmitter, reportCodecV1, reportCodecV2, reportCodecV3, lggr), nil } func (r *Relayer) NewFunctionsProvider(rargs commontypes.RelayArgs, pargs commontypes.PluginArgs) (commontypes.FunctionsProvider, error) { @@ -505,7 +506,6 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp if !common.IsHexAddress(relayOpts.ContractID) { return nil, fmt.Errorf("invalid contractID %s, expected hex address", relayOpts.ContractID) } - contractID := common.HexToAddress(relayOpts.ContractID) configWatcher, err := newConfigProvider(lggr, r.chain, relayOpts, r.eventBroadcaster) if err != nil { @@ -533,7 +533,7 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp // allow fallback until chain reader is default and median contract is removed, but still log just in case var chainReaderService commontypes.ChainReader if relayConfig.ChainReader != nil { - if chainReaderService, err = NewChainReaderService(lggr, r.chain.LogPoller(), contractID, *relayConfig.ChainReader); err != nil { + if chainReaderService, err = NewChainReaderService(lggr, r.chain.LogPoller(), r.chain, *relayConfig.ChainReader); err != nil { return nil, err } } else { @@ -541,6 +541,15 @@ func (r *Relayer) NewMedianProvider(rargs commontypes.RelayArgs, pargs commontyp } medianProvider.chainReader = chainReaderService + if relayConfig.Codec != nil { + medianProvider.codec, err = NewCodec(*relayConfig.Codec) + if err != nil { + return nil, err + } + } else { + lggr.Info("Codec missing from RelayConfig; falling back to internal MedianContract") + } + return &medianProvider, nil } @@ -552,6 +561,7 @@ type medianProvider struct { reportCodec median.ReportCodec medianContract *medianContract chainReader commontypes.ChainReader + codec commontypes.Codec ms services.MultiStart } @@ -604,3 +614,7 @@ func (p *medianProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker func (p *medianProvider) ChainReader() commontypes.ChainReader { return p.chainReader } + +func (p *medianProvider) Codec() commontypes.Codec { + return p.codec +} diff --git a/core/services/relay/evm/functions.go b/core/services/relay/evm/functions.go index 7c39c08a0be..8b0124c93a9 100644 --- a/core/services/relay/evm/functions.go +++ b/core/services/relay/evm/functions.go @@ -90,6 +90,10 @@ func (p *functionsProvider) ChainReader() commontypes.ChainReader { return nil } +func (p *functionsProvider) Codec() commontypes.Codec { + return nil +} + func NewFunctionsProvider(chain evm.Chain, rargs commontypes.RelayArgs, pargs commontypes.PluginArgs, lggr logger.Logger, ethKeystore keystore.Eth, pluginType functionsRelay.FunctionsPluginType) (evmRelayTypes.FunctionsProvider, error) { relayOpts := evmRelayTypes.NewRelayOpts(rargs) relayConfig, err := relayOpts.RelayConfig() diff --git a/core/services/relay/evm/mercury_provider.go b/core/services/relay/evm/mercury_provider.go index 9e25597291f..59d57a4920e 100644 --- a/core/services/relay/evm/mercury_provider.go +++ b/core/services/relay/evm/mercury_provider.go @@ -24,6 +24,7 @@ var _ commontypes.MercuryProvider = (*mercuryProvider)(nil) type mercuryProvider struct { configWatcher *configWatcher chainReader commontypes.ChainReader + codec commontypes.Codec mercuryChainReader relaymercury.ChainReader transmitter mercury.Transmitter reportCodecV1 relaymercuryv1.ReportCodec @@ -37,6 +38,7 @@ type mercuryProvider struct { func NewMercuryProvider( configWatcher *configWatcher, chainReader commontypes.ChainReader, + codec commontypes.Codec, mercuryChainReader relaymercury.ChainReader, transmitter mercury.Transmitter, reportCodecV1 relaymercuryv1.ReportCodec, @@ -47,6 +49,7 @@ func NewMercuryProvider( return &mercuryProvider{ configWatcher, chainReader, + codec, mercuryChainReader, transmitter, reportCodecV1, @@ -84,6 +87,10 @@ func (p *mercuryProvider) MercuryChainReader() relaymercury.ChainReader { return p.mercuryChainReader } +func (p *mercuryProvider) Codec() commontypes.Codec { + return p.codec +} + func (p *mercuryProvider) ContractConfigTracker() ocrtypes.ContractConfigTracker { return p.configWatcher.ContractConfigTracker() } diff --git a/core/services/relay/evm/ocr2keeper.go b/core/services/relay/evm/ocr2keeper.go index 9e056e8b983..b15436e0c79 100644 --- a/core/services/relay/evm/ocr2keeper.go +++ b/core/services/relay/evm/ocr2keeper.go @@ -134,6 +134,10 @@ func (c *ocr2keeperProvider) ChainReader() commontypes.ChainReader { return nil } +func (c *ocr2keeperProvider) Codec() commontypes.Codec { + return nil +} + func newOCR2KeeperConfigProvider(lggr logger.Logger, chain evm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/ocr2vrf.go b/core/services/relay/evm/ocr2vrf.go index f4d695b4cd1..9a6608b8498 100644 --- a/core/services/relay/evm/ocr2vrf.go +++ b/core/services/relay/evm/ocr2vrf.go @@ -114,6 +114,10 @@ func (c *dkgProvider) ChainReader() commontypes.ChainReader { return nil } +func (c *dkgProvider) Codec() commontypes.Codec { + return nil +} + type ocr2vrfProvider struct { *configWatcher contractTransmitter ContractTransmitter @@ -127,6 +131,10 @@ func (c *ocr2vrfProvider) ChainReader() commontypes.ChainReader { return nil } +func (c *ocr2vrfProvider) Codec() commontypes.Codec { + return nil +} + func newOCR2VRFConfigProvider(lggr logger.Logger, chain evm.Chain, rargs commontypes.RelayArgs) (*configWatcher, error) { var relayConfig types.RelayConfig err := json.Unmarshal(rargs.RelayConfig, &relayConfig) diff --git a/core/services/relay/evm/testfiles/chain_reader_test_contract.sol b/core/services/relay/evm/testfiles/chain_reader_test_contract.sol new file mode 100644 index 00000000000..ec38c20678a --- /dev/null +++ b/core/services/relay/evm/testfiles/chain_reader_test_contract.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8; + +struct TestStruct { + int32 Field; + string DifferentField; + uint8 OracleId; + uint8[32] OracleIds; + bytes32 Account; + bytes32[] Accounts; + int192 BigField; + MidLevelTestStruct NestedStruct; +} + +struct MidLevelTestStruct { + bytes2 FixedBytes; + InnerTestStruct Inner; +} + +struct InnerTestStruct { + int64 I; + string S; +} + +contract LatestValueHolder { + TestStruct[] private seen; + uint64[] private arr; + + constructor() { + // See chain_reader_interface_tests.go in chainlink-relay + arr.push(3); + arr.push(4); + } + + function AddTestStruct( + int32 field, + string calldata differentField, + uint8 oracleId, + uint8[32] calldata oracleIds, + bytes32 account, + bytes32[] calldata accounts, + int192 bigField, + MidLevelTestStruct calldata nestedStruct) public { + seen.push(TestStruct(field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct)); + } + + function GetElementAtIndex(uint256 i) public view returns (TestStruct memory) { + // See chain_reader_interface_tests.go in chainlink-relay + return seen[i-1]; + } + + function GetPrimitiveValue() public pure returns (uint64) { + // See chain_reader_interface_tests.go in chainlink-relay + return 3; + } + + function GetSliceValue() public view returns (uint64[] memory) { + return arr; + } +} \ No newline at end of file diff --git a/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.abi b/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.abi new file mode 100644 index 00000000000..6f73e9feb88 --- /dev/null +++ b/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"int32","name":"field","type":"int32"},{"internalType":"string","name":"differentField","type":"string"},{"internalType":"uint8","name":"oracleId","type":"uint8"},{"internalType":"uint8[32]","name":"oracleIds","type":"uint8[32]"},{"internalType":"bytes32","name":"account","type":"bytes32"},{"internalType":"bytes32[]","name":"accounts","type":"bytes32[]"},{"internalType":"int192","name":"bigField","type":"int192"},{"components":[{"internalType":"bytes2","name":"FixedBytes","type":"bytes2"},{"components":[{"internalType":"int64","name":"I","type":"int64"},{"internalType":"string","name":"S","type":"string"}],"internalType":"struct InnerTestStruct","name":"Inner","type":"tuple"}],"internalType":"struct MidLevelTestStruct","name":"nestedStruct","type":"tuple"}],"name":"AddTestStruct","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"}],"name":"GetElementAtIndex","outputs":[{"components":[{"internalType":"int32","name":"Field","type":"int32"},{"internalType":"string","name":"DifferentField","type":"string"},{"internalType":"uint8","name":"OracleId","type":"uint8"},{"internalType":"uint8[32]","name":"OracleIds","type":"uint8[32]"},{"internalType":"bytes32","name":"Account","type":"bytes32"},{"internalType":"bytes32[]","name":"Accounts","type":"bytes32[]"},{"internalType":"int192","name":"BigField","type":"int192"},{"components":[{"internalType":"bytes2","name":"FixedBytes","type":"bytes2"},{"components":[{"internalType":"int64","name":"I","type":"int64"},{"internalType":"string","name":"S","type":"string"}],"internalType":"struct InnerTestStruct","name":"Inner","type":"tuple"}],"internalType":"struct MidLevelTestStruct","name":"NestedStruct","type":"tuple"}],"internalType":"struct TestStruct","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GetPrimitiveValue","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"GetSliceValue","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.bin b/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.bin new file mode 100644 index 00000000000..90c9f543f97 --- /dev/null +++ b/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b50600180548082018255600082905260048082047fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6908101805460086003958616810261010090810a8088026001600160401b0391820219909416939093179093558654808801909755848704909301805496909516909202900a918202910219909216919091179055610e4d806100a96000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637dd6af5b146100515780639ca04f6714610066578063bdb37c901461008f578063da8e7a82146100a4575b600080fd5b61006461005f366004610921565b6100b3565b005b610079610074366004610a0c565b6102e2565b6040516100869190610b71565b60405180910390f35b610097610592565b6040516100869190610b23565b60405160038152602001610086565b60006040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b918390839080828437600092019190915250505081526020808201899052604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b602082015260400161018d84610cb2565b905281546001818101845560009384526020938490208351600a90930201805460039390930b63ffffffff1663ffffffff19909316929092178255838301518051939492936101e49392850192919091019061061e565b50604082015160028201805460ff191660ff909216919091179055606082015161021490600383019060206106a2565b506080820151600482015560a0820151805161023a916005840191602090910190610730565b5060c082015160068201805460179290920b6001600160c01b03166001600160c01b031990921691909117905560e082015180516007808401805460f09390931c61ffff1990931692909217825560208084015180516008870180549190940b67ffffffffffffffff1667ffffffffffffffff199091161783558082015180519193926102cf9260098901929091019061061e565b5050505050505050505050505050505050565b6102ea61076a565b60006102f7600184610c8d565b8154811061030757610307610deb565b90600052602060002090600a0201604051806101000160405290816000820160009054906101000a900460030b60030b60030b815260200160018201805461034e90610db6565b80601f016020809104026020016040519081016040528092919081815260200182805461037a90610db6565b80156103c75780601f1061039c576101008083540402835291602001916103c7565b820191906000526020600020905b8154815290600101906020018083116103aa57829003601f168201915b5050509183525050600282015460ff166020808301919091526040805161040081018083529190930192916003850191826000855b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116103fc57905050505050508152602001600482015481526020016005820180548060200260200160405190810160405280929190818152602001828054801561048c57602002820191906000526020600020905b815481526020019060010190808311610478575b50505091835250506006820154601790810b810b900b6020808301919091526040805180820182526007808601805460f01b6001600160f01b031916835283518085018552600888018054840b840b90930b8152600988018054959097019693959194868301949193928401919061050390610db6565b80601f016020809104026020016040519081016040528092919081815260200182805461052f90610db6565b801561057c5780601f106105515761010080835404028352916020019161057c565b820191906000526020600020905b81548152906001019060200180831161055f57829003601f168201915b5050509190925250505090525090525092915050565b6060600180548060200260200160405190810160405280929190818152602001828054801561061457602002820191906000526020600020906000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116105cf5790505b5050505050905090565b82805461062a90610db6565b90600052602060002090601f01602090048101928261064c5760008555610692565b82601f1061066557805160ff1916838001178555610692565b82800160010185558215610692579182015b82811115610692578251825591602001919060010190610677565b5061069e9291506107b9565b5090565b6001830191839082156106925791602002820160005b838211156106f657835183826101000a81548160ff021916908360ff16021790555092602001926001016020816000010492830192600103026106b8565b80156107235782816101000a81549060ff02191690556001016020816000010492830192600103026106f6565b505061069e9291506107b9565b8280548282559060005260206000209081019282156106925791602002820182811115610692578251825591602001919060010190610677565b60408051610100810182526000808252606060208301819052928201529081016107926107ce565b81526000602082018190526060604083018190528201526080016107b46107ed565b905290565b5b8082111561069e57600081556001016107ba565b6040518061040001604052806020906020820280368337509192915050565b604051806040016040528060006001600160f01b03191681526020016107b46040518060400160405280600060070b8152602001606081525090565b60008083601f84011261083b57600080fd5b50813567ffffffffffffffff81111561085357600080fd5b6020830191508360208260051b850101111561086e57600080fd5b9250929050565b80610400810183101561088757600080fd5b92915050565b8035601781900b811461089f57600080fd5b919050565b8035600381900b811461089f57600080fd5b60008083601f8401126108c857600080fd5b50813567ffffffffffffffff8111156108e057600080fd5b60208301915083602082850101111561086e57600080fd5b60006040828403121561090a57600080fd5b50919050565b803560ff8116811461089f57600080fd5b6000806000806000806000806000806104e08b8d03121561094157600080fd5b61094a8b6108a4565b995060208b013567ffffffffffffffff8082111561096757600080fd5b6109738e838f016108b6565b909b50995089915061098760408e01610910565b98506109968e60608f01610875565b97506104608d013596506104808d01359150808211156109b557600080fd5b6109c18e838f01610829565b90965094508491506109d66104a08e0161088d565b93506104c08d01359150808211156109ed57600080fd5b506109fa8d828e016108f8565b9150509295989b9194979a5092959850565b600060208284031215610a1e57600080fd5b5035919050565b600081518084526020808501945080840160005b83811015610a5557815187529582019590820190600101610a39565b509495945050505050565b8060005b6020808210610a735750610a8a565b825160ff1685529384019390910190600101610a64565b50505050565b6000815180845260005b81811015610ab657602081850181015186830182015201610a9a565b81811115610ac8576000602083870101525b50601f01601f19169290920160200192915050565b61ffff60f01b81511682526000602082015160406020850152805160070b60408501526020810151905060406060850152610b1b6080850182610a90565b949350505050565b6020808252825182820181905260009190848201906040850190845b81811015610b6557835167ffffffffffffffff1683529284019291840191600101610b3f565b50909695505050505050565b60208152610b8560208201835160030b9052565b600060208301516104e0806040850152610ba3610500850183610a90565b91506040850151610bb9606086018260ff169052565b506060850151610bcc6080860182610a60565b50608085015161048085015260a0850151601f1980868503016104a0870152610bf58483610a25565b935060c08701519150610c0e6104c087018360170b9052565b60e0870151915080868503018387015250610c298382610add565b9695505050505050565b6040805190810167ffffffffffffffff81118282101715610c5657610c56610e01565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610c8557610c85610e01565b604052919050565b600082821015610cad57634e487b7160e01b600052601160045260246000fd5b500390565b600060408236031215610cc457600080fd5b610ccc610c33565b82356001600160f01b031981168114610ce457600080fd5b815260208381013567ffffffffffffffff80821115610d0257600080fd5b818601915060408236031215610d1757600080fd5b610d1f610c33565b82358060070b8114610d3057600080fd5b81528284013582811115610d4357600080fd5b929092019136601f840112610d5757600080fd5b823582811115610d6957610d69610e01565b610d7b601f8201601f19168601610c5c565b92508083523685828601011115610d9157600080fd5b8085850186850137600090830185015280840191909152918301919091525092915050565b600181811c90821680610dca57607f821691505b6020821081141561090a57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfea26469706673582212206d3fcd0d6af66016f39be3d9948d2703697b0dfdbab6f47b494ab3d1f3d0d4c264736f6c63430008060033 \ No newline at end of file diff --git a/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.go b/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.go new file mode 100644 index 00000000000..2a8ee83e145 --- /dev/null +++ b/core/services/relay/evm/testfiles/chain_reader_test_contract_gen.go @@ -0,0 +1,341 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package testfiles + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// InnerTestStruct is an auto generated low-level Go binding around an user-defined struct. +type InnerTestStruct struct { + I int64 + S string +} + +// MidLevelTestStruct is an auto generated low-level Go binding around an user-defined struct. +type MidLevelTestStruct struct { + FixedBytes [2]byte + Inner InnerTestStruct +} + +// TestStruct is an auto generated low-level Go binding around an user-defined struct. +type TestStruct struct { + Field int32 + DifferentField string + OracleId uint8 + OracleIds [32]uint8 + Account [32]byte + Accounts [][32]byte + BigField *big.Int + NestedStruct MidLevelTestStruct +} + +// TestfilesMetaData contains all meta data concerning the Testfiles contract. +var TestfilesMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"int32\",\"name\":\"field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"differentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"oracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"oracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"bytes32\",\"name\":\"account\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"accounts\",\"type\":\"bytes32[]\"},{\"internalType\":\"int192\",\"name\":\"bigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"I\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"nestedStruct\",\"type\":\"tuple\"}],\"name\":\"AddTestStruct\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"i\",\"type\":\"uint256\"}],\"name\":\"GetElementAtIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"int32\",\"name\":\"Field\",\"type\":\"int32\"},{\"internalType\":\"string\",\"name\":\"DifferentField\",\"type\":\"string\"},{\"internalType\":\"uint8\",\"name\":\"OracleId\",\"type\":\"uint8\"},{\"internalType\":\"uint8[32]\",\"name\":\"OracleIds\",\"type\":\"uint8[32]\"},{\"internalType\":\"bytes32\",\"name\":\"Account\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32[]\",\"name\":\"Accounts\",\"type\":\"bytes32[]\"},{\"internalType\":\"int192\",\"name\":\"BigField\",\"type\":\"int192\"},{\"components\":[{\"internalType\":\"bytes2\",\"name\":\"FixedBytes\",\"type\":\"bytes2\"},{\"components\":[{\"internalType\":\"int64\",\"name\":\"I\",\"type\":\"int64\"},{\"internalType\":\"string\",\"name\":\"S\",\"type\":\"string\"}],\"internalType\":\"structInnerTestStruct\",\"name\":\"Inner\",\"type\":\"tuple\"}],\"internalType\":\"structMidLevelTestStruct\",\"name\":\"NestedStruct\",\"type\":\"tuple\"}],\"internalType\":\"structTestStruct\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GetPrimitiveValue\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GetSliceValue\",\"outputs\":[{\"internalType\":\"uint64[]\",\"name\":\"\",\"type\":\"uint64[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b50600180548082018255600082905260048082047fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6908101805460086003958616810261010090810a8088026001600160401b0391820219909416939093179093558654808801909755848704909301805496909516909202900a918202910219909216919091179055610e4d806100a96000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80637dd6af5b146100515780639ca04f6714610066578063bdb37c901461008f578063da8e7a82146100a4575b600080fd5b61006461005f366004610921565b6100b3565b005b610079610074366004610a0c565b6102e2565b6040516100869190610b71565b60405180910390f35b610097610592565b6040516100869190610b23565b60405160038152602001610086565b60006040518061010001604052808c60030b81526020018b8b8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060ff8a166020808301919091526040805161040081810183529190930192918b918390839080828437600092019190915250505081526020808201899052604080518883028181018401835289825291909301929189918991829190850190849080828437600092019190915250505090825250601785900b602082015260400161018d84610cb2565b905281546001818101845560009384526020938490208351600a90930201805460039390930b63ffffffff1663ffffffff19909316929092178255838301518051939492936101e49392850192919091019061061e565b50604082015160028201805460ff191660ff909216919091179055606082015161021490600383019060206106a2565b506080820151600482015560a0820151805161023a916005840191602090910190610730565b5060c082015160068201805460179290920b6001600160c01b03166001600160c01b031990921691909117905560e082015180516007808401805460f09390931c61ffff1990931692909217825560208084015180516008870180549190940b67ffffffffffffffff1667ffffffffffffffff199091161783558082015180519193926102cf9260098901929091019061061e565b5050505050505050505050505050505050565b6102ea61076a565b60006102f7600184610c8d565b8154811061030757610307610deb565b90600052602060002090600a0201604051806101000160405290816000820160009054906101000a900460030b60030b60030b815260200160018201805461034e90610db6565b80601f016020809104026020016040519081016040528092919081815260200182805461037a90610db6565b80156103c75780601f1061039c576101008083540402835291602001916103c7565b820191906000526020600020905b8154815290600101906020018083116103aa57829003601f168201915b5050509183525050600282015460ff166020808301919091526040805161040081018083529190930192916003850191826000855b825461010083900a900460ff168152602060019283018181049485019490930390920291018084116103fc57905050505050508152602001600482015481526020016005820180548060200260200160405190810160405280929190818152602001828054801561048c57602002820191906000526020600020905b815481526020019060010190808311610478575b50505091835250506006820154601790810b810b900b6020808301919091526040805180820182526007808601805460f01b6001600160f01b031916835283518085018552600888018054840b840b90930b8152600988018054959097019693959194868301949193928401919061050390610db6565b80601f016020809104026020016040519081016040528092919081815260200182805461052f90610db6565b801561057c5780601f106105515761010080835404028352916020019161057c565b820191906000526020600020905b81548152906001019060200180831161055f57829003601f168201915b5050509190925250505090525090525092915050565b6060600180548060200260200160405190810160405280929190818152602001828054801561061457602002820191906000526020600020906000905b82829054906101000a900467ffffffffffffffff1667ffffffffffffffff16815260200190600801906020826007010492830192600103820291508084116105cf5790505b5050505050905090565b82805461062a90610db6565b90600052602060002090601f01602090048101928261064c5760008555610692565b82601f1061066557805160ff1916838001178555610692565b82800160010185558215610692579182015b82811115610692578251825591602001919060010190610677565b5061069e9291506107b9565b5090565b6001830191839082156106925791602002820160005b838211156106f657835183826101000a81548160ff021916908360ff16021790555092602001926001016020816000010492830192600103026106b8565b80156107235782816101000a81549060ff02191690556001016020816000010492830192600103026106f6565b505061069e9291506107b9565b8280548282559060005260206000209081019282156106925791602002820182811115610692578251825591602001919060010190610677565b60408051610100810182526000808252606060208301819052928201529081016107926107ce565b81526000602082018190526060604083018190528201526080016107b46107ed565b905290565b5b8082111561069e57600081556001016107ba565b6040518061040001604052806020906020820280368337509192915050565b604051806040016040528060006001600160f01b03191681526020016107b46040518060400160405280600060070b8152602001606081525090565b60008083601f84011261083b57600080fd5b50813567ffffffffffffffff81111561085357600080fd5b6020830191508360208260051b850101111561086e57600080fd5b9250929050565b80610400810183101561088757600080fd5b92915050565b8035601781900b811461089f57600080fd5b919050565b8035600381900b811461089f57600080fd5b60008083601f8401126108c857600080fd5b50813567ffffffffffffffff8111156108e057600080fd5b60208301915083602082850101111561086e57600080fd5b60006040828403121561090a57600080fd5b50919050565b803560ff8116811461089f57600080fd5b6000806000806000806000806000806104e08b8d03121561094157600080fd5b61094a8b6108a4565b995060208b013567ffffffffffffffff8082111561096757600080fd5b6109738e838f016108b6565b909b50995089915061098760408e01610910565b98506109968e60608f01610875565b97506104608d013596506104808d01359150808211156109b557600080fd5b6109c18e838f01610829565b90965094508491506109d66104a08e0161088d565b93506104c08d01359150808211156109ed57600080fd5b506109fa8d828e016108f8565b9150509295989b9194979a5092959850565b600060208284031215610a1e57600080fd5b5035919050565b600081518084526020808501945080840160005b83811015610a5557815187529582019590820190600101610a39565b509495945050505050565b8060005b6020808210610a735750610a8a565b825160ff1685529384019390910190600101610a64565b50505050565b6000815180845260005b81811015610ab657602081850181015186830182015201610a9a565b81811115610ac8576000602083870101525b50601f01601f19169290920160200192915050565b61ffff60f01b81511682526000602082015160406020850152805160070b60408501526020810151905060406060850152610b1b6080850182610a90565b949350505050565b6020808252825182820181905260009190848201906040850190845b81811015610b6557835167ffffffffffffffff1683529284019291840191600101610b3f565b50909695505050505050565b60208152610b8560208201835160030b9052565b600060208301516104e0806040850152610ba3610500850183610a90565b91506040850151610bb9606086018260ff169052565b506060850151610bcc6080860182610a60565b50608085015161048085015260a0850151601f1980868503016104a0870152610bf58483610a25565b935060c08701519150610c0e6104c087018360170b9052565b60e0870151915080868503018387015250610c298382610add565b9695505050505050565b6040805190810167ffffffffffffffff81118282101715610c5657610c56610e01565b60405290565b604051601f8201601f1916810167ffffffffffffffff81118282101715610c8557610c85610e01565b604052919050565b600082821015610cad57634e487b7160e01b600052601160045260246000fd5b500390565b600060408236031215610cc457600080fd5b610ccc610c33565b82356001600160f01b031981168114610ce457600080fd5b815260208381013567ffffffffffffffff80821115610d0257600080fd5b818601915060408236031215610d1757600080fd5b610d1f610c33565b82358060070b8114610d3057600080fd5b81528284013582811115610d4357600080fd5b929092019136601f840112610d5757600080fd5b823582811115610d6957610d69610e01565b610d7b601f8201601f19168601610c5c565b92508083523685828601011115610d9157600080fd5b8085850186850137600090830185015280840191909152918301919091525092915050565b600181811c90821680610dca57607f821691505b6020821081141561090a57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052604160045260246000fdfea26469706673582212206d3fcd0d6af66016f39be3d9948d2703697b0dfdbab6f47b494ab3d1f3d0d4c264736f6c63430008060033", +} + +// TestfilesABI is the input ABI used to generate the binding from. +// Deprecated: Use TestfilesMetaData.ABI instead. +var TestfilesABI = TestfilesMetaData.ABI + +// TestfilesBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use TestfilesMetaData.Bin instead. +var TestfilesBin = TestfilesMetaData.Bin + +// DeployTestfiles deploys a new Ethereum contract, binding an instance of Testfiles to it. +func DeployTestfiles(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Testfiles, error) { + parsed, err := TestfilesMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(TestfilesBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &Testfiles{TestfilesCaller: TestfilesCaller{contract: contract}, TestfilesTransactor: TestfilesTransactor{contract: contract}, TestfilesFilterer: TestfilesFilterer{contract: contract}}, nil +} + +// Testfiles is an auto generated Go binding around an Ethereum contract. +type Testfiles struct { + TestfilesCaller // Read-only binding to the contract + TestfilesTransactor // Write-only binding to the contract + TestfilesFilterer // Log filterer for contract events +} + +// TestfilesCaller is an auto generated read-only Go binding around an Ethereum contract. +type TestfilesCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestfilesTransactor is an auto generated write-only Go binding around an Ethereum contract. +type TestfilesTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestfilesFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type TestfilesFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// TestfilesSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type TestfilesSession struct { + Contract *Testfiles // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestfilesCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type TestfilesCallerSession struct { + Contract *TestfilesCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// TestfilesTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type TestfilesTransactorSession struct { + Contract *TestfilesTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// TestfilesRaw is an auto generated low-level Go binding around an Ethereum contract. +type TestfilesRaw struct { + Contract *Testfiles // Generic contract binding to access the raw methods on +} + +// TestfilesCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type TestfilesCallerRaw struct { + Contract *TestfilesCaller // Generic read-only contract binding to access the raw methods on +} + +// TestfilesTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type TestfilesTransactorRaw struct { + Contract *TestfilesTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewTestfiles creates a new instance of Testfiles, bound to a specific deployed contract. +func NewTestfiles(address common.Address, backend bind.ContractBackend) (*Testfiles, error) { + contract, err := bindTestfiles(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &Testfiles{TestfilesCaller: TestfilesCaller{contract: contract}, TestfilesTransactor: TestfilesTransactor{contract: contract}, TestfilesFilterer: TestfilesFilterer{contract: contract}}, nil +} + +// NewTestfilesCaller creates a new read-only instance of Testfiles, bound to a specific deployed contract. +func NewTestfilesCaller(address common.Address, caller bind.ContractCaller) (*TestfilesCaller, error) { + contract, err := bindTestfiles(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &TestfilesCaller{contract: contract}, nil +} + +// NewTestfilesTransactor creates a new write-only instance of Testfiles, bound to a specific deployed contract. +func NewTestfilesTransactor(address common.Address, transactor bind.ContractTransactor) (*TestfilesTransactor, error) { + contract, err := bindTestfiles(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &TestfilesTransactor{contract: contract}, nil +} + +// NewTestfilesFilterer creates a new log filterer instance of Testfiles, bound to a specific deployed contract. +func NewTestfilesFilterer(address common.Address, filterer bind.ContractFilterer) (*TestfilesFilterer, error) { + contract, err := bindTestfiles(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &TestfilesFilterer{contract: contract}, nil +} + +// bindTestfiles binds a generic wrapper to an already deployed contract. +func bindTestfiles(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := TestfilesMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Testfiles *TestfilesRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Testfiles.Contract.TestfilesCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Testfiles *TestfilesRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Testfiles.Contract.TestfilesTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Testfiles *TestfilesRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Testfiles.Contract.TestfilesTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_Testfiles *TestfilesCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _Testfiles.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_Testfiles *TestfilesTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Testfiles.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_Testfiles *TestfilesTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _Testfiles.Contract.contract.Transact(opts, method, params...) +} + +// GetElementAtIndex is a free data retrieval call binding the contract method 0x9ca04f67. +// +// Solidity: function GetElementAtIndex(uint256 i) view returns((int32,string,uint8,uint8[32],bytes32,bytes32[],int192,(bytes2,(int64,string)))) +func (_Testfiles *TestfilesCaller) GetElementAtIndex(opts *bind.CallOpts, i *big.Int) (TestStruct, error) { + var out []interface{} + err := _Testfiles.contract.Call(opts, &out, "GetElementAtIndex", i) + + if err != nil { + return *new(TestStruct), err + } + + out0 := *abi.ConvertType(out[0], new(TestStruct)).(*TestStruct) + + return out0, err + +} + +// GetElementAtIndex is a free data retrieval call binding the contract method 0x9ca04f67. +// +// Solidity: function GetElementAtIndex(uint256 i) view returns((int32,string,uint8,uint8[32],bytes32,bytes32[],int192,(bytes2,(int64,string)))) +func (_Testfiles *TestfilesSession) GetElementAtIndex(i *big.Int) (TestStruct, error) { + return _Testfiles.Contract.GetElementAtIndex(&_Testfiles.CallOpts, i) +} + +// GetElementAtIndex is a free data retrieval call binding the contract method 0x9ca04f67. +// +// Solidity: function GetElementAtIndex(uint256 i) view returns((int32,string,uint8,uint8[32],bytes32,bytes32[],int192,(bytes2,(int64,string)))) +func (_Testfiles *TestfilesCallerSession) GetElementAtIndex(i *big.Int) (TestStruct, error) { + return _Testfiles.Contract.GetElementAtIndex(&_Testfiles.CallOpts, i) +} + +// GetPrimitiveValue is a free data retrieval call binding the contract method 0xda8e7a82. +// +// Solidity: function GetPrimitiveValue() pure returns(uint64) +func (_Testfiles *TestfilesCaller) GetPrimitiveValue(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _Testfiles.contract.Call(opts, &out, "GetPrimitiveValue") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// GetPrimitiveValue is a free data retrieval call binding the contract method 0xda8e7a82. +// +// Solidity: function GetPrimitiveValue() pure returns(uint64) +func (_Testfiles *TestfilesSession) GetPrimitiveValue() (uint64, error) { + return _Testfiles.Contract.GetPrimitiveValue(&_Testfiles.CallOpts) +} + +// GetPrimitiveValue is a free data retrieval call binding the contract method 0xda8e7a82. +// +// Solidity: function GetPrimitiveValue() pure returns(uint64) +func (_Testfiles *TestfilesCallerSession) GetPrimitiveValue() (uint64, error) { + return _Testfiles.Contract.GetPrimitiveValue(&_Testfiles.CallOpts) +} + +// GetSliceValue is a free data retrieval call binding the contract method 0xbdb37c90. +// +// Solidity: function GetSliceValue() view returns(uint64[]) +func (_Testfiles *TestfilesCaller) GetSliceValue(opts *bind.CallOpts) ([]uint64, error) { + var out []interface{} + err := _Testfiles.contract.Call(opts, &out, "GetSliceValue") + + if err != nil { + return *new([]uint64), err + } + + out0 := *abi.ConvertType(out[0], new([]uint64)).(*[]uint64) + + return out0, err + +} + +// GetSliceValue is a free data retrieval call binding the contract method 0xbdb37c90. +// +// Solidity: function GetSliceValue() view returns(uint64[]) +func (_Testfiles *TestfilesSession) GetSliceValue() ([]uint64, error) { + return _Testfiles.Contract.GetSliceValue(&_Testfiles.CallOpts) +} + +// GetSliceValue is a free data retrieval call binding the contract method 0xbdb37c90. +// +// Solidity: function GetSliceValue() view returns(uint64[]) +func (_Testfiles *TestfilesCallerSession) GetSliceValue() ([]uint64, error) { + return _Testfiles.Contract.GetSliceValue(&_Testfiles.CallOpts) +} + +// AddTestStruct is a paid mutator transaction binding the contract method 0x7dd6af5b. +// +// Solidity: function AddTestStruct(int32 field, string differentField, uint8 oracleId, uint8[32] oracleIds, bytes32 account, bytes32[] accounts, int192 bigField, (bytes2,(int64,string)) nestedStruct) returns() +func (_Testfiles *TestfilesTransactor) AddTestStruct(opts *bind.TransactOpts, field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account [32]byte, accounts [][32]byte, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _Testfiles.contract.Transact(opts, "AddTestStruct", field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +// AddTestStruct is a paid mutator transaction binding the contract method 0x7dd6af5b. +// +// Solidity: function AddTestStruct(int32 field, string differentField, uint8 oracleId, uint8[32] oracleIds, bytes32 account, bytes32[] accounts, int192 bigField, (bytes2,(int64,string)) nestedStruct) returns() +func (_Testfiles *TestfilesSession) AddTestStruct(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account [32]byte, accounts [][32]byte, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _Testfiles.Contract.AddTestStruct(&_Testfiles.TransactOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} + +// AddTestStruct is a paid mutator transaction binding the contract method 0x7dd6af5b. +// +// Solidity: function AddTestStruct(int32 field, string differentField, uint8 oracleId, uint8[32] oracleIds, bytes32 account, bytes32[] accounts, int192 bigField, (bytes2,(int64,string)) nestedStruct) returns() +func (_Testfiles *TestfilesTransactorSession) AddTestStruct(field int32, differentField string, oracleId uint8, oracleIds [32]uint8, account [32]byte, accounts [][32]byte, bigField *big.Int, nestedStruct MidLevelTestStruct) (*types.Transaction, error) { + return _Testfiles.Contract.AddTestStruct(&_Testfiles.TransactOpts, field, differentField, oracleId, oracleIds, account, accounts, bigField, nestedStruct) +} diff --git a/core/services/relay/evm/testfiles/chainlink_reader_test_setup.sh b/core/services/relay/evm/testfiles/chainlink_reader_test_setup.sh new file mode 100755 index 00000000000..60ddc6cdc1a --- /dev/null +++ b/core/services/relay/evm/testfiles/chainlink_reader_test_setup.sh @@ -0,0 +1,8 @@ +#!/bin/bash +set -e +# called from the directory above so cd to this directory +cd testfiles +solc --optimize --bin --abi chain_reader_test_contract.sol -o . +mv LatestValueHolder.bin chain_reader_test_contract_gen.bin +mv LatestValueHolder.abi chain_reader_test_contract_gen.abi +../../../../../tools/bin/abigen --bin=chain_reader_test_contract_gen.bin --abi=chain_reader_test_contract_gen.abi --pkg=testfiles --out=chain_reader_test_contract_gen.go diff --git a/core/services/relay/evm/types/types.go b/core/services/relay/evm/types/types.go index d9f6a56fa4e..6f3b6b5fc6f 100644 --- a/core/services/relay/evm/types/types.go +++ b/core/services/relay/evm/types/types.go @@ -20,11 +20,23 @@ import ( "github.com/smartcontractkit/chainlink/v2/core/utils" ) +type RelayOpts struct { + // TODO BCF-2508 -- should anyone ever get the raw config bytes that are embedded in args? if not, + // make this private and wrap the arg fields with funcs on RelayOpts + commontypes.RelayArgs + c *RelayConfig +} + type ChainReaderConfig struct { // ChainContractReaders key is contract name ChainContractReaders map[string]ChainContractReader `json:"chainContractReaders"` } +type CodecConfig struct { + // ChainCodecConfigs is the type's name for the codec + ChainCodecConfigs map[string]ChainCodedConfig `json:"chainCodecConfig"` +} + type ChainCodedConfig struct { TypeAbi string `json:"typeAbi"` // TODO transform configs that allow hard-coding values or transforming them (max, min, median etc) @@ -32,7 +44,7 @@ type ChainCodedConfig struct { type ChainContractReader struct { ContractABI string `json:"contractABI"` - // ChainReaderDefinitions key is chainAgnostic read name. + // key is genericName from config ChainReaderDefinitions map[string]ChainReaderDefinition `json:"chainReaderDefinitions"` } @@ -41,7 +53,7 @@ type ChainReaderDefinition struct { Params map[string]any `json:"params"` ReturnValues []string `json:"returnValues"` CacheEnabled bool `json:"cacheEnabled"` - ReadType ReadType `json:"readType"` + ReadType `json:"readType"` } type ReadType int64 @@ -57,6 +69,7 @@ type RelayConfig struct { EffectiveTransmitterID null.String `json:"effectiveTransmitterID"` ConfigContractAddress *common.Address `json:"configContractAddress"` ChainReader *ChainReaderConfig `json:"chainReader"` + Codec *CodecConfig `json:"codec"` // Contract-specific SendingKeys pq.StringArray `json:"sendingKeys"` @@ -65,13 +78,6 @@ type RelayConfig struct { FeedID *common.Hash `json:"feedID"` } -type RelayOpts struct { - // TODO BCF-2508 -- should anyone ever get the raw config bytes that are embedded in args? if not, - // make this private and wrap the arg fields with funcs on RelayOpts - commontypes.RelayArgs - c *RelayConfig -} - var ErrBadRelayConfig = errors.New("bad relay config") func NewRelayOpts(args types.RelayArgs) *RelayOpts { diff --git a/go.mod b/go.mod index c1f7d4aa28a..c7bcadcf346 100644 --- a/go.mod +++ b/go.mod @@ -66,10 +66,10 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7 - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606 - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90 - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7 + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/libocr v0.0.0-20231107151413-13e0202ae8d7 github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 diff --git a/go.sum b/go.sum index 8e68b54f53e..71526ba239f 100644 --- a/go.sum +++ b/go.sum @@ -1468,14 +1468,14 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7 h1:QLhOOIeFldRBc+ESAVZfEPrIpfeiduwnmHlvDt1iFmM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7/go.mod h1:ipa32mlAXbF3uUQqfVDNxQbBiyu2KKA3AwhK3ewhSSU= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606 h1:4RUoJlw/BEonMCkh7AuoTGBWbV+7CY8n0kpesz5tlqA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606/go.mod h1:xwuKim71kn4z+8r1MO6mjlxl3bGlGPpHnqWJW6fQsuU= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90 h1:tcnV7rhW0wC/oukNE/wPRQzC1K86YERbVaWF5KYda7g= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90/go.mod h1:VDRlSwdE7x/7dPIzg4mr+m80dvNm/vmub/7t9T9Mf/k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0 h1:rWrd5VnZWpFXH/UzcGI0PxhT6u4BAuoZDBr2QBy5YNc= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0/go.mod h1:L2d4754c/HjL8GzzdtzaRsWUxKh1OWMx2Kd6faRf/PA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7 h1:Q7Lif7+Hr3A1gsFooCp/StP93qVPuixn32XEllZnSdY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7/go.mod h1:ipa32mlAXbF3uUQqfVDNxQbBiyu2KKA3AwhK3ewhSSU= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 h1:iu1pNbUoSDTrp+7BUtfTygZ2C0f5C2ZOBQhIoJjp+S0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679/go.mod h1:2Jx7bTEk4ujFQdsZpZq3A0BydvaVPs6mX8clUfxHOEM= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a h1:JoyTazNcqXvZoMQjNfB0eapnlaoMS6pI0NeRvouRvog= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a/go.mod h1:rioELYwPY2xBtzPRN/D08Y7iTPbIQEjPknYdJK51CzQ= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006 h1:1GzOKT53e8N7ZPwsyf1hSbKsynZmXmLOIL3DMvGq9sc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006/go.mod h1:tJLhL7gJ+V3+4N/yLxXIvJ0DAiuyqZCa31+2BSbw7zo= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868/go.mod h1:Kn1Hape05UzFZ7bOUnm3GVsHzP0TNrVmpfXYNHdqGGs= github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss= diff --git a/integration-tests/go.mod b/integration-tests/go.mod index 56f87166f8e..582e102986b 100644 --- a/integration-tests/go.mod +++ b/integration-tests/go.mod @@ -24,7 +24,7 @@ require ( github.com/segmentio/ksuid v1.0.4 github.com/slack-go/slack v0.12.2 github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 - github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7 + github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7 github.com/smartcontractkit/chainlink-testing-framework v1.19.5 github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000 @@ -390,9 +390,9 @@ require ( github.com/shopspring/decimal v1.3.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect - github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606 // indirect - github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90 // indirect - github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0 // indirect + github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 // indirect + github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a // indirect + github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006 // indirect github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect github.com/smartcontractkit/wsrpc v0.7.2 // indirect github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect diff --git a/integration-tests/go.sum b/integration-tests/go.sum index ee9667496fc..25680abdfe1 100644 --- a/integration-tests/go.sum +++ b/integration-tests/go.sum @@ -2375,14 +2375,14 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459 h1:hJhuShYv9eUQxHJQdOmyEymVmApOrICrQdOY7kKQ5Io= github.com/smartcontractkit/chainlink-automation v1.0.0-alpha.0.0.20231120164534-d4cab696c459/go.mod h1:INSchkV3ntyDdlZKGWA030MPDpp6pbeuiRkRKYFCm2k= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7 h1:QLhOOIeFldRBc+ESAVZfEPrIpfeiduwnmHlvDt1iFmM= -github.com/smartcontractkit/chainlink-common v0.1.7-0.20231123003013-379db0b9e4c7/go.mod h1:ipa32mlAXbF3uUQqfVDNxQbBiyu2KKA3AwhK3ewhSSU= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606 h1:4RUoJlw/BEonMCkh7AuoTGBWbV+7CY8n0kpesz5tlqA= -github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231120072345-ec92d212f606/go.mod h1:xwuKim71kn4z+8r1MO6mjlxl3bGlGPpHnqWJW6fQsuU= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90 h1:tcnV7rhW0wC/oukNE/wPRQzC1K86YERbVaWF5KYda7g= -github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231126115247-e39408d74e90/go.mod h1:VDRlSwdE7x/7dPIzg4mr+m80dvNm/vmub/7t9T9Mf/k= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0 h1:rWrd5VnZWpFXH/UzcGI0PxhT6u4BAuoZDBr2QBy5YNc= -github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231120192657-3769c7fcc8f0/go.mod h1:L2d4754c/HjL8GzzdtzaRsWUxKh1OWMx2Kd6faRf/PA= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7 h1:Q7Lif7+Hr3A1gsFooCp/StP93qVPuixn32XEllZnSdY= +github.com/smartcontractkit/chainlink-common v0.1.7-0.20231128165554-4ca240b67cd7/go.mod h1:ipa32mlAXbF3uUQqfVDNxQbBiyu2KKA3AwhK3ewhSSU= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679 h1:iu1pNbUoSDTrp+7BUtfTygZ2C0f5C2ZOBQhIoJjp+S0= +github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231128204301-ee4297eff679/go.mod h1:2Jx7bTEk4ujFQdsZpZq3A0BydvaVPs6mX8clUfxHOEM= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a h1:JoyTazNcqXvZoMQjNfB0eapnlaoMS6pI0NeRvouRvog= +github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231130211003-6d1bb2f0b68a/go.mod h1:rioELYwPY2xBtzPRN/D08Y7iTPbIQEjPknYdJK51CzQ= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006 h1:1GzOKT53e8N7ZPwsyf1hSbKsynZmXmLOIL3DMvGq9sc= +github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231128204445-3d61b12a0006/go.mod h1:tJLhL7gJ+V3+4N/yLxXIvJ0DAiuyqZCa31+2BSbw7zo= github.com/smartcontractkit/chainlink-testing-framework v1.19.5 h1:Iq1L7wCA8IkBBtP3p6W2ttu8v9cJp95spusnozW1UrA= github.com/smartcontractkit/chainlink-testing-framework v1.19.5/go.mod h1:+FVgkz6phTc+piVT57AcQfr3I8xvZgZ1lOpRPOu/dLQ= github.com/smartcontractkit/chainlink-vrf v0.0.0-20231120191722-fef03814f868 h1:FFdvEzlYwcuVHkdZ8YnZR/XomeMGbz5E2F2HZI3I3w8= diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go index 4384e66d4b3..e06789309e3 100644 --- a/plugins/medianpoc/plugin_test.go +++ b/plugins/medianpoc/plugin_test.go @@ -12,6 +12,7 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" + "github.com/smartcontractkit/chainlink/v2/core/logger" ) @@ -75,6 +76,10 @@ func (p provider) ChainReader() types.ChainReader { return nil } +func (p provider) Codec() types.Codec { + return nil +} + func TestNewPlugin(t *testing.T) { lggr := logger.TestLogger(t) p := NewPlugin(lggr)