From 1e9a5a56ed2c94527ffced38a2fc6cf47750c77d Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Wed, 24 Apr 2024 16:15:19 -0400 Subject: [PATCH 01/15] draft impl with query params --- config/config.go | 77 ++++++++++++++++++++++-- config/keys.go | 4 +- config/test_utils.go | 15 ++++- main/main.go | 11 ---- peers/app_request_network.go | 8 +-- relayer/listener.go | 4 -- tests/utils/utils.go | 4 +- validators/canonical_validator_client.go | 26 ++++---- 8 files changed, 105 insertions(+), 44 deletions(-) diff --git a/config/config.go b/config/config.go index 6007fc24..56840235 100644 --- a/config/config.go +++ b/config/config.go @@ -11,10 +11,13 @@ import ( "net/url" "os" + "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/rpc" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/awm-relayer/utils" "github.com/ava-labs/subnet-evm/ethclient" @@ -122,16 +125,33 @@ type WarpQuorum struct { QuorumDenominator uint64 } +type apiClient struct { + BaseURL string `mapstructure:"base-url" json:"base-url"` + QueryParams map[string]string `mapstructure:"query-parameters" json:"query-parameters"` + + options []rpc.Option +} + +type PChainAPI struct { + apiClient + client platformvm.Client +} + +type InfoAPI struct { + apiClient + client info.Client +} + // Top-level configuration type Config struct { LogLevel string `mapstructure:"log-level" json:"log-level"` - PChainAPIURL string `mapstructure:"p-chain-api-url" json:"p-chain-api-url"` - InfoAPIURL string `mapstructure:"info-api-url" json:"info-api-url"` StorageLocation string `mapstructure:"storage-location" json:"storage-location"` RedisURL string `mapstructure:"redis-url" json:"redis-url"` APIPort uint16 `mapstructure:"api-port" json:"api-port"` MetricsPort uint16 `mapstructure:"metrics-port" json:"metrics-port"` + PChainAPI *PChainAPI `mapstructure:"p-chain-api" json:"p-chain-api"` + InfoAPI *InfoAPI `mapstructure:"info-api" json:"info-api"` SourceBlockchains []*SourceBlockchain `mapstructure:"source-blockchains" json:"source-blockchains"` DestinationBlockchains []*DestinationBlockchain `mapstructure:"destination-blockchains" json:"destination-blockchains"` ProcessMissedBlocks bool `mapstructure:"process-missed-blocks" json:"process-missed-blocks"` @@ -172,13 +192,17 @@ func BuildConfig(v *viper.Viper) (Config, bool, error) { ) cfg.LogLevel = v.GetString(LogLevelKey) - cfg.PChainAPIURL = v.GetString(PChainAPIURLKey) - cfg.InfoAPIURL = v.GetString(InfoAPIURLKey) cfg.StorageLocation = v.GetString(StorageLocationKey) cfg.RedisURL = v.GetString(RedisURLKey) cfg.ProcessMissedBlocks = v.GetBool(ProcessMissedBlocksKey) cfg.APIPort = v.GetUint16(APIPortKey) cfg.MetricsPort = v.GetUint16(MetricsPortKey) + if err := v.UnmarshalKey(PChainAPIKey, &cfg.PChainAPI); err != nil { + return Config{}, false, fmt.Errorf("failed to unmarshal P-Chain API: %w", err) + } + if err := v.UnmarshalKey(InfoAPIKey, &cfg.InfoAPI); err != nil { + return Config{}, false, fmt.Errorf("failed to unmarshal Info API: %w", err) + } if err := v.UnmarshalKey(ManualWarpMessagesKey, &cfg.ManualWarpMessages); err != nil { return Config{}, false, fmt.Errorf("failed to unmarshal manual warp messages: %w", err) } @@ -230,10 +254,10 @@ func (c *Config) Validate() error { if len(c.DestinationBlockchains) == 0 { return errors.New("relayer not configured to relay to any subnets. A list of destination subnets must be provided in the configuration file") } - if _, err := url.ParseRequestURI(c.PChainAPIURL); err != nil { + if err := c.PChainAPI.Validate(); err != nil { return err } - if _, err := url.ParseRequestURI(c.InfoAPIURL); err != nil { + if err := c.InfoAPI.Validate(); err != nil { return err } @@ -408,6 +432,47 @@ func (c *Config) InitializeWarpQuorums() error { return nil } +func (a *apiClient) Validate() error { + if _, err := url.ParseRequestURI(a.BaseURL); err != nil { + return fmt.Errorf("invalid base URL: %w", err) + } + a.options = make([]rpc.Option, 0, len(a.QueryParams)) + for key, value := range a.QueryParams { + a.options = append(a.options, rpc.WithQueryParam(key, value)) + } + return nil +} + +func (p *PChainAPI) Validate() error { + if err := p.apiClient.Validate(); err != nil { + return err + } + + p.client = platformvm.NewClient(p.BaseURL) + return nil +} + +func (i *InfoAPI) Validate() error { + if err := i.apiClient.Validate(); err != nil { + return err + } + + i.client = info.NewClient(i.BaseURL) + return nil +} + +func (c *apiClient) GetOptions() []rpc.Option { + return c.options +} + +func (p *PChainAPI) GetClient() platformvm.Client { + return p.client +} + +func (i *InfoAPI) GetClient() info.Client { + return i.client +} + // Validates the source subnet configuration, including verifying that the supported destinations are present in destinationBlockchainIDs // Does not modify the public fields as derived from the configuration passed to the application, // but does initialize private fields available through getters diff --git a/config/keys.go b/config/keys.go index ca5808b6..316445d0 100644 --- a/config/keys.go +++ b/config/keys.go @@ -7,8 +7,8 @@ package config const ( ConfigFileKey = "config-file" LogLevelKey = "log-level" - PChainAPIURLKey = "p-chain-api-url" - InfoAPIURLKey = "info-api-url" + PChainAPIKey = "p-chain-api" + InfoAPIKey = "info-api" APIPortKey = "api-port" MetricsPortKey = "metrics-port" SourceBlockchainsKey = "source-blockchains" diff --git a/config/test_utils.go b/config/test_utils.go index 40fe0ec4..9cea630e 100644 --- a/config/test_utils.go +++ b/config/test_utils.go @@ -16,9 +16,18 @@ var ( // Valid configuration objects to be used by tests in external packages var ( TestValidConfig = Config{ - LogLevel: "info", - PChainAPIURL: "http://test.avax.network", - InfoAPIURL: "http://test.avax.network", + LogLevel: "info", + PChainAPI: &PChainAPI{ + apiClient: apiClient{ + BaseURL: "http://test.avax.network", + }, + // BaseURL: "http://test.avax.network", + }, + InfoAPI: &InfoAPI{ + apiClient: apiClient{ + BaseURL: "http://test.avax.network", + }, + }, SourceBlockchains: []*SourceBlockchain{ { RPCEndpoint: fmt.Sprintf("http://test.avax.network/ext/bc/%s/rpc", testBlockchainID), diff --git a/main/main.go b/main/main.go index fa4759f2..8f045339 100644 --- a/main/main.go +++ b/main/main.go @@ -12,13 +12,11 @@ import ( "os" "github.com/alexliesenfeld/health" - "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/api/metrics" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/peers" @@ -70,10 +68,6 @@ func main() { } logger.Info(fmt.Sprintf("Set config options.%s", overwrittenLog)) - // Global P-Chain and Info clients used to get subnet validator sets - pChainClient := platformvm.NewClient(cfg.PChainAPIURL) - infoClient := info.NewClient(cfg.InfoAPIURL) - // Initialize all destination clients logger.Info("Initializing destination clients") destinationClients, err := vms.CreateDestinationClients(logger, cfg) @@ -106,8 +100,6 @@ func main() { networkLogLevel, registerer, &cfg, - infoClient, - pChainClient, ) if err != nil { logger.Error( @@ -214,7 +206,6 @@ func main() { metrics, db, *subnetInfo, - pChainClient, network, responseChans[blockchainID], destinationClients, @@ -239,7 +230,6 @@ func runRelayer( metrics *relayer.ApplicationRelayerMetrics, db database.RelayerDatabase, sourceSubnetInfo config.SourceBlockchain, - pChainClient platformvm.Client, network *peers.AppRequestNetwork, responseChan chan message.InboundMessage, destinationClients map[ids.ID]vms.DestinationClient, @@ -258,7 +248,6 @@ func runRelayer( metrics, db, sourceSubnetInfo, - pChainClient, network, responseChan, destinationClients, diff --git a/peers/app_request_network.go b/peers/app_request_network.go index cee48d09..31a7c596 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -19,7 +19,6 @@ import ( "github.com/ava-labs/avalanchego/utils/ips" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/utils" @@ -47,8 +46,6 @@ func NewNetwork( logLevel logging.Level, registerer prometheus.Registerer, cfg *config.Config, - infoClient info.Client, - pChainClient platformvm.Client, ) (*AppRequestNetwork, map[ids.ID]chan message.InboundMessage, error) { logger := logging.NewLogger( "awm-relayer-p2p", @@ -59,7 +56,8 @@ func NewNetwork( ), ) - networkID, err := infoClient.GetNetworkID(context.Background()) + infoClient := cfg.InfoAPI.GetClient() + networkID, err := infoClient.GetNetworkID(context.Background(), cfg.InfoAPI.GetOptions()...) if err != nil { logger.Error( "Failed to get network ID", @@ -100,7 +98,7 @@ func NewNetwork( return nil, nil, err } - validatorClient := validators.NewCanonicalValidatorClient(logger, pChainClient) + validatorClient := validators.NewCanonicalValidatorClient(logger, cfg.PChainAPI) arNetwork := &AppRequestNetwork{ Network: testNetwork, diff --git a/relayer/listener.go b/relayer/listener.go index edecb0e8..b03a04cc 100644 --- a/relayer/listener.go +++ b/relayer/listener.go @@ -13,7 +13,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/awm-relayer/config" "github.com/ava-labs/awm-relayer/database" "github.com/ava-labs/awm-relayer/messages" @@ -35,7 +34,6 @@ const ( // Listener handles all messages sent from a given source chain type Listener struct { Subscriber vms.Subscriber - pChainClient platformvm.Client currentRequestID uint32 responseChan chan message.InboundMessage contractMessage vms.ContractMessage @@ -53,7 +51,6 @@ func NewListener( metrics *ApplicationRelayerMetrics, db database.RelayerDatabase, sourceBlockchain config.SourceBlockchain, - pChainClient platformvm.Client, network *peers.AppRequestNetwork, responseChan chan message.InboundMessage, destinationClients map[ids.ID]vms.DestinationClient, @@ -120,7 +117,6 @@ func NewListener( ) lstnr := Listener{ Subscriber: sub, - pChainClient: pChainClient, currentRequestID: rand.Uint32(), // Initialize to a random value to mitigate requestID collision responseChan: responseChan, contractMessage: vms.NewContractMessage(logger, sourceBlockchain), diff --git a/tests/utils/utils.go b/tests/utils/utils.go index 27cc7b62..b41f2b5c 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -167,8 +167,8 @@ func CreateDefaultRelayerConfig( return config.Config{ LogLevel: logging.Info.LowerString(), - PChainAPIURL: sourceSubnetsInfo[0].NodeURIs[0], - InfoAPIURL: sourceSubnetsInfo[0].NodeURIs[0], + PChainAPI: sourceSubnetsInfo[0].NodeURIs[0], + InfoAPI: sourceSubnetsInfo[0].NodeURIs[0], StorageLocation: StorageLocation, ProcessMissedBlocks: false, SourceBlockchains: sources, diff --git a/validators/canonical_validator_client.go b/validators/canonical_validator_client.go index ccf44b03..227a7b4d 100644 --- a/validators/canonical_validator_client.go +++ b/validators/canonical_validator_client.go @@ -9,8 +9,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/rpc" "github.com/ava-labs/avalanchego/vms/platformvm" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/awm-relayer/config" "go.uber.org/zap" ) @@ -18,14 +20,16 @@ var _ validators.State = &CanonicalValidatorClient{} // CanonicalValidatorClient wraps platformvm.Client and implements validators.State type CanonicalValidatorClient struct { - client platformvm.Client - logger logging.Logger + client platformvm.Client + options []rpc.Option + logger logging.Logger } -func NewCanonicalValidatorClient(logger logging.Logger, client platformvm.Client) *CanonicalValidatorClient { +func NewCanonicalValidatorClient(logger logging.Logger, api *config.PChainAPI) *CanonicalValidatorClient { return &CanonicalValidatorClient{ - client: client, - logger: logger, + client: api.GetClient(), + logger: logger, + options: api.GetOptions(), } } @@ -59,15 +63,15 @@ func (v *CanonicalValidatorClient) GetCurrentCanonicalValidatorSet(subnetID ids. } func (v *CanonicalValidatorClient) GetMinimumHeight(ctx context.Context) (uint64, error) { - return v.client.GetHeight(ctx) + return v.client.GetHeight(ctx, v.options...) } func (v *CanonicalValidatorClient) GetCurrentHeight(ctx context.Context) (uint64, error) { - return v.client.GetHeight(ctx) + return v.client.GetHeight(ctx, v.options...) } func (v *CanonicalValidatorClient) GetSubnetID(ctx context.Context, blockchainID ids.ID) (ids.ID, error) { - return v.client.ValidatedBy(ctx, blockchainID) + return v.client.ValidatedBy(ctx, blockchainID, v.options...) } // Gets the current validator set of the given subnet ID, including the validators' BLS public @@ -84,7 +88,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( // Get the current subnet validators. These validators are not expected to include // BLS signing information given that addPermissionlessValidatorTx is only used to // add primary network validators. - subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil) + subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil, v.options...) if err != nil { return nil, err } @@ -100,7 +104,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( Weight: subnetVdr.Weight, } } - primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs) + primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.options...) if err != nil { return nil, err } @@ -142,7 +146,7 @@ func (v *CanonicalValidatorClient) GetValidatorSet( ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. - res, err := v.client.GetValidatorsAt(ctx, subnetID, height) + res, err := v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) if err != nil { v.logger.Debug( "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", From f3a819e8d4241348758f7445195a33ff2b8789bd Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Wed, 24 Apr 2024 19:26:27 -0400 Subject: [PATCH 02/15] fixing tests --- config/config.go | 50 +++++++++++++++++++++++--------------------- config/test_utils.go | 11 ++-------- tests/utils/utils.go | 10 ++++++--- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/config/config.go b/config/config.go index 56840235..6d146310 100644 --- a/config/config.go +++ b/config/config.go @@ -125,21 +125,22 @@ type WarpQuorum struct { QuorumDenominator uint64 } -type apiClient struct { +// P Chain API configuration, contains the base URL and query parameters +type PChainAPI struct { BaseURL string `mapstructure:"base-url" json:"base-url"` QueryParams map[string]string `mapstructure:"query-parameters" json:"query-parameters"` options []rpc.Option + client platformvm.Client } -type PChainAPI struct { - apiClient - client platformvm.Client -} - +// Info API configuration, contains the base URL and query parameters type InfoAPI struct { - apiClient - client info.Client + BaseURL string `mapstructure:"base-url" json:"base-url"` + QueryParams map[string]string `mapstructure:"query-parameters" json:"query-parameters"` + + options []rpc.Option + client info.Client } // Top-level configuration @@ -432,20 +433,13 @@ func (c *Config) InitializeWarpQuorums() error { return nil } -func (a *apiClient) Validate() error { - if _, err := url.ParseRequestURI(a.BaseURL); err != nil { +func (p *PChainAPI) Validate() error { + if _, err := url.ParseRequestURI(p.BaseURL); err != nil { return fmt.Errorf("invalid base URL: %w", err) } - a.options = make([]rpc.Option, 0, len(a.QueryParams)) - for key, value := range a.QueryParams { - a.options = append(a.options, rpc.WithQueryParam(key, value)) - } - return nil -} - -func (p *PChainAPI) Validate() error { - if err := p.apiClient.Validate(); err != nil { - return err + p.options = make([]rpc.Option, 0, len(p.QueryParams)) + for key, value := range p.QueryParams { + p.options = append(p.options, rpc.WithQueryParam(key, value)) } p.client = platformvm.NewClient(p.BaseURL) @@ -453,22 +447,30 @@ func (p *PChainAPI) Validate() error { } func (i *InfoAPI) Validate() error { - if err := i.apiClient.Validate(); err != nil { - return err + if _, err := url.ParseRequestURI(i.BaseURL); err != nil { + return fmt.Errorf("invalid base URL: %w", err) + } + i.options = make([]rpc.Option, 0, len(i.QueryParams)) + for key, value := range i.QueryParams { + i.options = append(i.options, rpc.WithQueryParam(key, value)) } i.client = info.NewClient(i.BaseURL) return nil } -func (c *apiClient) GetOptions() []rpc.Option { - return c.options +func (p *PChainAPI) GetOptions() []rpc.Option { + return p.options } func (p *PChainAPI) GetClient() platformvm.Client { return p.client } +func (i *InfoAPI) GetOptions() []rpc.Option { + return i.options +} + func (i *InfoAPI) GetClient() info.Client { return i.client } diff --git a/config/test_utils.go b/config/test_utils.go index 9cea630e..248623fa 100644 --- a/config/test_utils.go +++ b/config/test_utils.go @@ -1,5 +1,3 @@ -//go:build testing - package config import "fmt" @@ -18,15 +16,10 @@ var ( TestValidConfig = Config{ LogLevel: "info", PChainAPI: &PChainAPI{ - apiClient: apiClient{ - BaseURL: "http://test.avax.network", - }, - // BaseURL: "http://test.avax.network", + BaseURL: "http://test.avax.network", }, InfoAPI: &InfoAPI{ - apiClient: apiClient{ - BaseURL: "http://test.avax.network", - }, + BaseURL: "http://test.avax.network", }, SourceBlockchains: []*SourceBlockchain{ { diff --git a/tests/utils/utils.go b/tests/utils/utils.go index b41f2b5c..18c63811 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -166,9 +166,13 @@ func CreateDefaultRelayerConfig( } return config.Config{ - LogLevel: logging.Info.LowerString(), - PChainAPI: sourceSubnetsInfo[0].NodeURIs[0], - InfoAPI: sourceSubnetsInfo[0].NodeURIs[0], + LogLevel: logging.Info.LowerString(), + PChainAPI: &config.PChainAPI{ + BaseURL: sourceSubnetsInfo[0].NodeURIs[0], + }, + InfoAPI: &config.InfoAPI{ + BaseURL: sourceSubnetsInfo[0].NodeURIs[0], + }, StorageLocation: StorageLocation, ProcessMissedBlocks: false, SourceBlockchains: sources, From 837656b34711a2950ccc99cdc078ecd10e9c4864 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Thu, 25 Apr 2024 16:11:12 -0400 Subject: [PATCH 03/15] add back build tag and use var --- config/test_utils.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config/test_utils.go b/config/test_utils.go index 248623fa..e153fc81 100644 --- a/config/test_utils.go +++ b/config/test_utils.go @@ -1,3 +1,5 @@ +//go:build testing + package config import "fmt" @@ -9,6 +11,8 @@ var ( testAddress string = "0xd81545385803bCD83bd59f58Ba2d2c0562387F83" testPk1 string = "0xabc89e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8abc" testPk2 string = "0x12389e99c94b6912bfc12adc093c9b51124f0dc54ac7a766b2bc5ccf558d8123" + queryParamKey1 string = "key1" + queryParamVal1 string = "val1" ) // Valid configuration objects to be used by tests in external packages @@ -17,6 +21,9 @@ var ( LogLevel: "info", PChainAPI: &PChainAPI{ BaseURL: "http://test.avax.network", + QueryParams: map[string]string{ + queryParamKey1: queryParamVal1, + }, }, InfoAPI: &InfoAPI{ BaseURL: "http://test.avax.network", From b8549d93c4547e87de4586a907a76c94da85ef2f Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Thu, 25 Apr 2024 16:18:37 -0400 Subject: [PATCH 04/15] update readme --- README.md | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 2eb011a9..88d9d8d7 100644 --- a/README.md +++ b/README.md @@ -105,29 +105,45 @@ The relayer binary accepts a path to a JSON configuration file as the sole argum ### Configuration -The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: +The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "\_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: `"log-level": "verbo" | "debug" | "info" | "warn" | "error" | "fatal" | "panic"` - The log level for the relayer. Defaults to `info`. -`"p-chain-api-url": string` +`"p-chain-api": PChainAPI` -- The URL of the Avalanche P-Chain API node to which the relayer will connect. This API node needs to have the following methods enabled: - - platform.getHeight - - platform.validatedBy - - platform.getValidatorsAt OR platform.getCurrentValidators +- The configuration for the Avalanche P-Chain API node. The `PChainAPI` object has the following configuration: -`"info-api-url": string` + `"base-url": string` -- The URL of the Avalanche Info API node to which the relayer will connect. This API node needs to have the following methods enabled: + - The URL of the Avalanche P-Chain API node to which the relayer will connect. This API node needs to have the following methods enabled: + - platform.getHeight + - platform.validatedBy + - platform.getValidatorsAt OR platform.getCurrentValidators + + `"query-parameters": map[string]string` + + - Additional query parameters to include in the API requests. + +`"info-api": InfoAPI` + +- The configuration for the Avalanche Info API node. The `InfoAPI` object has the following configuration: + + `"base-url": string` - - info.peers - - info.getNetworkID + - The URL of the Avalanche Info API node to which the relayer will connect. This API node needs to have the following methods enabled: -- Additionally, if the Info API node is also a validator, it must have enabled: - - info.getNodeID - - info.getNodeIP + - info.peers + - info.getNetworkID + + - Additionally, if the Info API node is also a validator, it must have enabled: + - info.getNodeID + - info.getNodeIP + + `"query-parameters": map[string]string` + + - Additional query parameters to include in the API requests. `"storage-location": string` @@ -236,7 +252,7 @@ The relayer is configured via a JSON file, the path to which is passed in via th `"kms-aws-region": string` - The AWS region in which the KMS key is located. Required if `kms-key-id` is provided. - + ## Architecture ### Components From 683b07d28f6b740d3845440cc7d2ffb5d6ce006b Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Thu, 25 Apr 2024 16:21:26 -0400 Subject: [PATCH 05/15] update sample config --- sample-relayer-config.json | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/sample-relayer-config.json b/sample-relayer-config.json index 0880457c..9f2d0254 100644 --- a/sample-relayer-config.json +++ b/sample-relayer-config.json @@ -1,6 +1,10 @@ { - "info-api-url": "https://api.avax-test.network", - "p-chain-api-url": "https://api.avax-test.network", + "info-api": { + "base-url": "https://api.avax-test.network" + }, + "p-chain-api": { + "base-url": "https://api.avax-test.network" + }, "source-blockchains": [ { "subnet-id": "11111111111111111111111111111111LpoYY", @@ -22,9 +26,9 @@ { "subnet-id": "7WtoAMPhrmh5KosDUsFL9yTcvw7YSxiKHPpdfs4JsgW47oZT5", "blockchain-id": "2D8RG4UpSXbPbvPCAWppNJyqTG2i2CAXSkTgmTBBvs7GKNZjsY", - "vm":"evm", + "vm": "evm", "rpc-endpoint": "https://subnets.avax.network/dispatch/testnet/rpc", "account-private-key": "0x7493..." } ] -} \ No newline at end of file +} From f583856c00c9d7e3c8d84305989bec6538c1ade2 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Fri, 26 Apr 2024 09:24:25 -0400 Subject: [PATCH 06/15] save apis instead of client separately --- config/config.go | 8 ++++---- peers/app_request_network.go | 14 ++++++------- validators/canonical_validator_client.go | 26 ++++++++++-------------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/config/config.go b/config/config.go index 6d146310..a488b80c 100644 --- a/config/config.go +++ b/config/config.go @@ -459,19 +459,19 @@ func (i *InfoAPI) Validate() error { return nil } -func (p *PChainAPI) GetOptions() []rpc.Option { +func (p *PChainAPI) Options() []rpc.Option { return p.options } -func (p *PChainAPI) GetClient() platformvm.Client { +func (p *PChainAPI) Client() platformvm.Client { return p.client } -func (i *InfoAPI) GetOptions() []rpc.Option { +func (i *InfoAPI) Options() []rpc.Option { return i.options } -func (i *InfoAPI) GetClient() info.Client { +func (i *InfoAPI) Client() info.Client { return i.client } diff --git a/peers/app_request_network.go b/peers/app_request_network.go index 31a7c596..831c48b7 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -10,7 +10,6 @@ import ( "sync" "time" - "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/message" "github.com/ava-labs/avalanchego/network" @@ -35,7 +34,7 @@ const ( type AppRequestNetwork struct { Network network.Network Handler *RelayerExternalHandler - infoClient info.Client + infoAPI *config.InfoAPI logger logging.Logger lock *sync.Mutex validatorClient *validators.CanonicalValidatorClient @@ -56,8 +55,7 @@ func NewNetwork( ), ) - infoClient := cfg.InfoAPI.GetClient() - networkID, err := infoClient.GetNetworkID(context.Background(), cfg.InfoAPI.GetOptions()...) + networkID, err := cfg.InfoAPI.Client().GetNetworkID(context.Background(), cfg.InfoAPI.Options()...) if err != nil { logger.Error( "Failed to get network ID", @@ -103,7 +101,7 @@ func NewNetwork( arNetwork := &AppRequestNetwork{ Network: testNetwork, Handler: handler, - infoClient: infoClient, + infoAPI: cfg.InfoAPI, logger: logger, lock: new(sync.Mutex), validatorClient: validatorClient, @@ -150,7 +148,7 @@ func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[id // re-adding connections to already tracked peers. // Get the list of peers - peers, err := n.infoClient.Peers(context.Background()) + peers, err := n.infoAPI.Client().Peers(context.Background(), n.infoAPI.Options()...) if err != nil { n.logger.Error( "Failed to get peers", @@ -182,13 +180,13 @@ func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[id // If the Info API node is in nodeIDs, it will not be reflected in the call to info.Peers. // In this case, we need to manually track the API node. - if apiNodeID, _, err := n.infoClient.GetNodeID(context.Background()); err != nil { + if apiNodeID, _, err := n.infoAPI.Client().GetNodeID(context.Background(), n.infoAPI.Options()...); err != nil { n.logger.Error( "Failed to get API Node ID", zap.Error(err), ) } else if nodeIDs.Contains(apiNodeID) { - if apiNodeIP, err := n.infoClient.GetNodeIP(context.Background()); err != nil { + if apiNodeIP, err := n.infoAPI.Client().GetNodeIP(context.Background(), n.infoAPI.Options()...); err != nil { n.logger.Error( "Failed to get API Node IP", zap.Error(err), diff --git a/validators/canonical_validator_client.go b/validators/canonical_validator_client.go index 227a7b4d..23325764 100644 --- a/validators/canonical_validator_client.go +++ b/validators/canonical_validator_client.go @@ -9,8 +9,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/utils/rpc" - "github.com/ava-labs/avalanchego/vms/platformvm" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/config" "go.uber.org/zap" @@ -20,16 +18,14 @@ var _ validators.State = &CanonicalValidatorClient{} // CanonicalValidatorClient wraps platformvm.Client and implements validators.State type CanonicalValidatorClient struct { - client platformvm.Client - options []rpc.Option - logger logging.Logger + logger logging.Logger + pChainAPI *config.PChainAPI } -func NewCanonicalValidatorClient(logger logging.Logger, api *config.PChainAPI) *CanonicalValidatorClient { +func NewCanonicalValidatorClient(logger logging.Logger, pChainAPI *config.PChainAPI) *CanonicalValidatorClient { return &CanonicalValidatorClient{ - client: api.GetClient(), - logger: logger, - options: api.GetOptions(), + logger: logger, + pChainAPI: pChainAPI, } } @@ -63,15 +59,15 @@ func (v *CanonicalValidatorClient) GetCurrentCanonicalValidatorSet(subnetID ids. } func (v *CanonicalValidatorClient) GetMinimumHeight(ctx context.Context) (uint64, error) { - return v.client.GetHeight(ctx, v.options...) + return v.pChainAPI.Client().GetHeight(ctx, v.pChainAPI.Options()...) } func (v *CanonicalValidatorClient) GetCurrentHeight(ctx context.Context) (uint64, error) { - return v.client.GetHeight(ctx, v.options...) + return v.pChainAPI.Client().GetHeight(ctx, v.pChainAPI.Options()...) } func (v *CanonicalValidatorClient) GetSubnetID(ctx context.Context, blockchainID ids.ID) (ids.ID, error) { - return v.client.ValidatedBy(ctx, blockchainID, v.options...) + return v.pChainAPI.Client().ValidatedBy(ctx, blockchainID, v.pChainAPI.Options()...) } // Gets the current validator set of the given subnet ID, including the validators' BLS public @@ -88,7 +84,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( // Get the current subnet validators. These validators are not expected to include // BLS signing information given that addPermissionlessValidatorTx is only used to // add primary network validators. - subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil, v.options...) + subnetVdrs, err := v.pChainAPI.Client().GetCurrentValidators(ctx, subnetID, nil, v.pChainAPI.Options()...) if err != nil { return nil, err } @@ -104,7 +100,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( Weight: subnetVdr.Weight, } } - primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.options...) + primaryVdrs, err := v.pChainAPI.Client().GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.pChainAPI.Options()...) if err != nil { return nil, err } @@ -146,7 +142,7 @@ func (v *CanonicalValidatorClient) GetValidatorSet( ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. - res, err := v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) + res, err := v.pChainAPI.Client().GetValidatorsAt(ctx, subnetID, height, v.pChainAPI.Options()...) if err != nil { v.logger.Debug( "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", From 7d0f8d397edec2a7e80def2bee74ac7a14540815 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Fri, 26 Apr 2024 12:53:40 -0400 Subject: [PATCH 07/15] moving api initialization logic to peers and move validators file --- config/config.go | 62 ++----------------- config/test_utils.go | 4 +- peers/api_clients.go | 47 ++++++++++++++ peers/app_request_network.go | 50 ++++++++++----- .../validators}/canonical_validator_client.go | 27 ++++---- 5 files changed, 104 insertions(+), 86 deletions(-) create mode 100644 peers/api_clients.go rename {validators => peers/validators}/canonical_validator_client.go (86%) diff --git a/config/config.go b/config/config.go index a488b80c..c9c76659 100644 --- a/config/config.go +++ b/config/config.go @@ -11,13 +11,10 @@ import ( "net/url" "os" - "github.com/ava-labs/avalanchego/api/info" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/constants" "github.com/ava-labs/avalanchego/utils/logging" - "github.com/ava-labs/avalanchego/utils/rpc" "github.com/ava-labs/avalanchego/utils/set" - "github.com/ava-labs/avalanchego/vms/platformvm" "github.com/ava-labs/awm-relayer/utils" "github.com/ava-labs/subnet-evm/ethclient" @@ -125,22 +122,10 @@ type WarpQuorum struct { QuorumDenominator uint64 } -// P Chain API configuration, contains the base URL and query parameters -type PChainAPI struct { +// API configuration containing the base URL and query parameters +type APIConfig struct { BaseURL string `mapstructure:"base-url" json:"base-url"` QueryParams map[string]string `mapstructure:"query-parameters" json:"query-parameters"` - - options []rpc.Option - client platformvm.Client -} - -// Info API configuration, contains the base URL and query parameters -type InfoAPI struct { - BaseURL string `mapstructure:"base-url" json:"base-url"` - QueryParams map[string]string `mapstructure:"query-parameters" json:"query-parameters"` - - options []rpc.Option - client info.Client } // Top-level configuration @@ -151,8 +136,8 @@ type Config struct { APIPort uint16 `mapstructure:"api-port" json:"api-port"` MetricsPort uint16 `mapstructure:"metrics-port" json:"metrics-port"` - PChainAPI *PChainAPI `mapstructure:"p-chain-api" json:"p-chain-api"` - InfoAPI *InfoAPI `mapstructure:"info-api" json:"info-api"` + PChainAPI *APIConfig `mapstructure:"p-chain-api" json:"p-chain-api"` + InfoAPI *APIConfig `mapstructure:"info-api" json:"info-api"` SourceBlockchains []*SourceBlockchain `mapstructure:"source-blockchains" json:"source-blockchains"` DestinationBlockchains []*DestinationBlockchain `mapstructure:"destination-blockchains" json:"destination-blockchains"` ProcessMissedBlocks bool `mapstructure:"process-missed-blocks" json:"process-missed-blocks"` @@ -433,48 +418,13 @@ func (c *Config) InitializeWarpQuorums() error { return nil } -func (p *PChainAPI) Validate() error { - if _, err := url.ParseRequestURI(p.BaseURL); err != nil { +func (c *APIConfig) Validate() error { + if _, err := url.ParseRequestURI(c.BaseURL); err != nil { return fmt.Errorf("invalid base URL: %w", err) } - p.options = make([]rpc.Option, 0, len(p.QueryParams)) - for key, value := range p.QueryParams { - p.options = append(p.options, rpc.WithQueryParam(key, value)) - } - - p.client = platformvm.NewClient(p.BaseURL) return nil } -func (i *InfoAPI) Validate() error { - if _, err := url.ParseRequestURI(i.BaseURL); err != nil { - return fmt.Errorf("invalid base URL: %w", err) - } - i.options = make([]rpc.Option, 0, len(i.QueryParams)) - for key, value := range i.QueryParams { - i.options = append(i.options, rpc.WithQueryParam(key, value)) - } - - i.client = info.NewClient(i.BaseURL) - return nil -} - -func (p *PChainAPI) Options() []rpc.Option { - return p.options -} - -func (p *PChainAPI) Client() platformvm.Client { - return p.client -} - -func (i *InfoAPI) Options() []rpc.Option { - return i.options -} - -func (i *InfoAPI) Client() info.Client { - return i.client -} - // Validates the source subnet configuration, including verifying that the supported destinations are present in destinationBlockchainIDs // Does not modify the public fields as derived from the configuration passed to the application, // but does initialize private fields available through getters diff --git a/config/test_utils.go b/config/test_utils.go index e153fc81..e95f794d 100644 --- a/config/test_utils.go +++ b/config/test_utils.go @@ -19,13 +19,13 @@ var ( var ( TestValidConfig = Config{ LogLevel: "info", - PChainAPI: &PChainAPI{ + PChainAPI: &APIConfig{ BaseURL: "http://test.avax.network", QueryParams: map[string]string{ queryParamKey1: queryParamVal1, }, }, - InfoAPI: &InfoAPI{ + InfoAPI: &APIConfig{ BaseURL: "http://test.avax.network", }, SourceBlockchains: []*SourceBlockchain{ diff --git a/peers/api_clients.go b/peers/api_clients.go new file mode 100644 index 00000000..1b307039 --- /dev/null +++ b/peers/api_clients.go @@ -0,0 +1,47 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peers + +import ( + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/utils/rpc" + "github.com/ava-labs/avalanchego/vms/platformvm" + "github.com/ava-labs/awm-relayer/config" +) + +// PChainAPI holds a platformvm.Client and options for querying the P-Chain +type PChainAPI struct { + client platformvm.Client + options []rpc.Option +} + +// InfoAPI holds an info.Client and options for querying the Info API +type InfoAPI struct { + client info.Client + options []rpc.Option +} + +func NewPChainAPI(apiConfig *config.APIConfig) (*PChainAPI, error) { + client := platformvm.NewClient(apiConfig.BaseURL) + options := make([]rpc.Option, 0, len(apiConfig.QueryParams)) + for key, value := range apiConfig.QueryParams { + options = append(options, rpc.WithQueryParam(key, value)) + } + return &PChainAPI{ + client: client, + options: options, + }, nil +} + +func NewInfoAPI(apiConfig *config.APIConfig) (*InfoAPI, error) { + client := info.NewClient(apiConfig.BaseURL) + options := make([]rpc.Option, 0, len(apiConfig.QueryParams)) + for key, value := range apiConfig.QueryParams { + options = append(options, rpc.WithQueryParam(key, value)) + } + return &InfoAPI{ + client: client, + options: options, + }, nil +} diff --git a/peers/app_request_network.go b/peers/app_request_network.go index 831c48b7..848f3dfa 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -20,8 +20,8 @@ import ( "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/vms/platformvm/warp" "github.com/ava-labs/awm-relayer/config" + "github.com/ava-labs/awm-relayer/peers/validators" "github.com/ava-labs/awm-relayer/utils" - "github.com/ava-labs/awm-relayer/validators" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" ) @@ -34,7 +34,8 @@ const ( type AppRequestNetwork struct { Network network.Network Handler *RelayerExternalHandler - infoAPI *config.InfoAPI + infoAPI *InfoAPI + pChainAPI *PChainAPI logger logging.Logger lock *sync.Mutex validatorClient *validators.CanonicalValidatorClient @@ -55,15 +56,6 @@ func NewNetwork( ), ) - networkID, err := cfg.InfoAPI.Client().GetNetworkID(context.Background(), cfg.InfoAPI.Options()...) - if err != nil { - logger.Error( - "Failed to get network ID", - zap.Error(err), - ) - return nil, nil, err - } - // Create the test network for AppRequests var trackedSubnets set.Set[ids.ID] for _, sourceBlockchain := range cfg.SourceBlockchains { @@ -87,6 +79,23 @@ func NewNetwork( return nil, nil, err } + infoAPI, err := NewInfoAPI(cfg.InfoAPI) + if err != nil { + logger.Error( + "Failed to create info API", + zap.Error(err), + ) + return nil, nil, err + } + networkID, err := infoAPI.client.GetNetworkID(context.Background(), infoAPI.options...) + if err != nil { + logger.Error( + "Failed to get network ID", + zap.Error(err), + ) + return nil, nil, err + } + testNetwork, err := network.NewTestNetwork(logger, networkID, snowVdrs.NewManager(), trackedSubnets, handler) if err != nil { logger.Error( @@ -96,12 +105,21 @@ func NewNetwork( return nil, nil, err } - validatorClient := validators.NewCanonicalValidatorClient(logger, cfg.PChainAPI) + pChainAPI, err := NewPChainAPI(cfg.PChainAPI) + if err != nil { + logger.Error( + "Failed to create P-Chain API", + zap.Error(err), + ) + return nil, nil, err + } + validatorClient := validators.NewCanonicalValidatorClient(logger, pChainAPI.client, pChainAPI.options) arNetwork := &AppRequestNetwork{ Network: testNetwork, Handler: handler, - infoAPI: cfg.InfoAPI, + infoAPI: infoAPI, + pChainAPI: pChainAPI, logger: logger, lock: new(sync.Mutex), validatorClient: validatorClient, @@ -148,7 +166,7 @@ func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[id // re-adding connections to already tracked peers. // Get the list of peers - peers, err := n.infoAPI.Client().Peers(context.Background(), n.infoAPI.Options()...) + peers, err := n.infoAPI.client.Peers(context.Background(), n.infoAPI.options...) if err != nil { n.logger.Error( "Failed to get peers", @@ -180,13 +198,13 @@ func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[id // If the Info API node is in nodeIDs, it will not be reflected in the call to info.Peers. // In this case, we need to manually track the API node. - if apiNodeID, _, err := n.infoAPI.Client().GetNodeID(context.Background(), n.infoAPI.Options()...); err != nil { + if apiNodeID, _, err := n.infoAPI.client.GetNodeID(context.Background(), n.infoAPI.options...); err != nil { n.logger.Error( "Failed to get API Node ID", zap.Error(err), ) } else if nodeIDs.Contains(apiNodeID) { - if apiNodeIP, err := n.infoAPI.Client().GetNodeIP(context.Background(), n.infoAPI.Options()...); err != nil { + if apiNodeIP, err := n.infoAPI.client.GetNodeIP(context.Background(), n.infoAPI.options...); err != nil { n.logger.Error( "Failed to get API Node IP", zap.Error(err), diff --git a/validators/canonical_validator_client.go b/peers/validators/canonical_validator_client.go similarity index 86% rename from validators/canonical_validator_client.go rename to peers/validators/canonical_validator_client.go index 23325764..067dbcdf 100644 --- a/validators/canonical_validator_client.go +++ b/peers/validators/canonical_validator_client.go @@ -9,8 +9,9 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/snow/validators" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/avalanchego/utils/rpc" + "github.com/ava-labs/avalanchego/vms/platformvm" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" - "github.com/ava-labs/awm-relayer/config" "go.uber.org/zap" ) @@ -18,14 +19,16 @@ var _ validators.State = &CanonicalValidatorClient{} // CanonicalValidatorClient wraps platformvm.Client and implements validators.State type CanonicalValidatorClient struct { - logger logging.Logger - pChainAPI *config.PChainAPI + logger logging.Logger + client platformvm.Client + options []rpc.Option } -func NewCanonicalValidatorClient(logger logging.Logger, pChainAPI *config.PChainAPI) *CanonicalValidatorClient { +func NewCanonicalValidatorClient(logger logging.Logger, client platformvm.Client, options []rpc.Option) *CanonicalValidatorClient { return &CanonicalValidatorClient{ - logger: logger, - pChainAPI: pChainAPI, + logger: logger, + client: client, + options: options, } } @@ -59,15 +62,15 @@ func (v *CanonicalValidatorClient) GetCurrentCanonicalValidatorSet(subnetID ids. } func (v *CanonicalValidatorClient) GetMinimumHeight(ctx context.Context) (uint64, error) { - return v.pChainAPI.Client().GetHeight(ctx, v.pChainAPI.Options()...) + return v.client.GetHeight(ctx, v.options...) } func (v *CanonicalValidatorClient) GetCurrentHeight(ctx context.Context) (uint64, error) { - return v.pChainAPI.Client().GetHeight(ctx, v.pChainAPI.Options()...) + return v.client.GetHeight(ctx, v.options...) } func (v *CanonicalValidatorClient) GetSubnetID(ctx context.Context, blockchainID ids.ID) (ids.ID, error) { - return v.pChainAPI.Client().ValidatedBy(ctx, blockchainID, v.pChainAPI.Options()...) + return v.client.ValidatedBy(ctx, blockchainID, v.options...) } // Gets the current validator set of the given subnet ID, including the validators' BLS public @@ -84,7 +87,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( // Get the current subnet validators. These validators are not expected to include // BLS signing information given that addPermissionlessValidatorTx is only used to // add primary network validators. - subnetVdrs, err := v.pChainAPI.Client().GetCurrentValidators(ctx, subnetID, nil, v.pChainAPI.Options()...) + subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil, v.options...) if err != nil { return nil, err } @@ -100,7 +103,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( Weight: subnetVdr.Weight, } } - primaryVdrs, err := v.pChainAPI.Client().GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.pChainAPI.Options()...) + primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.options...) if err != nil { return nil, err } @@ -142,7 +145,7 @@ func (v *CanonicalValidatorClient) GetValidatorSet( ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. - res, err := v.pChainAPI.Client().GetValidatorsAt(ctx, subnetID, height, v.pChainAPI.Options()...) + res, err := v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) if err != nil { v.logger.Debug( "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", From 41a83ccbca15e3adf199426bfd8b738faed94457 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Fri, 26 Apr 2024 12:55:11 -0400 Subject: [PATCH 08/15] readme revert --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 88d9d8d7..fd8020d5 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ The relayer binary accepts a path to a JSON configuration file as the sole argum ### Configuration -The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "\_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: +The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: `"log-level": "verbo" | "debug" | "info" | "warn" | "error" | "fatal" | "panic"` From 1daaf54041c525b202b54bf2c07ab40af3856e77 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Fri, 26 Apr 2024 12:57:03 -0400 Subject: [PATCH 09/15] readme and test utils update --- README.md | 6 +++--- tests/utils/utils.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fd8020d5..7212f52e 100644 --- a/README.md +++ b/README.md @@ -105,13 +105,13 @@ The relayer binary accepts a path to a JSON configuration file as the sole argum ### Configuration -The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: +The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "\_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: `"log-level": "verbo" | "debug" | "info" | "warn" | "error" | "fatal" | "panic"` - The log level for the relayer. Defaults to `info`. -`"p-chain-api": PChainAPI` +`"p-chain-api": APIConfig` - The configuration for the Avalanche P-Chain API node. The `PChainAPI` object has the following configuration: @@ -126,7 +126,7 @@ The relayer is configured via a JSON file, the path to which is passed in via th - Additional query parameters to include in the API requests. -`"info-api": InfoAPI` +`"info-api": APIConfig` - The configuration for the Avalanche Info API node. The `InfoAPI` object has the following configuration: diff --git a/tests/utils/utils.go b/tests/utils/utils.go index 18c63811..1ee442ad 100644 --- a/tests/utils/utils.go +++ b/tests/utils/utils.go @@ -167,10 +167,10 @@ func CreateDefaultRelayerConfig( return config.Config{ LogLevel: logging.Info.LowerString(), - PChainAPI: &config.PChainAPI{ + PChainAPI: &config.APIConfig{ BaseURL: sourceSubnetsInfo[0].NodeURIs[0], }, - InfoAPI: &config.InfoAPI{ + InfoAPI: &config.APIConfig{ BaseURL: sourceSubnetsInfo[0].NodeURIs[0], }, StorageLocation: StorageLocation, From 10dd5fde7cdd26e3b792b6a10f20bcbd5c27549b Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Fri, 26 Apr 2024 12:57:44 -0400 Subject: [PATCH 10/15] remove extra char --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7212f52e..7828260c 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ The relayer binary accepts a path to a JSON configuration file as the sole argum ### Configuration -The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "\_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: +The relayer is configured via a JSON file, the path to which is passed in via the `--config-file` command line argument. Top level configuration options are also able to be set via environment variable. To get the environment variable corresponding to a key, upper case the key and change the delimiter from "-" to "_". For example, `LOG_LEVEL` sets the `"log-level"` JSON key. The following configuration options are available: `"log-level": "verbo" | "debug" | "info" | "warn" | "error" | "fatal" | "panic"` From 1316d7d76518cdf470141a507d02d7298f23004e Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Mon, 29 Apr 2024 10:50:31 -0400 Subject: [PATCH 11/15] info client wrapper and utils InitializeOptions --- peers/api_clients.go | 47 ---------- peers/app_request_network.go | 12 +-- peers/info_client.go | 87 +++++++++++++++++++ peers/utils/utils.go | 17 ++++ .../validators/canonical_validator_client.go | 24 ++++- 5 files changed, 125 insertions(+), 62 deletions(-) delete mode 100644 peers/api_clients.go create mode 100644 peers/info_client.go create mode 100644 peers/utils/utils.go diff --git a/peers/api_clients.go b/peers/api_clients.go deleted file mode 100644 index 1b307039..00000000 --- a/peers/api_clients.go +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package peers - -import ( - "github.com/ava-labs/avalanchego/api/info" - "github.com/ava-labs/avalanchego/utils/rpc" - "github.com/ava-labs/avalanchego/vms/platformvm" - "github.com/ava-labs/awm-relayer/config" -) - -// PChainAPI holds a platformvm.Client and options for querying the P-Chain -type PChainAPI struct { - client platformvm.Client - options []rpc.Option -} - -// InfoAPI holds an info.Client and options for querying the Info API -type InfoAPI struct { - client info.Client - options []rpc.Option -} - -func NewPChainAPI(apiConfig *config.APIConfig) (*PChainAPI, error) { - client := platformvm.NewClient(apiConfig.BaseURL) - options := make([]rpc.Option, 0, len(apiConfig.QueryParams)) - for key, value := range apiConfig.QueryParams { - options = append(options, rpc.WithQueryParam(key, value)) - } - return &PChainAPI{ - client: client, - options: options, - }, nil -} - -func NewInfoAPI(apiConfig *config.APIConfig) (*InfoAPI, error) { - client := info.NewClient(apiConfig.BaseURL) - options := make([]rpc.Option, 0, len(apiConfig.QueryParams)) - for key, value := range apiConfig.QueryParams { - options = append(options, rpc.WithQueryParam(key, value)) - } - return &InfoAPI{ - client: client, - options: options, - }, nil -} diff --git a/peers/app_request_network.go b/peers/app_request_network.go index 848f3dfa..76e18aa1 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -35,7 +35,6 @@ type AppRequestNetwork struct { Network network.Network Handler *RelayerExternalHandler infoAPI *InfoAPI - pChainAPI *PChainAPI logger logging.Logger lock *sync.Mutex validatorClient *validators.CanonicalValidatorClient @@ -105,21 +104,12 @@ func NewNetwork( return nil, nil, err } - pChainAPI, err := NewPChainAPI(cfg.PChainAPI) - if err != nil { - logger.Error( - "Failed to create P-Chain API", - zap.Error(err), - ) - return nil, nil, err - } - validatorClient := validators.NewCanonicalValidatorClient(logger, pChainAPI.client, pChainAPI.options) + validatorClient := validators.NewCanonicalValidatorClient(logger, cfg.PChainAPI) arNetwork := &AppRequestNetwork{ Network: testNetwork, Handler: handler, infoAPI: infoAPI, - pChainAPI: pChainAPI, logger: logger, lock: new(sync.Mutex), validatorClient: validatorClient, diff --git a/peers/info_client.go b/peers/info_client.go new file mode 100644 index 00000000..a10f6d8e --- /dev/null +++ b/peers/info_client.go @@ -0,0 +1,87 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package peers + +import ( + "context" + + "github.com/ava-labs/avalanchego/api/info" + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/rpc" + "github.com/ava-labs/avalanchego/vms/platformvm/signer" + "github.com/ava-labs/awm-relayer/config" + "github.com/ava-labs/awm-relayer/peers/utils" +) + +// InfoAPI is a wrapper around the info.Client, +// and provides additional options for the API +// passed in the config. +type InfoAPI struct { + client info.Client + options []rpc.Option +} + +func NewInfoAPI(apiConfig *config.APIConfig) (*InfoAPI, error) { + client := info.NewClient(apiConfig.BaseURL) + options := utils.InitializeOptions(apiConfig) + return &InfoAPI{ + client: client, + options: options, + }, nil +} + +// GetBlockchainID implements info.Client. +func (i *InfoAPI) GetBlockchainID(ctx context.Context, alias string) (ids.ID, error) { + return i.client.GetBlockchainID(ctx, alias, i.options...) +} + +// GetNetworkID implements info.Client. +func (i *InfoAPI) GetNetworkID(ctx context.Context) (uint32, error) { + return i.client.GetNetworkID(ctx, i.options...) +} + +// GetNetworkName implements info.Client. +func (i *InfoAPI) GetNetworkName(ctx context.Context) (string, error) { + return i.client.GetNetworkName(ctx, i.options...) +} + +// GetNodeID implements info.Client. +func (i *InfoAPI) GetNodeID(ctx context.Context) (ids.NodeID, *signer.ProofOfPossession, error) { + return i.client.GetNodeID(ctx, i.options...) +} + +// GetNodeIP implements info.Client. +func (i *InfoAPI) GetNodeIP(ctx context.Context) (string, error) { + return i.client.GetNodeIP(ctx, i.options...) +} + +// GetNodeVersion implements info.Client. +func (i *InfoAPI) GetNodeVersion(ctx context.Context) (*info.GetNodeVersionReply, error) { + return i.client.GetNodeVersion(ctx, i.options...) +} + +// GetTxFee implements info.Client. +func (i *InfoAPI) GetTxFee(ctx context.Context) (*info.GetTxFeeResponse, error) { + return i.client.GetTxFee(ctx, i.options...) +} + +// GetVMs implements info.Client. +func (i *InfoAPI) GetVMs(ctx context.Context) (map[ids.ID][]string, error) { + return i.client.GetVMs(ctx, i.options...) +} + +// IsBootstrapped implements info.Client. +func (i *InfoAPI) IsBootstrapped(ctx context.Context, chainID string) (bool, error) { + return i.client.IsBootstrapped(ctx, chainID, i.options...) +} + +// Peers implements info.Client. +func (i *InfoAPI) Peers(ctx context.Context) ([]info.Peer, error) { + return i.client.Peers(ctx, i.options...) +} + +// Uptime implements info.Client. +func (i *InfoAPI) Uptime(ctx context.Context, subnetID ids.ID) (*info.UptimeResponse, error) { + return i.client.Uptime(ctx, subnetID, i.options...) +} diff --git a/peers/utils/utils.go b/peers/utils/utils.go new file mode 100644 index 00000000..3b514b24 --- /dev/null +++ b/peers/utils/utils.go @@ -0,0 +1,17 @@ +// Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import ( + "github.com/ava-labs/avalanchego/utils/rpc" + "github.com/ava-labs/awm-relayer/config" +) + +func InitializeOptions(apiConfig *config.APIConfig) []rpc.Option { + options := make([]rpc.Option, 0, len(apiConfig.QueryParams)) + for key, value := range apiConfig.QueryParams { + options = append(options, rpc.WithQueryParam(key, value)) + } + return options +} diff --git a/peers/validators/canonical_validator_client.go b/peers/validators/canonical_validator_client.go index 067dbcdf..e93adc2f 100644 --- a/peers/validators/canonical_validator_client.go +++ b/peers/validators/canonical_validator_client.go @@ -12,6 +12,8 @@ import ( "github.com/ava-labs/avalanchego/utils/rpc" "github.com/ava-labs/avalanchego/vms/platformvm" avalancheWarp "github.com/ava-labs/avalanchego/vms/platformvm/warp" + "github.com/ava-labs/awm-relayer/config" + "github.com/ava-labs/awm-relayer/peers/utils" "go.uber.org/zap" ) @@ -24,7 +26,9 @@ type CanonicalValidatorClient struct { options []rpc.Option } -func NewCanonicalValidatorClient(logger logging.Logger, client platformvm.Client, options []rpc.Option) *CanonicalValidatorClient { +func NewCanonicalValidatorClient(logger logging.Logger, apiConfig *config.APIConfig) *CanonicalValidatorClient { + client := platformvm.NewClient(apiConfig.BaseURL) + options := utils.InitializeOptions(apiConfig) return &CanonicalValidatorClient{ logger: logger, client: client, @@ -73,6 +77,18 @@ func (v *CanonicalValidatorClient) GetSubnetID(ctx context.Context, blockchainID return v.client.ValidatedBy(ctx, blockchainID, v.options...) } +func (v *CanonicalValidatorClient) GetCurrentValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID) ([]platformvm.ClientPermissionlessValidator, error) { + return v.client.GetCurrentValidators(ctx, subnetID, nodeIDs, v.options...) +} + +func (v *CanonicalValidatorClient) GetValidatorsAt( + ctx context.Context, + subnetID ids.ID, + height uint64, +) (map[ids.NodeID]*validators.GetValidatorOutput, error) { + return v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) +} + // Gets the current validator set of the given subnet ID, including the validators' BLS public // keys. The implementation currently makes two RPC requests, one to get the subnet validators, // and another to get their BLS public keys. This is necessary in order to enable the use of @@ -87,7 +103,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( // Get the current subnet validators. These validators are not expected to include // BLS signing information given that addPermissionlessValidatorTx is only used to // add primary network validators. - subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil, v.options...) + subnetVdrs, err := v.GetCurrentValidators(ctx, subnetID, nil) if err != nil { return nil, err } @@ -103,7 +119,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( Weight: subnetVdr.Weight, } } - primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.options...) + primaryVdrs, err := v.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs) if err != nil { return nil, err } @@ -145,7 +161,7 @@ func (v *CanonicalValidatorClient) GetValidatorSet( ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. - res, err := v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) + res, err := v.GetValidatorsAt(ctx, subnetID, height) if err != nil { v.logger.Debug( "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", From cd7a5104b82e867564a35bc563fdbd02759cacd0 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Mon, 29 Apr 2024 11:21:11 -0400 Subject: [PATCH 12/15] add comment --- peers/utils/utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/peers/utils/utils.go b/peers/utils/utils.go index 3b514b24..ed713f9c 100644 --- a/peers/utils/utils.go +++ b/peers/utils/utils.go @@ -8,6 +8,7 @@ import ( "github.com/ava-labs/awm-relayer/config" ) +// InitializeOptions initializes the rpc options for an API func InitializeOptions(apiConfig *config.APIConfig) []rpc.Option { options := make([]rpc.Option, 0, len(apiConfig.QueryParams)) for key, value := range apiConfig.QueryParams { From 90fe662d3000456074782249f27222b56206c55c Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Mon, 29 Apr 2024 11:24:32 -0400 Subject: [PATCH 13/15] remove default comments --- peers/info_client.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/peers/info_client.go b/peers/info_client.go index a10f6d8e..2d6358c4 100644 --- a/peers/info_client.go +++ b/peers/info_client.go @@ -31,57 +31,46 @@ func NewInfoAPI(apiConfig *config.APIConfig) (*InfoAPI, error) { }, nil } -// GetBlockchainID implements info.Client. func (i *InfoAPI) GetBlockchainID(ctx context.Context, alias string) (ids.ID, error) { return i.client.GetBlockchainID(ctx, alias, i.options...) } -// GetNetworkID implements info.Client. func (i *InfoAPI) GetNetworkID(ctx context.Context) (uint32, error) { return i.client.GetNetworkID(ctx, i.options...) } -// GetNetworkName implements info.Client. func (i *InfoAPI) GetNetworkName(ctx context.Context) (string, error) { return i.client.GetNetworkName(ctx, i.options...) } -// GetNodeID implements info.Client. func (i *InfoAPI) GetNodeID(ctx context.Context) (ids.NodeID, *signer.ProofOfPossession, error) { return i.client.GetNodeID(ctx, i.options...) } -// GetNodeIP implements info.Client. func (i *InfoAPI) GetNodeIP(ctx context.Context) (string, error) { return i.client.GetNodeIP(ctx, i.options...) } -// GetNodeVersion implements info.Client. func (i *InfoAPI) GetNodeVersion(ctx context.Context) (*info.GetNodeVersionReply, error) { return i.client.GetNodeVersion(ctx, i.options...) } -// GetTxFee implements info.Client. func (i *InfoAPI) GetTxFee(ctx context.Context) (*info.GetTxFeeResponse, error) { return i.client.GetTxFee(ctx, i.options...) } -// GetVMs implements info.Client. func (i *InfoAPI) GetVMs(ctx context.Context) (map[ids.ID][]string, error) { return i.client.GetVMs(ctx, i.options...) } -// IsBootstrapped implements info.Client. func (i *InfoAPI) IsBootstrapped(ctx context.Context, chainID string) (bool, error) { return i.client.IsBootstrapped(ctx, chainID, i.options...) } -// Peers implements info.Client. func (i *InfoAPI) Peers(ctx context.Context) ([]info.Peer, error) { return i.client.Peers(ctx, i.options...) } -// Uptime implements info.Client. func (i *InfoAPI) Uptime(ctx context.Context, subnetID ids.ID) (*info.UptimeResponse, error) { return i.client.Uptime(ctx, subnetID, i.options...) } From d1e428fa9bd0d5794925786254027291a6eb33dc Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Mon, 29 Apr 2024 11:34:07 -0400 Subject: [PATCH 14/15] info client calls --- peers/app_request_network.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/peers/app_request_network.go b/peers/app_request_network.go index 76e18aa1..8fe1b803 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -86,7 +86,7 @@ func NewNetwork( ) return nil, nil, err } - networkID, err := infoAPI.client.GetNetworkID(context.Background(), infoAPI.options...) + networkID, err := infoAPI.GetNetworkID(context.Background()) if err != nil { logger.Error( "Failed to get network ID", @@ -156,7 +156,7 @@ func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[id // re-adding connections to already tracked peers. // Get the list of peers - peers, err := n.infoAPI.client.Peers(context.Background(), n.infoAPI.options...) + peers, err := n.infoAPI.Peers(context.Background()) if err != nil { n.logger.Error( "Failed to get peers", @@ -188,13 +188,13 @@ func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[id // If the Info API node is in nodeIDs, it will not be reflected in the call to info.Peers. // In this case, we need to manually track the API node. - if apiNodeID, _, err := n.infoAPI.client.GetNodeID(context.Background(), n.infoAPI.options...); err != nil { + if apiNodeID, _, err := n.infoAPI.GetNodeID(context.Background()); err != nil { n.logger.Error( "Failed to get API Node ID", zap.Error(err), ) } else if nodeIDs.Contains(apiNodeID) { - if apiNodeIP, err := n.infoAPI.client.GetNodeIP(context.Background(), n.infoAPI.options...); err != nil { + if apiNodeIP, err := n.infoAPI.GetNodeIP(context.Background()); err != nil { n.logger.Error( "Failed to get API Node IP", zap.Error(err), From 9caceb5d63cb7401a6110b61f3c2e83dfab13b22 Mon Sep 17 00:00:00 2001 From: Matthew Lam Date: Mon, 29 Apr 2024 15:20:03 -0400 Subject: [PATCH 15/15] update exported functions --- .../validators/canonical_validator_client.go | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/peers/validators/canonical_validator_client.go b/peers/validators/canonical_validator_client.go index e93adc2f..225638bc 100644 --- a/peers/validators/canonical_validator_client.go +++ b/peers/validators/canonical_validator_client.go @@ -77,16 +77,26 @@ func (v *CanonicalValidatorClient) GetSubnetID(ctx context.Context, blockchainID return v.client.ValidatedBy(ctx, blockchainID, v.options...) } -func (v *CanonicalValidatorClient) GetCurrentValidators(ctx context.Context, subnetID ids.ID, nodeIDs []ids.NodeID) ([]platformvm.ClientPermissionlessValidator, error) { - return v.client.GetCurrentValidators(ctx, subnetID, nodeIDs, v.options...) -} - -func (v *CanonicalValidatorClient) GetValidatorsAt( +// Gets the validator set of the given subnet at the given P-chain block height. +// Attempts to use the "getValidatorsAt" API first. If not available, falls back +// to use "getCurrentValidators", ignoring the specified P-chain block height. +func (v *CanonicalValidatorClient) GetValidatorSet( ctx context.Context, - subnetID ids.ID, height uint64, + subnetID ids.ID, ) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - return v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) + // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on + // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. + res, err := v.client.GetValidatorsAt(ctx, subnetID, height, v.options...) + if err != nil { + v.logger.Debug( + "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", + zap.String("subnetID", subnetID.String()), + zap.Uint64("pChainHeight", height), + zap.Error(err)) + return v.getCurrentValidatorSet(ctx, subnetID) + } + return res, nil } // Gets the current validator set of the given subnet ID, including the validators' BLS public @@ -103,7 +113,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( // Get the current subnet validators. These validators are not expected to include // BLS signing information given that addPermissionlessValidatorTx is only used to // add primary network validators. - subnetVdrs, err := v.GetCurrentValidators(ctx, subnetID, nil) + subnetVdrs, err := v.client.GetCurrentValidators(ctx, subnetID, nil, v.options...) if err != nil { return nil, err } @@ -119,7 +129,7 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( Weight: subnetVdr.Weight, } } - primaryVdrs, err := v.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs) + primaryVdrs, err := v.client.GetCurrentValidators(ctx, ids.Empty, subnetNodeIDs, v.options...) if err != nil { return nil, err } @@ -150,25 +160,3 @@ func (v *CanonicalValidatorClient) getCurrentValidatorSet( return res, nil } - -// Gets the validator set of the given subnet at the given P-chain block height. -// Attempts to use the "getValidatorsAt" API first. If not available, falls back -// to use "getCurrentValidators", ignoring the specified P-chain block height. -func (v *CanonicalValidatorClient) GetValidatorSet( - ctx context.Context, - height uint64, - subnetID ids.ID, -) (map[ids.NodeID]*validators.GetValidatorOutput, error) { - // First, attempt to use the "getValidatorsAt" RPC method. This method may not be available on - // all API nodes, in which case we can fall back to using "getCurrentValidators" if needed. - res, err := v.GetValidatorsAt(ctx, subnetID, height) - if err != nil { - v.logger.Debug( - "P-chain RPC to getValidatorAt returned error. Falling back to getCurrentValidators", - zap.String("subnetID", subnetID.String()), - zap.Uint64("pChainHeight", height), - zap.Error(err)) - return v.getCurrentValidatorSet(ctx, subnetID) - } - return res, nil -}