diff --git a/.github/workflows/mock_checker.yml b/.github/workflows/mock_checker.yml new file mode 100644 index 00000000..ac422cf2 --- /dev/null +++ b/.github/workflows/mock_checker.yml @@ -0,0 +1,35 @@ +name: Mock Checker + +on: + push: + branches: + - main + pull_request: + branches: + - "**" + +jobs: + generated_code: + name: generated_code + runs-on: ubuntu-22.04 + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: "go.mod" + + - name: Generate code + run: | + scripts/generate.sh + + - name: Print diff + run: git --no-pager diff + + - name: Fail if diff exists + run: git --no-pager diff --quiet diff --git a/README.md b/README.md index 8ca2e4af..a2fbe8e8 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,36 @@ Currently implemented applications are - Full-service cross-chain message delivery application that is configurable to listen to specific source and destination chain pairs and relay messages according to its configured rules. 2. [Signature Aggregator](signature-aggregator/README.md) - Lightweight API that requests and aggregates signatures from validators for any ICM message, and returns a valid signed message that the user can then self-deliver to the intended destination chain. + +## Updating dependencies and E2E testing + +Applications in this repository depend on the following upstream repositories, both directly in terms of code imports defined in the `go.mod` file as well as indirectly for E2E tests where binary versions are used to spin up the test network via `tmpnet`: + +1. [avalanchego](https://github.com/ava-labs/avalanchego/) +2. [coreth](https://github.com/ava-labs/coreth) (indirectly) +3. [subnet-evm](https://github.com/ava-labs/subnet-evm) + +> [!NOTE] +> We require any commits referenced in our `main` branch to be present in the default branches of the repositories above, but during active development it might be useful to work against changes in progress that are still on feature branches. + +When developing such features that require updates to one or more of the above, care must be taken to understand where the relevant code comes from. The binaries of applications built in this repo are built against versions referenced in the `go.mod` file. The E2E tests run against a simulated network running locally that is started by calling a separately compiled `avalanchego` binary as well as its plugins. These are compiled based on the values of `AVALANCHEGO_VERSION` in the local checkout of `subnet-evm` when running the tests locally and directly in this repository's `./scripts/versions.sh` when running E2E tests remotely through GitHub actions. + +`avalanchego` and `coreth` have a direct circular dependency and this repository is only indirectly dependent on `coreth` but directly dependent on `avalanchego`. Therefore if any updates are required from the `coreth` side, a corresponding `avalanchego` commit referencing those changes is required. On the other hand `subnet-evm` just depends directly on `avalanchego`. + +### Example dependency update flow + +The most complicated example case that can arise above is that a feature depends on a new change in `coreth`. And the steps below outline the necessary commits: + +1. If an `avalanchego` commit referencing this change in its `go.mod` file doesn't exist yet then it needs to be added. +2. Add a commit in `subnet-evm` that references the `avalanchego` commit from above in both its `go.mod` file as well as its `scripts/versions.sh` file. +3. Create a new commit in this repository referencing `avalanchego` and `subnet-evm` directly and `coreth` indirectly as well as update references in the `scripts/version.sh` file for both `AVALANCHEGO_VERSION` and `SUBNET_EVM_VERSION`. + +Publishing all of the commits mentioned above to GitHub branches will enable running E2E tests through the CI. + +Running the tests locally doesn't require publishing the `subnet-evm` commit since `./scripts/e2e_test.sh` takes a flag specifying local checkout of `subnet-evm` repository. + +> [!NOTE] +> Locally running E2E tests using local checkout of `subnet-evm` will install `avalanchego` version specified by the `AVALANCHEGO_VERSION` in that working tree's `./scripts/versions.sh`. + +> [!TIP] +> Using the local checkout it's possible to run tests against a `tmpnet` consisting of nodes using a different version of `avalanchego` than the application being tested which might be helpful when troubleshooting. \ No newline at end of file diff --git a/go.mod b/go.mod index 301b50cf..84673f3f 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,9 @@ require ( github.com/ava-labs/avalanchego v1.11.11-0.20240813203340-ab83fb41528d github.com/ava-labs/subnet-evm v0.6.9-0.20240816202746-18633729a0cd github.com/ava-labs/teleporter v1.0.5 - github.com/aws/aws-sdk-go-v2 v1.30.4 + github.com/aws/aws-sdk-go-v2 v1.30.5 github.com/aws/aws-sdk-go-v2/config v1.27.9 - github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 + github.com/aws/aws-sdk-go-v2/service/kms v1.35.6 github.com/ethereum/go-ethereum v1.13.8 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/onsi/ginkgo/v2 v2.20.2 @@ -33,8 +33,8 @@ require ( github.com/ava-labs/coreth v0.13.8-fixed-genesis-upgrade.0.20240813194342-7635a96aa180 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.17.9 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 // indirect diff --git a/go.sum b/go.sum index 4be54bcb..9f32168e 100644 --- a/go.sum +++ b/go.sum @@ -66,26 +66,26 @@ github.com/ava-labs/subnet-evm v0.6.9-0.20240816202746-18633729a0cd h1:5kJTOhmIh github.com/ava-labs/subnet-evm v0.6.9-0.20240816202746-18633729a0cd/go.mod h1:QfIzh7YxKj97jbendOHQbaAxM7SMj5MWdV13o1VLn70= github.com/ava-labs/teleporter v1.0.5 h1:51fRzvo7eo9VotYq6cldx+yoAkEk5o9yCXOJ6q3tSEo= github.com/ava-labs/teleporter v1.0.5/go.mod h1:C5/ETmPQh/MxZOCARrhPV+J40y4R3TOeT0lvQrjz/RE= -github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8= -github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= +github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g= +github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0= github.com/aws/aws-sdk-go-v2/config v1.27.9 h1:gRx/NwpNEFSk+yQlgmk1bmxxvQ5TyJ76CWXs9XScTqg= github.com/aws/aws-sdk-go-v2/config v1.27.9/go.mod h1:dK1FQfpwpql83kbD873E9vz4FyAxuJtR22wzoXn3qq0= github.com/aws/aws-sdk-go-v2/credentials v1.17.9 h1:N8s0/7yW+h8qR8WaRlPQeJ6czVMNQVNtNdUqf6cItao= github.com/aws/aws-sdk-go-v2/credentials v1.17.9/go.mod h1:446YhIdmSV0Jf/SLafGZalQo+xr2iw7/fzXGDPTU1yQ= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0 h1:af5YzcLf80tv4Em4jWVD75lpnOHSBkPUZxZfGkrI3HI= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.0/go.mod h1:nQ3how7DMnFMWiU1SpECohgC82fpn4cKZ875NDMmwtA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6 h1:b+E7zIUHMmcB4Dckjpkapoy47W6C9QBv/zoUP+Hn8Kc= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.6/go.mod h1:S2fNV0rxrP78NhPbCZeQgY8H9jdDMeGtwcfZIRxzBqU= -github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 h1:XUomV7SiclZl1QuXORdGcfFqHxEHET7rmNGtxTfNB+M= -github.com/aws/aws-sdk-go-v2/service/kms v1.35.5/go.mod h1:A5CS0VRmxxj2YKYLCY08l/Zzbd01m6JZn0WzxgT1OCA= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.6 h1:03MyCzKu/nQZRtHO7VZk1BAbSJvtv5whnfZweLdZxI0= +github.com/aws/aws-sdk-go-v2/service/kms v1.35.6/go.mod h1:K9lwD0Rsx9+NSaJKsdAdlDK4b2G4KKOEve9PzHxPoMI= github.com/aws/aws-sdk-go-v2/service/sso v1.20.3 h1:mnbuWHOcM70/OFUlZZ5rcdfA8PflGXXiefU/O+1S3+8= github.com/aws/aws-sdk-go-v2/service/sso v1.20.3/go.mod h1:5HFu51Elk+4oRBZVxmHrSds5jFXmFj8C3w7DVF2gnrs= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.3 h1:uLq0BKatTmDzWa/Nu4WO0M1AaQDaPpwTKAeByEc6WFM= diff --git a/peers/app_request_network.go b/peers/app_request_network.go index c6d9d4af..f25a8518 100644 --- a/peers/app_request_network.go +++ b/peers/app_request_network.go @@ -1,6 +1,8 @@ // Copyright (C) 2023, Ava Labs, Inc. All rights reserved. // See the file LICENSE for licensing terms. +//go:generate mockgen -source=$GOFILE -destination=./mocks/mock_app_request_network.go -package=mocks + package peers import ( @@ -28,7 +30,27 @@ const ( DefaultAppRequestTimeout = time.Second * 2 ) -type AppRequestNetwork struct { +type AppRequestNetwork interface { + ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[ids.NodeID] + ConnectToCanonicalValidators(subnetID ids.ID) ( + *ConnectedCanonicalValidators, + error, + ) + GetSubnetID(blockchainID ids.ID) (ids.ID, error) + RegisterAppRequest(requestID ids.RequestID) + RegisterRequestID( + requestID uint32, + numExpectedResponse int, + ) chan message.InboundMessage + Send( + msg message.OutboundMessage, + nodeIDs set.Set[ids.NodeID], + subnetID ids.ID, + allower subnets.Allower, + ) set.Set[ids.NodeID] +} + +type appRequestNetwork struct { network network.Network handler *RelayerExternalHandler infoAPI *InfoAPI @@ -44,7 +66,7 @@ func NewNetwork( registerer prometheus.Registerer, trackedSubnets set.Set[ids.ID], cfg Config, -) (*AppRequestNetwork, error) { +) (AppRequestNetwork, error) { logger := logging.NewLogger( "p2p-network", logging.NewWrappedCore( @@ -98,7 +120,7 @@ func NewNetwork( validatorClient := validators.NewCanonicalValidatorClient(logger, cfg.GetPChainAPI()) - arNetwork := &AppRequestNetwork{ + arNetwork := &appRequestNetwork{ network: testNetwork, handler: handler, infoAPI: infoAPI, @@ -116,7 +138,7 @@ func NewNetwork( // ConnectPeers connects the network to peers with the given nodeIDs. // Returns the set of nodeIDs that were successfully connected to. -func (n *AppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[ids.NodeID] { +func (n *appRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[ids.NodeID] { n.lock.Lock() defer n.lock.Unlock() @@ -200,7 +222,7 @@ func (c *ConnectedCanonicalValidators) GetValidator(nodeID ids.NodeID) (*warp.Va // ConnectToCanonicalValidators connects to the canonical validators of the given subnet and returns the connected // validator information -func (n *AppRequestNetwork) ConnectToCanonicalValidators(subnetID ids.ID) (*ConnectedCanonicalValidators, error) { +func (n *appRequestNetwork) ConnectToCanonicalValidators(subnetID ids.ID) (*ConnectedCanonicalValidators, error) { // Get the subnet's current canonical validator set startPChainAPICall := time.Now() validatorSet, totalValidatorWeight, err := n.validatorClient.GetCurrentCanonicalValidatorSet(subnetID) @@ -240,7 +262,7 @@ func (n *AppRequestNetwork) ConnectToCanonicalValidators(subnetID ids.ID) (*Conn }, nil } -func (n *AppRequestNetwork) Send( +func (n *appRequestNetwork) Send( msg message.OutboundMessage, nodeIDs set.Set[ids.NodeID], subnetID ids.ID, @@ -249,13 +271,13 @@ func (n *AppRequestNetwork) Send( return n.network.Send(msg, avagoCommon.SendConfig{NodeIDs: nodeIDs}, subnetID, allower) } -func (n *AppRequestNetwork) RegisterAppRequest(requestID ids.RequestID) { +func (n *appRequestNetwork) RegisterAppRequest(requestID ids.RequestID) { n.handler.RegisterAppRequest(requestID) } -func (n *AppRequestNetwork) RegisterRequestID(requestID uint32, numExpectedResponse int) chan message.InboundMessage { +func (n *appRequestNetwork) RegisterRequestID(requestID uint32, numExpectedResponse int) chan message.InboundMessage { return n.handler.RegisterRequestID(requestID, numExpectedResponse) } -func (n *AppRequestNetwork) GetSubnetID(blockchainID ids.ID) (ids.ID, error) { +func (n *appRequestNetwork) GetSubnetID(blockchainID ids.ID) (ids.ID, error) { return n.validatorClient.GetSubnetID(context.Background(), blockchainID) } @@ -263,10 +285,10 @@ func (n *AppRequestNetwork) GetSubnetID(blockchainID ids.ID) (ids.ID, error) { // Metrics // -func (n *AppRequestNetwork) setInfoAPICallLatencyMS(latency float64) { +func (n *appRequestNetwork) setInfoAPICallLatencyMS(latency float64) { n.metrics.infoAPICallLatencyMS.Observe(latency) } -func (n *AppRequestNetwork) setPChainAPICallLatencyMS(latency float64) { +func (n *appRequestNetwork) setPChainAPICallLatencyMS(latency float64) { n.metrics.pChainAPICallLatencyMS.Observe(latency) } diff --git a/peers/mocks/mock_app_request_network.go b/peers/mocks/mock_app_request_network.go new file mode 100644 index 00000000..f989358c --- /dev/null +++ b/peers/mocks/mock_app_request_network.go @@ -0,0 +1,128 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: app_request_network.go +// +// Generated by this command: +// +// mockgen -source=app_request_network.go -destination=./mocks/mock_app_request_network.go -package=mocks +// + +// Package mocks is a generated GoMock package. +package mocks + +import ( + reflect "reflect" + + ids "github.com/ava-labs/avalanchego/ids" + message "github.com/ava-labs/avalanchego/message" + subnets "github.com/ava-labs/avalanchego/subnets" + set "github.com/ava-labs/avalanchego/utils/set" + peers "github.com/ava-labs/awm-relayer/peers" + gomock "go.uber.org/mock/gomock" +) + +// MockAppRequestNetwork is a mock of AppRequestNetwork interface. +type MockAppRequestNetwork struct { + ctrl *gomock.Controller + recorder *MockAppRequestNetworkMockRecorder +} + +// MockAppRequestNetworkMockRecorder is the mock recorder for MockAppRequestNetwork. +type MockAppRequestNetworkMockRecorder struct { + mock *MockAppRequestNetwork +} + +// NewMockAppRequestNetwork creates a new mock instance. +func NewMockAppRequestNetwork(ctrl *gomock.Controller) *MockAppRequestNetwork { + mock := &MockAppRequestNetwork{ctrl: ctrl} + mock.recorder = &MockAppRequestNetworkMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockAppRequestNetwork) EXPECT() *MockAppRequestNetworkMockRecorder { + return m.recorder +} + +// ConnectPeers mocks base method. +func (m *MockAppRequestNetwork) ConnectPeers(nodeIDs set.Set[ids.NodeID]) set.Set[ids.NodeID] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConnectPeers", nodeIDs) + ret0, _ := ret[0].(set.Set[ids.NodeID]) + return ret0 +} + +// ConnectPeers indicates an expected call of ConnectPeers. +func (mr *MockAppRequestNetworkMockRecorder) ConnectPeers(nodeIDs any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectPeers", reflect.TypeOf((*MockAppRequestNetwork)(nil).ConnectPeers), nodeIDs) +} + +// ConnectToCanonicalValidators mocks base method. +func (m *MockAppRequestNetwork) ConnectToCanonicalValidators(subnetID ids.ID) (*peers.ConnectedCanonicalValidators, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ConnectToCanonicalValidators", subnetID) + ret0, _ := ret[0].(*peers.ConnectedCanonicalValidators) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ConnectToCanonicalValidators indicates an expected call of ConnectToCanonicalValidators. +func (mr *MockAppRequestNetworkMockRecorder) ConnectToCanonicalValidators(subnetID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ConnectToCanonicalValidators", reflect.TypeOf((*MockAppRequestNetwork)(nil).ConnectToCanonicalValidators), subnetID) +} + +// GetSubnetID mocks base method. +func (m *MockAppRequestNetwork) GetSubnetID(blockchainID ids.ID) (ids.ID, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetSubnetID", blockchainID) + ret0, _ := ret[0].(ids.ID) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetSubnetID indicates an expected call of GetSubnetID. +func (mr *MockAppRequestNetworkMockRecorder) GetSubnetID(blockchainID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSubnetID", reflect.TypeOf((*MockAppRequestNetwork)(nil).GetSubnetID), blockchainID) +} + +// RegisterAppRequest mocks base method. +func (m *MockAppRequestNetwork) RegisterAppRequest(requestID ids.RequestID) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RegisterAppRequest", requestID) +} + +// RegisterAppRequest indicates an expected call of RegisterAppRequest. +func (mr *MockAppRequestNetworkMockRecorder) RegisterAppRequest(requestID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterAppRequest", reflect.TypeOf((*MockAppRequestNetwork)(nil).RegisterAppRequest), requestID) +} + +// RegisterRequestID mocks base method. +func (m *MockAppRequestNetwork) RegisterRequestID(requestID uint32, numExpectedResponse int) chan message.InboundMessage { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RegisterRequestID", requestID, numExpectedResponse) + ret0, _ := ret[0].(chan message.InboundMessage) + return ret0 +} + +// RegisterRequestID indicates an expected call of RegisterRequestID. +func (mr *MockAppRequestNetworkMockRecorder) RegisterRequestID(requestID, numExpectedResponse any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterRequestID", reflect.TypeOf((*MockAppRequestNetwork)(nil).RegisterRequestID), requestID, numExpectedResponse) +} + +// Send mocks base method. +func (m *MockAppRequestNetwork) Send(msg message.OutboundMessage, nodeIDs set.Set[ids.NodeID], subnetID ids.ID, allower subnets.Allower) set.Set[ids.NodeID] { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Send", msg, nodeIDs, subnetID, allower) + ret0, _ := ret[0].(set.Set[ids.NodeID]) + return ret0 +} + +// Send indicates an expected call of Send. +func (mr *MockAppRequestNetworkMockRecorder) Send(msg, nodeIDs, subnetID, allower any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Send", reflect.TypeOf((*MockAppRequestNetwork)(nil).Send), msg, nodeIDs, subnetID, allower) +} diff --git a/relayer/application_relayer.go b/relayer/application_relayer.go index 025bc5c8..1465b69d 100644 --- a/relayer/application_relayer.go +++ b/relayer/application_relayer.go @@ -57,7 +57,7 @@ type CheckpointManager interface { type ApplicationRelayer struct { logger logging.Logger metrics *ApplicationRelayerMetrics - network *peers.AppRequestNetwork + network peers.AppRequestNetwork sourceBlockchain config.SourceBlockchain signingSubnetID ids.ID destinationClient vms.DestinationClient @@ -71,7 +71,7 @@ type ApplicationRelayer struct { func NewApplicationRelayer( logger logging.Logger, metrics *ApplicationRelayerMetrics, - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, relayerID database.RelayerID, destinationClient vms.DestinationClient, sourceBlockchain config.SourceBlockchain, diff --git a/relayer/main/main.go b/relayer/main/main.go index 3ee92351..06e4c6e5 100644 --- a/relayer/main/main.go +++ b/relayer/main/main.go @@ -368,7 +368,7 @@ func createApplicationRelayers( relayerMetrics *relayer.ApplicationRelayerMetrics, db database.RelayerDatabase, ticker *utils.Ticker, - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, cfg *config.Config, sourceClients map[ids.ID]ethclient.Client, destinationClients map[ids.ID]vms.DestinationClient, @@ -427,7 +427,7 @@ func createApplicationRelayersForSourceChain( db database.RelayerDatabase, ticker *utils.Ticker, sourceBlockchain config.SourceBlockchain, - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, cfg *config.Config, currentHeight uint64, destinationClients map[ids.ID]vms.DestinationClient, diff --git a/relayer/network_utils.go b/relayer/network_utils.go index 4931ee6a..fc1e925a 100644 --- a/relayer/network_utils.go +++ b/relayer/network_utils.go @@ -26,7 +26,7 @@ import ( // or if the subnet supports all destinations, by the quora of all configured destinations. func InitializeConnectionsAndCheckStake( logger logging.Logger, - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, cfg *config.Config, ) error { for _, sourceBlockchain := range cfg.SourceBlockchains { @@ -53,7 +53,7 @@ func InitializeConnectionsAndCheckStake( // verify that we have connected to a threshold of stake. func connectToNonPrimaryNetworkPeers( logger logging.Logger, - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, cfg *config.Config, sourceBlockchain *config.SourceBlockchain, ) error { @@ -87,7 +87,7 @@ func connectToNonPrimaryNetworkPeers( // to a threshold of stake for each blockchain. func connectToPrimaryNetworkPeers( logger logging.Logger, - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, cfg *config.Config, sourceBlockchain *config.SourceBlockchain, ) error { diff --git a/scripts/generate.sh b/scripts/generate.sh new file mode 100755 index 00000000..1193ec39 --- /dev/null +++ b/scripts/generate.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Copyright (C) 2024, Ava Labs, Inc. All rights reserved. +# See the file LICENSE for licensing terms. + +set -e errexit + +# Root directory +root=$( + cd "$(dirname "${BASH_SOURCE[0]}")" + cd .. && pwd +) + +source "$root"/scripts/versions.sh +go install -v "go.uber.org/mock/mockgen@$(getDepVersion go.uber.org/mock)" +PATH="$PATH:$(go env GOPATH)/bin" go generate ./... diff --git a/scripts/test.sh b/scripts/test.sh index a25ba856..7204adca 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -27,12 +27,14 @@ if [ "$HELP" = true ]; then fi # Directory above this script -RELAYER_PATH=$( +root=$( cd "$(dirname "${BASH_SOURCE[0]}")" cd .. && pwd ) -source "$RELAYER_PATH"/scripts/constants.sh +source "$root"/scripts/constants.sh go build -o tests/cmd/decider/decider ./tests/cmd/decider/ +"$root"/scripts/generate.sh + go test -tags testing $VERBOSE ./... diff --git a/signature-aggregator/aggregator/aggregator.go b/signature-aggregator/aggregator/aggregator.go index cea134a7..6878a744 100644 --- a/signature-aggregator/aggregator/aggregator.go +++ b/signature-aggregator/aggregator/aggregator.go @@ -54,7 +54,7 @@ var ( ) type SignatureAggregator struct { - network *peers.AppRequestNetwork + network peers.AppRequestNetwork // protected by subnetsMapLock subnetIDsByBlockchainID map[ids.ID]ids.ID logger logging.Logger @@ -67,7 +67,7 @@ type SignatureAggregator struct { } func NewSignatureAggregator( - network *peers.AppRequestNetwork, + network peers.AppRequestNetwork, logger logging.Logger, signatureCacheSize uint64, metrics *metrics.SignatureAggregatorMetrics, @@ -104,7 +104,7 @@ func (s *SignatureAggregator) CreateSignedMessage( var signingSubnet ids.ID var err error // If signingSubnet is not set we default to the subnet of the source blockchain - sourceSubnet, err := s.GetSubnetID(unsignedMessage.SourceChainID) + sourceSubnet, err := s.getSubnetID(unsignedMessage.SourceChainID) if err != nil { return nil, fmt.Errorf( "Source message subnet not found for chainID %s", @@ -338,7 +338,7 @@ func (s *SignatureAggregator) CreateSignedMessage( return nil, errNotEnoughSignatures } -func (s *SignatureAggregator) GetSubnetID(blockchainID ids.ID) (ids.ID, error) { +func (s *SignatureAggregator) getSubnetID(blockchainID ids.ID) (ids.ID, error) { s.subnetsMapLock.RLock() subnetID, ok := s.subnetIDsByBlockchainID[blockchainID] s.subnetsMapLock.RUnlock() @@ -350,11 +350,11 @@ func (s *SignatureAggregator) GetSubnetID(blockchainID ids.ID) (ids.ID, error) { if err != nil { return ids.ID{}, fmt.Errorf("source blockchain not found for chain ID %s", blockchainID) } - s.SetSubnetID(blockchainID, subnetID) + s.setSubnetID(blockchainID, subnetID) return subnetID, nil } -func (s *SignatureAggregator) SetSubnetID(blockchainID ids.ID, subnetID ids.ID) { +func (s *SignatureAggregator) setSubnetID(blockchainID ids.ID, subnetID ids.ID) { s.subnetsMapLock.Lock() s.subnetIDsByBlockchainID[blockchainID] = subnetID s.subnetsMapLock.Unlock() diff --git a/signature-aggregator/aggregator/aggregator_test.go b/signature-aggregator/aggregator/aggregator_test.go new file mode 100644 index 00000000..13a6952c --- /dev/null +++ b/signature-aggregator/aggregator/aggregator_test.go @@ -0,0 +1,87 @@ +package aggregator + +import ( + "testing" + + "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/warp" + "github.com/ava-labs/awm-relayer/peers" + "github.com/ava-labs/awm-relayer/peers/mocks" + "github.com/ava-labs/awm-relayer/signature-aggregator/metrics" + "github.com/prometheus/client_golang/prometheus" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +var sigAggMetrics *metrics.SignatureAggregatorMetrics +var messageCreator message.Creator + +func instantiateAggregator(t *testing.T) ( + *SignatureAggregator, + *mocks.MockAppRequestNetwork, +) { + mockNetwork := mocks.NewMockAppRequestNetwork(gomock.NewController(t)) + if sigAggMetrics == nil { + sigAggMetrics = metrics.NewSignatureAggregatorMetrics(prometheus.DefaultRegisterer) + } + if messageCreator == nil { + var err error + messageCreator, err = message.NewCreator( + logging.NoLog{}, + prometheus.DefaultRegisterer, + constants.DefaultNetworkCompressionType, + constants.DefaultNetworkMaximumInboundTimeout, + ) + require.Equal(t, err, nil) + } + aggregator, err := NewSignatureAggregator( + mockNetwork, + logging.NoLog{}, + 1024, + sigAggMetrics, + messageCreator, + ) + require.Equal(t, err, nil) + return aggregator, mockNetwork +} + +func TestCreateSignedMessageFailsWithNoValidators(t *testing.T) { + aggregator, mockNetwork := instantiateAggregator(t) + msg, err := warp.NewUnsignedMessage(0, ids.Empty, []byte{}) + require.Equal(t, err, nil) + mockNetwork.EXPECT().GetSubnetID(ids.Empty).Return(ids.Empty, nil) + mockNetwork.EXPECT().ConnectToCanonicalValidators(ids.Empty).Return( + &peers.ConnectedCanonicalValidators{ + ConnectedWeight: 0, + TotalValidatorWeight: 0, + ValidatorSet: []*warp.Validator{}, + }, + nil, + ) + _, err = aggregator.CreateSignedMessage(msg, ids.Empty, 80) + require.ErrorContains(t, err, "no signatures") +} + +func TestCreateSignedMessageFailsWithoutSufficientConnectedStake(t *testing.T) { + aggregator, mockNetwork := instantiateAggregator(t) + msg, err := warp.NewUnsignedMessage(0, ids.Empty, []byte{}) + require.Equal(t, err, nil) + mockNetwork.EXPECT().GetSubnetID(ids.Empty).Return(ids.Empty, nil) + mockNetwork.EXPECT().ConnectToCanonicalValidators(ids.Empty).Return( + &peers.ConnectedCanonicalValidators{ + ConnectedWeight: 0, + TotalValidatorWeight: 1, + ValidatorSet: []*warp.Validator{}, + }, + nil, + ) + _, err = aggregator.CreateSignedMessage(msg, ids.Empty, 80) + require.ErrorContains( + t, + err, + "failed to connect to a threshold of stake", + ) +}